/*
* 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.
* <p>
* 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 <code>get</code><var>Foo</var><code>AuthorityFactory</code>
* 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.
* <p>
* 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},
* <cite>etc.</cite> 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.
* <p>
* 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 <strong>not</strong> 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<TYPES.length; i++) {
final Hints.Key key = TYPES[i];
if (!keep.equals(key)) {
if (reduced == userHints) {
if (!userHints.containsKey(key)) {
continue;
}
// Copies the map only if we need to modify it.
reduced = new Hints(userHints);
}
reduced.remove(key);
}
}
}
return reduced;
}
/**
* Returns the {@linkplain #hints hints} extented will all hints specified in dependencies.
*/
private Hints hints() {
final Hints extended = new Hints(hints);
addAll(operationFactory, extended);
addAll( datumFactory, extended);
addAll( csFactory, extended);
addAll( crsFactory, extended);
extended.putAll(hints); // Gives precedence to the hints from this class.
return extended;
}
/**
* Adds all hints from the specified factory into the specified set of hints.
*/
private static void addAll(final AuthorityFactory factory, final Hints hints) {
if (factory instanceof Factory) {
hints.putAll(((Factory) factory).getImplementationHints());
}
}
/**
* Returns the direct dependencies. The returned list contains the backing store specified
* at construction time, or the exception if the backing store can't be obtained.
*/
@Override
Collection<? super AuthorityFactory> dependencies() {
final List<Object> dep = new ArrayList<Object>(4);
Object factory;
try {
factory = getAuthorityFactory(null);
} catch (FactoryException e) {
factory = e;
}
dep.add(factory);
return dep;
}
/**
* 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 and should not be public. We
* expect only a simple check, so we don't invoke the {@code getFooAuthorityFactory(...)}
* methods.
*/
@Override
boolean sameAuthorityCodes(final AuthorityFactory factory) {
if (!isCodeMethodOverriden()) {
/*
* Tests wrapped factories only if the 'toBackingFactoryCode(String)' method is not
* overwritten, otherwise we can't assume that the authority codes are the same. The
* impact on the main subclasses are usually as below:
*
* URN_AuthorityFactory - excluded
* HTTP_AuthorityFactory - excluded
* HTTP_URI_AuthorityFactory - excluded
* OrderedAxisAuthorityFactory - make the test below
* FallbackAuthorityFactory - make the test below
*
* Note: in the particular case of FallbackAuthorityFactory, we test the
* primary factory only, not the fallback. This behavior matches the
* FallbackAuthorityFactory.create(boolean,int,Iterator) need, which
* will process this case in a special way.
*/
if (sameAuthorityCodes(crsFactory, factory) &&
sameAuthorityCodes(csFactory, factory) &&
sameAuthorityCodes(datumFactory, factory) &&
sameAuthorityCodes(operationFactory, factory))
{
return true;
}
}
return super.sameAuthorityCodes(factory);
}
/**
* Helper methods for {@link #sameAuthorityCodes(AuthorityFactory)} and
* {@link FallbackAuthorityFactory#create(boolean,int,Iterator)} implementations. If there is no
* backing store, returns {@code true} in order to take in account only the backing stores that
* are assigned. This behavior match the need of the above-cited implementations.
*/
static boolean sameAuthorityCodes(final AuthorityFactory backingStore,
final AuthorityFactory factory)
{
if (backingStore instanceof AbstractAuthorityFactory) {
if (((AbstractAuthorityFactory) backingStore).sameAuthorityCodes(factory)) {
return true;
}
}
return (factory == backingStore) || (backingStore == null);
}
/**
* Returns {@code true} if this factory is ready for use. This default implementation
* checks the availability of CRS, CS, datum and operation authority factories specified
* at construction time.
*
* @return {@code true} if this factory is ready for use.
*/
@Override
public boolean isAvailable() {
return isAvailable( crsFactory) &&
isAvailable( csFactory) &&
isAvailable( datumFactory) &&
isAvailable(operationFactory);
}
/**
* Checks the availability of the specified factory.
*/
private static boolean isAvailable(final AuthorityFactory factory) {
return !(factory instanceof OptionalFactory) || ((OptionalFactory) factory).isAvailable();
}
/**
* Replaces the specified unit, if applicable.
* To be overridden with {@code protected} access by {@link TransformedAuthorityFactory}.
*/
Unit<?> replace(Unit<?> units) throws FactoryException {
return units;
}
/**
* Replaces (if needed) the specified axis by a new one.
* To be overridden with {@code protected} access by {@link TransformedAuthorityFactory}.
*/
CoordinateSystemAxis replace(CoordinateSystemAxis axis) throws FactoryException {
return axis;
}
/**
* Replaces (if needed) the specified coordinate system by a new one.
* To be overridden with {@code protected} access by {@link TransformedAuthorityFactory}.
*/
CoordinateSystem replace(CoordinateSystem cs) throws FactoryException {
return cs;
}
/**
* Replaces (if needed) the specified datum by a new one.
* To be overridden with {@code protected} access by {@link TransformedAuthorityFactory}.
*/
Datum replace(Datum datum) throws FactoryException {
return datum;
}
/**
* Replaces (if needed) the specified coordinate reference system.
* To be overridden with {@code protected} access by {@link TransformedAuthorityFactory}.
*/
CoordinateReferenceSystem replace(CoordinateReferenceSystem crs) throws FactoryException {
return crs;
}
/**
* Replaces (if needed) the specified coordinate operation.
* To be overridden with {@code protected} access by {@link TransformedAuthorityFactory}.
*/
CoordinateOperation replace(CoordinateOperation operation) throws FactoryException {
return operation;
}
/**
* Delegates the work to an appropriate {@code replace} method for the given object.
*/
private IdentifiedObject replaceObject(final IdentifiedObject object) throws FactoryException {
if (object instanceof CoordinateReferenceSystem) {
return replace((CoordinateReferenceSystem) object);
}
if (object instanceof CoordinateSystem) {
return replace((CoordinateSystem) object);
}
if (object instanceof CoordinateSystemAxis) {
return replace((CoordinateSystemAxis) object);
}
if (object instanceof Datum) {
return replace((Datum) object);
}
if (object instanceof CoordinateOperation) {
return replace((CoordinateOperation) object);
}
return object;
}
/**
* Returns one of the underlying factories as an instance of the GeoTools implementation. If
* there is none of them, then returns {@code null} or throws an exception if {@code caller}
* is not null.
*/
private AbstractAuthorityFactory getGeotoolsFactory(final String caller, final String code)
throws FactoryException
{
final AuthorityFactory candidate = getAuthorityFactory(code);
if (candidate instanceof AbstractAuthorityFactory) {
return (AbstractAuthorityFactory) candidate;
}
if (caller == null) {
return null;
}
throw new FactoryException(Errors.format(
ErrorKeys.GEOTOOLS_EXTENSION_REQUIRED_$1, caller));
}
/**
* 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 {
final AbstractAuthorityFactory factory = getGeotoolsFactory(null, null);
return (factory != null) ? factory.getBackingStoreDescription() : null;
}
/**
* Returns the vendor responsible for creating this factory implementation.
*/
@Override
public Citation getVendor() {
return getAuthorityFactory().getVendor();
}
/**
* Returns the organization or party responsible for definition and maintenance of the
* database.
*/
public Citation getAuthority() {
return getAuthorityFactory().getAuthority();
}
/**
* Returns the set of authority code for the specified type.
*
* @todo We should returns the union of authority codes from all underlying factories.
*/
public Set<String> getAuthorityCodes(final Class<? extends IdentifiedObject> type)
throws FactoryException
{
return getAuthorityFactory(null).getAuthorityCodes(type);
}
/**
* Returns a description for the object identified by the specified code.
*/
public InternationalString getDescriptionText(final String code) throws FactoryException {
return getAuthorityFactory(code).getDescriptionText(toBackingFactoryCode(code));
}
/**
* Returns an arbitrary object from a code.
*
* @throws FactoryException if the object creation failed.
*
* @see #createCoordinateReferenceSystem
* @see #createDatum
* @see #createEllipsoid
* @see #createUnit
*/
@Override
public IdentifiedObject createObject(final String code) throws FactoryException {
return replaceObject(getAuthorityFactory(code).createObject(toBackingFactoryCode(code)));
}
/**
* Returns an arbitrary {@linkplain Datum datum} from a code.
*
* @throws FactoryException if the object creation failed.
*
* @see #createGeodeticDatum
* @see #createVerticalDatum
* @see #createTemporalDatum
*/
@Override
public Datum createDatum(final String code) throws FactoryException {
return replace(getDatumAuthorityFactory(code).createDatum(toBackingFactoryCode(code)));
}
/**
* Creates a {@linkplain EngineeringDatum engineering datum} from a code.
*
* @throws FactoryException if the object creation failed.
*
* @see #createEngineeringCRS
*/
@Override
public EngineeringDatum createEngineeringDatum(final String code) throws FactoryException {
return (EngineeringDatum) replace(getDatumAuthorityFactory(code).
createEngineeringDatum(toBackingFactoryCode(code)));
}
/**
* Creates a {@linkplain ImageDatum image datum} from a code.
*
* @throws FactoryException if the object creation failed.
*
* @see #createImageCRS
*/
@Override
public ImageDatum createImageDatum(final String code) throws FactoryException {
return (ImageDatum) replace(getDatumAuthorityFactory(code).
createImageDatum(toBackingFactoryCode(code)));
}
/**
* Creates a {@linkplain VerticalDatum vertical datum} from a code.
*
* @throws FactoryException if the object creation failed.
*
* @see #createVerticalCRS
*/
@Override
public VerticalDatum createVerticalDatum(final String code) throws FactoryException {
return (VerticalDatum) replace(getDatumAuthorityFactory(code).
createVerticalDatum(toBackingFactoryCode(code)));
}
/**
* Creates a {@linkplain TemporalDatum temporal datum} from a code.
*
* @throws FactoryException if the object creation failed.
*
* @see #createTemporalCRS
*/
@Override
public TemporalDatum createTemporalDatum(final String code) throws FactoryException {
return (TemporalDatum) replace(getDatumAuthorityFactory(code).
createTemporalDatum(toBackingFactoryCode(code)));
}
/**
* Returns a {@linkplain GeodeticDatum geodetic datum} from a code.
*
* @throws FactoryException if the object creation failed.
*
* @see #createEllipsoid
* @see #createPrimeMeridian
* @see #createGeographicCRS
* @see #createProjectedCRS
*/
@Override
public GeodeticDatum createGeodeticDatum(final String code) throws FactoryException {
return (GeodeticDatum) replace(getDatumAuthorityFactory(code).
createGeodeticDatum(toBackingFactoryCode(code)));
}
/**
* Returns an {@linkplain Ellipsoid ellipsoid} from a code.
*
* @throws FactoryException if the object creation failed.
*
* @see #createGeodeticDatum
*/
@Override
public Ellipsoid createEllipsoid(final String code) throws FactoryException {
return getDatumAuthorityFactory(code).createEllipsoid(toBackingFactoryCode(code));
}
/**
* Returns a {@linkplain PrimeMeridian prime meridian} from a code.
*
* @throws FactoryException if the object creation failed.
*
* @see #createGeodeticDatum
*/
@Override
public PrimeMeridian createPrimeMeridian(final String code) throws FactoryException {
return getDatumAuthorityFactory(code).createPrimeMeridian(toBackingFactoryCode(code));
}
/**
* Returns a {@linkplain Extent extent} (usually an area of validity) from a code.
*
* @throws FactoryException if the object creation failed.
*/
@Override
public Extent createExtent(final String code) throws FactoryException {
return getGeotoolsFactory("createExtent", code).createExtent(toBackingFactoryCode(code));
}
/**
* Returns an arbitrary {@linkplain CoordinateSystem coordinate system} from a code.
*
* @throws FactoryException if the object creation failed.
*/
@Override
public CoordinateSystem createCoordinateSystem(final String code) throws FactoryException {
return replace(getCSAuthorityFactory(code).
createCoordinateSystem(toBackingFactoryCode(code)));
}
/**
* Creates a cartesian coordinate system from a code.
*
* @throws FactoryException if the object creation failed.
*/
@Override
public CartesianCS createCartesianCS(final String code) throws FactoryException {
return (CartesianCS) replace(getCSAuthorityFactory(code).
createCartesianCS(toBackingFactoryCode(code)));
}
/**
* Creates a polar coordinate system from a code.
*
* @throws FactoryException if the object creation failed.
*/
@Override
public PolarCS createPolarCS(final String code) throws FactoryException {
return (PolarCS) replace(getCSAuthorityFactory(code).
createPolarCS(toBackingFactoryCode(code)));
}
/**
* Creates a cylindrical coordinate system from a code.
*
* @throws FactoryException if the object creation failed.
*/
@Override
public CylindricalCS createCylindricalCS(final String code) throws FactoryException {
return (CylindricalCS) replace(getCSAuthorityFactory(code).
createCylindricalCS(toBackingFactoryCode(code)));
}
/**
* Creates a spherical coordinate system from a code.
*
* @throws FactoryException if the object creation failed.
*/
@Override
public SphericalCS createSphericalCS(final String code) throws FactoryException {
return (SphericalCS) replace(getCSAuthorityFactory(code).
createSphericalCS(toBackingFactoryCode(code)));
}
/**
* Creates an ellipsoidal coordinate system from a code.
*
* @throws FactoryException if the object creation failed.
*/
@Override
public EllipsoidalCS createEllipsoidalCS(final String code) throws FactoryException {
return (EllipsoidalCS) replace(getCSAuthorityFactory(code).
createEllipsoidalCS(toBackingFactoryCode(code)));
}
/**
* Creates a vertical coordinate system from a code.
*
* @throws FactoryException if the object creation failed.
*/
@Override
public VerticalCS createVerticalCS(final String code) throws FactoryException {
return (VerticalCS) replace(getCSAuthorityFactory(code).
createVerticalCS(toBackingFactoryCode(code)));
}
/**
* Creates a temporal coordinate system from a code.
*
* @throws FactoryException if the object creation failed.
*/
@Override
public TimeCS createTimeCS(final String code) throws FactoryException {
return (TimeCS) replace(getCSAuthorityFactory(code).
createTimeCS(toBackingFactoryCode(code)));
}
/**
* Returns a {@linkplain CoordinateSystemAxis coordinate system axis} from a code.
*
* @throws FactoryException if the object creation failed.
*/
@Override
public CoordinateSystemAxis createCoordinateSystemAxis(final String code)
throws FactoryException
{
return replace(getCSAuthorityFactory(code).
createCoordinateSystemAxis(toBackingFactoryCode(code)));
}
/**
* Returns an {@linkplain Unit unit} from a code.
*
* @throws FactoryException if the object creation failed.
*/
@Override
public Unit<?> createUnit(final String code) throws FactoryException {
return replace(getCSAuthorityFactory(code).
createUnit(toBackingFactoryCode(code)));
}
/**
* Returns an arbitrary {@linkplain CoordinateReferenceSystem coordinate reference system}
* from a code.
*
* @throws FactoryException if the object creation failed.
*
* @see #createGeographicCRS
* @see #createProjectedCRS
* @see #createVerticalCRS
* @see #createTemporalCRS
* @see #createCompoundCRS
*/
@Override
public CoordinateReferenceSystem createCoordinateReferenceSystem(final String code)
throws FactoryException
{
return replace(getCRSAuthorityFactory(code).
createCoordinateReferenceSystem(toBackingFactoryCode(code)));
}
/**
* Creates a 3D coordinate reference system from a code.
*
* @throws FactoryException if the object creation failed.
*/
@Override
public CompoundCRS createCompoundCRS(final String code) throws FactoryException {
return (CompoundCRS) replace(getCRSAuthorityFactory(code).
createCompoundCRS(toBackingFactoryCode(code)));
}
/**
* Creates a derived coordinate reference system from a code.
*
* @throws FactoryException if the object creation failed.
*/
@Override
public DerivedCRS createDerivedCRS(final String code) throws FactoryException {
return (DerivedCRS) replace(getCRSAuthorityFactory(code).
createDerivedCRS(toBackingFactoryCode(code)));
}
/**
* Creates a {@linkplain EngineeringCRS engineering coordinate reference system} from a code.
*
* @throws FactoryException if the object creation failed.
*/
@Override
public EngineeringCRS createEngineeringCRS(final String code) throws FactoryException {
return (EngineeringCRS) replace(getCRSAuthorityFactory(code).
createEngineeringCRS(toBackingFactoryCode(code)));
}
/**
* Returns a {@linkplain GeographicCRS geographic coordinate reference system} from a code.
*
* @throws FactoryException if the object creation failed.
*/
@Override
public GeographicCRS createGeographicCRS(final String code) throws FactoryException {
return (GeographicCRS) replace(getCRSAuthorityFactory(code).
createGeographicCRS(toBackingFactoryCode(code)));
}
/**
* Returns a {@linkplain GeocentricCRS geocentric coordinate reference system} from a code.
*
* @throws FactoryException if the object creation failed.
*/
@Override
public GeocentricCRS createGeocentricCRS(final String code) throws FactoryException {
return (GeocentricCRS) replace(getCRSAuthorityFactory(code).
createGeocentricCRS(toBackingFactoryCode(code)));
}
/**
* Creates a {@linkplain ImageCRS image coordinate reference system} from a code.
*
* @throws FactoryException if the object creation failed.
*/
@Override
public ImageCRS createImageCRS(final String code) throws FactoryException {
return (ImageCRS) replace(getCRSAuthorityFactory(code).
createImageCRS(toBackingFactoryCode(code)));
}
/**
* Returns a {@linkplain ProjectedCRS projected coordinate reference system} from a code.
*
* @throws FactoryException if the object creation failed.
*/
@Override
public ProjectedCRS createProjectedCRS(final String code) throws FactoryException {
return (ProjectedCRS) replace(getCRSAuthorityFactory(code).
createProjectedCRS(toBackingFactoryCode(code)));
}
/**
* Creates a {@linkplain TemporalCRS temporal coordinate reference system} from a code.
*
* @throws FactoryException if the object creation failed.
*/
@Override
public TemporalCRS createTemporalCRS(final String code) throws FactoryException {
return (TemporalCRS) replace(getCRSAuthorityFactory(code).
createTemporalCRS(toBackingFactoryCode(code)));
}
/**
* Creates a {@linkplain VerticalCRS vertical coordinate reference system} from a code.
*
* @throws FactoryException if the object creation failed.
*/
@Override
public VerticalCRS createVerticalCRS(final String code) throws FactoryException {
return (VerticalCRS) replace(getCRSAuthorityFactory(code).
createVerticalCRS(toBackingFactoryCode(code)));
}
/**
* Creates a parameter descriptor from a code.
*
* @throws FactoryException if the object creation failed.
*/
@Override
public ParameterDescriptor createParameterDescriptor(final String code) throws FactoryException {
return getGeotoolsFactory("createParameterDescriptor", code).
createParameterDescriptor(toBackingFactoryCode(code));
}
/**
* Creates an operation method from a code.
*
* @throws FactoryException if the object creation failed.
*/
@Override
public OperationMethod createOperationMethod(final String code) throws FactoryException {
return getGeotoolsFactory("createOperationMethod", code).
createOperationMethod(toBackingFactoryCode(code));
}
/**
* Creates an operation from a single operation code.
*
* @throws FactoryException if the object creation failed.
*/
@Override
public CoordinateOperation createCoordinateOperation(final String code) throws FactoryException {
return replace(getCoordinateOperationAuthorityFactory(code).
createCoordinateOperation(toBackingFactoryCode(code)));
}
/**
* Creates an operation from coordinate reference system codes.
*
* @throws FactoryException if the object creation failed.
*/
@Override
public Set<CoordinateOperation> createFromCoordinateReferenceSystemCodes(
final String sourceCRS, final String targetCRS) throws FactoryException
{
final CoordinateOperationAuthorityFactory factory, check;
factory = getCoordinateOperationAuthorityFactory(sourceCRS);
check = getCoordinateOperationAuthorityFactory(targetCRS);
if (factory != check) {
/*
* No coordinate operation because of mismatched factories. This is not
* illegal - the result is an empty set - but it is worth to notify the
* user since this case has some chances to be an user error.
*/
final LogRecord record = Loggings.format(Level.WARNING,
LoggingKeys.MISMATCHED_COORDINATE_OPERATION_FACTORIES_$2, sourceCRS, targetCRS);
record.setSourceMethodName("createFromCoordinateReferenceSystemCodes");
record.setSourceClassName(AuthorityFactoryAdapter.class.getName());
record.setLoggerName(LOGGER.getName());
LOGGER.log(record);
return Collections.emptySet();
}
return factory.createFromCoordinateReferenceSystemCodes(
toBackingFactoryCode(sourceCRS), toBackingFactoryCode(targetCRS));
}
/**
* Returns a finder which can be used for looking up unidentified objects.
* The default implementation delegates the lookups to the underlying factory.
*
* @throws FactoryException if the object creation failed.
*
* @since 2.4
*/
@Override
public IdentifiedObjectFinder getIdentifiedObjectFinder(Class<? extends IdentifiedObject> type)
throws FactoryException
{
return new Finder(type);
}
/**
* A {@link IdentifiedObjectFinder} which tests
* {@linkplain AuthorityFactoryAdapter#replaceObject modified objects}
* in addition of original object.
*/
class Finder extends IdentifiedObjectFinder.Adapter {
/**
* Creates a finder for the underlying backing store.
*/
protected Finder(final Class<? extends IdentifiedObject> type) throws FactoryException {
super(getGeotoolsFactory("getIdentifiedObjectFinder", null).getIdentifiedObjectFinder(type));
}
/**
* Returns {@code candidate}, or an object derived from {@code candidate}, if it is
* {@linkplain CRS#equalsIgnoreMetadata equals ignoring metadata} to the specified
* model. Otherwise returns {@code null}.
*
* @throws FactoryException if an error occured while creating a derived object.
*/
@Override
protected IdentifiedObject deriveEquivalent(final IdentifiedObject candidate,
final IdentifiedObject model)
throws FactoryException
{
final IdentifiedObject modified = replaceObject(candidate);
if (modified != candidate) {
if (CRS.equalsIgnoreMetadata(modified, model)) {
return modified;
}
}
return super.deriveEquivalent(candidate, model);
}
}
/**
* Creates an exception for a missing factory. We actually returns an instance of
* {@link NoSuchAuthorityCodeException} because this kind of exception is treated
* especially by {@link FallbackAuthorityFactory}.
*/
private FactoryException missingFactory(final Class category, final String code) {
return new NoSuchAuthorityCodeException(Errors.format(ErrorKeys.FACTORY_NOT_FOUND_$1,
category), Citations.getIdentifier(getAuthority()), trimAuthority(code));
}
/**
* For internal use by {@link #getAuthority} and {@link #getVendor} only. Its only purpose
* is to catch the {@link FactoryException} for methods that don't allow it. The protected
* method should be used instead when this exception is allowed.
*/
private AuthorityFactory getAuthorityFactory() {
try {
return getAuthorityFactory(null);
} catch (FactoryException cause) {
throw new IllegalStateException(Errors.format(ErrorKeys.UNDEFINED_PROPERTY), cause);
}
}
/**
* Returns an authority factory of the specified type. This method delegates to:
* <ul>
* <li>{@link #getCRSAuthorityFactory} if {@code type} is
* {@code CRSAuthorityFactory.class};</li>
* <li>{@link #getCSAuthorityFactory} if {@code type} is
* {@code CSAuthorityFactory.class};</li>
* <li>{@link #getDatumAuthorityFactory} if {@code type} is
* {@code DatumAuthorityFactory.class};</li>
* <li>{@link #CoordinateOperationAuthorityFactory} if {@code type} is
* {@code CoordinateOperationAuthorityFactory.class};</li>
* </ul>
*
* @throws IllegalArgumentException if the specified {@code type} is invalid.
* @throws FactoryException if no suitable factory were found.
*/
@SuppressWarnings("unchecked")
<T extends AuthorityFactory> T getAuthorityFactory(final Class<T> type, final String code)
throws FactoryException
{
final AuthorityFactory f;
if (CRSAuthorityFactory.class.equals(type)) {
f = getCRSAuthorityFactory(code);
} else if (CSAuthorityFactory.class.equals(type)) {
f = getCSAuthorityFactory(code);
} else if (DatumAuthorityFactory.class.equals(type)) {
f = getDatumAuthorityFactory(code);
} else if (CoordinateOperationAuthorityFactory.class.equals(type)) {
f = getCoordinateOperationAuthorityFactory(code);
} else {
throw new IllegalArgumentException(Errors.format(
ErrorKeys.ILLEGAL_ARGUMENT_$2, "type", type));
}
return type.cast(f);
}
/**
* Returns a generic object factory to use for the specified code. The default implementation
* returns one of the factory specified at construction time. Subclasses can override
* this method in order to select a different factory implementation depending on the
* code value.
* <p>
* <strong>Note:</strong> The value of the {@code code} argument given to this
* method may be {@code null} when a factory is needed for some global task,
* like {@link #getAuthorityCodes} method execution.
*
* @param code The authority code given to this class. Note that the code to be given
* to the returned factory {@linkplain #toBackingFactoryCode may be different}.
* @return A factory for the specified authority code (never {@code null}).
* @throws FactoryException if no suitable factory were found.
*
* @since 2.4
*/
protected AuthorityFactory getAuthorityFactory(final String code) throws FactoryException {
if ( crsFactory != null) return crsFactory;
if ( csFactory != null) return csFactory;
if ( datumFactory != null) return datumFactory;
if (operationFactory != null) return operationFactory;
throw missingFactory(AuthorityFactory.class, code);
}
/**
* Returns the datum factory to use for the specified code. The default implementation
* always returns the factory specified at construction time. Subclasses can override
* this method in order to select a different factory implementation depending on the
* code value.
*
* @param code The authority code given to this class. Note that the code to be given
* to the returned factory {@linkplain #toBackingFactoryCode may be different}.
* @return A factory for the specified authority code (never {@code null}).
* @throws FactoryException if no datum factory were specified at construction time.
*
* @since 2.4
*/
protected DatumAuthorityFactory getDatumAuthorityFactory(final String code)
throws FactoryException
{
if (datumFactory == null) {
throw missingFactory(DatumAuthorityFactory.class, code);
}
return datumFactory;
}
/**
* Returns the coordinate system factory to use for the specified code. The default
* implementation always returns the factory specified at construction time. Subclasses
* can override this method in order to select a different factory implementation
* depending on the code value.
*
* @param code The authority code given to this class. Note that the code to be given
* to the returned factory {@linkplain #toBackingFactoryCode may be different}.
* @return A factory for the specified authority code (never {@code null}).
* @throws FactoryException if no coordinate system factory were specified at construction time.
*
* @since 2.4
*/
protected CSAuthorityFactory getCSAuthorityFactory(final String code)
throws FactoryException
{
if (csFactory == null) {
throw missingFactory(CSAuthorityFactory.class, code);
}
return csFactory;
}
/**
* Returns the coordinate reference system factory to use for the specified code. The default
* implementation always returns the factory specified at construction time. Subclasses can
* override this method in order to select a different factory implementation depending on
* the code value.
*
* @param code The authority code given to this class. Note that the code to be given
* to the returned factory {@linkplain #toBackingFactoryCode may be different}.
* @return A factory for the specified authority code (never {@code null}).
* @throws FactoryException if no coordinate reference system factory were specified
* at construction time.
*
* @since 2.4
*/
protected CRSAuthorityFactory getCRSAuthorityFactory(final String code)
throws FactoryException
{
if (crsFactory == null) {
throw missingFactory(CRSAuthorityFactory.class, code);
}
return crsFactory;
}
/**
* Returns the coordinate operation factory to use for the specified code. The default
* implementation always returns the factory specified at construction time. Subclasses can
* override this method in order to select a different factory implementation depending on
* the code value.
*
* @param code The authority code given to this class. Note that the code to be given
* to the returned factory {@linkplain #toBackingFactoryCode may be different}.
* @return A factory for the specified authority code (never {@code null}).
* @throws FactoryException if no coordinate operation factory were specified
* at construction time.
*
* @since 2.4
*/
protected CoordinateOperationAuthorityFactory getCoordinateOperationAuthorityFactory(final String code)
throws FactoryException
{
if (operationFactory == null) {
throw missingFactory(CoordinateOperationAuthorityFactory.class, code);
}
return operationFactory;
}
/**
* Returns a coordinate operation factory for this adapter. This method will try to fetch
* this information from the coordinate operation authority factory, or will returns the
* default one if no explicit factory were found.
*/
final CoordinateOperationFactory getCoordinateOperationFactory() throws FactoryException {
if (operationFactory instanceof Factory) {
final Factory factory = (Factory) operationFactory;
final Map hints = factory.getImplementationHints();
final Object candidate = hints.get(Hints.COORDINATE_OPERATION_FACTORY);
if (candidate instanceof CoordinateOperationFactory) {
return (CoordinateOperationFactory) candidate;
}
}
return ReferencingFactoryFinder.getCoordinateOperationFactory(hints());
}
/**
* Suggests a low-level factory group. If {@code crs} is {@code true}, then this method will
* try to fetch the factory group from the CRS authority factory. Otherwise it will try to
* fetch the factory group from the CS authority factory. This is used by subclasses like
* {@link TransformedAuthorityFactory} that need low-level access to factories. Do not change
* this method into a public one; we would need a better API before to do such thing.
*/
final ReferencingFactoryContainer getFactoryContainer(final boolean crs) {
final AuthorityFactory factory;
if (crs) {
factory = crsFactory;
} else {
factory = csFactory;
}
if (factory instanceof DirectAuthorityFactory) {
return ((DirectAuthorityFactory) factory).factories;
}
// No predefined factory group. Create one.
if (factories == null) {
factories = ReferencingFactoryContainer.instance(hints());
}
return factories;
}
/**
* Returns the code to be given to the wrapped factories. This method is automatically
* invoked by all {@code create} methods before to forward the code to the
* {@linkplain #getCRSAuthorityFactory CRS}, {@linkplain #getCSAuthorityFactory CS},
* {@linkplain #getDatumAuthorityFactory datum} or {@linkplain #operationFactory operation}
* factory. The default implementation returns the {@code code} unchanged.
*
* @param code The code given to this factory.
* @return The code to give to the underlying factories.
* @throws FactoryException if the code can't be converted.
*
* @since 2.4
*/
protected String toBackingFactoryCode(final String code) throws FactoryException {
return code;
}
/**
* Returns {@code true} if the {@link #toBackingFactoryCode} method is overriden.
*/
final boolean isCodeMethodOverriden() {
final Class<?>[] arguments = new Class[] {String.class};
for (Class<?> type=getClass(); !AuthorityFactoryAdapter.class.equals(type); type=type.getSuperclass()) {
try {
type.getDeclaredMethod("toBackingFactoryCode", arguments);
} catch (NoSuchMethodException e) {
// The method is not overriden in this class.
// Checks in the super-class.
continue;
} catch (SecurityException e) {
// We are not allowed to get this information.
// Conservatively assumes that the method is overriden.
}
return true;
}
return false;
}
@Override
public void dispose() throws FactoryException {
super.dispose();
disposeAbstractAuthorityFactory(datumFactory);
disposeAbstractAuthorityFactory(csFactory);
disposeAbstractAuthorityFactory(crsFactory);
disposeAbstractAuthorityFactory(operationFactory);
}
private void disposeAbstractAuthorityFactory(Object factory) throws FactoryException {
if(factory instanceof AbstractAuthorityFactory) {
((AbstractAuthorityFactory) factory).dispose();
}
}
}