001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2005-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.factory;
017:
018: // J2SE dependencies
019: import java.util.*;
020: import java.lang.ref.Reference;
021: import java.lang.ref.WeakReference;
022: import java.lang.reflect.Modifier;
023: import java.lang.reflect.InvocationTargetException;
024: import javax.imageio.spi.ServiceRegistry; // For javadoc
025:
026: // Geotools dependencies
027: import org.geotools.resources.Utilities;
028: import org.geotools.resources.i18n.Errors;
029: import org.geotools.resources.i18n.ErrorKeys;
030:
031: /**
032: * A {@linkplain FactoryRegistry factory registry} capable to creates factories if no appropriate
033: * instance was found in the registry.
034: * <p>
035: * This class maintains a cache of previously created factories, as {@linkplain WeakReference
036: * weak references}. Calls to {@link #getServiceProvider getServiceProvider} first check if a
037: * previously created factory can fit.
038: *
039: * @since 2.1
040: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/metadata/src/main/java/org/geotools/factory/FactoryCreator.java $
041: * @version $Id: FactoryCreator.java 29058 2008-02-03 17:47:07Z desruisseaux $
042: * @author Martin Desruisseaux
043: * @author Jody Garnett
044: */
045: public class FactoryCreator extends FactoryRegistry {
046: /**
047: * The array of classes for searching the one-argument constructor.
048: */
049: private static final Class[] HINTS_ARGUMENT = new Class[] { Hints.class };
050:
051: /**
052: * List of factories already created. Used as a cache.
053: */
054: private final Map/*<Class, List<Reference>>*/cache = new HashMap();
055:
056: /**
057: * Objects under construction for each implementation class.
058: * Used by {@link #safeCreate} as a guard against infinite recursivity.
059: */
060: private final Set/*<Class>*/underConstruction = new HashSet();
061:
062: /**
063: * Constructs a new registry for the specified category.
064: *
065: * @param category The single category.
066: *
067: * @since 2.4
068: */
069: public FactoryCreator(final Class category) {
070: super (category);
071: }
072:
073: /**
074: * Constructs a new registry for the specified categories.
075: *
076: * @param categories The categories.
077: *
078: * @since 2.4
079: */
080: public FactoryCreator(final Class[] categories) {
081: super (categories);
082: }
083:
084: /**
085: * Constructs a new registry for the specified categories.
086: *
087: * @param categories The categories.
088: */
089: public FactoryCreator(final Collection categories) {
090: super (categories);
091: }
092:
093: /**
094: * Returns the providers available in the cache. To be used by {@link FactoryRegistry}.
095: */
096: final List/*<Reference>*/getCachedProviders(final Class category) {
097: List c = (List) cache.get(category);
098: if (c == null) {
099: c = new LinkedList();
100: cache.put(category, c);
101: }
102: return c;
103: }
104:
105: /**
106: * Caches the specified factory under the specified category.
107: */
108: private void cache(final Class category, final Object factory) {
109: getCachedProviders(category).add(new WeakReference(factory));
110: }
111:
112: /**
113: * Returns a provider for the specified category, using the specified map of hints (if any).
114: * If a provider matching the requirements is found in the registry, it is returned. Otherwise,
115: * a new provider is created and returned. This creation step is the only difference between
116: * this method and the {@linkplain FactoryRegistry#getServiceProvider super-class method}.
117: *
118: * @param category The category to look for.
119: * @param filter An optional filter, or {@code null} if none.
120: * @param hints A {@linkplain Hints map of hints}, or {@code null} if none.
121: * @param key The key to use for looking for a user-provided instance in the hints, or
122: * {@code null} if none.
123: * @return A factory for the specified category and hints (never {@code null}).
124: * @throws FactoryNotFoundException if no factory was found, and the specified hints don't
125: * provide suffisient information for creating a new factory.
126: * @throws FactoryRegistryException if the factory can't be created for some other reason.
127: */
128: public Object getServiceProvider(final Class category,
129: final Filter filter, final Hints hints, final Hints.Key key)
130: throws FactoryRegistryException {
131: final FactoryNotFoundException notFound;
132: try {
133: return super .getServiceProvider(category, filter, hints,
134: key);
135: } catch (FactoryNotFoundException exception) {
136: // Will be rethrown later in case of failure to create the factory.
137: notFound = exception;
138: }
139: /*
140: * No existing factory found. Creates one using reflection. First, we
141: * check if an implementation class was explicitly specified by the user.
142: */
143: final Class[] types;
144: if (hints == null || key == null) {
145: types = null;
146: } else {
147: final Object hint = hints.get(key);
148: if (hint == null) {
149: types = null;
150: } else {
151: if (hint instanceof Class[]) {
152: types = (Class[]) hint;
153: } else {
154: types = new Class[] { (Class) hint };
155: // Should not fails, since non-class argument should
156: // have been accepted by 'getServiceProvider(...)'.
157: }
158: final int length = types.length;
159: for (int i = 0; i < length; i++) {
160: final Class type = types[i];
161: if (type != null && category.isAssignableFrom(type)) {
162: final int modifiers = type.getModifiers();
163: if (!Modifier.isAbstract(modifiers)) {
164: final Object candidate = createSafe(
165: category, type, hints);
166: if (candidate == null) {
167: continue;
168: }
169: if (isAcceptable(candidate, category,
170: hints, filter)) {
171: cache(category, candidate);
172: return candidate;
173: }
174: dispose(candidate);
175: }
176: }
177: }
178: }
179: }
180: /*
181: * No implementation hint provided. Search the first implementation
182: * accepting a Hints argument. No-args constructor will be ignored.
183: * Note: all Factory objects should be fully constructed by now,
184: * since the super-class has already iterated over all factories.
185: */
186: for (final Iterator it = getUnfilteredProviders(category); it
187: .hasNext();) {
188: final Object factory = it.next();
189: final Class implementation = factory.getClass();
190: if (types != null && !isTypeOf(types, implementation)) {
191: continue;
192: }
193: if (filter != null && !filter.filter(factory)) {
194: continue;
195: }
196: final Object candidate;
197: try {
198: candidate = createSafe(category, implementation, hints);
199: } catch (FactoryRegistryException exception) {
200: if (exception.getCause() instanceof NoSuchMethodException) {
201: // No public constructor with the expected argument.
202: // Try an other implementation.
203: continue;
204: } else {
205: // Other kind of error, probably unexpected.
206: // Let the exception propagates.
207: throw exception;
208: }
209: }
210: if (candidate == null) {
211: continue;
212: }
213: if (isAcceptable(candidate, category, hints, filter)) {
214: cache(category, candidate);
215: return candidate;
216: }
217: dispose(candidate);
218: }
219: throw notFound;
220: }
221:
222: /**
223: * Returns {@code true} if the specified implementation is one of the specified types.
224: */
225: private static boolean isTypeOf(final Class[] types,
226: final Class implementation) {
227: for (int i = 0; i < types.length; i++) {
228: if (types[i].isAssignableFrom(implementation)) {
229: return true;
230: }
231: }
232: return false;
233: }
234:
235: /**
236: * Invokes {@link #createServiceProvider}, but checks against recursive calls. If the specified
237: * implementation is already under construction, returns {@code null} in order to tell to
238: * {@link #getServiceProvider} that it need to search for an other implementation. This is
239: * needed for avoiding infinite recursivity when a factory is a wrapper around an ther factory
240: * of the same category. For example this is the case of
241: * {@link org.geotools.referencing.operation.BufferedCoordinateOperationFactory}.
242: */
243: private Object createSafe(final Class category,
244: final Class implementation, final Hints hints) {
245: if (!underConstruction.add(implementation)) {
246: return null;
247: }
248: try {
249: return createServiceProvider(category, implementation,
250: hints);
251: } finally {
252: if (!underConstruction.remove(implementation)) {
253: throw new AssertionError();
254: }
255: }
256: }
257:
258: /**
259: * Creates a new instance of the specified factory using the specified hints.
260: * The default implementation tries to instantiate the given implementation class
261: * using the first of the following constructor found:
262: * <p>
263: * <ul>
264: * <li>Constructor with a single {@link Hints} argument.</li>
265: * <li>No-argument constructor.</li>
266: * </ul>
267: *
268: * @param category The category to instantiate.
269: * @param implementation The factory class to instantiate.
270: * @param hints The implementation hints.
271: * @return The factory.
272: * @throws FactoryRegistryException if the factory creation failed.
273: */
274: protected Object createServiceProvider(final Class category,
275: final Class implementation, final Hints hints)
276: throws FactoryRegistryException {
277: Throwable cause;
278: try {
279: try {
280: return implementation.getConstructor(HINTS_ARGUMENT)
281: .newInstance(new Object[] { hints });
282: } catch (NoSuchMethodException exception) {
283: // Constructor do not exists or is not public. We will fallback on the no-arg one.
284: cause = exception;
285: }
286: try {
287: return implementation.getConstructor((Class[]) null)
288: .newInstance((Object[]) null);
289: } catch (NoSuchMethodException exception) {
290: // No constructor accessible. Do not store the cause (we keep the one above).
291: }
292: } catch (IllegalAccessException exception) {
293: cause = exception; // constructor is not public (should not happen)
294: } catch (InstantiationException exception) {
295: cause = exception; // The class is abstract
296: } catch (InvocationTargetException exception) {
297: cause = exception.getCause(); // Exception in constructor
298: if (cause instanceof FactoryRegistryException) {
299: throw (FactoryRegistryException) cause;
300: }
301: }
302: throw new FactoryRegistryException(Errors.format(
303: ErrorKeys.CANT_CREATE_FACTORY_$1, Utilities
304: .getShortName(implementation)), cause);
305: }
306:
307: /**
308: * Dispose the specified factory after. This method is invoked when a factory has been
309: * created, and then {@code FactoryCreator} determined that the factory doesn't meet
310: * user's requirements.
311: */
312: private static void dispose(final Object factory) {
313: // Empty for now. This method is merely a reminder for disposal in future Geotools versions.
314: }
315: }
|