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) {