This is an automated email from the ASF dual-hosted git repository.

desruisseaux pushed a commit to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git

commit 450ad41fa3f3d11a0af7081702e9e065ad2f862a
Author: Martin Desruisseaux <martin.desruisse...@geomatys.com>
AuthorDate: Fri Jul 26 11:09:55 2024 +0200

    Move the Apache-licensed Java code of "non-free" modules from SVN 
repository.
    It does not include the EPSG data, which are subject to EPSG terms of use.
---
 .../apache/sis/buildtools/gradle/Conventions.java  |   3 +-
 .../apache/sis/buildtools/gradle/Dependency.java   |   2 +
 .../sis/buildtools/gradle/ModularCompilation.java  |   2 +-
 netbeans-project/nbproject/project.properties      |   5 +
 optional/build.gradle.kts                          |  77 ++++++-
 .../main/META-INF/NOTICE                           |   1 +
 .../org.apache.sis.setup.InstallationResources     |   4 +
 .../main/module-info.java                          |  43 ++++
 .../sis/resources/embedded/EmbeddedResources.java  | 153 ++++++++++++++
 .../sis/resources/embedded/package-info.java       |  35 ++++
 .../resources/embedded/EmbeddedResourcesTest.java  | 125 ++++++++++++
 .../apache/sis/resources/embedded/Generator.java   | 222 +++++++++++++++++++++
 .../main/META-INF/NOTICE                           |  10 +
 .../org.apache.sis.setup.InstallationResources     |   4 +
 .../main/module-info.java                          |  41 ++++
 .../sis/referencing/factory/sql/epsg/.gitignore    |   7 +
 .../sis/referencing/factory/sql/epsg/README.md     |  23 +++
 .../factory/sql/epsg/ScriptProvider.java           | 104 ++++++++++
 .../referencing/factory/sql/epsg/package-info.java |  37 ++++
 .../factory/sql/epsg/DataScriptFormatter.java      |   0
 .../factory/sql/epsg/DataScriptFormatterTest.java  |   2 +-
 .../referencing/factory/sql/epsg/DebugTools.sql    |   0
 .../sis/referencing/factory/sql/epsg/README.md     |   0
 .../factory/sql/epsg/ScriptProviderTest.java       |  84 ++++++++
 24 files changed, 977 insertions(+), 7 deletions(-)

diff --git 
a/buildSrc/src/main/java/org/apache/sis/buildtools/gradle/Conventions.java 
b/buildSrc/src/main/java/org/apache/sis/buildtools/gradle/Conventions.java
index 471f16d67c..445979d437 100644
--- a/buildSrc/src/main/java/org/apache/sis/buildtools/gradle/Conventions.java
+++ b/buildSrc/src/main/java/org/apache/sis/buildtools/gradle/Conventions.java
@@ -198,7 +198,8 @@ abstract class Conventions {
         }
         Files.createDirectories(target.getParent());
         try {
-            Files.createLink(target, source);
+            // `toRealPath()` is necessary, otherwise symbolic links are 
reproduced verbatim and become broken.
+            Files.createLink(target, source.toRealPath());
         } catch (UnsupportedOperationException e) {
             Files.copy(source, target, StandardCopyOption.COPY_ATTRIBUTES, 
StandardCopyOption.REPLACE_EXISTING);
         }
diff --git 
a/buildSrc/src/main/java/org/apache/sis/buildtools/gradle/Dependency.java 
b/buildSrc/src/main/java/org/apache/sis/buildtools/gradle/Dependency.java
index 4842e18481..d11e9bd05e 100644
--- a/buildSrc/src/main/java/org/apache/sis/buildtools/gradle/Dependency.java
+++ b/buildSrc/src/main/java/org/apache/sis/buildtools/gradle/Dependency.java
@@ -67,6 +67,8 @@ public final class Dependency {
         Map.entry("profile.japan",            "profiles:sis-japan-profile"),
         Map.entry("console",                  "application:sis-console"),
         Map.entry("openoffice",               "application:sis-openoffice"),
+        Map.entry("epsg",                     "non-free:sis-epsg"),            
     // Optional.
+        Map.entry("database",                 "non-free:sis-embedded-data"),   
     // Optional.
         Map.entry("gui",                      "application:sis-javafx"),       
     // Optional.
         Map.entry("cql",                      "core:sis-cql"),                 
     // Incubator.
         Map.entry("storage.shapefile",        "core:sis-shapefile"),
diff --git 
a/buildSrc/src/main/java/org/apache/sis/buildtools/gradle/ModularCompilation.java
 
b/buildSrc/src/main/java/org/apache/sis/buildtools/gradle/ModularCompilation.java
index 3f6486cdf3..bbce02b655 100644
--- 
a/buildSrc/src/main/java/org/apache/sis/buildtools/gradle/ModularCompilation.java
+++ 
b/buildSrc/src/main/java/org/apache/sis/buildtools/gradle/ModularCompilation.java
@@ -253,6 +253,6 @@ final class ModularCompilation extends Conventions {
         }
         String ext = resource.getName();
         ext = ext.substring(ext.lastIndexOf('.') + 1);
-        return !EXCLUDE_RESOURCES.contains(ext);
+        return !EXCLUDE_RESOURCES.contains(ext) || 
resource.getName().equals("LICENSE.html");
     }
 }
diff --git a/netbeans-project/nbproject/project.properties 
b/netbeans-project/nbproject/project.properties
index 1dcac2e5eb..cdadf6edb2 100644
--- a/netbeans-project/nbproject/project.properties
+++ b/netbeans-project/nbproject/project.properties
@@ -70,6 +70,8 @@ modules.list = org.apache.sis.cloud.aws,\
                org.apache.sis.profile.japan,\
                org.apache.sis.referencing,\
                org.apache.sis.referencing.gazetteer,\
+               org.apache.sis.referencing.epsg,\
+               org.apache.sis.referencing.database,\
                org.apache.sis.storage,\
                org.apache.sis.storage.earthobservation,\
                org.apache.sis.storage.geotiff,\
@@ -87,6 +89,8 @@ read.options = --add-reads 
org.apache.sis.cloud.aws=org.junit.jupiter.api,junit
                --add-reads 
org.apache.sis.profile.japan=org.junit.jupiter.api,junit \
                --add-reads 
org.apache.sis.referencing=org.junit.jupiter.api,junit \
                --add-reads 
org.apache.sis.referencing.gazetteer=org.junit.jupiter.api,junit \
+               --add-reads 
org.apache.sis.referencing.epsg=org.junit.jupiter.api,junit \
+               --add-reads 
org.apache.sis.referencing.database=org.apache.sis.referencing.epsg,org.junit.jupiter.api,junit
 \
                --add-reads org.apache.sis.storage=org.junit.jupiter.api,junit \
                --add-reads 
org.apache.sis.storage.gimi=org.junit.jupiter.api,junit \
                --add-reads 
