sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1720269 - in /sis/branches/JDK8/core: sis-referencing/src/main/java/org/apache/sis/referencing/factory/ sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/ sis-utility/src/main/java/org/apache/sis/util/resources/
Date Tue, 15 Dec 2015 22:57:07 GMT
Author: desruisseaux
Date: Tue Dec 15 22:57:07 2015
New Revision: 1720269

URL: http://svn.apache.org/viewvc?rev=1720269&view=rev
Log:
First real geodetic methods in EPSGFactory: createEllipsoid and createPrimeMeridian.

Modified:
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/GeodeticAuthorityFactory.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGFactory.java
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages.java
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages.properties
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages_fr.properties

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/GeodeticAuthorityFactory.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/GeodeticAuthorityFactory.java?rev=1720269&r1=1720268&r2=1720269&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/GeodeticAuthorityFactory.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/GeodeticAuthorityFactory.java
[UTF-8] Tue Dec 15 22:57:07 2015
@@ -133,8 +133,8 @@ public abstract class GeodeticAuthorityF
      * Returns an arbitrary object from a code. The returned object will typically be an
instance of {@link Datum},
      * {@link CoordinateSystem}, {@link CoordinateReferenceSystem} or {@link CoordinateOperation}.
      * This method may be used when the type of the object to create is unknown.
-     * But it is recommended to invoke the most specific {@code createFoo(String)} method
when the type is known,
-     * both for performance reason and for avoiding ambiguity.
+     * But it is recommended to invoke the most specific {@code createFoo(String)} method
when
+     * the desired type is known, both for performance reason and for avoiding ambiguity.
      *
      * <div class="section">Note for subclasses</div>
      * In default {@code GeodeticAuthorityFactory} implementation, all {@code createFoo(String)}
methods ultimately

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGFactory.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGFactory.java?rev=1720269&r1=1720268&r2=1720269&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGFactory.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGFactory.java
[UTF-8] Tue Dec 15 22:57:07 2015
@@ -44,6 +44,8 @@ import java.net.URISyntaxException;
 import javax.measure.unit.Unit;
 import javax.measure.unit.SI;
 import javax.measure.unit.NonSI;
+import javax.measure.quantity.Angle;
+import javax.measure.quantity.Length;
 import javax.measure.converter.ConversionException;
 
 import org.opengis.util.NameSpace;
@@ -60,6 +62,7 @@ import org.opengis.referencing.Identifie
 import org.opengis.metadata.citation.Citation;
 import org.opengis.metadata.citation.OnLineFunction;
 import org.opengis.metadata.quality.PositionalAccuracy;
+import org.opengis.metadata.extent.Extent;
 import org.opengis.referencing.NoSuchAuthorityCodeException;
 import org.apache.sis.internal.referencing.DeprecatedCode;
 import org.apache.sis.internal.system.Loggers;
@@ -67,6 +70,8 @@ import org.apache.sis.internal.util.Cons
 import org.apache.sis.metadata.iso.ImmutableIdentifier;
 import org.apache.sis.metadata.iso.citation.DefaultCitation;
 import org.apache.sis.metadata.iso.citation.DefaultOnlineResource;
+import org.apache.sis.metadata.iso.extent.DefaultExtent;
+import org.apache.sis.metadata.iso.extent.DefaultGeographicBoundingBox;
 import org.apache.sis.referencing.NamedIdentifier;
 import org.apache.sis.referencing.AbstractIdentifiedObject;
 import org.apache.sis.referencing.datum.BursaWolfParameters;
