sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1720255 - in /sis/branches/JDK8/core: sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/ sis-referencing/src/test/java/org/apache/sis/referencing/factory/sql/ sis-referencing/src/test/java/org/apache/sis/test/suite/ sis-...
Date Tue, 15 Dec 2015 21:20:13 GMT
Author: desruisseaux
Date: Tue Dec 15 21:20:12 2015
New Revision: 1720255

URL: http://svn.apache.org/viewvc?rev=1720255&view=rev
Log:
Reorder the EPSGFactory methods in order to keep related methods closer to each other.
Log a warning when a deprecated code is requested and provide information about the replacement.
Give control on the locale to use for error messages.

Added:
    sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/factory/sql/
    sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/factory/sql/EPSGFactoryTest.java   (with props)
Modified:
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGFactory.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/TableInfo.java
    sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java
    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
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.java
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.properties
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary_fr.properties

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=1720255&r1=1720254&r2=1720255&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 21:20:12 2015
@@ -28,6 +28,8 @@ import java.util.Calendar;
 import java.util.Date;
 import java.util.Locale;
 import java.util.TimeZone;
+import java.util.logging.Level;
+import java.util.logging.LogRecord;
 import java.sql.Connection;
 import java.sql.DatabaseMetaData;
 import java.sql.PreparedStatement;
@@ -50,7 +52,6 @@ import org.opengis.util.GenericName;
 import org.opengis.util.InternationalString;
 import org.opengis.util.FactoryException;
 import org.opengis.util.NoSuchIdentifierException;
-import org.opengis.parameter.*;
 import org.opengis.referencing.cs.*;
 import org.opengis.referencing.crs.*;
 import org.opengis.referencing.datum.*;
@@ -67,11 +68,13 @@ import org.apache.sis.metadata.iso.Immut
 import org.apache.sis.metadata.iso.citation.DefaultCitation;
 import org.apache.sis.metadata.iso.citation.DefaultOnlineResource;
 import org.apache.sis.referencing.NamedIdentifier;
+import org.apache.sis.referencing.AbstractIdentifiedObject;
 import org.apache.sis.referencing.datum.BursaWolfParameters;
 import org.apache.sis.referencing.factory.FactoryDataException;
 import org.apache.sis.referencing.factory.GeodeticAuthorityFactory;
 import org.apache.sis.referencing.factory.ConcurrentAuthorityFactory;
 import org.apache.sis.util.iso.SimpleInternationalString;
+import org.apache.sis.util.iso.DefaultNameSpace;
 import org.apache.sis.util.resources.Vocabulary;
 import org.apache.sis.util.resources.Messages;
 import org.apache.sis.util.resources.Errors;
@@ -79,6 +82,7 @@ import org.apache.sis.util.logging.Loggi
 import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.util.CharSequences;
 import org.apache.sis.util.Disposable;
+import org.apache.sis.util.Localized;
 import org.apache.sis.util.Version;
 import org.apache.sis.measure.Units;
 
@@ -124,7 +128,7 @@ import org.apache.sis.measure.Units;
  * @see <a href="http://sis.apache.org/book/tables/CoordinateReferenceSystems.html">List of authority codes</a>
  */
 public abstract class EPSGFactory extends GeodeticAuthorityFactory implements CRSAuthorityFactory,
