sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1718453 - in /sis/branches/JDK8/core: sis-referencing/src/main/java/org/apache/sis/referencing/factory/ sis-utility/src/main/java/org/apache/sis/util/
Date Mon, 07 Dec 2015 20:31:22 GMT
Author: desruisseaux
Date: Mon Dec  7 20:31:21 2015
New Revision: 1718453

URL: http://svn.apache.org/viewvc?rev=1718453&view=rev
Log:
Initial port of CachingAuthorityFactory - still incomplete. We commit this incomplete class before to retrofit
ThreadedAuthorityFactory into CachingAuthorityFactory in case we want to re-separate those classes in the future.

Added:
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/CacheRecord.java   (with props)
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/CachingAuthorityFactory.java   (with props)
Modified:
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/IdentifiedObjectFinder.java
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/ComparisonMode.java

Added: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/CacheRecord.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/CacheRecord.java?rev=1718453&view=auto
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/CacheRecord.java (added)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/CacheRecord.java [UTF-8] Mon Dec  7 20:31:21 2015
@@ -0,0 +1,141 @@
+/*
+ * 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;
+
+import java.util.Map;
+import java.util.List;
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Collection;
+import java.io.Console;
+import java.io.PrintWriter;
+import org.opengis.referencing.IdentifiedObject;
+import org.apache.sis.util.CharSequences;
+import org.apache.sis.util.Classes;
+import org.apache.sis.util.Debug;
+
+
+/**
+ * Implementation of {@link CachingAuthorityFactory#printCacheContent(PrintWriter)}.
+ * Instance of this class represent a single record in the cache content to be listed.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @since   0.7
+ * @version 0.7
+ * @module
+ *
+ * @see CachingAuthorityFactory#printCacheContent(PrintWriter)
+ */
+@Debug
+final class CacheRecord implements Comparable<CacheRecord> {
+    /**
+     * The key-value pair, and the identity string representation of the value.
+     */
+    private final String key, value, identity;
+
+    /**
+     * The key numeric value, using for sorting purpose only.
+     */
+    private final int code;
+
+    /**
+     * Creates a new record for the given key-value pair.
+     */
+    private CacheRecord(final Object key, Object value) {
+        identity = Classes.getShortClassName(value) + '@' + Integer.toHexString(System.identityHashCode(value));
+        String text;
+        if (value instanceof Collection<?>) {
+            final Iterator<?> it = ((Collection<?>) value).iterator();
+            value = it.hasNext() ? it.next() : null;
+        }
+        if (value instanceof IdentifiedObject) {
+            text = String.valueOf(((IdentifiedObject) value).getName());
+        } else {
+            text = null;
+        }
+        this.value = text;
+        this.key = text = String.valueOf(key);
+        text = text.substring(text.indexOf('[') + 1);
+        final int i = text.indexOf(' ');
+        if (i >= 1) {
+            text = text.substring(0, i);
+        }
+        int code;
+        try {
+            code = Integer.parseInt(text);
+        } catch (NumberFormatException e) {
+            code = Integer.MAX_VALUE;
+        }
+        this.code = code;
+    }
+
+    /**
+     * Compares with the given record for ordering.
+     */
+    @Override
+    public int compareTo(final CacheRecord other) {
+        if (code < other.code) return -1;
+        if (code > other.code) return +1;
+        return key.compareTo(other.key);
+    }
+
+    /**
+     * Implementation of the public {@link CachingAuthorityFactory#printCacheContent()} method.
+     *
+     * @param cache The cache.
+     * @param out The output writer, or {@code null} for the standard output stream.
+     */
+    @SuppressWarnings("UseOfSystemOutOrSystemErr")
+    static void printCacheContent(final Map<?,?> cache, PrintWriter out) {
+        final List<CacheRecord> list = new ArrayList<>(cache.size() + 10);
+        int codeLength = 0;
+        int identityLength = 0;
+        for (final Map.Entry<?,?> entry : cache.entrySet()) {
+            final CacheRecord record = new CacheRecord(entry.getKey(), entry.getValue());
+            int length = record.key.length();
+            if (length > codeLength) {
+                codeLength = length;
+            }
+            length = record.identity.length();
+            if (length > identityLength) {
+                identityLength = length;
+            }
+            list.add(record);
+        }
+        codeLength += 2;
+        identityLength += 2;
+        final CacheRecord[] records = list.toArray(new CacheRecord[list.size()]);
+        Arrays.sort(records);
+        if (out == null) {
+            final Console c = System.console();
+            out = (c != null) ? c.writer() : new PrintWriter(System.out);
+        }
+        for (final CacheRecord record : records) {
+            out.print(record.key);
+            out.print(CharSequences.spaces(codeLength - record.key.length()));
+            out.print(record.identity);
+            if (record.value != null) {
+                out.print(CharSequences.spaces(identityLength - record.identity.length()));
+                out.println(record.value);
+            } else {
+                out.println();
+            }
+        }
+        out.flush();
+    }
+}

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

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

