001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2004-2006, 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: package org.geotools.geometry.jts;
017:
018: // J2SE dependencies
019: import java.lang.reflect.Array;
020: import java.util.Arrays;
021: import java.util.Set;
022:
023: // JTS dependencies
024: import com.vividsolutions.jts.geom.CoordinateSequenceFactory;
025: import com.vividsolutions.jts.geom.GeometryFactory;
026: import com.vividsolutions.jts.geom.PrecisionModel;
027:
028: // Geotools dependencies
029: import org.geotools.factory.Hints;
030: import org.geotools.factory.FactoryCreator;
031: import org.geotools.factory.FactoryRegistry;
032: import org.geotools.factory.FactoryRegistryException;
033: import org.geotools.resources.LazySet;
034:
035: /**
036: * Defines static methods used to access {@linkplain GeometryFactory geometry},
037: * {@linkplain CoordinateSequenceFactory coordinate sequence} or
038: * {@linkplain PrecisionModel precision model} factories.
039: *
040: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/main/src/main/java/org/geotools/geometry/jts/FactoryFinder.java $
041: * @version $Id: FactoryFinder.java 20703 2006-07-24 16:57:44Z jgarnett $
042: * @author Martin Desruisseaux
043: */
044: public class FactoryFinder {
045: /**
046: * The service registry for this manager.
047: * Will be initialized only when first needed.
048: */
049: private static FactoryRegistry registry;
050:
051: /**
052: * Do not allows any instantiation of this class.
053: */
054: private FactoryFinder() {
055: // singleton
056: }
057:
058: /**
059: * Returns the service registry. The registry will be created the first
060: * time this method is invoked.
061: */
062: private static FactoryRegistry getServiceRegistry() {
063: assert Thread.holdsLock(FactoryFinder.class);
064: if (registry == null) {
065: registry = new Registry();
066: }
067: return registry;
068: }
069:
070: /**
071: * Returns the first implementation of {@link GeometryFactory} matching the specified hints.
072: * If no implementation matches, a new one is created if possible or an exception is thrown
073: * otherwise.
074: * <p>
075: * Hints that may be understood includes
076: * {@link Hints#JTS_COORDINATE_SEQUENCE_FACTORY JTS_COORDINATE_SEQUENCE_FACTORY},
077: * {@link Hints#JTS_PRECISION_MODEL JTS_PRECISION_MODEL} and
078: * {@link Hints#JTS_SRID JTS_SRID}.
079: *
080: * @param hints An optional map of hints, or {@code null} if none.
081: * @return The first geometry factory that matches the supplied hints.
082: * @throws FactoryRegistryException if no implementation was found or can be created for the
083: * {@link GeometryFactory} category and the given hints.
084: */
085: public static synchronized GeometryFactory getGeometryFactory(
086: final Hints hints) throws FactoryRegistryException {
087: return (GeometryFactory) getServiceRegistry()
088: .getServiceProvider(GeometryFactory.class, null, hints,
089: Hints.JTS_GEOMETRY_FACTORY);
090: }
091:
092: /**
093: * Returns a set of all available implementations for the {@link GeometryFactory} category.
094: *
095: * @return Set of available geometry factory implementations.
096: */
097: public static synchronized Set getGeometryFactories() {
098: return new LazySet(getServiceRegistry().getServiceProviders(
099: GeometryFactory.class));
100: }
101:
102: /**
103: * Returns the first implementation of {@link PrecisionModel} matching the specified hints.
104: * If no implementation matches, a new one is created if possible or an exception is thrown
105: * otherwise.
106: *
107: * @param hints An optional map of hints, or {@code null} if none.
108: * @return The first precision model that matches the supplied hints.
109: * @throws FactoryRegistryException if no implementation was found or can be created for the
110: * {@link PrecisionModel} category and the given hints.
111: */
112: public static synchronized PrecisionModel getPrecisionModel(
113: final Hints hints) throws FactoryRegistryException {
114: return (PrecisionModel) getServiceRegistry()
115: .getServiceProvider(PrecisionModel.class, null, hints,
116: Hints.JTS_PRECISION_MODEL);
117: }
118:
119: /**
120: * Returns a set of all available implementations for the {@link PrecisionModel} category.
121: *
122: * @return Set of available precision model implementations.
123: */
124: public static synchronized Set getPrecisionModels() {
125: return new LazySet(getServiceRegistry().getServiceProviders(
126: PrecisionModel.class));
127: }
128:
129: /**
130: * Returns the first implementation of {@link CoordinateSequenceFactory} matching the specified
131: * hints. If no implementation matches, a new one is created if possible or an exception is
132: * thrown otherwise.
133: *
134: * @param hints An optional map of hints, or {@code null} if none.
135: * @return The first coordinate sequence factory that matches the supplied hints.
136: * @throws FactoryRegistryException if no implementation was found or can be created for the
137: * {@link CoordinateSequenceFactory} interface and the given hints.
138: */
139: public static synchronized CoordinateSequenceFactory getCoordinateSequenceFactory(
140: final Hints hints) throws FactoryRegistryException {
141: return (CoordinateSequenceFactory) getServiceRegistry()
142: .getServiceProvider(CoordinateSequenceFactory.class,
143: null, hints,
144: Hints.JTS_COORDINATE_SEQUENCE_FACTORY);
145: }
146:
147: /**
148: * Returns a set of all available implementations for the {@link CoordinateSequenceFactory}
149: * interface.
150: *
151: * @return Set of available coordinate sequence factory implementations.
152: */
153: public static synchronized Set getCoordinateSequenceFactories() {
154: return new LazySet(getServiceRegistry().getServiceProviders(
155: CoordinateSequenceFactory.class));
156: }
157:
158: /**
159: * Scans for factory plug-ins on the application class path. This method is
160: * needed because the application class path can theoretically change, or
161: * additional plug-ins may become available. Rather than re-scanning the
162: * classpath on every invocation of the API, the class path is scanned
163: * automatically only on the first invocation. Clients can call this
164: * method to prompt a re-scan. Thus this method need only be invoked by
165: * sophisticated applications which dynamically make new plug-ins
166: * available at runtime.
167: */
168: public static void scanForPlugins() {
169: if (registry != null) {
170: registry.scanForPlugins();
171: }
172: }
173:
174: /**
175: * A custom registry for JTS factories. There is usually no need for custom implementation of
176: * {@link ServiceRegistry} for geotools object. However, JTS factories are an other story
177: * because they don't know anything about the Geotools's factory plugin system. Consequently
178: * we need to process JTS factories in a special way.
179: */
180: private static final class Registry extends FactoryCreator {
181: /**
182: * Creates a registry for JTS factories.
183: */
184: public Registry() {
185: super (Arrays.asList(new Class[] { GeometryFactory.class,
186: PrecisionModel.class,
187: CoordinateSequenceFactory.class }));
188: }
189:
190: /**
191: * Creates a new instance of the specified factory using the specified hints.
192: *
193: * @param category The category to instantiate.
194: * @param implementation The factory class to instantiate.
195: * @param hints The implementation hints.
196: * @return The factory.
197: * @throws FactoryRegistryException if the factory creation failed.
198: */
199: protected Object createServiceProvider(final Class category,
200: final Class implementation, final Hints hints)
201: throws FactoryRegistryException {
202: if (GeometryFactory.class.isAssignableFrom(category)
203: && GeometryFactory.class.equals(implementation)) {
204: return new GeometryFactory(getPrecisionModel(hints),
205: getSRID(hints),
206: getCoordinateSequenceFactory(hints));
207: }
208: return super .createServiceProvider(category,
209: implementation, hints);
210: }
211:
212: /**
213: * Extracts the SRID from the hints, or returns {@code 0} if none.
214: */
215: private static int getSRID(final Hints hints) {
216: if (hints != null) {
217: final Integer SRID = (Integer) hints
218: .get(Hints.JTS_SRID);
219: if (SRID != null) {
220: return SRID.intValue();
221: }
222: }
223: return 0;
224: }
225:
226: /**
227: * Returns {@code true} if the specified {@code provider} meets the requirements specified
228: * by a map of {@code hints}. This method is invoked automatically when the {@code provider}
229: * is known to meets standard Geotools requirements.
230: * <p>
231: * This implementation add JTS-specific checks. More specifically, we checks if
232: * {@link GeometryFactory} uses the required {@link CoordinateSequenceFactory}
233: * and {@link PrecisionModel}.
234: *
235: * @param provider The provider to checks.
236: * @param category The factory's category.
237: * @param hints The user requirements.
238: * @return {@code true} if the {@code provider} meets the user requirements.
239: */
240: protected boolean isAcceptable(final Object provider,
241: final Class category, final Hints hints) {
242: if (GeometryFactory.class.isAssignableFrom(category)) {
243: final GeometryFactory factory = (GeometryFactory) provider;
244: final CoordinateSequenceFactory sequence = factory
245: .getCoordinateSequenceFactory();
246: final PrecisionModel precision = factory
247: .getPrecisionModel();
248: if (!isAcceptable(sequence, hints
249: .get(Hints.JTS_COORDINATE_SEQUENCE_FACTORY))
250: || !isAcceptable(precision, hints
251: .get(Hints.JTS_PRECISION_MODEL))) {
252: return false;
253: }
254: final int SRID = getSRID(hints);
255: if (SRID != 0 && SRID != factory.getSRID()) {
256: return false;
257: }
258: }
259: return super .isAcceptable(provider, category, hints);
260: }
261:
262: /**
263: * Checks if an actual {@link GeometryFactory} property matches the given hint.
264: *
265: * @param actual The geometry factory property, either a {@link CoordinateSequenceFactory}
266: * or a {@link PrecisionModel} concrete implementation.
267: * @param requested The user's hint, either a concrete implementation of the same class
268: * than {@code actual}, a {@link Class} or an array of them.
269: * @return {@code true} if the {@code actual} value matches the {@code requested}
270: * value, or {@code false} otherwise.
271: */
272: private static boolean isAcceptable(final Object actual,
273: final Object requested) {
274: /*
275: * If the user didn't provided any hint, or if the factory
276: * already uses the requested object, accepts.
277: */
278: if (requested == null || requested.equals(actual)) {
279: return true;
280: }
281: /*
282: * If hint is an array (either Class object or concrete
283: * implementations), iterates over all array's elements.
284: */
285: if (requested.getClass().isArray()) {
286: final int length = Array.getLength(requested);
287: for (int i = 0; i < length; i++) {
288: if (!isAcceptable(actual, Array.get(requested, i))) {
289: return false;
290: }
291: }
292: return true;
293: }
294: /*
295: * If hint is only a class instead of an actual implementation,
296: * accepts instances of this class or any subclasses.
297: */
298: if (actual != null && requested instanceof Class) {
299: return ((Class) requested).isAssignableFrom(actual
300: .getClass());
301: }
302: return false;
303: }
304: }
305: }
|