sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1813747 [8/13] - in /sis/branches/JDK9: ./ application/ application/sis-console/ application/sis-console/src/main/artifact/ application/sis-console/src/main/artifact/lib/ application/sis-console/src/main/artifact/lib/darwin/ application/si...
Date Mon, 30 Oct 2017 10:25:10 GMT
Modified: sis/branches/JDK9/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java?rev=1813747&r1=1813746&r2=1813747&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java [UTF-8] Mon Oct 30 10:25:08 2017
@@ -31,6 +31,7 @@ import org.junit.BeforeClass;
  */
 @Suite.SuiteClasses({
     org.apache.sis.internal.metadata.AxisDirectionsTest.class,
+    org.apache.sis.internal.referencing.LazySetTest.class,
     org.apache.sis.internal.referencing.FormulasTest.class,
     org.apache.sis.internal.referencing.j2d.ShapeUtilitiesTest.class,
     org.apache.sis.internal.referencing.PositionalAccuracyConstantTest.class,
@@ -124,6 +125,7 @@ import org.junit.BeforeClass;
     org.apache.sis.referencing.operation.transform.ContextualParametersTest.class,
     org.apache.sis.referencing.operation.transform.EllipsoidToCentricTransformTest.class,
     org.apache.sis.referencing.operation.transform.MolodenskyTransformTest.class,
+    org.apache.sis.referencing.operation.transform.AbridgedMolodenskyTransformTest.class,
     org.apache.sis.referencing.operation.transform.SphericalToCartesianTest.class,
     org.apache.sis.referencing.operation.transform.CartesianToSphericalTest.class,
     org.apache.sis.referencing.operation.transform.PolarToCartesianTest.class,
@@ -220,6 +222,7 @@ import org.junit.BeforeClass;
     org.apache.sis.referencing.cs.CodesTest.class,
     org.apache.sis.referencing.CRSTest.class,
     org.apache.sis.internal.referencing.DefinitionVerifierTest.class,
+    org.apache.sis.internal.referencing.CoordinateOperationsTest.class,
 
     // Coordinate operation finders are last, since they need everything else.
     org.apache.sis.referencing.operation.CoordinateOperationRegistryTest.class,
@@ -228,12 +231,13 @@ import org.junit.BeforeClass;
     org.apache.sis.referencing.operation.builder.LinearTransformBuilderTest.class,
     org.apache.sis.referencing.operation.builder.LocalizationGridBuilderTest.class,
 
-    // Geometry
+    // Geometry and miscellaneous
     org.apache.sis.geometry.AbstractDirectPositionTest.class,
     org.apache.sis.geometry.GeneralDirectPositionTest.class,
     org.apache.sis.geometry.DirectPosition1DTest.class,
     org.apache.sis.geometry.DirectPosition2DTest.class,
     org.apache.sis.geometry.AbstractEnvelopeTest.class,
+    org.apache.sis.geometry.ArrayEnvelopeTest.class,
     org.apache.sis.geometry.GeneralEnvelopeTest.class,
     org.apache.sis.geometry.SubEnvelopeTest.class,
     org.apache.sis.geometry.ImmutableEnvelopeTest.class,
@@ -242,10 +246,12 @@ import org.junit.BeforeClass;
     org.apache.sis.geometry.Shapes2DTest.class,                 // Simpler than EnvelopesTest.
     org.apache.sis.geometry.EnvelopesTest.class,
     org.apache.sis.internal.referencing.ServicesForMetadataTest.class,
+    org.apache.sis.internal.metadata.EllipsoidalHeightCombinerTest.class,
     org.apache.sis.geometry.CoordinateFormatTest.class,
 
     org.apache.sis.distance.LatLonPointRadiusTest.class,        // Pending refactoring in a geometry package.
 
+    org.apache.sis.test.integration.CoordinateReferenceSystemTest.class,
     org.apache.sis.test.integration.CoordinateOperationTest.class,
     org.apache.sis.test.integration.DatumShiftTest.class,
     org.apache.sis.test.integration.MetadataTest.class,

Modified: sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/PrimitiveTypeProperties.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/PrimitiveTypeProperties.java?rev=1813747&r1=1813746&r2=1813747&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/PrimitiveTypeProperties.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/PrimitiveTypeProperties.java [UTF-8] Mon Oct 30 10:25:08 2017
@@ -89,7 +89,7 @@ public final class PrimitiveTypeProperti
         assert isValidKey(primitive) : primitive;
         synchronized (SENTINEL_VALUES) {
             final Object old = SENTINEL_VALUES.put(primitive, property);
-            if (old != null) { // Should never happen - this is rather debugging check.
+            if (old != null) {                          // Should never happen - this is rather debugging check.
                 SENTINEL_VALUES.put(primitive, old);
                 throw new AssertionError(primitive);
             }
@@ -103,8 +103,10 @@ public final class PrimitiveTypeProperti
      * @return the property associated to the given instance, or {@code null} if none.
      */
     public static Object property(final Object primitive) {
-        // No 'assert isValidKey(primitive)' because this method is sometime invoked
-        // only after a brief inspection (e.g. 'NilReason.mayBeNil(Object)' method).
+        /*
+         * No 'assert isValidKey(primitive)' because this method is sometime invoked
+         * only after a brief inspection (e.g. 'NilReason.mayBeNil(Object)' method).
+         */
         synchronized (SENTINEL_VALUES) {
             return SENTINEL_VALUES.get(primitive);
         }

Modified: sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/NameAdapter.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/NameAdapter.java?rev=1813747&r1=1813746&r2=1813747&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/NameAdapter.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/NameAdapter.java [UTF-8] Mon Oct 30 10:25:08 2017
@@ -23,7 +23,6 @@ import org.opengis.util.LocalName;
 import org.opengis.util.ScopedName;
 import org.opengis.util.MemberName;
 import org.opengis.util.GenericName;
-import org.apache.sis.internal.jaxb.gml.CodeType;
 import org.apache.sis.util.iso.DefaultLocalName;
 import org.apache.sis.util.iso.DefaultTypeName;
 import org.apache.sis.util.iso.DefaultMemberName;
@@ -45,7 +44,7 @@ import org.apache.sis.util.resources.Err
  * @author  Cédric Briançon (Geomatys)
  * @author  Martin Desruisseaux (Geomatys)
  * @author  Guilhem Legal (Geomatys)
- * @version 0.5
+ * @version 0.8
  * @since   0.3
  * @module
  */
@@ -76,22 +75,29 @@ abstract class NameAdapter<ValueType ext
 
     /**
      * Returns the {@code LocalName} or {@code ScopedName} to marshall. Returns {@code null} if the name
-     * is a {@link TypeName} or a {@link MemberName}, in order to use {@link #getNameType()} instead.
+     * is a {@link TypeName} or a {@link MemberName}, in order to use {@link #getName()} instead.
+     * Example:
+     *
+     * {@preformat xml
+     *   <gml:alias>
+     *     <gco:LocalName codeSpace=\"A code space\">A name in a scope</gco:LocalName>
+     *   </gml:alias>
+     * }
      *
      * @return the code for the current name, or {@code null} if none.
      */
     @XmlElementRef
-    public final CodeType getCodeType() {
+    public final NameValue getValue() {
         final GenericName name = this.name;
-        final CodeType code;
+        final NameValue code;
         if (name instanceof LocalName) {
             if (name instanceof TypeName || name instanceof MemberName) {
                 return null;
             } else {
-                code = new CodeType.LocalName();
+                code = new NameValue.Local();
             }
         } else if (name instanceof ScopedName) {
-            code = new CodeType.ScopedName();
+            code = new NameValue.Scoped();
         } else {
             return null;
         }
@@ -100,27 +106,24 @@ abstract class NameAdapter<ValueType ext
     }
 
     /**
-     * Sets the value for the {@code LocalName} or {@code ScopedName}.
-     * This method is called at unmarshalling-time by JAXB.
-     *
-     * @param  code  the new name.
-     * @throws IllegalStateException if a name is already defined.
-     */
-    public final void setCodeType(final CodeType code) throws IllegalStateException {
-        ensureUndefined();
-        if (code != null) {
-            name = code.getName();
-        }
-    }
-
-    /**
      * Returns the {@code TypeName} or {@code MemberName} to marshall. Returns {@code null} if the name
-     * is a {@link LocalName} or {@link ScopedName}, in order to use {@link #getCodeType()} instead.
+     * is a {@link LocalName} or {@link ScopedName}, in order to use {@link #getValue()} instead.
+     * Example:
+     *
+     * {@preformat xml
+     *   <gml:alias>
+     *     <gco:TypeName>
+     *       <gco:aName>
+     *         <gco:CharacterString>An other local name</gco:CharacterString>
+     *       </gco:aName>
+     *     </gco:TypeName>
+     *   </gml:alias>
+     * }
      *
      * @return the current name, or {@code null} if none.
      */
     @XmlElementRef
-    public final DefaultLocalName getNameType() {
+    public final DefaultLocalName getName() {
         final GenericName name = this.name;
         if (name instanceof TypeName) {
             return DefaultTypeName.castOrCopy((TypeName) name);
@@ -132,13 +135,27 @@ abstract class NameAdapter<ValueType ext
     }
 
     /**
+     * Sets the value for the {@code LocalName} or {@code ScopedName}.
+     * This method is called at unmarshalling-time by JAXB.
+     *
+     * @param  code  the new name.
+     * @throws IllegalStateException if a name is already defined.
+     */
+    public final void setValue(final NameValue code) throws IllegalStateException {
+        ensureUndefined();
+        if (code != null) {
+            name = code.getName();
+        }
+    }
+
+    /**
      * Sets the value from the {@code TypeName} or {@code MemberName}.
      * This method is called at unmarshalling-time by JAXB.
      *
      * @param  value  the new name.
      * @throws IllegalStateException if a name is already defined.
      */
-    public final void setNameType(final DefaultLocalName value) throws IllegalStateException {
+    public final void setName(final DefaultLocalName value) throws IllegalStateException {
         ensureUndefined();
         name = value;
     }

Modified: sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/system/DataDirectory.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/system/DataDirectory.java?rev=1813747&r1=1813746&r2=1813747&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/system/DataDirectory.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/system/DataDirectory.java [UTF-8] Mon Oct 30 10:25:08 2017
@@ -17,6 +17,10 @@
 package org.apache.sis.internal.system;
 
 import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.InvalidPathException;
 import java.util.logging.Level;
 import java.util.logging.LogRecord;
 import java.security.AccessController;
@@ -24,18 +28,12 @@ import java.security.PrivilegedAction;
 import org.apache.sis.util.logging.Logging;
 import org.apache.sis.util.resources.Messages;
 
-// Branch-dependent imports
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.nio.file.InvalidPathException;
-
 
 /**
  * Sub-directories of {@code SIS_DATA} where SIS looks for EPSG database, datum shift grids and other resources.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 0.7
+ * @version 0.8
  * @since   0.7
  * @module
  */
@@ -108,6 +106,40 @@ public enum DataDirectory {
     }
 
     /**
+     * Returns the value of {@value #ENV} environment variable, or {@code null} if none.
+     * This method does not perform any logging and does not verify if the directory exists.
+     * If the intend is to perform I/O operations, use {@link #getRootDirectory()} instead.
+     *
+     * @return the {@value #ENV} environment variable, or {@code null} if none.
+     * @throws SecurityException if this method is not allowed to query the environment variable.
+     *
+     * @see System#getenv(String)
+     *
+     * @since 0.8
+     */
+    public static String getenv() throws SecurityException {
+        return AccessController.doPrivileged((PrivilegedAction<String>) () -> System.getenv(ENV));
+    }
+
+    /**
+     * Returns {@code true} if the {@value #ENV} environment variable is unset. In case of doubt, this method
+     * returns {@code false}. This method is used for avoiding or at leat delaying the log messages emitted by
+     * {@link #getRootDirectory()} when a fallback exists in absence of any user attempt to configure the system.
+     *
+     * @return {@code true} if the {@value #ENV} environment variable is unset.
+     *
+     * @since 0.8
+     */
+    public static synchronized boolean isEnvClear() {
+        if (rootDirectory == null) try {
+            return getenv() == null;
+        } catch (SecurityException e) {
+            Logging.recoverableException(Logging.getLogger(Loggers.SYSTEM), DataDirectory.class, "isEnvClear", e);
+        }
+        return false;
+    }
+
+    /**
      * Returns the root directory fetched from the {@code SIS_DATA} environment variable.
      * If the environment variable is not set or the directory does not exist, then this method returns {@code null}.
      *
@@ -115,7 +147,7 @@ public enum DataDirectory {
      */
     public static synchronized Path getRootDirectory() {
         if (rootDirectory == null) try {
-            final String dir = AccessController.doPrivileged((PrivilegedAction<String>) () -> System.getenv(ENV));
+            final String dir = getenv();
             if (dir == null || dir.isEmpty()) {
                 warning("getRootDirectory", null, Messages.Keys.DataDirectoryNotSpecified_1, ENV);
             } else try {

Modified: sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/system/DefaultFactories.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/system/DefaultFactories.java?rev=1813747&r1=1813746&r2=1813747&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/system/DefaultFactories.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/system/DefaultFactories.java [UTF-8] Mon Oct 30 10:25:08 2017
@@ -67,14 +67,15 @@ public final class DefaultFactories exte
 
     /**
      * Returns {@code true} if the default factory of the given type is the given instance.
+     * A {@code null} factory is interpreted as the default one.
      *
      * @param  <T>      the interface type.
      * @param  type     the interface type.
-     * @param  factory  the factory implementation to test.
+     * @param  factory  the factory implementation to test, or {@code null}.
      * @return {@code true} if the given factory implementation is the default instance.
      */
     public static synchronized <T> boolean isDefaultInstance(final Class<T> type, final T factory) {
-        return FACTORIES.get(type) == factory;
+        return (factory == null) || FACTORIES.get(type) == factory;
     }
 
     /**

Modified: sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/system/OS.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/system/OS.java?rev=1813747&r1=1813746&r2=1813747&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/system/OS.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/system/OS.java [UTF-8] Mon Oct 30 10:25:08 2017
@@ -17,6 +17,7 @@
 package org.apache.sis.internal.system;
 
 import java.net.URL;
+import java.net.URI;
 import java.net.URISyntaxException;
 import java.io.File;
 import java.io.InputStream;
@@ -122,7 +123,7 @@ public enum OS {
      */
     public static void load(final Class<?> caller, final String name) {
         try {
-            System.load(current().nativeLibrary(caller.getClassLoader(), name));
+            System.load(current().nativeLibrary(caller, name));
         } catch (IOException | SecurityException e) {
             throw (UnsatisfiedLinkError) new UnsatisfiedLinkError(e.getMessage()).initCause(e);
         }
@@ -133,7 +134,7 @@ public enum OS {
      * If the resources can not be accessed by an absolute path, then this method
      * copies the resource in a temporary file.
      *
-     * @param  loader  the loader of the JAR file where to look for native resources.
+     * @param  caller  a class in the JAR file where to look for native resources.
      * @param  name    the native library name without {@code ".so"} or {@code ".dll"} extension.
      * @return absolute path to the library (may be a temporary file).
      * @throws IOException if an error occurred while copying the library to a temporary file.
@@ -142,11 +143,40 @@ public enum OS {
      *
      * @see System#load(String)
      */
-    private String nativeLibrary(final ClassLoader loader, final String name) throws IOException {
+    private String nativeLibrary(final Class<?> caller, final String name) throws IOException {
         if (libdir != null) {
             final String ext = unix ? ".so" : ".dll";
-            final String path = "native/" + libdir + '/' + name + ext;
-            final URL res = loader.getResource(path);
+            final String path = libdir + '/' + name + ext;
+            final ClassLoader loader = caller.getClassLoader();
+            /*
+             * First, verify if the "linux", "darwin" or "windows" directory exists at the same level
+             * than the JAR file containing the caller class. If it exists, then we will use it. This
+             * check avoid the need to copy the ".so" or ".dll" file in a temporary location.  If the
+             * directory does not exist, then we do NOT create it in order to reduce the risk to mess
+             * with user's installation.
+             *
+             * Example of URL for a JAR entry: jar:file:/home/…/sis-gdal.jar!/org/apache/…/PJ.class
+             */
+            URL res = loader.getResource(caller.getName().replace('.', '/').concat(".class"));
+            if (res != null && "jar".equals(res.getProtocol())) {
+                String file = res.getPath();
+                final int s = file.indexOf('!');
+                if (s >= 0) try {
+                    File location = new File(new URI(file.substring(0, s)));
+                    location = new File(location.getParentFile(), path);
+                    if (location.canExecute()) {
+                        return location.getAbsolutePath();
+                    }
+                } catch (IllegalArgumentException | URISyntaxException e) {
+                    Logging.recoverableException(Logging.getLogger(Loggers.SYSTEM), OS.class, "nativeLibrary", e);
+                }
+            }
+            /*
+             * If we didn't found an existing "linux", "darwin" or "windows" directory with native library,
+             * copy the library in a temporary file. That file will be deleted on JVM exists, so a new file
+             * will be copied each time the application is executed.
+             */
+            res = loader.getResource("native/".concat(path));
             if (res != null) {
                 try {
                     return new File(res.toURI()).getAbsolutePath();

Modified: sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/system/Supervisor.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/system/Supervisor.java?rev=1813747&r1=1813746&r2=1813747&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/system/Supervisor.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/system/Supervisor.java [UTF-8] Mon Oct 30 10:25:08 2017
@@ -37,6 +37,7 @@ import javax.management.InstanceAlreadyE
 import java.lang.management.ManagementFactory;
 
 import org.apache.sis.setup.About;
+import org.apache.sis.util.Configuration;
 import org.apache.sis.util.logging.Logging;
 import org.apache.sis.util.resources.Errors;
 import org.apache.sis.util.resources.Messages;
@@ -83,6 +84,7 @@ public final class Supervisor extends St
      * and the MBean will not be registered. This method does not propagate the exception
      * because the MBean is not a mandatory part of SIS library.</p>
      */
+    @Configuration
     public static synchronized void register() {
         if (name == null) {
             name = ObjectName.WILDCARD;                         // In case of failure.
@@ -113,6 +115,7 @@ public final class Supervisor extends St
      *
      * @throws JMException if an error occurred during unregistration.
      */
+    @Configuration
     static synchronized void unregister() throws JMException {
         final ObjectName n = name;
         if (n != null) {

Modified: sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/util/Citations.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/util/Citations.java?rev=1813747&r1=1813746&r2=1813747&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/util/Citations.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/util/Citations.java [UTF-8] Mon Oct 30 10:25:08 2017
@@ -345,7 +345,8 @@ public final class Citations extends Sta
      * <ul>
      *   <li>For information purpose (e.g. some {@code toString()} methods), use {@code getIdentifier(…, false)}.</li>
      *   <li>For WKT formatting, use {@code getIdentifier(…, true)} in order to preserve formatting characters.</li>
-     *   <li>For assigning a value to a {@code codeSpace} field, use {@link #getUnicodeIdentifier(Citation)}.</li>
+     *   <li>For assigning a value to a {@code codeSpace} field, use
+     *       {@link org.apache.sis.metadata.iso.citation.Citations#getUnicodeIdentifier(Citation)}.</li>
      * </ul>
      *
      * @param  citation  the citation for which to get the identifier, or {@code null}.
@@ -467,6 +468,9 @@ public final class Citations extends Sta
      *         or {@code null} if the given citation is null or does not have any Unicode identifier or title.
      *
      * @since 0.6
+     *
+     * @deprecated Implementation will be moved to {@link org.apache.sis.metadata.iso.citation.Citations}
+     *             after we moved the {@code sis-utility} code that use this method.
      */
     public static String getUnicodeIdentifier(final Citation citation) {
         final String identifier = getIdentifier(citation, true);
@@ -529,6 +533,9 @@ public final class Citations extends Sta
      *         or {@code null} if the given citation is null or does not have any Unicode identifier or title.
      *
      * @since 0.6
+     *
+     * @deprecated Implementation will be moved to {@link org.apache.sis.metadata.iso.citation.Citations}
+     *             after we moved the {@code sis-utility} code that use this method.
      */
     public static String getCodeSpace(final Citation citation) {
         if (citation instanceof IdentifierSpace<?>) {

Modified: sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/util/CollectionsExt.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/util/CollectionsExt.java?rev=1813747&r1=1813746&r2=1813747&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/util/CollectionsExt.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/util/CollectionsExt.java [UTF-8] Mon Oct 30 10:25:08 2017
@@ -321,6 +321,8 @@ public final class CollectionsExt extend
      * @return a set containing the array elements, or {@code null} if the given array was null.
      *
      * @see Collections#unmodifiableSet(Set)
+     *
+     * @todo Consider replacing by {@code Set.of(...)} in JDK9.
      */
     @SafeVarargs
     @SuppressWarnings("fallthrough")

Modified: sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/util/Constants.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/util/Constants.java?rev=1813747&r1=1813746&r2=1813747&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/util/Constants.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/util/Constants.java [UTF-8] Mon Oct 30 10:25:08 2017
@@ -107,7 +107,7 @@ public final class Constants extends Sta
     public static final byte CRS1 = 1;
 
     /**
-     * The NetCDF parameter name for the Earth radius.
+     * The netCDF parameter name for the Earth radius.
      */
     public static final String EARTH_RADIUS = "earth_radius";
 
@@ -118,7 +118,7 @@ public final class Constants extends Sta
                                SEMI_MINOR = "semi_minor";
 
     /**
-     * The NetCDF parameter name for inverse flattening, and whether that parameter is definitive.
+     * The netCDF parameter name for inverse flattening, and whether that parameter is definitive.
      * The later is specific to SIS.
      */
     public static final String INVERSE_FLATTENING = "inverse_flattening",
@@ -135,7 +135,7 @@ public final class Constants extends Sta
     public static final String LATITUDE_OF_ORIGIN = "latitude_of_origin";
 
     /**
-     * The NetCDF parameter name for the standard parallels.
+     * The netCDF parameter name for the standard parallels.
      */
     public static final String STANDARD_PARALLEL = "standard_parallel";
 

Modified: sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/util/DefinitionURI.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/util/DefinitionURI.java?rev=1813747&r1=1813746&r2=1813747&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/util/DefinitionURI.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/util/DefinitionURI.java [UTF-8] Mon Oct 30 10:25:08 2017
@@ -17,12 +17,14 @@
 package org.apache.sis.internal.util;
 
 import java.util.Map;
+import java.util.TreeMap;
 import java.util.Collections;
 import org.apache.sis.util.CharSequences;
+import org.apache.sis.util.logging.Logging;
+import org.apache.sis.internal.system.Loggers;
 
 import static org.apache.sis.util.CharSequences.*;
 import static org.apache.sis.util.ArgumentChecks.ensureNonNull;
-import static org.apache.sis.internal.util.Utilities.appendUnicodeIdentifier;
 
 
 /**
@@ -46,7 +48,7 @@ import static org.apache.sis.internal.ut
  *   <li>{@code http://www.opengis.net/def/uom/SI/0/m%2Fs}</li>
  * </ul>
  *
- * <div class="section">Components or URN</div>
+ * <div class="section">Parts of URN</div>
  * URN begins with {@code "urn:ogc:def:"} (formerly {@code "urn:x-ogc:def:"}) followed by:
  * <ul>
  *   <li>an object {@linkplain #type}</li>
@@ -103,7 +105,7 @@ import static org.apache.sis.internal.ut
  * {@code "urn:ogc:def:crs,crs:EPSG:6.3:27700,crs:EPSG:6.3:5701"}.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 0.7
+ * @version 0.8
  *
  * @see org.apache.sis.internal.metadata.NameMeaning
  * @see <a href="http://portal.opengeospatial.org/files/?artifact_id=24045">Definition identifier URNs in OGC namespace</a>
@@ -124,6 +126,43 @@ public final class DefinitionURI {
     public static final char SEPARATOR = ':';
 
     /**
+     * The separator between {@linkplain #components} in a URN.
+     *
+     * <div class="note"><b>Example:</b>
+     * in {@code "urn:ogc:def:crs,crs:EPSG:9.1:27700,crs:EPSG:9.1:5701"}, the components are
+     * {@code "crs:EPSG:9.1:27700"} and {@code "crs:EPSG:9.1:5701"}.</div>
+     */
+    public static final char COMPONENT_SEPARATOR = ',';
+
+    /**
+     * The separator between a URL and its first {@linkplain #components}.
+     * In URL syntax, this is the separator between URL path and the query.
+     *
+     * <div class="note"><b>Example:</b><code>
+     * http://www.opengis.net/def/crs-compound<u>?</u>1=…&amp;2=…
+     * </code></div>
+     */
+    private static final char COMPONENT_SEPARATOR_1 = '?';
+
+    /**
+     * The separator between {@linkplain #components} in a URL after the first component.
+     *
+     * <div class="note"><b>Example:</b><code>
+     * http://www.opengis.net/def/crs-compound?1=…<u>&amp;</u>2=…
+     * </code></div>
+     */
+    private static final char COMPONENT_SEPARATOR_2 = '&';
+
+    /**
+     * Separator between keys and values in the query part of a URL.
+     *
+     * <div class="note"><b>Example:</b><code>
+     * http://www.opengis.net/def/crs-compound?1<u>=</u>…&amp;2<u>=</u>…
+     * </code></div>
+     */
+    private static final char KEY_VALUE_SEPARATOR = '=';
+
+    /**
      * The domain of URLs in the OGC namespace.
      */
     public static final String DOMAIN = "www.opengis.net";
@@ -204,6 +243,30 @@ public final class DefinitionURI {
     public String[] parameters;
 
     /**
+     * If the URI contains sub-components, those sub-components. Otherwise {@code null}.
+     *
+     * <div class="note"><b>URN example:</b>
+     * if the URI is {@code "urn:ogc:def:crs,crs:EPSG:9.1:27700,crs:EPSG:9.1:5701"}, then this
+     * {@code DefinitionURI} will contain the {@code "urn:ogc:def:crs"} header with two components:
+     * <ol>
+     *   <li>{@code "urn:ogc:def:crs:EPSG:9.1:27700"}</li>
+     *   <li>{@code "urn:ogc:def:crs:EPSG:9.1:5701"}</li>
+     * </ol></div>
+     *
+     * <div class="note"><b>HTTP example:</b> if the URI is
+     * {@code "http://www.opengis.net/def/crs-compound?1=(…)/crs/EPSG/9.1/27700&2=(…)/crs/EPSG/9.1/5701"},
+     * then this {@code DefinitionURI} will contain the {@code "http://www.opengis.net/def/crs-compound"}
+     * header with two components:
+     * <ol>
+     *   <li>{@code "http://http://www.opengis.net/def/crs/EPSG/9.1/27700"}</li>
+     *   <li>{@code "http://http://www.opengis.net/def/crs/EPSG/9.1/5701"}</li>
+     * </ol></div>
+     *
+     * Note that this array may contain {@code null} elements if we failed to parse the corresponding component.
+     */
+    public DefinitionURI[] components;
+
+    /**
      * For {@link #parse(String)} usage only.
      */
     private DefinitionURI() {
@@ -219,9 +282,26 @@ public final class DefinitionURI {
      */
     public static DefinitionURI parse(final String uri) {
         ensureNonNull("uri", uri);
-        DefinitionURI result = null;
-        char separator = SEPARATOR;
-        int upper = -1;
+        return parse(uri, false, -1, uri.length());
+    }
+
+    /**
+     * Parses a sub-region of the given URI. This method can start parsing for an arbitrary URI part,
+     * no necessarily the root. The first URI part is identified by an ordinal number:
+     *
+     * This method may invoke itself recursively if the URI contains sub-components.
+     *
+     * @param  uri               the URI to parse.
+     * @param  prefixIsOptional  {@code true} if {@value #PREFIX} may not be present.
+     * @param  upper             upper index of the previous URI part, or -1 if none.
+     * @param  stopAt            index (exclusive) where to stop parsing.
+     * @return the parse result, or {@code null} if the URI is not recognized.
+     */
+    @SuppressWarnings("fallthrough")
+    private static DefinitionURI parse(final String uri, boolean prefixIsOptional, int upper, int stopAt) {
+        DefinitionURI result    = null;
+        char separator          = SEPARATOR;                    // Separator character of URI parts.
+        char componentSeparator = COMPONENT_SEPARATOR;          // Separator character of components.
         /*
          * Loop on all parts that we expect in the URI. Those parts are:
          *
@@ -234,19 +314,19 @@ public final class DefinitionURI {
          *   6:  code
          *   7:  parameters, or null if none.
          */
-        for (int p=0; p<=6; p++) {
+        for (int part = 0; part <= 6; part++) {
             final int lower = upper + 1;
             upper = uri.indexOf(separator, lower);
-            if (upper < 0) {
-                upper = uri.length();
+            if (upper < 0 || upper >= stopAt) {
+                upper = stopAt;
                 if (lower > upper) {
-                    return result;        // Happen if a component is missing.
+                    return result;                      // Happen if a part is missing.
                 }
             }
-            switch (p) {
+            switch (part) {
                 /*
-                 * Verifies that the 3 first components are "urn:ogc:def:" or "http://www.opengis.net/def/"
-                 * without storing them. In the particular case of second component, we also accept "x-ogc"
+                 * Verifies that the 3 first parts are "urn:ogc:def:" or "http://www.opengis.net/def/"
+                 * without storing them. In the particular case of second part, we also accept "x-ogc"
                  * in addition to "ogc" in URN.
                  */
                 case 0: {
@@ -257,74 +337,158 @@ public final class DefinitionURI {
                             return result;
                         }
                         if (!uri.regionMatches(upper, "//", 0, 2)) {
-                            return null;
+                            return null;                                // Prefix is never optional for HTTP.
                         }
                         upper++;
-                        separator = '/';    // Separator for the HTTP namespace.
-                    } else if (!regionMatches("urn", uri, lower, upper)) {
+                        separator = '/';                                // Separator for the HTTP namespace.
+                        componentSeparator = COMPONENT_SEPARATOR_1;     // Separator for the query part in URL.
+                        prefixIsOptional = false;
+                        break;
+                    } else if (regionMatches("urn", uri, lower, upper)) {
+                        prefixIsOptional = false;
+                        break;
+                    } else if (!prefixIsOptional) {
                         return null;
                     }
-                    break;
+                    part++;
+                    // Part is not "urn" but its presence was optional. Maybe it is "ogc". Fall through for checking.
                 }
                 case 1: {
                     final boolean isHTTP = (separator != SEPARATOR);
-                    if (!regionMatches(isHTTP ? DOMAIN : "ogc", uri, lower, upper)) {
-                        if (isHTTP  ||  !regionMatches("x-ogc", uri, lower, upper)) {
-                            return null;
-                        }
+                    if (regionMatches(isHTTP ? DOMAIN : "ogc", uri, lower, upper) ||
+                            (!isHTTP && regionMatches("x-ogc", uri, lower, upper)))
+                    {
+                        prefixIsOptional = false;
+                        break;
+                    } else if (!prefixIsOptional) {
+                        return null;
                     }
-                    break;
+                    part++;
+                    // Part is not "ogc" but its presence was optional. Maybe it is "def". Fall through for checking.
                 }
                 case 2: {
-                    if (!regionMatches("def", uri, lower, upper)) {
+                    if (regionMatches("def", uri, lower, upper)) {
+                        prefixIsOptional = false;
+                        break;
+                    } else if (!prefixIsOptional) {
                         return null;
                     }
-                    break;
+                    part++;
+                    // Part is not "def" but its presence was optional. Maybe it is "crs". Fall through for checking.
+                }
+                /*
+                 * The forth part is the first one that we want to remember; all cases before this one were
+                 * only verification. This case is also the first part where component separator may appear,
+                 * for example as in "urn:ogc:def:crs,crs:EPSG:9.1:27700,crs:EPSG:9.1:5701". We verify here
+                 * if such components exist, and if so we parse them recursively.
+                 */
+                case 3: {
+                    int splitAt = uri.indexOf(componentSeparator, lower);
+                    if (splitAt >= 0 && splitAt < stopAt) {
+                        final int componentsEnd = stopAt;
+                        stopAt = splitAt;                   // Upper limit of the DefinitionURI created in this method call.
+                        if (stopAt < upper) {
+                            upper = stopAt;
+                        }
+                        if (componentSeparator == COMPONENT_SEPARATOR_1) {
+                            componentSeparator =  COMPONENT_SEPARATOR_2;    // E.g. http://(…)/crs-compound?1=(…)&2=(…)
+                        }
+                        if (result == null) {
+                            result = new DefinitionURI();
+                        }
+                        final boolean isURN = !result.isHTTP;
+                        final Map<Integer,DefinitionURI> orderedComponents = new TreeMap<>();
+                        boolean hasMore;
+                        do {
+                            /*
+                             * Find indices of URI sub-component to parse. The sub-component will
+                             * go from 'splitAt' to 'next' exclusive ('splitAt' is exclusive too).
+                             */
+                            int next = uri.indexOf(componentSeparator, splitAt+1);
+                            hasMore = next >= 0 && next < componentsEnd;
+                            if (!hasMore) next = componentsEnd;
+                            /*
+                             * HTTP uses key-value pairs as in "http://something?1=...&2=...
+                             * URN uses a comma-separated value list without number.
+                             * We support both forms, regardless if HTTP or URN.
+                             */
+                            int sequenceNumber = orderedComponents.size() + 1;      // Default value if no explicit key.
+                            final int s = splitKeyValue(uri, splitAt+1, next);
+                            if (s >= 0) try {
+                                sequenceNumber = Integer.parseInt(trimWhitespaces(uri, splitAt+1, s).toString());
+                                splitAt = s;                      // Set only on success.
+                            } catch (NumberFormatException e) {
+                                // Ignore. The URN is likely to be invalid, but we let parse(…) determines that.
+                                Logging.recoverableException(Logging.getLogger(Loggers.CRS_FACTORY), DefinitionURI.class, "parse", e);
+                            }
+                            orderedComponents.put(sequenceNumber, parse(uri, isURN, splitAt, next));
+                            splitAt = next;
+                        } while (hasMore);
+                        result.components = orderedComponents.values().toArray(new DefinitionURI[orderedComponents.size()]);
+                    }
+                    // Fall through
                 }
                 /*
-                 * For all components after the first 3 ones, trim whitespaces and store non-empty values.
+                 * For all parts after the first 3 ones, trim whitespaces and store non-empty values.
                  */
                 default: {
                     final String value = trimWhitespaces(uri, lower, upper).toString();
-                    if (!value.isEmpty() && (p != 5 || !NO_VERSION.equals(value))) {
+                    if (!value.isEmpty() && (part != 5 || !NO_VERSION.equals(value))) {
                         if (result == null) {
                             result = new DefinitionURI();
                         }
-                        switch (p) {
+                        switch (part) {
                             case 3:  result.type      = value; break;
                             case 4:  result.authority = value; break;
                             case 5:  result.version   = value; break;
                             case 6:  result.code      = value; break;
-                            default: throw new AssertionError(p);
+                            default: throw new AssertionError(part);
                         }
                     }
                 }
             }
         }
         /*
-         * Take every remaining components as parameters.
+         * Take every remaining parts as parameters.
          */
-        if (result != null && ++upper < uri.length()) {
+        if (result != null && ++upper < stopAt) {
             result.parameters = (String[]) split(uri.substring(upper), separator);
         }
         return result;
     }
 
     /**
-     * Returns {@code true} if a sub-region of {@code urn} matches the given {@code component},
+     * Returns {@code true} if a sub-region of {@code urn} matches the given {@code part},
      * ignoring case, leading and trailing whitespaces.
      *
-     * @param  component  the expected component ({@code "urn"}, {@code "ogc"}, {@code "def"}, <i>etc.</i>)
+     * @param  part       the expected part ({@code "urn"}, {@code "ogc"}, {@code "def"}, <i>etc.</i>)
      * @param  urn        the URN for which to test a subregion.
      * @param  lower      index of the first character in {@code urn} to compare, after skipping whitespaces.
      * @param  upper      index after the last character in {@code urn} to compare, ignoring whitespaces.
-     * @return {@code true} if the given sub-region of {@code urn} match the given component.
+     * @return {@code true} if the given sub-region of {@code urn} match the given part.
      */
-    static boolean regionMatches(final String component, final String urn, int lower, int upper) {
+    static boolean regionMatches(final String part, final String urn, int lower, int upper) {
         lower = skipLeadingWhitespaces (urn, lower, upper);
         upper = skipTrailingWhitespaces(urn, lower, upper);
         final int length = upper - lower;
-        return (length == component.length()) && urn.regionMatches(true, lower, component, 0, length);
+        return (length == part.length()) && urn.regionMatches(true, lower, part, 0, length);
+    }
+
+    /**
+     * Returns the index of the {@code '='} character in the given sub-string, provided that all characters
+     * before it are spaces or decimal digits. Returns -1 if the key-value separator character is not found.
+     * Note that a positive return value does not guarantee that the number is parsable.
+     */
+    private static int splitKeyValue(final String uri, int lower, final int upper) {
+        while (lower < upper) {
+            int c = uri.codePointAt(lower);
+            if ((c < '0' || c > '9') && !Character.isWhitespace(c)) {
+                if (c == KEY_VALUE_SEPARATOR) return lower;
+                break;
+            }
+            lower += Character.charCount(c);
+        }
+        return -1;
     }
 
     /**
@@ -406,38 +570,38 @@ public final class DefinitionURI {
          * Check for supported protocols: only "urn" and "http" at this time.
          * All other protocols are rejected as unrecognized.
          */
-        String component;
+        String part;
         switch (length) {
-            case 3:  component = "urn";  break;
-            case 4:  component = "http"; break;
+            case 3:  part = "urn";  break;
+            case 4:  part = "http"; break;
             default: return null;
         }
-        if (!uri.regionMatches(true, lower, component, 0, length)) {
+        if (!uri.regionMatches(true, lower, part, 0, length)) {
             return null;
         }
         if (length == 4) {
             return codeForGML(type, authority, uri, upper+1, null);
         }
         /*
-         * At this point we have determined that the protocol is URN. The next components after "urn"
+         * At this point we have determined that the protocol is URN. The next parts after "urn"
          * shall be "ogc" or "x-ogc", then "def", then the type and authority given in arguments.
          */
         for (int p=0; p!=4; p++) {
             lower = upper + 1;
             upper = uri.indexOf(SEPARATOR, lower);
             if (upper < 0) {
-                return null;                                                    // No more components.
+                return null;                                                    // No more parts.
             }
             switch (p) {
                 // "ogc" is tested before "x-ogc" because more common.
                 case 0: if (regionMatches("ogc", uri, lower, upper)) continue;
-                        component = "x-ogc";   break;       // Fallback if the component is not "ogc".
-                case 1: component = "def";     break;
-                case 2: component = type;      break;
-                case 3: component = authority; break;
+                        part = "x-ogc";   break;       // Fallback if the part is not "ogc".
+                case 1: part = "def";     break;
+                case 2: part = type;      break;
+                case 3: part = authority; break;
                 default: throw new AssertionError(p);
             }
-            if (!regionMatches(component, uri, lower, upper)) {
+            if (!regionMatches(part, uri, lower, upper)) {
                 return null;
             }
         }
@@ -519,43 +683,6 @@ public final class DefinitionURI {
     }
 
     /**
-     * Formats the given identifier using the {@code "ogc:urn:def:"} syntax. The identifier code space,
-     * version and code are appended omitting any characters that are not valid for a Unicode identifier.
-     * If some information are missing in the given identifier, then this method returns {@code null}.
-     *
-     * @param  type       the object type as one of the types documented in class javadoc, or {@code null}.
-     * @param  authority  the authority as one of the values documented in class javadoc, or {@code null}.
-     * @param  version    the code version, or {@code null}. This is the only optional information.
-     * @param  code       the code, or {@code null}.
-     * @return an identifier using the URN syntax, or {@code null} if a mandatory information is missing.
-     *
-     * @see org.apache.sis.internal.metadata.NameMeaning#toURN(Class, String, String, String)
-     */
-    public static String format(final String type, final String authority, final String version, final String code) {
-        final StringBuilder buffer = new StringBuilder(PREFIX);
-loop:   for (int p=0; ; p++) {
-            final String component;
-            switch (p) {
-                case 0:  component = type;      break;
-                case 1:  component = authority; break;
-                case 2:  component = version;   break;
-                case 3:  component = code;      break;
-                default: break loop;
-            }
-            if (!appendUnicodeIdentifier(buffer.append(SEPARATOR), '\u0000', component, ".-", false)) {
-                /*
-                 * Only the version (p = 2) is optional. All other fields are mandatory.
-                 * If no character has been added for a mandatory field, we can not build a URN.
-                 */
-                if (p != 2) {
-                    return null;
-                }
-            }
-        }
-        return buffer.toString();
-    }
-
-    /**
      * Returns a string representation of this URI. If the URI were originally a GML's URL, then this method formats
      * the URI in the {@code "http://www.opengis.net/gml/srs/"} namespace. Otherwise the URI were originally a URL,
      * then this method formats the URI in the {@code "http://www.opengis.net/"} namespace.
@@ -571,10 +698,23 @@ loop:   for (int p=0; ; p++) {
                 return "http:" + path + authority + ".xml#" + code;
             }
         }
-        final StringBuilder buffer = new StringBuilder(PREFIX);
-        char separator = SEPARATOR;
+        final StringBuilder buffer = new StringBuilder(40);
+        if (!isHTTP) {
+            buffer.append(PREFIX);
+        }
+        toString(buffer, SEPARATOR);
+        return buffer.toString();
+    }
+
+    /**
+     * Formats the string representation of this URI into the given buffer. This method invoke itself recursively
+     * if this URI has {@linkplain #components}. The {@value #PREFIX} must be appended by the caller, if applicable.
+     *
+     * @param  buffer     where to format the string representation.
+     * @param  separator  first separator to append. Ignored if the URI is actually a URL.
+     */
+    private void toString(final StringBuilder buffer, char separator) {
         if (isHTTP) {
-            buffer.setLength(0);
             buffer.append("http://").append(DOMAIN).append("/def");
             separator = '/';
         }
@@ -583,21 +723,53 @@ loop:   for (int p=0; ; p++) {
             n += parameters.length;
         }
         for (int p=0; p<n; p++) {
-            String component;
+            String part;
             switch (p) {
-                case 0:  component = type;            break;
-                case 1:  component = authority;       break;
-                case 2:  component = version;         break;
-                case 3:  component = code;            break;
-                default: component = parameters[p-4]; break;
+                case 0:  part = type;            break;
+                case 1:  part = authority;       break;
+                case 2:  part = version;         break;
+                case 3:  part = code;            break;
+                default: part = parameters[p-4]; break;
             }
             buffer.append(separator);
-            if (component == null) {
-                if (!isHTTP) continue;
-                component = NO_VERSION;
+            if (isHTTP) {
+                if (part == null) {
+                    part = NO_VERSION;
+                }
+            } else {
+                separator = SEPARATOR;
+                if (part == null) {
+                    continue;
+                }
+            }
+            buffer.append(part);
+        }
+        /*
+         * Before to return the URI, trim trailing separators. For example if the URN has only a type
+         * (no authority, version, code, etc.), then we want to return only "urn:ogc:def:crs" instead
+         * of "urn:ogc:def:crs:::"). This happen with URN defining compound CRS for instance.
+         */
+        int length = buffer.length();
+        n = isHTTP ? 2 : 1;
+        while ((length -= n) >= 0 && buffer.charAt(length) == separator) {
+            if (isHTTP && buffer.charAt(length + 1) != NO_VERSION.charAt(0)) break;
+            buffer.setLength(length);
+        }
+        /*
+         * If there is components, format them recursively. Note that the format is different depending if
+         * we are formatting URN or HTTP.  Example: "urn:ogc:def:crs,crs:EPSG:9.1:27700,crs:EPSG:9.1:5701"
+         * and "http://www.opengis.net/def/crs-compound?1=(…)/crs/EPSG/9.1/27700&2=(…)/crs/EPSG/9.1/5701".
+         */
+        if (components != null) {
+            for (int i=0; i<components.length;) {
+                final DefinitionURI c = components[i++];
+                if (isHTTP) {
+                    buffer.append(i == 1 ? COMPONENT_SEPARATOR_1
+                                         : COMPONENT_SEPARATOR_2)
+                          .append(i).append(KEY_VALUE_SEPARATOR);
+                }
+                c.toString(buffer, COMPONENT_SEPARATOR);
             }
-            buffer.append(component);
         }
-        return buffer.toString();
     }
 }

Modified: sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/math/Fraction.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/math/Fraction.java?rev=1813747&r1=1813746&r2=1813747&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/math/Fraction.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/math/Fraction.java [UTF-8] Mon Oct 30 10:25:08 2017
@@ -220,7 +220,7 @@ public final class Fraction extends Numb
      * Returns this fraction rounded toward nearest integer. If the result is located
      * at equal distance from the two nearest integers, then rounds to the even one.
      *
-     * @return {@link #numerator} / {@link denominator} rounded toward nearest integer.
+     * @return {@link #numerator} / {@link #denominator} rounded toward nearest integer.
      */
     public int round() {
         if (denominator == Integer.MIN_VALUE) {
@@ -251,7 +251,7 @@ public final class Fraction extends Numb
      * <p><b>Tip:</b> if the numerator and the denominator are both positive or both negative,
      * then the result is positive and identical to {@code numerator / denominator}.</p>
      *
-     * @return {@link #numerator} / {@link denominator} rounded toward negative infinity.
+     * @return {@link #numerator} / {@link #denominator} rounded toward negative infinity.
      */
     public int floor() {
         int n = numerator / denominator;
@@ -265,7 +265,7 @@ public final class Fraction extends Numb
      * Returns this fraction rounded toward positive infinity.
      * This is different from the default operation on primitive types, which rounds toward zero.
      *
-     * @return {@link #numerator} / {@link denominator} rounded toward positive infinity.
+     * @return {@link #numerator} / {@link #denominator} rounded toward positive infinity.
      */
     public int ceil() {
         int n = numerator / denominator;
@@ -326,7 +326,7 @@ public final class Fraction extends Numb
     /**
      * Returns this fraction rounded toward zero.
      *
-     * @return {@link #numerator} / {@link denominator} rounded toward zero.
+     * @return {@link #numerator} / {@link #denominator} rounded toward zero.
      *
      * @see #round()
      * @see #floor()

Modified: sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/measure/UnitFormat.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/measure/UnitFormat.java?rev=1813747&r1=1813746&r2=1813747&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/measure/UnitFormat.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/measure/UnitFormat.java [UTF-8] Mon Oct 30 10:25:08 2017
@@ -58,11 +58,11 @@ import org.apache.sis.util.iso.DefaultNa
  * then {@code "####"} is parsed as an integer and forwarded to the {@link Units#valueOfEPSG(int)} method.
  *
  * <div class="section">NetCDF unit symbols</div>
- * The attributes in NetCDF files often merge the axis direction with the angular unit,
+ * The attributes in netCDF files often merge the axis direction with the angular unit,
  * as in {@code "degrees_east"}, {@code "degrees_north"} or {@code "Degrees North"}.
  * This class ignores those suffixes and unconditionally returns {@link Units#DEGREE} for all axis directions.
  * In particular, the units for {@code "degrees_west"} and {@code "degrees_east"} do <strong>not</strong> have
- * opposite sign. It is caller responsibility to handle the direction of axes associated to NetCDF units.
+ * opposite sign. It is caller responsibility to handle the direction of axes associated to netCDF units.
  *
  * <div class="section">Multi-threading</div>
  * {@code UnitFormat} is generally not thread-safe.

Modified: sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/measure/Units.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/measure/Units.java?rev=1813747&r1=1813746&r2=1813747&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/measure/Units.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/measure/Units.java [UTF-8] Mon Oct 30 10:25:08 2017
@@ -541,7 +541,7 @@ public final class Units extends Static
      * <div class="note"><p class="simpleTagLabel" style="margin-bottom:0">Related units:</p>
      * <table class="compact" summary="Related units" style="margin-left:30px; line-height:1.25">
      *   <tr><td>SI time units:</td>    <td style="word-spacing:1em">{@link #MILLISECOND}, <b>{@link #SECOND}</b>.</td></tr>
-     *   <tr><td>In other systems:</td> <td style="word-spacing:1em">{@link #MINUTE}, {@link #HOUR}, {@link #DAY}, <u>{@link WEEK}</u>, {@link #TROPICAL_YEAR}.</td></tr>
+     *   <tr><td>In other systems:</td> <td style="word-spacing:1em">{@link #MINUTE}, {@link #HOUR}, {@link #DAY}, <u>{@link #WEEK}</u>, {@link #TROPICAL_YEAR}.</td></tr>
      *   <tr><td>Derived units:</td>    <td style="word-spacing:1em">{@link #KILOMETRES_PER_HOUR}, {@link #HERTZ}.</td></tr>
      * </table></div>
      *
@@ -1619,12 +1619,12 @@ public final class Units extends Static
      * is parsed as an integer and forwarded to the {@link #valueOfEPSG(int)} method.
      *
      * <div class="section">NetCDF unit symbols</div>
-     * The attributes in NetCDF files often merge the axis direction with the angular unit,
+     * The attributes in netCDF files often merge the axis direction with the angular unit,
      * as in {@code "degrees_east"} or {@code "degrees_north"}. This {@code valueOf} method
      * ignores those suffixes and unconditionally returns {@link #DEGREE} for all axis directions.
      * In particular, the units for {@code "degrees_west"} and {@code "degrees_east"}
      * do <strong>not</strong> have opposite sign.
-     * It is caller responsibility to handle the direction of axes associated to NetCDF units.
+     * It is caller responsibility to handle the direction of axes associated to netCDF units.
      *
      * @param  uom  the symbol to parse, or {@code null}.
      * @return the parsed symbol, or {@code null} if {@code uom} was null.

Modified: sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/setup/About.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/setup/About.java?rev=1813747&r1=1813746&r2=1813747&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/setup/About.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/setup/About.java [UTF-8] Mon Oct 30 10:25:08 2017
@@ -39,8 +39,6 @@ import java.text.DateFormat;
 import java.text.FieldPosition;
 import java.nio.file.Path;
 import java.nio.charset.Charset;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
 import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.util.CharSequences;
 import org.apache.sis.util.Exceptions;
@@ -382,7 +380,7 @@ fill:   for (int i=0; ; i++) {
                     if (sections.contains(PATHS)) {
                         nameKey = Vocabulary.Keys.DataDirectory;
                         try {
-                            value = AccessController.doPrivileged((PrivilegedAction<String>) () -> System.getenv(DataDirectory.ENV));
+                            value = DataDirectory.getenv();
                         } catch (SecurityException e) {
                             value = e.toString();
                         }

Modified: sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/setup/InstallationResources.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/setup/InstallationResources.java?rev=1813747&r1=1813746&r2=1813747&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/setup/InstallationResources.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/setup/InstallationResources.java [UTF-8] Mon Oct 30 10:25:08 2017
@@ -27,7 +27,7 @@ import java.io.BufferedReader;
  * {@code InstallationResources} can be used for downloading large files that may not be of interest
  * to every users, or data that are subject to more restricting terms of use than the Apache license.
  *
- * <div class="note"><b>Examples:</b><ul>
+ * <div class="note"><b>Examples:</b><ul class="verbose">
  * <li>The NADCON grid files provide <cite>datum shifts</cite> data for North America.
  *     Since those files are in the public domain, they could be bundled in Apache SIS.
  *     But the weight of those files (about 2.4 Mb) is unnecessary for users who do not live in North America.</li>
@@ -37,8 +37,18 @@ import java.io.BufferedReader;
  *     are more restrictive than the Apache license and require that we inform the users about those conditions.</li>
  * </ul></div>
  *
- * Some classes that depend on installation resources are:
- * {@link org.apache.sis.referencing.factory.sql.EPSGFactory}.
+ * Some authorities implemented in Apache SIS modules are listed below.
+ * In this list, {@code "Embedded"} is a pseudo-authority for an embedded database containing EPSG and other data.
+ * The embedded database is provided as a convenience for avoiding the need to define a {@code SIS_DATA} directory
+ * on the local machine.
+ *
+ * <table class="sis">
+ *   <caption>Authorities supported by Apache SIS</caption>
+ *   <tr><th>Authority</th>          <th>Provided by Maven module</th>                          <th>Used by class</th></tr>
+ *   <tr><td>{@code "EPSG"}</td>     <td>{@code org.apache.sis.non-free:sis-epsg}</td>          <td>{@link org.apache.sis.referencing.factory.sql.EPSGFactory}</td></tr>
+ *   <tr><td>{@code "Embedded"}</td> <td>{@code org.apache.sis.non-free:sis-embedded-data}</td> <td>All the above</td></tr>
+ * </table>
+ *
  * In order to allow those classes to discover which resources are available,
  * {@code InstallationResources} implementations shall be declared in the following file:
  *
@@ -47,13 +57,13 @@ import java.io.BufferedReader;
  * }
  *
  * Above registration is usually done automatically when extension modules are added on the classpath.
- * For example adding the {@code org.apache.sis.non-free:​sis-epsg} Maven dependency as documented on
+ * For example adding the {@code org.apache.sis.non-free:sis-epsg} Maven dependency as documented on
  * the <a href="http://sis.apache.org/epsg.html">Apache SIS web site</a> is the only step needed for
  * allowing Apache SIS to read the EPSG scripts (however SIS still needs an installation directory
  * for writing the database; see above-cited web page for more information).
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 0.7
+ * @version 0.8
  * @since   0.7
  * @module
  */
@@ -69,9 +79,19 @@ public abstract class InstallationResour
      * The values recognized by SIS are listed below
      * (note that this list may be expanded in any future SIS versions):
      *
-     * <ul>
-     *   <li>{@code "EPSG"} for the EPSG geodetic dataset.</li>
-     * </ul>
+     * <table class="sis">
+     *   <caption>Authorities supported by Apache SIS</caption>
+     *   <tr><th>Authority</th>          <th>Resources</th></tr>
+     *   <tr><td>{@code "EPSG"}</td>     <td>SQL installation scripts for EPSG geodetic dataset.</td></tr>
+     *   <tr><td>{@code "Embedded"}</td> <td>Data source of embedded database containing EPSG and other resources.</td></tr>
+     * </table>
+     *
+     * <div class="note"><b>Note:</b>
+     * {@code "Embedded"} is a pseudo-authority for an embedded database containing EPSG and other data.
+     * This embedded database is provided by the {@code org.apache.sis.non-free:sis-embedded-data} module
+     * as a convenience for avoiding the need to define a {@code SIS_DATA} directory on the local machine.
+     * In this particular case, the resource is more for execution than for installation.
+     * </div>
      *
      * This method may return an empty set if this {@code InstallationResources} instance did not find the
      * resources (for example because of files not found) or does not have the permission to distribute them.
@@ -84,10 +104,19 @@ public abstract class InstallationResour
      * Returns the terms of use of the resources distributed by the specified authority, or {@code null} if none.
      * The terms of use can be returned in either plain text or HTML.
      *
-     * <div class="note"><b>Example:</b>
-     * For the {@code "EPSG"} authority, this method may return a copy of the
-     * <a href="http://www.epsg.org/TermsOfUse">http://www.epsg.org/TermsOfUse</a> page.
-     * </div>
+     * <table class="sis">
+     *   <caption>Licenses for some supported authorities</caption>
+     *   <tr>
+     *     <th>Authority</th>
+     *     <th>License</th>
+     *   </tr><tr>
+     *     <td>{@code "EPSG"}</td>
+     *     <td>A copy of the <a href="http://www.epsg.org/TermsOfUse">http://www.epsg.org/TermsOfUse</a> page.</td>
+     *   </tr><tr>
+     *     <td>{@code "Embedded"}</td>
+     *     <td>Above EPSG license.</td>
+     *   </tr>
+     * </table>
      *
      * @param  authority  one of the values returned by {@link #getAuthorities()}.
      * @param  locale     the preferred locale for the terms of use.
@@ -101,12 +130,18 @@ public abstract class InstallationResour
     /**
      * Returns the names of all resources of the specified authority that are distributed by this instance.
      * The resources will be used in the order they appear in the array.
+     * Examples:
      *
-     * <div class="note"><b>Example:</b>
-     * for the {@code "EPSG"} authority, this method may return the filenames of all SQL scripts to execute.
-     * One of the first script creates tables, followed by a script that populates tables with data,
-     * followed by a script that creates foreigner keys.
-     * </div>
+     * <ul class="verbose">
+     *   <li><b>{@code "EPSG"} authority:</b>
+     *     the resource names are the filenames of all SQL scripts to execute. One of the first script creates tables,
+     *     followed by a script that populates tables with data, followed by a script that creates foreigner keys.
+     *   </li>
+     *   <li><b>{@code "Embedded"} pseudo-authority:</b>
+     *     the database name, which is {@code "SpatialMetadata"}.
+     *     When embedded, this database is read-only.
+     *   </li>
+     * </ul>
      *
      * @param  authority  one of the values returned by {@link #getAuthorities()}.
      * @return the names of all resources of the given authority that are distributed by this instance.
@@ -116,6 +151,31 @@ public abstract class InstallationResour
     public abstract String[] getResourceNames(String authority) throws IOException;
 
     /**
+     * Returns an installation resource for the given authority, or {@code null} if not available.
+     * The return value may be an instance of any type, at implementation choice.
+     * This may be for example a {@link java.net.URL} referencing the actual resource.
+     *
+     * <p>The default implementation returns {@code null}. A null value means that the resource is fetched by
+     * {@link #openScript(String, int)} instead than this method. We do not return {@link java.net.URL} to text
+     * files in order to ensure that the file is opened with proper character encoding.</p>
+     *
+     * @param  authority  one of the values returned by {@link #getAuthorities()}.
+     * @param  index      index of the resource to get, from 0 inclusive to
+     *         <code>{@linkplain #getResourceNames(String) getResourceNames}(authority).length</code> exclusive.
+     * @return the resource as an URL or any other type (at implementation choice), or {@code null} if not available.
+     * @throws IllegalArgumentException if the given {@code authority} argument is not one of the expected values.
+     * @throws IndexOutOfBoundsException if the given {@code resource} argument is out of bounds.
+     * @throws IOException if an error occurred while fetching the resource.
+     *
+     * @see ClassLoader#getResource(String)
+     *
+     * @since 0.8
+     */
+    public Object getResource(String authority, int index) throws IOException {
+        return null;
+    }
+
+    /**
      * Returns a reader for the resources at the given index.
      * The resource may be a SQL script or any other resources readable as a text.
      * The returned {@link BufferedReader} instance shall be closed by the caller.

Modified: sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/setup/OptionKey.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/setup/OptionKey.java?rev=1813747&r1=1813746&r2=1813747&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/setup/OptionKey.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/setup/OptionKey.java [UTF-8] Mon Oct 30 10:25:08 2017
@@ -36,7 +36,7 @@ import org.apache.sis.internal.system.Mo
  * ({@link org.apache.sis.storage.DataStore}, <i>etc</i>).
  * {@code OptionKey}s are used for aspects that usually do not need to be configured, except in a few specialized cases.
  * For example most data file formats read by SIS do not require the user to specify the character encoding, since the
- * encoding it is often given in the file header or in the format specification. However if SIS may have to read plain
+ * encoding it is often given in the file header or in the format specification. However if SIS needs to read plain
  * text files <em>and</em> the default platform encoding is not suitable, then the user can specify the desired encoding
  * explicitely using the {@link #ENCODING} option.
  *
@@ -77,8 +77,8 @@ public class OptionKey<T> implements Ser
     /**
      * The library to use for creating geometric objects at reading time.
      * Some libraries are the Java Topology Suite (JTS), ESRI geometry API and Java2D.
-     * If this option is not specified, then a library will be selected automatically
-     * among the libraries available in the runtime environment.
+     * If this option is not specified, then a default library will be selected among
+     * the libraries available in the runtime environment.
      *
      * @since 0.8
      */

Modified: sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/util/ArraysExt.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/util/ArraysExt.java?rev=1813747&r1=1813746&r2=1813747&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/util/ArraysExt.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/util/ArraysExt.java [UTF-8] Mon Oct 30 10:25:08 2017
@@ -67,7 +67,7 @@ import java.lang.reflect.Array;
  * objects.
  *
  * @author Martin Desruisseaux (IRD, Geomatys)
- * @version 0.4
+ * @version 0.8
  *
  * @see Arrays
  *
@@ -1684,6 +1684,33 @@ public final class ArraysExt extends Sta
     }
 
     /**
+     * Returns {@code true} if all values in the specified array are equal to the specified value,
+     * which may be {@code null}.
+     *
+     * @param  array  the array to check.
+     * @param  value  the expected value.
+     * @return {@code true} if all elements in the given array are equal to the given value.
+     *
+     * @since 0.8
+     */
+    public static boolean allEquals(final Object[] array, final Object value) {
+        if (value == null) {
+            for (int i=0; i<array.length; i++) {
+                if (array[i] != null) {
+                    return false;
+                }
+            }
+        } else {
+            for (int i=0; i<array.length; i++) {
+                if (!value.equals(array[i])) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    /**
      * Returns {@code true} if all values in the specified array are equal to the specified value,
      * which may be {@link Double#NaN}.
      *

Modified: sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/util/CharSequences.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/util/CharSequences.java?rev=1813747&r1=1813746&r2=1813747&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/util/CharSequences.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/util/CharSequences.java [UTF-8] Mon Oct 30 10:25:08 2017
@@ -1200,7 +1200,7 @@ searchWordBreak:    while (true) {
      *
      *   <li>Next replace all occurrence of {@code '_'} by spaces in order to take in account
      *     an other common naming convention, which uses {@code '_'} as a word separator. This
-     *     convention is used by NetCDF attributes like {@code "project_name"}.</li>
+     *     convention is used by netCDF attributes like {@code "project_name"}.</li>
      *
      *   <li>Finally ensure that the first character is upper-case.</li>
      * </ol>

Modified: sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/util/Exceptions.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/util/Exceptions.java?rev=1813747&r1=1813746&r2=1813747&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/util/Exceptions.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/util/Exceptions.java [UTF-8] Mon Oct 30 10:25:08 2017
@@ -20,6 +20,7 @@ import java.util.List;
 import java.util.ArrayList;
 import java.util.Locale;
 import java.sql.SQLException;
+import java.nio.file.DirectoryIteratorException;
 import java.lang.reflect.InvocationTargetException;
 import org.opengis.util.InternationalString;
 import org.apache.sis.util.resources.Vocabulary;
@@ -211,6 +212,7 @@ public final class Exceptions extends St
      *   <li>It is an instance of {@link InvocationTargetException} (could be wrapping anything).</li>
      *   <li>It is an instance of {@link BackingStoreException} (typically wrapping a checked exception).</li>
      *   <li>It is an instance of {@link UncheckedIOException} (wrapping a {@link java.io.IOException}).</li>
+     *   <li>It is an instance of {@link DirectoryIteratorException} (wrapping a {@link java.io.IOException}).</li>
      *   <li>It is a parent type of the cause. For example some JDBC drivers wrap {@link SQLException}
      *       in other {@code SQLException} without additional information.</li>
      * </ul>
@@ -227,7 +229,8 @@ public final class Exceptions extends St
         if (exception != null) {
             while (exception instanceof InvocationTargetException ||
                    exception instanceof BackingStoreException ||
-                   exception instanceof UncheckedIOException)
+                   exception instanceof UncheckedIOException ||
+                   exception instanceof DirectoryIteratorException)
             {
                 final Throwable cause = exception.getCause();
                 if (!(cause instanceof Exception)) break;



Mime
View raw message