Added: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/CachingAuthorityFactory.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/CachingAuthorityFactory.java?rev=1718453&view=auto
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/CachingAuthorityFactory.java (added)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/CachingAuthorityFactory.java [UTF-8] Mon Dec  7 20:31:21 2015
@@ -0,0 +1,1289 @@
+/*
+ * 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;
+
+import java.util.Set;
+import java.util.Map;
+import java.util.WeakHashMap;
+import java.lang.ref.WeakReference;
+import java.io.PrintWriter;
+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.referencing.IdentifiedObject;
+import org.opengis.referencing.NoSuchAuthorityCodeException;
+import org.opengis.util.NameFactory;
+import org.opengis.util.FactoryException;
+import org.opengis.util.InternationalString;
+import org.opengis.metadata.extent.Extent;
+import org.opengis.metadata.citation.Citation;
+import org.opengis.parameter.ParameterDescriptor;
+import org.apache.sis.util.Debug;
+import org.apache.sis.util.Disposable;
+import org.apache.sis.util.ArgumentChecks;
+import org.apache.sis.util.logging.Logging;
+import org.apache.sis.util.collection.Cache;
+import org.apache.sis.internal.referencing.NilReferencingObject;
+import org.apache.sis.internal.simple.SimpleCitation;
+import org.apache.sis.internal.system.DefaultFactories;
+import org.apache.sis.internal.system.Loggers;
+
+
+/**
+ * An authority factory that caches all objects created by another factory.
+ * All {@code createFoo(String)} methods first check if a previously created object exists for the given code.
+ * If such object exists, it is returned. Otherwise, the object creation is delegated to another factory given
+ * by {@link #createBackingStore()} and the result is cached in this factory.
+ *
+ * <p>Objects are cached by strong references, up to the amount of objects specified at construction time.
+ * If a greater amount of objects are cached, the oldest ones will be retained through a
+ * {@linkplain WeakReference weak reference} instead of a strong one.
+ * This means that this caching factory will continue to return them as long as they are in use somewhere
+ * else in the Java virtual machine, but will be discarded (and recreated on the fly if needed) otherwise.</p>
+ *
+ * <div class="section">Not for subclasses</div>
+ * This abstract class does not implement any of the {@link DatumAuthorityFactory}, {@link CSAuthorityFactory},
+ * {@link CRSAuthorityFactory} and {@link CoordinateOperationAuthorityFactory} interfaces.
+ * Subclasses should select the interfaces that they choose to implement.
+ *
+ * @author  Martin Desruisseaux (IRD, Geomatys)
+ * @since   0.7
+ * @version 0.7
+ * @module
+ */
+public abstract class CachingAuthorityFactory extends GeodeticAuthorityFactory implements Disposable {
+    /**
+     * The default value for {@link #maxStrongReferences}.
+     */
+    static final int DEFAULT_MAX = 100;
+
+    /**
+     * The citation for unknown authority name.
+     */
+    private static final Citation UNKNOWN = new SimpleCitation("Unknown");
+
+    /**
+     * The authority, cached after first requested.
+     */
+    private transient volatile Citation authority;
+
+    /**
+     * The pool of cached objects. The keys are instances of {@link Key} or {@link CodePair}.
+     */
+    private final Cache<Object,Object> cache;
+
+    /**
+     * The pool of objects identified by {@link Finder#find(IdentifiedObject)} for each comparison modes.
+     * Values may be {@link NilReferencingObject} if an object has been searched but has not been found.
+     *
+     * <p>Every access to this pool must be synchronized on {@code findPool}.</p>
+     */
+    private final Map<IdentifiedObject, IdentifiedObject> findPool = new WeakHashMap<>();
+
+    /**
+     * Constructs an instance with a default number of entries to keep by strong reference.
+     */
+    protected CachingAuthorityFactory() {
+        this(DEFAULT_MAX);
+    }
+
+    /**
+     * Constructs an instance with the specified number of entries to keep by strong references.
+     * In a number of object greater than {@code maxStrongReferences} are created, then the strong references
+     * for the eldest ones will be replaced by weak references.
+     *
+     * @param maxStrongReferences The maximum number of objects to keep by strong reference.
+     */
+    protected CachingAuthorityFactory(final int maxStrongReferences) {
+        super(DefaultFactories.forBuildin(NameFactory.class));    // TODO
+        ArgumentChecks.ensurePositive("maxStrongReferences", maxStrongReferences);
+        cache = new Cache<>(20, maxStrongReferences, false);
+        cache.setKeyCollisionAllowed(true);
+        /*
+         * Key collision is usually an error. But in this case we allow them in order to enable recursivity.
+         * If during the creation of an object the program asks to this CachingAuthorityFactory for the same
+         * object (using the same key), then the default Cache implementation considers that situation as an
+         * error unless the above property has been set to 'true'.
+         */
+    }
+
+    /**
+     * Returns the backing store authority factory. The returned backing store must be thread-safe.
+     * This method shall be used together with {@link #release()} in a {@code try ... finally} block.
+     *
+     * @return The backing store to use in {@code createXXX(…)} methods.
+     * @throws FactoryException if the creation of backing store failed.
+     */
+    private GeodeticAuthorityFactory getBackingStore() throws FactoryException {
+        throw new UnsupportedOperationException("Not yet implemented");
+    }
+
+    /**
+     * Releases the backing store previously obtained with {@link #getBackingStore}.
+     */
+    private void release() {
+    }
+
+    /**
+     * Returns the organization or party responsible for definition and maintenance of the underlying database.
+     * The default implementation performs the following steps:
+     * <ul>
+     *   <li>Returns the cached value if it exists.</li>
+     *   <li>Otherwise:
+     *     <ol>
+     *       <li>get an instance of the backing store,</li>
+     *       <li>delegate to its {@link GeodeticAuthorityFactory#getAuthority()} method,</li>
+     *       <li>release the backing store,</li>
+     *       <li>cache the result.</li>
+     *     </ol>
+     *   </li>
+     * </ul>
+     *
+     * @return The organization responsible for definition of the database.
+     */
+    @Override
+    public Citation getAuthority() {
+        Citation c = authority;
+        if (c == null) try {
+            final GeodeticAuthorityFactory factory = getBackingStore();
+            try {
+                // Cache only in case of success. If we failed, we
+                // will try again next time this method is invoked.
+                authority = c = factory.getAuthority();
+            } finally {
+                release();
+            }
+        } catch (FactoryException e) {
+            c = UNKNOWN;
+            Logging.unexpectedException(Logging.getLogger(Loggers.CRS_FACTORY),
+                    CachingAuthorityFactory.class, "getAuthority", e);
+        }
+        return c;
+    }
+
+    /**
+     * Returns a description of the underlying backing store, or {@code null} if unknown.
+     * The default implementation performs the following steps:
+     * <ol>
+     *   <li>Get an instance of the backing store,</li>
+     *   <li>delegate to its {@link GeodeticAuthorityFactory#getBackingStoreDescription()} method,</li>
+     *   <li>release the backing store.</li>
+     * </ol>
+     *
+     * @return A description of the underlying backing store, or {@code null} if none.
+     * @throws FactoryException if a failure occurred while fetching the backing store description.
+     */
+    @Override
+    public InternationalString getBackingStoreDescription() throws FactoryException {
+        final GeodeticAuthorityFactory factory = getBackingStore();
+        try {
+            return factory.getBackingStoreDescription();
+        } finally {
+            release();
+        }
+    }
+
+    /**
+     * Returns the set of authority codes for objects of the given type.
+     * The default implementation performs the following steps:
+     * <ol>
+     *   <li>Get an instance of the backing store,</li>
+     *   <li>delegate to its {@link GeodeticAuthorityFactory#getAuthorityCodes(Class)} method,</li>
+     *   <li>release the backing store.</li>
+     * </ol>
+     *
+     * @param  type The spatial reference objects type (e.g. {@code ProjectedCRS.class}).
+     * @return The set of authority codes for spatial reference objects of the given type.
+     *         If this factory does not contains any object of the given type, then this method returns an empty set.
+     * @throws FactoryException if access to the underlying database failed.
+     */
+    @Override
+    public Set<String> getAuthorityCodes(final Class<? extends IdentifiedObject> type) throws FactoryException {
+        final GeodeticAuthorityFactory factory = getBackingStore();
+        try {
+            return factory.getAuthorityCodes(type);
+            /*
+             * In the particular case of EPSG factory, the returned Set maintains a live connection to the database.
+             * But it still okay to release the factory anyway because our implementation will really close
+             * the connection only when the iteration is over or the iterator has been garbage-collected.
+             */
+        } finally {
+            release();
+        }
+    }
+
+    /**
+     * Gets a description of the object corresponding to a code.
+     * The default implementation performs the following steps:
+     * <ol>
+     *   <li>Get an instance of the backing store,</li>
+     *   <li>delegate to its {@link GeodeticAuthorityFactory#getDescriptionText(String)} method,</li>
+     *   <li>release the backing store.</li>
+     * </ol>
+     *
+     * @param  code Value allocated by authority.
+     * @return A description of the object, or {@code null} if the object
+     *         corresponding to the specified {@code code} has no description.
+     * @throws NoSuchAuthorityCodeException if the specified {@code code} was not found.
+     * @throws FactoryException if the query failed for some other reason.
+     */
+    @Override
+    public InternationalString getDescriptionText(final String code)
+            throws NoSuchAuthorityCodeException, FactoryException
+    {
+        final GeodeticAuthorityFactory factory = getBackingStore();
+        try {
+            return factory.getDescriptionText(code);
+        } finally {
+            release();
+        }
+    }
+
+    /**
+     * Returns an arbitrary object from a code.
+     * The default implementation performs the following steps:
+     * <ul>
+     *   <li>Returns the cached instance for the given code if such instance already exists.</li>
+     *   <li>Otherwise:
+     *     <ol>
+     *       <li>get an instance of the backing store,</li>
+     *       <li>delegate to its {@link GeodeticAuthorityFactory#createObject(String)} method,</li>
+     *       <li>release the backing store,</li>
+     *       <li>cache the result.</li>
+     *     </ol>
+     *   </li>
+     * </ul>
+     *
+     * @return The object for the given code.
+     * @throws FactoryException if the object creation failed.
+     *
+     * @see #createCoordinateReferenceSystem(String)
+     * @see #createDatum(String)
+     * @see #createCoordinateSystem(String)
+     */
+    @Override
+    public IdentifiedObject createObject(final String code) throws FactoryException {
+        return create(AuthorityFactoryProxy.OBJECT, code);
+    }
+
+    /**
+     * Returns an arbitrary coordinate reference system from a code.
+     * The default implementation performs the following steps:
+     * <ul>
+     *   <li>Returns the cached instance for the given code if such instance already exists.</li>
+     *   <li>Otherwise delegate to the {@link GeodeticAuthorityFactory#createCoordinateReferenceSystem(String)}
+     *       method of a backing store and cache the result for future use.</li>
+     * </ul>
+     *
+     * @return The coordinate reference system for the given code.
+     * @throws FactoryException if the object creation failed.
+     *
+     * @see #createGeographicCRS(String)
+     * @see #createProjectedCRS(String)
+     * @see #createVerticalCRS(String)
+     * @see #createTemporalCRS(String)
+     */
+    @Override
+    public CoordinateReferenceSystem createCoordinateReferenceSystem(final String code) throws FactoryException {
+        return create(AuthorityFactoryProxy.CRS, code);
+    }
+
+    /**
+     * Returns a geographic coordinate reference system from a code.
+     * The default implementation performs the following steps:
+     * <ul>
+     *   <li>Returns the cached instance for the given code if such instance already exists.</li>
+     *   <li>Otherwise delegate to the {@link GeodeticAuthorityFactory#createGeographicCRS(String)}
+     *       method of a backing store and cache the result for future use.</li>
+     * </ul>
+     *
+     * @return The coordinate reference system for the given code.
+     * @throws FactoryException if the object creation failed.
+     *
+     * @see #createGeodeticDatum(String)
+     * @see #createEllipsoidalCS(String)
+     * @see org.apache.sis.referencing.crs.DefaultGeographicCRS
+     */
+    @Override
+    public GeographicCRS createGeographicCRS(final String code) throws FactoryException {
+        return create(AuthorityFactoryProxy.GEOGRAPHIC_CRS, code);
+    }
+
+    /**
+     * Returns a geocentric coordinate reference system from a code.
+     * The default implementation performs the following steps:
+     * <ul>
+     *   <li>Returns the cached instance for the given code if such instance already exists.</li>
+     *   <li>Otherwise delegate to the {@link GeodeticAuthorityFactory#createGeocentricCRS(String)}
+     *       method of a backing store and cache the result for future use.</li>
+     * </ul>
+     *
+     * @return The coordinate reference system for the given code.
+     * @throws FactoryException if the object creation failed.
+     *
+     * @see #createGeodeticDatum(String)
+     * @see #createCartesianCS(String)
+     * @see #createSphericalCS(String)
+     * @see org.apache.sis.referencing.crs.DefaultGeocentricCRS
+     */
+    @Override
+    public GeocentricCRS createGeocentricCRS(final String code) throws FactoryException {
+        return create(AuthorityFactoryProxy.GEOCENTRIC_CRS, code);
+    }
+
+    /**
+     * Returns a projected coordinate reference system from a code.
+     * The default implementation performs the following steps:
+     * <ul>
+     *   <li>Returns the cached instance for the given code if such instance already exists.</li>
+     *   <li>Otherwise delegate to the {@link GeodeticAuthorityFactory#createProjectedCRS(String)}
+     *       method of a backing store and cache the result for future use.</li>
+     * </ul>
+     *
+     * @return The coordinate reference system for the given code.
+     * @throws FactoryException if the object creation failed.
+     *
+     * @see #createGeographicCRS(String)
+     * @see #createCartesianCS(String)
+     * @see org.apache.sis.referencing.crs.DefaultProjectedCRS
+     */
+    @Override
+    public ProjectedCRS createProjectedCRS(final String code) throws FactoryException {
+        return create(AuthorityFactoryProxy.PROJECTED_CRS, code);
+    }
+
+    /**
+     * Returns a vertical coordinate reference system from a code.
+     * The default implementation performs the following steps:
+     * <ul>
+     *   <li>Returns the cached instance for the given code if such instance already exists.</li>
+     *   <li>Otherwise delegate to the {@link GeodeticAuthorityFactory#createVerticalCRS(String)}
+     *       method of a backing store and cache the result for future use.</li>
+     * </ul>
+     *
+     * @return The coordinate reference system for the given code.
+     * @throws FactoryException if the object creation failed.
+     *
+     * @see #createVerticalDatum(String)
+     * @see #createVerticalCS(String)
+     * @see org.apache.sis.referencing.crs.DefaultVerticalCRS
+     */
+    @Override
+    public VerticalCRS createVerticalCRS(final String code) throws FactoryException {
+        return create(AuthorityFactoryProxy.VERTICAL_CRS, code);
+    }
+
+    /**
+     * Returns a temporal coordinate reference system from a code.
+     * The default implementation performs the following steps:
+     * <ul>
+     *   <li>Returns the cached instance for the given code if such instance already exists.</li>
+     *   <li>Otherwise delegate to the {@link GeodeticAuthorityFactory#createTemporalCRS(String)}
+     *       method of a backing store and cache the result for future use.</li>
+     * </ul>
+     *
+     * @return The coordinate reference system for the given code.
+     * @throws FactoryException if the object creation failed.
+     *
+     * @see #createTemporalDatum(String)
+     * @see #createTimeCS(String)
+     * @see org.apache.sis.referencing.crs.DefaultTemporalCRS
+     */
+    @Override
+    public TemporalCRS createTemporalCRS(final String code) throws FactoryException {
+        return create(AuthorityFactoryProxy.TEMPORAL_CRS, code);
+    }
+
+    /**
+     * Returns a 3D or 4D coordinate reference system from a code.
+     * The default implementation performs the following steps:
+     * <ul>
+     *   <li>Returns the cached instance for the given code if such instance already exists.</li>
+     *   <li>Otherwise delegate to the {@link GeodeticAuthorityFactory#createCompoundCRS(String)}
+     *       method of a backing store and cache the result for future use.</li>
+     * </ul>
+     *
+     * @return The coordinate reference system for the given code.
+     * @throws FactoryException if the object creation failed.
+     *
+     * @see #createVerticalCRS(String)
+     * @see #createTemporalCRS(String)
+     * @see org.apache.sis.referencing.crs.DefaultCompoundCRS
+     */
+    @Override
+    public CompoundCRS createCompoundCRS(final String code) throws FactoryException {
+        return create(AuthorityFactoryProxy.COMPOUND_CRS, code);
+    }
+
+    /**
+     * Returns a derived coordinate reference system from a code.
+     * The default implementation performs the following steps:
+     * <ul>
+     *   <li>Returns the cached instance for the given code if such instance already exists.</li>
+     *   <li>Otherwise delegate to the {@link GeodeticAuthorityFactory#createDerivedCRS(String)}
+     *       method of a backing store and cache the result for future use.</li>
+     * </ul>
+     *
+     * @return The coordinate reference system for the given code.
+     * @throws FactoryException if the object creation failed.
+     *
+     * @see org.apache.sis.referencing.crs.DefaultDerivedCRS
+     */
+    @Override
+    public DerivedCRS createDerivedCRS(final String code) throws FactoryException {
+        return create(AuthorityFactoryProxy.DERIVED_CRS, code);
+    }
+
+    /**
+     * Returns an engineering coordinate reference system from a code.
+     * The default implementation performs the following steps:
+     * <ul>
+     *   <li>Returns the cached instance for the given code if such instance already exists.</li>
+     *   <li>Otherwise delegate to the {@link GeodeticAuthorityFactory#createEngineeringCRS(String)}
+     *       method of a backing store and cache the result for future use.</li>
+     * </ul>
+     *
+     * @return The coordinate reference system for the given code.
+     * @throws FactoryException if the object creation failed.
+     *
+     * @see #createEngineeringDatum(String)
+     * @see org.apache.sis.referencing.crs.DefaultEngineeringCRS
+     */
+    @Override
+    public EngineeringCRS createEngineeringCRS(final String code) throws FactoryException {
+        return create(AuthorityFactoryProxy.ENGINEERING_CRS, code);
+    }
+
+    /**
+     * Returns an image coordinate reference system from a code.
+     * The default implementation performs the following steps:
+     * <ul>
+     *   <li>Returns the cached instance for the given code if such instance already exists.</li>
+     *   <li>Otherwise delegate to the {@link GeodeticAuthorityFactory#createImageCRS(String)}
+     *       method of a backing store and cache the result for future use.</li>
+     * </ul>
+     *
+     * @return The coordinate reference system for the given code.
+     * @throws FactoryException if the object creation failed.
+     *
+     * @see #createImageDatum(String)
+     * @see org.apache.sis.referencing.crs.DefaultImageCRS
+     */
+    @Override
+    public ImageCRS createImageCRS(final String code) throws FactoryException {
+        return create(AuthorityFactoryProxy.IMAGE_CRS, code);
+    }
+
+    /**
+     * Returns an arbitrary datum from a code.
+     * The default implementation performs the following steps:
+     * <ul>
+     *   <li>Returns the cached instance for the given code if such instance already exists.</li>
+     *   <li>Otherwise delegate to the {@link GeodeticAuthorityFactory#createDatum(String)}
+     *       method of a backing store and cache the result for future use.</li>
+     * </ul>
+     *
+     * @return The datum for the given code.
+     * @throws FactoryException if the object creation failed.
+     *
+     * @see #createGeodeticDatum(String)
+     * @see #createVerticalDatum(String)
+     * @see #createTemporalDatum(String)
+     */
+    @Override
+    public Datum createDatum(final String code) throws FactoryException {
+        return create(AuthorityFactoryProxy.DATUM, code);
+    }
+
+    /**
+     * Returns a geodetic datum from a code.
+     * The default implementation performs the following steps:
+     * <ul>
+     *   <li>Returns the cached instance for the given code if such instance already exists.</li>
+     *   <li>Otherwise delegate to the {@link GeodeticAuthorityFactory#createGeodeticDatum(String)}
+     *       method of a backing store and cache the result for future use.</li>
+     * </ul>
+     *
+     * @return The datum for the given code.
+     * @throws FactoryException if the object creation failed.
+     *
+     * @see #createEllipsoid(String)
+     * @see #createPrimeMeridian(String)
+     * @see #createGeographicCRS(String)
+     * @see #createGeocentricCRS(String)
+     * @see org.apache.sis.referencing.datum.DefaultGeodeticDatum
+     */
+    @Override
+    public GeodeticDatum createGeodeticDatum(final String code) throws FactoryException {
+        return create(AuthorityFactoryProxy.GEODETIC_DATUM, code);
+    }
+
+    /**
+     * Returns a vertical datum from a code.
+     * The default implementation performs the following steps:
+     * <ul>
+     *   <li>Returns the cached instance for the given code if such instance already exists.</li>
+     *   <li>Otherwise delegate to the {@link GeodeticAuthorityFactory#createVerticalDatum(String)}
+     *       method of a backing store and cache the result for future use.</li>
+     * </ul>
+     *
+     * @return The datum for the given code.
+     * @throws FactoryException if the object creation failed.
+     *
+     * @see #createVerticalCRS(String)
+     * @see org.apache.sis.referencing.datum.DefaultVerticalDatum
+     */
+    @Override
+    public VerticalDatum createVerticalDatum(final String code) throws FactoryException {
+        return create(AuthorityFactoryProxy.VERTICAL_DATUM, code);
+    }
+
+    /**
+     * Returns a temporal datum from a code.
+     * The default implementation performs the following steps:
+     * <ul>
+     *   <li>Returns the cached instance for the given code if such instance already exists.</li>
+     *   <li>Otherwise delegate to the {@link GeodeticAuthorityFactory#createTemporalDatum(String)}
+     *       method of a backing store and cache the result for future use.</li>
+     * </ul>
+     *
+     * @return The datum for the given code.
+     * @throws FactoryException if the object creation failed.
+     *
+     * @see #createTemporalCRS(String)
+     * @see org.apache.sis.referencing.datum.DefaultTemporalDatum
+     */
+    @Override
+    public TemporalDatum createTemporalDatum(final String code) throws FactoryException {
+        return create(AuthorityFactoryProxy.TEMPORAL_DATUM, code);
+    }
+
+    /**
+     * Returns an engineering datum from a code.
+     * The default implementation performs the following steps:
+     * <ul>
+     *   <li>Returns the cached instance for the given code if such instance already exists.</li>
+     *   <li>Otherwise delegate to the {@link GeodeticAuthorityFactory#createEngineeringDatum(String)}
+     *       method of a backing store and cache the result for future use.</li>
+     * </ul>
+     *
+     * @return The datum for the given code.
+     * @throws FactoryException if the object creation failed.
+     *
+     * @see #createEngineeringCRS(String)
+     * @see org.apache.sis.referencing.datum.DefaultEngineeringDatum
+     */
+    @Override
+    public EngineeringDatum createEngineeringDatum(final String code) throws FactoryException {
+        return create(AuthorityFactoryProxy.ENGINEERING_DATUM, code);
+    }
+
+    /**
+     * Returns an image datum from a code.
+     * The default implementation performs the following steps:
+     * <ul>
+     *   <li>Returns the cached instance for the given code if such instance already exists.</li>
+     *   <li>Otherwise delegate to the {@link GeodeticAuthorityFactory#createImageDatum(String)}
+     *       method of a backing store and cache the result for future use.</li>
+     * </ul>
+     *
+     * @return The datum for the given code.
+     * @throws FactoryException if the object creation failed.
+     *
+     * @see #createImageCRS(String)
+     * @see org.apache.sis.referencing.datum.DefaultImageDatum
+     */
+    @Override
+    public ImageDatum createImageDatum(final String code) throws FactoryException {
+        return create(AuthorityFactoryProxy.IMAGE_DATUM, code);
+    }
+
+    /**
+     * Returns an ellipsoid from a code.
+     * The default implementation performs the following steps:
+     * <ul>
+     *   <li>Returns the cached instance for the given code if such instance already exists.</li>
+     *   <li>Otherwise delegate to the {@link GeodeticAuthorityFactory#createEllipsoid(String)}
+     *       method of a backing store and cache the result for future use.</li>
+     * </ul>
+     *
+     * @return The ellipsoid for the given code.
+     * @throws FactoryException if the object creation failed.
+     *
+     * @see #createGeodeticDatum(String)
+     * @see #createEllipsoidalCS(String)
+     * @see org.apache.sis.referencing.datum.DefaultEllipsoid
+     */
+    @Override
+    public Ellipsoid createEllipsoid(final String code) throws FactoryException {
+        return create(AuthorityFactoryProxy.ELLIPSOID, code);
+    }
+
+    /**
+     * Returns a prime meridian from a code.
+     * The default implementation performs the following steps:
+     * <ul>
+     *   <li>Returns the cached instance for the given code if such instance already exists.</li>
+     *   <li>Otherwise delegate to the {@link GeodeticAuthorityFactory#createPrimeMeridian(String)}
+     *       method of a backing store and cache the result for future use.</li>
+     * </ul>
+     *
+     * @return The prime meridian for the given code.
+     * @throws FactoryException if the object creation failed.
+     *
+     * @see #createGeodeticDatum(String)
+     * @see org.apache.sis.referencing.datum.DefaultPrimeMeridian
+     */
+    @Override
+    public PrimeMeridian createPrimeMeridian(final String code) throws FactoryException {
+        return create(AuthorityFactoryProxy.PRIME_MERIDIAN, code);
+    }
+
+    /**
+     * Returns an extent (usually a domain of validity) from a code.
+     * The default implementation performs the following steps:
+     * <ul>
+     *   <li>Returns the cached instance for the given code if such instance already exists.</li>
+     *   <li>Otherwise delegate to the {@link GeodeticAuthorityFactory#createExtent(String)}
+     *       method of a backing store and cache the result for future use.</li>
+     * </ul>
+     *
+     * @return The extent for the given code.
+     * @throws FactoryException if the object creation failed.
+     *
+     * @see #createCoordinateReferenceSystem(String)
+     * @see #createDatum(String)
+     * @see org.apache.sis.metadata.iso.extent.DefaultExtent
+     */
+    @Override
+    public Extent createExtent(final String code) throws FactoryException {
+        return create(AuthorityFactoryProxy.EXTENT, code);
+    }
+
+    /**
+     * Returns an arbitrary coordinate system from a code.
+     * The default implementation performs the following steps:
+     * <ul>
+     *   <li>Returns the cached instance for the given code if such instance already exists.</li>
+     *   <li>Otherwise delegate to the {@link GeodeticAuthorityFactory#createCoordinateSystem(String)}
+     *       method of a backing store and cache the result for future use.</li>
+     * </ul>
+     *
+     * @return The coordinate system for the given code.
+     * @throws FactoryException if the object creation failed.
+     *
+     * @see #createCoordinateSystemAxis(String)
+     * @see #createEllipsoidalCS(String)
+     * @see #createCartesianCS(String)
+     */
+    @Override
+    public CoordinateSystem createCoordinateSystem(final String code) throws FactoryException {
+        return create(AuthorityFactoryProxy.COORDINATE_SYSTEM, code);
+    }
+
+    /**
+     * Returns an ellipsoidal coordinate system from a code.
+     * The default implementation performs the following steps:
+     * <ul>
+     *   <li>Returns the cached instance for the given code if such instance already exists.</li>
+     *   <li>Otherwise delegate to the {@link GeodeticAuthorityFactory#createEllipsoidalCS(String)}
+     *       method of a backing store and cache the result for future use.</li>
+     * </ul>
+     *
+     * @return The coordinate system for the given code.
+     * @throws FactoryException if the object creation failed.
+     *
+     * @see #createEllipsoid(String)
+     * @see #createGeodeticDatum(String)
+     * @see #createGeographicCRS(String)
+     * @see org.apache.sis.referencing.cs.DefaultEllipsoidalCS
+     */
+    @Override
+    public EllipsoidalCS createEllipsoidalCS(final String code) throws FactoryException {
+        return create(AuthorityFactoryProxy.ELLIPSOIDAL_CS, code);
+    }
+
+    /**
+     * Returns a vertical coordinate system from a code.
+     * The default implementation performs the following steps:
+     * <ul>
+     *   <li>Returns the cached instance for the given code if such instance already exists.</li>
+     *   <li>Otherwise delegate to the {@link GeodeticAuthorityFactory#createVerticalCS(String)}
+     *       method of a backing store and cache the result for future use.</li>
+     * </ul>
+     *
+     * @return The coordinate system for the given code.
+     * @throws FactoryException if the object creation failed.
+     *
+     * @see #createVerticalDatum(String)
+     * @see #createVerticalCRS(String)
+     * @see org.apache.sis.referencing.cs.DefaultVerticalCS
+     */
+    @Override
+    public VerticalCS createVerticalCS(final String code) throws FactoryException {
+        return create(AuthorityFactoryProxy.VERTICAL_CS, code);
+    }
+
+    /**
+     * Returns a temporal coordinate system from a code.
+     * The default implementation performs the following steps:
+     * <ul>
+     *   <li>Returns the cached instance for the given code if such instance already exists.</li>
+     *   <li>Otherwise delegate to the {@link GeodeticAuthorityFactory#createTimeCS(String)}
+     *       method of a backing store and cache the result for future use.</li>
+     * </ul>
+     *
+     * @return The coordinate system for the given code.
+     * @throws FactoryException if the object creation failed.
+     *
+     * @see #createTemporalDatum(String)
+     * @see #createTemporalCRS(String)
+     * @see org.apache.sis.referencing.cs.DefaultTimeCS
+     */
+    @Override
+    public TimeCS createTimeCS(final String code) throws FactoryException {
+        return create(AuthorityFactoryProxy.TIME_CS, code);
+    }
+
+    /**
+     * Returns a Cartesian coordinate system from a code.
+     * The default implementation performs the following steps:
+     * <ul>
+     *   <li>Returns the cached instance for the given code if such instance already exists.</li>
+     *   <li>Otherwise delegate to the {@link GeodeticAuthorityFactory#createCartesianCS(String)}
+     *       method of a backing store and cache the result for future use.</li>
+     * </ul>
+     *
+     * @return The coordinate system for the given code.
+     * @throws FactoryException if the object creation failed.
+     *
+     * @see #createProjectedCRS(String)
+     * @see #createGeocentricCRS(String)
+     * @see org.apache.sis.referencing.cs.DefaultCartesianCS
+     */
+    @Override
+    public CartesianCS createCartesianCS(final String code) throws FactoryException {
+        return create(AuthorityFactoryProxy.CARTESIAN_CS, code);
+    }
+
+    /**
+     * Returns a spherical coordinate system from a code.
+     * The default implementation performs the following steps:
+     * <ul>
+     *   <li>Returns the cached instance for the given code if such instance already exists.</li>
+     *   <li>Otherwise delegate to the {@link GeodeticAuthorityFactory#createSphericalCS(String)}
+     *       method of a backing store and cache the result for future use.</li>
+     * </ul>
+     *
+     * @return The coordinate system for the given code.
+     * @throws FactoryException if the object creation failed.
+     *
+     * @see #createGeocentricCRS(String)
+     * @see org.apache.sis.referencing.cs.DefaultSphericalCS
+     */
+    @Override
+    public SphericalCS createSphericalCS(final String code) throws FactoryException {
+        return create(AuthorityFactoryProxy.SPHERICAL_CS, code);
+    }
+
+    /**
+     * Returns a cylindrical coordinate system from a code.
+     * The default implementation performs the following steps:
+     * <ul>
+     *   <li>Returns the cached instance for the given code if such instance already exists.</li>
+     *   <li>Otherwise delegate to the {@link GeodeticAuthorityFactory#createCylindricalCS(String)}
+     *       method of a backing store and cache the result for future use.</li>
+     * </ul>
+     *
+     * @return The coordinate system for the given code.
+     * @throws FactoryException if the object creation failed.
+     *
+     * @see org.apache.sis.referencing.cs.DefaultCylindricalCS
+     */
+    @Override
+    public CylindricalCS createCylindricalCS(final String code) throws FactoryException {
+        return create(AuthorityFactoryProxy.CYLINDRICAL_CS, code);
+    }
+
+    /**
+     * Returns a polar coordinate system from a code.
+     * The default implementation performs the following steps:
+     * <ul>
+     *   <li>Returns the cached instance for the given code if such instance already exists.</li>
+     *   <li>Otherwise delegate to the {@link GeodeticAuthorityFactory#createPolarCS(String)}
+     *       method of a backing store and cache the result for future use.</li>
+     * </ul>
+     *
+     * @return The coordinate system for the given code.
+     * @throws FactoryException if the object creation failed.
+     *
+     * @see org.apache.sis.referencing.cs.DefaultPolarCS
+     */
+    @Override
+    public PolarCS createPolarCS(final String code) throws FactoryException {
+        return create(AuthorityFactoryProxy.POLAR_CS, code);
+    }
+
+    /**
+     * Returns a coordinate system axis from a code.
+     * The default implementation performs the following steps:
+     * <ul>
+     *   <li>Returns the cached instance for the given code if such instance already exists.</li>
+     *   <li>Otherwise delegate to the {@link GeodeticAuthorityFactory#createCoordinateSystemAxis(String)}
+     *       method of a backing store and cache the result for future use.</li>
+     * </ul>
+     *
+     * @return The axis for the given code.
+     * @throws FactoryException if the object creation failed.
+     *
+     * @see #createCoordinateSystem(String)
+     * @see org.apache.sis.referencing.cs.DefaultCoordinateSystemAxis
+     */
+    @Override
+    public CoordinateSystemAxis createCoordinateSystemAxis(final String code) throws FactoryException {
+        return create(AuthorityFactoryProxy.AXIS, code);
+    }
+
+    /**
+     * Returns an unit of measurement from a code.
+     * The default implementation performs the following steps:
+     * <ul>
+     *   <li>Returns the cached instance for the given code if such instance already exists.</li>
+     *   <li>Otherwise delegate to the {@link GeodeticAuthorityFactory#createUnit(String)}
+     *       method of a backing store and cache the result for future use.</li>
+     * </ul>
+     *
+     * @return The unit of measurement for the given code.
+     * @throws FactoryException if the object creation failed.
+     */
+    @Override
+    public Unit<?> createUnit(final String code) throws FactoryException {
+        return create(AuthorityFactoryProxy.UNIT, code);
+    }
+
+    /**
+     * Returns a parameter descriptor from a code.
+     * The default implementation performs the following steps:
+     * <ul>
+     *   <li>Returns the cached instance for the given code if such instance already exists.</li>
+     *   <li>Otherwise delegate to the {@link GeodeticAuthorityFactory#createParameterDescriptor(String)}
+     *       method of a backing store and cache the result for future use.</li>
+     * </ul>
+     *
+     * @return The parameter descriptor for the given code.
+     * @throws FactoryException if the object creation failed.
+     *
+     * @see org.apache.sis.parameter.DefaultParameterDescriptor
+     */
+    @Override
+    public ParameterDescriptor<?> createParameterDescriptor(final String code) throws FactoryException {
+        return create(AuthorityFactoryProxy.PARAMETER, code);
+    }
+
+    /**
+     * Returns an operation method from a code.
+     * The default implementation performs the following steps:
+     * <ul>
+     *   <li>Returns the cached instance for the given code if such instance already exists.</li>
+     *   <li>Otherwise delegate to the {@link GeodeticAuthorityFactory#createOperationMethod(String)}
+     *       method of a backing store and cache the result for future use.</li>
+     * </ul>
+     *
+     * @return The operation method for the given code.
+     * @throws FactoryException if the object creation failed.
+     *
+     * @see org.apache.sis.referencing.operation.DefaultOperationMethod
+     */
+    @Override
+    public OperationMethod createOperationMethod(final String code) throws FactoryException {
+        return create(AuthorityFactoryProxy.METHOD, code);
+    }
+
+    /**
+     * Returns an operation from a code.
+     * The default implementation performs the following steps:
+     * <ul>
+     *   <li>Returns the cached instance for the given code if such instance already exists.</li>
+     *   <li>Otherwise delegate to the {@link GeodeticAuthorityFactory#createCoordinateOperation(String)}
+     *       method of a backing store and cache the result for future use.</li>
+     * </ul>
+     *
+     * @return The operation for the given code.
+     * @throws FactoryException if the object creation failed.
+     *
+     * @see org.apache.sis.referencing.operation.AbstractCoordinateOperation
+     */
+    @Override
+    public CoordinateOperation createCoordinateOperation(final String code) throws FactoryException {
+        return create(AuthorityFactoryProxy.OPERATION, code);
+    }
+
+    /**
+     * The key objects to use in the {@link CachingAuthorityFactory#cache}.
+     *
+     * @see <a href="http://jira.geotoolkit.org/browse/GEOTK-2">GEOTK-2</a>
+     */
+    private static final class Key {
+        /** The type of the cached object.    */ final Class<?> type;
+        /** The cached object authority code. */ final String code;
+
+        /** Creates a new key for the given type and code. */
+        Key(final Class<?> type, final String code) {
+            this.type = type;
+            this.code = code;
+        }
+
+        /** Returns the hash code value for this key. */
+        @Override public int hashCode() {
+            return type.hashCode() ^ code.hashCode();
+        }
+
+        /** Compares this key with the given object for equality .*/
+        @Override public boolean equals(final Object other) {
+            if (other instanceof Key) {
+                final Key that = (Key) other;
+                return type.equals(that.type) && code.equals(that.code);
+            }
+            return false;
+        }
+
+        /** String representation used by {@link CacheRecord}. */
+        @Override @Debug public String toString() {
+            final StringBuilder buffer = new StringBuilder("Key[").append(code);
+            if (buffer.length() > 15) { // Arbitrary limit in string length.
+                buffer.setLength(15);
+                buffer.append('…');
+            }
+            return buffer.append(" : ").append(type.getSimpleName()).append(']').toString();
+        }
+    }
+
+    /**
+     * Returns an object from a code using the given proxy. This method first checks in the cache.
+     * If no object exists in the cache for the given code, then a lock is created and the object
+     * creation is delegated to the {@linkplain #getBackingStore() backing store}.
+     * The result is then stored in the cache and returned.
+     *
+     * @param  <T>   The type of the object to be returned.
+     * @param  proxy The proxy to use for creating the object.
+     * @param  code  The code of the object to create.
+     * @return The object extracted from the cache or created.
+     * @throws FactoryException If an error occurred while creating the object.
+     */
+    private <T> T create(final AuthorityFactoryProxy<T> proxy, final String code) throws FactoryException {
+        ArgumentChecks.ensureNonNull("code", code);
+        final Class<T> type = proxy.type;
+        final Key key = new Key(type, trimAuthority(code));
+        Object value = cache.peek(key);
+        if (!type.isInstance(value)) {
+            final Cache.Handler<Object> handler = cache.lock(key);
+            try {
+                value = handler.peek();
+                if (!type.isInstance(value)) {
+                    final T result;
+                    final GeodeticAuthorityFactory factory = getBackingStore();
+                    try {
+                        result = proxy.create(factory, code);
+                    } finally {
+                        release();
+                    }
+                    value = result;     // For the finally block below.
+                    return result;
+                }
+            } finally {
+                handler.putAndUnlock(value);
+            }
+        }
+        return type.cast(value);
+    }
+
+    /**
+     * Returns operations from source and target coordinate reference system codes.
+     * The default implementation performs the following steps:
+     * <ul>
+     *   <li>Returns the cached collection for the given pair of codes if such collection already exists.</li>
+     *   <li>Otherwise:
+     *     <ol>
+     *       <li>get an instance of the backing store,</li>
+     *       <li>delegate to its {@link GeodeticAuthorityFactory#createFromCoordinateReferenceSystemCodes(String, String)} method,</li>
+     *       <li>release the backing store — <em>this step assumes that the collection obtained at step 2
+     *           is still valid after the backing store has been released</em>,</li>
+     *       <li>cache the result — <em>this step assumes that the collection obtained at step 2 is immutable</em>.</li>
+     *     </ol>
+     *   </li>
+     * </ul>
+     *
+     * @return The operations from {@code sourceCRS} to {@code targetCRS}.
+     * @throws FactoryException if the object creation failed.
+     */
+    @Override
+    @SuppressWarnings("unchecked")
+    public Set<CoordinateOperation> createFromCoordinateReferenceSystemCodes(
+            final String sourceCRS, final String targetCRS) throws FactoryException
+    {
+        ArgumentChecks.ensureNonNull("sourceCRS", sourceCRS);
+        ArgumentChecks.ensureNonNull("targetCRS", targetCRS);
+        final CodePair key = new CodePair(trimAuthority(sourceCRS), trimAuthority(targetCRS));
+        Object value = cache.peek(key);
+        if (!(value instanceof Set<?>)) {
+            final Cache.Handler<Object> handler = cache.lock(key);
+            try {
+                value = handler.peek();
+                if (!(value instanceof Set<?>)) {
+                    final GeodeticAuthorityFactory factory = getBackingStore();
+                    try {
+                        value = factory.createFromCoordinateReferenceSystemCodes(sourceCRS, targetCRS);
+                    } finally {
+                        release();
+                    }
+                }
+            } finally {
+                handler.putAndUnlock(value);
+            }
+        }
+        return (Set<CoordinateOperation>) value;
+    }
+
+    /**
+     * A pair of codes for operations to cache with
+     * {@link CachingAuthorityFactory#createFromCoordinateReferenceSystemCodes(String, String)}.
+     */
+    private static final class CodePair {
+        /** Codes of source and target CRS. */
+        private final String source, target;
+
+        /** Creates a new key for the given pair of codes. */
+        public CodePair(final String source, final String target) {
+            this.source = source;
+            this.target = target;
+        }
+
+        /** Returns the hash code value for this key. */
+        @Override public int hashCode() {
+            return source.hashCode() + target.hashCode() * 31;
+        }
+
+        /** Compares this key with the given object for equality .*/
+        @Override public boolean equals(final Object other) {
+            if (other instanceof CodePair) {
+                final CodePair that = (CodePair) other;
+                return source.equals(that.source) && target.equals(that.target);
+            }
+            return false;
+        }
+
+        /** String representation used by {@link CacheRecord}. */
+        @Override @Debug public String toString() {
+            return "CodePair[" + source + " → " + target + ']';
+        }
+    }
+
+    /**
+     * Returns a finder which can be used for looking up unidentified objects.
+     * The default implementation delegates lookup to the underlying backing store and caches the result.
+     *
+     * @return A finder to use for looking up unidentified objects.
+     * @throws FactoryException if the finder can not be created.
+     */
+    @Override
+    public IdentifiedObjectFinder createIdentifiedObjectFinder(final Class<? extends IdentifiedObject> type)
+            throws FactoryException
+    {
+        return new Finder(this, type);
+    }
+
+    /**
+     * An implementation of {@link IdentifiedObjectFinder} which delegates
+     * the work to the underlying backing store and caches the result.
+     *
+     * <div class="section">Implementation note</div>
+     * we will create objects using directly the underlying backing store, not using the cache.
+     * This is because hundred of objects may be created during a scan while only one will be typically retained.
+     * We do not want to flood the cache with every false candidates that we encounter during the scan.
+     *
+     * <div class="section">Synchronization note</div>
+     * our public API claims that {@link IdentifiedObjectFinder}s are not thread-safe.
+     * Nevertheless we synchronize this particular implementation for safety, because the consequence of misuse
+     * are more dangerous than other implementations. Furthermore this is also a way to assert that no code path
+     * go to the {@link #create(AuthorityFactoryProxy, String)} method from a non-overridden public method.
+     *
+     * @author  Martin Desruisseaux (IRD, Geomatys)
+     * @since   0.7
+     * @version 0.7
+     * @module
+     */
+    private static final class Finder extends IdentifiedObjectFinder {
+        /**
+         * The finder on which to delegate the work. This is acquired by {@link #acquire()}
+         * <strong>and must be released</strong> by call to {@link #release()} once finished.
+         */
+        private transient IdentifiedObjectFinder finder;
+
+        /**
+         * Number of time that {@link #acquire()} has been invoked.
+         * When this count reaches zero, the {@linkplain #finder} is released.
+         */
+        private transient int acquireCount;
+
+        /**
+         * Creates a finder for the given type of objects.
+         */
+        Finder(final CachingAuthorityFactory factory, final Class<? extends IdentifiedObject> type) {
+            super(factory, type);
+        }
+
+        /**
+         * Acquires a new {@linkplain #finder}.
+         * The {@link #release()} method must be invoked in a {@code finally} block after the call to {@code acquire}.
+         * The pattern must be as below (note that the call to {@code acquire()} is inside the {@code try} block):
+         *
+         * {@preformat java
+         *     try {
+         *         acquire();
+         *         (finder or proxy).doSomeStuff();
+         *     } finally {
+         *         release();
+         *     }
+         * }
+         */
+        private void acquire() throws FactoryException {
+            assert Thread.holdsLock(this);
+            assert (acquireCount == 0) == (finder == null) : acquireCount;
+            if (acquireCount == 0) {
+                final GeodeticAuthorityFactory delegate = ((CachingAuthorityFactory) factory).getBackingStore();
+                /*
+                 * Set 'acquireCount' only after we succeed in fetching the factory, and before any operation on it.
+                 * The intend is to get CachingAuthorityFactory.release() invoked if and only if the getBackingStore()
+                 * method succeed, no matter what happen after this point.
+                 */
+                acquireCount = 1;
+                finder = delegate.createIdentifiedObjectFinder(getObjectType());
+                finder.setWrapper(this);
+            } else {
+                acquireCount++;
+            }
+        }
+
+        /**
+         * Releases the {@linkplain #finder}.
+         */
+        private void release() {
+            assert Thread.holdsLock(this);
+            if (acquireCount == 0) {
+                // May happen only if a failure occurred during getBackingStore() execution.
+                return;
+            }
+            if (--acquireCount == 0) {
+                finder = null;
+                ((CachingAuthorityFactory) factory).release();
+            }
+        }
+
+        /**
+         * Returns the authority of the factory examined by this finder.
+         */
+        @Override
+        public synchronized Citation getAuthority() throws FactoryException {
+            try {
+                acquire();
+                return finder.getAuthority();
+            } finally {
+                release();
+            }
+        }
+
+        /**
+         * Returns a set of authority codes that <strong>may</strong> identify the same
+         * object than the specified one. This method delegates to the backing finder.
+         */
+        @Override
+        protected synchronized Set<String> getCodeCandidates(final IdentifiedObject object) throws FactoryException {
+            try {
+                acquire();
+                return finder.getCodeCandidates(object);
+            } finally {
+                release();
+            }
+        }
+
+        /**
+         * 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 backing store and caches the result.
+         */
+        @Override
+        public IdentifiedObject find(final IdentifiedObject object) throws FactoryException {
+            final Map<IdentifiedObject, IdentifiedObject> findPool = ((CachingAuthorityFactory) factory).findPool;
+            synchronized (findPool) {
+                final IdentifiedObject candidate = findPool.get(object);
+                if (candidate != null) {
+                    return (candidate == NilReferencingObject.INSTANCE) ? null : candidate;
+                }
+            }
+            /*
+             * Nothing has been found in the cache. Delegates the search to the backing store.
+             * We must delegate to 'finder' (not to 'super') in order to take advantage of overridden methods.
+             */
+            final IdentifiedObject candidate;
+            synchronized (this) {
+                try {
+                    acquire();
+                    candidate = finder.find(object);
+                } finally {
+                    release();
+                }
+            }
+            /*
+             * If the full scan was allowed, then stores the result even if null so
+             * we can remember that no object has been found for the given argument.
+             */
+            if (candidate != null || isFullScanAllowed()) {
+                synchronized (findPool) {
+                    findPool.put(object, (candidate == null) ? NilReferencingObject.INSTANCE : candidate);
+                }
+            }
+            return candidate;
+        }
+    }
+
+    /**
+     * Prints the cache content to the given writer.
+     * Keys are sorted by numerical order if possible, or alphabetical order otherwise.
+     * This method is used for debugging purpose only.
+     *
+     * @param out The output printer, or {@code null} for the {@linkplain System#out standard output stream}.
+     */
+    @Debug
+    public void printCacheContent(final PrintWriter out) {
+        CacheRecord.printCacheContent(cache, out);
+    }
+
+    /**
+     * Releases resources immediately instead of waiting for the garbage collector.
+     * Once a factory has been disposed, further {@code createFoo(String)} method invocations
+     * may throw a {@link FactoryException}. Disposing a previously-disposed factory, however, has no effect.
+     */
+    @Override
+    public void dispose() {
+        cache.clear();
+        authority = null;
+        synchronized (findPool) {
+            findPool.clear();
+        }
+    }
+}

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

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

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=1718453&r1=1718452&r2=1718453&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] Mon Dec  7 20:31:21 2015
@@ -34,10 +34,11 @@ import org.apache.sis.util.ArgumentCheck
 import org.apache.sis.util.ComparisonMode;
 import org.apache.sis.util.Utilities;
 import org.apache.sis.util.logging.Logging;
