/* * Geotoolkit.org - An Open Source Java GIS Toolkit * http://www.geotoolkit.org * * (C) 2002-2012, Open Source Geospatial Foundation (OSGeo) * (C) 2009-2012, Geomatys * * 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. */ package org.geotoolkit.factory; import java.util.Map; import java.util.Set; import java.util.Date; import java.util.HashSet; import java.util.Iterator; import java.util.Collections; import java.util.LinkedHashMap; import java.util.IdentityHashMap; import java.io.Writer; import java.io.IOException; import java.awt.RenderingHints; import javax.imageio.spi.ServiceRegistry; import org.opengis.util.InternationalString; import org.opengis.referencing.AuthorityFactory; import org.opengis.metadata.citation.Citation; import org.opengis.metadata.quality.ConformanceResult; import org.geotoolkit.io.TableWriter; import org.apache.sis.util.CharSequences; import org.apache.sis.util.logging.Logging; import org.apache.sis.util.Classes; import org.geotoolkit.resources.Descriptions; import org.geotoolkit.resources.Vocabulary; import org.geotoolkit.resources.Errors; /** * Base class for Geotoolkit.org factories. {@link FactoryRegistry} handles {@code Factory} * subclasses specially, but extending this class is not mandatory ({@code FactoryRegistry} * will work with arbitrary implementations as well). This base class provides some convenience * features for subclasses: * <p> * <ul> * <li>An initially empty map of {@linkplain #hints hints} to be filled by subclasses * constructors.</li> * <li>An opportunity to said whatever this factory {@linkplain #availability is available}.</li> * <li>An opportunity to {@linkplain #setOrdering set the ordering} of this factory relative * to other factories.</li> * <li>An opportunity to {@linkplain #dispose dispose} resources.</li> * </ul> * * {@section How hints are set} * Hints are used for two purposes. The distinction is important because the set * of hints may not be identical in both cases: * <p> * <ol> * <li>Hints are used for creating new factories.</li> * <li>Hints are used in order to check if an <em>existing</em> factory is suitable.</li> * </ol> * <p> * This {@code Factory} base class does <strong>not</strong> provide any facility for the first * case. Subclasses shall inspect themselves all relevant hints supplied by the user, and pass them * to any dependencies. Do <strong>not</strong> use the {@link #hints} field for that; use the hints * provided by the user in the constructor. If all dependencies are created at construction time * (<cite>constructor injection</cite>), there is no need to keep all user's hints once the * construction is finished. * <p> * The {@link #hints} field is for the second case only. Implementations shall copy in this field * only the user's hints that are know to be relevant to this factory. If a hint is relevant but * the user didn't specified any value, the hint key should be added to the {@link #hints} map * anyway with a {@code null} value. Only direct dependencies shall be put in the {@link #hints} * map. Indirect dependencies (i.e. hints used by other factories used by this factory) will be * inspected automatically by {@link FactoryRegistry} in a recursive way. * * {@section Guidline for subclasses} * <ul> * <li>Provide a public no-arguments constructor.</li> * <li>In addition, it is recommended that implementations provide a constructor expecting * a single {@link Hints} argument. This optional argument gives to the user some control * over the factory's low-level details. The amount of control is factory specific.</li> * <li>The lack of constructor expecting a {@link Map} argument is intentional. This is in * order to discourage blind-copy of all user-supplied hints to the {@link #hints} map.</li> * <li>Add the fully qualified name of the <u>implementation</u> class in the * {@code META-INF/services/}<var>classname</var> file where <var>classname</var> * is the fully qualified name of the service <u>interface</u>.</li> * <li>The factory implementations will be discovered when * {@link FactoryRegistry#scanForPlugins} will be invoked.</li> * </ul> * * {@section Example} * An application supplied a {@linkplain Hints#DATUM_FACTORY datum factory hint}, being passed to a * {@linkplain org.opengis.referencing.datum.DatumAuthorityFactory datum authority factory} so that * all datum created from an authority code will come from the supplied datum factory. * * <blockquote> * <b>Java code</b> * {@preformat java * class DatumAuthorityFactory extends Factory { * final DatumFactory datumFactory; * * DatumAuthorityFactory() { * this(EMPTY_HINTS); * } * * DatumAuthorityFactory(Hints userHints) { * datumFactory = FactoryFinder.getDatumFactory(userHints); * hints.put(Hints.DATUM_FACTORY, datumFactory); * } * } * } * * <b>Remarks</b><br> * <ul> * <li>The map of ({@code userHints}) is never modified, since it is supplied by the user.</li> * <li>All hints relevant to other factories are used in the constructor. Hints relevant to * {@code DatumFactory} are used when {@code FactoryFinder.getDatumFactory(...)} is invoked.</li> * <li>The {@code DatumAuthorityFactory} constructor stores only the hints relevant to it. * Indirect dependencies (e.g. hints relevant to {@code DatumFactory}) will be inspected * recursively by {@link FactoryRegistry}.</li> * <li>In the above example, {@link #hints} will never be used for creating new factories.</li> * </ul></blockquote> * <p> * As seen in those examples this concept of a hint becomes more interesting when * the operation being controlled is discovery of other services used by the factory. * By supplying appropriate hints one can chain together several factories and retarget * them to an application specific task. * * @author Ian Schneider * @author Martin Desruisseaux (IRD, Geomatys) * @author Jody Garnett (Refractions) * @version 3.03 * * @see Hints * @see FactoryRegistry * * @since 2.1 * @module */ public abstract class Factory { /** * An internal hint used for keeping trace of factories that has been processed. */ private static final Hints.Key DONE = new Hints.Key(Set.class); /** * An internal hint meaning that a factory has been disposed. * The value is the disposal time, used for debugging purpose. */ @SuppressWarnings("serial") private static final Hints.Key DISPOSED = new Hints.Key(Date.class) { @Override public String toString() { return "DISPOSED"; } }; /** * An immutable empty set of hints. This is different than a {@code null} hints, which means * <em>default</em> hints and may not be empty. * * @since 3.00 */ public static final Hints EMPTY_HINTS = new EmptyHints(); /** * The {@linkplain #getImplementationHints implementation hints}. This map should be filled by * subclasses at construction time. If possible, constructors should not copy blindly all * user-provided hints. They should select only the relevant hints and resolve them as of * {@linkplain #getImplementationHints implementation hints} contract. For example if a * {@linkplain org.opengis.referencing.datum.DatumAuthorityFactory datum authority factory} * uses an ordinary {@linkplain org.opengis.referencing.datum.DatumFactory datum factory}, * its method could be implemented as below (note that we should not check if the datum * factory is null, since key with null value is the expected behavior in this case). * * {@preformat java * hints.put(Hints.DATUM_FACTORY, datumFactory); * } * * This field is not an instance of {@code Hints} because: * <ul> * <li>The primary use of this map is to check if this factory can be reused. * It is not for creating new factories.</li> * <li>This map needs to allow {@code null} values, as of * {@link #getImplementationHints} contract.</li> * </ul> */ protected final Map<RenderingHints.Key, Object> hints; /** * An unmodifiable view of {@link #hints}. This is the actual map to be returned * by {@link #getImplementationHints}. Its content reflects the {@link #hints} * map even if the later is modified. */ private final Map<RenderingHints.Key, Object> unmodifiableHints; /** * Creates a new factory instance. */ protected Factory() { hints = new LinkedHashMap<>(); unmodifiableHints = Collections.unmodifiableMap(hints); } /** * Controls the ordering of the enclosing {@linkplain Factory factory} relative to other * factories. An instance of this class is passed to the * {@link Factory#setOrdering Factory.setOrdering(Organizer)} method * after the enclosing factory has been registered in {@link FactoryRegistry}. That * {@code setOrdering} method can invoke any method in this class in order to specify whatever * the encloding factory should be selected {@linkplain #before(Class, boolean) before} * or {@linkplain #after(Class, boolean) after} an other factory. * * @author Martin Desruisseaux (Geomatys) * @version 3.03 * * @since 3.00 * @module */ protected final class Organizer { /** * The factory registry that created this {@code Organizer}. */ private final ServiceRegistry registry; /** * The category for which to set ordering. */ private final Class<?> category; /** * Creates a new organizer. */ Organizer(final ServiceRegistry registry, final Class<?> category) { this.registry = registry; this.category = category; } /** * Implementation of {@link #before(Class, boolean)} and {@link #after(Class, boolean)}. * The {@code before} boolean argument tells which method is invoking this one. */ @SuppressWarnings("unchecked") private void setOrdering(final Class<?> type, final boolean subclasses, final boolean before) { final Factory factory = Factory.this; assert category.isInstance(factory); if (!subclasses && !category.isAssignableFrom(type)) { throw new ClassCastException(Errors.format( Errors.Keys.IllegalClass_2, type, category)); } /* * The 'exclude' below is the base type of factory to not order, or null if none. * Usually this is null. However there is one special case: if the user specified * a type which is the same class or a parent class of the factory class, then we * have a contradiction: the factory can not be before (or after) itself. * * We could have limited the exclusion to exactly the factory class, which is * - strictly speaking - the only contradiction. However we extend the exclusion * to factory subclasses as well on the assumption that if the caller gave a parent * class, he probably means "put before (or after) every factory that are not of my * kind". This is what HTTP and URN AuthorityFactories mean for instance. * * This behavior is not documented for now, so it may be revised in the future. */ final Class<?> exclude = type.isInstance(factory) ? factory.getClass() : null; @SuppressWarnings("rawtypes") final Class category = (Class) this.category; // Intentionnaly unchecked. final Iterator<?> it; if (subclasses) { it = registry.getServiceProviders(category, false); } else { it = Collections.singleton(registry.getServiceProviderByClass(type)).iterator(); } while (it.hasNext()) { final Object other = it.next(); if (type.isInstance(other) && (exclude == null || !exclude.isInstance(other))) { if (before) { registry.setOrdering(category, factory, other); } else { registry.setOrdering(category, other, factory); } } } } /** * Implementation of {@link #before(String, boolean)} and {@link #after(String, boolean)}. * The {@code before} boolean argument tells which method is invoking this one. */ private void setOrdering(final String type, final boolean subclasses, final boolean before) { final Class<?> c; try { c = Class.forName(type); } catch (ClassNotFoundException e) { Logging.recoverableException(null, Organizer.class, before ? "before" : "after", e); return; } setOrdering(c, subclasses, before); } /** * Specifies that the enclosing factory should be selected before the specified factory. * In other words, the enclosing factory should have precedence over the given one. * <p> * If {@code subclasses} is {@code false}, then this method searches for a factory of * exactly the given class. Invoking this method has no effect on the ordering relative * to subclasses of the given factory class, unless {@code subclasses} is {@code true}. * * @param type The class of the factory which should be selected after the enclosing one. * @param subclasses {@code false} if the ordering should apply to the given class only, * or {@code true} if it should apply to subclasses of {@code type} as well. */ public void before(final Class<?> type, final boolean subclasses) { setOrdering(type, subclasses, true); } /** * Specifies that the enclosing factory should be selected before the specified factory. * This method delegates to {@link #before(Class, boolean)}, or does nothing if no class * are found for the given fully-qualified name. This variant is useful when a class may * or may not be on the classpath. * * @param type The fully-qualified name of the factory which should be selected after * the enclosing one. * @param subclasses {@code false} if the ordering should apply to the given class only, * or {@code true} if it should apply to subclasses of {@code type} as well. */ public void before(final String type, final boolean subclasses) { setOrdering(type, subclasses, true); } /** * Specifies that the enclosing factory should be selected after the specified factory. * In other words, the given factory should have precedence over the enclosing one. * <p> * If {@code subclasses} is {@code false}, then this method searches for a factory of * exactly the given class. Invoking this method has no effect on the ordering relative * to subclasses of the given factory class, unless {@code subclasses} is {@code true}. * * @param type The class of the factory which should be selected before the enclosing one. * @param subclasses {@code false} if the ordering should apply to the given class only, * or {@code true} if it should apply to subclasses of {@code type} as well. */ public void after(final Class<?> type, final boolean subclasses) { setOrdering(type, subclasses, false); } /** * Specifies that the enclosing factory should be selected after the specified factory. * This method delegates to {@link #after(Class, boolean)}, or does nothing if no class * are found for the given fully-qualified name. This variant is useful when a class may * or may not be on the classpath. * * @param type The fully-qualified name of the factory which should be selected before * the enclosing one. * @param subclasses {@code false} if the ordering should apply to the given class only, * or {@code true} if it should apply to subclasses of {@code type} as well. */ public void after(final String type, final boolean subclasses) { setOrdering(type, subclasses, false); } } /** * Invoked by {@link FactoryRegistry} after registration in order to set the relative ordering. * The default implementation does nothing. Subclasses should override this method and invoke * {@link Organizer#before(Class, boolean) Organizer.before(...)} or * {@link Organizer#after(Class, boolean) Organizer.after(...)} if they know which factory * should have precedence over an other factory. * * @param organizer A handler given by {@link FactoryRegistry} for controlling the ordering * of this factory relative to other factories. * * @since 3.00 */ protected void setOrdering(final Organizer organizer) { } /** * Returns an {@linkplain Collections#unmodifiableMap unmodifiable} view of {@linkplain #hints} * used by this factory to customize its use. This map is <strong>not</strong> guaranteed * to contains all the hints supplied by the user; it may be only a subset. Consequently, * hints provided here are usually not suitable for creating new factories. * <p> * The primary purpose of this method is to determine if an <strong>existing</strong> * factory instance can be reused for a set of user-supplied hints. This method is invoked by * {@link FactoryRegistry} in order to compare this factory's hints against user's hints. * This is dependency introspection only; {@code FactoryRegistry} <strong>never</strong> * invokes this method for creating new factories. * * {@section Content} * Keys are usually static constants from the {@link Hints} class, while values are * instances of some key-dependent class. The {@linkplain Map#keySet key set} must contains * at least all hints impacting functionality. While the key set may contains all hints * supplied by the user, it is recommended to limit the set to only the hints used by this * particular factory instance. A minimal set will helps {@link FactoryRegistry} to compare * only hints that matter and avoid the creation of unnecessary instances of this factory. * <p> * The hint values may be different than the one supplied by the user. If a user supplied a * hint as a {@link Class} object, this method shall replace it by the actual instance used, * if possible. * * {@section Lifetime} * If possible, do not keep a reference to the returned map or map entries for a long time. * The map may reference resources to make elligible for garbage collection at a later time. * This is especially true for factories created by {@link DynamicFactoryRegistry}. * * @return The map of hints, or an empty map if none. */ public Map<RenderingHints.Key, ?> getImplementationHints() { return unmodifiableHints; } /** * Returns {@code true} if this factory meets the requirements specified by a map of hints. The * default implementation checks if, for any key in the given map which is also presents in this * factory's {@linkplain #getImplementationHints implementation hints}, the corresponding values * are compatible. Values are considered compatible if they meet one of the following conditions: * <p> * <ul> * <li>The values are {@linkplain Object#equals equal}</li> * <li>The value in the {@code hint} map is a {@link Class} or an array of classes, and the * value in this factory's implementation hints is an instance of at least one of those * classes.</li> * </ul> * <p> * If this factory has dependencies toward other factories (or to be more specific, if the * values of some {@linkplain #getImplementationHints implementation hints} are instances * of {@code Factory}), then this method will invokes itself recursively for those factory * instances, with a guard against infinite loops if there is cyclic dependencies. * * @param hints The user requirements (never {@code null}). * @return {@code true} if this factory meets the hints requirements. * * @since 3.00 */ protected boolean hasCompatibleHints(final Hints hints) { /* * Do not synchronize this method. Experience show that it is deadlock-prone. * Most subclasses have already synchronized getImplementationHints() if they * needed to do so. Keeping this method non-synchronized is safe if subclasses * do not modify their hints after initialization (which is the expected behavior). */ final Map<RenderingHints.Key, ?> implementationHints = getImplementationHints(); if (implementationHints == null) { // Factory was bad and did not meet contract - assume it used no Hints. return true; } if (implementationHints.containsKey(DISPOSED)) { // Factory has been disposed, so it is not available anymore for any use. return false; } Hints remaining = null; @SuppressWarnings("unchecked") Set<Factory> alreadyDone = (Set<Factory>) hints.get(DONE); for (final Map.Entry<?,?> entry : implementationHints.entrySet()) { final Object key = entry.getKey(); final Object value = entry.getValue(); final Object expected = hints.get(key); if (expected != null) { /* * We have found a hint that matter. Checks its value as described in * the javadoc. Any mismatch cause immediate termination of this method. */ if (expected instanceof Class<?>) { if (!((Class<?>) expected).isInstance(value)) { return false; } } else if (expected instanceof Class<?>[]) { final Class<?>[] types = (Class<?>[]) expected; int i = 0; do if (i >= types.length) return false; while (!types[i++].isInstance(value)); } else if (!expected.equals(value)) { return false; } } /* * Checks recursively in factory dependencies, if any. Note that the dependencies * will be checked against a subset of user's hints. More specifically, all hints * processed by the current pass will NOT be passed to the factories dependencies. * This is because the same hint may appears in the "parent" factory and a "child" * dependency with different value. For example the FORCE_LONGITUDE_FIRST_AXIS_ORDER * hint has the value TRUE in OrderedAxisAuthorityFactory, but the later is basically * a wrapper around the EPSG Factory (typically), which has the value FALSE for the * same hint. * * Additional note: The 'alreadyDone' set is a safety against cyclic dependencies, * in order to protect ourself against never-ending loops. This is not the same * kind of dependencies than 'FactoryRegistry.testingHints'. It is a "factory A * depends on factory B which depends on factory A" loop, which is legal. */ if (value instanceof Factory) { final Factory dependency = (Factory) value; if (alreadyDone != null && alreadyDone.contains(dependency)) { // The dependency has already been processed. Executing the // remainder of this code would cause an infinite loop. continue; } if (remaining == null) { remaining = hints.clone(); remaining.keySet().removeAll(implementationHints.keySet()); if (remaining.isEmpty()) { // No need to perform any additional checks, so do not execute the // remaining lines in order to avoid the creation of useless objects. continue; } if (alreadyDone == null) { alreadyDone = new HashSet<>(); if (remaining.put(DONE, alreadyDone) != null) { throw new AssertionError(); // Paranoiac check (should never happen). } } if (!alreadyDone.add(this)) { throw new AssertionError(); // Paranoiac check (should never happen). } } /* * Recursive call to this method for scanning dependencies. Useful only if * there is at least 1 remaining user hint, not counting the internal DONE * hint which has been added to the map. */ assert remaining.isEmpty() || remaining.containsKey(DONE); if (remaining.size() > 1) { assert alreadyDone.contains(this); if (!dependency.hasCompatibleHints(remaining)) { return false; } } } } return true; } /** * Returns whatever this factory is ready for use. The {@link ConformanceResult#pass} method * shall returns {@code false} if this factory is not available in the current configuration, * typically because some external resources were not found. Implementors are encouraged to * provide an {@linkplain ConformanceResult#getExplanation() explanation} when the factory * is not available. * <p> * This method can <strong>not</strong> be used as a manager for automatic download of external * resources. It is just a way to tell to {@link FactoryRegistry} that this factory exists, but * can't do its job for whatever reasons, so {@code FactoryRegistry} has to choose an other * factory. In other words, this method is used only as a filter. * <p> * Note also that {@code FactoryRegistry} is not designed for factories with intermittent state * (i.e. value of {@code availability().pass()} varying with time). The behavior is undetermined * in such case. * <p> * In the default implementation, the conformance result passes as long as this factory * has not been {@linkplain #dispose disposed}. * * @return A conformance result with {@code pass() == true} if this factory is ready for use. * * @since 3.03 */ public ConformanceResult availability() { return new Availability(); } /** * The default conformance result returned by {@link Factory#availability()}. This class * is <cite>live</cite>, i.e. the {@link #pass()} method will return {@code false} if the * enclosing factory has been {@linkplain Factory#dispose disposed} without the need to * create a new instance of {@code Availability}. * * @author Martin Desruisseaux (Geomatys) * @version 3.14 * * @since 3.03 * @module */ protected class Availability implements ConformanceResult { /** * The value returned by {@link #getExplanation()}. * Will be created only when first needed. */ private InternationalString explanation; /** * If the factory is not available because of some exception, * the exception that caused the failure. Otherwise {@code null}. */ private final Throwable failureCause; /** * Creates a default {@code Availability} object. The new conformance result * will {@linkplain #pass() pass} as long as the enclosing factory has not * been disposed. */ public Availability() { failureCause = null; } /** * Creates a conformance result which declares that the factory is not * available because of the given exception. * * @param failureCause The raison why the factory is not available. */ public Availability(final Throwable failureCause) { this.failureCause = failureCause; } /** * Returns the requirement against which the factory is being evaluated. * The default implementation returns {@code null}, which is a departure * from ISO 19115 since this information is supposed to be mandatory. * Subclasses are encouraged to provide a value. */ @Override public Citation getSpecification() { return null; } /** * Returns an explanation of the meaning of conformance for this result. If this * {@code Availability} object has been constructed with a {@link Throwable}, then this * method returns {@code "Error"} completed with the message of the <em>last</em> cause * having a {@linkplain Throwable#getLocalizedMessage() localized message} (we pickup * the last cause on the assumption that it is the root of the problem). Otherwise this * method returns a text like "<cite>This result indicates if the factory is available * for use</cite>". */ @Override public synchronized InternationalString getExplanation() { if (explanation == null) { String message = null; for (Throwable cause=failureCause; cause!=null; cause=cause.getCause()) { final String candidate = cause.getLocalizedMessage(); if (candidate != null) { message = candidate; } } if (message != null) { explanation = Vocabulary.formatInternational(Vocabulary.Keys.Error_1, message); } else { explanation = Descriptions.formatInternational(Descriptions.Keys. ConformanceMeansFactoryAvailable_1, Factory.this.getClass()); } } return explanation; } /** * If the factory is not available because of some exception, the exception that caused * the failure. Otherwise {@code null}. Note that a {@code null} value doesn't mean that * the factory is available - the {@link #pass()} method still need to be invoked. * * @return The exception which make the factory unavailable, or {@code null} if none. */ public Throwable getFailureCause() { return failureCause; } /** * Returns {@code true} if the enclosing factory is ready for use. The default * implementation returns {@code true} if no throwable was given at construction * time and the enclosing factory has not been {@linkplain Factory#dispose disposed}. * * @return {@code true} if the enclosing factory is ready for use. */ @Override public Boolean pass() { if (failureCause != null) { return Boolean.FALSE; } synchronized (Factory.this) { return !hints.containsKey(DISPOSED); } } /** * Returns a string representation of this conformance result. * This is mostly for debugging purpose. */ @Override public String toString() { Class<?> c = getClass(); while (c.isAnonymousClass()) { c = c.getSuperclass(); } return Classes.getShortClassName(Factory.this) + '.' + c.getSimpleName() + '[' + pass() + ": " + getExplanation() + ']'; } } /** * Disposes resources used by this factory. This method is invoked mainly by * {@link DynamicFactoryRegistry}; users are not expected to dispose resources * themself since factories may be shared by many classes in the system. * <p> * The default implementation clears the {@linkplain #hints} map and remember that this factory * has been disposed. Subclasses should override this method as below if they can perform more * resource disposal. * * {@preformat java * protected synchronized void dispose(boolean shutdown) { * // Disposes more resources here. * super.dispose(shutdown); * } * } * * @param shutdown {@code false} for normal disposal, or {@code true} if this method is invoked * during the process of a JVM shutdown. In the later case this method may dispose * resources more aggressively. For example * {@link org.geotoolkit.referencing.factory.epsg.ThreadedEpsgFactory} * may shutdown the JavaDB embedded database. * * @since 3.00 */ protected synchronized void dispose(boolean shutdown) { hints.clear(); hints.put(DISPOSED, new Date()); } /** * Returns a hash value for this factory. This method computes the hash value using * only immutable properties. This computation does <strong>not</strong> rely on * {@linkplain #getImplementationHints implementation hints}, since there is no * guarantee that they will not change. * * @since 2.3 */ @Override public final int hashCode() { return getClass().hashCode() ^ 105470090; } /** * Compares this factory with the specified object for equality. * This method returns {@code true} if and only if: * <p> * <ul> * <li>Both objects are of the exact same class * (a <cite>is instance of</cite> relationship is not enough).</li> * <li>{@linkplain #getImplementationHints implementation hints} are * {@linkplain Map#equals equal}.</li> * </ul> * <p> * The requirement for the <cite>exact same class</cite> is needed for consistency with the * {@linkplain FactoryRegistry factory registry} working, since at most one instance of a given * class {@linkplain FactoryRegistry#getServiceProviderByClass is allowed} in a registry. * * @param object The object to compare. * @return {@code true} if the given object is equal to this factory. * * @since 2.3 */ @Override public final boolean equals(final Object object) { if (object == this) { return true; } if (object != null && object.getClass() == getClass()) { final Factory that = (Factory) object; final Set<FactoryComparator> comparators = new HashSet<>(); return new FactoryComparator(this, that).compare(comparators); } return false; } /** * Returns a string representation of this factory. This method is mostly for debugging purpose, * so the string format may vary across different implementations or versions. The default * implementation formats all {@linkplain #getImplementationHints implementation hints} as a * tree. If the implementation hints include some {@linkplain Factory factory} dependencies, * then the implementation hints for those dependencies will appears under a tree branch. * * @return A string representation of this factory. * * @since 2.3 */ @Override public String toString() { final String name = format(this); final Map<Factory,String> done = new IdentityHashMap<>(); // We used IdentityHashMap above because we don't want to rely on Factory.equals(...) done.put(this, name); final String tree = format(getImplementationHints(), done); return name + System.lineSeparator() + tree; } /** * Returns a string representation of the specified hints. This is used by * {@link Hints#toString} in order to share the code provided in this class. */ static String toString(final Map<?,?> hints) { return format(hints, new IdentityHashMap<Factory, String>()); } /** * Formats a name for the specified factory. */ private static String format(final Factory factory) { String name = Classes.getShortClassName(factory); if (factory instanceof AuthorityFactory) { final Citation authority = ((AuthorityFactory) factory).getAuthority(); if (authority != null) { name = name + "[\"" + authority.getTitle() + "\"]"; } } return name; } /** * Formats the specified hints. This method is just the starting * point for {@link #format(Writer, Map, String, Map)} below. */ private static String format(final Map<?,?> hints, final Map<Factory,String> done) { final Writer table; try { table = new TableWriter(null, " "); format(table, hints, " ", done); } catch (IOException e) { // Should never happen, since we are writing in a buffer. throw new AssertionError(e); } return table.toString(); } /** * Formats recursively the tree. This method invoke itself. */ private static void format(final Writer table, final Map<?,?> hints, final String indent, final Map<Factory,String> done) throws IOException { for (final Map.Entry<?,?> entry : hints.entrySet()) { final Object k = entry.getKey(); String key = (k instanceof RenderingHints.Key) ? Hints.nameOf((RenderingHints.Key) k) : String.valueOf(k); Object value = entry.getValue(); table.write(indent); table.write(key); char separator = ':'; Factory recursive = null; if (value instanceof Factory) { recursive = (Factory) value; value = format(recursive); final String previous = done.put(recursive, key); if (previous != null) { done.put(recursive, previous); separator = '='; value = previous; recursive = null; } } table.write('\t'); table.write(separator); table.write(' '); table.write(String.valueOf(value)); table.write('\n'); if (recursive != null) { final String nextIndent = CharSequences.spaces(indent.length() + 2).toString(); format(table, recursive.getImplementationHints(), nextIndent, done); } } } }