sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1803070 [9/20] - in /sis/branches/JDK9: ./ application/sis-console/src/main/java/org/apache/sis/console/ core/sis-build-helper/ core/sis-build-helper/src/main/java/org/apache/sis/internal/book/ core/sis-build-helper/src/main/java/org/apach...
Date Wed, 26 Jul 2017 16:14:14 GMT
Modified: sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/GeographicOffsets.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/GeographicOffsets.java?rev=1803070&r1=1803069&r2=1803070&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/GeographicOffsets.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/GeographicOffsets.java [UTF-8] Wed Jul 26 16:14:09 2017
@@ -77,7 +77,6 @@ public class GeographicOffsets extends G
     /**
      * Constructs a provider with default parameters.
      */
-    @SuppressWarnings("ThisEscapedInObjectConstruction")
     public GeographicOffsets() {
         super(3, 3, PARAMETERS, new GeographicOffsets[4]);
         redimensioned[0] = new GeographicOffsets2D(redimensioned);

Modified: sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/GeographicToGeocentric.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/GeographicToGeocentric.java?rev=1803070&r1=1803069&r2=1803070&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/GeographicToGeocentric.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/GeographicToGeocentric.java [UTF-8] Wed Jul 26 16:14:09 2017
@@ -19,14 +19,20 @@ package org.apache.sis.internal.referenc
 import javax.measure.Unit;
 import javax.measure.quantity.Length;
 import org.opengis.util.FactoryException;
+import org.opengis.parameter.ParameterValue;
 import org.opengis.parameter.ParameterValueGroup;
+import org.opengis.parameter.ParameterDescriptor;
 import org.opengis.parameter.ParameterDescriptorGroup;
-import org.opengis.parameter.ParameterValue;
+import org.opengis.referencing.cs.CartesianCS;
+import org.opengis.referencing.cs.EllipsoidalCS;
 import org.opengis.referencing.operation.Conversion;
 import org.opengis.referencing.operation.MathTransform;
 import org.opengis.referencing.operation.MathTransformFactory;
 import org.apache.sis.referencing.operation.transform.EllipsoidToCentricTransform;
+import org.apache.sis.referencing.operation.transform.DefaultMathTransformFactory;
 import org.apache.sis.metadata.iso.citation.Citations;
+import org.apache.sis.parameter.ParameterBuilder;
+import org.apache.sis.parameter.Parameters;
 import org.apache.sis.internal.util.Constants;
 
 
@@ -35,35 +41,50 @@ import org.apache.sis.internal.util.Cons
  * This provider creates transforms from geographic to geocentric coordinate reference systems.
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
- * @version 0.7
+ * @version 0.8
  *
  * @see GeocentricToGeographic
  *
  * @since 0.7
  * @module
  */
-public final class GeographicToGeocentric extends AbstractProvider {
+public final class GeographicToGeocentric extends GeodeticOperation {
     /**
      * Serial number for inter-operability with different versions.
      */
     private static final long serialVersionUID = -5690807111952562344L;
 
     /**
-     * The OGC name used for this operation method. The OGC name is preferred to the EPSG name
-     * because it allows to distinguish between the forward and the inverse conversion.
+     * The OGC name used for this operation method. The OGC name is preferred to the EPSG name in Apache SIS
+     * implementation because it allows to distinguish between the forward and the inverse conversion.
+     */
+    public static final String NAME = "Ellipsoid_To_Geocentric";
+
+    /**
+     * An Apache SIS specific parameter for the number of dimensions (2 or 3).
+     * This parameter is practically the same than {@link GeocentricAffineBetweenGeographic#DIMENSION} except:
+     *
+     * <ul>
+     *   <li>The code space is {@code "SIS"} instead than {@code "OGC"} since this parameter is not defined in OGC 01-009.</li>
+     *   <li>The default number of dimensions is 3 instead of unspecified.</li>
+     * </ul>
+     *
+     * @see GeocentricAffineBetweenGeographic#DIMENSION
      */
-    static final String NAME = "Ellipsoid_To_Geocentric";
+    public static final ParameterDescriptor<Integer> DIMENSION;
 
     /**
      * The group of all parameters expected by this coordinate operation.
      */
     public static final ParameterDescriptorGroup PARAMETERS;
     static {
-        PARAMETERS = builder()
+        final ParameterBuilder builder = builder();
+        DIMENSION = builder.addName(Citations.SIS, "dim").setRequired(false).createBounded(Integer.class, 2, 3, 3);
+        PARAMETERS = builder
                 .addIdentifier("9602")
                 .addName("Geographic/geocentric conversions")
                 .addName(Citations.OGC, NAME)
-                .createGroupForMapProjection();
+                .createGroupForMapProjection(DIMENSION);
                 // Not really a map projection, but we leverage the same axis parameters.
     }
 
@@ -71,7 +92,19 @@ public final class GeographicToGeocentri
      * Constructs a provider for the 3-dimensional case.
      */
     public GeographicToGeocentric() {
-        super(3, 3, PARAMETERS);
+        this(3, new GeographicToGeocentric[4]);
+        redimensioned[1] = new GeographicToGeocentric(2, redimensioned);
+        redimensioned[3] = this;
+    }
+
+    /**
+     * Constructs a provider for the given dimensions.
+     *
+     * @param sourceDimensions  number of dimensions in the source CRS of this operation method.
+     * @param redimensioned     providers for all combinations between 2D and 3D cases.
+     */
+    private GeographicToGeocentric(int sourceDimensions, GeodeticOperation[] redimensioned) {
+        super(sourceDimensions, 3, PARAMETERS, redimensioned);
     }
 
     /**
@@ -85,6 +118,28 @@ public final class GeographicToGeocentri
     }
 
     /**
+     * If the user asked for the <cite>"Geographic/geocentric conversions"</cite> operation but the parameter types
+     * suggest that (s)he intended to convert in the opposite direction, return the name of operation method to use.
+     * We need this check because EPSG defines a single operation method for both {@code "Ellipsoid_To_Geocentric"}
+     * and {@code "Geocentric_To_Ellipsoid"} methods.
+     *
+     * <p><b>Note:</b>  we do not define similar method in {@link GeocentricToGeographic} class because the only
+     * way to obtain that operation method is to ask explicitely for {@code "Geocentric_To_Ellipsoid"} operation.
+     * The ambiguity that we try to resolve here exists only if the user asked for the EPSG:9602 operation, which
+     * is defined only in this class.</p>
+     *
+     * @return {@code "Geocentric_To_Ellipsoid"} if the user apparently wanted to get the inverse of this
+     *         {@code "Ellipsoid_To_Geocentric"} operation, or {@code null} if none.
+     */
+    @Override
+    public String resolveAmbiguity(final DefaultMathTransformFactory.Context context) {
+        if (context.getSourceCS() instanceof CartesianCS && context.getTargetCS() instanceof EllipsoidalCS) {
+            return GeocentricToGeographic.NAME;
+        }
+        return super.resolveAmbiguity(context);
+    }
+
+    /**
      * Notifies {@code DefaultMathTransformFactory} that Geographic/geocentric conversions
      * require values for the {@code "semi_major"} and {@code "semi_minor"} parameters.
      *
@@ -96,6 +151,16 @@ public final class GeographicToGeocentri
     }
 
     /**
+     * Specifies that the inverse of this operation is a different kind of operation.
+     *
+     * @return {@code false}.
+     */
+    @Override
+    public boolean isInvertible() {
+        return false;
+    }
+
+    /**
      * Creates a transform from the specified group of parameter values.
      *
      * @param  factory  the factory to use for creating the transform.
@@ -107,20 +172,20 @@ public final class GeographicToGeocentri
     public MathTransform createMathTransform(final MathTransformFactory factory, final ParameterValueGroup values)
             throws FactoryException
     {
-        return create(factory, values);
+        return create(factory, Parameters.castOrWrap(values));
     }
 
     /**
      * Implementation of {@link #createMathTransform(MathTransformFactory, ParameterValueGroup)}
      * shared with {@link GeocentricToGeographic}.
      */
-    static MathTransform create(final MathTransformFactory factory, final ParameterValueGroup values)
+    static MathTransform create(final MathTransformFactory factory, final Parameters values)
             throws FactoryException
     {
         final ParameterValue<?> semiMajor = values.parameter(Constants.SEMI_MAJOR);
         final Unit<Length> unit = semiMajor.getUnit().asType(Length.class);
         return EllipsoidToCentricTransform.createGeodeticConversion(factory, semiMajor.doubleValue(),
-                values.parameter(Constants.SEMI_MINOR).doubleValue(unit), unit, true,
+                values.parameter(Constants.SEMI_MINOR).doubleValue(unit), unit, values.intValue(DIMENSION) >= 3,
                 EllipsoidToCentricTransform.TargetType.CARTESIAN);
     }
 }

Modified: sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/LambertConformal2SP.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/LambertConformal2SP.java?rev=1803070&r1=1803069&r2=1803070&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/LambertConformal2SP.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/LambertConformal2SP.java [UTF-8] Wed Jul 26 16:14:09 2017
@@ -125,7 +125,8 @@ public final class LambertConformal2SP e
          * Special case: default value shall be the value of LATITUDE_OF_FALSE_ORIGIN.
          */
         STANDARD_PARALLEL_1 = createMandatoryLatitude(builder
-                .addNamesAndIdentifiers(Mercator2SP.STANDARD_PARALLEL));
+                .addNamesAndIdentifiers(Mercator2SP.STANDARD_PARALLEL)
+                .rename(Citations.PROJ4, "lat_1"));
         /*
          * EPSG:    Latitude of 2nd standard parallel
          * OGC:     standard_parallel_2

Modified: sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/LongitudeRotation.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/LongitudeRotation.java?rev=1803070&r1=1803069&r2=1803070&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/LongitudeRotation.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/LongitudeRotation.java [UTF-8] Wed Jul 26 16:14:09 2017
@@ -65,7 +65,6 @@ public final class LongitudeRotation ext
     /**
      * Constructs a provider with default parameters.
      */
-    @SuppressWarnings("ThisEscapedInObjectConstruction")
     public LongitudeRotation() {
         this(2, 2, new LongitudeRotation[4]);
         redimensioned[0] = this;

Modified: sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Molodensky.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Molodensky.java?rev=1803070&r1=1803069&r2=1803070&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Molodensky.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Molodensky.java [UTF-8] Wed Jul 26 16:14:09 2017
@@ -133,7 +133,6 @@ public final class Molodensky extends Ge
     /**
      * Constructs a new provider.
      */
-    @SuppressWarnings("ThisEscapedInObjectConstruction")
     public Molodensky() {
         this(3, 3, new Molodensky[4]);
         redimensioned[0] = new Molodensky(2, 2, redimensioned);

Modified: sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/MolodenskyInterpolation.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/MolodenskyInterpolation.java?rev=1803070&r1=1803069&r2=1803070&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/MolodenskyInterpolation.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/MolodenskyInterpolation.java [UTF-8] Wed Jul 26 16:14:09 2017
@@ -54,7 +54,6 @@ public final class MolodenskyInterpolati
     /**
      * Constructs a provider.
      */
-    @SuppressWarnings("ThisEscapedInObjectConstruction")
     public MolodenskyInterpolation() {
         super(2, 2, builder().setCodeSpace(null, Constants.SIS).addName("Molodensky interpolation")
                         .createGroupWithSameParameters(PARAMETERS), new MolodenskyInterpolation[4]);

Modified: sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/PositionVector7Param3D.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/PositionVector7Param3D.java?rev=1803070&r1=1803069&r2=1803070&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/PositionVector7Param3D.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/PositionVector7Param3D.java [UTF-8] Wed Jul 26 16:14:09 2017
@@ -54,7 +54,6 @@ public final class PositionVector7Param3
     /**
      * Constructs the provider.
      */
-    @SuppressWarnings("ThisEscapedInObjectConstruction")
     public PositionVector7Param3D() {
         this(3, 3, new GeocentricAffineBetweenGeographic[4]);
         redimensioned[0] = new PositionVector7Param2D(      redimensioned);

Modified: sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Providers.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Providers.java?rev=1803070&r1=1803069&r2=1803070&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Providers.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Providers.java [UTF-8] Wed Jul 26 16:14:09 2017
@@ -27,6 +27,8 @@ import org.apache.sis.internal.util.Lazy
  *
  * <p>This class is <strong>not</strong> thread-safe. Synchronization are user's responsibility.</p>
  *
+ * <p>This class is not needed on the JDK9 branch.</p>
+ *
  * @author  Martin Desruisseaux (Geomatys)
  * @version 0.7
  * @since   0.7
@@ -54,11 +56,11 @@ public final class Providers extends Laz
                 for (int i=cached.size(); --i >= 0;) {
                     final OperationMethod m = cached.get(i);
                     if (m.getClass() == variant3D) {
-                        final GeodeticOperation candidate = ((GeodeticOperation) m).redimensioned[0];
-                        if (candidate != null) {            // Should not be null, but let be safe.
-                            assert candidate.getClass() == element.getClass() : variant3D;
-                            element = candidate;
-                            break;
+                        for (final GeodeticOperation candidate : ((GeodeticOperation) m).redimensioned) {
+                            if (candidate != null && candidate.getClass() == element.getClass()) {
+                                element = candidate;
+                                break;
+                            }
                         }
                     }
                 }

Modified: sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterValueGroup.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterValueGroup.java?rev=1803070&r1=1803069&r2=1803070&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterValueGroup.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterValueGroup.java [UTF-8] Wed Jul 26 16:14:09 2017
@@ -22,6 +22,7 @@ import java.util.LinkedList;
 import java.util.Map;
 import java.util.IdentityHashMap;
 import java.util.Iterator;
+import java.util.Objects;
 import java.io.Serializable;
 import javax.xml.bind.annotation.XmlType;
 import javax.xml.bind.annotation.XmlElement;
@@ -43,9 +44,6 @@ import org.apache.sis.util.resources.Err
 import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.util.Utilities;
 
-// Branch-dependent imports
-import java.util.Objects;
-
 
 /**
  * A group of related parameter values. Parameter groups have some similarities with {@code java.util.Map}:
@@ -103,7 +101,7 @@ import java.util.Objects;
  * <p>Calls to {@code values().clear()} restore this {@code DefaultParameterValueGroup} to its initial state.</p>
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
- * @version 0.7
+ * @version 0.8
  *
  * @see DefaultParameterDescriptorGroup
  * @see DefaultParameterValue
@@ -283,48 +281,41 @@ public class DefaultParameterValueGroup
      */
     @Override
     ParameterValue<?> parameterIfExist(final String name) throws ParameterNotFoundException {
-        final ParameterValueList values = this.values; // Protect against accidental changes.
+        final ParameterValueList values = this.values;          // Protect against accidental changes.
         /*
-         * Quick search for an exact match. By invoking 'descriptor(i)' instead of 'get(i)',
-         * we avoid the creation of mandatory ParameterValue which was deferred. If we find
-         * a matching name, the ParameterValue will be lazily created (if not already done)
-         * by the call to 'get(i)'.
+         * Search for an exact match. By invoking 'descriptor(i)' instead of 'get(i)', we avoid the
+         * creation of mandatory ParameterValue which was deferred. If we find a matching name, the
+         * ParameterValue will be lazily created (if not already done) by the call to 'get(i)'.
          */
-        final int size = values.size();
-        for (int i=0; i<size; i++) {
-            final GeneralParameterDescriptor descriptor = values.descriptor(i);
-            if (descriptor instanceof ParameterDescriptor<?>) {
-                if (name.equals(descriptor.getName().toString())) {
-                    return (ParameterValue<?>) values.get(i);
-                }
-            }
-        }
-        /*
-         * More costly search, including aliases, before to give up.
-         */
-        int fallback  = -1;
+        int index     = -1;
         int ambiguity = -1;
+        final int size = values.size();
         for (int i=0; i<size; i++) {
             final GeneralParameterDescriptor descriptor = values.descriptor(i);
             if (descriptor instanceof ParameterDescriptor<?>) {
                 if (IdentifiedObjects.isHeuristicMatchForName(descriptor, name)) {
-                    if (fallback < 0) {
-                        fallback = i;
+                    if (index < 0) {
+                        index = i;
                     } else {
                         ambiguity = i;
                     }
                 }
             }
         }
-        if (fallback >= 0) {
-            if (ambiguity < 0) {
-                return (ParameterValue<?>) values.get(fallback);   // May lazily create a ParameterValue.
-            }
-            throw new ParameterNotFoundException(Errors.format(Errors.Keys.AmbiguousName_3,
-                    IdentifiedObjects.toString(values.descriptor(fallback) .getName()),
-                    IdentifiedObjects.toString(values.descriptor(ambiguity).getName()), name), name);
+        if (ambiguity < 0) {
+            return (index >= 0) ? (ParameterValue<?>) values.get(index) : null;     // May lazily create a ParameterValue.
+        }
+        final GeneralParameterDescriptor d1 = values.descriptor(index);
+        final GeneralParameterDescriptor d2 = values.descriptor(ambiguity);
+        final String message;
+        if (d1 == d2) {
+            message = Errors.format(Errors.Keys.MultiOccurenceValueAtIndices_3, name, index, ambiguity);
+        } else {
+            message = Errors.format(Errors.Keys.AmbiguousName_3,
+                        IdentifiedObjects.toString(d1.getName()),
+                        IdentifiedObjects.toString(d2.getName()), name);
         }
-        return null;
+        throw new ParameterNotFoundException(message, name);
     }
 
     /**

Modified: sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/parameter/ParameterFormat.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/parameter/ParameterFormat.java?rev=1803070&r1=1803069&r2=1803070&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/parameter/ParameterFormat.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/parameter/ParameterFormat.java [UTF-8] Wed Jul 26 16:14:09 2017
@@ -687,7 +687,8 @@ public class ParameterFormat extends Tab
                 /*
                  * Writes the values, each on its own line, together with their unit of measurement.
                  */
-                table.setCellAlignment(TableAppender.ALIGN_RIGHT);
+                final byte alignment = Number.class.isAssignableFrom(valueClass) ? TableAppender.ALIGN_RIGHT : TableAppender.ALIGN_LEFT;
+                table.setCellAlignment(alignment);
                 final int length = row.values.size();
                 for (int i=0; i<length; i++) {
                     Object value = row.values.get(i);
@@ -705,7 +706,7 @@ public class ParameterFormat extends Tab
                                 table.append(ditto);
                                 nextColumn(table);
                             }
-                            table.setCellAlignment(TableAppender.ALIGN_RIGHT);
+                            table.setCellAlignment(alignment);
                         }
                         /*
                          * Format the value followed by the unit of measure, or followed by spaces if there is no unit

Modified: sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/parameter/Parameters.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/parameter/Parameters.java?rev=1803070&r1=1803069&r2=1803070&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/parameter/Parameters.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/parameter/Parameters.java [UTF-8] Wed Jul 26 16:14:09 2017
@@ -37,15 +37,13 @@ import org.apache.sis.util.ObjectConvert
 import org.apache.sis.util.resources.Errors;
 import org.apache.sis.util.Debug;
 
-import static org.apache.sis.referencing.IdentifiedObjects.isHeuristicMatchForName;
-
 
 /**
  * Convenience methods for fetching parameter values despite the variations in parameter names, value types and units.
  * See {@link DefaultParameterValueGroup} javadoc for a description of the standard way to get and set a particular
  * parameter in a group. The remaining of this javadoc is specific to Apache SIS.
  *
- * <div class="section">Convenience static methods</div>
+ * <div class="section">Convenience methods</div>
  * This class provides the following convenience static methods:
  * <ul>
  *   <li>{@link #cast(ParameterValue, Class) cast(…, Class)} for type safety with parameterized types.</li>
@@ -54,32 +52,8 @@ import static org.apache.sis.referencing
  *   <li>{@link #copy(ParameterValueGroup, ParameterValueGroup)} for copying values into an existing instance.</li>
  * </ul>
  *
- *
- * <div class="section">Fetching parameter values despite different names, types or units</div>
- * The common way to get a parameter is to invoke the {@link #parameter(String)} method.
- * This {@code Parameters} class provides an alternative way, using a {@link ParameterDescriptor} argument
- * instead than a {@code String}. The methods in this class use the additional information provided by the
- * descriptor for choosing a {@code String} argument that the above-cited {@code parameter(String)} method
- * is more likely to know (by giving preference to a {@linkplain DefaultParameterDescriptor#getName() name}
- * or {@linkplain DefaultParameterDescriptor#getAlias() alias} defined by a common
- * {@linkplain org.apache.sis.metadata.iso.ImmutableIdentifier#getAuthority() authority}),
- * and for applying type and unit conversions.
- *
- * <div class="note"><b>Example:</b>
- * The same parameter may be known under different names. For example the
- * {@linkplain org.apache.sis.referencing.datum.DefaultEllipsoid#getSemiMajorAxis()
- * length of the semi-major axis of the ellipsoid} is commonly known as {@code "semi_major"}.
- * But that parameter can also be named {@code "semi_major_axis"}, {@code "earth_radius"} or simply {@code "a"}
- * in other libraries. When fetching parameter values, we do not always know in advance which of the above-cited
- * names is recognized by an arbitrary {@code ParameterValueGroup} implementation.
- *
- * <p>This uncertainty is mitigated with the Apache SIS implementation since
- * {@link DefaultParameterValueGroup#parameter(String)} compares the given {@code String} argument
- * against all parameter's {@linkplain DefaultParameterDescriptor#getAlias() aliases} in addition
- * to the {@linkplain DefaultParameterDescriptor#getName() name}.
- * However we do not have the guarantee that all implementations do that.</p></div>
- *
- * The method names in this class follow the names of methods provided by the {@link ParameterValue} interface.
+ * Most instance methods in this class follow the same naming pattern
+ * than the methods provided by the {@link ParameterValue} interface.
  * Those methods are themselves inspired by JDK methods:
  *
  * <table class="sis">
@@ -95,21 +69,38 @@ import static org.apache.sis.referencing
  * </table>
  *
  *
+ * <div class="section">Fetching parameter values despite different names, types or units</div>
+ * The common way to get a parameter is to invoke the {@link #parameter(String)} method.
+ * This {@code Parameters} class provides alternative ways, using a {@link ParameterDescriptor} argument
+ * instead than a {@code String} argument. Those descriptors provide additional information like the various
+ * {@linkplain DefaultParameterDescriptor#getAlias() aliases} under which the same parameter may be known.
+ * By using this information, {@code Parameters} can choose the most appropriate parameter name or alias
+ * (by searching for a common {@linkplain org.apache.sis.metadata.iso.ImmutableIdentifier#getAuthority() authority})
+ * when it delegates its work to the {@code parameter(String)} method.
+ *
+ * <div class="note"><b>Example:</b>
+ * The same parameter may be known under different names. For example the
+ * {@linkplain org.apache.sis.referencing.datum.DefaultEllipsoid#getSemiMajorAxis()
+ * length of the semi-major axis of the ellipsoid} is commonly known as {@code "semi_major"}.
+ * But that parameter can also be named {@code "semi_major_axis"}, {@code "earth_radius"} or simply {@code "a"}
+ * in other libraries. When fetching parameter values, we do not always know in advance which of the above-cited
+ * names is recognized by an arbitrary {@code ParameterValueGroup} instance.</div>
+ *
+ * {@code Parameters} uses also the descriptor information for applying type and unit conversions
+ * (i.e. returned values are converted to the units of measurement specified by the given parameter descriptor).
+ *
+ *
  * <div class="section">Note for subclass implementors</div>
+ * This class does not implement any method from the {@link ParameterValueGroup} interface
+ * (this class is not named “{@code AbstractParameterValueGroup}” for that reason).
+ * Extending this class or extending {@link Object} make almost no difference for implementors;
+ * {@code Parameters} purpose is mostly to extend the API for users convenience.
  * All methods in this class get their information from the {@link ParameterValueGroup} methods.
- * In addition, each method in this class is isolated from all others: overriding one method has
- * no impact on other methods.
- *
- * <div class="note"><b>Note on this class name:</b>
- * Despite implementing the {@link ParameterValueGroup} interface, this class is not named
- * {@code AbstractParameterValueGroup} because it does not implement any method from the interface.
- * Extending this class or extending {@link Object} make almost no difference for implementors.
- * The intend of this {@code Parameters} class is rather to extend the API with methods
- * that are convenient for the way Apache SIS uses parameters.
- * In other words, this class is intended for users rather than implementors.</div>
+ * In addition, unless otherwise specified, methods in this class is isolated from all others:
+ * overriding one method has no impact on other methods.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 0.7
+ * @version 0.8
  * @since   0.4
  * @module
  */
@@ -347,13 +338,14 @@ public abstract class Parameters impleme
      * If no name or alias for this group's authority can be found, then the primary name will be returned.
      *
      * @param  source  the parameter for which the name is wanted.
-     * @return the name of the given parameter.
+     * @return the name of the given parameter. May be {@code null} if there is no name at all,
+     *         but such nameless descriptors are not legal.
      */
     private String getName(final GeneralParameterDescriptor source) {
         final ParameterDescriptorGroup descriptor = getDescriptor();
-        if (descriptor != null) {   // Paranoiac check (should never be null)
+        if (descriptor != null) {                                   // Paranoiac check (should never be null)
             final Identifier group = descriptor.getName();
-            if (group != null) {    // Paranoiac check (should never be null)
+            if (group != null) {                                    // Paranoiac check (should never be null)
                 final Citation authority = group.getAuthority();
                 final String name = IdentifiedObjects.getName(source, authority);
                 if (name != null || authority == null) {
@@ -372,30 +364,52 @@ public abstract class Parameters impleme
      */
     @SuppressWarnings("null")
     ParameterValue<?> parameterIfExist(final String name) throws ParameterNotFoundException {
-        ParameterValue<?> fallback  = null;
+        int i1 = 0, i2 = 0;
+        ParameterValue<?> first     = null;
         ParameterValue<?> ambiguity = null;
-        for (final GeneralParameterValue value : values()) {
+        final List<GeneralParameterValue> values = values();
+        final int size = values.size();
+        for (int i=0; i<size; i++) {
+            final GeneralParameterValue value = values.get(i);
             if (value instanceof ParameterValue<?>) {
                 final ParameterValue<?> param = (ParameterValue<?>) value;
-                final ParameterDescriptor<?> descriptor = param.getDescriptor();
-                if (name.equals(descriptor.getName().toString())) {
-                    return param;
-                }
-                if (isHeuristicMatchForName(descriptor, name)) {
-                    if (fallback == null) {
-                        fallback = param;
+                if (IdentifiedObjects.isHeuristicMatchForName(param.getDescriptor(), name)) {
+                    if (first == null) {
+                        first = param;
+                        i1 = i;
                     } else {
                         ambiguity = param;
+                        i2 = i;
                     }
                 }
             }
         }
-        if (ambiguity != null) {
-            throw new ParameterNotFoundException(Errors.format(Errors.Keys.AmbiguousName_3,
-                    IdentifiedObjects.toString(fallback .getDescriptor().getName()),
-                    IdentifiedObjects.toString(ambiguity.getDescriptor().getName()), name), name);
+        /*
+         * If there is no ambiguity, we are done. In case of ambiguity we should throw an exception.
+         * However we will not throw the exception if this method is invoked from the getParameter(…)
+         * method of a Parameters instance wrapping a non-SIS implementation. The reason is that for
+         * foreigner implementations, the package-private getParameter(…) method will conservatively
+         * delegate to the public parameter(…) method, in case the implementor overrides it. But for
+         * Apache SIS implementations in this package, we rely on the exception being thrown.
+         *
+         * Note that all classes in this package except UnmodifiableParameterValueGroup override this
+         * method in a way that unconditionally throw the exception.  UnmodifiableParameterValueGroup
+         * is the class that needs the exception to be thrown.
+         */
+        if (ambiguity == null || !isKnownImplementation()) {
+            return first;
+        }
+        final GeneralParameterDescriptor d1 = first    .getDescriptor();
+        final GeneralParameterDescriptor d2 = ambiguity.getDescriptor();
+        final String message;
+        if (d1 == d2) {
+            message = Errors.format(Errors.Keys.MultiOccurenceValueAtIndices_3, name, i1, i2);
+        } else {
+            message = Errors.format(Errors.Keys.AmbiguousName_3,
+                        IdentifiedObjects.toString(d1.getName()),
+                        IdentifiedObjects.toString(d2.getName()), name);
         }
-        return fallback;
+        throw new ParameterNotFoundException(message, name);
     }
 
     /**
@@ -474,6 +488,8 @@ public abstract class Parameters impleme
      *         default value} otherwise (which may be {@code null}).
      * @throws ParameterNotFoundException if the given {@code parameter} name or alias is not legal for this group.
      *
+     * @see #getMandatoryValue(ParameterDescriptor)
+     * @see #getOrCreate(ParameterDescriptor)
      * @see DefaultParameterValueGroup#parameter(String)
      * @see DefaultParameterValue#getValue()
      *
@@ -511,6 +527,9 @@ public abstract class Parameters impleme
      * @throws ParameterNotFoundException if the given {@code parameter} name or alias is not legal for this group.
      * @throws IllegalStateException if the value is not defined and there is no default value.
      *
+     * @see #getValue(ParameterDescriptor)
+     * @see #getOrCreate(ParameterDescriptor)
+     *
      * @since 0.7
      */
     public <T> T getMandatoryValue(final ParameterDescriptor<T> parameter) throws ParameterNotFoundException {
@@ -693,6 +712,8 @@ public abstract class Parameters impleme
      * @return the requested parameter instance.
      * @throws ParameterNotFoundException if the given {@code parameter} name or alias is not legal for this group.
      *
+     * @see #getValue(ParameterDescriptor)
+     * @see #getMandatoryValue(ParameterDescriptor)
      * @see DefaultParameterValueGroup#parameter(String)
      *
      * @since 0.6

Modified: sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/CRS.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/CRS.java?rev=1803070&r1=1803069&r2=1803070&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/CRS.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/CRS.java [UTF-8] Wed Jul 26 16:14:09 2017
@@ -20,6 +20,7 @@ import java.util.Map;
 import java.util.List;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.logging.LogRecord;
 import org.opengis.util.FactoryException;
 import org.opengis.geometry.Envelope;
 import org.opengis.referencing.NoSuchAuthorityCodeException;
@@ -55,8 +56,10 @@ import org.apache.sis.internal.metadata.
 import org.apache.sis.internal.referencing.PositionalAccuracyConstant;
 import org.apache.sis.internal.referencing.CoordinateOperations;
 import org.apache.sis.internal.referencing.ReferencingUtilities;
+import org.apache.sis.internal.referencing.DefinitionVerifier;
 import org.apache.sis.internal.system.DefaultFactories;
 import org.apache.sis.internal.system.Modules;
+import org.apache.sis.internal.system.Loggers;
 import org.apache.sis.referencing.cs.DefaultVerticalCS;
 import org.apache.sis.referencing.cs.DefaultEllipsoidalCS;
 import org.apache.sis.referencing.crs.DefaultGeographicCRS;
@@ -70,6 +73,7 @@ import org.apache.sis.metadata.iso.exten
 import org.apache.sis.metadata.iso.extent.Extents;
 import org.apache.sis.util.resources.Errors;
 import org.apache.sis.util.logging.Logging;
+import org.apache.sis.util.logging.WarningListener;
 import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.util.Utilities;
 import org.apache.sis.util.Static;
@@ -183,6 +187,8 @@ public final class CRS extends Static {
      *
      * Note that the {@link IdentifiedObjects#lookupURN(IdentifiedObject, Citation)}
      * method can be seen as a converse of this method.
+     * More codes may also be supported depending on which extension modules are available.
+     * See for example the {@linkplain org.apache.sis.storage.gdal bindings to Proj.4 library}.
      *
      * @param  code  the authority code.
      * @return the Coordinate Reference System for the given authority code.
@@ -232,6 +238,14 @@ public final class CRS extends Static {
      * }
      * </div>
      *
+     * If the parsing produced warnings, they will be reported in a logger named {@code "org.apache.sis.io.wkt"}.
+     * In particular, this method verifies if the description provided by the WKT matches the description provided
+     * by the authority ({@code "EPSG:5641"} in above example) and reports discrepancies.
+     * Note that this comparison between parsed CRS and authoritative CRS is specific to this convenience method;
+     * other APIs documented in <cite>see also</cite> section do not perform this comparison automatically.
+     * Should the WKT description and the authoritative description be in conflict, the WKT description prevails
+     * as mandated by ISO 19162 standard (see {@link #fromAuthority fromAuthority(…)} if a different behavior is needed).
+     *
      * <div class="section">Usage and performance considerations</div>
      * This convenience method delegates to
      * {@link org.apache.sis.referencing.factory.GeodeticObjectFactory#createFromWKT(String)}
@@ -250,7 +264,8 @@ public final class CRS extends Static {
      * @return the parsed Coordinate Reference System.
      * @throws FactoryException if the given WKT can not be parsed.
      *
-     * @see org.apache.sis.io.wkt
+     * @see org.apache.sis.io.wkt.WKTFormat
+     * @see org.apache.sis.referencing.factory.GeodeticObjectFactory#createFromWKT(String)
      * @see org.apache.sis.geometry.Envelopes#fromWKT(CharSequence)
      * @see <a href="http://docs.opengeospatial.org/is/12-063r5/12-063r5.html">WKT 2 specification</a>
      *
@@ -258,7 +273,9 @@ public final class CRS extends Static {
      */
     public static CoordinateReferenceSystem fromWKT(final String text) throws FactoryException {
         ArgumentChecks.ensureNonNull("text", text);
-        return DefaultFactories.forBuildin(CRSFactory.class).createFromWKT(text);
+        final CoordinateReferenceSystem crs = DefaultFactories.forBuildin(CRSFactory.class).createFromWKT(text);
+        DefinitionVerifier.withAuthority(crs, Loggers.WKT, CRS.class, "fromWKT");
+        return crs;
     }
 
     /**
@@ -267,17 +284,116 @@ public final class CRS extends Static {
      * For reading XML documents from readers or input streams,
      * see static methods in the {@link org.apache.sis.xml.XML} class.
      *
+     * <p>If the unmarshalling produced warnings, they will be reported in a logger named {@code "org.apache.sis.xml"}.
+     * In particular, this method verifies if the description provided by the XML matches the description provided by
+     * the authority code given in {@code <gml:identifier>} element, and reports discrepancies.
+     * Note that this comparison between unmarshalled CRS and authoritative CRS is specific to this convenience method;
+     * other APIs documented in <cite>see also</cite> section do not perform this comparison automatically.
+     * Should the XML description and the authoritative description be in conflict, the XML description prevails
+     * (see {@link #fromAuthority fromAuthority(…)} if a different behavior is needed).</p>
+     *
      * @param  xml  coordinate reference system encoded in XML format.
      * @return the unmarshalled Coordinate Reference System.
      * @throws FactoryException if the object creation failed.
      *
+     * @see org.apache.sis.referencing.factory.GeodeticObjectFactory#createFromXML(String)
      * @see org.apache.sis.xml.XML#unmarshal(String)
      *
      * @since 0.7
      */
     public static CoordinateReferenceSystem fromXML(final String xml) throws FactoryException {
         ArgumentChecks.ensureNonNull("text", xml);
-        return DefaultFactories.forBuildin(CRSFactory.class).createFromXML(xml);
+        final CoordinateReferenceSystem crs = DefaultFactories.forBuildin(CRSFactory.class).createFromXML(xml);
+        DefinitionVerifier.withAuthority(crs, Loggers.XML, CRS.class, "fromXML");
+        return crs;
+    }
+
+    /**
+     * Replaces the given coordinate reference system by an authoritative description, if one can be found.
+     * This method can be invoked after constructing a CRS in a context where the EPSG (or other authority)
+     * code is suspected more reliable than the rest of the description. A common case is a <cite>Well Known
+     * Text</cite> (WKT) string declaring wrong projection method or parameter values for the EPSG code that
+     * it pretends to describe. For example:
+     *
+     * <blockquote>
+     *   {@code PROJCS["WGS 84 / Pseudo-Mercator",}<br>
+     *   {@code   }(…base CRS omitted for brevity…)<br>
+     *   {@code   PROJECTION["Mercator (variant A)"],} — <em><b>wrong:</b> shall be "Popular Visualisation Pseudo Mercator"</em><br>
+     *   {@code   }(…parameters and axes omitted for brevity…)<br>
+     *   {@code   AUTHORITY["EPSG", "3857"]]}
+     * </blockquote>
+     *
+     * In such cases, Apache SIS behavior in {@link #fromWKT(String)}, {@link #fromXML(String)} and other methods is
+     * conform to the <a href="http://docs.opengeospatial.org/is/12-063r5/12-063r5.html">ISO 19162 specification</a>:
+     *
+     * <blockquote><cite>"Should any attributes or values given in the cited identifier be in conflict with attributes
+     * or values given explicitly in the WKT description, the WKT values shall prevail."</cite></blockquote>
+     *
+     * In situations where the opposite behavior is desired (i.e. to make the authority identifier prevails),
+     * this method can be invoked. This method performs the following actions:
+     *
+     * <ul>
+     *   <li>If the given CRS has an {@linkplain AbstractIdentifiedObject#getIdentifiers() identifier} and if the authority factory can
+     *     {@linkplain org.apache.sis.referencing.factory.GeodeticAuthorityFactory#createCoordinateReferenceSystem(String) create a CRS}
+     *     for that identifier, then:
+     *     <ul>
+     *       <li>If the CRS defined by the authority is {@linkplain Utilities#equalsIgnoreMetadata equal, ignoring metadata},
+     *         to the given CRS, then this method returns silently the <em>authoritative</em> CRS.</li>
+     *       <li>Otherwise if the CRS defined by the authority is equal, ignoring axis order and units, to the given CRS,
+     *         then this method returns a <em>new</em> CRS derived from the authoritative one but with same
+     *         {@linkplain org.apache.sis.referencing.cs.AxesConvention axes convention} than the given CRS.
+     *         A warning is emitted.</li>
+     *       <li>Otherwise this method discards the given CRS and returns the <em>authoritative</em> CRS.
+     *         A warning is emitted with a message indicating where a difference has been found.</li>
+     *     </ul>
+     *   </li>
+     *   <li>Otherwise if the given CRS does not have identifier, then this method
+     *       {@linkplain org.apache.sis.referencing.factory.IdentifiedObjectFinder searches for an equivalent CRS}
+     *       defined by the authority factory. If such CRS is found, then:
+     *     <ul>
+     *       <li>If the CRS defined by the authority is {@linkplain Utilities#equalsIgnoreMetadata equal, ignoring metadata},
+     *         to the given CRS, then this method returns silently the <em>authoritative</em> CRS.</li>
+     *       <li>Otherwise if the CRS defined by the authority is equal, ignoring axis order and units, to the given CRS,
+     *         then this method returns silently a <em>new</em> CRS derived from the authoritative one but with same
+     *         {@linkplain org.apache.sis.referencing.cs.AxesConvention axes convention} than the given CRS.</li>
+     *     </ul>
+     *   </li>
+     *   <li>Otherwise this method silently returns the given CRS as-is.</li>
+     * </ul>
+     *
+     * <b>Note:</b> the warnings emitted by this method are redundant with the warnings emitted by
+     * {@link #fromWKT(String)} and {@link #fromXML(String)}, so the {@code warnings} argument should be {@code null}
+     * when {@code fromAuthority(…)} is invoked for the CRS parsed by one of above-mentioned methods.
+     * A non-null {@code warnings} argument is more useful for CRS parsed by {@link org.apache.sis.io.wkt.WKTFormat}
+     * or {@link org.apache.sis.xml.XML#unmarshal(String)} for instance.
+     *
+     * @param  crs       the CRS to replace by an authoritative CRS, or {@code null}.
+     * @param  factory   the factory where to search for authoritative definitions, or {@code null} for the default.
+     * @param  listener  where to send warnings, or {@code null} for ignoring warnings.
+     * @return the suggested CRS to use (may be the {@code crs} argument itself), or {@code null} if the given CRS was null.
+     * @throws FactoryException if an error occurred while querying the authority factory.
+     *
+     * @since 0.8
+     */
+    public static CoordinateReferenceSystem fromAuthority(CoordinateReferenceSystem crs,
+            final CRSAuthorityFactory factory, final WarningListener<?> listener) throws FactoryException
+    {
+        if (crs != null) {
+            final DefinitionVerifier verification = DefinitionVerifier.withAuthority(crs, factory, true);
+            if (verification != null) {
+                crs = verification.authoritative;
+                if (listener != null) {
+                    final LogRecord record = verification.warning(false);
+                    if (record != null) {
+                        record.setLoggerName(Modules.REFERENCING);
+                        record.setSourceClassName(CRS.class.getName());
+                        record.setSourceMethodName("fromAuthority");
+                        listener.warningOccured(null, record);
+                    }
+                }
+            }
+        }
+        return crs;
     }
 
     /**
@@ -293,7 +409,7 @@ public final class CRS extends Static {
      * they need to be {@linkplain Envelopes#transform(Envelope, CoordinateReferenceSystem) transformed} in the same CRS.
      * However if one CRS is a Transverse Mercator projection while the other CRS is a world-wide geographic CRS, then
      * attempts to use the Transverse Mercator projection as the common CRS is likely to fail since the geographic envelope
-     * may span an area far outside the projection domain of validity. This {@code suggestTargetCRS(…)} method can used
+     * may span an area far outside the projection domain of validity. This {@code suggestCommonTarget(…)} method can used
      * for choosing a common CRS which is less likely to fail.</div>
      *
      * @param  regionOfInterest  the geographic area for which the coordinate operations will be applied,
@@ -305,8 +421,8 @@ public final class CRS extends Static {
      *
      * @since 0.8
      */
-    public static CoordinateReferenceSystem suggestTargetCRS(GeographicBoundingBox regionOfInterest,
-                                                             CoordinateReferenceSystem... sourceCRS)
+    public static CoordinateReferenceSystem suggestCommonTarget(GeographicBoundingBox regionOfInterest,
+                                                                CoordinateReferenceSystem... sourceCRS)
     {
         CoordinateReferenceSystem bestCRS = null;
         /*

Modified: sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/CommonCRS.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/CommonCRS.java?rev=1803070&r1=1803069&r2=1803070&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/CommonCRS.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/CommonCRS.java [UTF-8] Wed Jul 26 16:14:09 2017
@@ -1503,7 +1503,17 @@ public enum CommonCRS {
      */
     public enum Temporal {
         /**
-         * Time measured as days since January 1st, 4713 BC at 12:00 UTC.
+         * Time measured as days since January 1st, 4713 BC at 12:00 UTC in proleptic Julian calendar.
+         * This epoch is equivalent to November 24, 4714 BC when expressed in the proleptic Gregorian
+         * calendar instead than the Julian one.
+         *
+         * <p><b>Note on dates formatting:</b>
+         * the legacy date/time formatting classes in the {@link java.text} package uses the proleptic
+         * Julian calendar for dates before October 15, 1582, while the new date/time formatting classes
+         * in the {@link java.time.format} package use the ISO-8601 calendar system, which is equivalent
+         * to the proleptic Gregorian calendar for every dates. For parsing and formatting of Julian days,
+         * the {@link java.text.SimpleDateFormat} class is closer to the common practice (but not ISO 8601
+         * compliant).</p>
          */
         JULIAN(Vocabulary.Keys.Julian, -2440588L * MILLISECONDS_PER_DAY + MILLISECONDS_PER_DAY/2),
 

Modified: sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/ConcurrentAuthorityFactory.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/ConcurrentAuthorityFactory.java?rev=1803070&r1=1803069&r2=1803070&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/ConcurrentAuthorityFactory.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/ConcurrentAuthorityFactory.java [UTF-8] Wed Jul 26 16:14:09 2017
@@ -56,6 +56,7 @@ import org.apache.sis.internal.system.De
 import org.apache.sis.internal.system.Shutdown;
 import org.apache.sis.internal.system.Loggers;
 import org.apache.sis.internal.util.CollectionsExt;
+import org.apache.sis.internal.util.StandardDateFormat;
 import org.apache.sis.util.logging.PerformanceLevel;
 import org.apache.sis.util.resources.Errors;
 import org.apache.sis.util.resources.Messages;
@@ -92,7 +93,7 @@ import org.apache.sis.util.resources.Mes
  * Subclasses should select the interfaces that they choose to implement.
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
- * @version 0.7
+ * @version 0.8
  *
  * @param <DAO>  the type of factory used as Data Access Object (DAO).
  *
@@ -189,7 +190,7 @@ public abstract class ConcurrentAuthorit
                 value = depth;
             } else {
                 text = "%s made available %d seconds ago";
-                value = Math.round((System.nanoTime() - timestamp) / 1E+9);   // Convert nanoseconds to seconds.
+                value = Math.round((System.nanoTime() - timestamp) / (double) StandardDateFormat.NANOS_PER_SECOND);
             }
             return String.format(text, Classes.getShortClassName(factory), value);
         }
@@ -447,8 +448,8 @@ public abstract class ConcurrentAuthorit
                 if (caller == null) {
                     caller = "create".concat(type.getSimpleName());
                 }
-                final Double duration = time / 1E+9;
                 final PerformanceLevel level = PerformanceLevel.forDuration(time, TimeUnit.NANOSECONDS);
+                final Double duration = time / (double) StandardDateFormat.NANOS_PER_SECOND;
                 final Messages resources = Messages.getResources(null);
                 final LogRecord record;
                 if (code != null) {
@@ -640,6 +641,8 @@ public abstract class ConcurrentAuthorit
      *
      * @param  factory  the Data Access Object which is about to be closed.
      * @return {@code true} if the given Data Access Object can be closed.
+     *
+     * @see #close()
      */
     protected boolean canClose(DAO factory) {
         return true;
@@ -1632,9 +1635,9 @@ public abstract class ConcurrentAuthorit
      * creation is delegated to the {@linkplain #getDataAccess() Data Access Object}.
      * The result is then stored in the cache and returned.
      *
-     * @param  <T>   The type of the object to be returned.
-     * @param  proxy The proxy to use for creating the object.
-     * @param  code  The code of the object to create.
+     * @param  <T>    the type of the object to be returned.
+     * @param  proxy  the proxy to use for creating the object.
+     * @param  code   the code of the object to create.
      * @return the object extracted from the cache or created.
      * @throws FactoryException if an error occurred while creating the object.
      */
@@ -1655,7 +1658,9 @@ public abstract class ConcurrentAuthorit
                     } finally {
                         release(null, type, code);
                     }
-                    value = result;                                     // For the finally block below.
+                    if (isCacheable(code, result)) {
+                        value = result;                                 // For the finally block below.
+                    }
                     return result;
                 }
             } finally {
@@ -1935,11 +1940,36 @@ public abstract class ConcurrentAuthorit
     }
 
     /**
+     * Returns whether the given object can be cached. This method is invoked after the
+     * {@linkplain #newDataAccess() Data Access Object} created a new object not previously in the cache.
+     * If this {@code isCacheable(…)} method returns {@code true}, then the newly created object will be cached so
+     * that next calls to the same {@code createFoo(String)} method with the same code may return the same object.
+     * If this method returns {@code false}, then the newly created object will not be cached and next call to
+     * the {@code createFoo(String)} method with the same code will return a new object.
+     *
+     * <p>The default implementation always returns {@code true}.
+     * Subclasses can override this method for filtering the objects to store in the cache.</p>
+     *
+     * @param  code    the authority code specified by the caller for creating an object.
+     * @param  object  the object created for the given authority code.
+     * @return whether the given object should be cached.
+     *
+     * @see #printCacheContent(PrintWriter)
+     *
+     * @since 0.8
+     */
+    protected boolean isCacheable(String code, Object object) {
+        return true;
+    }
+
+    /**
      * Prints the cache content to the given writer.
      * Keys are sorted by numerical order if possible, or alphabetical order otherwise.
      * This method is used for debugging purpose only.
      *
      * @param  out  the output printer, or {@code null} for the {@linkplain System#out standard output stream}.
+     *
+     * @see #isCacheable(String, Object)
      */
     @Debug
     public void printCacheContent(final PrintWriter out) {
@@ -2057,6 +2087,8 @@ public abstract class ConcurrentAuthorit
      * depending which event happen first.</p>
      *
      * @throws FactoryException if an error occurred while closing the Data Access Objects.
+     *
+     * @see #canClose(GeodeticAuthorityFactory)
      */
     @Override
     public void close() throws FactoryException {
@@ -2081,6 +2113,8 @@ public abstract class ConcurrentAuthorit
      * The string returned by this method may change in any future SIS version.
      *
      * @return a string representation for debugging purpose.
+     *
+     * @see #printCacheContent(PrintWriter)
      */
     @Debug
     @Override

Modified: sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/GeodeticAuthorityFactory.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/GeodeticAuthorityFactory.java?rev=1803070&r1=1803069&r2=1803070&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/GeodeticAuthorityFactory.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/GeodeticAuthorityFactory.java [UTF-8] Wed Jul 26 16:14:09 2017
@@ -63,7 +63,7 @@ import org.apache.sis.util.Debug;
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
  * @author  Johann Sorel (Geomatys)
- * @version 0.7
+ * @version 0.8
  * @since   0.7
  * @module
  */
@@ -1237,8 +1237,10 @@ public abstract class GeodeticAuthorityF
      * @param  code  the code to trim.
      * @return the code with the namespace part removed if that part matched one of the values given by
      *         {@link #getCodeSpaces()}.
+     *
+     * @since 0.8
      */
-    final String trimNamespace(final String code) {
+    protected final String trimNamespace(final String code) {
         int s = code.indexOf(DefaultNameSpace.DEFAULT_SEPARATOR);
         if (s >= 0) {
             final int end   = CharSequences.skipTrailingWhitespaces(code, 0, s);

Modified: sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/GeodeticObjectFactory.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/GeodeticObjectFactory.java?rev=1803070&r1=1803069&r2=1803070&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/GeodeticObjectFactory.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/GeodeticObjectFactory.java [UTF-8] Wed Jul 26 16:14:09 2017
@@ -62,6 +62,7 @@ import org.apache.sis.util.resources.Err
 import org.apache.sis.util.logging.Logging;
 import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.io.wkt.Parser;
+import org.apache.sis.util.Exceptions;
 import org.apache.sis.xml.XML;
 
 
@@ -1557,7 +1558,26 @@ public class GeodeticObjectFactory exten
         try {
             object = XML.unmarshal(xml);
         } catch (JAXBException e) {
-            throw new FactoryException(e.getLocalizedMessage(), e);
+            /*
+             * The JAXB exception if often a wrapper around other exceptions, sometime InvocationTargetException.
+             * The exception cause is called "linked exception" by JAXB, presumably because it predates standard
+             * chained exception mechanism introduced in Java 1.4. The JAXB linked exceptions do not propagate the
+             * error message, so we have to take it from the cause, skipping InvocationTargetException since they
+             * are wrapper for other causes. If the cause is a JAXBException, we will keep it as the declared cause
+             * for simplifying the stack trace.
+             */
+            String message = e.getLocalizedMessage();
+            Throwable cause = e.getCause();
+            if (cause instanceof Exception) {
+                cause = Exceptions.unwrap((Exception) cause);
+                if (cause instanceof JAXBException) {
+                    e = (JAXBException) cause;
+                }
+                if (message == null) {
+                    message = cause.getLocalizedMessage();
+                }
+            }
+            throw new FactoryException(message, e);
         }
         if (object instanceof CoordinateReferenceSystem) {
             return (CoordinateReferenceSystem) object;
@@ -1594,6 +1614,13 @@ public class GeodeticObjectFactory exten
      * }
      * </div>
      *
+     * If the given text contains non-fatal anomalies
+     * (unknown or unsupported WKT elements, inconsistent unit definitions, unparsable axis abbreviations, <i>etc.</i>),
+     * warnings may be reported in a {@linkplain java.util.logging.Logger logger} named {@code "org.apache.sis.io.wkt"}.
+     * However this parser does not verify if the overall parsed object matches the EPSG (or other authority) definition,
+     * since this geodetic object factory is not an {@linkplain GeodeticAuthorityFactory authority factory}.
+     * For such verification, see the {@link org.apache.sis.referencing.CRS#fromWKT(String)} convenience method.
+     *
      * <div class="section">Usage and performance considerations</div>
      * The default implementation uses a shared instance of {@link org.apache.sis.io.wkt.WKTFormat}
      * with the addition of thread-safety. This is okay for occasional use,

Modified: sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/IdentifiedObjectSet.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/IdentifiedObjectSet.java?rev=1803070&r1=1803069&r2=1803070&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/IdentifiedObjectSet.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/IdentifiedObjectSet.java [UTF-8] Wed Jul 26 16:14:09 2017
@@ -315,12 +315,10 @@ public class IdentifiedObjectSet<T exten
                      * object but we do not put it in this IdentifiedObjectSet. This behavior is as if this method
                      * has been invoked before the concurrent removal happened.
                      */
-                    if (objects.containsKey(code)) {
+                    if (objects.containsKey(code)) {        // Needed because code may be associated to null value.
                         final T c = objects.putIfAbsent(code, object);
                         if (c != null) {
                             object = c;                     // The object has been created concurrently.
-                        } else {
-                            codes = null;
                         }
                     }
                 } else if (objects.remove(code, null)) {    // Do not remove if a concurrent thread succeeded.

Modified: sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/MissingFactoryResourceException.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/MissingFactoryResourceException.java?rev=1803070&r1=1803069&r2=1803070&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/MissingFactoryResourceException.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/MissingFactoryResourceException.java [UTF-8] Wed Jul 26 16:14:09 2017
@@ -20,7 +20,7 @@ import org.opengis.util.FactoryException
 
 
 /**
- * Thrown when an object can not be created because a resource is missing.
+ * Thrown when a particular object can not be created because a resource is missing.
  * The most common case is a NADCON or NTv2 datum shift operation requested while the
  * datum shift grids was not found in the {@code $SIS_DATA/DatumChanges} directory.
  *

Modified: sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/MultiAuthoritiesFactory.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/MultiAuthoritiesFactory.java?rev=1803070&r1=1803069&r2=1803070&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/MultiAuthoritiesFactory.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/MultiAuthoritiesFactory.java [UTF-8] Wed Jul 26 16:14:09 2017
@@ -778,6 +778,9 @@ public class MultiAuthoritiesFactory ext
             end = CharSequences.skipTrailingWhitespaces(code, start, afterVersion);
             version = (start < end && !code.regionMatches(start, DefinitionURI.NO_VERSION, 0,
                     DefinitionURI.NO_VERSION.length())) ? code.substring(start, end) : null;
+            if (version != null && !Character.isUnicodeIdentifierPart(version.codePointAt(0))) {
+                throw new NoSuchAuthorityCodeException(Errors.format(Errors.Keys.InvalidVersionIdentifier_1, version), authority, code);
+            }
             /*
              * Separate the code from the authority and the version.
              */

Modified: sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/UnavailableFactoryException.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/UnavailableFactoryException.java?rev=1803070&r1=1803069&r2=1803070&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/UnavailableFactoryException.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/UnavailableFactoryException.java [UTF-8] Wed Jul 26 16:14:09 2017
@@ -20,7 +20,7 @@ import org.opengis.referencing.Authority
 
 
 /**
- * Thrown when a factory can not be created because a resource is missing.
+ * Thrown when a whole factory can not be created because a resource is missing.
  * The most common case is when the {@link org.apache.sis.referencing.factory.sql.EPSGFactory}
  * has no connection to an EPSG database.
  *

Modified: sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/AuthorityCodes.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/AuthorityCodes.java?rev=1803070&r1=1803069&r2=1803070&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/AuthorityCodes.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/AuthorityCodes.java [UTF-8] Wed Jul 26 16:14:09 2017
@@ -51,13 +51,9 @@ import org.apache.sis.util.Debug;
  * @since   0.7
  * @module
  */
+@SuppressWarnings("serial")   // serialVersionUID not needed because of writeReplace().
 final class AuthorityCodes extends AbstractMap<String,String> implements Serializable {
     /**
-     * For compatibility with different versions.
-     */
-    private static final long serialVersionUID = 6118171679321975503L;
-
-    /**
      * Highest code value (inclusive) that this {@code AuthorityCodes} support during iterations.
      * This is based on the upper value of the highest range of codes once used by EPSG.
      */

Modified: sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/CoordinateOperationSet.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/CoordinateOperationSet.java?rev=1803070&r1=1803069&r2=1803070&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/CoordinateOperationSet.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/CoordinateOperationSet.java [UTF-8] Wed Jul 26 16:14:09 2017
@@ -21,7 +21,9 @@ import java.util.HashMap;
 import org.opengis.metadata.Identifier;
 import org.opengis.util.FactoryException;
 import org.opengis.referencing.AuthorityFactory;
+import org.opengis.referencing.crs.GeneralDerivedCRS;
 import org.opengis.referencing.crs.CRSAuthorityFactory;
+import org.opengis.referencing.crs.CoordinateReferenceSystem;
 import org.opengis.referencing.operation.CoordinateOperation;
 import org.opengis.referencing.operation.CoordinateOperationAuthorityFactory;
 import org.apache.sis.metadata.iso.citation.Citations;
@@ -32,9 +34,31 @@ import org.apache.sis.referencing.factor
 /**
  * A lazy set of {@link CoordinateOperation} objects to be returned by the
  * {@link EPSGDataAccess#createFromCoordinateReferenceSystemCodes(String, String)} method.
+ * There is two different ways in which {@link EPSGDataAccess} get coordinate operations:
  *
- * @author  Martin Desruisseaux (IRD)
- * @version 0.7
+ * <ol>
+ *   <li>The coordinate operation may be the <cite>conversion from base</cite> property of a projected CRS.
+ *       Those conversions are obtained by a SQL query like below (note that this query can return at most
+ *       one result, because {@code COORD_REF_SYS_CODE} is a primary key):
+ *
+ *       {@preformat sql
+ *         SELECT PROJECTION_CONV_CODE FROM "Coordinate Reference System" WHERE SOURCE_GEOGCRS_CODE = ? AND COORD_REF_SYS_CODE = ?
+ *       }
+ *   </li>
+ *
+ *   <li>The coordinate operation may be standalone. This is the case of coordinate transformations having stochastic errors.
+ *       Those transformations are obtained by a SQL query like below (note that this query can return many results):
+ *
+ *       {@preformat sql
+ *         SELECT COORD_OP_CODE FROM "Coordinate_Operation" … WHERE … AND SOURCE_CRS_CODE = ? AND TARGET_CRS_CODE = ?
+ *       }
+ *   </li>
+ * </ol>
+ *
+ * We distinguish those two cases by the presence or absence of a coordinate operation code in the {@link #projections} map.
+ *
+ * @author  Martin Desruisseaux (IRD, Geomatys)
+ * @version 0.8
  * @since   0.7
  * @module
  */
@@ -42,6 +66,15 @@ final class CoordinateOperationSet exten
     /**
      * The codes of {@link org.opengis.referencing.crs.ProjectedCRS} objects for
      * the specified {@link org.opengis.referencing.operation.Conversion} codes.
+     *
+     * <ul>
+     *   <li>Keys a coordinate operation codes.</li>
+     *   <li>Values are coordinate reference system codes. They are usually {@code ProjectedCRS},
+     *       but the EPSG database sometime use this mechanisms for other kind of CRS.</li>
+     * </ul>
+     *
+     * This map does <strong>not</strong> contain all operations to be returned by this {@code CoordinateOperationSet},
+     * but only the ones to be returned by the first SQL query documented in the class Javadoc.
      */
     private final Map<String,Integer> projections;
 
@@ -77,15 +110,40 @@ final class CoordinateOperationSet exten
     }
 
     /**
-     * Creates an object for the specified code.
+     * Creates a coordinate operation for the specified EPSG code.
      */
     @Override
     protected CoordinateOperation createObject(final String code) throws FactoryException {
-        final Integer crs = projections.get(code);
-        if (crs != null) {
-            return ((CRSAuthorityFactory) factory).createProjectedCRS(String.valueOf(crs)).getConversionFromBase();
-        } else {
-            return ((CoordinateOperationAuthorityFactory) factory).createCoordinateOperation(code);
+        final Integer base = projections.get(code);
+        if (base != null) {
+            /*
+             * First case documented in class Javadoc:
+             *
+             *     SELECT PROJECTION_CONV_CODE FROM "Coordinate Reference System" …
+             *
+             * The result is usually a ProjectedCRS, but not always.
+             */
+            CoordinateReferenceSystem crs;
+            crs = ((CRSAuthorityFactory) factory).createCoordinateReferenceSystem(String.valueOf(base));
+            if (crs instanceof GeneralDerivedCRS) {
+                return ((GeneralDerivedCRS) crs).getConversionFromBase();
+            }
         }
+        /*
+         * Following line is either for the second case documented in class Javadoc, or the first case
+         * when the result is not a derived CRS. Note that we could create a derived CRS here as below:
+         *
+         *     CoordinateOperation op = …,
+         *     if (crs != null && op instanceof Conversion) {
+         *         return DefaultDerivedCRS.create(IdentifiedObjects.getProperties(crs), baseCRS,
+         *                 (Conversion) op, crs.getCoordinateSystem()).getConversionFromBase();
+         *     }
+         *
+         * We don't do that for now because because EPSGDataAccess.createCoordinateReferenceSystem(String)
+         * would be a better place, by generalizing the work done for ProjectedCRS.
+         *
+         * https://issues.apache.org/jira/browse/SIS-357
+         */
+        return ((CoordinateOperationAuthorityFactory) factory).createCoordinateOperation(code);
     }
 }

Modified: sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGDataAccess.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGDataAccess.java?rev=1803070&r1=1803069&r2=1803070&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGDataAccess.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGDataAccess.java [UTF-8] Wed Jul 26 16:14:09 2017
@@ -71,6 +71,7 @@ import org.apache.sis.internal.metadata.
 import org.apache.sis.internal.metadata.TransformationAccuracy;
 import org.apache.sis.internal.metadata.WKTKeywords;
 import org.apache.sis.internal.metadata.sql.SQLUtilities;
+import org.apache.sis.internal.referencing.DeferredCoordinateOperation;
 import org.apache.sis.internal.referencing.DeprecatedCode;
 import org.apache.sis.internal.referencing.EPSGParameterDomain;
 import org.apache.sis.internal.referencing.ReferencingUtilities;
@@ -1615,11 +1616,29 @@ addURIs:    for (int i=0; ; i++) {
                 if (anchor != null) {
                     properties.put(Datum.ANCHOR_POINT_KEY, anchor);
                 }
-                if (epoch != null && !epoch.isEmpty()) try {
-                    final int year = Integer.parseInt(epoch);
-                    final Calendar calendar = getCalendar();
-                    calendar.set(year, 0, 1);
-                    properties.put(Datum.REALIZATION_EPOCH_KEY, calendar.getTime());
+                if (epoch != null) try {
+                    /*
+                     * Parse the date manually because it is declared as a VARCHAR instead than DATE in original
+                     * SQL scripts. Apache SIS installer replaces VARCHAR by DATE, but we have no guarantee that
+                     * we are reading an EPSG database created by our installer. Furthermore an older version of
+                     * EPSG installer was using SMALLINT instead than DATE, because scripts before EPSG 9.0 were
+                     * reporting only the epoch year.
+                     */
+                    final CharSequence[] fields = CharSequences.split(epoch, '-');
+                    int year = 0, month = 0, day = 1;
+                    for (int i = Math.min(fields.length, 3); --i >= 0;) {
+                        final int f = Integer.parseInt(fields[i].toString());
+                        switch (i) {
+                            case 0: year  = f;   break;
+                            case 1: month = f-1; break;
+                            case 2: day   = f;   break;
+                        }
+                    }
+                    if (year != 0) {
+                        final Calendar calendar = getCalendar();
+                        calendar.set(year, month, day);
+                        properties.put(Datum.REALIZATION_EPOCH_KEY, calendar.getTime());
+                    }
                 } catch (NumberFormatException exception) {
                     unexpectedException("createDatum", exception);          // Not a fatal error.
                 }
@@ -2822,12 +2841,10 @@ next:               while (r.next()) {
                      * (it was checked by getInteger(code, result, …) above in this method) but optional
                      * for concatenated operations. Fetching parameter values is part of this block.
                      */
-                    OperationMethod method;
-                    ParameterValueGroup parameters;
-                    if (methodCode == null) {
-                        method      = null;
-                        parameters  = null;
-                    } else {
+                    final boolean       isDeferred = Semaphores.query(Semaphores.METADATA_ONLY);
+                    ParameterValueGroup parameters = null;
+                    OperationMethod     method     = null;
+                    if (methodCode != null && !isDeferred) {
                         method = owner.createOperationMethod(methodCode.toString());
                         if (isDimensionKnown) {
                             method = DefaultOperationMethod.redimension(method, sourceDimensions, targetDimensions);
@@ -2858,7 +2875,9 @@ next:               while (r.next()) {
                      */
                     final CoordinateOperation operation;
                     final CoordinateOperationFactory copFactory = owner.copFactory;
-                    if (isConversion && (sourceCRS == null || targetCRS == null)) {
+                    if (isDeferred) {
+                        operation = new DeferredCoordinateOperation(opProperties, sourceCRS, targetCRS, owner);
+                    } else if (isConversion && (sourceCRS == null || targetCRS == null)) {
                         operation = copFactory.createDefiningConversion(opProperties, method, parameters);
                     } else if (isConcatenated) {
                         /*
@@ -3031,7 +3050,9 @@ next:               while (r.next()) {
          * Before to return the set, tests the creation of 1 object in order to report early (i.e. now)
          * any problems with SQL statements. Remaining operations will be created only when first needed.
          */
-        set.resolve(1);
+        if (!Semaphores.query(Semaphores.METADATA_ONLY)) {
+            set.resolve(1);
+        }
         return set;
     }
 

Modified: sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGFactory.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGFactory.java?rev=1803070&r1=1803069&r2=1803070&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGFactory.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGFactory.java [UTF-8] Wed Jul 26 16:14:09 2017
@@ -38,6 +38,7 @@ import org.opengis.referencing.operation
 import org.opengis.referencing.operation.CoordinateOperationAuthorityFactory;
 import org.opengis.referencing.operation.MathTransformFactory;
 import org.apache.sis.internal.metadata.sql.Initializer;
+import org.apache.sis.internal.referencing.DeferredCoordinateOperation;
 import org.apache.sis.internal.referencing.Resources;
 import org.apache.sis.internal.system.DefaultFactories;
 import org.apache.sis.internal.util.Constants;
@@ -459,7 +460,11 @@ public class EPSGFactory extends Concurr
             } catch (SQLException e2) {
                 e.addSuppressed(e2);
             }
-            exception = new UnavailableFactoryException(message(e), e);
+            /*
+             * Derby sometime wraps SQLException into another SQLException.  For making the stack strace a
+             * little bit simpler, keep only the root cause provided that the exception type is compatible.
+             */
+            exception = new UnavailableFactoryException(message(e), Exceptions.unwrap(e));
         }
         exception.setUnavailableFactory(this);
         throw exception;
@@ -503,4 +508,17 @@ public class EPSGFactory extends Concurr
     protected boolean canClose(final EPSGDataAccess factory) {
         return factory.canClose();
     }
+
+    /**
+     * Returns whether the given object can be cached.
+     * This method is invoked after {@link EPSGDataAccess} created a new object not previously in the cache.
+     *
+     * @return whether the given object should be cached.
+     *
+     * @since 0.8
+     */
+    @Override
+    protected boolean isCacheable(String code, Object object) {
+        return !(object instanceof DeferredCoordinateOperation);
+    }
 }

Modified: sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGInstaller.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGInstaller.java?rev=1803070&r1=1803069&r2=1803070&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGInstaller.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGInstaller.java [UTF-8] Wed Jul 26 16:14:09 2017
@@ -30,6 +30,7 @@ import org.apache.sis.util.StringBuilder
 import org.apache.sis.internal.metadata.sql.ScriptRunner;
 import org.apache.sis.internal.metadata.sql.SQLUtilities;
 import org.apache.sis.internal.system.DefaultFactories;
+import org.apache.sis.internal.util.StandardDateFormat;
 import org.apache.sis.internal.util.Fallback;
 import org.apache.sis.util.Exceptions;
 import org.apache.sis.util.resources.Messages;
@@ -42,7 +43,8 @@ import static org.apache.sis.internal.ut
 /**
  * Runs the SQL scripts for creating an EPSG database.
  *
- * See {@code EPSGDataFormatter} in the test directory for more information about how the scripts are formatted.
+ * See {@code org.apache.sis.referencing.factory.sql.epsg.DataScriptFormatter}
+ * in the test directory for more information about how the scripts are formatted.
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @version 0.8
@@ -248,7 +250,7 @@ final class EPSGInstaller extends Script
         time = System.nanoTime() - time;
         InstallationScriptProvider.log(Messages.getResources(locale).getLogRecord(
                 PerformanceLevel.forDuration(time, TimeUnit.NANOSECONDS),
-                Messages.Keys.InsertDuration_2, numRows, time / 1E9f));
+                Messages.Keys.InsertDuration_2, numRows, time / (float) StandardDateFormat.NANOS_PER_SECOND));
     }
 
     /**

Modified: sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/SQLTranslator.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/SQLTranslator.java?rev=1803070&r1=1803069&r2=1803070&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/SQLTranslator.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/SQLTranslator.java [UTF-8] Wed Jul 26 16:14:09 2017
@@ -101,7 +101,7 @@ import java.util.function.Function;
  * @author  Martin Desruisseaux (IRD)
  * @author  Didier Richard (IGN)
  * @author  John Grange
- * @version 0.7
+ * @version 0.8
  * @since   0.7
  * @module
  */
@@ -161,14 +161,21 @@ public class SQLTranslator implements Fu
      *       In such case, {@code SQLTranslator} will tries to automatically detect the schema.</li>
      * </ul>
      *
-     * <p><b>Consider this field as final.</b> This field is non-final only for construction convenience,
-     * or for updating after the {@link EPSGInstaller} class created the database.</p>
+     * <b>Consider this field as final.</b> This field is non-final only for construction convenience,
+     * or for updating after the {@link EPSGInstaller} class created the database.
      *
      * @see #getSchema()
      */
     private String schema;
 
     /**
+     * Whether the table names are prefixed by {@value #TABLE_PREFIX}. When installed by Apache SIS,
+     * the table names are not prefixed if the tables are stored in a schema. However the dataset may
+     * have been installed manually by users following different rules.
+     */
+    private boolean isPrefixed;
+
+    /**
      * Mapping from words used in the MS-Access database to words used in the ANSI versions of EPSG databases.
      * A word may be a table or a column name, or a part of it. A table name may consist in many words separated
      * by spaces.
@@ -256,6 +263,7 @@ public class SQLTranslator implements Fu
             try (ResultSet result = md.getTables(catalog, schema, table, null)) {
                 if (result.next()) {
                     isTableFound    = true;
+                    isPrefixed      = table.startsWith(TABLE_PREFIX);
                     quoteTableNames = (i == MIXED_CASE);
                     do {
                         catalog = result.getString("TABLE_CAT");
@@ -406,7 +414,7 @@ public class SQLTranslator implements Fu
                 if (quoteTableNames) {
                     ansi.append(quote);
                 }
-                if (schema == null) {
+                if (isPrefixed) {
                     ansi.append(TABLE_PREFIX);
                 }
                 if (quoteTableNames) {



Mime
View raw message