+import org.apache.sis.util.resources.Errors;
 
 
 /**
- * Search in an authority factory for objects equal, ignoring metadata, to a given object.
+ * Search 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}
  * from an incomplete one, for example from an object without "{@code ID[…]}" or "{@code AUTHORITY[…]}"
  * element in <cite>Well Known Text</cite>.
@@ -49,7 +50,7 @@ import org.apache.sis.util.logging.Loggi
  *   <li>Optionally configure that instance by calling its setter methods.</li>
  *   <li>Perform a search by invoking the {@link #find(IdentifiedObject)} or
  *       {@link #findIdentifier(IdentifiedObject)} methods.</li>
- *   <li>Reuse the same {@code IdentifiedObjectFinder} instance for consecutive searches.</li>
+ *   <li>The same {@code IdentifiedObjectFinder} instance can be reused for consecutive searches.</li>
  * </ol>
  *
  * <div class="section">Thread safety</div>
@@ -66,43 +67,34 @@ import org.apache.sis.util.logging.Loggi
  */
 public class IdentifiedObjectFinder {
     /**
+     * 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;
+
+    /**
      * The factory to use for creating objects. This is the factory specified at construction time.
-     * We do not put this field in public API because the factory may not be the one the user would
-     * expect. Some of our {@link GeodeticAuthorityFactory} implementations will use a wrapper
-     * factory rather than the factory on which {@code createIdentifiedObjectFinder()} was invoked.
      */
     final AuthorityFactory factory;
 
     /**
      * The proxy for objects creation. This is usually set at construction time.
-     * But in the particular case of {@link CachingAuthorityFactory#Finder}, this
-     * is left to {@code null} and assigned only when a backing store factory is
-     * in use.
-     *
-     * <p>If this field is initialized only when needed, then the following methods
-     * must be overridden and ensure that the initialization has been performed:</p>
-     * <ul>
-     *   <li>{@link #getAuthority()}</li>
-     *   <li>{@link #getCodeCandidates(IdentifiedObject)}</li>
-     *   <li>{@link #find(IdentifiedObject)} (see note below)</li>
-     *   <li>{@link #findIdentifier(IdentifiedObject)} (see note below)</li>
-     * </ul>
-     *
-     * Note: the {@code find(…)} methods do not need to be overridden if all other methods
-     * are overridden and the {@link #create(String, int)} method is overridden too.
      */
     private AuthorityFactoryProxy<?> proxy;
 
     /**
-     * The parent finder, or {@code null} if none. This field is non-null only if this
-     * finder is wrapped by another finder like {@link CachingAuthorityFactory.Finder}.
+     * The cache or the adapter which is wrapping this finder, or {@code null} if none.
+     * An example of wrapper is {@link CachingAuthorityFactory}'s finder.
+     *
+     * @see #setWrapper(IdentifiedObjectFinder)
      */
-    private IdentifiedObjectFinder parent;
+    private IdentifiedObjectFinder wrapper;
 
     /**
-     * The comparison mode.
+     * The object in process of being searched, or {@code null} if none.
+     * This is used by {@link #find(IdentifiedObject)} for detecting recursivity.
      */
-    private ComparisonMode comparisonMode = ComparisonMode.IGNORE_METADATA;
+    private transient IdentifiedObject searching;
 
     /**
      * {@code true} for performing full scans, or {@code false} otherwise.
@@ -121,14 +113,31 @@ public class IdentifiedObjectFinder {
      *
      * @see GeodeticAuthorityFactory#createIdentifiedObjectFinder(Class)
      */
-    protected IdentifiedObjectFinder(final AuthorityFactory factory,
-            final Class<? extends IdentifiedObject> type)
-    {
+    protected IdentifiedObjectFinder(final AuthorityFactory factory, final Class<? extends IdentifiedObject> type) {
         this.factory = factory;
         proxy = AuthorityFactoryProxy.getInstance(type);
     }
 
     /**
+     * Declares that the given cache or adapter is the wrapper of this finder.
+     * This method should be invoked at wrapper construction time.
+     * An example of wrapper is {@link CachingAuthorityFactory}'s finder.
+     *
+     * <p>This method also copies the configuration of the given finder, thus providing a central place
+     * where to add calls to setters methods if such methods are added in a future SIS version.</p>
+     *
+     * <div class="section">Maintenance note</div>
+     * Adding properties to this method is not sufficient. See also the classes that override
+     * {@link #setFullScanAllowed(boolean)} — some of them may be defined in other packages.
+     *
+     * @param other The cache or the adapter wrapping this finder.
+     */
+    final void setWrapper(final IdentifiedObjectFinder other) {
+        wrapper = other;
+        setFullScanAllowed(other.isFullScanAllowed());
+    }
+
+    /**
      * Returns the type of the objects to be created by the proxy instance.
      * This method needs to be overridden by the sub-classes that do not define a proxy.
      *
@@ -149,51 +158,6 @@ public class IdentifiedObjectFinder {
     }
 
     /**
-     * Sets the given finder as the parent of this finder. The parent is typically a
-     * {@link CachingAuthorityFactory} which will be used by {@link #findFromParent}.
-     *
-     * @param other The new parent.
-     */
-    final void setParent(final IdentifiedObjectFinder other) {
-        parent = other;
-    }
-
-    /**
-     * Copies the configuration of the given finder. This method provides a central place
-     * where to add call to setters methods if such methods are added in a future version.
-     *
-     * <div class="section">Maintenance note</div>
-     * Adding properties to this method is not sufficient. See also the classes that override
-     * {@link #setFullScanAllowed(boolean)} - some of them may be defined in other packages.
-     */
-    final void copyConfiguration(final IdentifiedObjectFinder other) {
-        setComparisonMode (other.getComparisonMode());
-        setFullScanAllowed(other.isFullScanAllowed());
-    }
-
-    /**
-     * Returns the criterion used for determining if a candidate found by this {@code IdentifiedObjectFinder}
-     * should be considered equals to the requested object.
-     * The default value is {@link ComparisonMode#IGNORE_METADATA}.
-     *
-     * @return The criterion to use for comparing objects.
-     */
-    public ComparisonMode getComparisonMode() {
-        return comparisonMode;
-    }
-
-    /**
-     * Sets the criterion used for determining if a candidate found by this {@code IdentifiedObjectFinder}
-     * should be considered equals to the requested object.
-     *
-     * @param mode The criterion to use for comparing objects.
-     */
-    public void setComparisonMode(final ComparisonMode mode) {
-        ArgumentChecks.ensureNonNull("mode", mode);
-        comparisonMode = mode;
-    }
-
-    /**
      * 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}.
@@ -216,15 +180,14 @@ public class IdentifiedObjectFinder {
     }
 
     /**
-     * Lookups an object which is {@linkplain Utilities#equalsIgnoreMetadata equal, ignoring metadata},
-     * to the specified object.
+     * Lookups an object which is approximatively equal to the specified object.
      * The default implementation tries to instantiate some {@linkplain AbstractIdentifiedObject identified objects}
      * from the authority factory specified at construction time, in the following order:
      *
      * <ul>
      *   <li>If the specified object contains {@linkplain AbstractIdentifiedObject#getIdentifiers() identifiers}
      *       associated to the same authority than the factory, then those identifiers are used for
-     *       {@linkplain GeodeticAuthorityFactory#createObject creating objects} to be tested.</li>
+     *       {@linkplain GeodeticAuthorityFactory#createObject(String) creating objects} to be tested.</li>
      *   <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>
@@ -233,7 +196,7 @@ public class IdentifiedObjectFinder {
      * </ul>
      *
      * The first of the above created objects which is equal to the specified object in the
-     * the sense of {@link Utilities#equalsIgnoreMetadata(Object, Object)} is returned.
+     * the sense of {@link ComparisonMode#APPROXIMATIVE} is returned.
      *
      * @param  object The object looked up.
      * @return The identified object, or {@code null} if not found.
@@ -241,33 +204,70 @@ public class IdentifiedObjectFinder {
      */
     public IdentifiedObject find(final IdentifiedObject object) throws FactoryException {
         ArgumentChecks.ensureNonNull("object", object);
-        /*
-         * First check if one of the identifiers can be used to spot directly an
-         * identified object (and check it's actually equal to one in the factory).
-         */
-        IdentifiedObject candidate = createFromIdentifiers(object);
-        if (candidate != null) {
-            return 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 candidate;
+        final IdentifiedObject state = searching;
+        try {
+            searching = object;
+            if (state == null) {
+                /*
+                 * 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 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 candidate;
+                }
+                /*
+                 * Here we exhausted the quick paths. Perform a full scan (costly) if we are allowed to,
+                 * otherwise abandon.
+                 */
+                return fullScan ? createFromCodes(object) : null;
+            } else if (state != object) {
+                /*
+                 * 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,
+                 * the finder backed by the EPSG database will first search for a matching ellipsoid in order
+                 * to reduce the scope of the search. Note that since dependencies can be of any type, we
+                 * have to temporarily change the proxy.
+                 *
+                 * When invoked recursively, we delegate to the wrapper if it exists. The wrapper is typically
+                 * a CachingAuthorityFactory.Finder, in which case we want to leverage its cache capability.
+                 */
+                IdentifiedObjectFinder finder = wrapper;
+                if (finder == null) {
+                    finder = this;
+                }
+                final AuthorityFactoryProxy<?> old = finder.proxy;
+                finder.proxy = AuthorityFactoryProxy.getInstance(object.getClass());
+                try {
+                    return finder.find(object);
+                } finally {
+                    finder.proxy = old;
+                }
+            } else {
+                throw new IllegalArgumentException(Errors.format(Errors.Keys.RecursiveCreateCallForKey_1, object.getName()));
+            }
+        } finally {
+            searching = state;
         }
-        /*
-         * Here we exhausted the quick paths. Bail out if the user does not want a full scan.
-         */
-        return fullScan ? createFromCodes(object) : null;
     }
 
     /**
-     * Returns the identifier of the specified object, or {@code null} if none. The default
-     * implementation invokes <code>{@linkplain #find find}(object)</code> and extracts the
-     * code from the returned {@linkplain AbstractIdentifiedObject identified object}.
+     * Returns the identifier of the specified object, or {@code null} if none.
+     * On some implementations, invoking this method is more efficient than invoking
+     * {@link #find(IdentifiedObject)} when the full object is not needed.
+     *
+     * <p>The default implementation invokes <code>{@linkplain #find find}(object)</code> and extracts
+     * the identifier code from the returned {@linkplain AbstractIdentifiedObject identified object}.</p>
      *
      * @param  object The object looked up.
      * @return The identifier of the given object, or {@code null} if none were found.
@@ -287,53 +287,8 @@ public class IdentifiedObjectFinder {
     }
 
     /**
-     * Lookups an object from the parent finder, of from this finder if there is no parent.
-     * A parent finder exists only if this finder is wrapped by another finder. The parent
-     * can be the {@link CachingAuthorityFactory} finder or {@link AuthorityFactoryAdapter}
-     * finder.
-     *
-     * <p>This method should be considered as an implementation details.
-     * It is needed by {@link org.apache.sis.referencing.factory.epsg.EPSGFactory}.
-     * The main purpose of this method is to allow {@link DirectAuthorityFactory}
-     * implementations to look for dependencies while leveraging the cache managed
-     * by their {@link CachingAuthorityFactory} wrappers.</p>
-     *
-     * @param  object The object looked up.
-     * @param  type The type of object to look for. It does not need to be the type specified
-     *         at construction time. This relaxation exists in order to allow dependencies lookup,
-     *         since the dependencies may be of different kinds.
-     * @return The identified object, or {@code null} if not found.
-     * @throws FactoryException if an error occurred while creating an object.
-     */
-    final IdentifiedObject findFromParent(final IdentifiedObject object,
-            final Class<? extends IdentifiedObject> type) throws FactoryException
-    {
-        return findFromParent(object, AuthorityFactoryProxy.getInstance(type));
-    }
-
-    /**
-     * Implementation of {@link #findFromParent(IdentifiedObject, Class)}, which may invoke itself
-     * recursively.  This method delegates to the parent if there is one, but still save and setup
-     * the proxy for this context frame. This is because {@link CachingAuthorityFactory} delegates
-     * to {@link DirectAuthorityFactory}, which delegate back to {@link CachingAuthorityFactory}
-     * through this method (for object dependencies lookup), etc.
-     */
-    private IdentifiedObject findFromParent(final IdentifiedObject object,
-            final AuthorityFactoryProxy<?> type) throws FactoryException
-    {
-        final IdentifiedObjectFinder parent = this.parent;
-        final AuthorityFactoryProxy<?> old = proxy;
-        proxy = type;
-        try {
-            return (parent != null) ? parent.findFromParent(object, type) : find(object);
-        } finally {
-            proxy = old;
-        }
-    }
-
-    /**
-     * Creates an object {@linkplain Utilities#equalsIgnoreMetadata equal, ignoring metadata}, to the
-     * specified object using only the {@linkplain AbstractIdentifiedObject#getIdentifiers identifiers}.
+     * Creates an object equals (optionally ignoring metadata), to the specified object
+     * using only the {@linkplain AbstractIdentifiedObject#getIdentifiers identifiers}.
      * If no such object is found, returns {@code null}.
      *
      * <p>This method may be used in order to get a fully identified object from a partially identified one.</p>
@@ -355,12 +310,12 @@ public class IdentifiedObjectFinder {
             final String code = IdentifiedObjects.toString(id);
             final IdentifiedObject candidate;
             try {
-                candidate = (IdentifiedObject) proxy.createFromAPI(factory, code);
+                candidate = create(code);
             } catch (NoSuchAuthorityCodeException e) {
                 // The identifier was not recognized. No problem, let's go on.
                 continue;
             }
-            if (Utilities.deepEquals(candidate, object, comparisonMode)) {
+            if (Utilities.deepEquals(candidate, object, COMPARISON_MODE)) {
                 return candidate;
             }
         }
@@ -368,10 +323,9 @@ public class IdentifiedObjectFinder {
     }
 
     /**
-     * Creates an object {@linkplain Utilities#equalsIgnoreMetadata equal, ignoring metadata}, to
-     * the specified object using only the {@linkplain AbstractIdentifiedObject#getName name} and
-     * {@linkplain AbstractIdentifiedObject#getAlias aliases}. If no such object is found, returns
-     * {@code null}.
+     * Creates an object equals (optionally ignoring metadata), to the specified object using only the
+     * {@linkplain AbstractIdentifiedObject#getName name} and {@linkplain AbstractIdentifiedObject#getAlias aliases}.
+     * If no such object is found, returns {@code null}.
      *
      * <p>This method may be used with some {@linkplain GeodeticAuthorityFactory authority factory}
      * implementations like the one backed by the EPSG database, which are capable to find an object
@@ -388,7 +342,7 @@ public class IdentifiedObjectFinder {
         String code = object.getName().getCode();
         IdentifiedObject candidate;
         try {
-            candidate = (IdentifiedObject) proxy.createFromAPI(factory, code);
+            candidate = create(code);
         } catch (FactoryException e) {
             /*
              * The identifier was not recognized. We will continue later will aliases.
@@ -398,18 +352,18 @@ public class IdentifiedObjectFinder {
              */
             candidate = null;
         }
-        if (Utilities.deepEquals(candidate, object, comparisonMode)) {
+        if (Utilities.deepEquals(candidate, object, COMPARISON_MODE)) {
             return candidate;
         }
         for (final GenericName id : object.getAlias()) {
             code = id.toString();
             try {
-                candidate = (IdentifiedObject) proxy.createFromAPI(factory, code);
+                candidate = create(code);
             } catch (FactoryException e) {
                 // The name was not recognized. No problem, let's go on.
                 continue;
             }
-            if (Utilities.deepEquals(candidate, object, comparisonMode)) {
+            if (Utilities.deepEquals(candidate, object, COMPARISON_MODE)) {
                 return candidate;
             }
         }
@@ -417,10 +371,10 @@ public class IdentifiedObjectFinder {
     }
 
     /**
-     * Creates an object {@linkplain Utilities#equalsIgnoreMetadata equal, ignoring metadata}, to the
-     * specified object. This method scans the {@linkplain #getAuthorityCodes authority codes},
+     * Creates an object equals (optionally ignoring metadata), to the specified object.
+     * This method scans the {@linkplain #getCodeCandidates(IdentifiedObject) authority codes},
      * create the objects and returns the first one which is equal to the specified object in
-     * the sense of {@link Utilities#equalsIgnoreMetadata equalsIgnoreMetadata}.
+     * the sense of {@link Utilities#deepEquals(Object, Object, ComparisonMode)}.
      *
      * <p>This method may be used in order to get a fully {@linkplain AbstractIdentifiedObject identified
      * object} from an object without {@linkplain AbstractIdentifiedObject#getIdentifiers() identifiers}.</p>
@@ -443,11 +397,11 @@ public class IdentifiedObjectFinder {
             for (final String code : codes) {
                 final IdentifiedObject candidate;
                 try {
-                    candidate = (IdentifiedObject) proxy.createFromAPI(factory, code);
+                    candidate = create(code);
                 } catch (FactoryException e) {
                     continue;
                 }
-                if (Utilities.deepEquals(candidate, object, comparisonMode)) {
+                if (Utilities.deepEquals(candidate, object, COMPARISON_MODE)) {
                     return candidate;
                 }
             }
@@ -459,11 +413,24 @@ public class IdentifiedObjectFinder {
     }
 
     /**
+     * Creates an object for the given code. This method is invoked by the default implementation of
+     * {@link #find(IdentifiedObject)} and {@link #findIdentifier(IdentifiedObject)} methods for each
+     * code returned by the {@link #getCodeCandidates(IdentifiedObject)} method, in iteration order,
+     * until an object approximatively equals to the requested object is found.
+     *
+     * @param  code The authority code for which to create an object.
+     * @return The identified object for the given code, or {@code null} to stop attempts.
+     * @throws FactoryException if an error occurred while creating the object.
+     */
+    private IdentifiedObject create(final String code) throws FactoryException {
+        return (IdentifiedObject) proxy.createFromAPI(factory, code);
+    }
+
+    /**
      * Returns a set of authority codes that <strong>may</strong> identify the same object than the specified one.
      * The returned set must contains <em>at least</em> the code of every objects that are
-     * {@linkplain Utilities#equalsIgnoreMetadata equal, ignoring metadata}, to the specified one.
-     * However the set is not required to contains only the codes of those objects;
-     * it may conservatively contains the code for more objects if an exact search is too expensive.
+     * {@linkplain ComparisonMode#APPROXIMATIVE approximatively equal} to the specified one.
+     * However the set may conservatively contains the code for more objects if an exact search is too expensive.
      *
      * <p>This method is invoked by the default {@link #find(IdentifiedObject)} method implementation.
      * The caller iterates through the returned codes, instantiate the objects and compare them with

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/ComparisonMode.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/ComparisonMode.java?rev=1718453&r1=1718452&r2=1718453&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/ComparisonMode.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/ComparisonMode.java [UTF-8] Mon Dec  7 20:31:21 2015
@@ -178,14 +178,15 @@ public enum ComparisonMode {
     }
 
     /**
-     * If the two given objects are equals according one of the modes enumerated in this class,
-     * then returns that mode. Otherwise returns {@code null}. This method is used mostly for
-     * diagnostic purpose.
+     * If the two given objects are equal according one of the modes enumerated in this class,
+     * then returns that mode. Otherwise returns {@code null}.
+     *
+     * <p><b>Note:</b> this method never return the {@link #DEBUG} mode.</p>
      *
      * @param  o1 The first object to compare, or {@code null}.
      * @param  o2 The second object to compare, or {@code null}.
-     * @return The must suitable comparison mode, or {@code null} if the two given objects
-     *         are not equal for any mode in this enumeration.
+     * @return The most suitable comparison mode, or {@code null} if the two given objects
+     *         are not equal according any mode in this enumeration.
      */
     public static ComparisonMode equalityLevel(final Object o1, Object o2) {
         if (o1 == o2) {




Mime
View raw message