/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2005-2008, Open Source Geospatial Foundation (OSGeo) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. */ package org.geotools.referencing.factory; import java.util.*; import org.opengis.referencing.*; import org.opengis.referencing.cs.*; import org.opengis.referencing.crs.*; import org.opengis.referencing.datum.*; import org.opengis.referencing.operation.*; import org.opengis.util.InternationalString; import org.opengis.metadata.citation.Citation; import org.geotools.factory.Factory; import org.geotools.factory.FactoryRegistryException; import org.geotools.metadata.iso.citation.Citations; import org.geotools.util.GenericName; import org.geotools.resources.i18n.ErrorKeys; import org.geotools.resources.i18n.Errors; import org.geotools.resources.i18n.Vocabulary; import org.geotools.resources.i18n.VocabularyKeys; /** * An authority factory that delegates the object creation to an other factory determined from the * authority name in the code. This factory requires that every codes given to a {@code createFoo} * method are prefixed by the authority name, for example {@code "EPSG:4326"}. This is different * from using a factory from a known authority, in which case the authority part was optional (for * example when using the {@linkplain org.geotools.referencing.factory.epsg EPSG authority factory}, * the {@code "EPSG:"} part in {@code "EPSG:4326"} is optional). * <p> * This class parses the authority name and delegates the work the corresponding factory. For * example if any {@code createFoo(...)} method in this class is invoked with a code starting * by {@code "EPSG:"}, then this class delegates the object creation to one of the authority * factories provided to the constructor. * <p> * This class is not registered in {@link ReferencingFactoryFinder}, because it is not a real * authority factory. There is not a single authority name associated to this factory, but rather * a set of names determined from all available authority factories. * * @since 2.4 * * @source $URL$ * @version $Id$ * @author Martin Desruisseaux (IRD) */ public class ManyAuthoritiesFactory extends AuthorityFactoryAdapter implements CRSAuthorityFactory, CSAuthorityFactory, DatumAuthorityFactory, CoordinateOperationAuthorityFactory { /** * The types to be recognized for the {@code factories} argument in * constructors. Must be consistent with the types expected by the * {@link AllAuthoritiesFactory#fromFactoryRegistry(String, Class)} method. */ @SuppressWarnings("unchecked") private static final Class<? extends AuthorityFactory>[] FACTORY_TYPES = new Class[] { CRSAuthorityFactory.class, DatumAuthorityFactory.class, CSAuthorityFactory.class, CoordinateOperationAuthorityFactory.class }; /** * The types created by {@link #FACTORY_TYPES}. For each type {@code OBJECT_TYPES[i]}, * the factory to be used must be {@code FACTORY_TYPES[i]}. */ @SuppressWarnings("unchecked") private static final Class<? extends IdentifiedObject>[] OBJECT_TYPES = new Class[] { CoordinateReferenceSystem.class, Datum.class, CoordinateSystem.class, CoordinateOperation.class }; /** * The set of user-specified factories, or {@code null} if none. * This field should be modified by {@link #setFactories} only. */ private Collection<AuthorityFactory> factories; /** * Guard against infinite recursivity in {@link #getAuthorityCodes}. */ private final ThreadLocal<Boolean> inProgress; /** * Creates a new factory using the specified set of user factories. Any call to a * {@code createFoo(code)} method will scan the supplied factories in their iteration * order. The first factory implementing the appropriate interface and having the expected * {@linkplain AuthorityFactory#getAuthority authority name} will be used. * <p> * If the {@code factories} collection contains more than one factory for the same authority * and interface, then all additional factories will be {@linkplain FallbackAuthorityFactory * fallbacks}, to be tried in iteration order only if the first acceptable factory failed to * create the requested object. * * @param factories A set of user-specified factories to try before to delegate * to {@link GeometryFactoryFinder}. */ public ManyAuthoritiesFactory(final Collection<? extends AuthorityFactory> factories) { super(NORMAL_PRIORITY); inProgress = new ThreadLocal<Boolean>(); if (factories!=null && !factories.isEmpty()) { for (final AuthorityFactory factory : factories) { if (factory instanceof Factory) { hints.putAll(((Factory) factory).getImplementationHints()); } } this.factories = createFallbacks(factories); } } /** * Returns the factories. This method should not be public since it returns directly the * internal instance. This method is to be overriden by {@link AllAuthoritiesFactory} only. */ Collection<AuthorityFactory> getFactories() { return factories; } /** * Sets the factories. This method is invoked by the {@link AllAuthoritiesFactory} subclass * only. No one else should invoke this method, since factories should be immutable. */ synchronized final void setFactories(final Collection<AuthorityFactory> factories) { this.factories = createFallbacks(factories); } /** * If more than one factory is found for the same authority and interface, * then wraps them as a chain of {@link FallbackAuthorityFactory}. */ private static Collection<AuthorityFactory> createFallbacks( final Collection<? extends AuthorityFactory> factories) { /* * 'authorities' Will contains the set of all authorities found without duplicate values * in the sense of Citations.identifierMatches(...). 'factoriesByAuthority' will contains * the collection of factories for each authority. */ int authorityCount = 0; final Citation[] authorities = new Citation[factories.size()]; @SuppressWarnings("unchecked") final List<AuthorityFactory>[] factoriesByAuthority = new List[authorities.length]; for (final AuthorityFactory factory : factories) { /* * Check if the authority has already been meet previously. If the authority is found * then 'authorityIndex' is set to its index. Otherwise the new authority is added to * the 'authorities' list. */ Citation authority = factory.getAuthority(); int authorityIndex; for (authorityIndex=0; authorityIndex<authorityCount; authorityIndex++) { final Citation candidate = authorities[authorityIndex]; if (Citations.identifierMatches(candidate, authority)) { authority = candidate; break; } } final List<AuthorityFactory> list; if (authorityIndex == authorityCount) { authorities[authorityCount++] = authority; factoriesByAuthority[authorityIndex] = list = new ArrayList<AuthorityFactory>(4); } else { list = factoriesByAuthority[authorityIndex]; } if (!list.contains(factory)) { list.add(factory); } } /* * For each authority, chains the factories into a FallbackAuthorityFactory object. */ final ArrayList<AuthorityFactory> result = new ArrayList<AuthorityFactory>(); final List<AuthorityFactory> buffer = new ArrayList<AuthorityFactory>(4); for (int i=0; i<authorityCount; i++) { final Collection<AuthorityFactory> list = factoriesByAuthority[i]; while (!list.isEmpty()) { AuthorityFactory primary = null; boolean needOtherChains = false; for (final Iterator<AuthorityFactory> it=list.iterator(); it.hasNext();) { final AuthorityFactory fallback = it.next(); if (primary == null) { primary = fallback; } else if (!FallbackAuthorityFactory.chainable(primary, fallback)) { needOtherChains = true; continue; } buffer.add(fallback); if (!needOtherChains) { it.remove(); } } result.add(FallbackAuthorityFactory.create(buffer)); buffer.clear(); } } result.trimToSize(); return result; } /** * If this factory is a wrapper for the specified factory that do not add any additional * {@linkplain #getAuthorityCodes authority codes}, returns {@code true}. This method is * for {@link FallbackAuthorityFactory} internal use only. */ @Override boolean sameAuthorityCodes(final AuthorityFactory factory) { // We don't want to inherit AuthorityFactoryAdapter implementation here. return factory == this; } /** * Returns the character separator for the specified code. The default implementation returns * the {@linkplain GenericName#DEFAULT_SEPARATOR default name separator} {@code ':'}, except * if the code looks like a URL (e.g. {@code "http://www.opengis.net/"}), in which case this * method returns {@code '/'}. * <p> * In the current implementation, "looks like a URL" means that the first * non-{@linkplain Character#isLetterOrDigit(char) aplhanumeric} characters * are {@code "://"}. But this heuristic rule may change in future implementations. */ protected char getSeparator(String code) { code = code.trim(); final int length = code.length(); for (int i=0; i<length; i++) { if (!Character.isLetterOrDigit(code.charAt(i))) { if (code.regionMatches(i, "://", 0, 3)) { return '/'; } break; } } return GenericName.DEFAULT_SEPARATOR; } /** * Returns {@code true} if the specified code can be splitted in a (<cite>authority</cite>, * <cite>code</cite>) pair at the specified index. The default implementation returns * {@code true} if the first non-whitespace character on the left and right side are * valid Java identifiers. * <p> * The purpose of this method is to avoid considering the {@code "//"} part in * {@code "http://www.opengis.net/gml/srs/epsg.xml"} as separators. In case of * failure to parse the code, this restriction will produce and error message * like "<cite>Unknown <code>http://www.opengis.net</code> authority</cite>" * instead of "<cite>Unknown <code>http:</code> authority</cite>". * <p> * We may consider to turn this method into a protected one if the users need to override it. */ private static boolean canSeparateAt(final String code, final int index) { char c; int i = index; do { if (--i < 0) { return false; } c = code.charAt(i); } while (Character.isWhitespace(c)); if (!Character.isJavaIdentifierPart(c)) { return false; } final int length = code.length(); i = index; do { if (++i >= length) { return false; } c = code.charAt(i); } while (Character.isWhitespace(c)); return Character.isJavaIdentifierPart(c) || c == '-'; } /** * Returns the vendor responsible for creating this factory implementation. * The default implementation returns {@linkplain Citations#GEOTOOLS Geotools}. */ @Override public Citation getVendor() { return Citations.GEOTOOLS; } /** * Returns the organization or party responsible for definition and maintenance of the * database. The default implementation returns a citation with title "All". */ @Override public Citation getAuthority() { return ALL; } /** * Returns the authority names of every factories given at construction time. */ public Set<String> getAuthorityNames() { final Set<String> names = new HashSet<String>(); final Collection<AuthorityFactory> factories = getFactories(); if (factories != null) { for (final AuthorityFactory factory : factories) { names.add(Citations.getIdentifier(factory.getAuthority())); } } return names; } /** * Returns a description of the underlying backing store, or {@code null} if unknow. * * @throws FactoryException if a failure occured while fetching the engine description. */ @Override public String getBackingStoreDescription() throws FactoryException { // We have no authority code, so we can't pick a particular factory. return null; } /** * Returns the direct dependencies. Current implementation returns the internal structure * because we know that this package will not modifies it. But if the method become public, * we will need to returns a unmodifiable view. */ @Override Collection<? super AuthorityFactory> dependencies() { return getFactories(); } /** * Returns {@code true} if the specified factory should be excluded from the search. * We exclude adapters around {@link AllAuthoritiesFactory}. This code actually aims * to exclude {@link URN_AuthorityFactory} and similar adapters around all factories, * since it leads to duplicated search and innacurate identifier to be returned by * {@link #findIdentifier}. */ private static boolean exclude(final AuthorityFactory factory) { if (ManyAuthoritiesFactory.class.isInstance(factory)) { return true; } if (factory instanceof AuthorityFactoryAdapter) { final AuthorityFactoryAdapter adapter = (AuthorityFactoryAdapter) factory; return exclude(adapter.crsFactory) || exclude(adapter.csFactory) || exclude(adapter.datumFactory) || exclude(adapter.operationFactory); } return false; } /** * Same as {@link #fromFactoryRegistry(String, Class)}, but returns every factories * that fit the given type. The factories are added to the specified set. */ final void fromFactoryRegistry(final String authority, final Class<? extends AuthorityFactory> type, final Set<AuthorityFactory> addTo) { for (int i=0; i<OBJECT_TYPES.length; i++) { if (OBJECT_TYPES[i].isAssignableFrom(type)) { final AuthorityFactory factory; try { factory = fromFactoryRegistry(authority, FACTORY_TYPES[i]); } catch (FactoryRegistryException e) { // No factory for the given authority. It may be normal. continue; } if (!exclude(factory)) { addTo.add(factory); } } } } /** * Returns a factory for the specified authority, or {@code null} if none. * To be overriden by {@link AllAuthoritiesFactory} in order to search among * factories registered on a system-wide basis. * * @param authority The authority to query. * @param type The interface to be implemented. * @return The factory. * @throws FactoryRegistryException if there is no factory registered for the supplied * authority and hints. */ <T extends AuthorityFactory> T fromFactoryRegistry(final String authority, final Class<T> type) throws FactoryRegistryException { return null; } /** * Searchs for a factory of the given type. This method first search in user-supplied * factories. If no user factory is found, then this method request for a factory using * {@link GeometryFactoryFinder}. The authority name is inferred from the specified code. * * @param type The interface to be implemented. * @param code The code of the object to create. * @return The factory. * @throws NoSuchAuthorityCodeException if no suitable factory were found. */ @Override final <T extends AuthorityFactory> T getAuthorityFactory(final Class<T> type, final String code) throws NoSuchAuthorityCodeException { ensureNonNull("code", code); String authority = null; FactoryRegistryException cause = null; final Collection<AuthorityFactory> factories = getFactories(); final char separator = getSeparator(code); for (int split = code.lastIndexOf(separator); split >= 0; split = code.lastIndexOf(separator, split-1)) { if (!canSeparateAt(code, split)) { continue; } /* * Try all possible authority names, begining with the most specific ones. * For example if the code is "urn:ogc:def:crs:EPSG:6.8:4326", then we will * try "urn:ogc:def:crs:EPSG:6.8" first, "urn:ogc:def:crs:EPSG" next, etc. * until a suitable factory is found (searching into user-supplied factories * first). */ authority = code.substring(0, split).trim(); if (factories != null) { for (final AuthorityFactory factory : factories) { if (type.isAssignableFrom(factory.getClass())) { if (Citations.identifierMatches(factory.getAuthority(), authority)) { return type.cast(factory); } } } } /* * No suitable user-supplied factory. Now query FactoryFinder. */ final AuthorityFactory factory; try { factory = fromFactoryRegistry(authority, type); } catch (FactoryRegistryException exception) { cause = exception; continue; } if (factory != null) { return type.cast(factory); } } /* * No factory found. Creates an error message from the most global authority name * (for example "urn" if the code was "urn:ogc:def:crs:EPSG:6.8:4326") and the * corresponding cause. Both the authority and cause may be null if the code didn't * had any authority part. */ throw noSuchAuthority(code, authority, cause); } /** * Formats the exception to be throw when the user asked for a code from an unknown authority. * * @param code The code with an unknown authority. * @param authority The authority, or {@code null} if none. * @param cause The cause for the exception to be formatted, or {@code null} if none. * @return The formatted exception to be throw. */ private NoSuchAuthorityCodeException noSuchAuthority(final String code, String authority, final FactoryRegistryException cause) { final String message; if (authority == null) { authority = Vocabulary.format(VocabularyKeys.UNKNOWN); message = Errors.format(ErrorKeys.MISSING_AUTHORITY_$1, code); } else { message = Errors.format(ErrorKeys.UNKNOW_AUTHORITY_$1, authority); } final NoSuchAuthorityCodeException exception; exception = new NoSuchAuthorityCodeException(message, authority, code); exception.initCause(cause); return exception; } /** * Returns a generic object authority factory for the specified {@code "AUTHORITY:NUMBER"} * code. * * @param code The code to parse. * @return The authority factory. * @throws NoSuchAuthorityCodeException if no authority name has been found. */ @Override protected AuthorityFactory getAuthorityFactory(final String code) throws NoSuchAuthorityCodeException { return getAuthorityFactory(AuthorityFactory.class, code); } /** * Returns the datum authority factory for the specified {@code "AUTHORITY:NUMBER"} code. * * @param code The code to parse. * @return The authority factory. * @throws NoSuchAuthorityCodeException if no authority name has been found. */ @Override protected DatumAuthorityFactory getDatumAuthorityFactory(final String code) throws NoSuchAuthorityCodeException { return getAuthorityFactory(DatumAuthorityFactory.class, code); } /** * Returns the CS authority factory for the specified {@code "AUTHORITY:NUMBER"} code. * * @param code The code to parse. * @return The authority factory. * @throws NoSuchAuthorityCodeException if no authority name has been found. */ @Override protected CSAuthorityFactory getCSAuthorityFactory(final String code) throws NoSuchAuthorityCodeException { return getAuthorityFactory(CSAuthorityFactory.class, code); } /** * Returns the CRS authority factory for the specified {@code "AUTHORITY:NUMBER"} code. * * @param code The code to parse. * @return The authority factory. * @throws NoSuchAuthorityCodeException if no authority name has been found. */ @Override protected CRSAuthorityFactory getCRSAuthorityFactory(final String code) throws NoSuchAuthorityCodeException { return getAuthorityFactory(CRSAuthorityFactory.class, code); } /** * Returns the operation authority factory for the specified {@code "AUTHORITY:NUMBER"} code. * * @param code The code to parse. * @return The authority factory. * @throws NoSuchAuthorityCodeException if no authority name has been found. */ @Override protected CoordinateOperationAuthorityFactory getCoordinateOperationAuthorityFactory(final String code) throws NoSuchAuthorityCodeException { return getAuthorityFactory(CoordinateOperationAuthorityFactory.class, code); } /** * Returns the set of authority codes of the given type. * * @param type The spatial reference objects type (may be {@code IdentifiedObject.class}). * @return The set of authority codes for spatial reference objects of the given type. * If this factory doesn't contains any object of the given type, then this method * returns an {@linkplain java.util.Collections#EMPTY_SET empty set}. * @throws FactoryException if access to the underlying database failed. */ @Override public Set<String> getAuthorityCodes(final Class<? extends IdentifiedObject> type) throws FactoryException { if (Boolean.TRUE.equals(inProgress.get())) { /* * 'getAuthorityCodes' is invoking itself (indirectly). Returns an empty set in order * to avoid infinite recursivity. Note that the end result (the output of the caller) * will usually not be empty. */ return Collections.emptySet(); } final Set<String> codes = new LinkedHashSet<String>(); final Set<AuthorityFactory> done = new HashSet<AuthorityFactory>(); done.add(this); // Safety for avoiding recursive calls. try { inProgress.set(Boolean.TRUE); for (String authority : getAuthorityNames()) { authority = authority.trim(); final char separator = getSeparator(authority); /* * Prepares a buffer with the "AUTHORITY:" part in "AUTHORITY:NUMBER". * We will reuse this buffer in order to prefix the authority name in * front of every codes. */ final StringBuilder code = new StringBuilder(authority); int codeBase = code.length(); if (codeBase != 0 && code.charAt(codeBase - 1) != separator) { code.append(separator); codeBase = code.length(); } code.append("all"); final String dummyCode = code.toString(); /* * Now scan over all factories. We will process a factory only if this particular * factory has not already been done in a previous iteration (some implementation * apply to more than one factory). */ scanForType: for (int i=0; i<FACTORY_TYPES.length; i++) { if (!OBJECT_TYPES[i].isAssignableFrom(type)) { continue; } final Class<? extends AuthorityFactory> factoryType = FACTORY_TYPES[i]; final AuthorityFactory factory; try { factory = getAuthorityFactory(factoryType, dummyCode); } catch (NoSuchAuthorityCodeException e) { continue; } if (!done.add(factory)) { continue; } AuthorityFactory wrapped = factory; while (wrapped instanceof AuthorityFactoryAdapter) { final AuthorityFactoryAdapter adapter = (AuthorityFactoryAdapter) wrapped; try { wrapped = adapter.getAuthorityFactory(factoryType, dummyCode); } catch (NoSuchAuthorityCodeException exception) { /* * The factory doesn't understand our dummy code. It happen with * URN_AuthorityFactory, which expect the type ("CRS", etc.) in the URN. */ continue scanForType; } if (!done.add(wrapped)) { /* * Avoid the factories that are wrapper around an other factory already * done. If we don't do that, we will duplicate the whole set of EPSG * identifiers (more than 3000 codes) for OrderedAuthorityFactory, * HTTP_AuthorityFactory, URN_AuthorityFactory, etc. */ continue scanForType; } } for (String candidate : factory.getAuthorityCodes(type)) { candidate = candidate.trim(); if (candidate.length() < codeBase || Character.isLetterOrDigit (candidate.charAt(codeBase-1)) || !authority.equalsIgnoreCase(candidate.substring(0, codeBase-1))) { // Prepend the authority code if it was not already presents. code.setLength(codeBase); code.append(candidate); candidate = code.toString(); } codes.add(candidate); } } } } finally { inProgress.remove(); } return codes; } /** * Gets a description of the object corresponding to a code. * * @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 FactoryException { final Set<AuthorityFactory> done = new HashSet<AuthorityFactory>(); done.add(this); // Safety for avoiding recursive calls. FactoryException failure = null; for (int type=0; type<FACTORY_TYPES.length; type++) { /* * Try all factories, starting with the CRS factory because it is the only one most * users care about. If the CRS factory doesn't know about the specified object, then * we will try the other factories (datum, CS, ...) before to rethrow the exception. */ final AuthorityFactory factory; try { factory = getAuthorityFactory(FACTORY_TYPES[type], code); } catch (NoSuchAuthorityCodeException exception) { if (failure == null) { failure = exception; } continue; } if (done.add(factory)) try { return factory.getDescriptionText(code); } catch (FactoryException exception) { /* * Failed to creates an object using the current factory. We will retain only the * first exception and discart all other ones, except if the first exceptions were * due to unknown authority (we will prefer exception due to unknown code instead). * The first exception is usually thrown by the CRS factory, which is the only * factory most users care about. */ if (failure==null || failure.getCause() instanceof FactoryRegistryException) { failure = exception; } } } if (failure == null) { failure = noSuchAuthorityCode(IdentifiedObject.class, code); } throw failure; } /** * Returns an arbitrary object from a code. * * @see #createCoordinateReferenceSystem * @see #createDatum * @see #createEllipsoid * @see #createUnit */ @Override public IdentifiedObject createObject(final String code) throws FactoryException { final Set<AuthorityFactory> done = new HashSet<AuthorityFactory>(); done.add(this); // Safety for avoiding recursive calls. FactoryException failure = null; for (int type=0; type<FACTORY_TYPES.length; type++) { /* * Try all factories, starting with the CRS factory because it is the only one most * users care about. If the CRS factory doesn't know about the specified object, then * we will try the other factories (datum, CS, ...) before to rethrow the exception. */ final AuthorityFactory factory; try { factory = getAuthorityFactory(FACTORY_TYPES[type], code); } catch (NoSuchAuthorityCodeException exception) { if (failure == null) { failure = exception; } continue; } if (done.add(factory)) try { return factory.createObject(code); } catch (FactoryException exception) { /* * Failed to creates an object using the current factory. We will retain only the * first exception and discart all other ones, except if the first exceptions were * due to unknown authority (we will prefer exception due to unknown code instead). * The first exception is usually thrown by the CRS factory, which is the only * factory most users care about. */ if (failure==null || failure.getCause() instanceof FactoryRegistryException) { failure = exception; } } } if (failure == null) { failure = noSuchAuthorityCode(IdentifiedObject.class, code); } throw failure; } /** * Returns a finder which can be used for looking up unidentified objects. * The default implementation delegates the lookups to the underlying factories. */ @Override public IdentifiedObjectFinder getIdentifiedObjectFinder(Class<? extends IdentifiedObject> type) throws FactoryException { return new Finder(this, type); } /** * A {@link IdentifiedObjectFinder} which tests every factories. */ static class Finder extends IdentifiedObjectFinder { /** * Creates a finder for the specified type. */ protected Finder(final ManyAuthoritiesFactory factory, final Class<? extends IdentifiedObject> type) { super(factory, type); } /** * Returns the user-supplied factories. */ final Collection<AuthorityFactory> getFactories() { return ((ManyAuthoritiesFactory) getProxy().getAuthorityFactory()).getFactories(); } /** * Returns the next finder in the specified set of factories, or {@code null} if none. */ final IdentifiedObjectFinder next(final Iterator<AuthorityFactory> it) throws FactoryException { while (it.hasNext()) { final AuthorityFactory factory = it.next(); if (exclude(factory)) { continue; } if (factory instanceof AbstractAuthorityFactory) { final IdentifiedObjectFinder finder = ((AbstractAuthorityFactory) factory). getIdentifiedObjectFinder(getProxy().getType()); if (finder != null) { finder.setFullScanAllowed(isFullScanAllowed()); return finder; } } } return null; } /** * Lookups for the specified object. */ @Override public IdentifiedObject find(final IdentifiedObject object) throws FactoryException { /* * Try to create from the identifier before to scan over every factories, * because the identifier may contains the authority name, in which case * we can pickup directly the right factory instead of trying them all. */ IdentifiedObject candidate = createFromIdentifiers(object); if (candidate != null) { return candidate; } final Collection<AuthorityFactory> factories = getFactories(); if (factories != null) { IdentifiedObjectFinder finder; final Iterator<AuthorityFactory> it = factories.iterator(); while ((finder = next(it)) != null) { candidate = finder.find(object); if (candidate != null) { break; } } } return candidate; } /** * Returns the identifier of the specified object, or {@code null} if none. */ @Override public String findIdentifier(final IdentifiedObject object) throws FactoryException { /* * Try to create from the identifier for the same reason than find(IdentifiedObject). * Note that we returns directly the primary name; we don't try to locate a name for * a given authority since the "All" authority do not really exists. */ IdentifiedObject candidate = createFromIdentifiers(object); if (candidate != null) { return candidate.getName().toString(); } final Collection<AuthorityFactory> factories = getFactories(); if (factories != null) { IdentifiedObjectFinder finder; final Iterator<AuthorityFactory> it = factories.iterator(); while ((finder = next(it)) != null) { final String id = finder.findIdentifier(object); if (id != null) { return id; } } } return null; } } }