/* * 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. * * This package contains documentation from OpenGIS specifications. * OpenGIS consortium's work is fully acknowledged here. */ package org.geotools.referencing.factory; import java.util.Set; import javax.measure.unit.Unit; import org.geotools.factory.BufferedFactory; import org.geotools.factory.FactoryRegistryException; import org.geotools.factory.GeoTools; import org.geotools.factory.Hints; import org.geotools.util.ObjectCache; import org.geotools.util.ObjectCaches; import org.opengis.metadata.citation.Citation; import org.opengis.referencing.AuthorityFactory; import org.opengis.referencing.FactoryException; import org.opengis.referencing.IdentifiedObject; import org.opengis.referencing.crs.CRSAuthorityFactory; import org.opengis.referencing.crs.CompoundCRS; import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.opengis.referencing.crs.DerivedCRS; import org.opengis.referencing.crs.EngineeringCRS; import org.opengis.referencing.crs.GeocentricCRS; import org.opengis.referencing.crs.GeographicCRS; import org.opengis.referencing.crs.ImageCRS; import org.opengis.referencing.crs.ProjectedCRS; import org.opengis.referencing.crs.TemporalCRS; import org.opengis.referencing.crs.VerticalCRS; import org.opengis.referencing.cs.CSAuthorityFactory; import org.opengis.referencing.cs.CartesianCS; import org.opengis.referencing.cs.CoordinateSystem; import org.opengis.referencing.cs.CoordinateSystemAxis; import org.opengis.referencing.cs.CylindricalCS; import org.opengis.referencing.cs.EllipsoidalCS; import org.opengis.referencing.cs.PolarCS; import org.opengis.referencing.cs.SphericalCS; import org.opengis.referencing.cs.TimeCS; import org.opengis.referencing.cs.VerticalCS; import org.opengis.referencing.datum.Datum; import org.opengis.referencing.datum.DatumAuthorityFactory; import org.opengis.referencing.datum.Ellipsoid; import org.opengis.referencing.datum.EngineeringDatum; import org.opengis.referencing.datum.GeodeticDatum; import org.opengis.referencing.datum.ImageDatum; import org.opengis.referencing.datum.PrimeMeridian; import org.opengis.referencing.datum.TemporalDatum; import org.opengis.referencing.datum.VerticalDatum; import org.opengis.referencing.operation.CoordinateOperation; import org.opengis.referencing.operation.CoordinateOperationAuthorityFactory; import org.opengis.util.InternationalString; /** * An authority factory that caches all objects created by delegate factories. * This class is set up to cache the full complement of referencing objects: * In many cases a single implementation will be used for several the authority factory * interfaces - but this is not a requirement. * </p> * The behaviour of the {@code createFoo(String)} methods first looks if a * previously created object exists for the given code. If such an object * exists, it is returned directly. The testing of the cache is synchronized and * may block if the referencing object is under construction. * <p> * If the object is not yet created, the definition is delegated to the * appropriate the {@linkplain an AuthorityFactory authority factory} and the * result is cached for next time. * <p> * This object is responsible for owning a {{ReferencingObjectCache}}; there are * several implementations to choose from on construction. * </p> * * @since 2.4 * * @source $URL$ * @version $Id$ * @author Jody Garnett */ public final class CachedAuthorityDecorator extends AbstractAuthorityFactory implements AuthorityFactory, CRSAuthorityFactory, CSAuthorityFactory, DatumAuthorityFactory, CoordinateOperationAuthorityFactory, BufferedFactory { /** Cache to be used for referencing objects. */ ObjectCache cache; /** The delegate authority. */ private AuthorityFactory authority; /** The delegate authority for coordinate reference systems. */ private CRSAuthorityFactory crsAuthority; /** The delegate authority for coordinate sytems. */ private CSAuthorityFactory csAuthority; /** The delegate authority for datums. */ private DatumAuthorityFactory datumAuthority; /** The delegate authority for coordinate operations. */ private CoordinateOperationAuthorityFactory operationAuthority; /** The delegate authority for searching. */ private AbstractAuthorityFactory delegate; /** * Constructs an instance wrapping the specified factory with a default * cache. * <p> * The provided authority factory must implement * {@link DatumAuthorityFactory}, {@link CSAuthorityFactory}, * {@link CRSAuthorityFactory} and * {@link CoordinateOperationAuthorityFactory} . * * @param factory * The factory to cache. Can not be {@code null}. */ public CachedAuthorityDecorator(final AuthorityFactory factory) { this(factory, createCache(GeoTools.getDefaultHints())); } /** * Constructs an instance wrapping the specified factory. The * {@code maxStrongReferences} argument specify the maximum number of * objects to keep by strong reference. If a greater amount of objects are * created, then the strong references for the oldest ones are replaced by * weak references. * <p> * This constructor is protected because subclasses must declare which of * the {@link DatumAuthorityFactory}, {@link CSAuthorityFactory}, * {@link CRSAuthorityFactory} {@link SearchableAuthorityFactory} and * {@link CoordinateOperationAuthorityFactory} interfaces they choose to * implement. * * @param factory * The factory to cache. Can not be {@code null}. * @param maxStrongReferences * The maximum number of objects to keep by strong reference. */ protected CachedAuthorityDecorator(AuthorityFactory factory, ObjectCache cache) { super( ((ReferencingFactory)factory).getPriority() ); // TODO this.cache = cache; authority = factory; crsAuthority = (CRSAuthorityFactory) factory; csAuthority = (CSAuthorityFactory) factory; datumAuthority = (DatumAuthorityFactory) factory; operationAuthority = (CoordinateOperationAuthorityFactory) factory; this.delegate = (AbstractAuthorityFactory) factory; } /** Utility method used to produce cache based on hint */ protected static ObjectCache createCache(final Hints hints) throws FactoryRegistryException { return ObjectCaches.create(hints); } // // Utility Methods and Cache Care and Feeding // protected String toKey(String code) { return ObjectCaches.toKey( getAuthority(), code); } // // AuthorityFactory // public IdentifiedObject createObject(String code) throws FactoryException { final String key = toKey(code); IdentifiedObject obj = (IdentifiedObject) cache.get(key); if (obj == null) { try { cache.writeLock(key); obj = (IdentifiedObject) cache.peek(key); if (obj == null) { obj = authority.createObject(code); cache.put(key, obj); } } finally { cache.writeUnLock(key); } } return obj; } public Citation getAuthority() { return authority.getAuthority(); } public Set getAuthorityCodes(Class type) throws FactoryException { return authority.getAuthorityCodes(type); } public InternationalString getDescriptionText(String code) throws FactoryException { return authority.getDescriptionText(code); } // // CRSAuthority // public synchronized CompoundCRS createCompoundCRS(final String code) throws FactoryException { final String key = toKey(code); CompoundCRS crs = (CompoundCRS) cache.get(key); if (crs == null) { try { cache.writeLock(key); crs = (CompoundCRS) cache.peek(key); if (crs == null) { crs = crsAuthority.createCompoundCRS(code); cache.put(key, crs); } } finally { cache.writeUnLock(key); } } return crs; } public CoordinateReferenceSystem createCoordinateReferenceSystem(String code) throws FactoryException { final String key = toKey(code); CoordinateReferenceSystem crs = (CoordinateReferenceSystem) cache .get(key); if (crs == null) { try { cache.writeLock(key); crs = (CoordinateReferenceSystem) cache.peek(key); if (crs == null) { crs = crsAuthority.createCoordinateReferenceSystem(code); cache.put(key, crs); } } finally { cache.writeUnLock(key); } } return crs; } public DerivedCRS createDerivedCRS(String code) throws FactoryException { final String key = toKey(code); DerivedCRS crs = (DerivedCRS) cache.get(key); if (crs == null) { try { cache.writeLock(key); crs = (DerivedCRS) cache.peek(key); if (crs == null) { crs = crsAuthority.createDerivedCRS(code); cache.put(key, crs); } } finally { cache.writeUnLock(key); } } return crs; } public EngineeringCRS createEngineeringCRS(String code) throws FactoryException { final String key = toKey(code); EngineeringCRS crs = (EngineeringCRS) cache.get(key); if (crs == null) { try { cache.writeLock(key); crs = (EngineeringCRS) cache.peek(key); if (crs == null) { crs = crsAuthority.createEngineeringCRS(code); cache.put(key, crs); } } finally { cache.writeUnLock(key); } } return crs; } public GeocentricCRS createGeocentricCRS(String code) throws FactoryException { final String key = toKey(code); GeocentricCRS crs = (GeocentricCRS) cache.get(key); if (crs == null) { try { cache.writeLock(key); crs = (GeocentricCRS) cache.peek(key); if (crs == null) { crs = crsAuthority.createGeocentricCRS(code); cache.put(key, crs); } } finally { cache.writeUnLock(key); } } return crs; } public GeographicCRS createGeographicCRS(String code) throws FactoryException { final String key = toKey(code); GeographicCRS crs = (GeographicCRS) cache.get(key); if (crs == null) { try { cache.writeLock(key); crs = (GeographicCRS) cache.peek(key); if (crs == null) { crs = crsAuthority.createGeographicCRS(code); cache.put(key, crs); } } finally { cache.writeUnLock(key); } } return crs; } public ImageCRS createImageCRS(String code) throws FactoryException { final String key = toKey(code); ImageCRS crs = (ImageCRS) cache.get(key); if (crs == null) { try { cache.writeLock(key); crs = (ImageCRS) cache.peek(key); if (crs == null) { crs = crsAuthority.createImageCRS(code); cache.put(key, crs); } } finally { cache.writeUnLock(key); } } return crs; } public ProjectedCRS createProjectedCRS(String code) throws FactoryException { final String key = toKey(code); ProjectedCRS crs = (ProjectedCRS) cache.get(key); if (crs == null) { try { cache.writeLock(key); crs = (ProjectedCRS) cache.peek(key); if (crs == null) { crs = crsAuthority.createProjectedCRS(code); cache.put(key, crs); } } finally { cache.writeUnLock(key); } } return crs; } public TemporalCRS createTemporalCRS(String code) throws FactoryException { final String key = toKey(code); TemporalCRS crs = (TemporalCRS) cache.get(key); if (crs == null) { try { cache.writeLock(key); crs = (TemporalCRS) cache.peek(key); if (crs == null) { crs = crsAuthority.createTemporalCRS(code); cache.put(key, crs); } } finally { cache.writeUnLock(key); } } return crs; } public VerticalCRS createVerticalCRS(String code) throws FactoryException { final String key = toKey(code); VerticalCRS crs = (VerticalCRS) cache.get(key); if (crs == null) { try { cache.writeLock(key); crs = (VerticalCRS) cache.peek(key); if (crs == null) { crs = crsAuthority.createVerticalCRS(code); cache.put(key, crs); } } finally { cache.writeUnLock(key); } } return crs; } // // CSAuthority // public CartesianCS createCartesianCS(String code) throws FactoryException { final String key = toKey(code); CartesianCS cs = (CartesianCS) cache.get(key); if (cs == null) { try { cache.writeLock(key); cs = (CartesianCS) cache.peek(key); if (cs == null) { cs = csAuthority.createCartesianCS(code); cache.put(key, cs); } } finally { cache.writeUnLock(key); } } return cs; } public CoordinateSystem createCoordinateSystem(String code) throws FactoryException { final String key = toKey(code); CoordinateSystem cs = (CoordinateSystem) cache.get(key); if (cs == null) { try { cache.writeLock(key); cs = (CoordinateSystem) cache.peek(key); if (cs == null) { cs = csAuthority.createCoordinateSystem(code); cache.put(key, cs); } } finally { cache.writeUnLock(key); } } return cs; } // sample implemenation with get/test public CoordinateSystemAxis createCoordinateSystemAxis(String code) throws FactoryException { final String key = toKey(code); CoordinateSystemAxis axis = (CoordinateSystemAxis) cache.get(key); if (axis == null) { try { cache.writeLock(key); axis = (CoordinateSystemAxis) cache.peek(key); if (axis == null) { axis = csAuthority.createCoordinateSystemAxis(code); cache.put(key, axis); } } finally { cache.writeUnLock(key); } } return axis; } public CylindricalCS createCylindricalCS(String code) throws FactoryException { final String key = toKey(code); CylindricalCS cs = (CylindricalCS) cache.get(key); if (cs == null) { try { cache.writeLock(key); cs = (CylindricalCS) cache.peek(key); if (cs == null) { cs = csAuthority.createCylindricalCS(code); cache.put(key, cs); } } finally { cache.writeUnLock(key); } } return cs; } public EllipsoidalCS createEllipsoidalCS(String code) throws FactoryException { final String key = toKey(code); EllipsoidalCS cs = (EllipsoidalCS) cache.get(key); if (cs == null) { try { cache.writeLock(key); cs = (EllipsoidalCS) cache.peek(key); if (cs == null) { cs = csAuthority.createEllipsoidalCS(code); cache.put(key, cs); } } finally { cache.writeUnLock(key); } } return cs; } public PolarCS createPolarCS(String code) throws FactoryException { final String key = toKey(code); PolarCS cs = (PolarCS) cache.get(key); if (cs == null) { try { cache.writeLock(key); cs = (PolarCS) cache.peek(key); if (cs == null) { cs = csAuthority.createPolarCS(code); cache.put(key, cs); } } finally { cache.writeUnLock(key); } } return cs; } public SphericalCS createSphericalCS(String code) throws FactoryException { final String key = toKey(code); SphericalCS cs = (SphericalCS) cache.get(key); if (cs == null) { try { cache.writeLock(key); cs = (SphericalCS) cache.peek(key); if (cs == null) { cs = csAuthority.createSphericalCS(code); cache.put(key, cs); } } finally { cache.writeUnLock(key); } } return cs; } public TimeCS createTimeCS(String code) throws FactoryException { final String key = toKey(code); TimeCS cs = (TimeCS) cache.get(key); if (cs == null) { try { cache.writeLock(key); cs = (TimeCS) cache.peek(key); if (cs == null) { cs = csAuthority.createTimeCS(code); cache.put(key, cs); } } finally { cache.writeUnLock(key); } } return cs; } public Unit<?> createUnit(String code) throws FactoryException { final String key = toKey(code); Unit<?> unit = (Unit) cache.get(key); if (unit == null) { try { cache.writeLock(key); unit = (Unit) cache.peek(key); if (unit == null) { unit = csAuthority.createUnit(code); cache.put(key, unit); } } finally { cache.writeUnLock(key); } } return unit; } public VerticalCS createVerticalCS(String code) throws FactoryException { final String key = toKey(code); VerticalCS cs = (VerticalCS) cache.get(key); if (cs == null) { try { cache.writeLock(key); cs = (VerticalCS) cache.peek(key); if (cs == null) { cs = csAuthority.createVerticalCS(code); cache.put(key, cs); } } finally { cache.writeUnLock(key); } } return cs; } // // DatumAuthorityFactory // public Datum createDatum(String code) throws FactoryException { final String key = toKey(code); Datum datum = (Datum) cache.get(key); if (datum == null) { try { cache.writeLock(key); datum = (Datum) cache.peek(key); if (datum == null) { datum = datumAuthority.createDatum(code); cache.put(key, datum); } } finally { cache.writeUnLock(key); } } return datum; } public Ellipsoid createEllipsoid(String code) throws FactoryException { final String key = toKey(code); Ellipsoid ellipsoid = (Ellipsoid) cache.get(key); if (ellipsoid == null) { try { cache.writeLock(key); ellipsoid = (Ellipsoid) cache.peek(key); if (ellipsoid == null) { ellipsoid = datumAuthority.createEllipsoid(code); cache.put(key, ellipsoid); } } finally { cache.writeUnLock(key); } } return ellipsoid; } public EngineeringDatum createEngineeringDatum(String code) throws FactoryException { final String key = toKey(code); EngineeringDatum datum = (EngineeringDatum) cache.get(key); if (datum == null) { try { cache.writeLock(key); datum = (EngineeringDatum) cache.peek(key); if (datum == null) { datum = datumAuthority.createEngineeringDatum(code); cache.put(key, datum); } } finally { cache.writeUnLock(key); } } return datum; } public GeodeticDatum createGeodeticDatum(String code) throws FactoryException { final String key = toKey(code); GeodeticDatum datum = (GeodeticDatum) cache.get(key); if (datum == null) { try { cache.writeLock(key); datum = (GeodeticDatum) cache.peek(key); if (datum == null) { datum = datumAuthority.createGeodeticDatum(code); cache.put(key, datum); } } finally { cache.writeUnLock(key); } } return datum; } public ImageDatum createImageDatum(String code) throws FactoryException { final String key = toKey(code); ImageDatum datum = (ImageDatum) cache.get(key); if (datum == null) { try { cache.writeLock(key); datum = (ImageDatum) cache.peek(key); if (datum == null) { datum = datumAuthority.createImageDatum(code); cache.put(key, datum); } } finally { cache.writeUnLock(key); } } return datum; } public PrimeMeridian createPrimeMeridian(String code) throws FactoryException { final String key = toKey(code); PrimeMeridian datum = (PrimeMeridian) cache.get(key); if (datum == null) { try { cache.writeLock(key); datum = (PrimeMeridian) cache.peek(key); if (datum == null) { datum = datumAuthority.createPrimeMeridian(code); cache.put(key, datum); } } finally { cache.writeUnLock(key); } } return datum; } public TemporalDatum createTemporalDatum(String code) throws FactoryException { final String key = toKey(code); TemporalDatum datum = (TemporalDatum) cache.get(key); if (datum == null) { try { cache.writeLock(key); datum = (TemporalDatum) cache.peek(key); if (datum == null) { datum = datumAuthority.createTemporalDatum(code); cache.put(key, datum); } } finally { cache.writeUnLock(key); } } return datum; } public VerticalDatum createVerticalDatum(String code) throws FactoryException { final String key = toKey(code); VerticalDatum datum = (VerticalDatum) cache.get(key); if (datum == null) { try { cache.writeLock(key); datum = (VerticalDatum) cache.peek(key); if (datum == null) { datum = datumAuthority.createVerticalDatum(code); cache.put(key, datum); } } finally { cache.writeUnLock(key); } } return datum; } public CoordinateOperation createCoordinateOperation(String code) throws FactoryException { final String key = toKey(code); CoordinateOperation operation = (CoordinateOperation) cache.get(key); if (operation == null) { try { cache.writeLock(key); operation = (CoordinateOperation) cache.peek(key); if (operation == null) { operation = operationAuthority .createCoordinateOperation(code); cache.put(key, operation); } } finally { cache.writeUnLock(key); } } return operation; } public synchronized Set/*<CoordinateOperation>*/ createFromCoordinateReferenceSystemCodes( final String sourceCode, final String targetCode) throws FactoryException { final Object key = ObjectCaches.toKey( getAuthority(), sourceCode, targetCode ); Set operations = (Set) cache.get(key); if (operations == null) { try { cache.writeLock(key); operations = (Set) cache.peek(key); if (operations == null) { operations = operationAuthority.createFromCoordinateReferenceSystemCodes( sourceCode, targetCode ); // can we not trust operationAuthority to return us an unmodifiableSet ? //operations = Collections.unmodifiableSet( operations ); cache.put( key, operations ); } } finally { cache.writeUnLock(key); } } return operations; } // // AbstractAuthorityFactory // public IdentifiedObjectFinder getIdentifiedObjectFinder( Class type ) throws FactoryException { return delegate.getIdentifiedObjectFinder( type ); } public void dispose() throws FactoryException { delegate.dispose(); cache.clear(); cache = null; delegate = null; } public String getBackingStoreDescription() throws FactoryException { return delegate.getBackingStoreDescription(); } }