@@ -309,6 +314,21 @@ public abstract class EPSGFactory extend
     protected final Connection connection;
 
     /**
+     * The factory to use for creating {@link CoordinateReferenceSystem} instances from the
properties read in the database.
+     */
+    protected final CRSFactory crsFactory;
+
+    /**
+     * The factory to use for creating {@link CoordinateSystem} instances from the properties
read in the database.
+     */
+    protected final CSFactory csFactory;
+
+    /**
+     * The factory to use for creating {@link Datum} instances from the properties read in
the database.
+     */
+    protected final DatumFactory datumFactory;
+
+    /**
      * The locale for producing error messages. This is usually the default locale.
      *
      * @see #getLocale()
@@ -319,15 +339,29 @@ public abstract class EPSGFactory extend
      * Creates a factory using the given connection. The connection will be {@linkplain Connection#close()
closed}
      * when this factory will be {@linkplain #dispose() disposed}.
      *
-     * @param connection   The connection to the underlying EPSG database.
-     * @param nameFactory  The factory to use for creating authority codes as {@link GenericName}
instances.
-     */
-    public EPSGFactory(final Connection connection, final NameFactory nameFactory) {
+     * @param connection    The connection to the underlying EPSG database.
+     * @param crsFactory    The factory to use for creating {@link CoordinateReferenceSystem}
instances.
+     * @param csFactory     The factory to use for creating {@link CoordinateSystem} instances.
+     * @param datumFactory  The factory to use for creating {@link Datum} instances.
+     * @param nameFactory   The factory to use for creating authority codes as {@link GenericName}
instances.
+     */
+    public EPSGFactory(final Connection   connection,
+                       final CRSFactory   crsFactory,
+                       final CSFactory    csFactory,
+                       final DatumFactory datumFactory,
+                       final NameFactory  nameFactory)
+    {
         super(nameFactory);
-        ArgumentChecks.ensureNonNull("connection", connection);
-        this.connection = connection;
-        this.namespace  = nameFactory.createNameSpace(nameFactory.createLocalName(null, Constants.EPSG),
null);
-        this.locale     = Locale.getDefault(Locale.Category.DISPLAY);
+        ArgumentChecks.ensureNonNull("connection",   connection);
+        ArgumentChecks.ensureNonNull("crsFactory",   crsFactory);
+        ArgumentChecks.ensureNonNull("csFactory",    csFactory);
+        ArgumentChecks.ensureNonNull("datumFactory", datumFactory);
+        this.connection   = connection;
+        this.crsFactory   = crsFactory;
+        this.csFactory    = csFactory;
+        this.datumFactory = datumFactory;
+        this.namespace    = nameFactory.createNameSpace(nameFactory.createLocalName(null,
Constants.EPSG), null);
+        this.locale       = Locale.getDefault(Locale.Category.DISPLAY);
     }
 
     /**
@@ -958,6 +992,431 @@ addURIs:    for (int i=0; ; i++) {
         return properties;
     }
 
+    /**
+     * Returns the name, aliases and domain of validity for the {@link IdentifiedObject}
to construct.
+     *
+     * @param  table      The table on which a query has been executed.
+     * @param  name       The name for the {@link IndentifiedObject} to construct.
+     * @param  code       The EPSG code of the object to construct.
+     * @param  domainCode The code for the domain of validity, or {@code null} if none.
+     * @param  scope      The scope, or {@code null} if none.
+     * @param  remarks    Remarks, or {@code null} if none.
+     * @param  deprecated {@code true} if the object to create is deprecated.
+     * @return The name together with a set of properties.
+     */
+    private Map<String,Object> createProperties(final String table, final String name,
final String code,
+            String domainCode, String scope, String remarks, final boolean deprecated) throws
SQLException, FactoryException
+    {
+        final Map<String,Object> properties = createProperties(table, name, code, remarks,
deprecated);
+        if (domainCode != null  &&  !(domainCode = domainCode.trim()).isEmpty())
{
+            final Extent extent = buffered.createExtent(domainCode);
+            properties.put(Datum.DOMAIN_OF_VALIDITY_KEY, extent);
+        }
+        if (scope != null &&  !(scope = scope.trim()).isEmpty()) {
+            properties.put(Datum.SCOPE_KEY, scope);
+        }
+        return properties;
+    }
+
+    /**
+     * Returns an arbitrary object from a code. The default implementation delegates to more
specific methods,
+     * for example {@link #createCoordinateReferenceSystem(String)}, {@link #createDatum(String)},
<i>etc.</i>
+     * until a successful one is found.
+     *
+     * <p><strong>Note that this method may be ambiguous</strong> since
the same EPSG code can be used for different
+     * kind of objects. This method may throw an exception if it detects an ambiguity, but
this is not guaranteed.
+     * It is recommended to invoke the most specific {@code createFoo(String)} method when
the desired type is known,
+     * both for performance reason and for avoiding ambiguity.</p>
+     *
+     * @param  code Value allocated by EPSG.
+     * @return The object for the given code.
+     * @throws NoSuchAuthorityCodeException if the specified {@code code} was not found.
+     * @throws FactoryException if the object creation failed for some other reason.
+     *
+     * @see #createCoordinateReferenceSystem(String)
+     * @see #createDatum(String)
+     * @see #createCoordinateSystem(String)
+     */
+    @Override
+    public synchronized IdentifiedObject createObject(final String code)
+            throws NoSuchAuthorityCodeException, FactoryException
+    {
+        ArgumentChecks.ensureNonNull("code", code);
+        final String      KEY   = "IdentifiedObject";
+        PreparedStatement stmt  = statements.get(KEY);  // Null allowed.
+        StringBuilder     query = null;                 // Will be created only if the last
statement does not suit.
+        /*
+         * Iterates through all tables listed in TableInfo.EPSG, starting with the table
used during the last call to
+         * this method. This approach assumes that two consecutive calls will often return
the same type of object.
+         * If the object type changed, then this method will have to discard the old prepared
statement and prepare
+         * a new one, which may be a costly operation. Only the last successful prepared
statement is cached,
+         * in order to keep the amount of statements low. Unsuccessful statements are immediately
disposed.
+         */
+        final String  epsg         = trimAuthority(code);
+        final boolean isPrimaryKey = isPrimaryKey(epsg);
+        final int     tupleToSkip  = isPrimaryKey ? lastObjectType : -1;
+        int index = -1;
+        for (int i = -1; i < TableInfo.EPSG.length; i++) {
+            if (i == tupleToSkip) {
+                // Avoid to test the same table twice. Avoid also a NullPointerException
+                // if 'stmt' is null, since 'lastObjectType' should be -1 in this case.
+                continue;
+            }
+            try {
+                if (i >= 0) {
+                    final TableInfo table = TableInfo.EPSG[i];
+                    final String column = isPrimaryKey ? table.codeColumn : table.nameColumn;
+                    if (column == null) {
+                        continue;
+                    }
+                    if (query == null) {
+                        query = new StringBuilder("SELECT ");
+                    }
+                    query.setLength(7);   // 7 is the length of "SELECT " in the above line.
+                    query.append(table.codeColumn).append(" FROM ").append(table.table)
+                         .append(" WHERE ").append(column).append(" = ?");
+                    if (isPrimaryKey) {
+                        assert !statements.containsKey(KEY) : table;
+                        stmt = prepareStatement(KEY, query.toString());
+                    } else {
+                        // Do not cache the statement for names.
+                        stmt = connection.prepareStatement(adaptSQL(query.toString()));
+                    }
+                }
+                /*
+                 * Checks if at least one record is found for the code. If the code is the
primary key, then we
+                 * will stop at the first table found since the EPSG database contains few
duplicate identifiers
+                 * (actually it still have some, but we assume that the risk of collision
is low).
+                 * If the code is a name, then we need to search in all tables since duplicate
names exist.
+                 */
+                final ResultSet result;
+                if (isPrimaryKey) {
+                    result = executeQuery(stmt, epsg);
+                } else {
+                    stmt.setString(1, epsg);
+                    result = stmt.executeQuery();
+                }
+                final boolean present;
+                try {
+                    present = result.next();
+                } finally {
+                    result.close();
+                }
+                if (present) {
+                    if (index >= 0) {
+                        throw new FactoryDataException(error().getString(Errors.Keys.DuplicatedIdentifier_1,
code));
+                    }
+                    index = (i < 0) ? lastObjectType : i;
+                    if (isPrimaryKey) {
+                        // Do not scan other tables, since EPSG code are more likely to be
unique.
+                        // Note that names are more at risk to be duplicated, so we do not
stop for names.
+                        break;
+                    }
+                }
+                if (isPrimaryKey) {
+                    if (statements.remove(KEY) == null) {
+                        throw new AssertionError(code);         // Should never happen.
+                    }
+                }
+                stmt.close();
+            } catch (SQLException exception) {
+                throw databaseFailure(IdentifiedObject.class, code, exception);
+            }
+        }
+        /*
+         * If a record has been found in one table, then delegates to the appropriate method.
+         */
+        if (isPrimaryKey) {
+            lastObjectType = index;
+        }
+        if (index >= 0) {
+            switch (index) {
+                case 0:  return createCoordinateReferenceSystem(code);
+                case 1:  return createCoordinateSystem         (code);
+                case 2:  return createCoordinateSystemAxis     (code);
+                case 3:  return createDatum                    (code);
+                case 4:  return createEllipsoid                (code);
+                case 5:  return createPrimeMeridian            (code);
+                case 6:  return createCoordinateOperation      (code);
+                case 7:  return createOperationMethod          (code);
+                case 8:  return createParameterDescriptor      (code);
+                case 9:  break; // Can not cast Unit to IdentifiedObject
+                default: throw new AssertionError(index);                   // Should not
happen
+            }
+        }
+        throw noSuchAuthorityCode(IdentifiedObject.class, code);
+    }
+
+    /**
+     * Creates a geometric figure that can be used to describe the approximate shape of the
earth.
+     * In mathematical terms, it is a surface formed by the rotation of an ellipse about
its minor axis.
+     *
+     * @param  code Value allocated by EPSG.
+     * @return The ellipsoid for the given code.
+     * @throws NoSuchAuthorityCodeException if the specified {@code code} was not found.
+     * @throws FactoryException if the object creation failed for some other reason.
+     *
+     * @see #createGeodeticDatum(String)
+     * @see #createEllipsoidalCS(String)
+     * @see org.apache.sis.referencing.datum.DefaultEllipsoid
+     */
+    @Override
+    public synchronized Ellipsoid createEllipsoid(final String code)
+            throws NoSuchAuthorityCodeException, FactoryException
+    {
+        ArgumentChecks.ensureNonNull("code", code);
+        Ellipsoid returnValue = null;
+        try {
+            final String primaryKey = toPrimaryKey(Ellipsoid.class, code,
+                    "[Ellipsoid]", "ELLIPSOID_CODE", "ELLIPSOID_NAME");
+            final PreparedStatement stmt = prepareStatement("[Ellipsoid]",
+                    "SELECT ELLIPSOID_CODE," +
+                          " ELLIPSOID_NAME," +
+                          " SEMI_MAJOR_AXIS," +
+                          " INV_FLATTENING," +
+                          " SEMI_MINOR_AXIS," +
+                          " UOM_CODE," +
+                          " REMARKS," +
+                          " DEPRECATED" +
+                    " FROM [Ellipsoid]" +
+                    " WHERE ELLIPSOID_CODE = ?");
+            try (ResultSet result = executeQuery(stmt, primaryKey)) {
+                while (result.next()) {
+                    /*
+                     * One of 'semiMinorAxis' and 'inverseFlattening' values can be NULL
in the database.
+                     * Consequently, we don't use 'getString(ResultSet, int)' for those parameters
because
+                     * we do not want to thrown an exception if a NULL value is found.
+                     */
+                    final String  epsg              = getString(result, 1, code);
+                    final String  name              = getString(result, 2, code);
+                    final double  semiMajorAxis     = getDouble(result, 3, code);
+                    final double  inverseFlattening = result.getDouble( 4);
+                    final double  semiMinorAxis     = result.getDouble( 5);
+                    final String  unitCode          = getString(result, 6, code);
+                    final String  remarks           = result.getString( 7);
+                    final boolean deprecated        = result.getInt   ( 8) != 0;
+                    final Unit<Length> unit         = buffered.createUnit(unitCode).asType(Length.class);
+                    final Map<String,Object> properties = createProperties("[Ellipsoid]",
name, epsg, remarks, deprecated);
+                    final Ellipsoid ellipsoid;
+                    if (inverseFlattening == 0) {
+                        if (semiMinorAxis == 0) {
+                            // Both are null, which is not allowed.
+                            final String column = result.getMetaData().getColumnName(3);
+                            throw new FactoryDataException(error().getString(Errors.Keys.NullValueInTable_3,
code, column));
+                        } else {
+                            // We only have semiMinorAxis defined. It is OK
+                            ellipsoid = datumFactory.createEllipsoid(properties, semiMajorAxis,
semiMinorAxis, unit);
+                        }
+                    } else {
+                        if (semiMinorAxis != 0) {
+                            // Both 'inverseFlattening' and 'semiMinorAxis' are defined.
+                            // Log a warning and create the ellipsoid using the inverse flattening.
+                            final LogRecord record = Messages.getResources(locale).getLogRecord(Level.WARNING,
+                                    Messages.Keys.AmbiguousEllipsoid_1, Constants.EPSG +
DefaultNameSpace.DEFAULT_SEPARATOR + code);
+                            record.setLoggerName(Loggers.CRS_FACTORY);
+                            Logging.log(EPSGFactory.class, "createEllipsoid", record);
+                        }
+                        ellipsoid = datumFactory.createFlattenedSphere(properties, semiMajorAxis,
inverseFlattening, unit);
+                    }
+                    returnValue = ensureSingleton(ellipsoid, returnValue, code);
+                }
+            }
+        } catch (SQLException exception) {
+            throw databaseFailure(Ellipsoid.class, code, exception);
+        }
+        if (returnValue == null) {
+             throw noSuchAuthorityCode(Ellipsoid.class, code);
+        }
+        return returnValue;
+    }
+
+    /**
+     * Creates a prime meridian defining the origin from which longitude values are determined.
+     *
+     * @param  code Value allocated by EPSG.
+     * @return The prime meridian for the given code.
+     * @throws NoSuchAuthorityCodeException if the specified {@code code} was not found.
+     * @throws FactoryException if the object creation failed for some other reason.
+     *
+     * @see #createGeodeticDatum(String)
+     * @see org.apache.sis.referencing.datum.DefaultPrimeMeridian
+     */
+    @Override
+    public synchronized PrimeMeridian createPrimeMeridian(final String code)
+            throws NoSuchAuthorityCodeException, FactoryException
+    {
+        ArgumentChecks.ensureNonNull("code", code);
+        PrimeMeridian returnValue = null;
+        try {
+            final String primaryKey = toPrimaryKey(PrimeMeridian.class, code,
+                    "[Prime Meridian]", "PRIME_MERIDIAN_CODE", "PRIME_MERIDIAN_NAME");
+            final PreparedStatement stmt = prepareStatement("[Prime Meridian]",
+                    "SELECT PRIME_MERIDIAN_CODE," +
+                          " PRIME_MERIDIAN_NAME," +
+                          " GREENWICH_LONGITUDE," +
+                          " UOM_CODE," +
+                          " REMARKS," +
+                          " DEPRECATED" +
+                    " FROM [Prime Meridian]" +
+                    " WHERE PRIME_MERIDIAN_CODE = ?");
+            try (ResultSet result = executeQuery(stmt, primaryKey)) {
+                while (result.next()) {
+                    final String  epsg       = getString(result, 1, code);
+                    final String  name       = getString(result, 2, code);
+                    final double  longitude  = getDouble(result, 3, code);
+                    final String  unitCode   = getString(result, 4, code);
+                    final String  remarks    = result.getString( 5);
+                    final boolean deprecated = result.getInt   ( 6) != 0;
+                    final Unit<Angle> unit = buffered.createUnit(unitCode).asType(Angle.class);
+                    Map<String,Object> properties = createProperties("[Prime Meridian]",
name, epsg, remarks, deprecated);
+                    PrimeMeridian primeMeridian = datumFactory.createPrimeMeridian(properties,
longitude, unit);
+                    returnValue = ensureSingleton(primeMeridian, returnValue, code);
+                }
+            }
+        } catch (SQLException exception) {
+            throw databaseFailure(PrimeMeridian.class, code, exception);
+        }
+        if (returnValue == null) {
+            throw noSuchAuthorityCode(PrimeMeridian.class, code);
+        }
+        return returnValue;
+    }
+
+    /**
+     * Creates information about spatial, vertical, and temporal extent (usually a domain
of validity) from a code.
+     *
+     * @param  code Value allocated by EPSG.
+     * @return The extent for the given code.
+     * @throws NoSuchAuthorityCodeException if the specified {@code code} was not found.
+     * @throws FactoryException if the object creation failed for some other reason.
+     *
+     * @see #createCoordinateReferenceSystem(String)
+     * @see #createDatum(String)
+     * @see org.apache.sis.metadata.iso.extent.DefaultExtent
+     */
+    @Override
+    public synchronized Extent createExtent(final String code)
+            throws NoSuchAuthorityCodeException, FactoryException
+    {
+        ArgumentChecks.ensureNonNull("code", code);
+        Extent returnValue = null;
+        try {
+            final String primaryKey = toPrimaryKey(Extent.class, code,
+                    "[Area]", "AREA_CODE", "AREA_NAME");
+            final PreparedStatement stmt = prepareStatement("[Area]",
+                    "SELECT AREA_OF_USE," +
+                          " AREA_SOUTH_BOUND_LAT," +
+                          " AREA_NORTH_BOUND_LAT," +
+                          " AREA_WEST_BOUND_LON," +
+                          " AREA_EAST_BOUND_LON" +
+                    " FROM [Area]" +
+                    " WHERE AREA_CODE = ?");
+            try (ResultSet result = executeQuery(stmt, primaryKey)) {
+                while (result.next()) {
+                    String description = result.getString(1);
+                    if (description != null && (description = description.trim()).isEmpty())
{
+                        description = null;
+                    }
+                    DefaultGeographicBoundingBox bbox = null;
+                    double ymin = result.getDouble(2); if (result.wasNull()) ymin = Double.NaN;
+                    double ymax = result.getDouble(3); if (result.wasNull()) ymax = Double.NaN;
+                    double xmin = result.getDouble(4); if (result.wasNull()) xmin = Double.NaN;
+                    double xmax = result.getDouble(5); if (result.wasNull()) xmax = Double.NaN;
+                    if (!Double.isNaN(ymin) || !Double.isNaN(ymax) || !Double.isNaN(xmin)
|| !Double.isNaN(xmax)) {
+                        /*
+                         * Fix an error found in EPSG:3790 New Zealand - South Island - Mount
Pleasant mc
+                         * for older database (this error is fixed in EPSG database 8.2).
+                         *
+                         * Do NOT apply anything similar for the x axis, because xmin >
xmax is not error:
+                         * it describes a bounding box spanning the anti-meridian (±180°
of longitude).
+                         */
+                        if (ymin > ymax) {
+                            final double t = ymin;
+                            ymin = ymax;
+                            ymax = t;
+                        }
+                        bbox = new DefaultGeographicBoundingBox(xmin, xmax, ymin, ymax);
+                    }
+                    if (description != null || bbox != null) {
+                        DefaultExtent extent = new DefaultExtent(description, bbox, null,
null);
+                        extent.freeze();
+                        returnValue = ensureSingleton(extent, returnValue, code);
+                    }
+                }
+            }
+        } catch (SQLException exception) {
+            throw databaseFailure(Extent.class, code, exception);
+        }
+        if (returnValue == null) {
+            throw noSuchAuthorityCode(Extent.class, code);
+        }
+        return returnValue;
+    }
+
+    /**
+     * Creates an unit of measurement from a code.
+     *
+     * @param  code Value allocated by EPSG.
+     * @return The unit of measurement for the given code.
+     * @throws NoSuchAuthorityCodeException if the specified {@code code} was not found.
+     * @throws FactoryException if the object creation failed for some other reason.
+     */
+    @Override
+    public synchronized Unit<?> createUnit(final String code) throws NoSuchAuthorityCodeException,
FactoryException {
+        ArgumentChecks.ensureNonNull("code", code);
+        Unit<?> returnValue = null;
+        try {
+            final String primaryKey = toPrimaryKey(Unit.class, code,
+                    "[Unit of Measure]", "UOM_CODE", "UNIT_OF_MEAS_NAME");
+            final PreparedStatement stmt;
+            stmt = prepareStatement("[Unit of Measure]",
+                    "SELECT UOM_CODE," +
+                          " FACTOR_B," +
+                          " FACTOR_C," +
+                          " TARGET_UOM_CODE," +
+                          " UNIT_OF_MEAS_NAME" +
+                    " FROM [Unit of Measure]" +
+                    " WHERE UOM_CODE = ?");
+            try (ResultSet result = executeQuery(stmt, primaryKey)) {
+                while (result.next()) {
+                    final int source = getInt(result,   1, code);
+                    final double   b = result.getDouble(2);
+                    final double   c = result.getDouble(3);
+                    final int target = getInt(result,   4, code);
+                    if (source == target) {
+                        /*
+                         * The unit is a base unit. Verify its consistency:
+                         * conversion from 'source' to itself shall be the identity function.
+                         */
+                        final boolean pb = (b != 1);
+                        if (pb || c != 1) {
+                            throw new FactoryDataException(error().getString(Errors.Keys.InconsistentAttribute_2,
+                                        pb ? "FACTOR_B" : "FACTOR_C", pb ? b : c));
+                        }
+                    }
+                    Unit<?> unit = Units.valueOfEPSG(source);           // Check in
our list of hard-coded unit codes.
+                    if (unit == null) {
+                        final Unit<?> base = Units.valueOfEPSG(target);
+                        if (base != null && b != 0 && c != 0) {         //
May be 0 if the conversion is non-linear.
+                            unit = Units.multiply(base, b/c);
+                        } else try {
+                            unit = Units.valueOf(result.getString(5));  // Try parsing the
unit symbol as a fallback.
+                        } catch (IllegalArgumentException e) {
+                            throw new FactoryDataException(error().getString(Errors.Keys.UnknownUnit_1,
code), e);
+                        }
+                    }
+                    returnValue = ensureSingleton(unit, returnValue, code);
+                }
+            }
+        } catch (SQLException exception) {
+            throw databaseFailure(Unit.class, code, exception);
+        }
+        if (returnValue == null) {
+            throw noSuchAuthorityCode(Unit.class, code);
+        }
+        return returnValue;
+    }
+
     final boolean isProjection(final int code) throws NoSuchIdentifierException, SQLException
{
         return false;
     }
@@ -1048,6 +1507,20 @@ addURIs:    for (int i=0; ; i++) {
     }
 
     /**
+     * Constructs an exception for recursive calls.
+     */
+    private FactoryException recursiveCall(final Class<?> type, final String code)
{
+        return new FactoryException(error().getString(Errors.Keys.RecursiveCreateCallForCode_2,
type, code));
+    }
+
+    /**
+     * Constructs an exception for a database failure.
+     */
+    private FactoryException databaseFailure(Class<?> type, String code, SQLException
cause) {
+        return new FactoryException(error().getString(Errors.Keys.DatabaseError_2, type,
code), cause);
+    }
+
+    /**
      * Minor shortcut for fetching the error resources.
      */
     private Errors error() {

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java?rev=1720269&r1=1720268&r2=1720269&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java
[UTF-8] Tue Dec 15 22:57:07 2015
@@ -191,6 +191,11 @@ public final class Errors extends Indexe
         public static final short ColinearAxisDirections_2 = 14;
 
         /**
+         * Database error while creating a ‘{0}’ object for code “{1}”.
+         */
+        public static final short DatabaseError_2 = 209;
+
+        /**
          * Thread “{0}” is dead.
          */
         public static final short DeadThread_1 = 15;
@@ -861,6 +866,11 @@ public final class Errors extends Indexe
         public static final short RecordAlreadyDefined_2 = 161;
 
         /**
+         * Recursive call while creating an object of type ‘{0}’ for code “{1}”.
+         */
+        public static final short RecursiveCreateCallForCode_2 = 210;
+
+        /**
          * Recursive call while creating an object for the “{0}” key.
          */
         public static final short RecursiveCreateCallForKey_1 = 99;
@@ -1011,6 +1021,11 @@ public final class Errors extends Indexe
         public static final short UnknownType_1 = 118;
 
         /**
+         * Unit “{0}” is not recognized.
+         */
+        public static final short UnknownUnit_1 = 211;
+
+        /**
          * This affine transform is unmodifiable.
          */
         public static final short UnmodifiableAffineTransform = 119;

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties?rev=1720269&r1=1720268&r2=1720269&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties
[ISO-8859-1] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties
[ISO-8859-1] Tue Dec 15 22:57:07 2015
@@ -49,6 +49,7 @@ CircularReference                 = Circ
 ClassNotFinal_1                   = Class \u2018{0}\u2019 is not final.
 CloneNotSupported_1               = Can not clone an object of type \u2018{0}\u2019.
 ColinearAxisDirections_2          = Axis directions {0} and {1} are colinear.
+DatabaseError_2                   = Database error while creating a \u2018{0}\u2019 object
for code \u201c{1}\u201d.
 DeadThread_1                      = Thread \u201c{0}\u201d is dead.
 DirectoryNotExpected_1            = The \u201c{0}\u201d file points to a directory instead
of a regular file.
 DisposedFactory                   = The factory has been disposed.
@@ -184,6 +185,7 @@ ParameterNotFound_2               = No p
 PropertyNotFound_2                = No property named \u201c{1}\u201d has been found in \u201c{0}\u201d.
 RecordAlreadyDefined_2            = Record \u201c{1}\u201d is already defined in schema \u201c{0}\u201d.
 RecursiveCreateCallForKey_1       = Recursive call while creating an object for the \u201c{0}\u201d
key.
+RecursiveCreateCallForCode_2      = Recursive call while creating an object of type \u2018{0}\u2019
for code \u201c{1}\u201d.
 RequireDecimalSeparator           = A decimal separator is required.
 SingularMatrix                    = Matrix is singular.
 StalledThread_1                   = Thread \u201c{0}\u201d seems stalled.
@@ -213,6 +215,7 @@ UnknownKeyword_1                  = Keyw
 UnknownOption_1                   = Option \u201c{0}\u201d is not recognized.
 UnknownType_1                     = Type \u2018{0}\u2019 is unknown in this context.
 UnknownTypeForProperty_1          = Type of the \u201c{0}\u201d property is unknown.
+UnknownUnit_1                     = Unit \u201c{0}\u201d is not recognized.
 UnmodifiableAffineTransform       = This affine transform is unmodifiable.
 UnmodifiableCellValue_2           = The cell at column \u201c{1}\u201d of row \u201c{0}\u201d
is unmodifiable.
 UnmodifiableGeometry              = This geometry is not modifiable.

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties?rev=1720269&r1=1720268&r2=1720269&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties
[ISO-8859-1] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties
[ISO-8859-1] Tue Dec 15 22:57:07 2015
@@ -46,6 +46,7 @@ CircularReference                 = R\u0
 ClassNotFinal_1                   = La classe \u2018{0}\u2019 n\u2019est pas finale.
 CloneNotSupported_1               = Un objet de type \u2018{0}\u2019 ne peut pas \u00eatre
clon\u00e9.
 ColinearAxisDirections_2          = Les directions d\u2019axes {0} et {1} sont colin\u00e9aires.
+DatabaseError_2                   = Erreur de base de donn\u00e9es lors de la cr\u00e9ation
d\u2019un objet \u2018{0}\u2019 pour le code \u00ab\u202f{1}\u202f\u00bb.
 DeadThread_1                      = La t\u00e2che \u00ab\u202f{0}\u202f\u00bb est morte.
 DirectoryNotExpected_1            = Le fichier \u00ab\u202f{0}\u202f\u00bb d\u00e9signe un
r\u00e9pertoire plut\u00f4t qu\u2019un fichier r\u00e9gulier.
 DisposedFactory                   = La fabrique a \u00e9t\u00e9 dispos\u00e9e.
@@ -180,6 +181,7 @@ ParameterNotFound_2               = Aucu
 PropertyNotFound_2                = Aucune propri\u00e9t\u00e9 nomm\u00e9e \u00ab\u202f{1}\u202f\u00bb
n\u2019a \u00e9t\u00e9 trouv\u00e9e dans \u00ab\u202f{0}\u202f\u00bb.
 RecordAlreadyDefined_2            = L\u2019enregistrement \u00ab\u202f{1}\u202f\u00bb est
d\u00e9j\u00e0 d\u00e9finit dans le sch\u00e9ma \u00ab\u202f{0}\u202f\u00bb.
 RecursiveCreateCallForKey_1       = Appel r\u00e9cursif lors de la cr\u00e9ation d\u2019un
objet pour la cl\u00e9 \u00ab\u202f{0}\u202f\u00bb.
+RecursiveCreateCallForCode_2      = Appels r\u00e9cursifs lors de la cr\u00e9ation d\u2019un
objet de type \u2018{0}\u2019 pour le code \u00ab\u202f{1}\u202f\u00bb.
 RequireDecimalSeparator           = Un s\u00e9parateur d\u00e9cimal est requis.
 SingularMatrix                    = La matrice est singuli\u00e8re.
 StalledThread_1                   = La t\u00e2che \u00ab\u202f{0}\u202f\u00bb semble bloqu\u00e9e.
@@ -209,6 +211,7 @@ UnknownKeyword_1                  = Le m
 UnknownOption_1                   = L\u2019option \u00ab\u202f{0}\u202f\u00bb n\u2019est
pas reconnue.
 UnknownType_1                     = Le type \u2018{0}\u2019 n\u2019est pas reconnu dans ce
contexte.
 UnknownTypeForProperty_1          = Le type de la propri\u00e9t\u00e9 \u00ab\u202f{0}\u202f\u00bb
est inconnu.
+UnknownUnit_1                     = Les unit\u00e9s \u00ab\u202f{0}\u202f\u00bb ne sont pas
reconnues.
 UnmodifiableAffineTransform       = Cette transformation affine n\u2019est pas modifiable.
 UnmodifiableCellValue_2           = La cellule \u00e0 la colonne \u00ab\u202f{1}\u202f\u00bb
de la ligne \u00ab\u202f{0}\u202f\u00bb n\u2019est pas modifiable.
 UnmodifiableGeometry              = Cette g\u00e9om\u00e9trie n\u2019est pas modifiable.

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages.java?rev=1720269&r1=1720268&r2=1720269&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages.java
[UTF-8] Tue Dec 15 22:57:07 2015
@@ -61,6 +61,12 @@ public final class Messages extends Inde
         public static final short AlreadyRegistered_2 = 0;
 
         /**
+         * Ambiguity between inverse flattening and semi minor axis length for “{0}”.
Using inverse
+         * flattening.
+         */
+        public static final short AmbiguousEllipsoid_1 = 30;
+
+        /**
          * Changed the container capacity from {0} to {1} elements.
          */
         public static final short ChangedContainerCapacity_2 = 1;

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages.properties
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages.properties?rev=1720269&r1=1720268&r2=1720269&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages.properties
[ISO-8859-1] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages.properties
[ISO-8859-1] Tue Dec 15 22:57:07 2015
@@ -15,6 +15,7 @@
 # limitations under the License.
 #
 AlreadyRegistered_2             = {0} \u201c{1}\u201d is already registered. The second instance
will be ignored.
+AmbiguousEllipsoid_1            = Ambiguity between inverse flattening and semi minor axis
length for \u201c{0}\u201d. Using inverse flattening.
 ChangedContainerCapacity_2      = Changed the container capacity from {0} to {1} elements.
 ConformanceMeansDatumShift      = This result indicates if a datum shift method has been
applied.
 ConstantProjParameterValue_1    = This parameter is shown for completeness, but should never
have a value different than {0} for this projection.

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages_fr.properties
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages_fr.properties?rev=1720269&r1=1720268&r2=1720269&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages_fr.properties
[ISO-8859-1] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages_fr.properties
[ISO-8859-1] Tue Dec 15 22:57:07 2015
@@ -22,6 +22,7 @@
 #   U+00A0 NO-BREAK SPACE         before  :
 #
 AlreadyRegistered_2             = Le {0} \u00ab\u202f{1}\u202f\u00bb est d\u00e9j\u00e0 inscrit
dans le registre. La seconde instance sera ignor\u00e9e.
+AmbiguousEllipsoid_1            = Ambigu\u00eft\u00e9 entre l\u2019aplatissement et la longueur
du semi-axe mineur pour \u00ab\u202f{0}\u202f\u00bb. Utilise l\u2019aplatissement.
 ChangedContainerCapacity_2      = Changement de la capacit\u00e9 du conteneur de {0} vers
{1} \u00e9l\u00e9ments.
 ConformanceMeansDatumShift      = Ce r\u00e9sultat indique si un changement de r\u00e9f\u00e9rentiel
a \u00e9t\u00e9 appliqu\u00e9.
 ConstantProjParameterValue_1    = Ce param\u00e8tre est montr\u00e9 pour \u00eatre plus complet,
mais sa valeur ne devrait jamais \u00eatre diff\u00e9rente de {0} pour cette projection.




Mime
View raw message