-        CSAuthorityFactory, DatumAuthorityFactory, CoordinateOperationAuthorityFactory, Disposable
+        CSAuthorityFactory, DatumAuthorityFactory, CoordinateOperationAuthorityFactory, Localized, Disposable
 {
     /**
      * The prefix in table names. The SQL scripts are provided by EPSG with this prefix in front of all table names.
@@ -133,15 +137,13 @@ public abstract class EPSGFactory extend
      */
     private static final String TABLE_PREFIX = "epsg_";
 
+    // See org.apache.sis.measure.Units.valueOfEPSG(int) for hard-coded units from EPSG codes.
+    // See TableInfo.EPSG for hard-coded table names, column names and GeoAPI types.
 
-
-    //////////////////////////////////////////////////////////////////////////////////////////////
-    //////                                                                                 ///////
-    //////   HARD CODED VALUES (other than SQL statements) RELATIVE TO THE EPSG DATABASE   ///////
-    //////                                                                                 ///////
-    //////////////////////////////////////////////////////////////////////////////////////////////
-
-    // See org.apache.sis.measure.Units.valueOfEPSG(int) for hard-code units from EPSG codes.
+    // Hard-coded values for datum shift operation methods
+    /** First Bursa-Wolf method. */ private static final int BURSA_WOLF_MIN_CODE = 9603;
+    /** Last Bursa-Wolf method.  */ private static final int BURSA_WOLF_MAX_CODE = 9607;
+    /** Rotation frame method.   */ private static final int ROTATION_FRAME_CODE = 9607;
 
     /**
      * Sets a Bursa-Wolf parameter from an EPSG parameter.
@@ -152,7 +154,7 @@ public abstract class EPSGFactory extend
      * @param  unit       The unit of the parameter value from the [UOM_CODE] column.
      * @throws FactoryException if the code is unrecognized.
      */
-    private static void setBursaWolfParameter(final BursaWolfParameters parameters,
+    private void setBursaWolfParameter(final BursaWolfParameters parameters,
             final int code, double value, final Unit<?> unit) throws FactoryException
     {
         Unit<?> target = unit;
@@ -164,7 +166,7 @@ public abstract class EPSGFactory extend
         if (target != unit) try {
             value = unit.getConverterToAny(target).convert(value);
         } catch (ConversionException e) {
-            throw new FactoryDataException(Errors.format(Errors.Keys.IncompatibleUnit_1, unit), e);
+            throw new FactoryDataException(error().getString(Errors.Keys.IncompatibleUnit_1, unit), e);
         }
         switch (code) {
             case 8605: parameters.tX = value; break;
@@ -174,108 +176,9 @@ public abstract class EPSGFactory extend
             case 8609: parameters.rY = value; break;
             case 8610: parameters.rZ = value; break;
             case 8611: parameters.dS = value; break;
-            default: throw new FactoryDataException(Errors.format(Errors.Keys.UnexpectedParameter_1, code));
+            default: throw new FactoryDataException(error().getString(Errors.Keys.UnexpectedParameter_1, code));
         }
     }
-    // Datum shift operation methods
-    /** First Bursa-Wolf method. */ private static final int BURSA_WOLF_MIN_CODE = 9603;
-    /** Last Bursa-Wolf method.  */ private static final int BURSA_WOLF_MAX_CODE = 9607;
-    /** Rotation frame method.   */ private static final int ROTATION_FRAME_CODE = 9607;
-
-    /**
-     * List of tables and columns to test for codes values. Those tables are used by the {@link #createObject(String)}
-     * method in order to detect which of the following methods should be invoked for a given code:
-     *
-     * {@link #createCoordinateReferenceSystem(String)}
-     * {@link #createCoordinateSystem(String)}
-     * {@link #createDatum(String)}
-     * {@link #createEllipsoid(String)}
-     * {@link #createUnit(String)}
-     *
-     * The order is significant: it is the key for a {@code switch} statement.
-     *
-     * @see #createObject(String)
-     * @see #lastObjectType
-     */
-    private static final TableInfo[] TABLES_INFO = {
-        new TableInfo(CoordinateReferenceSystem.class,
-                "[Coordinate Reference System]",
-                "COORD_REF_SYS_CODE",
-                "COORD_REF_SYS_NAME",
-                "COORD_REF_SYS_KIND",
-                new Class<?>[] { ProjectedCRS.class, GeographicCRS.class, GeocentricCRS.class,
-                                 VerticalCRS.class,  CompoundCRS.class,   EngineeringCRS.class},
-                new String[]   {"projected",        "geographic",        "geocentric",
-                                "vertical",         "compound",          "engineering"}),
-
-        new TableInfo(CoordinateSystem.class,
-                "[Coordinate System]",
-                "COORD_SYS_CODE",
-                "COORD_SYS_NAME",
-                "COORD_SYS_TYPE",
-                new Class<?>[] { CartesianCS.class, EllipsoidalCS.class, SphericalCS.class, VerticalCS.class},
-                new String[]   {"Cartesian",       "ellipsoidal",       "spherical",       "vertical"}),
-                               //Really upper-case C.
-        new TableInfo(CoordinateSystemAxis.class,
-                "[Coordinate Axis] AS CA INNER JOIN [Coordinate Axis Name] AS CAN" +
-                                 " ON CA.COORD_AXIS_NAME_CODE=CAN.COORD_AXIS_NAME_CODE",
-                "COORD_AXIS_CODE",
-                "COORD_AXIS_NAME"),
-
-        new TableInfo(Datum.class,
-                "[Datum]",
-                "DATUM_CODE",
-                "DATUM_NAME",
-                "DATUM_TYPE",
-                new Class<?>[] { GeodeticDatum.class, VerticalDatum.class, EngineeringDatum.class},
-                new String[]   {"geodetic",          "vertical",          "engineering"}),
-
-        new TableInfo(Ellipsoid.class,
-                "[Ellipsoid]",
-                "ELLIPSOID_CODE",
-                "ELLIPSOID_NAME"),
-
-        new TableInfo(PrimeMeridian.class,
-                "[Prime Meridian]",
-                "PRIME_MERIDIAN_CODE",
-                "PRIME_MERIDIAN_NAME"),
-
-        new TableInfo(CoordinateOperation.class,
-                "[Coordinate_Operation]",
-                "COORD_OP_CODE",
-                "COORD_OP_NAME",
-                "COORD_OP_TYPE",
-                new Class<?>[] { Projection.class, Conversion.class, Transformation.class},
-                new String[]   {"conversion",     "conversion",     "transformation"}),
-                // Note: Projection is handle in a special way.
-
-        new TableInfo(OperationMethod.class,
-                "[Coordinate_Operation Method]",
-                "COORD_OP_METHOD_CODE",
-                "COORD_OP_METHOD_NAME"),
-
-        new TableInfo(ParameterDescriptor.class,
-                "[Coordinate_Operation Parameter]",
-                "PARAMETER_CODE",
-                "PARAMETER_NAME"),
-
-        new TableInfo(Unit.class,
-                "[Unit of Measure]",
-                "UOM_CODE",
-                "UNIT_OF_MEAS_NAME")
-    };
-
-    ///////////////////////////////////////////////////////////////////////////////
-    ////////                                                               ////////
-    ////////                    END OF HARD CODED VALUES                   ////////
-    ////////                                                               ////////
-    ////////    NOTE: 'createFoo(...)' methods may still have hard-coded   ////////
-    ////////    values (others than SQL statements) in 'equalsIgnoreCase'  ////////
-    ////////    expressions.                                               ////////
-    ///////////////////////////////////////////////////////////////////////////////
-
-
-
 
     /**
      * The name for the transformation accuracy metadata.
@@ -284,13 +187,6 @@ public abstract class EPSGFactory extend
             Vocabulary.formatInternational(Vocabulary.Keys.TransformationAccuracy);
 
     /**
-     * The authority for this database. Will be created only when first needed. This authority will contain
-     * the database version in the {@linkplain Citation#getEdition() edition} attribute, together with the
-     * {@linkplain Citation#getEditionDate() edition date}.
-     */
-    private Citation authority;
-
-    /**
      * The namespace of EPSG names and codes. This namespace is needed by all {@code createFoo(String)} methods.
      * The {@code EPSGFactory} constructor relies on the {@link #nameFactory} caching mechanism for giving us
      * the same {@code NameSpace} instance than the one used by previous {@code EPSGFactory} instances, if any.
@@ -299,7 +195,7 @@ public abstract class EPSGFactory extend
 
     /**
      * Last object type returned by {@link #createObject(String)}, or -1 if none.
-     * This type is an index in the {@link #TABLES_INFO} array and is strictly for {@link #createObject} internal use.
+     * This type is an index in the {@link TableInfo#EPSG} array and is strictly for {@link #createObject} internal use.
      */
     private int lastObjectType = -1;
 
@@ -380,11 +276,12 @@ public abstract class EPSGFactory extend
     private final Map<Double,PositionalAccuracy> accuracies = new HashMap<>();
 
     /**
-     * Pool of naming systems, used for caching. There is usually few of them (about 15).
+     * Cache of naming systems other than EPSG. There is usually few of them (at most 15).
+     * This is used for aliases.
      *
      * @see #createProperties(String, String, String, String, boolean)
      */
-    private final Map<String,NameSpace> scopes = new HashMap<>();
+    private final Map<String,NameSpace> namingSystems = new HashMap<>();
 
     /**
      * The properties to be given the objects to construct.
@@ -412,6 +309,13 @@ public abstract class EPSGFactory extend
     protected final Connection connection;
 
     /**
+     * The locale for producing error messages. This is usually the default locale.
+     *
+     * @see #getLocale()
+     */
+    private Locale locale;
+
+    /**
      * Creates a factory using the given connection. The connection will be {@linkplain Connection#close() closed}
      * when this factory will be {@linkplain #dispose() disposed}.
      *
@@ -423,6 +327,30 @@ public abstract class EPSGFactory extend
         ArgumentChecks.ensureNonNull("connection", connection);
         this.connection = connection;
         this.namespace  = nameFactory.createNameSpace(nameFactory.createLocalName(null, Constants.EPSG), null);
+        this.locale     = Locale.getDefault(Locale.Category.DISPLAY);
+    }
+
+    /**
+     * Returns the locale used by this factory for producing error messages.
+     * This locale does not change the way data are read from the EPSG database.
+     *
+     * @return The locale for error messages.
+     */
+    @Override
+    public synchronized Locale getLocale() {
+        return locale;
+    }
+
+    /**
+     * Sets the locale to use for producing error messages.
+     * The given locale will be honored on a <cite>best effort</cite> basis.
+     * It does not change the way data are read from the EPSG database.
+     *
+     * @param locale The new locale to use for error message.
+     */
+    public synchronized void setLocale(final Locale locale) {
+        ArgumentChecks.ensureNonNull("locale", locale);
+        this.locale = locale;
     }
 
     /**
@@ -444,76 +372,77 @@ public abstract class EPSGFactory extend
      */
     @Override
     public synchronized Citation getAuthority() {
-        if (authority == null) {
-            final DefaultCitation c = new DefaultCitation("EPSG Geodetic Parameter Dataset");
-            c.setIdentifiers(Collections.singleton(new ImmutableIdentifier(null, null, Constants.EPSG)));
-            try {
-                /*
-                 * Get the most recent version number from the history table. We get the date in local timezone
-                 * instead then UTC because the date is for information purpose only, and the local timezone is
-                 * more likely to be shown nicely (without artificial hours) to the user.
-                 */
-                final String query = adaptSQL("SELECT VERSION_NUMBER, VERSION_DATE FROM [Version History]" +
-                                              " ORDER BY VERSION_DATE DESC, VERSION_HISTORY_CODE DESC");
-                String version = null;
-                try (Statement statement = connection.createStatement();
-                     ResultSet result = statement.executeQuery(query))
-                {
-                    while (result.next()) {
-                        version = result.getString(1);
-                        final Date date = result.getDate(2);                            // Local timezone.
-                        if (version != null && date != null) {                          // Paranoiac check.
-                            c.setEdition(new SimpleInternationalString(version));
-                            c.setEditionDate(date);
-                            break;
-                        }
+        /*
+         * We do not cache this citation because the caching service is already provided by ConcurrentAuthorityFactory
+         * and we overridden the trimAuthority(…) and noSuchAuthorityCode(…) methods that invoked this getAuthority().
+         */
+        final DefaultCitation c = new DefaultCitation("EPSG Geodetic Parameter Dataset");
+        c.setIdentifiers(Collections.singleton(new ImmutableIdentifier(null, null, Constants.EPSG)));
+        try {
+            /*
+             * Get the most recent version number from the history table. We get the date in local timezone
+             * instead then UTC because the date is for information purpose only, and the local timezone is
+             * more likely to be shown nicely (without artificial hours) to the user.
+             */
+            final String query = adaptSQL("SELECT VERSION_NUMBER, VERSION_DATE FROM [Version History]" +
+                                          " ORDER BY VERSION_DATE DESC, VERSION_HISTORY_CODE DESC");
+            String version = null;
+            try (Statement statement = connection.createStatement();
+                 ResultSet result = statement.executeQuery(query))
+            {
+                while (result.next()) {
+                    version = result.getString(1);
+                    final Date date = result.getDate(2);                            // Local timezone.
+                    if (version != null && date != null) {                          // Paranoiac check.
+                        c.setEdition(new SimpleInternationalString(version));
+                        c.setEditionDate(date);
+                        break;
                     }
                 }
-                /*
-                 * Add some hard-coded links to EPSG resources, and finally add the JDBC driver name and version number.
-                 * The list last OnlineResource looks like:
-                 *
-                 *    Linkage:      jdbc:derby:/my/path/to/SIS_DATA/Metadata
-                 *    Function:     Connection
-                 *    Description:  EPSG dataset version 8.8 on “Apache Derby Embedded JDBC Driver” version 10.12.
-                 */
-                final DatabaseMetaData metadata  = connection.getMetaData();
-addURIs:        for (int i=0; ; i++) {
-                    String url;
-                    OnLineFunction function;
-                    InternationalString description = null;
-                    switch (i) {
-                        case 0: url = "http://epsg-registry.org/"; function = OnLineFunction.SEARCH; break;
-                        case 1: url = "http://www.epsg.org/"; function = OnLineFunction.DOWNLOAD; break;
-                        case 2: {
-                            url = metadata.getURL();
-                            function = OnLineFunction.valueOf("CONNECTION");
-                            description = Messages.formatInternational(Messages.Keys.DataBase_4,
-                                    Constants.EPSG, version, metadata.getDatabaseProductName(),
-                                    Version.valueOf(metadata.getDatabaseMajorVersion(),
-                                                    metadata.getDatabaseMinorVersion()));
-                            break;
-                        }
-                        default: break addURIs;     // Finished adding all URIs.
-                    }
-                    final DefaultOnlineResource r = new DefaultOnlineResource();
-                    try {
-                        r.setLinkage(new URI(url));
-                    } catch (URISyntaxException exception) {
-                        unexpectedException("getAuthority", exception);
+            }
+            /*
+             * Add some hard-coded links to EPSG resources, and finally add the JDBC driver name and version number.
+             * The list last OnlineResource looks like:
+             *
+             *    Linkage:      jdbc:derby:/my/path/to/SIS_DATA/Metadata
+             *    Function:     Connection
+             *    Description:  EPSG dataset version 8.8 on “Apache Derby Embedded JDBC Driver” version 10.12.
+             */
+            final DatabaseMetaData metadata  = connection.getMetaData();
+addURIs:    for (int i=0; ; i++) {
+                String url;
+                OnLineFunction function;
+                InternationalString description = null;
+                switch (i) {
+                    case 0: url = "http://epsg-registry.org/"; function = OnLineFunction.SEARCH; break;
+                    case 1: url = "http://www.epsg.org/"; function = OnLineFunction.DOWNLOAD; break;
+                    case 2: {
+                        url = metadata.getURL();
+                        function = OnLineFunction.valueOf("CONNECTION");
+                        description = Messages.formatInternational(Messages.Keys.DataBase_4,
+                                Constants.EPSG, version, metadata.getDatabaseProductName(),
+                                Version.valueOf(metadata.getDatabaseMajorVersion(),
+                                                metadata.getDatabaseMinorVersion()));
+                        break;
                     }
-                    r.setFunction(function);
-                    r.setDescription(description);
-                    c.getOnlineResources().add(r);
-                }
-            } catch (SQLException exception) {
-                unexpectedException("getAuthority", exception);
-            } finally {
-                c.freeze();
-                authority = c;
+                    default: break addURIs;     // Finished adding all URIs.
+                }
+                final DefaultOnlineResource r = new DefaultOnlineResource();
+                try {
+                    r.setLinkage(new URI(url));
+                } catch (URISyntaxException exception) {
+                    unexpectedException("getAuthority", exception);
+                }
+                r.setFunction(function);
+                r.setDescription(description);
+                c.getOnlineResources().add(r);
             }
+        } catch (SQLException exception) {
+            unexpectedException("getAuthority", exception);
+        } finally {
+            c.freeze();
         }
-        return authority;
+        return c;
     }
 
     /**
@@ -550,7 +479,7 @@ addURIs:        for (int i=0; ; i++) {
             }
         }
         Map<String,String> result = Collections.emptyMap();
-        for (final TableInfo table : TABLES_INFO) {
+        for (final TableInfo table : TableInfo.EPSG) {
             /*
              * We test 'isAssignableFrom' in the two ways for catching the following use cases:
              *
@@ -617,7 +546,7 @@ addURIs:        for (int i=0; ; i++) {
     @Override
     public InternationalString getDescriptionText(final String code) throws NoSuchAuthorityCodeException, FactoryException {
         final String primaryKey = trimAuthority(code);
-        for (final TableInfo table : TABLES_INFO) {
+        for (final TableInfo table : TableInfo.EPSG) {
             final String text = getCodeMap(table.type).get(primaryKey);
             if (text != null) {
                 return (table.nameColumn != null) ? new SimpleInternationalString(text) : null;
@@ -627,21 +556,84 @@ addURIs:        for (int i=0; ; i++) {
     }
 
     /**
+     * Converts a code from an arbitrary name to the numerical identifier (the primary key).
+     * If the supplied code is already a numerical value, then it is returned unchanged.
+     * If the code is not found in the name column, it is returned unchanged as well
+     * so that the caller will produce an appropriate "Code not found" error message.
+     * If the code is found more than once, then an exception is thrown.
+     *
+     * <p>Note that this method includes a call to {@link #trimAuthority(String)},
+     * so there is no need to call it before or after this method.</p>
+     *
+     * <div class="note"><b>Note:</b>
+     * this method could be seen as the converse of above {@link #getDescriptionText(String)} method.</div>
+     *
+     * @param  type        The type of object to create.
+     * @param  code        The code to check.
+     * @param  table       The table where the code should appears.
+     * @param  codeColumn  The column name for the code.
+     * @param  nameColumn  The column name for the name.
+     * @return The numerical identifier (i.e. the table primary key value).
+     * @throws SQLException if an error occurred while reading the database.
+     */
+    private String toPrimaryKey(final Class<?> type, final String code, final String table,
+            final String codeColumn, final String nameColumn) throws SQLException, FactoryException
+    {
+        assert Thread.holdsLock(this);
+        String identifier = trimAuthority(code);
+        if (!isPrimaryKey(identifier)) {
+            /*
+             * The given string is not a numerical code. Search the value in the database.
+             * If a prepared statement is already available, reuse it providing that it was
+             * created for the current table. Otherwise we will create a new statement.
+             */
+            final String KEY = "NumericalIdentifier";
+            PreparedStatement statement = statements.get(KEY);
+            if (statement != null) {
+                if (!table.equals(lastTableForName)) {
+                    statements.remove(KEY);
+                    statement.close();
+                    statement        = null;
+                    lastTableForName = null;
+                }
+            }
+            if (statement == null) {
+                final String query = "SELECT " + codeColumn + " FROM " + table +
+                                     " WHERE " + nameColumn + " = ?";
+                statement = connection.prepareStatement(adaptSQL(query));
+                statements.put(KEY, statement);
+            }
+            // Do not use executeQuery(statement, primaryKey) because "identifier" is a name here.
+            statement.setString(1, identifier);
+            identifier = null;
+            try (ResultSet result = statement.executeQuery()) {
+                while (result.next()) {
+                    identifier = ensureSingleton(result.getString(1), identifier, code);
+                }
+            }
+            if (identifier == null) {
+                throw noSuchAuthorityCode(type, code);
+            }
+        }
+        return identifier;
+    }
+
+    /**
      * Returns a prepared statement for the specified name. Most {@link PreparedStatement} creations are performed
      * through this method, except {@link #toPrimaryKey} and {@link #createObject(String)}.
      *
-     * @param  key  A key uniquely identifying the caller (e.g. {@code "Ellipsoid"} for {@link #createEllipsoid(String)}).
-     * @param  sql  The SQL statement to use if for creating the {@link PreparedStatement} object.
-     *              Will be used only if no prepared statement was already created for the specified key.
+     * @param  table A key uniquely identifying the caller (e.g. {@code "Ellipsoid"} for {@link #createEllipsoid(String)}).
+     * @param  sql   The SQL statement to use if for creating the {@link PreparedStatement} object.
+     *               Will be used only if no prepared statement was already created for the specified key.
      * @return The prepared statement.
      * @throws SQLException if the prepared statement can not be created.
      */
-    private PreparedStatement prepareStatement(final String key, final String sql) throws SQLException {
+    private PreparedStatement prepareStatement(final String table, final String sql) throws SQLException {
         assert Thread.holdsLock(this);
-        PreparedStatement stmt = statements.get(key);
+        PreparedStatement stmt = statements.get(table);
         if (stmt == null) {
             stmt = connection.prepareStatement(adaptSQL(sql));
-            statements.put(key, stmt);
+            statements.put(table, stmt);
         }
         // Partial check that the statement is for the right SQL query.
         assert stmt.getParameterMetaData().getParameterCount() == CharSequences.count(sql, '?');
@@ -649,27 +641,65 @@ addURIs:        for (int i=0; ; i++) {
     }
 
     /**
-     * Makes sure that the last result was non-null.
-     * Used for {@code getString(…)}, {@code getDouble(…)} and {@code getInt(…)} methods only.
+     * Sets the value of the primary key to search for, and executes the given prepared statement.
+     * The primary key should be the value returned by {@link #toPrimaryKey}.
+     * Its values is assigned to the parameter #1.
+     *
+     * @param  stmt  The prepared statement in which to set the primary key.
+     * @param  primaryKey  The primary key.
+     * @throws NoSuchIdentifierException if the primary key has not been found.
+     * @throws SQLException if an error occurred while querying the database.
      */
-    private static void ensureNonNull(final ResultSet result, final int columnIndex, final Object code)
-            throws SQLException, FactoryDataException
+    private ResultSet executeQuery(final PreparedStatement stmt, final String primaryKey)
+            throws NoSuchIdentifierException, SQLException
     {
-        if (result.wasNull()) {
-            final ResultSetMetaData metadata = result.getMetaData();
-            final String column = metadata.getColumnName(columnIndex);
-            final String table  = metadata.getTableName (columnIndex);
-            result.close();
-            throw new FactoryDataException(Errors.format(Errors.Keys.NullValueInTable_3, table, column, code));
+        final int n;
+        try {
+            n = Integer.parseInt(primaryKey);
+        } catch (NumberFormatException e) {
+            final NoSuchIdentifierException ne = new NoSuchIdentifierException(error().getString(
+                    Errors.Keys.IllegalIdentifierForCodespace_2, Constants.EPSG, primaryKey), primaryKey);
+            ne.initCause(e);
+            throw ne;
         }
+        stmt.setInt(1, n);
+        return stmt.executeQuery();
+    }
+
+    /**
+     * Sets the value of the primary keys to search for, and executes the given prepared statement.
+     * The primary keys should be the values returned by {@link #toPrimaryKey}.
+     * Their values are assigned to parameters #1 and 2.
+     *
+     * @param  stmt The prepared statement in which to set the primary key.
+     * @param  primaryKey The primary key.
+     * @throws SQLException If an error occurred.
+     */
+    private ResultSet executeQuery(final PreparedStatement stmt, final String pk1, final String pk2)
+            throws NoSuchIdentifierException, SQLException
+    {
+        final int n1, n2;
+        String key = pk1;
+        try {
+            n1 = Integer.parseInt(      pk1);
+            n2 = Integer.parseInt(key = pk2);
+        } catch (NumberFormatException e) {
+            final NoSuchIdentifierException ne = new NoSuchIdentifierException(error().getString(
+                    Errors.Keys.IllegalIdentifierForCodespace_2, Constants.EPSG, key), key);
+            ne.initCause(e);
+            throw ne;
+        }
+        stmt.setInt(1, n1);
+        stmt.setInt(2, n2);
+        return stmt.executeQuery();
     }
 
     /**
      * Same as {@link #getString(ResultSet, int, Object)},
      * but reports the fault on an alternative column if the value is null.
      */
-    private static String getString(final ResultSet result, final int columnIndex,
-                                    final String    code,   final int columnFault)
+    private String getString(final ResultSet result, final int columnIndex,
+                             final String    code,   final int columnFault)
             throws SQLException, FactoryDataException
     {
         final String str = result.getString(columnIndex);
@@ -678,7 +708,7 @@ addURIs:        for (int i=0; ; i++) {
             final String column = metadata.getColumnName(columnFault);
             final String table  = metadata.getTableName (columnFault);
             result.close();
-            throw new FactoryDataException(Errors.format(Errors.Keys.NullValueInTable_3, table, column, code));
+            throw new FactoryDataException(error().getString(Errors.Keys.NullValueInTable_3, table, column, code));
         }
         return str.trim();
     }
@@ -694,7 +724,7 @@ addURIs:        for (int i=0; ; i++) {
      * @throws SQLException if an error occurred while querying the database.
      * @throws FactoryDataException if a null value was found.
      */
-    private static String getString(final ResultSet result, final int columnIndex, final Object code)
+    private String getString(final ResultSet result, final int columnIndex, final Object code)
             throws SQLException, FactoryDataException
     {
         final String value = result.getString(columnIndex);
@@ -713,7 +743,7 @@ addURIs:        for (int i=0; ; i++) {
      * @throws SQLException if an error occurred while querying the database.
      * @throws FactoryDataException if a null value was found.
      */
-    private static double getDouble(final ResultSet result, final int columnIndex, final Object code)
+    private double getDouble(final ResultSet result, final int columnIndex, final Object code)
             throws SQLException, FactoryDataException
     {
         final double value = result.getDouble(columnIndex);
@@ -732,7 +762,7 @@ addURIs:        for (int i=0; ; i++) {
      * @throws SQLException if an error occurred while querying the database.
      * @throws FactoryDataException if a null value was found.
      */
-    private static int getInt(final ResultSet result, final int columnIndex, final Object code)
+    private int getInt(final ResultSet result, final int columnIndex, final Object code)
             throws SQLException, FactoryDataException
     {
         final int value = result.getInt(columnIndex);
@@ -741,117 +771,19 @@ addURIs:        for (int i=0; ; i++) {
     }
 
     /**
-     * Sets the value of the primary key to search for, and executes the given prepared statement.
-     * The primary key should be the value returned by {@link #toPrimaryKey}.
-     * Its values is assigned to the parameter #1.
-     *
-     * @param  stmt  The prepared statement in which to set the primary key.
-     * @param  primaryKey  The primary key.
-     * @throws NoSuchIdentifierException if the primary key has not been found.
-     * @throws SQLException if an error occurred while querying the database.
-     */
-    private static ResultSet executeQuery(final PreparedStatement stmt, final String primaryKey)
-            throws NoSuchIdentifierException, SQLException
-    {
-        final int n;
-        try {
-            n = Integer.parseInt(primaryKey);
-        } catch (NumberFormatException e) {
-            final NoSuchIdentifierException ne = new NoSuchIdentifierException(Errors.format(
-                    Errors.Keys.IllegalIdentifierForCodespace_2, Constants.EPSG, primaryKey), primaryKey);
-            ne.initCause(e);
-            throw ne;
-        }
-        stmt.setInt(1, n);
-        return stmt.executeQuery();
-    }
-
-    /**
-     * Sets the value of the primary keys to search for, and executes the given prepared statement.
-     * The primary keys should be the values returned by {@link #toPrimaryKey}.
-     * Their values are assigned to parameters #1 and 2.
-     *
-     * @param  stmt The prepared statement in which to set the primary key.
-     * @param  primaryKey The primary key.
-     * @throws SQLException If an error occurred.
-     */
-    private static ResultSet executeQuery(final PreparedStatement stmt, final String pk1, final String pk2)
-            throws NoSuchIdentifierException, SQLException
-    {
-        final int n1, n2;
-        String key = pk1;
-        try {
-            n1 = Integer.parseInt(      pk1);
-            n2 = Integer.parseInt(key = pk2);
-        } catch (NumberFormatException e) {
-            final NoSuchIdentifierException ne = new NoSuchIdentifierException(Errors.format(
-                    Errors.Keys.IllegalIdentifierForCodespace_2, Constants.EPSG, key), key);
-            ne.initCause(e);
-            throw ne;
-        }
-        stmt.setInt(1, n1);
-        stmt.setInt(2, n2);
-        return stmt.executeQuery();
-    }
-
-    /**
-     * Converts a code from an arbitrary name to the numerical identifier (the primary key).
-     * If the supplied code is already a numerical value, then it is returned unchanged.
-     * If the code is not found in the name column, it is returned unchanged as well
-     * so that the caller will produce an appropriate "Code not found" error message.
-     * If the code is found more than once, then an exception is thrown.
-     *
-     * <p>Note that this method includes a call to {@link #trimAuthority(String)},
-     * so there is no need to call it before or after this method.</p>
-     *
-     * @param  type        The type of object to create.
-     * @param  code        The code to check.
-     * @param  table       The table where the code should appears.
-     * @param  codeColumn  The column name for the code.
-     * @param  nameColumn  The column name for the name.
-     * @return The numerical identifier (i.e. the table primary key value).
-     * @throws SQLException if an error occurred while reading the database.
+     * Makes sure that the last result was non-null.
+     * Used for {@code getString(…)}, {@code getDouble(…)} and {@code getInt(…)} methods only.
      */
-    private String toPrimaryKey(final Class<?> type, final String code, final String table,
-            final String codeColumn, final String nameColumn) throws SQLException, FactoryException
+    private void ensureNonNull(final ResultSet result, final int columnIndex, final Object code)
+            throws SQLException, FactoryDataException
     {
-        assert Thread.holdsLock(this);
-        String identifier = trimAuthority(code);
-        if (!isPrimaryKey(identifier)) {
-            /*
-             * The given string is not a numerical code. Search the value in the database.
-             * If a prepared statement is already available, reuse it providing that it was
-             * created for the current table. Otherwise we will create a new statement.
-             */
-            final String KEY = "NumericalIdentifier";
-            PreparedStatement statement = statements.get(KEY);
-            if (statement != null) {
-                if (!table.equals(lastTableForName)) {
-                    statements.remove(KEY);
-                    statement.close();
-                    statement        = null;
-                    lastTableForName = null;
-                }
-            }
-            if (statement == null) {
-                final String query = "SELECT " + codeColumn + " FROM " + table +
-                                     " WHERE " + nameColumn + " = ?";
-                statement = connection.prepareStatement(adaptSQL(query));
-                statements.put(KEY, statement);
-            }
-            // Do not use executeQuery(statement, primaryKey) because "identifier" is a name here.
-            statement.setString(1, identifier);
-            identifier = null;
-            try (ResultSet result = statement.executeQuery()) {
-                while (result.next()) {
-                    identifier = ensureSingleton(result.getString(1), identifier, code);
-                }
-            }
-            if (identifier == null) {
-                throw noSuchAuthorityCode(type, code);
-            }
+        if (result.wasNull()) {
+            final ResultSetMetaData metadata = result.getMetaData();
+            final String column = metadata.getColumnName(columnIndex);
+            final String table  = metadata.getTableName (columnIndex);
+            result.close();
+            throw new FactoryDataException(error().getString(Errors.Keys.NullValueInTable_3, table, column, code));
         }
-        return identifier;
     }
 
     /**
@@ -867,14 +799,82 @@ addURIs:        for (int i=0; ; i++) {
      * @param  code The EPSG code (for formatting error message).
      * @throws FactoryDataException if a duplication has been detected.
      */
-    private static <T> T ensureSingleton(final T newValue, final T oldValue, final String code) throws FactoryDataException {
+    private <T> T ensureSingleton(final T newValue, final T oldValue, final String code) throws FactoryDataException {
         if (oldValue == null) {
             return newValue;
         }
         if (oldValue.equals(newValue)) {
             return oldValue;
         }
-        throw new FactoryDataException(Errors.format(Errors.Keys.DuplicatedIdentifier_1, code));
+        throw new FactoryDataException(error().getString(Errors.Keys.DuplicatedIdentifier_1, code));
+    }
+
+    /**
+     * Returns {@code true} if the given table {@code name} matches the {@code expected} name.
+     * The given {@code name} may be prefixed by {@code "epsg_"} and may contain abbreviations of the full name.
+     * For example {@code "epsg_coordoperation"} is considered as a match for {@code "Coordinate_Operation"}.
+     *
+     * @param  expected  The expected table name (e.g. {@code "Coordinate_Operation"}).
+     * @param  name      The actual table name.
+     * @return Whether the given {@code name} is considered to match the expected name.
+     */
+    static boolean tableMatches(final String expected, String name) {
+        if (name == null) {
+            return false;
+        }
+        if (name.startsWith(TABLE_PREFIX)) {
+            name = name.substring(TABLE_PREFIX.length());
+        }
+        return CharSequences.isAcronymForWords(name, expected);
+    }
+
+    /**
+     * Logs a warning saying that the given code is deprecated and returns a message proposing a replacement.
+     *
+     * @param  table  The table of the deprecated code.
+     * @param  code   The deprecated code.
+     * @return A message proposing a replacement, or {@code null} if none.
+     */
+    private InternationalString getDeprecation(final String table, final String code)
+            throws SQLException, NoSuchIdentifierException
+    {
+        final PreparedStatement stmt = prepareStatement("[Deprecation]",
+                "SELECT OBJECT_TABLE_NAME, DEPRECATION_REASON, REPLACED_BY" +
+                " FROM [Deprecation] WHERE OBJECT_CODE = ?");
+        String reason = null;
+        Object replacedBy = null;
+        try (ResultSet result = executeQuery(stmt, code)) {
+            while (result.next()) {
+                if (tableMatches(table, result.getString(1))) {
+                    reason = result.getString(2);
+                    replacedBy = result.getInt(3);
+                    if (result.wasNull()) {
+                        replacedBy = null;
+                    }
+                    break;
+                }
+            }
+        }
+        if (replacedBy == null) {
+            replacedBy = '(' + Vocabulary.getResources(locale).getString(Vocabulary.Keys.None).toLowerCase(locale) + ')';
+        }
+        /*
+         * Try to infer the method name from the table name. For example if the deprecated code was found in
+         * the [Coordinate Reference System] table, then we declare createCoordinateReferenceSystem(String)
+         * as the source of the log message.
+         */
+        String method = "create";
+        for (final TableInfo info : TableInfo.EPSG) {
+            if (tableMatches(info.table, table)) {
+                method += info.type.getSimpleName();
+                break;
+            }
+        }
+        LogRecord record = Messages.getResources(locale).getLogRecord(Level.WARNING, Messages.Keys.DeprecatedCode_3,
+                Constants.EPSG + DefaultNameSpace.DEFAULT_SEPARATOR + code, replacedBy, reason);
+        record.setLoggerName(Loggers.CRS_FACTORY);
+        Logging.log(EPSGFactory.class, method, record);
+        return Vocabulary.formatInternational(Vocabulary.Keys.SupersededBy_1, replacedBy);
     }
 
     /**
@@ -888,19 +888,22 @@ addURIs:        for (int i=0; ; i++) {
      * @return The name together with a set of properties.
      */
     @SuppressWarnings("ReturnOfCollectionOrArrayField")
-    private Map<String,Object> createProperties(final String table, final String name, String code,
+    private Map<String,Object> createProperties(final String table, String name, String code,
             String remarks, final boolean deprecated) throws SQLException, FactoryException
     {
         properties.clear();
         GenericName gn = null;
-        final Citation authority = getAuthority();
+        final Citation authority = buffered.getAuthority();
         final InternationalString edition = authority.getEdition();
         final String version = (edition != null) ? edition.toString() : null;
         if (name != null) {
-            properties.put("name", gn = nameFactory.createLocalName(namespace, name.trim()));
-            properties.put(NamedIdentifier.AUTHORITY_KEY, authority);
-            properties.put(NamedIdentifier.CODE_KEY,      name.trim());
+            name = name.trim();
+            gn = nameFactory.createLocalName(namespace, name);
+            properties.put("name", gn);
+            properties.put(NamedIdentifier.CODE_KEY,      name);
             properties.put(NamedIdentifier.VERSION_KEY,   version);
+            properties.put(NamedIdentifier.AUTHORITY_KEY, authority);
+            properties.put(AbstractIdentifiedObject.LOCALE_KEY, locale);
             final NamedIdentifier id = new NamedIdentifier(properties);
             properties.clear();
             properties.put(IdentifiedObject.NAME_KEY, id);
@@ -909,7 +912,7 @@ addURIs:        for (int i=0; ; i++) {
             code = code.trim();
             final ImmutableIdentifier identifier;
             if (deprecated) {
-                identifier = new DeprecatedCode(authority, Constants.EPSG, code, version, null);
+                identifier = new DeprecatedCode(authority, Constants.EPSG, code, version, getDeprecation(table, code));
             } else {
                 identifier = new ImmutableIdentifier(authority, Constants.EPSG, code, version,
                                     (gn != null) ? gn.toInternationalString() : null);
@@ -920,55 +923,38 @@ addURIs:        for (int i=0; ; i++) {
             properties.put(IdentifiedObject.REMARKS_KEY, remarks);
         }
         /*
-         * Search for aliases.
+         * Search for aliases. Note that searching for the object code is not sufficient. We also need to check if the
+         * record is really from the table we are looking for since different tables may have objects with the same ID.
          */
-        List<GenericName> alias = null;
-        final PreparedStatement stmt;
-        stmt = prepareStatement(
-                "[Alias]", "SELECT NAMING_SYSTEM_NAME, ALIAS, OBJECT_TABLE_NAME" +
+        final List<GenericName> aliases = new ArrayList<>();
+        final PreparedStatement stmt = prepareStatement("[Alias]",
+                "SELECT OBJECT_TABLE_NAME, NAMING_SYSTEM_NAME, ALIAS" +
                 " FROM [Alias] INNER JOIN [Naming System]" +
                   " ON [Alias].NAMING_SYSTEM_CODE =" +
                 " [Naming System].NAMING_SYSTEM_CODE" +
                 " WHERE OBJECT_CODE = ?");
         try (ResultSet result = executeQuery(stmt, code)) {
             while (result.next()) {
-                String owner = result.getString(3);
-                if (owner != null) {
-                    /*
-                     * We have found an alias for a object having the ID we are looking for, but we need to check if
-                     * it is really from the same table since a few different tables have objects with the same ID.
-                     */
-                    if (owner.startsWith(TABLE_PREFIX)) {
-                        owner = owner.substring(TABLE_PREFIX.length());
-                    }
-                    if (!CharSequences.isAcronymForWords(owner, table)) {
-                        continue;
-                    }
-                }
-                final String scope = result.getString(1);
-                final String local = getString(result, 2, code);
-                final GenericName generic;
-                if (scope == null) {
-                    generic = nameFactory.createLocalName(null, local);
-                } else {
-                    NameSpace cached = scopes.get(scope);
-                    if (cached == null) {
-                        cached = nameFactory.createNameSpace(
-                                 nameFactory.createLocalName(null, scope),
-                                 Collections.singletonMap("separator", ":"));
-                        scopes.put(scope, cached);
+                if (tableMatches(table, result.getString(1))) {
+                    String naming = result.getString(2);
+                    String alias = getString(result, 3, code);
+                    NameSpace ns = null;
+                    if (naming != null) {
+                        naming = naming.trim();
+                        ns = namingSystems.get(naming);
+                        if (ns == null) {
+                            ns = nameFactory.createNameSpace(nameFactory.createLocalName(null, naming), null);
+                            namingSystems.put(naming, ns);
+                        }
                     }
-                    generic = nameFactory.createLocalName(cached, local);
-                }
-                if (alias == null) {
-                    alias = new ArrayList<>();
+                    aliases.add(nameFactory.createLocalName(ns, alias));
                 }
-                alias.add(generic);
             }
         }
-        if (alias != null) {
-            properties.put(IdentifiedObject.ALIAS_KEY, alias.toArray(new GenericName[alias.size()]));
+        if (!aliases.isEmpty()) {
+            properties.put(IdentifiedObject.ALIAS_KEY, aliases.toArray(new GenericName[aliases.size()]));
         }
+        properties.put(AbstractIdentifiedObject.LOCALE_KEY, locale);
         return properties;
     }
 
@@ -1031,6 +1017,44 @@ addURIs:        for (int i=0; ; i++) {
     }
 
     /**
+     * Removes the {@code "EPSG:"} prefix from the given string, if present.
+     * This method overrides the more generic implementation provided by the subclasses for efficiency.
+     * In particular, this method avoid to call the potentially costly {@link #getAuthority()} method.
+     *
+     * @param  code The code to trim.
+     * @return The code without the {@code "EPSG:"} prefix.
+     */
+    @Override
+    protected String trimAuthority(String code) {
+        int s = code.indexOf(DefaultNameSpace.DEFAULT_SEPARATOR);
+        if (s >= 0 && Constants.EPSG.equals(code.substring(0, s).trim())) {
+            code = code.substring(s+1).trim();
+        }
+        return code;
+    }
+
+    /**
+     * Creates an exception for an unknown authority code.
+     * This convenience method is provided for implementation of {@code createFoo(String)} methods.
+     *
+     * @param  type  The GeoAPI interface that was to be created (e.g. {@code CoordinateReferenceSystem.class}).
+     * @param  code  The unknown authority code.
+     * @return An exception initialized with an error message built from the specified informations.
+     */
+    @Override
+    protected NoSuchAuthorityCodeException noSuchAuthorityCode(final Class<?> type, final String code) {
+        return new NoSuchAuthorityCodeException(error().getString(Errors.Keys.NoSuchAuthorityCode_3,
+                Constants.EPSG, type, code), Constants.EPSG, trimAuthority(code), code);
+    }
+
+    /**
+     * Minor shortcut for fetching the error resources.
+     */
+    private Errors error() {
+        return Errors.getResources(locale);
+    }
+
+    /**
      * Logs a warning about an unexpected but non-fatal exception.
      *
      * @param method    The source method.

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/TableInfo.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/TableInfo.java?rev=1720255&r1=1720254&r2=1720255&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/TableInfo.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/TableInfo.java [UTF-8] Tue Dec 15 21:20:12 2015
@@ -16,6 +16,13 @@
  */
 package org.apache.sis.referencing.factory.sql;
 
+import javax.measure.unit.Unit;
+import org.opengis.referencing.cs.*;
+import org.opengis.referencing.crs.*;
+import org.opengis.referencing.datum.*;
+import org.opengis.referencing.operation.*;
+import org.opengis.parameter.ParameterDescriptor;
+
 
 /**
  * Information about a specific table. The MS-Access dialect of SQL is assumed;
@@ -28,6 +35,87 @@ package org.apache.sis.referencing.facto
  */
 final class TableInfo {
     /**
+     * List of tables and columns to test for codes values.
+     * Those tables are used by the {@link EPSGFactory#createObject(String)} method
+     * in order to detect which of the following methods should be invoked for a given code:
+     *
+     * {@link EPSGFactory#createCoordinateReferenceSystem(String)}
+     * {@link EPSGFactory#createCoordinateSystem(String)}
+     * {@link EPSGFactory#createDatum(String)}
+     * {@link EPSGFactory#createEllipsoid(String)}
+     * {@link EPSGFactory#createUnit(String)}
+     *
+     * The order is significant: it is the key for a {@code switch} statement.
+     */
+    static final TableInfo[] EPSG = {
+        new TableInfo(CoordinateReferenceSystem.class,
+                "[Coordinate Reference System]",
+                "COORD_REF_SYS_CODE",
+                "COORD_REF_SYS_NAME",
+                "COORD_REF_SYS_KIND",
+                new Class<?>[] { ProjectedCRS.class, GeographicCRS.class, GeocentricCRS.class,
+                                 VerticalCRS.class,  CompoundCRS.class,   EngineeringCRS.class},
+                new String[]   {"projected",        "geographic",        "geocentric",
+                                "vertical",         "compound",          "engineering"}),
+
+        new TableInfo(CoordinateSystem.class,
+                "[Coordinate System]",
+                "COORD_SYS_CODE",
+                "COORD_SYS_NAME",
+                "COORD_SYS_TYPE",
+                new Class<?>[] { CartesianCS.class, EllipsoidalCS.class, SphericalCS.class, VerticalCS.class},
+                new String[]   {"Cartesian",       "ellipsoidal",       "spherical",       "vertical"}),
+                               //Really upper-case C.
+        new TableInfo(CoordinateSystemAxis.class,
+                "[Coordinate Axis] AS CA INNER JOIN [Coordinate Axis Name] AS CAN" +
+                                 " ON CA.COORD_AXIS_NAME_CODE=CAN.COORD_AXIS_NAME_CODE",
+                "COORD_AXIS_CODE",
+                "COORD_AXIS_NAME"),
+
+        new TableInfo(Datum.class,
+                "[Datum]",
+                "DATUM_CODE",
+                "DATUM_NAME",
+                "DATUM_TYPE",
+                new Class<?>[] { GeodeticDatum.class, VerticalDatum.class, EngineeringDatum.class},
+                new String[]   {"geodetic",          "vertical",          "engineering"}),
+
+        new TableInfo(Ellipsoid.class,
+                "[Ellipsoid]",
+                "ELLIPSOID_CODE",
+                "ELLIPSOID_NAME"),
+
+        new TableInfo(PrimeMeridian.class,
+                "[Prime Meridian]",
+                "PRIME_MERIDIAN_CODE",
+                "PRIME_MERIDIAN_NAME"),
+
+        new TableInfo(CoordinateOperation.class,
+                "[Coordinate_Operation]",
+                "COORD_OP_CODE",
+                "COORD_OP_NAME",
+                "COORD_OP_TYPE",
+                new Class<?>[] { Projection.class, Conversion.class, Transformation.class},
+                new String[]   {"conversion",     "conversion",     "transformation"}),
+                // Note: Projection is handled in a special way.
+
+        new TableInfo(OperationMethod.class,
+                "[Coordinate_Operation Method]",
+                "COORD_OP_METHOD_CODE",
+                "COORD_OP_METHOD_NAME"),
+
+        new TableInfo(ParameterDescriptor.class,
+                "[Coordinate_Operation Parameter]",
+                "PARAMETER_CODE",
+                "PARAMETER_NAME"),
+
+        new TableInfo(Unit.class,
+                "[Unit of Measure]",
+                "UOM_CODE",
+                "UNIT_OF_MEAS_NAME")
+    };
+
+    /**
      * The class of object to be created.
      */
     final Class<?> type;
@@ -65,8 +153,8 @@ final class TableInfo {
     /**
      * Stores information about a specific table.
      */
-    TableInfo(final Class<?> type, final String table,
-              final String codeColumn, final String nameColumn)
+    private TableInfo(final Class<?> type, final String table,
+                      final String codeColumn, final String nameColumn)
     {
         this(type, table, codeColumn, nameColumn, null, null, null);
     }
@@ -74,9 +162,9 @@ final class TableInfo {
     /**
      * Stores information about a specific table.
      */
-    TableInfo(final Class<?> type,
-              final String table, final String codeColumn, final String nameColumn,
-              final String typeColumn, final Class<?>[] subTypes, final String[] typeNames)
+    private TableInfo(final Class<?> type,
+                      final String table, final String codeColumn, final String nameColumn,
+                      final String typeColumn, final Class<?>[] subTypes, final String[] typeNames)
     {
         this.type       = type;
         this.table      = table;

Added: sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/factory/sql/EPSGFactoryTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/factory/sql/EPSGFactoryTest.java?rev=1720255&view=auto
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/factory/sql/EPSGFactoryTest.java (added)
+++ sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/factory/sql/EPSGFactoryTest.java [UTF-8] Tue Dec 15 21:20:12 2015
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.referencing.factory.sql;
+
+import org.apache.sis.test.TestCase;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+
+/**
+ * Tests {@link EPSGFactory}.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @since   0.7
+ * @version 0.7
+ * @module
+ */
+public final strictfp class EPSGFactoryTest extends TestCase {
+    /**
+     * Tests {@link EPSGFactory#tableMatches(String, String)}.
+     */
+    @Test
+    public void testTableMatches() {
+        assertTrue(EPSGFactory.tableMatches("Coordinate_Operation",          "epsg_coordoperation"));
+        assertTrue(EPSGFactory.tableMatches("[Coordinate Reference System]", "epsg_coordinatereferencesystem"));
+    }
+}

Propchange: sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/factory/sql/EPSGFactoryTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/factory/sql/EPSGFactoryTest.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain;charset=UTF-8

Modified: sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java?rev=1720255&r1=1720254&r2=1720255&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java [UTF-8] Tue Dec 15 21:20:12 2015
@@ -177,6 +177,7 @@ import org.junit.BeforeClass;
     org.apache.sis.referencing.factory.CommonAuthorityFactoryTest.class,
     org.apache.sis.referencing.factory.AuthorityFactoryProxyTest.class,
     org.apache.sis.referencing.factory.IdentifiedObjectFinderTest.class,
+    org.apache.sis.referencing.factory.sql.EPSGFactoryTest.class,
 
     org.apache.sis.io.wkt.MathTransformParserTest.class,
     org.apache.sis.io.wkt.GeodeticObjectParserTest.class,

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=1720255&r1=1720254&r2=1720255&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 21:20:12 2015
@@ -129,6 +129,11 @@ public final class Messages extends Inde
         public static final short DataDirectory_2 = 24;
 
         /**
+         * Code “{0}” is deprecated and replaced by code {1}. Reason is: {2}
+         */
+        public static final short DeprecatedCode_3 = 29;
+
+        /**
          * Property “{0}” has been discarded in favor of “{1}”, because those two properties are
          * mutually exclusive.
          */

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=1720255&r1=1720254&r2=1720255&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 21:20:12 2015
@@ -28,6 +28,7 @@ DataDirectoryNotAuthorized_1    = Apache
 DataDirectoryNotReadable_2      = The \u201c{1}\u201d directory specified by the {0} environment variable exists but is not readable.
 DataDirectoryNotSpecified_1     = The \u201c{0}\u201d environment variable is not set.
 DataDirectoryNotWritable_2      = Apache SIS can not write in the \u201c{1}\u201d directory given by the {0} environment variable.
+DeprecatedCode_3                = Code \u201c{0}\u201d is deprecated and replaced by code {1}. Reason is: {2}
 DiscardedExclusiveProperty_2    = Property \u201c{0}\u201d has been discarded in favor of \u201c{1}\u201d, because those two properties are mutually exclusive.
 IgnoredPropertiesAfterFirst_1   = Ignored properties after the first occurrence of \u2018{0}\u2019.
 IgnoredPropertyAssociatedTo_1   = Ignored property associated to \u2018{0}\u2019.

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=1720255&r1=1720254&r2=1720255&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 21:20:12 2015
@@ -35,6 +35,7 @@ DataDirectoryNotAuthorized_1    = Apache
 DataDirectoryNotReadable_2      = Le r\u00e9pertoire \u00ab\u202f{1}\u202f\u00bb sp\u00e9cifi\u00e9 par la variable environnementale {0} existe bien mais ne peut pas \u00eatre lu.
 DataDirectoryNotSpecified_1     = La variable environnementale \u00ab\u202f{0}\u202f\u00bb n\u2019est pas d\u00e9finie.
 DataDirectoryNotWritable_2      = Apache SIS ne peut pas \u00e9crire dans le r\u00e9pertoire \u00ab\u202f{1}\u202f\u00bb sp\u00e9cifi\u00e9e par la variable environnementale {0}.
+DeprecatedCode_3                = Le code \u00ab\u202f{0}\u202f\u00bb est d\u00e9pr\u00e9ci\u00e9 et remplac\u00e9 par le code {1}. La raison est\u00a0: {2}
 DiscardedExclusiveProperty_2    = La propri\u00e9t\u00e9 \u00ab\u202f{0}\u202f\u00bb a \u00e9t\u00e9 \u00e9cart\u00e9e en faveur de \u00ab\u202f{1}\u202f\u00bb, parce que ces deux propri\u00e9t\u00e9s sont mutuellement exclusives.
 IgnoredPropertiesAfterFirst_1   = Des propri\u00e9t\u00e9s ont \u00e9t\u00e9 ignor\u00e9es apr\u00e8s la premi\u00e8re occurrence de \u2018{0}\u2019.
 IgnoredPropertyAssociatedTo_1   = Une propri\u00e9t\u00e9 associ\u00e9e \u00e0 \u2018{0}\u2019 a \u00e9t\u00e9 ignor\u00e9e.

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.java?rev=1720255&r1=1720254&r2=1720255&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.java [UTF-8] Tue Dec 15 21:20:12 2015
@@ -311,6 +311,11 @@ public final class Vocabulary extends In
         public static final short Name = 37;
 
         /**
+         * None
+         */
+        public static final short None = 91;
+
+        /**
          * Number of ‘NaN’
          */
         public static final short NumberOfNaN = 38;

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.properties
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.properties?rev=1720255&r1=1720254&r2=1720255&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.properties [ISO-8859-1] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.properties [ISO-8859-1] Tue Dec 15 21:20:12 2015
@@ -65,6 +65,7 @@ MeanValue               = Mean value
 MinimumValue            = Minimum value
 ModifiedJulian          = Modified Julian
 Name                    = Name
+None                    = None
 NumberOfValues          = Number of values
 NumberOfNaN             = Number of \u2018NaN\u2019
 Obligation              = Obligation

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary_fr.properties
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary_fr.properties?rev=1720255&r1=1720254&r2=1720255&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary_fr.properties [ISO-8859-1] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary_fr.properties [ISO-8859-1] Tue Dec 15 21:20:12 2015
@@ -72,6 +72,7 @@ MeanValue               = Valeur moyenne
 MinimumValue            = Valeur minimale
 ModifiedJulian          = Julien modifi\u00e9
 Name                    = Nom
+None                    = Aucun
 NumberOfValues          = Nombre de valeurs
 NumberOfNaN             = Nombre de \u2018NaN\u2019
 Obligation              = Obligation



Mime
View raw message