001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2007, GeoTools Project Managment Committee (PMC)
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation;
009: * version 2.1 of the License.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: *
016: * This package contains documentation from OpenGIS specifications.
017: * OpenGIS consortium's work is fully acknowledged here.
018: */
019: package org.geotools.referencing.factory;
020:
021: // J2SE dependencies
022: import java.util.Set;
023: import java.util.Arrays;
024: import java.lang.reflect.Method;
025: import java.lang.reflect.InvocationTargetException;
026:
027: // OpenGIS dependencies
028: import org.opengis.referencing.*;
029: import org.opengis.referencing.cs.*;
030: import org.opengis.referencing.crs.*;
031: import org.opengis.referencing.datum.*;
032: import org.opengis.referencing.operation.*;
033: import org.opengis.parameter.ParameterDescriptor;
034:
035: // Geotools dependencies
036: import org.geotools.resources.Utilities;
037: import org.geotools.resources.i18n.Errors;
038: import org.geotools.resources.i18n.ErrorKeys;
039:
040: /**
041: * Delegates object creations to one of the {@code create} methods in a backing
042: * {@linkplain AuthorityFactory authority factory}. It is possible to use the generic
043: * {@link AuthorityFactory#createObject createObject} method instead of this class,
044: * but some factory implementations are more efficient when we use the most specific
045: * {@code create} method. For example when using a
046: * {@linkplain org.geotools.referencing.factory.epsg.DirectEpsgFactory EPSG factory backed
047: * by a SQL database}, invoking {@link CRSAuthorityFactory#createCoordinateReferenceSystem
048: * createCoordinateReferenceSystem} instead of {@link AuthorityFactory#createObject createObject}
049: * method will reduce the amount of tables to be queried.
050: * <p>
051: * This class is useful when the same {@code create} method need to be invoked often,
052: * but is unknown at compile time. It may also be used as a workaround for authority
053: * factories that don't implement the {@code createObject} method.
054: * <p>
055: * <b>Example:</b> The following code creates a proxy which will delegates its work to the
056: * {@link CRSAuthorityFactory#createGeographicCRS createGeographicCRS} method.
057: *
058: * <blockquote><pre>
059: * AuthorityFactory factory = ...;
060: * AuthorityFactoryProxy<GeographicCRS> proxy =
061: * AuthorityFactoryProxy.getInstance(GeographicCRS.class, factory);
062: *
063: * String code = ...;
064: * // Invokes CRSAuthorityFactory.createGeographicCRS(code);
065: * GeographicCRS crs = proxy.create(code);
066: * </pre></blockquote>
067: *
068: * @since 2.4
069: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/referencing/src/main/java/org/geotools/referencing/factory/AuthorityFactoryProxy.java $
070: * @version $Id: AuthorityFactoryProxy.java 26212 2007-07-12 02:34:18Z jgarnett $
071: * @author Martin Desruisseaux
072: */
073: abstract class AuthorityFactoryProxy {
074: /**
075: * The types that factories can be create. The most
076: * specific types must appear first in this list.
077: */
078: private static final Class/*<? extends IdentifiedObject>*/[] TYPES = {
079: CoordinateOperation.class, OperationMethod.class,
080: ParameterDescriptor.class, ProjectedCRS.class,
081: GeographicCRS.class, GeocentricCRS.class, ImageCRS.class,
082: DerivedCRS.class, VerticalCRS.class, TemporalCRS.class,
083: EngineeringCRS.class, CompoundCRS.class,
084: CoordinateReferenceSystem.class,
085: CoordinateSystemAxis.class, CartesianCS.class,
086: EllipsoidalCS.class, SphericalCS.class,
087: CylindricalCS.class, PolarCS.class, VerticalCS.class,
088: TimeCS.class, CoordinateSystem.class, PrimeMeridian.class,
089: Ellipsoid.class, GeodeticDatum.class, ImageDatum.class,
090: VerticalDatum.class, TemporalDatum.class,
091: EngineeringDatum.class, Datum.class, IdentifiedObject.class };
092:
093: /**
094: * Creates a new proxy.
095: */
096: AuthorityFactoryProxy() {
097: }
098:
099: /**
100: * Returns a proxy instance which will create objects of the specified type using the
101: * specified factory.
102: *
103: * @param factory The factory to use for object creations.
104: * @param type The type of objects to be created by the proxy.
105: */
106: public static AuthorityFactoryProxy getInstance(
107: final AuthorityFactory factory,
108: Class/*<? extends IdentifiedObject>*/type) {
109: AbstractAuthorityFactory.ensureNonNull("type", type);
110: AbstractAuthorityFactory.ensureNonNull("factory", factory);
111: type = getType(type);
112: /*
113: * Checks for some special cases for which a fast implementation is available.
114: */
115: if (factory instanceof CRSAuthorityFactory) {
116: final CRSAuthorityFactory crsFactory = (CRSAuthorityFactory) factory;
117: if (type.equals(ProjectedCRS.class))
118: return new Projected(crsFactory);
119: if (type.equals(GeographicCRS.class))
120: return new Geographic(crsFactory);
121: if (type.equals(CoordinateReferenceSystem.class))
122: return new CRS(crsFactory);
123: }
124: /*
125: * Fallback on the generic case using reflection.
126: */
127: return new Default(factory, type);
128: }
129:
130: /**
131: * Returns the main GeoAPI interface implemented by an object of the specified type.
132: * The {@code type} argument is often some implementation class like
133: * {@link org.geotools.referencing.crs.DefaultProjectedCRS}. This method returns
134: * the most specific GeoAPI interface implemented by {@code type}, providing that
135: * a corresponding {@code create} method exists in some {@linkplain AuthorityFactory
136: * authority factory}. For example this method may returns {@link ProjectedCRS} or
137: * {@link DerivedCRS} class, but not {@link GeneralDerivedCRS}.
138: *
139: * @param type The implementation class.
140: * @return The most specific GeoAPI interface implemented by {@code type}.
141: * @throws IllegalArgumentException if the type doesn't implement a valid interface.
142: */
143: public static Class/*<? extends IdentifiedObject>*/getType(
144: final Class/*<? extends IdentifiedObject>*/type)
145: throws IllegalArgumentException {
146: for (int i = 0; i < TYPES.length; i++) {
147: final Class/*<? extends IdentifiedObject>*/candidate = TYPES[i];
148: if (candidate.isAssignableFrom(type)) {
149: return candidate;
150: }
151: }
152: throw new IllegalArgumentException(Errors.format(
153: ErrorKeys.ILLEGAL_CLASS_$2, type,
154: IdentifiedObject.class));
155: }
156:
157: /**
158: * Returns the type of the objects to be created by this proxy instance.
159: */
160: public abstract Class/*<? extends IdentifiedObject>*/getType();
161:
162: /**
163: * Returns the authority factory used by the {@link #create create} method.
164: */
165: public abstract AuthorityFactory getAuthorityFactory();
166:
167: /**
168: * Returns the set of authority codes.
169: *
170: * @throws FactoryException if access to the underlying database failed.
171: */
172: public final Set/*<String>*/getAuthorityCodes()
173: throws FactoryException {
174: return getAuthorityFactory().getAuthorityCodes(getType());
175: }
176:
177: /**
178: * Creates an object for the specified code. This method will delegates to the most
179: * specific {@code create} method from the authority factory. The returned object
180: * will always be of the type returned by {@link #getType()}.
181: *
182: * @throws NoSuchAuthorityCodeException if the specified {@code code} was not found.
183: * @throws FactoryException if the object creation failed for some other reason.
184: */
185: public abstract IdentifiedObject create(String code)
186: throws NoSuchAuthorityCodeException, FactoryException;
187:
188: /**
189: * Returns a string representation of this proxy, for debugging purpose only.
190: */
191: //@Override
192: public String toString() {
193: return toString(AuthorityFactoryProxy.class);
194: }
195:
196: /**
197: * Returns a string representation of the specified object, for debugging purpose only.
198: */
199: final String toString(final Class owner) {
200: final AuthorityFactory factory = getAuthorityFactory();
201: return Utilities.getShortName(owner) + '['
202: + Utilities.getShortName(getType()) + " in "
203: + Utilities.getShortClassName(factory) + "(\""
204: + factory.getAuthority().getTitle() + "\")]";
205: }
206:
207: /**
208: * A default implementation using reflections. To be used only when we don't provide
209: * a specialized, more efficient, implementation.
210: *
211: * @version $Id: AuthorityFactoryProxy.java 26212 2007-07-12 02:34:18Z jgarnett $
212: * @author Martin Desruisseaux
213: */
214: private static final class Default extends AuthorityFactoryProxy {
215: /**
216: * The argument types of {@code createFoo} methods.
217: */
218: private static final Class[] PARAMETERS = new Class[] { String.class };
219:
220: /**
221: * The authority factory on which to delegates.
222: */
223: private final AuthorityFactory factory;
224:
225: /**
226: * The type of the objects to be created.
227: */
228: private final Class/*<? extends IdentifiedObject>*/type;
229:
230: /**
231: * The {@code createFoo} method to invoke.
232: */
233: private final Method method;
234:
235: /**
236: * Creates a new proxy which will delegates the object creation to the specified instance.
237: */
238: Default(final AuthorityFactory factory,
239: final Class/*<? extends IdentifiedObject>*/type)
240: throws IllegalArgumentException {
241: this .factory = factory;
242: this .type = type;
243: final Method[] candidates = factory.getClass().getMethods();
244: for (int i = 0; i < candidates.length; i++) {
245: final Method c = candidates[i];
246: if (c.getName().startsWith("create")
247: && type.equals(c.getReturnType())
248: && Arrays.equals(PARAMETERS, c
249: .getParameterTypes())) {
250: method = c;
251: return;
252: }
253: }
254: throw new IllegalArgumentException(Errors.format(
255: ErrorKeys.UNKNOW_TYPE_$1, type));
256: }
257:
258: /**
259: * {@inheritDoc}
260: */
261: public Class/*<? extends IdentifiedObject>*/getType() {
262: return type;
263: }
264:
265: /**
266: * {@inheritDoc}
267: */
268: public AuthorityFactory getAuthorityFactory() {
269: return factory;
270: }
271:
272: /**
273: * {@inheritDoc}
274: */
275: public IdentifiedObject create(final String code)
276: throws FactoryException {
277: try {
278: return (IdentifiedObject) method.invoke(factory,
279: new String[] { code });
280: } catch (InvocationTargetException exception) {
281: final Throwable cause = exception.getCause();
282: if (cause instanceof FactoryException) {
283: throw (FactoryException) cause;
284: }
285: if (cause instanceof RuntimeException) {
286: throw (RuntimeException) cause;
287: }
288: if (cause instanceof Error) {
289: throw (Error) cause;
290: }
291: throw new FactoryException(cause.getLocalizedMessage(),
292: cause);
293: } catch (IllegalAccessException exception) {
294: throw new FactoryException(exception
295: .getLocalizedMessage(), exception);
296: }
297: }
298: }
299:
300: /**
301: * An implementation for {@link CoordinateReferenceSystem} objects.
302: *
303: * @version $Id: AuthorityFactoryProxy.java 26212 2007-07-12 02:34:18Z jgarnett $
304: * @author Martin Desruisseaux
305: */
306: private static class CRS extends AuthorityFactoryProxy {
307: /** The authority factory on which to delegates. */
308: protected final CRSAuthorityFactory factory;
309:
310: protected CRS(final CRSAuthorityFactory factory) {
311: this .factory = factory;
312: }
313:
314: public Class getType() {
315: return CoordinateReferenceSystem.class;
316: }
317:
318: public final AuthorityFactory getAuthorityFactory() {
319: return factory;
320: }
321:
322: public IdentifiedObject create(final String code)
323: throws FactoryException {
324: return factory.createCoordinateReferenceSystem(code);
325: }
326: }
327:
328: /**
329: * An implementation for {@link GeographicCRS} objects.
330: *
331: * @version $Id: AuthorityFactoryProxy.java 26212 2007-07-12 02:34:18Z jgarnett $
332: * @author Martin Desruisseaux
333: */
334: private static final class Geographic extends CRS {
335: protected Geographic(final CRSAuthorityFactory factory) {
336: super (factory);
337: }
338:
339: //@Override
340: public Class getType() {
341: return GeographicCRS.class;
342: }
343:
344: //@Override
345: public IdentifiedObject create(final String code)
346: throws FactoryException {
347: return factory.createGeographicCRS(code);
348: }
349: }
350:
351: /**
352: * An implementation for {@link ProjectedCRS} objects.
353: *
354: * @version $Id: AuthorityFactoryProxy.java 26212 2007-07-12 02:34:18Z jgarnett $
355: * @author Martin Desruisseaux
356: */
357: private static final class Projected extends CRS {
358: protected Projected(final CRSAuthorityFactory factory) {
359: super (factory);
360: }
361:
362: //@Override
363: public Class getType() {
364: return ProjectedCRS.class;
365: }
366:
367: //@Override
368: public IdentifiedObject create(final String code)
369: throws FactoryException {
370: return factory.createProjectedCRS(code);
371: }
372: }
373: }
|