sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1817597 [10/19] - in /sis/branches/ISO-19115-3: ./ 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/ applic...
Date Sat, 09 Dec 2017 10:57:47 GMT
Modified: sis/branches/ISO-19115-3/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/AlbersEqualAreaTest.java
URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/AlbersEqualAreaTest.java?rev=1817597&r1=1817596&r2=1817597&view=diff
==============================================================================
--- sis/branches/ISO-19115-3/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/AlbersEqualAreaTest.java [UTF-8] (original)
+++ sis/branches/ISO-19115-3/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/AlbersEqualAreaTest.java [UTF-8] Sat Dec  9 10:57:44 2017
@@ -62,12 +62,12 @@ public final strictfp class AlbersEqualA
     @Test
     public void testSphere() throws FactoryException, TransformException {
         createCompleteProjection(new org.apache.sis.internal.referencing.provider.AlbersEqualArea(),
-                6370997,    // Semi-major axis from Synder table 15
+                6370997,    // Semi-major axis from Snyder table 15
                 6370997,    // Semi-minor axis
                 0,          // Central meridian
                 0,          // Latitude of origin
-                29.5,       // Standard parallel 1 (from Synder table 15)
-                45.5,       // Standard parallel 2 (from Synder table 15)
+                29.5,       // Standard parallel 1 (from Snyder table 15)
+                45.5,       // Standard parallel 2 (from Snyder table 15)
                 NaN,        // Scale factor (none)
                 0,          // False easting
                 0);         // False northing
@@ -78,10 +78,10 @@ public final strictfp class AlbersEqualA
         tolerance = Formulas.LINEAR_TOLERANCE;
         final AlbersEqualArea kernel = (AlbersEqualArea) getKernel();
         assertTrue("isSpherical", isSpherical(kernel));
-        assertEquals("n", 0.6028370, kernel.nm, 0.5E-7);                    // Expected 'n' value from Synder table 15.
+        assertEquals("n", 0.6028370, kernel.nm, 0.5E-7);                    // Expected 'n' value from Snyder table 15.
         /*
          * When stepping into the AlbersEqualArea.Sphere.transform(…) method with a debugger, the
-         * expected value of 6370997*ρ/n is 6910941 (value taken from ρ column in Synder table 15).
+         * expected value of 6370997*ρ/n is 6910941 (value taken from ρ column in Snyder table 15).
          */
         verifyTransform(new double[] {0, 50}, new double[] {0, 5373933.180});
         /*
@@ -111,12 +111,12 @@ public final strictfp class AlbersEqualA
     @DependsOnMethod("testSphere")
     public void testEllipse() throws FactoryException, TransformException {
         createCompleteProjection(new org.apache.sis.internal.referencing.provider.AlbersEqualArea(),
-                6378206.4,  // Semi-major axis from Synder table 15
+                6378206.4,  // Semi-major axis from Snyder table 15
                 6356583.8,  // Semi-minor axis
                 0,          // Central meridian
                 0,          // Latitude of origin
-                29.5,       // Standard parallel 1 (from Synder table 15)
-                45.5,       // Standard parallel 2 (from Synder table 15)
+                29.5,       // Standard parallel 1 (from Snyder table 15)
+                45.5,       // Standard parallel 2 (from Snyder table 15)
                 NaN,        // Scale factor (none)
                 0,          // False easting
                 0);         // False northing
@@ -128,13 +128,13 @@ public final strictfp class AlbersEqualA
         final AlbersEqualArea kernel = (AlbersEqualArea) getKernel();
         assertFalse("isSpherical", isSpherical(kernel));
         /*
-         * Expected 'n' value from Synder table 15. The division by (1-ℯ²) is because Apache SIS omits this factor
+         * Expected 'n' value from Snyder table 15. The division by (1-ℯ²) is because Apache SIS omits this factor
          * in its calculation of n (we rather take it in account in (de)normalization matrices and elsewhere).
          */
         assertEquals("n", 0.6029035, kernel.nm / (1 - kernel.eccentricitySquared), 0.5E-7);
         /*
          * When stepping into the AlbersEqualArea.Sphere.transform(…) method with a debugger, the expected
-         * value of 6378206.4*ρ/(nm/(1-ℯ²)) is 6931335 (value taken from ρ column in Synder table 15).
+         * value of 6378206.4*ρ/(nm/(1-ℯ²)) is 6931335 (value taken from ρ column in Snyder table 15).
          */
         verifyTransform(new double[] {0, 50}, new double[] {0, 5356698.435});
         /*

Modified: sis/branches/ISO-19115-3/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/MathTransformTestCase.java
URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/MathTransformTestCase.java?rev=1817597&r1=1817596&r2=1817597&view=diff
==============================================================================
--- sis/branches/ISO-19115-3/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/MathTransformTestCase.java [UTF-8] (original)
+++ sis/branches/ISO-19115-3/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/MathTransformTestCase.java [UTF-8] Sat Dec  9 10:57:44 2017
@@ -310,6 +310,7 @@ public abstract strictfp class MathTrans
 
     /**
      * Generates random numbers that can be used for the current transform.
+     * The number of dimensions is given by {@code transform.getSourceDimensions()}.
      *
      * @param  domain   the domain of the numbers to be generated.
      * @param  propNaN  approximative percentage of NaN values as a fraction between 0 and 1, or 0 if none.

Modified: sis/branches/ISO-19115-3/core/sis-referencing/src/test/java/org/apache/sis/test/ReferencingAssert.java
URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-referencing/src/test/java/org/apache/sis/test/ReferencingAssert.java?rev=1817597&r1=1817596&r2=1817597&view=diff
==============================================================================
--- sis/branches/ISO-19115-3/core/sis-referencing/src/test/java/org/apache/sis/test/ReferencingAssert.java [UTF-8] (original)
+++ sis/branches/ISO-19115-3/core/sis-referencing/src/test/java/org/apache/sis/test/ReferencingAssert.java [UTF-8] Sat Dec  9 10:57:44 2017
@@ -22,6 +22,7 @@ import java.awt.geom.RectangularShape;
 import java.awt.geom.AffineTransform;
 import javax.measure.Unit;
 import org.opengis.geometry.Envelope;
+import org.opengis.geometry.DirectPosition;
 import org.opengis.metadata.Identifier;
 import org.opengis.parameter.GeneralParameterValue;
 import org.opengis.parameter.ParameterDescriptor;
@@ -51,7 +52,7 @@ import static java.lang.StrictMath.*;
  * from other modules and libraries.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 0.7
+ * @version 0.8
  * @since   0.3
  * @module
  */
@@ -281,15 +282,19 @@ public strictfp class ReferencingAssert
     public static void assertEnvelopeEquals(final Envelope expected, final Envelope actual, final double... tolerances) {
         final int dimension = expected.getDimension();
         assertEquals("dimension", dimension, actual.getDimension());
+        final DirectPosition expectedLower = expected.getLowerCorner();
+        final DirectPosition expectedUpper = expected.getUpperCorner();
+        final DirectPosition actualLower   = actual  .getLowerCorner();
+        final DirectPosition actualUpper   = actual  .getUpperCorner();
         double tolerance = 0;
         for (int i=0; i<dimension; i++) {
             if (i < tolerances.length) {
                 tolerance = tolerances[i];
             }
-            if (abs(expected.getMinimum(i) - actual.getMinimum(i)) > tolerance ||
-                abs(expected.getMaximum(i) - actual.getMaximum(i)) > tolerance)
+            if (abs(expectedLower.getOrdinate(i) - actualLower.getOrdinate(i)) > tolerance ||
+                abs(expectedUpper.getOrdinate(i) - actualUpper.getOrdinate(i)) > tolerance)
             {
-                fail("Envelopes are not equal:\n"
+                fail("Envelopes are not equal in dimension " + i + ":\n"
                         + "expected " + Envelopes.toString(expected) + "\n"
                         + " but got " + Envelopes.toString(actual));
             }

Modified: sis/branches/ISO-19115-3/core/sis-referencing/src/test/java/org/apache/sis/test/integration/ConsistencyTest.java
URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-referencing/src/test/java/org/apache/sis/test/integration/ConsistencyTest.java?rev=1817597&r1=1817596&r2=1817597&view=diff
==============================================================================
--- sis/branches/ISO-19115-3/core/sis-referencing/src/test/java/org/apache/sis/test/integration/ConsistencyTest.java [UTF-8] (original)
+++ sis/branches/ISO-19115-3/core/sis-referencing/src/test/java/org/apache/sis/test/integration/ConsistencyTest.java [UTF-8] Sat Dec  9 10:57:44 2017
@@ -25,6 +25,7 @@ import org.opengis.util.FactoryException
 import org.opengis.util.NoSuchIdentifierException;
 import org.opengis.referencing.crs.CoordinateReferenceSystem;
 import org.apache.sis.referencing.factory.FactoryDataException;
+import org.apache.sis.referencing.factory.UnavailableFactoryException;
 import org.apache.sis.referencing.IdentifiedObjects;
 import org.apache.sis.referencing.CRS;
 import org.apache.sis.io.wkt.Convention;
@@ -39,8 +40,8 @@ import org.apache.sis.test.DependsOn;
 import org.apache.sis.test.TestCase;
 import org.junit.Test;
 
-import static org.junit.Assume.*;
 import static org.junit.Assert.*;
+import static org.junit.Assume.assumeTrue;
 
 
 /**
@@ -67,11 +68,22 @@ public final strictfp class ConsistencyT
      * Codes to exclude for now.
      */
     private static final Set<String> EXCLUDES = new HashSet<>(Arrays.asList(
-        "CRS:1",            // Computer display
-        "EPSG:5819"         // EPSG topocentric example A
+        "CRS:1",            // Computer display: WKT parser alters the (i,j) axis names.
+        "EPSG:5819",        // EPSG topocentric example A: error while parsing WKT.
+        "AUTO2:42001",      // This projection requires parameters, but we provide none.
+        "AUTO2:42002",      // This projection requires parameters, but we provide none.
+        "AUTO2:42003",      // This projection requires parameters, but we provide none.
+        "AUTO2:42004",      // This projection requires parameters, but we provide none.
+        "AUTO2:42005"       // This projection requires parameters, but we provide none.
     ));
 
     /**
+     * Width of the code columns in the warnings formatted by {@link #print(String, String, Object)}.
+     * We begin with an arbitrary width and will expand if necessary.
+     */
+    private int codeWidth = 15;
+
+    /**
      * Verifies the WKT consistency of all CRS instances.
      *
      * @throws FactoryException if an error other than "unsupported operation method" occurred.
@@ -92,7 +104,7 @@ public final strictfp class ConsistencyT
                 final CoordinateReferenceSystem crs;
                 try {
                     crs = CRS.forCode(code);
-                } catch (NoSuchIdentifierException | FactoryDataException e) {
+                } catch (UnavailableFactoryException | NoSuchIdentifierException | FactoryDataException e) {
                     print(code, "WARNING", e.getLocalizedMessage());
                     continue;
                 }
@@ -118,9 +130,13 @@ public final strictfp class ConsistencyT
      * Prints the given code followed by spaces and the given {@code "ERROR"} or {@code "WARNING"} word,
      * then the given message.
      */
-    private static void print(final String code, final String word, final Object message) {
+    private void print(final String code, final String word, final Object message) {
+        final int currentWidth = code.length();
+        if (currentWidth >= codeWidth) {
+            codeWidth = currentWidth + 1;
+        }
         out.print(code);
-        out.print(CharSequences.spaces(15 - code.length()));
+        out.print(CharSequences.spaces(codeWidth - currentWidth));
         out.print(word);
         out.print(": ");
         out.println(message);
@@ -135,7 +151,7 @@ public final strictfp class ConsistencyT
      * @param  crs   the CRS to test.
      * @return the parsed CRS.
      */
-    private static CoordinateReferenceSystem parseAndFormat(final WKTFormat f,
+    private CoordinateReferenceSystem parseAndFormat(final WKTFormat f,
             final String code, final CoordinateReferenceSystem crs)
     {
         String wkt = f.format(crs);

Modified: sis/branches/ISO-19115-3/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java
URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java?rev=1817597&r1=1817596&r2=1817597&view=diff
==============================================================================
--- sis/branches/ISO-19115-3/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java [UTF-8] (original)
+++ sis/branches/ISO-19115-3/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java [UTF-8] Sat Dec  9 10:57:44 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/ISO-19115-3/core/sis-utility/pom.xml
URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-utility/pom.xml?rev=1817597&r1=1817596&r2=1817597&view=diff
==============================================================================
--- sis/branches/ISO-19115-3/core/sis-utility/pom.xml (original)
+++ sis/branches/ISO-19115-3/core/sis-utility/pom.xml Sat Dec  9 10:57:44 2017
@@ -28,7 +28,7 @@
   <parent>
     <groupId>org.apache.sis</groupId>
     <artifactId>core</artifactId>
-    <version>0.8-jdk8-SNAPSHOT</version>
+    <version>1.0-jdk8-SNAPSHOT</version>
   </parent>
 
 

Modified: sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/PrimitiveTypeProperties.java
URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/PrimitiveTypeProperties.java?rev=1817597&r1=1817596&r2=1817597&view=diff
==============================================================================
--- sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/PrimitiveTypeProperties.java [UTF-8] (original)
+++ sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/PrimitiveTypeProperties.java [UTF-8] Sat Dec  9 10:57:44 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/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/NameAdapter.java
URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/NameAdapter.java?rev=1817597&r1=1817596&r2=1817597&view=diff
==============================================================================
--- sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/NameAdapter.java [UTF-8] (original)
+++ sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/NameAdapter.java [UTF-8] Sat Dec  9 10:57:44 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/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/UnitAdapter.java
URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/UnitAdapter.java?rev=1817597&r1=1817596&r2=1817597&view=diff
==============================================================================
--- sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/UnitAdapter.java [UTF-8] (original)
+++ sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/UnitAdapter.java [UTF-8] Sat Dec  9 10:57:44 2017
@@ -66,7 +66,7 @@ public class UnitAdapter extends XmlAdap
     }
 
     /**
-     * A variant of {@link UnitAdapter} which marshal units as an URN for Coordinate System (CS) axes.
+     * A variant of {@link UnitAdapter} which marshal units as a URN for Coordinate System (CS) axes.
      * Example: {@code "urn:ogc:def:uom:EPSG::9001"}.
      *
      * The difference between coordinate system axis and other uses (prime meridian, etc.) is in the choice of EPSG

Modified: sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gml/CodeListUID.java
URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gml/CodeListUID.java?rev=1817597&r1=1817596&r2=1817597&view=diff
==============================================================================
--- sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gml/CodeListUID.java [UTF-8] (original)
+++ sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gml/CodeListUID.java [UTF-8] Sat Dec  9 10:57:44 2017
@@ -32,7 +32,7 @@ import org.apache.sis.util.iso.Types;
  */
 public final class CodeListUID {
     /**
-     * The code space of the {@link #value} as an URI, or {@code null}.
+     * The code space of the {@link #value} as a URI, or {@code null}.
      */
     @XmlAttribute
     String codeSpace;

Modified: sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/internal/system/DataDirectory.java
URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/internal/system/DataDirectory.java?rev=1817597&r1=1817596&r2=1817597&view=diff
==============================================================================
--- sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/internal/system/DataDirectory.java [UTF-8] (original)
+++ sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/internal/system/DataDirectory.java [UTF-8] Sat Dec  9 10:57:44 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
  */
@@ -86,25 +84,67 @@ public enum DataDirectory {
     private Path directory;
 
     /**
+     * Prevents the log message about {@code SIS_DATA} environment variable not set.
+     * This is used for the "About" command line action only.
+     */
+    public static void quiet() {
+        lastWarning = Messages.Keys.DataDirectoryNotSpecified_1;
+    }
+
+    /**
      * Logs a message to the {@code "org.apache.sis.system"} logger only if different than the last warning.
      */
-    private static void warning(final String method, final Exception e, final short key, final Object... parameters) {
+    private static void warning(final Exception e, final short key, final Object... parameters) {
         if (key != lastWarning) {
             lastWarning = key;
-            log(Level.WARNING, method, e, key, parameters);
+            log(Level.WARNING, e, key, parameters);
         }
     }
 
     /**
      * Logs a message to the {@code "org.apache.sis.system"} logger.
      */
-    private static void log(final Level level, final String method, final Exception e, final short key, final Object... parameters) {
+    private static void log(final Level level, final Exception e, final short key, final Object... parameters) {
         final LogRecord record = Messages.getResources(null).getLogRecord(level, key, parameters);
         record.setLoggerName(Loggers.SYSTEM);
         if (e != null) {
             record.setThrown(e);
         }
-        Logging.log(DataDirectory.class, method, record);
+        Logging.log(null, null, record);            // Let Logging.log(…) infers the public caller.
+    }
+
+    /**
+     * 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;
     }
 
     /**
@@ -115,24 +155,24 @@ 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);
+                warning(null, Messages.Keys.DataDirectoryNotSpecified_1, ENV);
             } else try {
                 final Path path = Paths.get(dir);
                 if (!Files.isDirectory(path)) {
-                    warning("getRootDirectory", null, Messages.Keys.DataDirectoryDoesNotExist_2, ENV, path);
+                    warning(null, Messages.Keys.DataDirectoryDoesNotExist_2, ENV, path);
                 } else if (!Files.isReadable(path)) {
-                    warning("getRootDirectory", null, Messages.Keys.DataDirectoryNotReadable_2, ENV, path);
+                    warning(null, Messages.Keys.DataDirectoryNotReadable_2, ENV, path);
                 } else {
-                    log(Level.CONFIG, "getRootDirectory", null, Messages.Keys.DataDirectory_2, ENV, path);
+                    log(Level.CONFIG, null, Messages.Keys.DataDirectory_2, ENV, path);
                     rootDirectory = path;
                 }
             } catch (InvalidPathException e) {
-                warning("getRootDirectory", e, Messages.Keys.DataDirectoryDoesNotExist_2, ENV, dir);
+                warning(e, Messages.Keys.DataDirectoryDoesNotExist_2, ENV, dir);
             }
         } catch (SecurityException e) {
-            warning("getRootDirectory", e, Messages.Keys.DataDirectoryNotAuthorized_1, ENV);
+            warning(e, Messages.Keys.DataDirectoryNotAuthorized_1, ENV);
         }
         return rootDirectory;
     }
@@ -162,12 +202,12 @@ public enum DataDirectory {
                     } else if (Files.isWritable(root)) try {
                         directory = Files.createDirectory(dir);
                     } catch (IOException e) {
-                        warning("getDirectory", e, Messages.Keys.DataDirectoryNotWritable_2, ENV, root);
+                        warning(e, Messages.Keys.DataDirectoryNotWritable_2, ENV, root);
                     } else {
-                        warning("getDirectory", null, Messages.Keys.DataDirectoryNotWritable_2, ENV, root);
+                        warning(null, Messages.Keys.DataDirectoryNotWritable_2, ENV, root);
                     }
                 } catch (SecurityException e) {
-                    warning("getDirectory", e, Messages.Keys.DataDirectoryNotAccessible_2, ENV, name);
+                    warning(e, Messages.Keys.DataDirectoryNotAccessible_2, ENV, name);
                 }
             }
         }

Modified: sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/internal/system/DefaultFactories.java
URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/internal/system/DefaultFactories.java?rev=1817597&r1=1817596&r2=1817597&view=diff
==============================================================================
--- sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/internal/system/DefaultFactories.java [UTF-8] (original)
+++ sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/internal/system/DefaultFactories.java [UTF-8] Sat Dec  9 10:57:44 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/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/internal/system/Modules.java
URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/internal/system/Modules.java?rev=1817597&r1=1817596&r2=1817597&view=diff
==============================================================================
--- sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/internal/system/Modules.java [UTF-8] (original)
+++ sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/internal/system/Modules.java [UTF-8] Sat Dec  9 10:57:44 2017
@@ -87,14 +87,14 @@ public final class Modules {
      *
      * @see org.apache.sis.util.Version
      */
-    public static final int MAJOR_VERSION = 0;
+    public static final int MAJOR_VERSION = 1;
 
     /**
      * The minor version number of all Apache SIS modules.
      *
      * @see org.apache.sis.util.Version
      */
-    public static final int MINOR_VERSION = 8;
+    public static final int MINOR_VERSION = 0;
 
     /**
      * The prefix of all classnames in Apache SIS, including a trailing dot.

Modified: sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/internal/system/OS.java
URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/internal/system/OS.java?rev=1817597&r1=1817596&r2=1817597&view=diff
==============================================================================
--- sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/internal/system/OS.java [UTF-8] (original)
+++ sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/internal/system/OS.java [UTF-8] Sat Dec  9 10:57:44 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/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/internal/system/Supervisor.java
URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/internal/system/Supervisor.java?rev=1817597&r1=1817596&r2=1817597&view=diff
==============================================================================
--- sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/internal/system/Supervisor.java [UTF-8] (original)
+++ sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/internal/system/Supervisor.java [UTF-8] Sat Dec  9 10:57:44 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/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/internal/util/Citations.java
URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/internal/util/Citations.java?rev=1817597&r1=1817596&r2=1817597&view=diff
==============================================================================
--- sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/internal/util/Citations.java [UTF-8] (original)
+++ sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/internal/util/Citations.java [UTF-8] Sat Dec  9 10:57:44 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/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/internal/util/CollectionsExt.java
URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/internal/util/CollectionsExt.java?rev=1817597&r1=1817596&r2=1817597&view=diff
==============================================================================
--- sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/internal/util/CollectionsExt.java [UTF-8] (original)
+++ sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/internal/util/CollectionsExt.java [UTF-8] Sat Dec  9 10:57:44 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/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/internal/util/Constants.java
URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/internal/util/Constants.java?rev=1817597&r1=1817596&r2=1817597&view=diff
==============================================================================
--- sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/internal/util/Constants.java [UTF-8] (original)
+++ sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/internal/util/Constants.java [UTF-8] Sat Dec  9 10:57:44 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/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/internal/util/DefinitionURI.java
URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/internal/util/DefinitionURI.java?rev=1817597&r1=1817596&r2=1817597&view=diff
==============================================================================
--- sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/internal/util/DefinitionURI.java [UTF-8] (original)
+++ sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/internal/util/DefinitionURI.java [UTF-8] Sat Dec  9 10:57:44 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,45 +683,8 @@ 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 an URL,
+     * 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.
      * Otherwise this method formats the URI as a URN.
      *
@@ -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/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/math/Fraction.java
URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/math/Fraction.java?rev=1817597&r1=1817596&r2=1817597&view=diff
==============================================================================
--- sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/math/Fraction.java [UTF-8] (original)
+++ sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/math/Fraction.java [UTF-8] Sat Dec  9 10:57:44 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/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/measure/UnitFormat.java
URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/measure/UnitFormat.java?rev=1817597&r1=1817596&r2=1817597&view=diff
==============================================================================
--- sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/measure/UnitFormat.java [UTF-8] (original)
+++ sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/measure/UnitFormat.java [UTF-8] Sat Dec  9 10:57:44 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/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/measure/UnitRegistry.java
URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/measure/UnitRegistry.java?rev=1817597&r1=1817596&r2=1817597&view=diff
==============================================================================
--- sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/measure/UnitRegistry.java [UTF-8] (original)
+++ sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/measure/UnitRegistry.java [UTF-8] Sat Dec  9 10:57:44 2017
@@ -121,22 +121,34 @@ final class UnitRegistry implements Syst
      */
     static <Q extends Quantity<Q>> SystemUnit<Q> init(final SystemUnit<Q> unit) {
         assert !Units.initialized : unit;        // This assertion happens during Units initialization, but it is okay.
-        boolean existed;
-        existed  = HARD_CODED.put(unit.dimension,   unit) != null;
-        existed |= HARD_CODED.put(unit.quantity,    unit) != null;
-        existed |= HARD_CODED.put(unit.getSymbol(), unit) != null;
+        int existed;
+        existed  = (HARD_CODED.put(unit.dimension,   unit) == null) ? 0 : 1;
+        existed |= (HARD_CODED.put(unit.quantity,    unit) == null) ? 0 : 2;
+        existed |= (HARD_CODED.put(unit.getSymbol(), unit) == null) ? 0 : 4;
         if (unit.epsg != 0) {
-            existed |= HARD_CODED.put(unit.epsg, unit) != null;
+            existed |= (HARD_CODED.put(unit.epsg, unit) == null) ? 0 : 8;
         }
         /*
          * Key collision on dimension and quantity tolerated for dimensionless units only, with an
          * an exception for "candela" because "lumen" is candela divided by a dimensionless unit.
+         * Another exception is "Hz" because it come after rad/s, which has the same dimension.
          */
-        assert !existed || unit.dimension.isDimensionless() || "cd".equals(unit.getSymbol()) : unit;
+        assert filter(existed, unit) == 0 : unit;
         return unit;
     }
 
     /**
+     * Clears the {@code existed} bits for the cases where we allow dimension or quantity type collisions.
+     * This method is invoked for assertions only.
+     */
+    private static int filter(int existed, final SystemUnit<?> unit) {
+        final String s = unit.getSymbol();
+        if (unit.dimension.isDimensionless()) existed &= ~(1 | 2);      // Accepts dimension and quantity collisions.
+        if (s.equals("cd") || s.equals("Hz")) existed &= ~(1    );      // Accepts dimension collisions only;
+        return s.isEmpty() ? 0 : existed;
+    }
+
+    /**
      * Invoked by {@link Units} static class initializer for registering SI conventional units.
      * This method shall be invoked in a single thread by the {@code Units} class initializer only.
      */



Mime
View raw message