/*
* 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.Set;
import java.util.Map;
import java.util.List;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import javax.measure.unit.Unit;
import org.opengis.metadata.extent.Extent;
import org.opengis.metadata.citation.Citation;
import org.opengis.referencing.NoSuchAuthorityCodeException;
import org.opengis.util.InternationalString;
import org.opengis.parameter.ParameterDescriptor;
import org.opengis.referencing.IdentifiedObject;
import org.opengis.referencing.AuthorityFactory;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.cs.*;
import org.opengis.referencing.crs.*;
import org.opengis.referencing.datum.*;
import org.opengis.referencing.operation.*;
import org.geotools.factory.Hints;
import org.geotools.factory.Factory;
import org.geotools.factory.AbstractFactory;
import org.geotools.factory.OptionalFactory;
import org.geotools.factory.FactoryRegistryException;
import org.geotools.metadata.iso.citation.Citations;
import org.geotools.referencing.CRS;
import org.geotools.referencing.ReferencingFactoryFinder;
import org.geotools.resources.i18n.Loggings;
import org.geotools.resources.i18n.LoggingKeys;
import org.geotools.resources.i18n.ErrorKeys;
import org.geotools.resources.i18n.Errors;
/**
* An authority factory which delegates {@linkplain CoordinateReferenceSystem CRS},
* {@linkplain CoordinateSystem CS} or {@linkplain Datum datum} objects creation to
* some other factory implementations.
*
* All constructors are protected because this class must be subclassed in order to determine
* which of the {@link DatumAuthorityFactory}, {@link CSAuthorityFactory} and
* {@link CRSAuthorityFactory} interfaces to implement.
*
* @since 2.2
*
* @source $URL$
* @version $Id$
* @author Martin Desruisseaux (IRD)
*/
public class AuthorityFactoryAdapter extends AbstractAuthorityFactory implements OptionalFactory {
/**
* List of hint keys related to authority factories.
*/
private static final Hints.Key[] TYPES = new Hints.Key[] {
Hints.CRS_AUTHORITY_FACTORY,
Hints.CS_AUTHORITY_FACTORY,
Hints.DATUM_AUTHORITY_FACTORY,
Hints.COORDINATE_OPERATION_AUTHORITY_FACTORY
};
/**
* The underlying {@linkplain Datum datum} authority factory,
* or {@code null} if none.
*/
final DatumAuthorityFactory datumFactory;
/**
* The underlying {@linkplain CoordinateSystem coordinate system} authority factory,
* or {@code null} if none.
*/
final CSAuthorityFactory csFactory;
/**
* The underlying {@linkplain CoordinateReferenceSystem coordinate reference system}
* authority factory, or {@code null} if none.
*/
final CRSAuthorityFactory crsFactory;
/**
* The underlying {@linkplain CoordinateOperation coordinate operation} authority factory,
* or {@code null} if none.
*/
final CoordinateOperationAuthorityFactory operationFactory;
/**
* A set of low-level factories to be used if none were found in {@link #datumFactory},
* {@link #csFactory}, {@link #crsFactory} or {@link #operationFactory}. Will be created
* only when first needed.
*
* @see #getFactoryContainer
*/
private transient ReferencingFactoryContainer factories;
/**
* Creates a wrapper around no factory. This constructor should never be used except by
* subclasses overriding the getFooAuthorityFactory
* methods.
*
* @param priority The priority for this factory, as a number between
* {@link #MINIMUM_PRIORITY MINIMUM_PRIORITY} and
* {@link #MAXIMUM_PRIORITY MAXIMUM_PRIORITY} inclusive.
*/
AuthorityFactoryAdapter(final int priority) {
super(priority);
datumFactory = null;
csFactory = null;
crsFactory = null;
operationFactory = null;
}
/**
* Creates a wrapper around the specified factory. The {@link #priority priority} field
* will be set to the same value than the specified factory. Subclasses should override
* the {@link #getPriority() getPriority()} method if they want to set a higher or lower
* priority for this instance.
*
* @param factory The factory to wrap.
*/
protected AuthorityFactoryAdapter(final AuthorityFactory factory) {
this(factory, null);
}
/**
* For {@link FallbackAuthorityFactory} constructor only.
*/
AuthorityFactoryAdapter(final AuthorityFactory factory, final AuthorityFactory fallback) {
this((factory instanceof CRSAuthorityFactory) ? (CRSAuthorityFactory) factory :
(fallback instanceof CRSAuthorityFactory) ? (CRSAuthorityFactory) fallback : null,
(factory instanceof CSAuthorityFactory) ? (CSAuthorityFactory) factory :
(fallback instanceof CSAuthorityFactory) ? (CSAuthorityFactory) fallback : null,
(factory instanceof DatumAuthorityFactory) ? (DatumAuthorityFactory) factory :
(fallback instanceof DatumAuthorityFactory) ? (DatumAuthorityFactory) fallback : null,
(factory instanceof CoordinateOperationAuthorityFactory) ?
(CoordinateOperationAuthorityFactory) factory :
(fallback instanceof CoordinateOperationAuthorityFactory) ?
(CoordinateOperationAuthorityFactory) fallback : null);
}
/**
* Creates a wrapper around the specified factories. The {@link #priority priority} field will
* be set to the highest priority found in the specified factories. Subclasses should override
* the {@link #getPriority() getPriority()} method if they want to set a higher or lower
* priority for this instance.
*
* @param crsFactory The {@linkplain CoordinateReferenceSystem coordinate reference system}
* authority factory, or {@code null}.
* @param csFactory The {@linkplain CoordinateSystem coordinate system} authority factory,
* or {@code null}.
* @param datumFactory The {@linkplain Datum datum} authority factory, or {@code null}.
* @param opFactory The {@linkplain CoordinateOperation coordinate operation} authority
* factory, or {@code null}.
*/
protected AuthorityFactoryAdapter(final CRSAuthorityFactory crsFactory,
final CSAuthorityFactory csFactory,
final DatumAuthorityFactory datumFactory,
final CoordinateOperationAuthorityFactory opFactory)
{
super(Math.max(getPriority(datumFactory),
Math.max(getPriority( csFactory),
Math.max(getPriority( crsFactory),
getPriority( opFactory)))));
if (this instanceof CRSAuthorityFactory) {
ensureNonNull("crsFactory", crsFactory);
}
if (this instanceof CSAuthorityFactory) {
ensureNonNull("csFactory", csFactory);
}
if (this instanceof DatumAuthorityFactory) {
ensureNonNull("datumFactory", datumFactory);
}
if (this instanceof CoordinateOperationAuthorityFactory) {
ensureNonNull("opFactory", opFactory);
}
store(Hints. DATUM_AUTHORITY_FACTORY, this. datumFactory = datumFactory);
store(Hints. CS_AUTHORITY_FACTORY, this. csFactory = csFactory);
store(Hints. CRS_AUTHORITY_FACTORY, this. crsFactory = crsFactory);
store(Hints.COORDINATE_OPERATION_AUTHORITY_FACTORY, this.operationFactory = opFactory);
}
/**
* Returns the priority of the specified factory, or {@link #NORMAL_PRIORITY} if unknown.
*/
private static int getPriority(final AuthorityFactory factory) {
return (factory instanceof AbstractFactory) ?
((AbstractFactory) factory).getPriority() : NORMAL_PRIORITY;
}
/**
* Adds the specified factory to the set of hints, if non null.
*/
private void store(final Hints.Key key, final AuthorityFactory factory) {
if (factory != null) {
if (hints.put(key, factory) != null) {
// Should never happen since 'hints' should be initially empty.
throw new AssertionError(key);
}
}
}
/**
* Creates a wrappers around the default factories for the specified authority.
* The factories are fetched using {@link ReferencingFactoryFinder}.
*
* @param authority The authority to wraps (example: {@code "EPSG"}). If {@code null},
* then all authority factories must be explicitly specified in the
* set of hints.
* @param userHints An optional set of hints, or {@code null} if none.
* @throws FactoryRegistryException if at least one factory can not be obtained.
*
* @since 2.4
*/
protected AuthorityFactoryAdapter(final String authority, final Hints userHints)
throws FactoryRegistryException
{
this(ReferencingFactoryFinder.getCRSAuthorityFactory(authority,
trim(userHints, Hints.CRS_AUTHORITY_FACTORY)),
ReferencingFactoryFinder.getCSAuthorityFactory(authority,
trim(userHints, Hints.CS_AUTHORITY_FACTORY)),
ReferencingFactoryFinder.getDatumAuthorityFactory(authority,
trim(userHints, Hints.DATUM_AUTHORITY_FACTORY)),
ReferencingFactoryFinder.getCoordinateOperationAuthorityFactory(authority,
trim(userHints, Hints.COORDINATE_OPERATION_AUTHORITY_FACTORY)));
}
/**
* Removes every {@code *_AUTHORITY_FACTORY} hints except the specified one. The removal,
* if needed, is performed in a copy of the supplied hints in order to keep user's map
* unmodified.
*
* This removal is performed because {@code *_AUTHORITY_FACTORY} hints are typically supplied
* to the above constructor in order to initialize the {@link #crsFactory}, {@link #csFactory},
* etc. fields. But because the same map of hints is used for every call to {@code
* ReferencingFactoryFinder.getFooAuthorityFactory(...)}, if we don't perform this removal, then
* the {@code CRS_AUTHORITY_FACTORY} hint is taken in account for fetching other factories like
* {@link CSAuthorityFactory}. We may think that it is not a problem since CS authority factory
* should not care about {@code CRS_AUTHORITY_FACTORY} hint. But... our EPSG authority factory
* implements both {@link CRSAuthorityFactory} and {@link CSAuthorityFactory} interfaces, so
* our {@link CSAuthorityFactory} implementation do have CRS-related hints.
*
* Conclusion: if we do not remove those hints, it typically leads to failure to find
* a CS authority factory using this specific CRS authority factory. We may argue that
* this is a Geotools design problem. Maybe... this is not a trivial issue. So we are
* better to not document that in public API for now.
*
* @param userHints The user hints to trim. This map will never be modified.
* @param keep The hint to not remove.
* @return A copy of {@code userHints} without the authority hints, or {@code userHints}
* if no change were required.
*/
private static Hints trim(final Hints userHints, final Hints.Key keep) {
Hints reduced = userHints;
if (userHints != null) {
for (int i=0; i dependencies() {
final List