sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1723633 [1/2] - in /sis/branches/JDK8: core/sis-referencing/src/main/java/org/apache/sis/referencing/ core/sis-referencing/src/main/java/org/apache/sis/referencing/crs/ core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/ core...
Date Thu, 07 Jan 2016 22:05:24 GMT
Author: desruisseaux
Date: Thu Jan  7 22:05:23 2016
New Revision: 1723633

URL: http://svn.apache.org/viewvc?rev=1723633&view=rev
Log:
IdentifiedObjectFinder.find(...) of a ProjectedCRS should be able to ignore axis order of the base CRS.
This feature implies the addition of a new ComparisonMode.ALLOW_VARIANT, which tell to ignore axes in CRS comparison.
Special care is needed for the comparison of CoordinateOperation objects, since the MathTransform to be compared need
adjustement in axis order and units are not the same.

Modified:
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/AbstractIdentifiedObject.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/crs/AbstractDerivedCRS.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/crs/DefaultDerivedCRS.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/crs/DefaultProjectedCRS.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/AbstractCS.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DefaultEllipsoid.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/ConcurrentAuthorityFactory.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/IdentifiedObjectFinder.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGDataAccess.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/Matrices.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java
    sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/GeodeticObjectBuilder.java
    sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/crs/DefaultProjectedCRSTest.java
    sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/factory/IdentifiedObjectFinderTest.java
    sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/factory/sql/EPSGFactoryTest.java
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/ComparisonMode.java
    sis/branches/JDK8/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/MetadataReader.java

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/AbstractIdentifiedObject.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/AbstractIdentifiedObject.java?rev=1723633&r1=1723632&r2=1723633&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/AbstractIdentifiedObject.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/AbstractIdentifiedObject.java [UTF-8] Thu Jan  7 22:05:23 2016
@@ -644,6 +644,8 @@ public class AbstractIdentifiedObject ex
      *           <cite>When object name matter</cite> below.</td></tr>
      *   <tr><td>{@link ComparisonMode#APPROXIMATIVE APPROXIMATIVE}:</td>
      *       <td>Same as {@code IGNORE_METADATA}, with some tolerance threshold on numerical values.</td></tr>
+     *   <tr><td>{@link ComparisonMode#ALLOW_VARIANT ALLOW_VARIANT}:</td>
+     *       <td>Same as {@code APPROXIMATIVE}, but ignores coordinate system axes.</td></tr>
      *   <tr><td>{@link ComparisonMode#DEBUG DEBUG}:</td>
      *        <td>Special mode for figuring out why two objects expected to be equal are not.</td></tr>
      * </table>
@@ -718,6 +720,7 @@ public class AbstractIdentifiedObject ex
             }
             case IGNORE_METADATA:
             case APPROXIMATIVE:
+            case ALLOW_VARIANT:
             case DEBUG: {
                 return implementsSameInterface(object);
             }

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/crs/AbstractDerivedCRS.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/crs/AbstractDerivedCRS.java?rev=1723633&r1=1723632&r2=1723633&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/crs/AbstractDerivedCRS.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/crs/AbstractDerivedCRS.java [UTF-8] Thu Jan  7 22:05:23 2016
@@ -217,11 +217,21 @@ abstract class AbstractDerivedCRS<C exte
      */
     @Override
     public boolean equals(final Object object, final ComparisonMode mode) {
+        if (object == this) {
+            return true;
+        }
         if (super.equals(object, mode)) {
             final boolean strict = (mode == ComparisonMode.STRICT);
             /*
              * Avoid never-ending recursivity: Conversion has a 'targetCRS' field (inherited from
              * the AbstractCoordinateOperation super-class) that is set to this AbstractDerivedCRS.
+             *
+             * Do NOT compare the baseCRS explicitely. This is done implicitely in the comparison of the Conversion
+             * objects, since (this.baseCRS == Conversion.sourceCRS) in Apache SIS.  The reason why we delegate the
+             * comparison of that CRS to the Conversion object is because we want to ignore the baseCRS axes if the
+             * mode said to ignore metadata, but ignoring axis order and units has implication on the MathTransform
+             * instances to compare.  The AbstractCoordinateOperation.equals(…) method implementation handles those
+             * cases.
              */
             if (Semaphores.queryAndSet(Semaphores.COMPARING)) {
                 return true;

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/crs/DefaultDerivedCRS.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/crs/DefaultDerivedCRS.java?rev=1723633&r1=1723632&r2=1723633&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/crs/DefaultDerivedCRS.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/crs/DefaultDerivedCRS.java [UTF-8] Thu Jan  7 22:05:23 2016
@@ -484,6 +484,12 @@ public class DefaultDerivedCRS extends A
 
     /**
      * Compares this coordinate reference system with the specified object for equality.
+     * In addition to the metadata documented in the
+     * {@linkplain org.apache.sis.referencing.AbstractIdentifiedObject#equals(Object, ComparisonMode) parent class},
+     * this method considers coordinate system axes of the {@linkplain #getBaseCRS() base CRS} as metadata.
+     * This means that if the given {@code ComparisonMode} is {@code IGNORE_METADATA} or {@code APPROXIMATIVE},
+     * then axis order of the base CRS are ignored
+     * (but <strong>not</strong> axis order of <strong>this</strong> derived CRS).
      *
      * @param  object The object to compare to {@code this}.
      * @param  mode {@link ComparisonMode#STRICT STRICT} for performing a strict comparison, or
@@ -493,7 +499,7 @@ public class DefaultDerivedCRS extends A
      */
     @Override
     public boolean equals(final Object object, final ComparisonMode mode) {
-        return (object == this) || super.equals(object, mode);
+        return super.equals(object, mode);
     }
 
     /**

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/crs/DefaultProjectedCRS.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/crs/DefaultProjectedCRS.java?rev=1723633&r1=1723632&r2=1723633&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/crs/DefaultProjectedCRS.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/crs/DefaultProjectedCRS.java [UTF-8] Thu Jan  7 22:05:23 2016
@@ -288,6 +288,12 @@ public class DefaultProjectedCRS extends
 
     /**
      * Compares this coordinate reference system with the specified object for equality.
+     * In addition to the metadata documented in the
+     * {@linkplain org.apache.sis.referencing.AbstractIdentifiedObject#equals(Object, ComparisonMode) parent class},
+     * this method considers coordinate system axes of the {@linkplain #getBaseCRS() base CRS} as metadata.
+     * This means that if the given {@code ComparisonMode} is {@code IGNORE_METADATA} or {@code APPROXIMATIVE},
+     * then axis order of the base geographic CRS are ignored
+     * (but <strong>not</strong> axis order of <strong>this</strong> projected CRS).
      *
      * @param  object The object to compare to {@code this}.
      * @param  mode {@link ComparisonMode#STRICT STRICT} for performing a strict comparison, or
@@ -297,7 +303,7 @@ public class DefaultProjectedCRS extends
      */
     @Override
     public boolean equals(final Object object, final ComparisonMode mode) {
-        return (object == this) || super.equals(object, mode);
+        return super.equals(object, mode);
     }
 
     /**

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/AbstractCS.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/AbstractCS.java?rev=1723633&r1=1723632&r2=1723633&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/AbstractCS.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/AbstractCS.java [UTF-8] Thu Jan  7 22:05:23 2016
@@ -408,9 +408,11 @@ public class AbstractCS extends Abstract
                 if (dimension != that.getDimension()) {
                     return false;
                 }
-                for (int i=0; i<dimension; i++) {
-                    if (!deepEquals(getAxis(i), that.getAxis(i), mode)) {
-                        return false;
+                if (mode != ComparisonMode.ALLOW_VARIANT) {
+                    for (int i=0; i<dimension; i++) {
+                        if (!deepEquals(getAxis(i), that.getAxis(i), mode)) {
+                            return false;
+                        }
                     }
                 }
                 return true;

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DefaultEllipsoid.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DefaultEllipsoid.java?rev=1723633&r1=1723632&r2=1723633&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DefaultEllipsoid.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DefaultEllipsoid.java [UTF-8] Thu Jan  7 22:05:23 2016
@@ -279,7 +279,7 @@ public class DefaultEllipsoid extends Ab
      * @param unit          The units of the semi-major and semi-minor axis values.
      * @return An ellipsoid with the given axis length.
      *
-     * @see org.apache.sis.referencing.factory.GeodeticObjectFactory#createEllipsoide(Map, double, double, Unit)
+     * @see org.apache.sis.referencing.factory.GeodeticObjectFactory#createEllipsoid(Map, double, double, Unit)
      */
     public static DefaultEllipsoid createEllipsoid(final Map<String,?> properties,
                                                    final double semiMajorAxis,

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/ConcurrentAuthorityFactory.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/ConcurrentAuthorityFactory.java?rev=1723633&r1=1723632&r2=1723633&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/ConcurrentAuthorityFactory.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/ConcurrentAuthorityFactory.java [UTF-8] Thu Jan  7 22:05:23 2016
@@ -133,7 +133,7 @@ public abstract class ConcurrentAuthorit
      *
      * <p>Every access to this pool must be synchronized on {@code findPool}.</p>
      */
-    private final Map<IdentifiedObject, Set<IdentifiedObject>> findPool = new WeakHashMap<>();
+    private final Map<IdentifiedObject,FindEntry> findPool = new WeakHashMap<>();
 
     /**
      * Holds the reference to a Data Access Object used by {@link ConcurrentAuthorityFactory}, together with
@@ -232,7 +232,7 @@ public abstract class ConcurrentAuthorit
      *
      * @see #getTimeout(TimeUnit)
      */
-    private long timeout = 10_000_000_000L;     // 10 seconds
+    private long timeout = 60_000_000_000L;     // 1 minute
 
     /**
      * The maximal difference between the scheduled time and the actual time in order to perform the factory disposal,
@@ -526,6 +526,7 @@ public abstract class ConcurrentAuthorit
      */
     final void closeExpired() {
         final List<DAO> factories;
+        final boolean isEmpty;
         synchronized (availableDAOs) {
             factories = new ArrayList<>(availableDAOs.size());
             final Iterator<DataAccessRef<DAO>> it = availableDAOs.iterator();
@@ -559,7 +560,7 @@ public abstract class ConcurrentAuthorit
              * added to the queue only after completion of their work.
              * In the later case, release() will reschedule a new task.
              */
-            isCleanScheduled = !availableDAOs.isEmpty();
+            isCleanScheduled = !(isEmpty = availableDAOs.isEmpty());
         }
         /*
          * We must close the factories from outside the synchronized block.
@@ -570,6 +571,28 @@ public abstract class ConcurrentAuthorit
         } catch (Exception exception) {
             unexpectedException("closeExpired", exception);
         }
+        /*
+         * If the queue of Data Access Objects (DAO) become empty, this means that this ConcurrentAuthorityFactory
+         * has not created new object for a while (at least the amount of time given by the timeout), ignoring any
+         * request which may be under execution in another thread right now. Reduce the amount of objects retained
+         * in the cache of IdentifiedObjectFinder.find(…) results by removing all results containing more than one
+         * element, except for results of CoordinateReferenceSystem and CoordinateOperation lookups. The reason is
+         * that IdentifiedObjectFinder is almost always used for resolving CoordinateReferenceSystem objects, and
+         * all other kind of elements in the cache were dependencies searched as a side effect of the CRS search.
+         * Since we have the result of the CRS search, we often do not need anymore the result of dependency search.
+         *
+         * Touching 'findPool' also has the desired side-effect of letting WeakHashMap expunges stale entries.
+         */
+        if (isEmpty) {
+            synchronized (findPool) {
+                final Iterator<FindEntry> it = findPool.values().iterator();
+                while (it.hasNext()) {
+                    if (it.next().cleanup()) {
+                        it.remove();
+                    }
+                }
+            }
+        }
     }
 
     /**
@@ -1706,6 +1729,11 @@ public abstract class ConcurrentAuthorit
         private transient int acquireCount;
 
         /**
+         * The object in process of being searched, for information purpose only.
+         */
+        private transient IdentifiedObject searching;
+
+        /**
          * Creates a finder for the given type of objects.
          */
         Finder(final ConcurrentAuthorityFactory<?> factory) {
@@ -1787,48 +1815,111 @@ public abstract class ConcurrentAuthorit
         }
 
         /**
-         * Looks up an object from this authority factory which is approximatively equal to the specified object.
-         * The default implementation performs the same lookup than the Data Access Object and caches the result.
+         * Returns the cached value for the given object, or {@code null} if none.
          */
         @Override
-        public Set<IdentifiedObject> find(final IdentifiedObject object) throws FactoryException {
-            final Map<IdentifiedObject, Set<IdentifiedObject>> findPool = ((ConcurrentAuthorityFactory<?>) factory).findPool;
-            Set<IdentifiedObject> candidate;
+        final Set<IdentifiedObject> getFromCache(final IdentifiedObject object) {
+            final Map<IdentifiedObject,FindEntry> findPool = ((ConcurrentAuthorityFactory<?>) factory).findPool;
             synchronized (findPool) {
-                candidate = findPool.get(object);
+                final FindEntry entry = findPool.get(object);
+                if (entry != null) {
+                    // 'finder' may be null if this method is invoked directly by this Finder.
+                    return entry.get((finder != null ? finder : this).isIgnoringAxes());
+                }
             }
-            if (candidate != null) {
-                return candidate;
+            return null;
+        }
+
+        /**
+         * Stores the given result in the cache.
+         * This method shall be invoked only when {@link #getSearchDomain()} is not {@link Domain#DECLARATION}.
+         */
+        @Override
+        final Set<IdentifiedObject> cache(final IdentifiedObject object, Set<IdentifiedObject> result) {
+            final Map<IdentifiedObject,FindEntry> findPool = ((ConcurrentAuthorityFactory<?>) factory).findPool;
+            result = CollectionsExt.unmodifiableOrCopy(result);
+            FindEntry entry = new FindEntry();
+            synchronized (findPool) {
+                final FindEntry c = findPool.putIfAbsent(object, entry);
+                if (c != null) {
+                    entry = c;      // May happen if the same set has been computed in another thread.
+                }
+                // 'finder' should never be null since this method is not invoked directly by this Finder.
+                result = entry.set(finder.isIgnoringAxes(), result, object == searching);
             }
-            /*
-             * Nothing has been found in the cache. Delegates the search to the Data Access Object.
-             * We must delegate to 'finder' (not to 'super') in order to take advantage of overridden methods.
-             */
-            synchronized (this) {
-                try {
-                    acquire();
-                    candidate = finder.find(object);
-                } finally {
-                    release();
+            return result;
+        }
+
+        /**
+         * Looks up an object from this authority factory which is approximatively equal to the specified object.
+         * The default implementation performs the same lookup than the Data Access Object and caches the result.
+         */
+        @Override
+        public Set<IdentifiedObject> find(final IdentifiedObject object) throws FactoryException {
+            Set<IdentifiedObject> candidate = getFromCache(object);
+            if (candidate == null) {
+                /*
+                 * Nothing has been found in the cache. Delegates the search to the Data Access Object.
+                 * Note that the Data Access Object will itself callbacks our 'cache(…)' method, so there
+                 * is no need that we cache the result here.
+                 */
+                synchronized (this) {
+                    try {
+                        acquire();
+                        searching = object;
+                        candidate = finder.find(object);
+                    } finally {
+                        searching = null;
+                        release();
+                    }
                 }
             }
-            /*
-             * If the full scan was allowed, then stores the result even if empty so
-             * we can remember that no object has been found for the given argument.
-             * Note that we need to distinguish whether deprecated objects are included or not.
-             * Since the need to include deprecated objects should be rare, we do not cache them.
-             */
-            if (isFullScanAllowed() && !getIncludeDeprecated()) {
-                candidate = CollectionsExt.unmodifiableOrCopy(candidate);
-                Set<IdentifiedObject> concurrent;
-                synchronized (findPool) {
-                    concurrent = findPool.putIfAbsent(object, candidate);
+            return candidate;
+        }
+    }
+
+    /**
+     * Cache for the result of {@link IdentifiedObjectFinder#find(IdentifiedObject)} operations.
+     * All access to this object must be done in a block synchronized on {@link #findPool}.
+     */
+    private static final class FindEntry {
+        /** Result of the search with our without ignoring axes. */
+        private Set<IdentifiedObject> strict, lenient;
+
+        /** Whether the cache is the result of an explicit request instead than a dependency search. */
+        private boolean explicitStrict, explicitLenient;
+
+        /** Returns the cached instance. */
+        Set<IdentifiedObject> get(final boolean ignoreAxes) {
+            return ignoreAxes ? lenient : strict;
+        }
+
+        /** Cache an instance, or return previous instance if computed concurrently. */
+        @SuppressWarnings({"AssignmentToCollectionOrArrayFieldFromParameter", "ReturnOfCollectionOrArrayField"})
+        Set<IdentifiedObject> set(final boolean ignoreAxes, Set<IdentifiedObject> result, final boolean explicit) {
+            if (ignoreAxes) {
+                if (lenient != null) {
+                    result = lenient;
+                } else {
+                    lenient = result;
                 }
-                if (concurrent != null) {
-                    return concurrent;      // May happen if the same set has been computed in another thread.
+                explicitLenient |= explicit;
+            } else {
+                if (strict != null) {
+                    result = strict;
+                } else {
+                    strict = result;
                 }
+                explicitStrict |= explicit;
             }
-            return candidate;
+            return result;
+        }
+
+        /** Forgets the set that were not explicitely requested. */
+        boolean cleanup() {
+            if (!explicitStrict)  strict  = null;
+            if (!explicitLenient) lenient = null;
+            return (strict == null) && (lenient == null);
         }
     }
 

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/IdentifiedObjectFinder.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/IdentifiedObjectFinder.java?rev=1723633&r1=1723632&r2=1723633&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/IdentifiedObjectFinder.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/IdentifiedObjectFinder.java [UTF-8] Thu Jan  7 22:05:23 2016
@@ -30,16 +30,17 @@ import org.opengis.referencing.NoSuchAut
 import org.apache.sis.referencing.AbstractIdentifiedObject;
 import org.apache.sis.referencing.IdentifiedObjects;
 import org.apache.sis.internal.util.Citations;
+import org.apache.sis.internal.system.Loggers;
 import org.apache.sis.util.collection.BackingStoreException;
+import org.apache.sis.util.logging.Logging;
 import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.util.ComparisonMode;
 import org.apache.sis.util.Utilities;
-import org.apache.sis.util.resources.Errors;
 
 
 /**
  * Searches in an authority factory for objects approximatively equal to a given object.
- * This class can be used for fetching a fully {@linkplain AbstractIdentifiedObject identified object}
+ * This class can be used for fetching a fully defined {@linkplain AbstractIdentifiedObject identified object}
  * from an incomplete one, for example from an object without "{@code ID[…]}" or "{@code AUTHORITY[…]}"
  * element in <cite>Well Known Text</cite>.
  *
@@ -48,7 +49,7 @@ import org.apache.sis.util.resources.Err
  *   <li>Get a new instance by calling
  *       {@link GeodeticAuthorityFactory#newIdentifiedObjectFinder()}.</li>
  *   <li>Optionally configure that instance by calling its setter methods.</li>
- *   <li>Perform a search by invoking the {@link #find(IdentifiedObject)} method.</li>
+ *   <li>Perform a search by invoking the {@link #find(IdentifiedObject)} or {@link #findSingleton(IdentifiedObject)} method.</li>
  *   <li>The same {@code IdentifiedObjectFinder} instance can be reused for consecutive searches.</li>
  * </ol>
  *
@@ -66,10 +67,59 @@ import org.apache.sis.util.resources.Err
  */
 public class IdentifiedObjectFinder {
     /**
+     * The domain of the search (for example whether to include deprecated objects in the search).
+     *
+     * @author  Martin Desruisseaux (Geomatys)
+     * @since   0.7
+     * @version 0.7
+     * @module
+     */
+    public static enum Domain {
+        /**
+         * Fast lookup based only on embedded identifiers and names. If those identification information
+         * does not allow to locate an object in the factory, then the search will return an empty set.
+         *
+         * <div class="note"><b>Example:</b>
+         * if {@link IdentifiedObjectFinder#find(IdentifiedObject)} is invoked with an object having the {@code "4326"}
+         * {@linkplain AbstractIdentifiedObject#getIdentifiers() identifier}, then the {@code find(…)} method will invoke
+         * <code>factory.{@linkplain GeodeticAuthorityFactory#createGeographicCRS(String) createGeographicCRS}("4326")</code>
+         * and compare the object from the factory with the object to search.
+         * If the objects do not match, then another attempt will be done using the
+         * {@linkplain AbstractIdentifiedObject#getName() object name}. If using name does not work neither,
+         * then {@code find(…)} method makes no other attempt and returns an empty set.
+         * </div>
+         */
+        DECLARATION,
+
+        /**
+         * Lookup based on valid (non-deprecated) objects known to the factory.
+         * First, a fast lookup is performed based on {@link #DECLARATION}.
+         * If the fast lookup gave no result, then a more extensive search is performed by scanning the content
+         * of the dataset.
+         *
+         * <div class="note"><b>Example:</b>
+         * if {@link IdentifiedObjectFinder#find(IdentifiedObject)} is invoked with an object equivalent to the
+         * {@linkplain org.apache.sis.referencing.CommonCRS#WGS84 WGS84} geographic CRS but does not declare the
+         * {@code "4326"} identifier and does not have the <cite>"WGS 84"</cite> name, then the search based on
+         * {@link #DECLARATION} will give no result. The {@code find(…)} method will then scan the dataset for
+         * geographic CRS using equivalent datum and coordinate system. This may be a costly operation.
+         * </div>
+         */
+        VALID_DATASET,
+
+        /**
+         * Lookup based on all objects (both valid and deprecated) known to the factory.
+         * This is the same search than {@link #VALID_DATASET} except that deprecated objects
+         * are included in the search.
+         */
+        ALL_DATASET
+    }
+
+    /**
      * The criterion for determining if a candidate found by {@code IdentifiedObjectFinder}
      * should be considered equals to the requested object.
      */
-    private static final ComparisonMode COMPARISON_MODE = ComparisonMode.APPROXIMATIVE;
+    static final ComparisonMode COMPARISON_MODE = ComparisonMode.APPROXIMATIVE;
 
     /**
      * The factory to use for creating objects. This is the factory specified at construction time.
@@ -90,38 +140,18 @@ public class IdentifiedObjectFinder {
     private IdentifiedObjectFinder wrapper;
 
     /**
-     * The object in process of being searched, or {@code null} if none.
-     * This is used by {@link #find(IdentifiedObject)} for detecting recursivity.
-     */
-    private transient IdentifiedObject searching;
-
-    /**
-     * {@code true} if the {@link #find(IdentifiedObject)} method is invoked from the {@link #wrapper}.
-     * This is used for controlling the interactions with {@link #wrapper} during recursive calls.
-     * Example with {@code EPSGDataAccess}:
+     * The domain of the search (for example whether to include deprecated objects in the search).
      *
-     * <ol>
-     *   <li>When searching for a {@code ProjectedCRS}, first search for its base {@code GeodeticCRS}
-     *       in order to restrict the search scope.</li>
-     *   <li>The search for {@code GeodeticCRS} will in turn searches for {@code GeodeticDatum} in order
-     *       to restrict the search scope.</li>
-     *   <li>The search for {@code GeodeticDatum} will in turn searches for {@code Ellipsoid} in order
-     *       to restrict the search scope.</li>
-     * </ol>
-     *
-     * As seen from the above examples, there is usually no more than 4 recursive calls.
+     * @see #getSearchDomain()
      */
-    private transient boolean isInvokedFromWrapper;
+    private Domain domain = Domain.VALID_DATASET;
 
     /**
-     * {@code true} for performing full scans, or {@code false} otherwise.
-     */
-    private boolean fullScan = true;
-
-    /**
-     * {@code true} if the search should include deprecated objects.
+     * {@code true} if the search should ignore coordinate system axes.
+     *
+     * @see #isIgnoringAxes()
      */
-    private boolean includeDeprecated;
+    private boolean ignoreAxes;
 
     /**
      * Creates a finder using the specified factory.
@@ -151,8 +181,8 @@ public class IdentifiedObjectFinder {
      */
     final void setWrapper(final IdentifiedObjectFinder other) {
         wrapper = other;
-        setFullScanAllowed(other.isFullScanAllowed());
-        setIncludeDeprecated(other.getIncludeDeprecated());
+        setSearchDomain(other.domain);
+        setIgnoringAxes(other.ignoreAxes);
     }
 
     /**
@@ -167,45 +197,82 @@ public class IdentifiedObjectFinder {
     }
 
     /**
-     * If {@code true}, an exhaustive full scan against all registered objects will be performed (may be slow).
-     * Otherwise only a fast lookup based on embedded identifiers and names will be performed.
-     * The default value is {@code true}.
+     * Returns the domain of the search (for example whether to include deprecated objects in the search).
+     * If {@code DECLARATION}, only a fast lookup based on embedded identifiers and names will be performed.
+     * Otherwise an exhaustive full scan against all registered objects will be performed (may be slow).
      *
-     * @return {@code true} if exhaustive scans are allowed, or {@code false} for faster but less complete searches.
+     * <p>The default value is {@link Domain#VALID_DATASET}.</p>
+     *
+     * @return The domain of the search.
      */
-    public boolean isFullScanAllowed() {
-        return fullScan;
+    public Domain getSearchDomain() {
+        return domain;
     }
 
     /**
-     * Sets whether an exhaustive scan against all registered objects is allowed.
-     * The default value is {@code true}.
+     * Sets the domain of the search (for example whether to include deprecated objects in the search).
+     * If this method is never invoked, then the default value is {@link Domain#VALID_DATASET}.
      *
-     * @param allowed {@code true} for allowing exhaustive scans,
-     *        or {@code false} for faster but less complete searches.
+     * @param domain The domain of the search.
      */
-    public void setFullScanAllowed(final boolean allowed) {
-        fullScan = allowed;
+    public void setSearchDomain(final Domain domain) {
+        ArgumentChecks.ensureNonNull("domain", domain);
+        this.domain = domain;
     }
 
     /**
-     * If {@code true} and full scans are allowed, then the search will include deprecated objects.
+     * Returns {@code true} if the search should ignore coordinate system axes.
      * The default value is {@code false}.
      *
-     * @return {@code true} if the search should include deprecated objects.
+     * @return {@code true} if the search should ignore coordinate system axes.
      */
-    public boolean getIncludeDeprecated() {
-        return includeDeprecated;
+    public boolean isIgnoringAxes() {
+        return ignoreAxes;
     }
 
     /**
-     * Sets whether the search should include deprecated objects.
-     * The default value is {@code false}.
+     * Sets whether the search should ignore coordinate system axes.
+     * If this property is set to {@code true}, then the search will compare only the coordinate system type
+     * and dimension. The axis names, orientation and units will be ignored. For example the {@code find(…)}
+     * method may return a Coordinate Reference System object with (<var>latitude</var>, <var>longitude</var>)
+     * axes even if the given object had (<var>longitude</var>, <var>latitude</var>) axes.
+     *
+     * @param ignore {@code true} if the search should ignore coordinate system axes.
+     */
+    public void setIgnoringAxes(final boolean ignore) {
+        ignoreAxes = ignore;
+    }
+
+    /**
+     * Returns {@code true} if a candidate found by {@code IdentifiedObjectFinder} should be considered equals to the
+     * requested object. This method invokes the {@code equals(…)} method on the {@code candidate} argument instead
+     * than on the user-specified {@code object} on the assumption that implementations coming from the factory are
+     * more reliable than user-specified objects.
+     */
+    private boolean match(final IdentifiedObject candidate, final IdentifiedObject object) {
+        return Utilities.deepEquals(candidate, object, ignoreAxes ? ComparisonMode.ALLOW_VARIANT : COMPARISON_MODE);
+    }
+
+    /**
+     * Returns the cached value for the given object, or {@code null} if none.
+     */
+    Set<IdentifiedObject> getFromCache(final IdentifiedObject object) {
+        return (wrapper != null) ? wrapper.getFromCache(object) : null;
+    }
+
+    /**
+     * Stores the given result in the cache, if any.
+     * This method will be invoked by {@link #find(IdentifiedObject)}
+     * only if {@link #getSearchDomain()} is not {@link Domain#DECLARATION}.
      *
-     * @param include {@code true} for including deprecated objects in the search.
+     * @return The given {@code result}, or another set equal to the result if it has been computed
+     *         concurrently in another thread.
      */
-    public void setIncludeDeprecated(final boolean include) {
-        includeDeprecated = include;
+    Set<IdentifiedObject> cache(final IdentifiedObject object, Set<IdentifiedObject> result) {
+        if (wrapper != null) {
+            result = wrapper.cache(object, result);
+        }
+        return result;
     }
 
     /**
@@ -220,8 +287,8 @@ public class IdentifiedObjectFinder {
      *   <li>If the authority factory can create objects from their {@linkplain AbstractIdentifiedObject#getName() name}
      *       in addition of identifiers, then the name and {@linkplain AbstractIdentifiedObject#getAlias() aliases} are
      *       used for creating objects to be tested.</li>
-     *   <li>If {@linkplain #isFullScanAllowed() full scan is allowed}, then full {@linkplain #getCodeCandidates
-     *       set of candidate codes} is used for creating objects to be tested.</li>
+     *   <li>If a full scan of the dataset is allowed, then full {@linkplain #getCodeCandidates set of candidate codes}
+     *       is used for creating objects to be tested.</li>
      * </ul>
      *
      * The created objects which are equal to the specified object in the
@@ -233,61 +300,43 @@ public class IdentifiedObjectFinder {
      */
     public Set<IdentifiedObject> find(final IdentifiedObject object) throws FactoryException {
         ArgumentChecks.ensureNonNull("object", object);
-        final IdentifiedObject state = searching;
-        if (state == object) {
-            throw new IllegalArgumentException(Errors.format(Errors.Keys.RecursiveCreateCallForKey_1, object.getName()));
-        }
-        if (state != null) {
-            /*
-             * This 'find' method may be invoked recursively by some implementations that need to resolve
-             * the dependencies of the given object. For example if the given object is a geodetic CRS,
-             * then the finder backed by the EPSG database will first search for a matching ellipsoid in
-             * order to reduce the scope of the search.
-             *
-             * When invoked recursively, we delegate to the wrapper if it exists. The wrapper is typically
-             * a ConcurrentAuthorityFactory.Finder, in which case we want to leverage its cache capability.
-             */
-            if (wrapper != null && !isInvokedFromWrapper) try {
-                isInvokedFromWrapper = true;
-                return wrapper.find(object);
+        Set<IdentifiedObject> result = getFromCache(object);
+        if (result == null) {
+            final AuthorityFactoryProxy<?> previous = proxy;
+            proxy = AuthorityFactoryProxy.getInstance(object.getClass());
+            try {
+                /*
+                 * First check if one of the identifiers can be used to find directly an identified object.
+                 * Verify that the object that we found is actually equal to given one; we do not blindly
+                 * trust the identifiers in the user object.
+                 */
+                IdentifiedObject candidate = createFromIdentifiers(object);
+                if (candidate != null) {
+                    return Collections.singleton(candidate);    // Not worth to cache.
+                }
+                /*
+                 * We are unable to find the object from its identifiers. Try a quick name lookup.
+                 * Some implementations like the one backed by the EPSG database are capable to find
+                 * an object from its name.
+                 */
+                candidate = createFromNames(object);
+                if (candidate != null) {
+                    return Collections.singleton(candidate);    // Not worth to cache.
+                }
+                /*
+                 * Here we exhausted the quick paths.
+                 * Perform a full scan (costly) if we are allowed to, otherwise abandon.
+                 */
+                if (domain == Domain.DECLARATION) {
+                    return Collections.emptySet();              // Do NOT cache.
+                }
+                result = createFromCodes(object);
             } finally {
-                isInvokedFromWrapper = false;
+                proxy = previous;
             }
+            result = cache(object, result);     // Costly operation (even if the result is empty) worth to cache.
         }
-        final boolean ivf = isInvokedFromWrapper;
-        final AuthorityFactoryProxy<?> p = proxy;
-        proxy = AuthorityFactoryProxy.getInstance(object.getClass());
-        isInvokedFromWrapper = false;
-        searching = object;
-        try {
-            /*
-             * First check if one of the identifiers can be used to find directly an identified object.
-             * Verify that the object that we found is actually equal to given one; we do not blindly
-             * trust the identifiers in the user object.
-             */
-            IdentifiedObject candidate = createFromIdentifiers(object);
-            if (candidate != null) {
-                return Collections.singleton(candidate);
-            }
-            /*
-             * We are unable to find the object from its identifiers. Try a quick name lookup.
-             * Some implementations like the one backed by the EPSG database are capable to find
-             * an object from its name.
-             */
-            candidate = createFromNames(object);
-            if (candidate != null) {
-                return Collections.singleton(candidate);
-            }
-            /*
-             * Here we exhausted the quick paths. Perform a full scan (costly) if we are allowed to,
-             * otherwise abandon.
-             */
-            return fullScan ? createFromCodes(object) : Collections.emptySet();
-        } finally {
-            isInvokedFromWrapper = ivf;
-            searching = state;
-            proxy = p;
-        }
+        return result;
     }
 
     /**
@@ -345,9 +394,10 @@ public class IdentifiedObjectFinder {
                     candidate = create(code);
                 } catch (NoSuchAuthorityCodeException e) {
                     // The identifier was not recognized. No problem, let's go on.
+                    exceptionOccurred(e);
                     continue;
                 }
-                if (Utilities.deepEquals(candidate, object, COMPARISON_MODE)) {
+                if (match(candidate, object)) {
                     return candidate;
                 }
             }
@@ -383,9 +433,10 @@ public class IdentifiedObjectFinder {
              *       this attempt may fail for various reasons (character string not supported
              *       by the underlying database for primary key, duplicated name found, etc.).
              */
+            exceptionOccurred(e);
             candidate = null;
         }
-        if (Utilities.deepEquals(candidate, object, COMPARISON_MODE)) {
+        if (match(candidate, object)) {
             return candidate;
         }
         for (final GenericName id : object.getAlias()) {
@@ -394,9 +445,10 @@ public class IdentifiedObjectFinder {
                 candidate = create(code);
             } catch (FactoryException e) {
                 // The name was not recognized. No problem, let's go on.
+                exceptionOccurred(e);
                 continue;
             }
-            if (Utilities.deepEquals(candidate, object, COMPARISON_MODE)) {
+            if (match(candidate, object)) {
                 return candidate;
             }
         }
@@ -425,24 +477,16 @@ public class IdentifiedObjectFinder {
      * @see #createFromNames(IdentifiedObject)
      */
     private Set<IdentifiedObject> createFromCodes(final IdentifiedObject object) throws FactoryException {
-        boolean strict = false;
         final Set<IdentifiedObject> result = new LinkedHashSet<>();     // We need to preserve order.
         for (final String code : getCodeCandidates(object)) {
             final IdentifiedObject candidate;
             try {
                 candidate = create(code);
             } catch (FactoryException e) {
+                exceptionOccurred(e);
                 continue;
             }
-            if (Utilities.deepEquals(candidate, object, strict ? ComparisonMode.IGNORE_METADATA : COMPARISON_MODE)) {
-                if (!strict && Utilities.deepEquals(candidate, object, ComparisonMode.IGNORE_METADATA)) {
-                    /*
-                     * If we find at least one object without rounding error, do not accept rounding error for anyone.
-                     * The most typical case is when comparing ellipsoids.
-                     */
-                    result.clear();
-                    strict = true;
-                }
+            if (match(candidate, object)) {
                 result.add(candidate);
             }
         }
@@ -485,11 +529,13 @@ public class IdentifiedObjectFinder {
      * @throws FactoryException if an error occurred while fetching the set of code candidates.
      */
     protected Set<String> getCodeCandidates(final IdentifiedObject object) throws FactoryException {
-        AuthorityFactoryProxy<?> p = proxy;
-        if (object != searching) {
-            // Should not happen, unless the user invokes this method with unusual objects.
-            p = AuthorityFactoryProxy.getInstance(object.getClass());
-        }
-        return factory.getAuthorityCodes(p.type.asSubclass(IdentifiedObject.class));
+        return factory.getAuthorityCodes(proxy.type.asSubclass(IdentifiedObject.class));
+    }
+
+    /**
+     * Invoked when an exception occurred during the creation of a candidate from a code.
+     */
+    private static void exceptionOccurred(final FactoryException exception) {
+        Logging.recoverableException(Logging.getLogger(Loggers.CRS_FACTORY), IdentifiedObjectFinder.class, "fine", exception);
     }
 }

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGDataAccess.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGDataAccess.java?rev=1723633&r1=1723632&r2=1723633&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGDataAccess.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGDataAccess.java [UTF-8] Thu Jan  7 22:05:23 2016
@@ -73,6 +73,7 @@ import org.apache.sis.internal.referenci
 import org.apache.sis.internal.referencing.Formulas;
 import org.apache.sis.internal.system.Loggers;
 import org.apache.sis.internal.util.Constants;
+import org.apache.sis.internal.util.CollectionsExt;
 import org.apache.sis.metadata.iso.ImmutableIdentifier;
 import org.apache.sis.metadata.iso.citation.Citations;
 import org.apache.sis.metadata.iso.citation.DefaultCitation;
@@ -103,8 +104,9 @@ import org.apache.sis.util.ArgumentCheck
 import org.apache.sis.util.CharSequences;
 import org.apache.sis.util.Localized;
 import org.apache.sis.util.Version;
-import org.apache.sis.measure.Units;
+import org.apache.sis.util.collection.Containers;
 import org.apache.sis.measure.MeasurementRange;
+import org.apache.sis.measure.Units;
 
 // Branch-dependent imports
 
@@ -2771,12 +2773,14 @@ addURIs:    for (int i=0; ; i++) {
             String select = "COORD_REF_SYS_CODE";
             String from   = "Coordinate Reference System";
             final String where;
-            final Set<Comparable<?>> codes;
+            final Set<Number> codes;
+            boolean isFloat = false;
             if (object instanceof Ellipsoid) {
-                select = "ELLIPSOID_CODE";
-                from   = "Ellipsoid";
-                where  = "SEMI_MAJOR_AXIS";
-                codes  = Collections.singleton(((Ellipsoid) object).getSemiMajorAxis());
+                select  = "ELLIPSOID_CODE";
+                from    = "Ellipsoid";
+                where   = "SEMI_MAJOR_AXIS";
+                codes   = Collections.singleton(((Ellipsoid) object).getSemiMajorAxis());
+                isFloat = true;
             } else {
                 final IdentifiedObject dependency;
                 if (object instanceof GeneralDerivedCRS) {
@@ -2802,11 +2806,21 @@ addURIs:    for (int i=0; ; i++) {
                  * cache. This is desirable since this method may be invoked (indirectly) in a loop for many CRS objects
                  * sharing the same CoordinateSystem or Datum dependencies.
                  */
-                codes = new LinkedHashSet<>();
-                for (final IdentifiedObject dep : find(dependency)) {
+                final boolean previous = isIgnoringAxes();
+                final Set<IdentifiedObject> find;
+                try {
+                    setIgnoringAxes(true);
+                    find = find(dependency);
+                } finally {
+                    setIgnoringAxes(previous);
+                }
+                codes = new LinkedHashSet<>(Containers.hashMapCapacity(find.size()));
+                for (final IdentifiedObject dep : find) {
                     Identifier id = IdentifiedObjects.getIdentifier(dep, Citations.EPSG);
-                    if (id != null) {               // Should never be null, but let be safe.
-                        codes.add(id.getCode());
+                    if (id != null) try {           // Should never be null, but let be safe.
+                        codes.add(Integer.parseInt(id.getCode()));
+                    } catch (NumberFormatException e) {
+                        Logging.recoverableException(Logging.getLogger(Loggers.CRS_FACTORY), Finder.class, "getCodeCandidates", e);
                     }
                 }
                 codes.remove(null);                 // Paranoiac safety.
@@ -2816,7 +2830,28 @@ addURIs:    for (int i=0; ; i++) {
                 }
             }
             /*
-             * Build the SQL statement. The code can be any of the following types:
+             * Build the SQL statement. The parameters depend on whether the search criterion is an EPSG code
+             * or a numeric value.
+             *
+             * - If EPSG code, there is only one parameter which is the code to search.
+             * - If numeric, there is 3 parameters: lower value, upper value, exact value to search.
+             */
+            final StringBuilder buffer = new StringBuilder(60);
+            buffer.append("SELECT ").append(select).append(" FROM [").append(from).append("] WHERE ").append(where);
+            if (isFloat) {
+                buffer.append(">=? AND ").append(where).append("<=?");
+            } else {
+                buffer.append("=?");
+            }
+            buffer.append(getSearchDomain() == Domain.ALL_DATASET
+                          ? " ORDER BY ABS(DEPRECATED), "
+                          : " AND DEPRECATED=0 ORDER BY ");
+            if (isFloat) {
+                buffer.append("ABS(").append(select).append("-?), ");
+            }
+            buffer.append(select);          // Only for making order determinist.
+            /*
+             * Run the SQL statement. The parameter can be any of the following types:
              *
              * - A String, which represent a foreigner key as an integer value.
              *   The search will require an exact match.
@@ -2825,42 +2860,27 @@ addURIs:    for (int i=0; ; i++) {
              *   with a tolerance threshold of 1 cm for a planet of the size of Earth.
              */
             final Set<String> result = new LinkedHashSet<>();       // We need to preserve order in this set.
-            final StringBuilder buffer = new StringBuilder(60);
-            buffer.append("SELECT ").append(select).append(" FROM [").append(from).append("] WHERE ").append(where);
-            final int start = buffer.length();
-            for (final Comparable<?> code : codes) {
-                buffer.setLength(start);
-                if (code instanceof Number) {
-                    final double value = ((Number) code).doubleValue();
-                    final double tolerance = Math.abs(value * (Formulas.LINEAR_TOLERANCE / ReferencingServices.AUTHALIC_RADIUS));
-                    buffer.append(">=").append(value - tolerance).append(" AND ").append(where)
-                          .append("<=").append(value + tolerance);
-                } else {
-                    buffer.append('=').append(code);
-                }
-                if (!getIncludeDeprecated()) {
-                    buffer.append(" AND DEPRECATED=0");
-                }
-                buffer.append(" ORDER BY ABS(DEPRECATED), ");
-                if (code instanceof Number) {
-                    buffer.append("ABS(").append(select).append('-').append(code).append(')');
-                } else {
-                    buffer.append(select);          // Only for making order determinist.
-                }
-                try {
-                    final String sql = translator.apply(buffer.toString());
-                    try (Statement s = connection.createStatement();
-                         ResultSet r = s.executeQuery(sql))
-                    {
+            try (PreparedStatement s = connection.prepareStatement(translator.apply(buffer.toString()))) {
+                for (final Number code : codes) {
+                    if (isFloat) {
+                        final double value = code.doubleValue();
+                        final double tolerance = Math.abs(value * (Formulas.LINEAR_TOLERANCE / ReferencingServices.AUTHALIC_RADIUS));
+                        s.setDouble(1, value - tolerance);
+                        s.setDouble(2, value + tolerance);
+                        s.setDouble(3, value);
+                    } else {
+                        s.setInt(1, code.intValue());
+                    }
+                    try (ResultSet r = s.executeQuery()) {
                         while (r.next()) {
                             result.add(r.getString(1));
                         }
                     }
-                } catch (SQLException exception) {
-                    throw databaseFailure(Identifier.class, String.valueOf(code), exception);
                 }
-                result.remove(null);    // Should not have null element, but let be safe.
+            } catch (SQLException exception) {
+                throw databaseFailure(Identifier.class, String.valueOf(CollectionsExt.first(codes)), exception);
             }
+            result.remove(null);    // Should not have null element, but let be safe.
             return result;
         }
     }
@@ -3017,7 +3037,7 @@ addURIs:    for (int i=0; ; i++) {
     /**
      * Constructs an exception for a database failure.
      */
-    private FactoryException databaseFailure(Class<?> type, Comparable<?> code, SQLException cause) {
+    final FactoryException databaseFailure(Class<?> type, Comparable<?> code, SQLException cause) {
         return new FactoryException(error().getString(Errors.Keys.DatabaseError_2, type, code), cause);
     }
 

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java?rev=1723633&r1=1723632&r2=1723633&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java [UTF-8] Thu Jan  7 22:05:23 2016
@@ -24,6 +24,7 @@ import javax.xml.bind.annotation.XmlType
 import javax.xml.bind.annotation.XmlSeeAlso;
 import javax.xml.bind.annotation.XmlElement;
 import javax.xml.bind.annotation.XmlRootElement;
+import javax.measure.converter.ConversionException;
 import org.opengis.util.InternationalString;
 import org.opengis.metadata.Identifier;
 import org.opengis.metadata.extent.Extent;
@@ -44,8 +45,11 @@ import org.apache.sis.util.ComparisonMod
 import org.apache.sis.util.resources.Errors;
 import org.apache.sis.util.collection.Containers;
 import org.apache.sis.util.UnsupportedImplementationException;
+import org.apache.sis.util.logging.Logging;
 import org.apache.sis.parameter.Parameterized;
+import org.apache.sis.referencing.cs.CoordinateSystems;
 import org.apache.sis.referencing.AbstractIdentifiedObject;
+import org.apache.sis.referencing.operation.transform.MathTransforms;
 import org.apache.sis.referencing.operation.transform.PassThroughTransform;
 import org.apache.sis.internal.referencing.PositionalAccuracyConstant;
 import org.apache.sis.internal.referencing.ReferencingUtilities;
@@ -54,6 +58,7 @@ import org.apache.sis.internal.metadata.
 import org.apache.sis.internal.util.CollectionsExt;
 import org.apache.sis.internal.util.UnmodifiableArrayList;
 import org.apache.sis.internal.system.Semaphores;
+import org.apache.sis.internal.system.Loggers;
 
 import static org.apache.sis.util.Utilities.deepEquals;
 
@@ -718,7 +723,7 @@ check:      for (int isTarget=0; ; isTar
      * @return {@code true} if both objects are equal for the given comparison mode.
      */
     @Override
-    public boolean equals(final Object object, final ComparisonMode mode) {
+    public boolean equals(final Object object, ComparisonMode mode) {
         if (super.equals(object, mode)) {
             if (mode == ComparisonMode.STRICT) {
                 final AbstractCoordinateOperation that = (AbstractCoordinateOperation) object;
@@ -739,26 +744,68 @@ check:      for (int isTarget=0; ; isTar
                     }
                 }
             } else {
+                /*
+                 * This block is for all ComparisonModes other than STRICT. At this point we know that the metadata
+                 * properties (class, name, identifiers, etc.) match the criterion of the given comparison mode.
+                 * Before to continue perform the following checks:
+                 *
+                 *   - Scope, domain and accuracy properties only if NOT in "ignore metadata" mode.
+                 *   - Interpolation CRS in all cases (regardless if ignoring metadata or not).
+                 */
                 final CoordinateOperation that = (CoordinateOperation) object;
-                if (mode == ComparisonMode.BY_CONTRACT) {
-                    if (!deepEquals(getScope(),                       that.getScope(), mode) ||
-                        !deepEquals(getDomainOfValidity(),            that.getDomainOfValidity(), mode) ||
-                        !deepEquals(getCoordinateOperationAccuracy(), that.getCoordinateOperationAccuracy(), mode))
-                    {
-                        return false;
-                    }
-                }
-                if (deepEquals(getMathTransform(),    that.getMathTransform(),   mode) &&
-                    deepEquals(getSourceCRS(),        that.getSourceCRS(),       mode) &&
-                    deepEquals(getInterpolationCRS(), getInterpolationCRS(that), mode))
+                if ((mode.isIgnoringMetadata() ||
+                    (deepEquals(getScope(),                       that.getScope(), mode) &&
+                     deepEquals(getDomainOfValidity(),            that.getDomainOfValidity(), mode) &&
+                     deepEquals(getCoordinateOperationAccuracy(), that.getCoordinateOperationAccuracy(), mode))) &&
+                     deepEquals(getInterpolationCRS(),            getInterpolationCRS(that), mode))
                 {
+                    /*
+                     * At this point all metdata match or can be ignored. First, compare the targetCRS.
+                     * We need to perform this comparison only if this 'equals(…)' method is not invoked
+                     * from AbstractDerivedCRS, otherwise we would fall in an infinite recursive loop
+                     * (because targetCRS is the DerivedCRS, which in turn wants to compare this operation).
+                     *
+                     * We also opportunistically use this "anti-recursivity" check for another purpose.
+                     * The Semaphores.COMPARING flag should be set only when AbstractDerivedCRS is comparing
+                     * its "from base" conversion. The flag should never be set in any other circumstance,
+                     * since this is an internal Apache SIS mechanism. If we know that we are comparing the
+                     * AbstractDerivedCRS.fromBase conversion, then (in the way Apache SIS is implemented)
+                     * this.sourceCRS == AbstractDerivedCRS.baseCRS. Consequently we can relax the check
+                     * sourceCRS axis order if the mode is ComparisonMode.IGNORE_METADATA.
+                     */
                     if (Semaphores.queryAndSet(Semaphores.COMPARING)) {
-                        return true;
+                        if (mode.isIgnoringMetadata()) {
+                            mode = ComparisonMode.ALLOW_VARIANT;
+                        }
                     } else try {
-                        return deepEquals(getTargetCRS(), that.getTargetCRS(), mode);
+                        if (!deepEquals(getTargetCRS(), that.getTargetCRS(), mode)) {
+                            return false;
+                        }
                     } finally {
                         Semaphores.clear(Semaphores.COMPARING);
                     }
+                    /*
+                     * Now compare the sourceCRS, potentially with a relaxed ComparisonMode (see above comment).
+                     * If the comparison mode allows the two CRS to have different axis order and units, then we
+                     * need to take in account those difference before to compare the MathTransform. We proceed
+                     * by modifying 'tr2' as if it was a MathTransform with crs1 as the source instead of crs2.
+                     */
+                    final CoordinateReferenceSystem crs1 = this.getSourceCRS();
+                    final CoordinateReferenceSystem crs2 = that.getSourceCRS();
+                    if (deepEquals(crs1, crs2, mode)) {
+                        MathTransform tr1 = this.getMathTransform();
+                        MathTransform tr2 = that.getMathTransform();
+                        if (mode == ComparisonMode.ALLOW_VARIANT) try {
+                            final MathTransform swap = MathTransforms.linear(
+                                    CoordinateSystems.swapAndScaleAxes(crs1.getCoordinateSystem(),
+                                                                       crs2.getCoordinateSystem()));
+                            tr2 = MathTransforms.concatenate(swap, tr2);
+                        } catch (ConversionException | RuntimeException e) {
+                            Logging.recoverableException(Logging.getLogger(Loggers.COORDINATE_OPERATION),
+                                    AbstractCoordinateOperation.class, "equals", e);
+                        }
+                        return deepEquals(tr1, tr2, mode);
+                    }
                 }
             }
         }

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/Matrices.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/Matrices.java?rev=1723633&r1=1723632&r2=1723633&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/Matrices.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/Matrices.java [UTF-8] Thu Jan  7 22:05:23 2016
@@ -914,6 +914,7 @@ public final class Matrices extends Stat
             case BY_CONTRACT:     // Fall through
             case IGNORE_METADATA: return equals(m1, m2, 0, false);
             case DEBUG:           // Fall through
+            case ALLOW_VARIANT:   // Fall through
             case APPROXIMATIVE:   return equals(m1, m2, Numerics.COMPARISON_THRESHOLD, true);
             default: throw new IllegalArgumentException(Errors.format(
                     Errors.Keys.UnknownEnumValue_2, ComparisonMode.class, mode));

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java?rev=1723633&r1=1723632&r2=1723633&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java [UTF-8] Thu Jan  7 22:05:23 2016
@@ -498,6 +498,17 @@ public class DefaultMathTransformFactory
         }
 
         /**
+         * Sets the source ellipsoid to the given value.
+         * The source coordinate system is unconditionally set to {@code null}.
+         *
+         * @param ellipsoid The ellipsoid to set as the source (can be {@code null}).
+         */
+        public void setSource(final Ellipsoid ellipsoid) {
+            sourceEllipsoid = ellipsoid;
+            sourceCS = null;
+        }
+
+        /**
          * Sets the source coordinate system to the given value.
          * The source ellipsoid is unconditionally set to {@code null}.
          *
@@ -521,6 +532,17 @@ public class DefaultMathTransformFactory
         }
 
         /**
+         * Sets the target ellipsoid to the given value.
+         * The target coordinate system is unconditionally set to {@code null}.
+         *
+         * @param ellipsoid The ellipsoid to set as the target (can be {@code null}).
+         */
+        public void setTarget(final Ellipsoid ellipsoid) {
+            targetEllipsoid = ellipsoid;
+            targetCS = null;
+        }
+
+        /**
          * Sets the target coordinate system to the given value.
          * The target ellipsoid is unconditionally set to {@code null}.
          *

Modified: sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/GeodeticObjectBuilder.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/GeodeticObjectBuilder.java?rev=1723633&r1=1723632&r2=1723633&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/GeodeticObjectBuilder.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/GeodeticObjectBuilder.java [UTF-8] Thu Jan  7 22:05:23 2016
@@ -17,12 +17,14 @@
 package org.apache.sis.referencing;
 
 import javax.measure.unit.Unit;
+import javax.measure.quantity.Length;
 import org.opengis.parameter.ParameterValueGroup;
 import org.opengis.parameter.ParameterNotFoundException;
 import org.opengis.parameter.InvalidParameterValueException;
 import org.opengis.referencing.crs.GeographicCRS;
 import org.opengis.referencing.crs.ProjectedCRS;
 import org.opengis.referencing.cs.CartesianCS;
+import org.opengis.referencing.datum.Ellipsoid;
 import org.opengis.referencing.operation.Conversion;
 import org.opengis.referencing.operation.MathTransform;
 import org.opengis.referencing.operation.MathTransformFactory;
@@ -31,6 +33,8 @@ import org.opengis.util.FactoryException
 import org.apache.sis.internal.system.DefaultFactories;
 import org.apache.sis.referencing.crs.DefaultProjectedCRS;
 import org.apache.sis.referencing.operation.DefaultConversion;
+import org.apache.sis.referencing.operation.transform.DefaultMathTransformFactory;
+import org.apache.sis.internal.util.Constants;
 import org.apache.sis.util.resources.Errors;
 
 
@@ -43,7 +47,7 @@ import org.apache.sis.util.resources.Err
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.6
- * @version 0.6
+ * @version 0.7
  * @module
  */
 public strictfp class GeodeticObjectBuilder extends Builder<GeodeticObjectBuilder> {
@@ -177,8 +181,21 @@ public strictfp class GeodeticObjectBuil
      */
     public ProjectedCRS createProjectedCRS(final GeographicCRS baseCRS, final CartesianCS derivedCS) throws FactoryException {
         ensureConversionMethodSet();
+        final Ellipsoid ellipsoid = baseCRS.getDatum().getEllipsoid();
         final MathTransformFactory mtFactory = getMathTransformFactory();
-        final MathTransform mt = mtFactory.createBaseToDerived(baseCRS, parameters, derivedCS);
+        final MathTransform mt;
+        if (mtFactory instanceof DefaultMathTransformFactory) {
+            // The Apache SIS implementation avoid to override any value explicitely set by the user.
+            final DefaultMathTransformFactory.Context context = new DefaultMathTransformFactory.Context();
+            context.setSource(ellipsoid);
+            mt = ((DefaultMathTransformFactory) mtFactory).createParameterizedTransform(parameters, context);
+        } else {
+            // Fallback for non-SIS implementations.
+            final Unit<Length> unit = ellipsoid.getAxisUnit();
+            parameters.parameter(Constants.SEMI_MAJOR).setValue(ellipsoid.getSemiMajorAxis(), unit);
+            parameters.parameter(Constants.SEMI_MINOR).setValue(ellipsoid.getSemiMinorAxis(), unit);
+            mt = mtFactory.createParameterizedTransform(parameters);
+        }
         onCreate(false);
         try {
             /*

Modified: sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/crs/DefaultProjectedCRSTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/crs/DefaultProjectedCRSTest.java?rev=1723633&r1=1723632&r2=1723633&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/crs/DefaultProjectedCRSTest.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/crs/DefaultProjectedCRSTest.java [UTF-8] Thu Jan  7 22:05:23 2016
@@ -26,14 +26,19 @@ import org.opengis.referencing.crs.Geogr
 import org.opengis.referencing.cs.AxisDirection;
 import org.opengis.referencing.operation.Projection;
 import org.opengis.parameter.ParameterValueGroup;
-import org.opengis.test.Validators;
 import org.apache.sis.metadata.iso.citation.Citations;
+import org.apache.sis.referencing.CommonCRS;
 import org.apache.sis.referencing.cs.HardCodedCS;
 import org.apache.sis.referencing.GeodeticObjectBuilder;
 import org.apache.sis.internal.util.Constants;
 import org.apache.sis.internal.system.Loggers;
 import org.apache.sis.io.wkt.Convention;
 import org.apache.sis.util.logging.Logging;
+import org.apache.sis.util.ComparisonMode;
+import org.apache.sis.util.LenientComparable;
+
+// Test dependencies
+import org.opengis.test.Validators;
 import org.apache.sis.test.LoggingWatcher;
 import org.apache.sis.test.DependsOnMethod;
 import org.apache.sis.test.DependsOn;
@@ -49,7 +54,7 @@ import static org.apache.sis.test.Refere
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.6
- * @version 0.6
+ * @version 0.7
  * @module
  */
 @DependsOn({
@@ -455,4 +460,23 @@ public final strictfp class DefaultProje
         assertMarshalEqualsFile(XML_FILE, crs, STRICT, new String[] {"gml:name"},
                 new String[] {"xmlns:*", "xsi:schemaLocation", "gml:id"});
     }
+
+    /**
+     * Tests {@link DefaultProjectedCRS#equals(Object, ComparisonMode)}.
+     * In particular, we want to test the ability to ignore axis order of the base CRS in "ignore metadata" mode.
+     *
+     * @throws FactoryException if the CRS creation failed.
+     *
+     * @since 0.7
+     */
+    @Test
+    public void testEquals() throws FactoryException {
+        final ProjectedCRS standard   = create(CommonCRS.WGS84.geographic());
+        final ProjectedCRS normalized = create(CommonCRS.WGS84.normalizedGeographic());
+        assertFalse("STRICT",          ((LenientComparable) standard).equals(normalized, ComparisonMode.STRICT));
+        assertFalse("BY_CONTRACT",     ((LenientComparable) standard).equals(normalized, ComparisonMode.BY_CONTRACT));
+        assertTrue ("IGNORE_METADATA", ((LenientComparable) standard).equals(normalized, ComparisonMode.IGNORE_METADATA));
+        assertTrue ("APPROXIMATIVE",   ((LenientComparable) standard).equals(normalized, ComparisonMode.APPROXIMATIVE));
+        assertTrue ("ALLOW_VARIANT",   ((LenientComparable) standard).equals(normalized, ComparisonMode.ALLOW_VARIANT));
+    }
 }

Modified: sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/factory/IdentifiedObjectFinderTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/factory/IdentifiedObjectFinderTest.java?rev=1723633&r1=1723632&r2=1723633&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/factory/IdentifiedObjectFinderTest.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/factory/IdentifiedObjectFinderTest.java [UTF-8] Thu Jan  7 22:05:23 2016
@@ -67,13 +67,14 @@ public final strictfp class IdentifiedOb
     public void testFindSingleton() throws FactoryException {
         final GeographicCRS CRS84 = factory.createGeographicCRS("CRS:84");
         final IdentifiedObjectFinder finder = factory.newIdentifiedObjectFinder();
-        assertTrue("Newly created finder should default to full scan.", finder.isFullScanAllowed());
+        assertEquals("Newly created finder should default to full scan.",
+                IdentifiedObjectFinder.Domain.VALID_DATASET, finder.getSearchDomain());
 
-        finder.setFullScanAllowed(false);
+        finder.setSearchDomain(IdentifiedObjectFinder.Domain.DECLARATION);
         assertSame("Should find without the need for scan, since we can use the CRS:84 identifier.",
                    CRS84, finder.findSingleton(CRS84));
 
-        finder.setFullScanAllowed(true);
+        finder.setSearchDomain(IdentifiedObjectFinder.Domain.VALID_DATASET);
         assertSame("Allowing scanning should not make any difference for this CRS84 instance.",
                    CRS84, finder.findSingleton(CRS84));
         /*
@@ -85,11 +86,11 @@ public final strictfp class IdentifiedOb
                 CRS84.getDatum(), CRS84.getCoordinateSystem());
         assertEqualsIgnoreMetadata(CRS84, search);              // Required condition for next test.
 
-        finder.setFullScanAllowed(false);
+        finder.setSearchDomain(IdentifiedObjectFinder.Domain.DECLARATION);
         assertNull("Should not find WGS84 without a full scan, since it does not contains the CRS:84 identifier.",
                    finder.findSingleton(search));
 
-        finder.setFullScanAllowed(true);
+        finder.setSearchDomain(IdentifiedObjectFinder.Domain.VALID_DATASET);
         assertSame("A full scan should allow us to find WGS84, since it is equals ignoring metadata to CRS:84.",
                    CRS84, finder.findSingleton(search));
     }

Modified: 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=1723633&r1=1723632&r2=1723633&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/factory/sql/EPSGFactoryTest.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/factory/sql/EPSGFactoryTest.java [UTF-8] Thu Jan  7 22:05:23 2016
@@ -31,6 +31,7 @@ import org.opengis.referencing.operation
 import org.opengis.referencing.operation.Transformation;
 import org.opengis.referencing.operation.CoordinateOperation;
 import org.opengis.referencing.operation.CylindricalProjection;
+import org.opengis.referencing.operation.SingleOperation;
 import org.opengis.referencing.operation.OperationMethod;
 import org.opengis.referencing.operation.MathTransform;
 import org.opengis.util.FactoryException;
@@ -592,8 +593,8 @@ public final strictfp class EPSGFactoryT
          * Compare the conversion obtained directly with the conversion obtained
          * indirectly through a projected CRS. Both should use the same method.
          */
-        final OperationMethod copMethod = ((Conversion) operation) .getMethod();
-        final OperationMethod crsMethod = ((Conversion) projection).getMethod();
+        final OperationMethod copMethod = ((SingleOperation) operation) .getMethod();
+        final OperationMethod crsMethod = ((SingleOperation) projection).getMethod();
         assertEpsgNameAndIdentifierEqual("Transverse Mercator", 9807, copMethod);
         assertEpsgNameAndIdentifierEqual("Transverse Mercator", 9807, crsMethod);
         try {
@@ -744,27 +745,28 @@ public final strictfp class EPSGFactoryT
          * First, search for a CRS with axis order that does not match the ones in the EPSG database.
          * IdentifiedObjectFinder should not accept EPSG::4326 as a match for the given CRS.
          */
-        assertTrue("Full scan should be enabled by default.", finder.isFullScanAllowed());
+        assertEquals("Full scan should be enabled by default.",
+                IdentifiedObjectFinder.Domain.VALID_DATASET, finder.getSearchDomain());
         assertTrue("Should not find WGS84 because the axis order is not the same.",
-                   finder.find(crs.forConvention(AxesConvention.NORMALIZED)).isEmpty());
+                finder.find(crs.forConvention(AxesConvention.NORMALIZED)).isEmpty());
         /*
          * Ensure that the cache is empty.
          */
-        finder.setFullScanAllowed(false);
+        finder.setSearchDomain(IdentifiedObjectFinder.Domain.DECLARATION);
         assertTrue("Should not find without a full scan, because the WKT contains no identifier " +
                    "and the CRS name is ambiguous (more than one EPSG object have this name).",
                    finder.find(crs).isEmpty());
         /*
          * Scan the database for searching the CRS.
          */
-        finder.setFullScanAllowed(true);
+        finder.setSearchDomain(IdentifiedObjectFinder.Domain.VALID_DATASET);
         final IdentifiedObject found = finder.findSingleton(crs);
         assertNotNull("With full scan allowed, the CRS should be found.", found);
         assertEpsgNameAndIdentifierEqual("WGS 84", 4326, found);
         /*
          * Should find the CRS without the need of a full scan, because of the cache.
          */
-        finder.setFullScanAllowed(false);
+        finder.setSearchDomain(IdentifiedObjectFinder.Domain.DECLARATION);
         assertSame("The CRS should still in the cache.", found, finder.findSingleton(crs));
     }
 
@@ -790,8 +792,8 @@ public final strictfp class EPSGFactoryT
                 "       SPHEROID[“Krassowsky 1940”, 6378245.00000006, 298.299999999998]],\n" +  // Intentional rounding error.
                 "     PRIMEM[“Greenwich”, 0.0],\n" +
                 "     UNIT[“degree”, 0.017453292519943295],\n" +
-                "     AXIS[“Geodetic latitude”, NORTH],\n" +
-                "     AXIS[“Geodetic longitude”, EAST]],\n" +
+                "     AXIS[“Geodetic longitude”, EAST],\n" +
+                "     AXIS[“Geodetic latitude”, NORTH]],\n" +                   // Wrong axis order, but should not block.
                 "   PROJECTION[“Transverse Mercator”],\n" +
                 "   PARAMETER[“central_meridian”, 135.0000000000013],\n" +      // Intentional rounding error.
                 "   PARAMETER[“latitude_of_origin”, 0.0],\n" +
@@ -802,10 +804,10 @@ public final strictfp class EPSGFactoryT
                 "   AXIS[“Northing”, NORTH],\n" +
                 "   AXIS[“Easting”, EAST]]");
 
-        finder.setFullScanAllowed(false);
+        finder.setSearchDomain(IdentifiedObjectFinder.Domain.DECLARATION);
         assertTrue("Should not find the CRS without a full scan.", finder.find(crs).isEmpty());
 
-        finder.setFullScanAllowed(true);
+        finder.setSearchDomain(IdentifiedObjectFinder.Domain.VALID_DATASET);
         final Set<IdentifiedObject> find = finder.find(crs);
         assertFalse("With full scan allowed, the CRS should be found.", find.isEmpty());
         /*



Mime
View raw message