org.apache.sis.storage.coveragejson=org.junit.jupiter.api,junit \
@@ -111,6 +115,7 @@ test.options = --add-modules jama,GeographicLib.Java,\
                --add-exports 
org.apache.sis.metadata/org.apache.sis.xml.privy=org.apache.sis.storage.geotiff 
\
                --add-exports 
org.apache.sis.metadata/org.apache.sis.xml.bind.gcx=org.apache.sis.referencing \
                --add-exports 
org.apache.sis.metadata/org.apache.sis.metadata.privy=org.apache.sis.referencing.gazetteer
 \
+               --add-exports 
org.apache.sis.metadata/org.apache.sis.metadata.sql.privy=org.apache.sis.referencing.epsg
 \
                --add-exports 
org.apache.sis.referencing/org.apache.sis.referencing.internal=org.apache.sis.openoffice
 \
                --add-exports 
org.apache.sis.feature/org.apache.sis.feature.privy=org.apache.sis.storage.sql \
                --add-exports 
org.apache.sis.feature/org.apache.sis.geometry.wrapper.jts=org.apache.sis.storage.sql,org.apache.sis.portrayal.map
 \
diff --git a/optional/build.gradle.kts b/optional/build.gradle.kts
index 1a7be341ac..3393fafdc7 100644
--- a/optional/build.gradle.kts
+++ b/optional/build.gradle.kts
@@ -68,8 +68,8 @@ dependencies {
     runtimeOnly   (files("${mainDepPath}/org.apache.sis.storage.geotiff"))
     runtimeOnly   
(files("${mainDepPath}/org.apache.sis.storage.earthobservation"))
     api           (files("${mainDepPath}/org.apache.sis.portrayal"))
-    runtimeOnly   (drivers.derby.core)
-    runtimeOnly   (drivers.derby.tools)
+    api           (drivers.derby.core)
+    api           (drivers.derby.tools)
 
     // Test dependencies
     testImplementation(tests.junit5)
@@ -92,6 +92,7 @@ tasks.compileTestJava {
     srcDir.list().forEach {
         addRead(options.compilerArgs, it, 
"org.apache.sis.test.optional,org.junit.jupiter.api")
     }
+    addRead(options.compilerArgs, "org.apache.sis.referencing.database", 
"org.apache.sis.referencing.epsg");
 }
 
 /*
@@ -127,8 +128,15 @@ fun patchModuleWithTests(args : MutableList<String>, 
module : String) {
  */
 fun patchForTests(args : MutableList<String>) {
     patchModuleWithTests(args, "org.apache.sis.util")
+    patchModuleWithTests(args, "org.apache.sis.metadata")
     patchModuleWithTests(args, "org.apache.sis.feature")
-    addExport(args, "org.apache.sis.util", "org.apache.sis.test", 
"org.apache.sis.gui")
+    // ――――――――――――― Module name ――――――――――――――――――――――― Package to export 
―――――――――――――――
+    addExport(args, "org.apache.sis.util",              "org.apache.sis.test",
+                    "org.apache.sis.gui," +
+                    "org.apache.sis.referencing.epsg," +
+                    "org.apache.sis.referencing.database")
+    addExport(args, "org.apache.sis.metadata",          
"org.apache.sis.metadata.sql.privy",
+                    "org.apache.sis.referencing.epsg")
 }
 
 /*
@@ -166,6 +174,8 @@ tasks.test {
     addRead  (args, "org.apache.sis.util",                                
"ALL-UNNAMED")
     addExport(args, "org.apache.sis.util", "org.apache.sis.test",         
"ALL-UNNAMED")
     addExport(args, "org.apache.sis.gui",  "org.apache.sis.gui.internal", 
"ALL-UNNAMED")
+    args.add("--add-opens")
+    
args.add("org.apache.sis.metadata/org.apache.sis.metadata.sql=org.apache.sis.referencing.database")
     setAllJvmArgs(args)
     testLogging {
         events("FAILED", "STANDARD_OUT", "STANDARD_ERROR")
@@ -192,6 +202,65 @@ tasks.jar {
  */
 publishing {
     publications {
+        create<MavenPublication>("epsg") {
+            var module = "org.apache.sis.referencing.epsg"
+            groupId    = "org.apache.sis.non-free"
+            artifactId = "sis-epsg"
+            artifact(layout.buildDirectory.file("libs/${module}.jar"))
+            artifact(layout.buildDirectory.file("docs/${module}-sources.jar")) 
{classifier = "sources"}
+            artifact(layout.buildDirectory.file("docs/${module}-javadoc.jar")) 
{classifier = "javadoc"}
+            pom {
+                name        = "EPSG dataset for Apache SIS"
+                description = "The EPSG geodetic dataset provides definitions 
for thousands of Coordinate Reference Systems (CRS), " +
+                              "together with parameter values for thousands of 
Coordinate Operations between various pairs of CRS. " +
+                              "This module contains the SQL scripts for 
creating a local copy of EPSG geodetic dataset. " +
+                              "EPSG is maintained by the IOGP Surveying &amp; 
Positioning Committee and reproduced in this module " +
+                              "with same content. See https://epsg.org/ for 
more information."
+                licenses {
+                    license {
+                        // Not included in source code, user must download 
explicitly.
+                        // name = "EPSG terms of use"
+                        url = "https://epsg.org/terms-of-use.html";
+                        distribution = "manual"
+                    }
+                    license {
+                        // name = "Apache License, Version 2.0"
+                        url = "https://www.apache.org/licenses/LICENSE-2.0.txt";
+                        distribution = "repo"
+                    }
+                }
+            }
+        }
+        create<MavenPublication>("database") {
+            var module = "org.apache.sis.referencing.database"
+            groupId    = "org.apache.sis.non-free"
+            artifactId = "sis-embedded-data"
+            artifact(layout.buildDirectory.file("libs/${module}.jar"))
+            artifact(layout.buildDirectory.file("docs/${module}-sources.jar")) 
{classifier = "sources"}
+            artifact(layout.buildDirectory.file("docs/${module}-javadoc.jar")) 
{classifier = "javadoc"}
+            pom {
+                name        = "Data in embedded environment"
+                description = "Provides non-free data, including the EPSG 
geodetic dataset, in a single read-only JAR file. " +
+                              "This module contains a copy of EPSG geodetic 
dataset in an embedded Apache Derby database. " +
+                              "Having this artifact on the module path avoid 
the need to set the 'SIS_DATA' environment variable " +
+                              "for using the Coordinate Reference Systems 
(CRS) and Coordinate Operations defined by EPSG. " +
+                              "EPSG is maintained by the IOGP Surveying &amp; 
Positioning Committee and reproduced in this module " +
+                              "with same content. See https://epsg.org/ for 
more information."
+                licenses {
+                    license {
+                        // Not included in source code, user must download 
explicitly.
+                        // name = "EPSG terms of use"
+                        url = "https://epsg.org/terms-of-use.html";
+                        distribution = "manual"
+                    }
+                    license {
+                        // name = "Apache License, Version 2.0"
+                        url = "https://www.apache.org/licenses/LICENSE-2.0.txt";
+                        distribution = "repo"
+                    }
+                }
+            }
+        }
         create<MavenPublication>("gui") {
             var module = "org.apache.sis.gui"
             groupId    = "org.apache.sis.application"
@@ -200,7 +269,7 @@ publishing {
             artifact(layout.buildDirectory.file("docs/${module}-sources.jar")) 
{classifier = "sources"}
             artifact(layout.buildDirectory.file("docs/${module}-javadoc.jar")) 
{classifier = "javadoc"}
             pom {
-                name        = "Apache SIS application for JavaFX (optional)"
+                name        = "Apache SIS application for JavaFX"
                 description = "Client application for JavaFX. " +
                               "This module requires the JavaFX environment to 
be pre-installed. " +
                               "See 
https://openjfx.io/openjfx-docs/#install-javafx for details."
diff --git 
a/optional/src/org.apache.sis.referencing.database/main/META-INF/NOTICE 
b/optional/src/org.apache.sis.referencing.database/main/META-INF/NOTICE
new file mode 120000
index 0000000000..9a752793d4
--- /dev/null
+++ b/optional/src/org.apache.sis.referencing.database/main/META-INF/NOTICE
@@ -0,0 +1 @@
+../../../org.apache.sis.referencing.epsg/main/META-INF/NOTICE
\ No newline at end of file
diff --git 
a/optional/src/org.apache.sis.referencing.database/main/META-INF/services/org.apache.sis.setup.InstallationResources
 
b/optional/src/org.apache.sis.referencing.database/main/META-INF/services/org.apache.sis.setup.InstallationResources
new file mode 100644
index 0000000000..ade1840ca9
--- /dev/null
+++ 
b/optional/src/org.apache.sis.referencing.database/main/META-INF/services/org.apache.sis.setup.InstallationResources
@@ -0,0 +1,4 @@
+# Workaround for Maven bug https://issues.apache.org/jira/browse/MNG-7855
+# The content of this file is automatically derived from module-info.class 
file.
+# Should be used only if the JAR file was on class-path rather than 
module-path.
+org.apache.sis.resources.embedded.EmbeddedResources
diff --git 
a/optional/src/org.apache.sis.referencing.database/main/module-info.java 
b/optional/src/org.apache.sis.referencing.database/main/module-info.java
new file mode 100644
index 0000000000..cb55c1261a
--- /dev/null
+++ b/optional/src/org.apache.sis.referencing.database/main/module-info.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Embedded EPSG geodetic dataset.
+ * This module contains the data of the {@code 
org.apache.sis.referencing.epsg} module,
+ * but in a form that does not require the installation of a local database.
+ *
+ * <h2>Licensing</h2>
+ * EPSG is maintained by the <a href="https://www.iogp.org/";>International 
Association of Oil and Gas Producers</a>
+ * (IOGP) Surveying &amp; Positioning Committee and is subject to <a 
href="https://epsg.org/terms-of-use.html";>EPSG
+ * terms of use</a>. This module is not included in the Apache 
<abbr>SIS</abbr> distribution of convenience binaries,
+ * and the source code contains only the Java classes without the 
<abbr>EPSG</abbr> data. For use in an application,
+ * see <a href="https://sis.apache.org/epsg.html";>How to use EPSG geodetic 
dataset</a> on the <abbr>SIS</abbr> web site.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @version 1.5
+ * @since   0.7
+ */
+module org.apache.sis.referencing.database {
+    requires transitive org.apache.sis.referencing;
+    requires            org.apache.derby.tools;
+
+    exports org.apache.sis.resources.embedded;
+
+    uses     org.apache.sis.setup.InstallationResources;
+    provides org.apache.sis.setup.InstallationResources
+        with org.apache.sis.resources.embedded.EmbeddedResources;
+}
diff --git 
a/optional/src/org.apache.sis.referencing.database/main/org/apache/sis/resources/embedded/EmbeddedResources.java
 
b/optional/src/org.apache.sis.referencing.database/main/org/apache/sis/resources/embedded/EmbeddedResources.java
new file mode 100644
index 0000000000..fade9c8e45
--- /dev/null
+++ 
b/optional/src/org.apache.sis.referencing.database/main/org/apache/sis/resources/embedded/EmbeddedResources.java
@@ -0,0 +1,153 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.resources.embedded;
+
+import java.util.Set;
+import java.util.Collections;
+import java.util.Locale;
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.io.IOException;
+import javax.sql.DataSource;
+import org.apache.derby.jdbc.EmbeddedDataSource;
+import org.apache.sis.util.privy.MetadataServices;
+import org.apache.sis.metadata.sql.privy.Initializer;
+import org.apache.sis.setup.InstallationResources;
+import org.apache.sis.util.resources.Errors;
+
+
+/**
+ * Provides an embedded database for the EPSG geodetic dataset and other 
resources.
+ * Provides also a copy of the <a 
href="https://epsg.org/terms-of-use.html";>EPSG terms of use</a>,
+ * which should be accepted by users before the EPSG dataset can be installed.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @version 1.5
+ * @since   0.8
+ *
+ * @see <a href="https://epsg.org/";>https://epsg.org/</a>
+ */
+public class EmbeddedResources extends InstallationResources {
+    /**
+     * The name of the database embedded in the JAR file.
+     * It must be an invalid package name, because otherwise the Java Platform 
Module System (JPMS) enforces
+     * encapsulation in the same way as non-exported packages, which makes the 
database inaccessible to Derby.
+     * This naming trick is part of JPMS specification, so it should be 
reliable.
+     */
+    static final String EMBEDDED_DATABASE = "spatial-metadata";
+
+    /**
+     * Creates a new provider for connections to the embedded database.
+     */
+    public EmbeddedResources() {
+    }
+
+    /**
+     * Returns the pseudo-authority, which is {@code "Embedded"}.
+     *
+     * @return {@code "Embedded"} pseudo-authority.
+     */
+    @Override
+    public Set<String> getAuthorities() {
+        return Collections.singleton(MetadataServices.EMBEDDED);
+    }
+
+    /**
+     * Verifies that the given authority is the expected values.
+     */
+    private void verifyAuthority(final String authority) {
+        if (!MetadataServices.EMBEDDED.equalsIgnoreCase(authority)) {
+            throw new 
IllegalArgumentException(Errors.format(Errors.Keys.IllegalArgumentValue_2, 
"authority", authority));
+        }
+    }
+
+    /**
+     * Returns the license of embedded data.
+     *
+     * @param  authority  shall be {@code "Embedded"}.
+     * @param  locale     the preferred locale for the terms of use.
+     * @param  mimeType   either {@code "text/plain"} or {@code "text/html"}.
+     * @return the terms of use in plain text or HTML, or {@code null} if none.
+     * @throws IllegalArgumentException if the given {@code authority} 
argument is not the expected values.
+     * @throws IOException if an error occurred while reading the license file.
+     */
+    @Override
+    public String getLicense(String authority, Locale locale, String mimeType) 
throws IOException {
+        verifyAuthority(authority);
+        final String filename;
+        if ("text/plain".equalsIgnoreCase(mimeType)) {
+            filename = "LICENSE.txt";
+        } else if ("text/html".equalsIgnoreCase(mimeType)) {
+            filename = "LICENSE.html";
+        } else {
+            return null;
+        }
+        final StringBuilder buffer = new StringBuilder();
+        final String lineSeparator = System.lineSeparator();
+        try (BufferedReader in = new BufferedReader(new InputStreamReader(
+                EmbeddedResources.class.getResourceAsStream(filename), 
"UTF-8")))
+        {
+            String line;
+            while ((line = in.readLine()) != null) {
+                buffer.append(line).append(lineSeparator);
+            }
+        }
+        return buffer.toString();
+    }
+
+    /**
+     * Returns the data source name, which is {@code "SpatialMetadata"}.
+     *
+     * @param  authority  shall be {@code "Embedded"}.
+     * @return {@code "SpatialMetadata"}.
+     */
+    @Override
+    public String[] getResourceNames(String authority) {
+        verifyAuthority(authority);
+        return new String[] {Initializer.DATABASE};
+    }
+
+    /**
+     * Returns the data source for embedded database.
+     *
+     * @param  authority  shall be {@code "Embedded"}.
+     * @param  index      shall be 0.
+     * @return the embedded data source.
+     */
+    @Override
+    public DataSource getResource(String authority, int index) {
+        verifyAuthority(authority);
+        final EmbeddedDataSource ds = new EmbeddedDataSource();
+        ds.setDataSourceName(Initializer.DATABASE);
+        ds.setDatabaseName("classpath:SIS_DATA/Databases/" + 
EMBEDDED_DATABASE);
+        return ds;
+    }
+
+    /**
+     * Unconditionally throws an exception since the embedded database is not 
provided as SQL scripts.
+     *
+     * @param  authority  shall be {@code "Embedded"}.
+     * @param  resource   shall be 0.
+     * @return never return.
+     * @throws IOException always thrown.
+     */
+    @Override
+    public BufferedReader openScript(String authority, int resource) throws 
IOException {
+        verifyAuthority(authority);
+        throw new 
IOException(Errors.format(Errors.Keys.CanNotConvertFromType_2, 
DataSource.class, BufferedReader.class));
+    }
+}
diff --git 
a/optional/src/org.apache.sis.referencing.database/main/org/apache/sis/resources/embedded/package-info.java
 
b/optional/src/org.apache.sis.referencing.database/main/org/apache/sis/resources/embedded/package-info.java
new file mode 100644
index 0000000000..2ccb718065
--- /dev/null
+++ 
b/optional/src/org.apache.sis.referencing.database/main/org/apache/sis/resources/embedded/package-info.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Provides non-free data, including the EPSG geodetic dataset, in a single 
read-only JAR file.
+ * This module contains a copy of EPSG geodetic dataset in an embedded Apache 
Derby database.
+ * Having this module on the module-path avoid the need to set the {@code 
SIS_DATA} environment variable
+ * for using the Coordinate Reference Systems (<abbr>CRS</abbr>) and 
Coordinate Operations defined by EPSG.
+ *
+ * <h2>Licensing</h2>
+ * EPSG is maintained by the <a href="https://www.iogp.org/";>International 
Association of Oil and Gas Producers</a>
+ * (IOGP) Surveying &amp; Positioning Committee and is subject to <a 
href="https://epsg.org/terms-of-use.html";>EPSG
+ * terms of use</a>. This module is not included in the Apache 
<abbr>SIS</abbr> distribution of convenience binaries,
+ * and the source code contains only the Java classes without the 
<abbr>EPSG</abbr> data. For use in an application,
+ * see <a href="https://sis.apache.org/epsg.html";>How to use EPSG geodetic 
dataset</a> on the <abbr>SIS</abbr> web site.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @version 1.5
+ * @since   0.8
+ */
+package org.apache.sis.resources.embedded;
diff --git 
a/optional/src/org.apache.sis.referencing.database/test/org/apache/sis/resources/embedded/EmbeddedResourcesTest.java
 
b/optional/src/org.apache.sis.referencing.database/test/org/apache/sis/resources/embedded/EmbeddedResourcesTest.java
new file mode 100644
index 0000000000..4809fbd9e9
--- /dev/null
+++ 
b/optional/src/org.apache.sis.referencing.database/test/org/apache/sis/resources/embedded/EmbeddedResourcesTest.java
@@ -0,0 +1,125 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.resources.embedded;
+
+import java.io.IOException;
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.Statement;
+import java.util.ServiceLoader;
+import javax.sql.DataSource;
+import org.opengis.util.FactoryException;
+import org.opengis.referencing.crs.CoordinateReferenceSystem;
+import org.apache.sis.setup.InstallationResources;
+import org.apache.sis.metadata.sql.privy.Initializer;
+import org.apache.sis.system.DataDirectory;
+import org.apache.sis.referencing.CRS;
+import org.apache.sis.referencing.factory.sql.epsg.ScriptProvider;
+
+// Test dependencies
+import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.*;
+import static org.junit.jupiter.api.Assumptions.assumeTrue;
+import org.apache.sis.test.TestUtilities;
+
+
+/**
+ * Tests {@link EmbeddedResources}.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ */
+public final strictfp class EmbeddedResourcesTest {
+    /**
+     * Whether the database has been created.
+     */
+    private static boolean databaseCreated;
+
+    /**
+     * Returns the {@link EmbeddedResources} instance declared in the {@code 
META-INF/services/} directory.
+     * The provider may coexist with providers defined in other modules, so we 
need to filter them.
+     */
+    private static synchronized InstallationResources getInstance() {
+        assumeTrue(ScriptProvider.class.getResource("LICENSE.txt") != null,
+                "EPSG resources not found. See `README.md` for manual 
installation.");
+
+        if (!databaseCreated) try {
+            new Generator().run();
+            databaseCreated = true;
+        } catch (Exception e) {
+            throw new AssertionError(e);
+        }
+
+        InstallationResources provider = null;
+        for (InstallationResources candidate : 
ServiceLoader.load(InstallationResources.class)) {
+            if (candidate instanceof EmbeddedResources) {
+                assertNull(provider, "Expected only one instance.");
+                provider = candidate;
+            }
+        }
+        assertNotNull(provider, "Expected an instance.");
+        return provider;
+    }
+
+    /**
+     * Tests fetching the licenses.
+     *
+     * @throws IOException if an error occurred while reading a license.
+     */
+//  @Test
+    public void testLicences() throws IOException {
+        final InstallationResources provider = getInstance();
+        assertTrue(provider.getLicense("Embedded", null, 
"text/plain").contains("IOGP"));
+        assertTrue(provider.getLicense("Embedded", null, "text/html" 
).contains("IOGP"));
+    }
+
+    /**
+     * Tests connecting to the database.
+     *
+     * @throws Exception if an error occurred while fetching the data source, 
or connecting to the database.
+     */
+//  @Test
+    public void testConnection() throws Exception {
+        final String dir = DataDirectory.getenv();
+        assertTrue((dir == null) || dir.isEmpty(), "The SIS_DATA environment 
variable must be unset for enabling this test.");
+        final DataSource ds = Initializer.getDataSource();
+        assertNotNull(ds, "Cannot find the data source.");
+        try (Connection c = ds.getConnection()) {
+            
assertEquals("jdbc:derby:classpath:SIS_DATA/Databases/spatial-metadata", 
c.getMetaData().getURL(), "URL");
+            try (Statement s = c.createStatement()) {
+                try (ResultSet r = s.executeQuery("SELECT COORD_REF_SYS_NAME 
FROM EPSG.\"Coordinate Reference System\" WHERE COORD_REF_SYS_CODE = 4326")) {
+                    assertTrue(r.next(), "ResultSet.next()");
+                    assertEquals(r.getString(1), "WGS 84");
+                    assertFalse(r.next(), "ResultSet.next()");
+                }
+            }
+        }
+    }
+
+    /**
+     * Tests {@link CRS#forCode(String)} with the embedded database. This test 
asks for a CRS for which
+     * no hard-coded fallback exists in {@link 
org.apache.sis.referencing.CommonCRS}. Consequently this
+     * test should fail if we do not have a connection to a complete EPSG 
database.
+     *
+     * @throws FactoryException if an error occurred while creating the CRS.
+     */
+    @Test
+    public void testCrsforCode() throws FactoryException {
+        CoordinateReferenceSystem crs = CRS.forCode("EPSG:6676");
+        String area = 
TestUtilities.getSingleton(crs.getDomains()).getDomainOfValidity().getDescription().toString();
+        assertTrue(area.contains("Japan"), area);
+    }
+}
diff --git 
a/optional/src/org.apache.sis.referencing.database/test/org/apache/sis/resources/embedded/Generator.java
 
b/optional/src/org.apache.sis.referencing.database/test/org/apache/sis/resources/embedded/Generator.java
new file mode 100644
index 0000000000..05a9c266a2
--- /dev/null
+++ 
b/optional/src/org.apache.sis.referencing.database/test/org/apache/sis/resources/embedded/Generator.java
@@ -0,0 +1,222 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.resources.embedded;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.HashMap;
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.FileNotFoundException;
+import java.lang.reflect.Method;
+import java.net.URISyntaxException;
+import java.nio.file.FileAlreadyExistsException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.sql.CallableStatement;
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import org.opengis.util.FactoryException;
+import org.opengis.referencing.crs.GeographicCRS;
+import org.apache.derby.jdbc.EmbeddedDataSource;
+import org.apache.sis.metadata.sql.privy.Initializer;
+import org.apache.sis.metadata.sql.privy.LocalDataSource;
+import org.apache.sis.system.DataDirectory;
+import org.apache.sis.system.Shutdown;
+import org.apache.sis.metadata.MetadataStandard;
+import org.apache.sis.metadata.sql.MetadataSource;
+import org.apache.sis.metadata.sql.MetadataStoreException;
+import org.apache.sis.referencing.factory.sql.EPSGFactory;
+import org.apache.sis.referencing.factory.sql.epsg.ScriptProvider;
+
+
+/**
+ * Generates {@code SpatialMetadata} database with EPSG geodetic dataset.
+ * This class is invoked only at build time and should be excluded from the 
final JAR file.
+ * The {@link #main(String[])} method generates resources directly in the 
{@code target/classes} directory.
+ *
+ * <p><b>Note:</b>
+ * Maven usage is to generate resources in the {@code 
target/generated-resources} directory.
+ * We don't do that for avoiding unnecessary file copy operations before the 
package phase.
+ * Instead we write the files right in their final destination, {@code 
target/classes}.</p>
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ */
+public final class Generator extends ScriptProvider {
+    /**
+     * Generates the embedded resources in the {@code target/classes} 
directory.
+     * See class Javadoc for more information.
+     *
+     * @param  args  ignored. Can be null.
+     * @throws Exception if a failure occurred while searching directories,
+     *         executing SQL scripts, copying data or any other operation.
+     */
+    public static void main(String[] args) throws Exception {
+        new Generator().run();
+        Shutdown.stop(Generator.class);
+    }
+
+    /**
+     * Generates the embedded resources in the {@code target/classes} 
directory.
+     *
+     * @throws Exception if a failure occurred while searching directories,
+     *         executing SQL scripts, copying data or any other operation.
+     */
+    final void run() throws Exception {
+        if (dataSource != null) {
+            createMetadata();
+            createEPSG();
+            compress();
+            shutdown();
+        }
+    }
+
+    /**
+     * Provides a connection to the "SpatialMetadata" database, or {@code 
null} if the database already exists.
+     * The connection URL will reference the {@code 
SIS_DATA/Databases/spatial-metadata} directory in the Maven
+     * {@code target/classes} directory.
+     */
+    private final EmbeddedDataSource dataSource;
+
+    /**
+     * Creates a new database generator.
+     */
+    Generator() throws URISyntaxException, IOException {
+        Path target = copyLicenseFiles();
+        do target = target.getParent();     // Move to the root directory of 
classes.
+        while (!target.getFileName().toString().startsWith("org.apache.sis."));
+        target = target.resolve("META-INF").resolve(DataDirectory.ENV);
+        if (Files.isDirectory(target)) {
+            dataSource = null;
+            return;
+        }
+        /*
+         * Creates sub-directory step by step instead of invoking 
Files.createDirectories(…)
+         * for making sure that we do not create undesirable directories.
+         */
+        target = Files.createDirectory(target);
+        target = Files.createDirectory(target.resolve(Path.of("Databases")));
+        dataSource = new EmbeddedDataSource();
+        dataSource.setDataSourceName(Initializer.DATABASE);
+        
dataSource.setDatabaseName(target.resolve(EmbeddedResources.EMBEDDED_DATABASE).toString());
+        dataSource.setCreateDatabase("create");
+    }
+
+    /**
+     * Copies the EPSG terms of use from the {@code sis-epsg} module to this 
{@code sis-embedded-data} module.
+     * We copy those files ourselves instead than relying on {@code 
maven-resources-plugin} because a future
+     * version may combine more licenses in a single file.
+     *
+     * @return the directory where the licenses have been copied.
+     */
+    private Path copyLicenseFiles() throws URISyntaxException, IOException {
+        final Class<?> consumer = EmbeddedResources.class;
+        final Path target = 
Path.of(consumer.getResource(consumer.getSimpleName() + 
".class").toURI()).getParent();
+        final String[] files = {"LICENSE.txt", "LICENSE.html"};
+        for (String file : files) {
+            try (InputStream in = openStream(file)) {
+                if (in == null) throw new FileNotFoundException(file);
+                Files.copy(in, target.resolve(file));
+            } catch (FileAlreadyExistsException e) {
+                break;
+            }
+        }
+        return target;
+    }
+
+    /**
+     * Creates the metadata database schema.
+     *
+     * @throws FactoryException  if an error occurred while creating or 
querying the database.
+     */
+    private void createMetadata() throws MetadataStoreException, 
ReflectiveOperationException {
+        try (MetadataSource md = new 
MetadataSource(MetadataStandard.ISO_19115, dataSource, "metadata", null)) {
+            Method install = md.getClass().getDeclaredMethod("install");
+            install.setAccessible(true);
+            install.invoke(md);
+        }
+    }
+
+    /**
+     * Creates the EPSG database schema.
+     *
+     * @throws FactoryException  if an error occurred while creating or 
querying the database.
+     */
+    private void createEPSG() throws FactoryException {
+        final Map<String,Object> properties = new HashMap<>();
+        properties.put("dataSource", dataSource);
+        properties.put("scriptProvider", this);
+        /*
+         * Asking for any CRS will trig the database creation.
+         * We perform a simple verification on the created CRS as a matter of 
principle.
+         */
+        final GeographicCRS crs;
+        try (EPSGFactory factory = new EPSGFactory(properties)) {
+            crs = factory.createGeographicCRS("4326");
+        }
+        if (!crs.getName().getCode().equals("WGS 84")) {
+            throw new FactoryException("Unexpected CRS: " + crs.getName());
+        }
+    }
+
+    /**
+     * Compresses all tables in the EPSG schema. Compression can save space if 
there was many update
+     * or delete operations in the database. In the case of the database 
generated by this class,
+     * the benefit is very small because the database is fresh. But it is 
still non-zero.
+     */
+    private void compress() throws SQLException {
+        try (Connection c = dataSource.getConnection()) {
+            List<String> tables = new ArrayList<>(80);  // As (schema,table) 
pairs.
+            try (ResultSet r = c.getMetaData().getTables(null, null, null, 
null)) {
+                while (r.next()) {
+                    final String schema = r.getString("TABLE_SCHEM");
+                    if (!schema.startsWith("SYS")) {
+                        tables.add(schema);
+                        tables.add(r.getString("TABLE_NAME"));
+                    }
+                }
+            }
+            try (CallableStatement cs = c.prepareCall("CALL 
SYSCS_UTIL.SYSCS_COMPRESS_TABLE(?, ?, ?)")) {
+                for (int i=0; i<tables.size();) {
+                    cs.setString(1, tables.get(i++));       // Schema name.
+                    cs.setString(2, tables.get(i++));       // Table name.
+                    cs.setShort (3, (short) 1);
+                    cs.execute();
+                }
+            }
+        }
+    }
+
+    /**
+     * Shutdowns the Derby database.
+     */
+    private void shutdown() throws SQLException {
+        dataSource.setCreateDatabase("no");
+        dataSource.setShutdownDatabase("shutdown");
+        try {
+            dataSource.getConnection().close();
+        } catch (SQLException e) {
+            if (LocalDataSource.isSuccessfulShutdown(e)) {
+                return;
+            }
+            throw e;
+        }
+        throw new SQLException("Shutdown has not been completed.");
+    }
+}
diff --git a/optional/src/org.apache.sis.referencing.epsg/main/META-INF/NOTICE 
b/optional/src/org.apache.sis.referencing.epsg/main/META-INF/NOTICE
new file mode 100644
index 0000000000..276d8b81c7
--- /dev/null
+++ b/optional/src/org.apache.sis.referencing.epsg/main/META-INF/NOTICE
@@ -0,0 +1,10 @@
+EPSG geodetic dataset for Apache SIS
+Copyright The Apache Software Foundation
+Copyright International Association of Oil and Gas Producers (IOGP)
+
+This product includes software developed at
+The Apache Software Foundation (https://www.apache.org/).
+
+This product includes geodetic dataset maintained by the
+International Association of Oil and Gas Producers (IOGP)
+https://epsg.org/
diff --git 
a/optional/src/org.apache.sis.referencing.epsg/main/META-INF/services/org.apache.sis.setup.InstallationResources
 
b/optional/src/org.apache.sis.referencing.epsg/main/META-INF/services/org.apache.sis.setup.InstallationResources
new file mode 100644
index 0000000000..ad7e1b09e3
--- /dev/null
+++ 
b/optional/src/org.apache.sis.referencing.epsg/main/META-INF/services/org.apache.sis.setup.InstallationResources
@@ -0,0 +1,4 @@
+# Workaround for Maven bug https://issues.apache.org/jira/browse/MNG-7855
+# The content of this file is automatically derived from module-info.class 
file.
+# Should be used only if the JAR file was on class-path rather than 
module-path.
+org.apache.sis.referencing.factory.sql.epsg.ScriptProvider
diff --git a/optional/src/org.apache.sis.referencing.epsg/main/module-info.java 
b/optional/src/org.apache.sis.referencing.epsg/main/module-info.java
new file mode 100644
index 0000000000..a6ec05daf8
--- /dev/null
+++ b/optional/src/org.apache.sis.referencing.epsg/main/module-info.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2023 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * SQL scripts for EPSG geodetic data set installation.
+ * This module provides the <abbr>EPSG</abbr> data of the {@code 
org.apache.sis.referencing.database} module,
+ * but in a form that allows installation on <abbr>SQL</abbr> databases other 
than Apache Derby.
+ *
+ * <h2>Licensing</h2>
+ * EPSG is maintained by the <a href="https://www.iogp.org/";>International 
Association of Oil and Gas Producers</a>
+ * (IOGP) Surveying &amp; Positioning Committee and is subject to <a 
href="https://epsg.org/terms-of-use.html";>EPSG
+ * terms of use</a>. This module is not included in the Apache 
<abbr>SIS</abbr> distribution of convenience binaries,
+ * and the source code contains only the Java classes without the 
<abbr>EPSG</abbr> data. For use in an application,
+ * see <a href="https://sis.apache.org/epsg.html";>How to use EPSG geodetic 
dataset</a> on the <abbr>SIS</abbr> web site.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @version 1.5
+ * @since   0.7
+ */
+module org.apache.sis.referencing.epsg {
+    requires transitive org.apache.sis.referencing;
+
+    exports org.apache.sis.referencing.factory.sql.epsg;
+
+    uses     org.apache.sis.setup.InstallationResources;
+    provides org.apache.sis.setup.InstallationResources
+        with org.apache.sis.referencing.factory.sql.epsg.ScriptProvider;
+}
diff --git 
a/optional/src/org.apache.sis.referencing.epsg/main/org/apache/sis/referencing/factory/sql/epsg/.gitignore
 
b/optional/src/org.apache.sis.referencing.epsg/main/org/apache/sis/referencing/factory/sql/epsg/.gitignore
new file mode 100644
index 0000000000..f7436babfc
--- /dev/null
+++ 
b/optional/src/org.apache.sis.referencing.epsg/main/org/apache/sis/referencing/factory/sql/epsg/.gitignore
@@ -0,0 +1,7 @@
+# Those files must be put manually for licensing reasons.
+# See README.md for more details.
+Data.sql
+FKeys.sql
+LICENSE.html
+LICENSE.txt
+Tables.sql
diff --git 
a/optional/src/org.apache.sis.referencing.epsg/main/org/apache/sis/referencing/factory/sql/epsg/README.md
 
b/optional/src/org.apache.sis.referencing.epsg/main/org/apache/sis/referencing/factory/sql/epsg/README.md
new file mode 100644
index 0000000000..fcd650b55e
--- /dev/null
+++ 
b/optional/src/org.apache.sis.referencing.epsg/main/org/apache/sis/referencing/factory/sql/epsg/README.md
@@ -0,0 +1,23 @@
+# Manual installation of EPSG data
+
+This is the directory where EPSG data can be placed.
+Those data are not commited in the Apache SIS source code repository because
+they are licensed under [EPSG terms of 
use](https://epsg.org/terms-of-use.html).
+For including the EPSG data in the `org.apache.sis.referencing.epsg` artifact,
+the following commands must be executed manually with this directory as the
+current directory:
+
+```shell
+# Execute the following in a separated directory.
+svn checkout https://svn.apache.org/repos/asf/sis/data/non-free/
+cd non-free
+export NON_FREE_DIR=`pwd`
+
+# Execute the following in the directory of this `README.md` file.
+ln --symbolic $NON_FREE_DIR/EPSG/LICENSE.txt
+ln --symbolic $NON_FREE_DIR/EPSG/LICENSE.html
+ln --symbolic $NON_FREE_DIR/EPSG/Tables.sql
+ln --symbolic $NON_FREE_DIR/EPSG/Data.sql
+ln --symbolic $NON_FREE_DIR/EPSG/FKeys.sql
+cd -
+```
diff --git 
a/optional/src/org.apache.sis.referencing.epsg/main/org/apache/sis/referencing/factory/sql/epsg/ScriptProvider.java
 
b/optional/src/org.apache.sis.referencing.epsg/main/org/apache/sis/referencing/factory/sql/epsg/ScriptProvider.java
new file mode 100644
index 0000000000..333a9b1dbf
--- /dev/null
+++ 
b/optional/src/org.apache.sis.referencing.epsg/main/org/apache/sis/referencing/factory/sql/epsg/ScriptProvider.java
@@ -0,0 +1,104 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.referencing.factory.sql.epsg;
+
+import java.util.Locale;
+import java.io.BufferedReader;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import org.apache.sis.util.resources.Errors;
+import org.apache.sis.util.privy.Constants;
+import org.apache.sis.referencing.factory.sql.InstallationScriptProvider;
+
+
+/**
+ * Provides SQL scripts for creating a local copy of the EPSG geodetic dataset.
+ * Provides also a copy of the <a 
href="https://epsg.org/terms-of-use.html";>EPSG terms of use</a>,
+ * which should be accepted by users before the EPSG dataset can be installed.
+ *
+ * <p><b>Notice</b></p>
+ * EPSG is maintained by the <a href="http://www.iogp.org/";>International 
Association of Oil and Gas Producers</a>
+ * (IOGP) Surveying &amp; Positioning Committee. The SQL scripts are given by 
this class with identical content,
+ * but in a more compact format.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @version 1.5
+ * @since   0.7
+ *
+ * @see <a href="https://epsg.org/";>https://epsg.org/</a>
+ */
+public class ScriptProvider extends InstallationScriptProvider {
+    /**
+     * Creates a new EPSG scripts provider.
+     */
+    public ScriptProvider() {
+        super(Constants.EPSG, PREPARE, "Tables.sql", "Data.sql", "FKeys.sql", 
FINISH);
+    }
+
+    /**
+     * Returns a copy of EPSG terms of use.
+     *
+     * @param  authority  one of the values returned by {@link 
#getAuthorities()}.
+     * @param  locale     the preferred locale for the terms of use.
+     * @param  mimeType   either {@code "text/plain"} or {@code "text/html"}.
+     * @return the terms of use in plain text or HTML, or {@code null} if the 
specified MIME type is not recognized.
+     * @throws IOException if an error occurred while reading the license file.
+     *
+     * @see <a 
href="https://epsg.org/terms-of-use.html";>https://epsg.org/terms-of-use.html</a>
+     */
+    @Override
+    public String getLicense(final String authority, final Locale locale, 
final String mimeType) throws IOException {
+        if (!Constants.EPSG.equals(authority)) {
+            throw new 
IllegalArgumentException(Errors.format(Errors.Keys.IllegalArgumentValue_2, 
"authority", authority));
+        }
+        final String filename;
+        if ("text/plain".equalsIgnoreCase(mimeType)) {
+            filename = "LICENSE.txt";
+        } else if ("text/html".equalsIgnoreCase(mimeType)) {
+            filename = "LICENSE.html";
+        } else {
+            return null;
+        }
+        final InputStream in = 
ScriptProvider.class.getResourceAsStream(filename);
+        if (in == null) {
+            throw new FileNotFoundException(filename);
+        }
+        final StringBuilder buffer = new StringBuilder();
+        final String lineSeparator = System.lineSeparator();
+        try (BufferedReader r = new BufferedReader(new InputStreamReader(in, 
"UTF-8"))) {
+            String line;
+            while ((line = r.readLine()) != null) {
+                buffer.append(line).append(lineSeparator);
+            }
+        }
+        return buffer.toString();
+    }
+
+    /**
+     * Returns the content for the SQL script of the given name.
+     * The file encoding is UTF-8.
+     *
+     * @param name  either {@code "Tables.sql"}, {@code "Data.sql"} or {@code 
"FKeys.sql"}.
+     * @return the SQL script of the given name, or {@code null} if the given 
name is not one of the expected names.
+     */
+    @Override
+    protected InputStream openStream(final String name) {
+        return ScriptProvider.class.getResourceAsStream(name);
+    }
+}
diff --git 
a/optional/src/org.apache.sis.referencing.epsg/main/org/apache/sis/referencing/factory/sql/epsg/package-info.java
 
b/optional/src/org.apache.sis.referencing.epsg/main/org/apache/sis/referencing/factory/sql/epsg/package-info.java
new file mode 100644
index 0000000000..982b8fa434
--- /dev/null
+++ 
b/optional/src/org.apache.sis.referencing.epsg/main/org/apache/sis/referencing/factory/sql/epsg/package-info.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * SQL scripts for EPSG geodetic data set installation.
+ * The <a href="https://epsg.org/";>EPSG</a> geodetic dataset provides 
definitions for thousands of
+ * {@linkplain org.opengis.referencing.crs.CoordinateReferenceSystem 
Coordinate Reference Systems} (<abbr>CRS</abbr>),
+ * together with parameter values for thousands of {@linkplain 
org.opengis.referencing.operation.ConcatenatedOperation
+ * Coordinate Operations} between various <abbr>CRS</abbr> pairs.
+ * This module contains the SQL scripts for creating a local copy of EPSG 
geodetic dataset.
+ *
+ * <h2>Licensing</h2>
+ * EPSG is maintained by the <a href="https://www.iogp.org/";>International 
Association of Oil and Gas Producers</a>
+ * (IOGP) Surveying &amp; Positioning Committee and is subject to <a 
href="https://epsg.org/terms-of-use.html";>EPSG
+ * terms of use</a>. This module is not included in the Apache 
<abbr>SIS</abbr> distribution of convenience binaries,
+ * and the source code contains only the Java classes without the 
<abbr>EPSG</abbr> data. For use in an application,
+ * see <a href="https://sis.apache.org/epsg.html";>How to use EPSG geodetic 
dataset</a> on the <abbr>SIS</abbr> web site.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @version 1.5
+ * @since   0.7
+ */
+package org.apache.sis.referencing.factory.sql.epsg;
diff --git 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/factory/sql/epsg/DataScriptFormatter.java
 
b/optional/src/org.apache.sis.referencing.epsg/test/org/apache/sis/referencing/factory/sql/epsg/DataScriptFormatter.java
similarity index 100%
rename from 
endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/factory/sql/epsg/DataScriptFormatter.java
rename to 
optional/src/org.apache.sis.referencing.epsg/test/org/apache/sis/referencing/factory/sql/epsg/DataScriptFormatter.java
diff --git 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/factory/sql/epsg/DataScriptFormatterTest.java
 
b/optional/src/org.apache.sis.referencing.epsg/test/org/apache/sis/referencing/factory/sql/epsg/DataScriptFormatterTest.java
similarity index 93%
rename from 
endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/factory/sql/epsg/DataScriptFormatterTest.java
rename to 
optional/src/org.apache.sis.referencing.epsg/test/org/apache/sis/referencing/factory/sql/epsg/DataScriptFormatterTest.java
index 094f688605..e34bf5b554 100644
--- 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/factory/sql/epsg/DataScriptFormatterTest.java
+++ 
b/optional/src/org.apache.sis.referencing.epsg/test/org/apache/sis/referencing/factory/sql/epsg/DataScriptFormatterTest.java
@@ -39,7 +39,7 @@ public final class DataScriptFormatterTest extends TestCase {
      */
     @Test
     public void testRemoveLF() {
-        final StringBuilder buffer = new StringBuilder(" \nOne,\nTwo, \n Three 
Four\nFive \nSix \n");
+        final var buffer = new StringBuilder(" \nOne,\nTwo, \n Three 
Four\nFive \nSix \n");
         DataScriptFormatter.removeLF(buffer);
         assertEquals("One,Two,Three Four Five Six", buffer.toString());
     }
diff --git 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/factory/sql/epsg/DebugTools.sql
 
b/optional/src/org.apache.sis.referencing.epsg/test/org/apache/sis/referencing/factory/sql/epsg/DebugTools.sql
similarity index 100%
rename from 
endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/factory/sql/epsg/DebugTools.sql
rename to 
optional/src/org.apache.sis.referencing.epsg/test/org/apache/sis/referencing/factory/sql/epsg/DebugTools.sql
diff --git 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/factory/sql/epsg/README.md
 
b/optional/src/org.apache.sis.referencing.epsg/test/org/apache/sis/referencing/factory/sql/epsg/README.md
similarity index 100%
rename from 
endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/factory/sql/epsg/README.md
rename to 
optional/src/org.apache.sis.referencing.epsg/test/org/apache/sis/referencing/factory/sql/epsg/README.md
diff --git 
a/optional/src/org.apache.sis.referencing.epsg/test/org/apache/sis/referencing/factory/sql/epsg/ScriptProviderTest.java
 
b/optional/src/org.apache.sis.referencing.epsg/test/org/apache/sis/referencing/factory/sql/epsg/ScriptProviderTest.java
new file mode 100644
index 0000000000..111bdd6474
--- /dev/null
+++ 
b/optional/src/org.apache.sis.referencing.epsg/test/org/apache/sis/referencing/factory/sql/epsg/ScriptProviderTest.java
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.referencing.factory.sql.epsg;
+
+import java.io.IOException;
+import java.io.BufferedReader;
+import java.util.ServiceLoader;
+import org.apache.sis.setup.InstallationResources;
+
+// Test dependencies
+import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.*;
+import static org.junit.jupiter.api.Assumptions.assumeTrue;
+
+
+/**
+ * Test {@link ScriptProvider}.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ */
+public final strictfp class ScriptProviderTest {
+    /**
+     * Returns the {@link ScriptProvider} instance declared in the {@code 
META-INF/services/} directory.
+     * The provider may coexist with providers defined in other modules, so we 
need to filter them.
+     */
+    private static InstallationResources getInstance() {
+        assumeTrue(ScriptProvider.class.getResource("LICENSE.txt") != null,
+                "EPSG resources not found. See `README.md` for manual 
installation.");
+
+        InstallationResources provider = null;
+        for (InstallationResources candidate : 
ServiceLoader.load(InstallationResources.class)) {
+            if (candidate instanceof ScriptProvider) {
+                assertNull(provider, "Expected only one instance.");
+                provider = candidate;
+            }
+        }
+        assertNotNull(provider, "Expected an instance.");
+        return provider;
+    }
+
+    /**
+     * Tests fetching the licenses.
+     *
+     * @throws IOException if an error occurred while reading a license.
+     */
+    @Test
+    public void testLicences() throws IOException {
+        final InstallationResources provider = getInstance();
+        assertTrue(provider.getLicense("EPSG", null, 
"text/plain").contains("IOGP"));
+        assertTrue(provider.getLicense("EPSG", null, "text/html" 
).contains("IOGP"));
+    }
+
+    /**
+     * Tests fetching the resources.
+     *
+     * @throws IOException if an error occurred while reading a resource.
+     */
+    @Test
+    public void testResources() throws IOException {
+        final InstallationResources provider = getInstance();
+        final String[] names = provider.getResourceNames("EPSG");
+        assertArrayEquals(new String[] {"Prepare", "Tables.sql", "Data.sql", 
"FKeys.sql", "Finish"}, names);
+        for (int i=0; i<names.length; i++) {
+            try (BufferedReader in = provider.openScript("EPSG", i)) {
+                // Just verify that we can read.
+                assertFalse(in.readLine().isEmpty());
+            }
+        }
+    }
+}

Reply via email to