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.Map;
020: import java.util.HashSet;
021: import java.util.LinkedHashMap;
022: import java.util.IdentityHashMap;
023: import java.util.Collections;
024: import java.util.Iterator;
025: import java.io.Writer;
026: import java.io.IOException;
027: import java.awt.RenderingHints;
028: import javax.imageio.spi.ServiceRegistry;
029: import javax.imageio.spi.RegisterableService;
030:
031: // OpenGIS dependencies
032: import org.opengis.referencing.AuthorityFactory;
033:
034: // Geotools dependencies
035: import org.geotools.io.TableWriter;
036: import org.geotools.resources.Utilities;
037: import org.geotools.resources.i18n.Errors;
038: import org.geotools.resources.i18n.ErrorKeys;
039:
040: /**
041: * Skeletal implementation of factories. This base classe provides no {@code createFoo} methods,
042: * (they must be provided by subclasses), but provides two convenience features:
043: * <p>
044: * <ul>
045: * <li>An initially empty {@linkplain #hints map of hints} to be filled by subclasses
046: * constructors. They are the hints to be returned by {@link #getImplementationHints}.</li>
047: * <li>An automatic {@linkplain ServiceRegistry#setOrdering ordering} applied
048: * on the basis of subclasses-provided {@linkplain #priority} rank.</li>
049: * </ul>
050: * <p>
051: * When more than one factory implementation is
052: * {@linkplain ServiceRegistry#registerServiceProvider registered} for the same category (i.e. they
053: * implement the same {@link Factory} sub-interface), the actual instance to be used is selected
054: * according their {@linkplain ServiceRegistry#setOrdering ordering} and user-supplied
055: * {@linkplain Hints hints}. Hints have precedence. If more than one factory matches the hints
056: * (including the common case where the user doesn't provide any hint at all), then ordering
057: * matter.
058: * <p>
059: * The ordering is unspecified for every pairs of factories with the same {@linkplain #priority}.
060: * This implies that the ordering is unspecified between all factories created with the
061: * {@linkplain #AbstractFactory() default constructor}, since they all have the same
062: * {@linkplain #NORMAL_PRIORITY default priority} level.
063: *
064: * <h3>How hints are set</h3>
065: * Hints are used for two purposes. The distinction is important because the set
066: * of hints may not be identical in both cases:
067: * <p>
068: * <ol>
069: * <li>Hints are used for creating new factories.</li>
070: * <li>Hints are used in order to check if an <em>existing</em> factory is suitable.</li>
071: * </ol>
072: * <p>
073: * {@code AbstractFactory} do <strong>not</strong> provides any facility for the first case.
074: * Factories implementations shall inspect themselves all relevant hints supplied by the user,
075: * and pass them to any dependencies. Do <strong>not</strong> use the {@link #hints} field for
076: * that; use the hints provided by the user in the constructor. If all dependencies are created
077: * at construction time (<cite>constructor injection</cite>), there is no need to keep user's hints
078: * once the construction is finished.
079: * <p>
080: * The {@link #hints} field is for the second case only. Implementations shall copy in this
081: * field only the user's hints that are know to be relevant to this factory. If a hint is
082: * relevant but the user didn't specified any value, the hint key should be added to the
083: * {@link #hints} map anyway with a {@code null} value. Only direct dependencies shall be put
084: * in the {@link #hints} map. Indirect dependencies (i.e. hints used by other factories used
085: * by this factory) will be inspected automatically by {@link FactoryRegistry} in a recursive way.
086: * <p>
087: * <strong>Note:</strong> The lack of constructor expecting a {@link Map} argument is intentional.
088: * This is in order to discourage blind-copy of all user-supplied hints to the {@link #hints} map.
089: * <p>
090: * <strong>Example:</strong> Lets two factories, A and B. Factory A need an instance of Factory B.
091: * Factory A can be implemented as below:
092: *
093: * <table border='1'>
094: * <tr><th>Code</th><th>Observations</th></tr>
095: * <tr><td><blockquote><pre>
096: * class FactoryA extends AbstractFactory {
097: * FactoryB fb;
098: *
099: * FactoryA(Hints userHints) {
100: * fb = FactoryFinder.getFactoryB(userHints);
101: * hints.put(Hints.FACTORY_B, fb);
102: * }
103: * }
104: * </pre></blockquote></td>
105: * <td>
106: * <ul>
107: * <li>The user-supplied map ({@code userHints}) is never modified.</li>
108: * <li>All hints relevant to other factories are used in the constructor. Hints relevant to
109: * factory B are used when {@code FactoryFinder.getFactoryB(...)} is invoked.</li>
110: * <li>The {@code FactoryA} constructor stores only the hints relevant to {@code FactoryA}.
111: * Indirect dependencies (e.g. hints relevant to {@code FactoryB}) will be inspected
112: * recursively by {@link FactoryRegistry}.</li>
113: * <li>In the above example, {@link #hints} will never be used for creating new factories.</li>
114: * </ul>
115: * </td></tr></table>
116: *
117: * @since 2.1
118: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/metadata/src/main/java/org/geotools/factory/AbstractFactory.java $
119: * @version $Id: AbstractFactory.java 25421 2007-05-05 16:55:18Z desruisseaux $
120: * @author Martin Desruisseaux
121: */
122: public class AbstractFactory implements Factory, RegisterableService {
123: /**
124: * The minimum priority for a factory, which is {@value}. Factories with lowest priority
125: * will be used only if there is no other factory in the same
126: * {@linkplain ServiceRegistry#getCategories category}.
127: *
128: * @see #priority
129: * @see #onRegistration
130: */
131: public static final int MINIMUM_PRIORITY = 1;
132:
133: /**
134: * The default priority, which is {@value}.
135: *
136: * @see #priority
137: * @see #onRegistration
138: */
139: public static final int NORMAL_PRIORITY = 50;
140:
141: /**
142: * The maximum priority for a factory, which is {@value}. Factories with highest
143: * priority will be preferred to any other factory in the same
144: * {@linkplain ServiceRegistry#getCategories category}.
145: *
146: * @see #priority
147: * @see #onRegistration
148: */
149: public static final int MAXIMUM_PRIORITY = 100;
150:
151: /**
152: * The priority for this factory, as a number between {@link #MINIMUM_PRIORITY} and
153: * {@link #MAXIMUM_PRIORITY} inclusive. Priorities are used by {@link FactoryRegistry}
154: * for selecting a preferred factory when many are found for the same service.
155: *
156: * @see #getPriority
157: *
158: * @todo Consider deprecating this field. See
159: * <A HREF="http://jira.codehaus.org/browse/GEOT-1100">GEOT-1100</A> for details.
160: */
161: protected final int priority;
162:
163: /**
164: * The {@linkplain Factory#getImplementationHints implementation hints}. This map should be
165: * filled by subclasses at construction time. If possible, constructors should not copy blindly
166: * all user-provided hints. They should select only the relevant hints and resolve them as of
167: * {@linkplain Factory#getImplementationHints implementation hints} contract.
168: * <p>
169: * <b>Note:</b> This field is not an instance of {@link Hints} because:
170: * <ul>
171: * <li>The primary use of this map is to check if this factory can be reused.
172: * It is not for creating new factories.</li>
173: * <li>This map needs to allow null values, as of
174: * {@linkplain Factory#getImplementationHints implementation hints} contract.</li>
175: * </ul>
176: */
177: protected final Map hints = new LinkedHashMap();
178:
179: /**
180: * An unmodifiable view of {@link #hints}. This is the actual map to be returned
181: * by {@link #getImplementationHints}. Its content reflects the {@link #hints}
182: * map even if the later is modified.
183: */
184: private final Map/*<RenderingHints.Key,Object>*/unmodifiableHints = Collections
185: .unmodifiableMap(hints);
186:
187: /**
188: * Creates a new factory with the {@linkplain #NORMAL_PRIORITY default priority}.
189: */
190: protected AbstractFactory() {
191: this (NORMAL_PRIORITY);
192: }
193:
194: /**
195: * Constructs a factory with the specified priority.
196: *
197: * @param priority The priority for this factory, as a number between
198: * {@link #MINIMUM_PRIORITY} and {@link #MAXIMUM_PRIORITY} inclusive.
199: */
200: protected AbstractFactory(final int priority) {
201: this .priority = priority;
202: if (priority < MINIMUM_PRIORITY || priority > MAXIMUM_PRIORITY) {
203: throw new IllegalArgumentException(Errors.format(
204: ErrorKeys.ILLEGAL_ARGUMENT_$2, "priority",
205: new Integer(priority)));
206: }
207: }
208:
209: /**
210: * Returns the priority for this factory, as a number between {@link #MINIMUM_PRIORITY} and
211: * {@link #MAXIMUM_PRIORITY} inclusive. Priorities are used by {@link FactoryRegistry} for
212: * selecting a preferred factory when many are found for the same service. The default
213: * implementation returns {@link #priority} with no change. Subclasses should override
214: * this method if they want to return a higher or lower priority.
215: *
216: * @since 2.3
217: */
218: public int getPriority() {
219: return priority;
220: }
221:
222: /**
223: * Returns an {@linkplain Collections#unmodifiableMap unmodifiable} view of
224: * {@linkplain #hints}.
225: *
226: * @return The map of hints, or an empty map if none.
227: */
228: public Map getImplementationHints() {
229: return unmodifiableHints;
230: }
231:
232: /**
233: * Called when this factory is added to the given {@code category} of the given
234: * {@code registry}. The factory may already be registered under another category
235: * or categories.
236: * <p>
237: * This method is invoked automatically when this factory is registered as a plugin,
238: * and should not be invoked directly by the user. The default implementation iterates
239: * through all services under the same category that extends the {@code AbstractFactory}
240: * class, and set the ordering according the priority given at construction time.
241: *
242: * @param registry A service registry where this factory has been registered.
243: * @param category The registry category under which this object has been registered.
244: *
245: * @see #MINIMUM_PRIORITY
246: * @see #MAXIMUM_PRIORITY
247: */
248: public void onRegistration(final ServiceRegistry registry,
249: final Class category) {
250: for (final Iterator it = registry.getServiceProviders(category,
251: false); it.hasNext();) {
252: final Object provider = it.next();
253: if (provider != this && provider instanceof AbstractFactory) {
254: final AbstractFactory factory = (AbstractFactory) provider;
255: final int priority = getPriority();
256: final int compare = factory.getPriority();
257: final Object first, second;
258: if (priority > compare) {
259: first = this ;
260: second = factory;
261: } else if (priority < compare) {
262: first = factory;
263: second = this ;
264: } else {
265: continue; // No ordering
266: }
267: registry.setOrdering(category, first, second);
268: }
269: }
270: }
271:
272: /**
273: * Called when this factory is removed from the given {@code category} of the given
274: * {@code registry}. The object may still be registered under another category or categories.
275: * <p>
276: * This method is invoked automatically when this factory is no longer registered as a plugin,
277: * and should not be invoked directly by the user.
278: *
279: * @param registry A service registry from which this object is being (wholly or partially)
280: * deregistered.
281: * @param category The registry category from which this object is being deregistered.
282: */
283: public void onDeregistration(final ServiceRegistry registry,
284: final Class category) {
285: // No action needed.
286: }
287:
288: /**
289: * Returns a hash value for this factory. The default implementation computes the hash
290: * value using only immutable properties. This computation do <strong>not</strong> relies
291: * on {@linkplain #getImplementationHints implementation hints}, since there is no garantee
292: * that they will not change.
293: *
294: * @since 2.3
295: */
296: public final int hashCode() {
297: return getClass().hashCode() + (37 * priority);
298: }
299:
300: /**
301: * Compares this factory with the specified object for equality.
302: * The default implementation returns {@code true} if and only if:
303: * <p>
304: * <ul>
305: * <li>Both objects are of the exact same class
306: * (a <cite>is instance of</cite> relationship is not enough).</li>
307: * <li>{@linkplain #getImplementationHints implementation hints} are
308: * {@linkplain Map#equals equal}.</li>
309: * </ul>
310: * <p>
311: * The requirement for the <cite>exact same class</cite> is needed for consistency with the
312: * {@linkplain FactoryRegistry factory registry} working, since at most one instance of a given
313: * class {@linkplain FactoryRegistry#getServiceProviderByClass) is allowed} in a registry.
314: *
315: * @since 2.3
316: */
317: public final boolean equals(final Object object) {
318: if (object == this ) {
319: return true;
320: }
321: if (object != null && object.getClass().equals(getClass())) {
322: final AbstractFactory that = (AbstractFactory) object;
323: return this .priority == that.priority
324: && new FactoryComparator(this , that)
325: .compare(new HashSet());
326: }
327: return false;
328: }
329:
330: /**
331: * Returns a string representation of this factory. This method is mostly for debugging purpose,
332: * so the string format may vary across different implementations or versions. The default
333: * implementation formats all {@linkplain #getImplementationHints implementation hints} as a
334: * tree. If the implementation hints include some {@linkplain Factory factory} dependencies,
335: * then the implementation hints for those dependencies will appears under a tree branch.
336: *
337: * @since 2.3
338: */
339: public String toString() {
340: final String name = format(this );
341: final Map done = new IdentityHashMap(); // We don't want to rely on Factory.equals(...)
342: done.put(this , name);
343: final String tree = format(getImplementationHints(), done);
344: return name + System.getProperty("line.separator", "\n") + tree;
345: }
346:
347: /**
348: * Returns a string representation of the specified hints. This is used by
349: * {@link Hints#toString} in order to share the code provided in this class.
350: */
351: static String toString(final Map hints) {
352: return format(hints, new IdentityHashMap());
353: }
354:
355: /**
356: * Formats a name for the specified factory.
357: */
358: private static String format(final Factory factory) {
359: String name = Utilities.getShortClassName(factory);
360: if (factory instanceof AuthorityFactory) {
361: name = name
362: + "[\""
363: + ((AuthorityFactory) factory).getAuthority()
364: .getTitle() + "\"]";
365: }
366: return name;
367: }
368:
369: /**
370: * Formats the specified hints. This method is just the starting
371: * point for {@link #format(Writer, Map, String, Map)} below.
372: */
373: private static String format(final Map hints, final Map done) {
374: final Writer table;
375: try {
376: table = new TableWriter(null, " ");
377: format(table, hints, " ", done);
378: } catch (IOException e) {
379: // Should never happen, since we are writing in a buffer.
380: throw new AssertionError(e);
381: }
382: return table.toString();
383: }
384:
385: /**
386: * Formats recursively the tree. This method invoke itself.
387: */
388: private static void format(final Writer table, final Map hints,
389: final String indent, final Map/*<Object,String>*/done)
390: throws IOException {
391: final String[] keys = new String[hints.size()];
392: final Object[] values = new Object[keys.length];
393: for (final Iterator it = hints.entrySet().iterator(); it
394: .hasNext();) {
395: Map.Entry entry = (Map.Entry) it.next();
396: String key = String.valueOf(entry.getKey());
397: Object value = entry.getValue();
398: table.write(indent);
399: table.write(key);
400: table.write("\t= ");
401: Factory recursive = null;
402: if (value instanceof Factory) {
403: recursive = (Factory) value;
404: value = format(recursive);
405: final String previous = (String) done.put(recursive,
406: key);
407: if (previous != null) {
408: done.put(recursive, previous);
409: table.write("(same as "); // TODO: localize
410: table.write(previous);
411: value = ")";
412: recursive = null;
413: }
414: }
415: table.write(String.valueOf(value));
416: table.write('\n');
417: if (recursive != null) {
418: final String nextIndent = Utilities.spaces(indent
419: .length() + 2);
420: format(table, recursive.getImplementationHints(),
421: nextIndent, done);
422: }
423: }
424: }
425: }
|