/*
 *    GeoTools - OpenSource mapping toolkit
 *    http://geotools.org
 *    (C) 2005-2006, GeoTools Project Managment Committee (PMC)
 *
 *    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.geotools.factory;

// J2SE dependencies
import java.awt.RenderingHints;
import java.io.File;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.Map;
import java.util.HashMap;
import java.util.Set;
import java.util.TreeSet;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import javax.sql.DataSource;

import org.opengis.util.InternationalString;

import org.geotools.util.logging.Logging;
import org.geotools.resources.Utilities;


/**
 * A set of hints providing control on factories to be used. Those hints are typically used by
 * renderers or {@linkplain org.opengis.coverage.processing.GridCoverageProcessor grid coverage
 * processors} for example. They provides a way to control low-level details. Example:
 * <p>
 * <blockquote><pre>
 * CoordinateOperationFactory myFactory = &amp;hellip
 * Hints hints = new Hints(Hints.{@linkplain #COORDINATE_OPERATION_FACTORY}, myFactory);
 * AbstractProcessor processor = new DefaultProcessor(hints);
 * </pre></blockquote>
 * <p>
 * Any hint mentioned by this class is considered to be API, failure to make
 * use of a hint by a GeoTools factory implementation is considered a bug (as
 * it will prevent the use of this library for application specific tasks).
 * <p>
 * When hints are used in conjuction with the {@linkplain FactoryRegistry factory service
 * discovery mechanism} we have the complete geotools plugin system. By using hints to
 * allow application code to effect service discovery we allow client code to
 * retarget the geotools library for their needs.
 *
 * @since 2.1
 * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/metadata/src/main/java/org/geotools/factory/Hints.java $
 * @version $Id: Hints.java 29443 2008-02-25 10:04:01Z jgarnett $
 * @author Martin Desruisseaux
 * @author Jody Garnett
 */
public class Hints extends RenderingHints {
    /**
     * A set of system-wide hints to use by default.
     */
    private static final Hints GLOBAL = new Hints(Collections.EMPTY_MAP);

    /**
     * {@code true} if {@link #scanSystemProperties} needs to be invoked.
     */
    private static boolean needScan = true;

    ////////////////////////////////////////////////////////////////////////
    ////////                                                        ////////
    ////////                        Factories                       ////////
    ////////                                                        ////////
    ////////  The following keys are mainly about getting instances ////////
    ////////  of the right factory.  Keys for factory configuration ////////
    ////////  other than JTS are defined in their own categories    ////////
    ////////  after this one.  JTS factories are treated especially ////////
    ////////  because they are implemented outside Geotools.        ////////
    ////////                                                        ////////
    ////////////////////////////////////////////////////////////////////////

    /**
     * The {@link com.vividsolutions.jts.geom.GeometryFactory} instance to use.
     *
     * @see org.geotools.geometry.jts.FactoryFinder#getGeometryFactory
     */
    public static final ClassKey JTS_GEOMETRY_FACTORY = new ClassKey(
            "com.vividsolutions.jts.geom.GeometryFactory");

    /**
     * The {@link com.vividsolutions.jts.geom.CoordinateSequenceFactory} instance to use.
     *
     * @see org.geotools.geometry.jts.FactoryFinder#getCoordinateSequenceFactory
     */
    public static final ClassKey JTS_COORDINATE_SEQUENCE_FACTORY = new ClassKey(
            "com.vividsolutions.jts.geom.CoordinateSequenceFactory");

    /**
     * The {@link com.vividsolutions.jts.geom.PrecisionModel} instance to use.
     *
     * @see org.geotools.geometry.jts.FactoryFinder#getPrecisionModel
     */
    public static final Key JTS_PRECISION_MODEL = new Key(
            "com.vividsolutions.jts.geom.PrecisionModel");

    /**
     * The spatial reference ID for {@link com.vividsolutions.jts.geom.GeometryFactory}.
     *
     * @see org.geotools.geometry.jts.FactoryFinder#getGeometryFactory
     */
    public static final Key JTS_SRID = new Key(Integer.class);

    /**
     * The {@link org.opengis.referencing.crs.CRSAuthorityFactory} instance to use.
     *
     * @see org.geotools.referencing.FactoryFinder#getCRSAuthorityFactory
     */
    public static final ClassKey CRS_AUTHORITY_FACTORY = new ClassKey(
            "org.opengis.referencing.crs.CRSAuthorityFactory");

    /**
     * The {@link org.opengis.referencing.cs.CSAuthorityFactory} instance to use.
     *
     * @see org.geotools.referencing.FactoryFinder#getCSAuthorityFactory
     */
    public static final ClassKey CS_AUTHORITY_FACTORY = new ClassKey(
            "org.opengis.referencing.cs.CSAuthorityFactory");

    /**
     * The {@link org.opengis.referencing.datum.DatumAuthorityFactory} instance to use.
     *
     * @see org.geotools.referencing.FactoryFinder#getDatumAuthorityFactory
     */
    public static final ClassKey DATUM_AUTHORITY_FACTORY = new ClassKey(
            "org.opengis.referencing.datum.DatumAuthorityFactory");

    /**
     * The {@link org.opengis.referencing.crs.CRSFactory} instance to use.
     *
     * @see org.geotools.referencing.FactoryFinder#getCRSFactory
     */
    public static final ClassKey CRS_FACTORY = new ClassKey(
            "org.opengis.referencing.crs.CRSFactory");

    /**
     * The {@link org.opengis.referencing.cs.CSFactory} instance to use.
     *
     * @see org.geotools.referencing.FactoryFinder#getCSFactory
     */
    public static final ClassKey CS_FACTORY = new ClassKey(
            "org.opengis.referencing.cs.CSFactory");

    /**
     * The {@link org.opengis.referencing.datum.DatumFactory} instance to use.
     *
     * @see org.geotools.referencing.FactoryFinder#getDatumFactory
     */
    public static final ClassKey DATUM_FACTORY = new ClassKey(
            "org.opengis.referencing.datum.DatumFactory");

    /**
     * The {@link org.opengis.referencing.operation.CoordinateOperationFactory} instance to use.
     *
     * @see org.geotools.referencing.FactoryFinder#getCoordinateOperationFactory
     */
    public static final ClassKey COORDINATE_OPERATION_FACTORY = new ClassKey(
            "org.opengis.referencing.operation.CoordinateOperationFactory");

    /**
     * The {@link org.opengis.referencing.operation.CoordinateOperationAuthorityFactory} instance
     * to use.
     *
     * @see org.geotools.referencing.FactoryFinder#getCoordinateOperationAuthorityFactory
     */
    public static final ClassKey COORDINATE_OPERATION_AUTHORITY_FACTORY = new ClassKey(
            "org.opengis.referencing.operation.CoordinateOperationAuthorityFactory");

    /**
     * The {@link org.opengis.referencing.operation.MathTransformFactory} instance to use.
     *
     * @see org.geotools.referencing.FactoryFinder#getMathTransformFactory
     */
    public static final ClassKey MATH_TRANSFORM_FACTORY = new ClassKey(
            "org.opengis.referencing.operation.MathTransformFactory");

    /**
     * The {@link org.geotools.data.FeatureLockFactory} instance to use.
     *
     * @see CommonFactoryFinder#getFeatureLockFactory
     *
     * @since 2.4
     */
    public static final ClassKey FEATURE_LOCK_FACTORY = new ClassKey(
            "org.geotools.data.FeatureLockFactory");

    /**
     * The {@link org.geotools.feature.FeatureCollections} instance to use.
     *
     * @see CommonFactoryFinder#getFeatureCollections
     *
     * @since 2.4
     */
    public static final ClassKey FEATURE_COLLECTIONS = new ClassKey(
            "org.geotools.feature.FeatureCollections");

    /**
     * The {@link org.geotools.feature.FeatureTypeFactory} instance to use.
     *
     * @see CommonFactoryFinder#getFeatureTypeFactory
     *
     * @since 2.4
     */
    public static final ClassKey FEATURE_TYPE_FACTORY = new ClassKey(
            "org.geotools.feature.FeatureTypeFactory");

    /**
     * Used to provide the <cite>type name</cite> for the returned
     * {@link org.geotools.feature.FeatureTypeFactory}. Values should
     * be instances of {@link String}.
     *
     * @since 2.4
     */
    public static final Key FEATURE_TYPE_FACTORY_NAME = new Key(String.class);

    /**
     * Whether the features returned by the feature collections should be considered detached from the
     * datastore, that is, they are updatable without altering the backing store (makes sense only
     * if features are kept in memory or if there is some transparent persistent mechanism in place,
     * such as the Hibernate one)
     *
     * @since 2.4
     */
    public static final Key FEATURE_DETACHED = new Key(Boolean.class);

    /**
     * Request that the features returned by the feature collections should
     * be 2D only, can be used to prevent the request of the third ordinate
     * when only two are going to be used.
     *
     * @since 2.4.1
     */
    public static final Key FEATURE_2D = new Key(Boolean.class);
    
    /**
     * The {@link org.geotools.styling.StyleFactory} instance to use.
     *
     * @see CommonFactoryFinder#getStyleFactory
     *
     * @since 2.4
     */
    public static final ClassKey STYLE_FACTORY = new ClassKey(
            "org.geotools.styling.StyleFactory");

    /**
     * The {@link org.geotools.feature.AttributeTypeFactory} instance to use.
     *
     * @see CommonFactoryFinder#getAttributeTypeFactory
     *
     * @since 2.4
     */
    public static final ClassKey ATTRIBUTE_TYPE_FACTORY = new ClassKey(
            "org.geotools.feature.AttributeTypeFactory");

    /**
     * The {@link org.opengis.filter.FilterFactory} instance to use.
     *
     * @see CommonFactoryFinder#getFilterFactory
     *
     * @since 2.4
     */
    public static final ClassKey FILTER_FACTORY = new ClassKey(
            "org.opengis.filter.FilterFactory");



    ////////////////////////////////////////////////////////////////////////
    ////////                                                        ////////
    ////////              Coordinate Reference Systems              ////////
    ////////                                                        ////////
    ////////////////////////////////////////////////////////////////////////

    /**
     * The default {@link org.opengis.referencing.crs.CoordinateReferenceSystem}
     * to use. This is used by some factories capable to provide a default CRS
     * when no one were explicitly specified by the user.
     *
     * @since 2.2
     */
    public static final Key DEFAULT_COORDINATE_REFERENCE_SYSTEM = new Key(
            "org.opengis.referencing.crs.CoordinateReferenceSystem");

    /**
     * Used to direct WKT CRS Authority to a directory containing extra definitions.
     * The value should be an instance of {@link File} or {@link String} refering to
     * an existing directory.
     * <p>
     * Filenames in the supplied directory should be of the form
     * <code><var>authority</var>.properties</code> where <var>authority</var>
     * is the authority name space to use. For example the
     * {@value org.geotools.referencing.factory.epsg.FactoryUsingWKT#FILENAME}
     * file contains extra CRS to add as new EPSG codes.
     * <p>
     * To set the directory on the command line:
     *
     * <blockquote><pre>
     * -D{@value GeoTools#CRS_AUTHORITY_EXTRA_DIRECTORY}=<var>path</var>
     * </pre></blockquote>
     *
     * @since 2.4
     */
    public static final FileKey CRS_AUTHORITY_EXTRA_DIRECTORY = new FileKey(false);

    /**
     * The {@linkplain javax.sql.DataSource data source} name to lookup from JNDI when
     * initializing the {@linkplain org.geotools.referencing.factory.epsg EPSG factory}.
     * Possible values:
     * <ul>
     *   <li>{@link String} - used with JNDI to locate datasource. This hint has no effect if
     *       there is no {@linkplain javax.naming.InitialContext JNDI initial context} setup.</li>
     *   <li>{@linkplain javax.sql.DataSource} - used as is.</li>
     *   <li>missing - default to
     *       {@value org.geotools.referencing.factory.epsg.ThreadedEpsgFactory#DATASOURCE_NAME}.</li>
     * </ul>
     * <p>
     * To set on the command line:
     * <blockquote><pre>
     * -D{@value GeoTools#EPSG_DATA_SOURCE}=<var>jndiReference</var>
     * </pre></blockquote>
     *
     * @since 2.4
     */
    public static final Key EPSG_DATA_SOURCE = new DataSourceKey();

    /**
     * The preferred datum shift method to use for
     * {@linkplain org.opengis.referencing.operation.CoordinateOperation coordinate operations}.
     * Valid values are {@code "Molodenski"}, {@code "Abridged_Molodenski"} or {@code "Geocentric"}.
     * Other values may be supplied if a {@linkplain org.opengis.referencing.operation.MathTransform
     * math transform} exists for that name, but this is not guaranteed to work.
     *
     * @see org.geotools.referencing.FactoryFinder#getCoordinateOperationFactory
     */
    public static final OptionKey DATUM_SHIFT_METHOD = new OptionKey("Molodenski","Abridged_Molodenski","Geocentric","*");

    /**
     * Tells if {@linkplain org.opengis.referencing.operation.CoordinateOperation coordinate
     * operations} should be allowed even when a datum shift is required while no method is
     * found applicable. It may be for example that no
     * {@linkplain org.geotools.referencing.datum.BursaWolfParameters Bursa Wolf parameters}
     * were found for a datum shift. The default value is {@link Boolean#FALSE FALSE}, which means
     * that {@linkplain org.geotools.referencing.operation.DefaultCoordinateOperationFactory
     * coordinate operation factory} throws an exception if such a case occurs. If this hint is
     * set to {@code TRUE}, then the user is strongly encouraged to check the
     * {@linkplain org.opengis.referencing.operation.CoordinateOperation#getPositionalAccuracy
     * positional accuracy} for every transformation created. If the set of positional accuracy
     * contains {@link org.geotools.metadata.iso.quality.PositionalAccuracyImpl#DATUM_SHIFT_OMITTED
     * DATUM_SHIFT_OMITTED}, this means that an "ellipsoid shift" were applied without real datum
     * shift method available, and the transformed coordinates may have one kilometer error. The
     * application should warn the user (e.g. popup a message dialog box) in such case.
     *
     * @see org.geotools.referencing.FactoryFinder#getCoordinateOperationFactory
     */
    public static final Key LENIENT_DATUM_SHIFT = new Key(Boolean.class);

    /**
     * Tells if the {@linkplain org.opengis.referencing.cs.CoordinateSystem coordinate systems}
     * created by an {@linkplain org.opengis.referencing.cs.CSAuthorityFactory authority factory}
     * should be forced to (<var>longitude</var>,<var>latitude</var>) axis order. This hint is
     * especially useful for creating
     * {@linkplan org.opengis.referencing.crs.CoordinateReferenceSystem coordinate reference system}
     * objects from <A HREF="http://www.epsg.org">EPSG</A> codes. Most
     * {@linkplan org.opengis.referencing.crs.GeographicCRS geographic CRS} defined in the EPSG
     * database use (<var>latitude</var>,<var>longitude</var>) axis order. Unfortunatly, many data
     * sources available in the world uses the opposite axis order and still claim to use a CRS
     * described by an EPSG code. This hint allows to handle such data.
     * <p>
     * This hint shall be passed to the
     * <code>{@linkplain org.geotools.referencing.FactoryFinder#getCRSAuthorityFactory
     * FactoryFinder.getCRSAuthorityFactory}(...)</code> method. Whatever this hint is supported
     * or not is authority dependent. In the default Geotools configuration, this hint is supported
     * for the {@code "EPSG"} authority.
     * <p>
     * If this hint is not provided, then the default value depends on many factors including
     * {@linkplain System#getProperties system properties} and plugins available in the classpath.
     * In Geotools implementation, the default value is usually {@link Boolean#FALSE FALSE} with
     * one exception: If the <code>{@value
     * org.geotools.referencing.factory.epsg.LongitudeFirstFactory#SYSTEM_DEFAULT_KEY}</code>
     * system property is set to {@code true}, then the default value is {@code true} at least
     * for the {@linkplain org.geotools.referencing.factory.epsg.ThreadedEpsgFactory default EPSG
     * factory}.
     * <p>
     * If both the above-cited system property and this hint are provided, then
     * this hint has precedence. This allow axis order control on a data store
     * basis, and keep the system-wide property as the default value only for
     * cases where axis order is unspecified.
     * <p>
     * To set on the command line:
     * <blockquote><pre>
     * -D{@value GeoTools#FORCE_LONGITUDE_FIRST_AXIS_ORDER}=<var>longitudeFirst</var>
     * </pre></blockquote>
     *
     * @see org.geotools.referencing.FactoryFinder#getCSFactory
     * @see org.geotools.referencing.FactoryFinder#getCRSFactory
     * @see org.geotools.referencing.factory.OrderedAxisAuthorityFactory
     * @see org.geotools.referencing.factory.epsg.LongitudeFirstFactory
     * @tutorial http://docs.codehaus.org/display/GEOTOOLS/The+axis+order+issue
     *
     * @since 2.3
     */
    public static final Key FORCE_LONGITUDE_FIRST_AXIS_ORDER = new Key(Boolean.class);

    /**
     * Applies the {@link #FORCE_LONGITUDE_FIRST_AXIS_ORDER} hint to some factories that usually
     * ignore it. The <cite>axis order</cite> issue is of concern mostly to the {@code "EPSG"} name
     * space. Codes in the {@value org.geotools.referencing.factory.HTTP_AuthorityFactory#BASE_URL}
     * or {@code "urn:ogc"} name space usually ignore the axis order hint, especially the later
     * which is clearly defined by OGC - in theory users are not allowed to change its behavior.
     * If nevertheless a user really need to change its behavior, then he must provides explicitly
     * a comma separated list of authorities with this {@code FORCE_AXIS_ORDER_HONORING} hint in
     * addition to setting the {@link #FORCE_LONGITUDE_FIRST_AXIS_ORDER} hint.
     * <p>
     * <b>Example:</b> In order to apply the (<var>longitude</var>,<var>latitude</var>) axis order
     * to {@code "http://www.opengis.net/"} and {@code "urn:ogc"} name spaces in addition to EPSG,
     * use the following hints:
     *
     * <blockquote>
     * hints.put(FORCE_LONGITUDE_FIRST_AXIS_ORDER, Boolean.TRUE);
     * hints.put(FORCE_AXIS_ORDER_HONORING, "http, urn");
     * </blockquote>
     *
     * Note that the application of (<var>longitude</var>,<var>latitude</var>) axis order
     * to the {@code "urn:ogc"} name space is a clear violation of OGC specification.
     *
     * @since 2.4
     */
    public static final Key FORCE_AXIS_ORDER_HONORING = new Key(String.class);

    /**
     * Tells if the {@linkplain org.opengis.referencing.cs.CoordinateSystem coordinate systems}
     * created by an {@linkplain org.opengis.referencing.cs.CSAuthorityFactory authority factory}
     * should be forced to standard
     * {@linkplain org.opengis.referencing.cs.CoordinateSystemAxis#getDirection axis directions}.
     * If {@code true}, then {@linkplain org.opengis.referencing.cs.AxisDirection#SOUTH South} axis
     * directions are forced to {@linkplain org.opengis.referencing.cs.AxisDirection#NORTH North},
     * {@linkplain org.opengis.referencing.cs.AxisDirection#WEST West} axis directions are forced to
     * {@linkplain org.opengis.referencing.cs.AxisDirection#EAST East}, <cite>etc.</cite>
     * If {@code false}, then the axis directions are left unchanged.
     * <p>
     * This hint shall be passed to the
     * <code>{@linkplain org.geotools.referencing.FactoryFinder#getCRSAuthorityFactory
     * FactoryFinder.getCRSAuthorityFactory}(...)</code>
     * method. Whatever this hint is supported or not is authority dependent.
     *
     * @see org.geotools.referencing.FactoryFinder#getCSFactory
     * @see org.geotools.referencing.FactoryFinder#getCRSFactory
     * @see org.geotools.referencing.factory.OrderedAxisAuthorityFactory
     *
     * @since 2.3
     */
    public static final Key FORCE_STANDARD_AXIS_DIRECTIONS = new Key(Boolean.class);

    /**
     * Tells if the {@linkplain org.opengis.referencing.cs.CoordinateSystem coordinate systems}
     * created by an {@linkplain org.opengis.referencing.cs.CSAuthorityFactory authority factory}
     * should be forced to standard
     * {@linkplain org.opengis.referencing.cs.CoordinateSystemAxis#getUnit axis units}.
     * If {@code true}, then all angular units are forced to degrees and linear units to meters.
     * If {@code false}, then the axis units are left unchanged.
     * <p>
     * This hint shall be passed to the
     * <code>{@linkplain org.geotools.referencing.FactoryFinder#getCRSAuthorityFactory
     * FactoryFinder.getCRSAuthorityFactory}(...)</code> method. Whatever this hint is
     * supported or not is authority dependent.
     *
     * @see org.geotools.referencing.FactoryFinder#getCSFactory
     * @see org.geotools.referencing.FactoryFinder#getCRSFactory
     * @see org.geotools.referencing.factory.OrderedAxisAuthorityFactory
     *
     * @since 2.3
     */
    public static final Key FORCE_STANDARD_AXIS_UNITS = new Key(Boolean.class);

    /**
     * Policy to use for caching referencing objects. Valid values are:
     * <p>
     * <ul>
     *   <li>{@code "weak"} for holding values through {@linkplain java.lang.ref.WeakReference
     *       weak references}. This option do not actually cache the objects since the garbage
     *       collector cleans weak references aggressively, but it allows sharing the instances
     *       already created and still in use.</li>
     *   <li>{@code "all"} for holding values through strong references.</li>
     *   <li>{@code "none"} for disabling the cache.</li>
     * </ul>
     *
     * @since 2.4
     */
    public static final OptionKey BUFFER_POLICY = new OptionKey("weak", "all", "fixed","none","default");

    /**
     * The recommended maximum number of referencing objects to hold in a
     * {@linkplain org.opengis.referencing.AuthorityFactory authority factory}.
     *
     * @since 2.4
     */
    public static final IntegerKey BUFFER_LIMIT = new IntegerKey(50);

    /**
     * The maximum number of active AuthorityFactories.
     * <p>
     * This hint is treated as an absolute LIMIT for AbstractAuthorityMediator
     * instances such as as HsqlDialectEpsgMediator. As such this will be the
     * absolute limit on the number of database connections the mediator will
     * make use of. When non-positive there is no limit to the number of
     * connections used.
     * <p>
     * When this limit it reached, code will be forced to block while waiting
     * for a connection to become available.
     *
     * @since 2.4
     */
    public static final IntegerKey AUTHORITY_MAX_ACTIVE = new IntegerKey(8);

    /**
     * Minimum number of objects required before the evictor will begin
     * removing objects.  This value is also used by
     * AUTHORITY_SOFTMIN_EVICT_IDLETIME to keep this many idle workers
     * around.
     *
     * @since 2.4
     */
    public static final IntegerKey AUTHORITY_MIN_IDLE = new IntegerKey(1);

    /**
     * The number of idle AuthorityFactories.
     * <p>
     * This hint is treated as a recommendation for AbstractAuthorityMediator
     * instances such as HsqlDialectEpsgMediator. As such this will control
     * the number of connections the mediator is comfortable having open.
     * <p>
     * If AUTHORITY_MAX_ACTIVE is set to 20, up to twenty connections will
     * be used during heavy load. If the AUTHORITY_MAX_IDLE is set to 10,
     * connections will be immediately reclaimed until only 10 are open.
     * <p>
     * When the amount of time specified by AUTHORITY_IDLE_WAIT
     * Max idle controls the maximum number of objects that can sit idle in the
     * pool at any time. When negative, there is no limit to the number of
     * objects that may be idle at one time.
     *
     * @since 2.4
     */
    public static final IntegerKey AUTHORITY_MAX_IDLE = new IntegerKey(8);

    /**
     * When the evictor is run, if more time (in milliseconds) than the value in
     * AUTHORITY_MIN_EVICT_IDLETIME has passed, then the worker is destroyed.
     *
     * @since 2.4
     */
    public static final IntegerKey AUTHORITY_MIN_EVICT_IDLETIME = new IntegerKey(2 * 60 * 000);

    /**
     * When the evictor is run, workers which have been idle for more than this
     * value will be destroyed iff the number of idle workers exceeds
     * AUTHORITY_MIN_IDLE.
     *
     * @since 2.4
     */
    public static final IntegerKey AUTHORITY_SOFTMIN_EVICT_IDLETIME = new IntegerKey(10 * 000);

    /**
     * Time in milliseconds to wait between eviction runs.
     *
     * @since 2.4
     */
    public static final IntegerKey AUTHORITY_TIME_BETWEEN_EVICTION_RUNS = new IntegerKey(5 * 000);

    /**
     * Version number of the requested service. This hint is used for example in order to get
     * a {@linkplain org.opengis.referencing.crs.CRSAuthorityFactory CRS authority factory}
     * backed by a particular version of EPSG database. The value should be an instance of
     * {@link org.geotools.util.Version}.
     *
     * @since 2.4
     */
    public static final Key VERSION = new Key("org.geotools.util.Version");



    ////////////////////////////////////////////////////////////////////////
    ////////                                                        ////////
    ////////                     Grid Coverages                     ////////
    ////////                                                        ////////
    ////////////////////////////////////////////////////////////////////////

    /**
     * The {@link org.opengis.coverage.processing.GridCoverageProcessor} instance to use.
     *
     * @deprecated The {@code GridCoverageProcessor} interface is not yet
     *             stable. Avoid dependencies if possible.
     */
    public static final Key GRID_COVERAGE_PROCESSOR = new Key(Object.class);
    // TODO new Key("org.opengis.coverage.processing.GridCoverageProcessor");

    /**
     * Tells to the {@link org.opengis.coverage.grid.GridCoverageReader} instances to ignore
     * the built-in overviews when creating a {@link org.opengis.coverage.grid.GridCoverage}
     * object during a read. This hints also implied that no decimation on reading is performed.
     *
     * @since 2.3
     */
    public static final Key IGNORE_COVERAGE_OVERVIEW = new Key(Boolean.class);

    /**
     * Tells to the {@link org.opengis.coverage.grid.GridCoverageReader} instances to read
     * the image using the JAI ImageRead operation (leveraging on Deferred Execution Model, 
     * Tile Caching,...) or the direct {@code ImageReader}'s read methods.
     *
     * @since 2.4
     */
    public static final Key USE_JAI_IMAGEREAD = new Key(Boolean.class);
    
    /**
     * Forces the {@linkplain org.opengis.coverage.processing.GridCoverageProcessor grid coverage
     * processor} to perform operations on the non-geophysics view.
     * <p>
     * Some operation when called on a {@linkplain org.geotools.coverage.grid.GridCoverage grid
     * coverage} that is the non-geophysics view of another grid coverage try to go back to the
     * latter before executing even if they could execute fine without doing so. The rationale
     * behind this is that the non-geophysics view is just the rendered version of a coverage,
     * hence operations should be applied onto the geophysics companion.
     * <p>
     * However, in some cases like when doing pure rendering of photographic images, we might want
     * to force this operations to work on the non-geophysics view directly, even performing color
     * expansions as needed. This can be accomplished by setting this hint to {@code true}. Be
     * aware that interpolations in non-photographic images (i.e. a colormap like a <cite>Sea
     * Surface Temperature</cite> image) after color expansions may produce wrong colors.
     *
     * @since 2.4
     *
     * @todo We may need to find a more accurate name, especially when the enumeration in
     *       {@link org.geotools.coverage.grid.ViewType} will be ready to work. Maybe
     *       something like {@code PROCESS_ON_VISUAL_VIEW}.
     */
    public static final Key REPLACE_NON_GEOPHYSICS_VIEW = new Key(Boolean.class);

    /**
     * The {@linkplain javax.media.jai.tilecodec.TileEncoder tile encoder} name
     * (as a {@link String} value) to use during serialization of image data in
     * a {@link org.geotools.coverage.grid.GridCoverage2D} object. This encoding
     * is given to the {@link javax.media.jai.remote.SerializableRenderedImage}
     * constructor. Valid values include (but is not limited to) {@code "raw"},
     * {@code "gzip"} and {@code "jpeg"}.
     * <p>
     * <strong>Note:</strong> We recommand to avoid the {@code "jpeg"} codec
     * for grid coverages.
     *
     * @see org.geotools.coverage.FactoryFinder#getGridCoverageFactory
     *
     * @since 2.3
     */
    public static final Key TILE_ENCODING = new Key(String.class);

    /**
     * The {@link javax.media.jai.JAI} instance to use.
     */
    public static final Key JAI_INSTANCE = new Key("javax.media.jai.JAI");

    /**
     * The {@link org.opengis.coverage.SampleDimensionType} to use.
     */
    public static final Key SAMPLE_DIMENSION_TYPE = new Key(
            "org.opengis.coverage.SampleDimensionType");

    /**
     * Constructs a new object with the specified key/value pair.
     *
     * @param key   The key of the particular hint property.
     * @param value The value of the hint property specified with {@code key}.
     */
    public Hints(final RenderingHints.Key key, final Object value) {
        super(key, value);
    }

    /**
     * Constructs a new object with two key/value pair.
     *
     * @param key1   The key for the first pair
     * @param value1 The value for the first pair
     * @param key2   The key2 for the second pair
     * @param value2 The value2 for the second pair
     */
    public Hints(final RenderingHints.Key key1, final Object value1, final RenderingHints.Key key2, final Object value2) {
        this( fromPairs( new Object[]{ key1, value1, key2, value2 } ) );
    }

    /**
     * Constructs a new object with key/value pairs from an array.
     *
     * @param pairs
     *            An array containing pairs of RenderingHints keys and values
     */
    public Hints(final RenderingHints.Key key1, final Object value1, final Object[] pairs) {
        this(fromPairs(key1, value1, pairs));
    }

    /**
     * Constructs a new object from key/value pair.
     * <p>
     * This method is intended for use with Java5:
     * <code>new Hints( Hints.BUFFER_LIMIT,1,Hints.BUFFER_POLICY,"weak" )</code>
     *
     * @param pairs
     *            Key value pairs
     */
    public Hints(final RenderingHints.Key key1, final Object value1,
            final RenderingHints.Key key2, final Object value2, Object[] pairs) {
        this(fromPairs(key1, value1, key2, value2, pairs));
    }

    /**
     * Utility method used to organize pairs into a Map.
     *
     * @param pairs
     *            An array of Key/Value pairs
     * @return Map<Key,Value>
     * @throws ClassCastException
     *             if Key/Value pairs do not match
     */
    private static Map fromPairs(RenderingHints.Key key1, Object value1,
            Object[] pairs) {
        Map map = fromPairs(pairs);
        map.put(key1, value1);
        return map;
    }

    /**
     * Utility method used to organize pairs into a Map.
     *
     * @param pairs
     *            An array of Key/Value pairs
     * @return Map<Key,Value>
     * @throws ClassCastException
     *             if Key/Value pairs do not match
     */
    private static Map fromPairs(RenderingHints.Key key1, Object value1,
            RenderingHints.Key key2, Object value2, Object[] pairs) {
        Map map = fromPairs(pairs);
        map.put(key1, value1);
        map.put(key2, value2);
        return map;
    }

    /**
     * Utility method used to organize pairs into a Map.
     *
     * @param pairs
     *            An array of Key/Value pairs
     * @return Map<Key,Value>
     * @throws ClassCastException
     *             if Key/Value pairs do not match
     */
    private static Map fromPairs(Object[] pairs) {
        Map map = new HashMap();
        for (int i = 0; i < pairs.length; i += 2) {
            Key key = (Key) pairs[i];
            Object value = pairs[i+1];
            if (!key.isCompatibleValue(value)) {
                throw new ClassCastException(key + " requires "
                        + key.getValueClass() + " - could cast " + value);
            }
            map.put(key, value);
        }
        return map;
    }

    /**
     * Constructs a new object with keys and values initialized from the
     * specified map (which may be null).
     *
     * @param hints A map of key/value pairs to initialize the hints, or
     *              {@code null} if the object should be empty.
     */
    public Hints(final Map hints) {
        super(stripNonKeys(hints));
    }

    /**
     * Returns a map with the same hints than the specified one, minus every (key,value)
     * pairs where the key is not an instance of {@link RenderingHints.Key}. If the given
     * map contains only valid keys, then it is returned unchanged.
     */
    static Map stripNonKeys(final Map hints) {
        if (hints == null) {
            return null;
        }
        Map filtered = hints;
        for (final Iterator it=hints.keySet().iterator(); it.hasNext();) {
            final Object key = it.next();
            if (!(key instanceof RenderingHints.Key)) {
                if (filtered == hints) {
                    // Copy the map only if needed.
                    filtered = new HashMap(hints);
                }
                filtered.remove(key);
            }
        }
        return filtered;
    }

    /**
     * Notifies that {@linkplain System#getProperties system properties} will need to be scanned
     * for any property keys defined in the {@link GeoTools} class. New values found (if any) will
     * be added to the set of {@linkplain GeoTools#getDefaultHints default hints}. For example if
     * the {@value GeoTools#FORCE_LONGITUDE_FIRST_AXIS_ORDER} system property is defined, then the
     * {@link #FORCE_LONGITUDE_FIRST_AXIS_ORDER} hint will be added to the set of default hints.
     * <p>
     * This method is invoked automatically the first time it is needed. It usually don't
     * need to be invoked explicitly, except if the {@linkplain System#getProperties system
     * properties} changed. The scan may not be performed immediately, but rather when needed
     * at some later stage.
     *
     * @since 2.4
     */
    public static void scanSystemProperties() {
        synchronized (GLOBAL) {
            needScan = true;
        }
    }

    /**
     * Invokes {@link GeoTools#scanSystemProperties} when first needed. The caller is
     * responsible for invoking {@link GeoTools#fireConfigurationChanged} outside the
     * synchronized block if this method returns {@code true}.
     *
     * @return {@code true} if at least one hint changed as a result of this scan,
     *         or {@code false} otherwise.
     */
    private static boolean ensureSystemDefaultLoaded() {
        assert Thread.holdsLock(GLOBAL);
        if (needScan) {
            needScan = false;
            return GeoTools.scanForSystemHints(GLOBAL);
        } else {
            return false;
        }
    }

    /**
     * Returns a copy of the system hints. This is for
     * {@link GeoTools#getDefaultHints} implementation only.
     */
    static Hints getDefaults(final boolean strict) {
        final boolean changed;
        final Hints hints;
        synchronized (GLOBAL) {
            changed = ensureSystemDefaultLoaded();
            if (strict) {
                hints = new StrictHints(GLOBAL);
            } else {
                hints = new Hints(GLOBAL);
            }
        }
        if (changed) {
            GeoTools.fireConfigurationChanged();
        }
        return hints;
    }

    /**
     * Adds all specified hints to the system hints. This
     * is for {@link GeoTools#init} implementation only.
     */
    static void putSystemDefault(final RenderingHints hints) {
        synchronized (GLOBAL) {
            ensureSystemDefaultLoaded();
            GLOBAL.add(hints);
        }
        GeoTools.fireConfigurationChanged();
    }

    /**
     * Returns the hint {@linkplain GeoTools#getDefaultHints default value}
     * for the specified key.
     *
     * @param  key The hints key.
     * @return The value for the specified key, or {@code null}
     *         if the key did not have a mapping.
     *
     * @since 2.4
     */
    public static Object getSystemDefault(final RenderingHints.Key key) {
        final boolean changed;
        final Object value;
        synchronized (GLOBAL) {
            changed = ensureSystemDefaultLoaded();
            value = GLOBAL.get(key);
        }
        if (changed) {
            GeoTools.fireConfigurationChanged();
        }
        return value;
    }

    /**
     * Adds a hint value to the set of {@linkplain GeoTools#getDefaultHints default hints}.
     * Default hints can be added by call to this {@code putDefaultHint} method, to the
     * {@link GeoTools#init} method or by {@linkplain System#getProperties system properties}
     * with keys defined by the {@link String} constants in the {@link GeoTools} class.
     *
     * @param key   The hint key.
     * @param value The hint value.
     * @return The previous value of the specified key, or {@code null} if none.
     * @throws IllegalArgumentException If {@link Hints.Key#isCompatibleValue()}
     *         returns {@code false} for the specified value.
     *
     * @since 2.4
     */
    public static Object putSystemDefault(final RenderingHints.Key key, final Object value) {
        final boolean changed;
        final Object old;
        synchronized (GLOBAL) {
            changed = ensureSystemDefaultLoaded();
            old = GLOBAL.put(key, value);
        }
        if (changed || !Utilities.equals(value, old)) {
            GeoTools.fireConfigurationChanged();
        }
        return old;
    }

    /**
     * Removes the specified hints from the set of
     * {@linkplain GeoTools#getDefaultHints default hints}.
     *
     * @param  key The hints key that needs to be removed.
     * @return The value to which the key had previously been mapped,
     *         or {@code null} if the key did not have a mapping.
     *
     * @since 2.4
     */
    public static Object removeSystemDefault(final RenderingHints.Key key) {
        final boolean changed;
        final Object old;
        synchronized (GLOBAL) {
            changed = ensureSystemDefaultLoaded();
            old = GLOBAL.remove(key);
        }
        if (changed || old != null) {
            GeoTools.fireConfigurationChanged();
        }
        return old;
    }

    /**
     * Returns a string representation of the hints. This method formats the set of hints
     * as a tree. If some system-wide {@linkplain GeoTools#getDefaultHints default hints}
     * exist, they are formatted after those hints for completeness.
     *
     * @since 2.4
     */
    public String toString() {
        final String lineSeparator = System.getProperty("line.separator", "\n");
        final StringBuffer buffer = new StringBuffer("Hints:"); // TODO: localize
        buffer.append(lineSeparator).append(AbstractFactory.toString(this));
        Map extra = null;
        final boolean changed;
        synchronized (GLOBAL) {
            changed = ensureSystemDefaultLoaded();
            if (!GLOBAL.isEmpty()) {
                extra = new HashMap(GLOBAL);
            }
        }
        if (changed) {
            GeoTools.fireConfigurationChanged();
        }
        if (extra != null) {
            extra.keySet().removeAll(keySet());
            if (!extra.isEmpty()) {
                buffer.append("System defaults:")  // TODO: localize
                      .append(lineSeparator).append(AbstractFactory.toString(extra));
            }
        }
        return buffer.toString();
    }

    /**
     * The type for keys used to control various aspects of the factory
     * creation. Factory creation impacts rendering (which is why extending
     * {@linkplain java.awt.RenderingHints.Key rendering key} is not a complete
     * non-sense), but may impact other aspects of an application as well.
     *
     * @since 2.1
     * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/metadata/src/main/java/org/geotools/factory/Hints.java $
     * @version $Id: Hints.java 29443 2008-02-25 10:04:01Z jgarnett $
     * @author Martin Desruisseaux
     */
    public static class Key/*<T>*/ extends RenderingHints.Key {
        /**
         * The number of key created up to date.
         */
        private static int count;

        /**
         * The class name for {@link #valueClass}.
         */
        private final String className;

        /**
         * Base class of all values for this key. Will be created from {@link #className} only when
         * first required, in order to avoid too early class loading. This is significant for the
         * {@link #JAI_INSTANCE} key for example, in order to avoid JAI dependencies in applications
         * that do not need it.
         */
        private transient Class/*<T>*/ valueClass;

        /**
         * Constructs a new key for values of the given class.
         *
         * @param classe
         *            The base class for all valid values.
         */
        public Key(final Class/*<T>*/ classe) {
            this(classe.getName());
            valueClass = classe;
        }

        /**
         * Constructs a new key for values of the given class. The class is
         * specified by name instead of a {@link Class} object. This allows to
         * defer class loading until needed.
         *
         * @param className
         *            Name of base class for all valid values.
         */
        Key(final String className) {
            super(count());
            this.className = className;
        }

        /**
         * Workaround for RFE #4093999 ("Relax constraint on placement of this()/super()
         * call in constructors"): {@code count++} need to be executed in a synchronized
         * block since it is not an atomic operation.
         */
        private static synchronized int count() {
            return count++;
        }

        /**
         * Returns the expected class for values stored under this key.
         */
        public Class/*<T>*/ getValueClass() {
            if (valueClass == null) {
                try {
                    valueClass = Class.forName(className);
                } catch (ClassNotFoundException exception) {
                    Logging.unexpectedException("org.geotools.factory",
                            Key.class, "getValueClass", exception);
                    valueClass = Object.class;
                }
            }
            return valueClass;
        }

        /**
         * Returns {@code true} if the specified object is a valid value for
         * this key. The default implementation checks if the specified value
         * {@linkplain Class#isInstance is an instance} of the {@linkplain
         * #getValueClass value class}.
         * <p>
         * Note that many hint keys defined in the {@link Hints} class relax this rule and accept
         * {@link Class} object assignable to the expected {@linkplain #getValueClass value class}
         * as well.
         *
         * @param value
         *            The object to test for validity.
         * @return {@code true} if the value is valid; {@code false} otherwise.
         *
         * @see Hints.ClassKey#isCompatibleValue
         * @see Hints.FileKey#isCompatibleValue
         * @see Hints.IntegerKey#isCompatibleValue
         * @see Hints.OptionKey#isCompatibleValue
         */
        public boolean isCompatibleValue(final Object value) {
            return getValueClass().isInstance(value);
        }

        /**
         * Returns a string representation of this key. The string
         * representation is mostly for debugging purpose. The default
         * implementation tries to infer the key name using reflection.
         */
        public String toString() {
            int t = 0;
            while (true) {
                final Class type;
                switch (t++) {
                    case 0:  type = Hints.class;      break;
                    case 1:  type = getValueClass();  break;
                    default: return super.toString();
                }
                final Field[] fields = type.getFields();
                for (int i=0; i<fields.length; i++) {
                    final Field f = fields[i];
                    if (Modifier.isStatic(f.getModifiers())) {
                        final Object v;
                        try {
                            v = f.get(null);
                        } catch (IllegalAccessException e) {
                            continue;
                        }
                        if (v == this) {
                            return f.getName();
                        }
                    }
                }
            }
        }
    }

    /**
     * A key for value that may be specified either as instance of {@code T}, or as
     * {@code Class<T>}.
     *
     * @since 2.4
     * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/metadata/src/main/java/org/geotools/factory/Hints.java $
     * @version $Id: Hints.java 29443 2008-02-25 10:04:01Z jgarnett $
     * @author Martin Desruisseaux
     */
    public static final class ClassKey/*<T>*/ extends Key/*<T>*/ {
        /**
         * Constructs a new key for values of the given class.
         *
         * @param classe
         *            The base class for all valid values.
         */
        public ClassKey(final Class/*<T>*/ classe) {
            super(classe);
        }

        /**
         * Constructs a new key for values of the given class. The class is
         * specified by name instead of a {@link Class} object. This allows to
         * defer class loading until needed.
         *
         * @param className
         *            Name of base class for all valid values.
         */
        ClassKey(final String className) {
            super(className);
        }

        /**
         * Returns {@code true} if the specified object is a valid value for
         * this key. This method checks if the specified value is non-null and
         * is one of the following:
         * <p>
         * <ul>
         *   <li>An instance of the {@linkplain #getValueClass expected value class}.</li>
         *   <li>A {@link Class} assignable to the expected value class.</li>
         *   <li>An array of {@code Class} objects assignable to the expected value class.</li>
         * </ul>
         */
        //@Override
        public boolean isCompatibleValue(final Object value) {
            if (value == null) {
                return false;
            }
            if (value instanceof Class[]) {
                final Class[] types = (Class[]) value;
                for (int i = 0; i < types.length; i++) {
                    if (!isCompatibleValue(types[i])) {
                        return false;
                    }
                }
                return types.length != 0;
            }
            final Class type;
            if (value instanceof Class) {
                type = (Class) value;
            } else {
                type = value.getClass();
            }
            return getValueClass().isAssignableFrom(type);
        }
    }

    /**
     * Key for hints to be specified as a {@link File}.
     * The file may also be specified as a {@link String} object.
     *
     * @since 2.4
     * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/metadata/src/main/java/org/geotools/factory/Hints.java $
     * @version $Id: Hints.java 29443 2008-02-25 10:04:01Z jgarnett $
     * @author Jody Garnett
     * @author Martin Desruisseaux
     */
    public static final class FileKey extends Key {
        /**
         * {@code true} if write operations need to be allowed.
         */
        private final boolean writable;

        /**
         * Creates a new key for {@link File} value.
         *
         * @param writable
         *          {@code true} if write operations need to be allowed.
         */
        public FileKey(final boolean writable) {
            super(File.class);
            this.writable = writable;
        }

        /**
         * Returns {@code true} if the specified object is a valid file or directory.
         */
        //@Override
        public boolean isCompatibleValue(final Object value) {
            final File file;
            if (value instanceof File) {
                file = (File) value;
            } else if (value instanceof String) {
                file = new File((String) value);
            } else {
                return false;
            }
            if (file.exists()) {
                return !writable || file.canWrite();
            }
            final File parent = file.getParentFile();
            return parent!=null && parent.canWrite();
        }
    }

    /**
     * A hint used to capture a configuration setting as an integer.
     * A default value is provided and may be checked with {@link #getDefault()}.
     *
     * @since 2.4
     * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/metadata/src/main/java/org/geotools/factory/Hints.java $
     * @version $Id: Hints.java 29443 2008-02-25 10:04:01Z jgarnett $
     * @author Jody Garnett
     */
    public static final class IntegerKey extends Key {
        /**
         * The default value.
         */
        private final int number;

        /**
         * Creates a new key with the specified default value.
         */
        public IntegerKey(final int number) {
            super(Integer.class);
            this.number = number;
        }

        /**
         * Returns the default value.
         */
        public int getDefault(){
            return number;
        }

        /**
         * Returns the value from the specified hints as an integer. If no value were found
         * for this key, then this method returns the {@linkplain #getDefault default value}.
         *
         * @param  hints The map where to fetch the hint value, or {@code null}.
         * @return The hint value as an integer, or the default value if not hint
         *         was explicitly set.
         */
        public int toValue(final Hints hints) {
            if (hints != null) {
                final Object value = hints.get(this);
                if (value instanceof Number) {
                    return ((Number) value).intValue();
                } else if (value instanceof CharSequence) {
                    return Integer.parseInt(value.toString());
                }
            }
            return number;
        }

        /**
         * Returns {@code true} if the specified object is a valid integer.
         */
        //@Override
        public boolean isCompatibleValue(final Object value) {
            if (value instanceof Short || value instanceof Integer) {
                return true;
            }
            if (value instanceof String || value instanceof InternationalString) {
                try {
                    Integer.parseInt(value.toString());
                } catch (NumberFormatException e) {
                    Logging.getLogger("org.geotools.factory").finer(e.toString());
                }
            }
            return false;
        }
    }

    /**
     * Key that allows the choice of several options.
     * You can use {@code "*"} as a wild card to indicate that undocumented options
     * may be supported (but there is no assurances - {@link Hints#DATUM_SHIFT_METHOD}).
     *
     * @since 2.4
     * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/metadata/src/main/java/org/geotools/factory/Hints.java $
     * @version $Id: Hints.java 29443 2008-02-25 10:04:01Z jgarnett $
     * @author Jody Garnett
     */
    public static final class OptionKey extends Key {
        /**
         * The set of options allowed.
         */
        private final Set options;

        /**
         * {@code true} if the {@code "*"} wildcard was given in the set of options.
         */
        private final boolean wildcard;

        /**
         * Creates a new key for a configuration option.
         *
         * @todo Replace by variable arguments length when we will be allowed to compile for J2SE 1.5.
         */
        public OptionKey(String option1, String option2) {
            this(new String[] {option1, option2});
        }

        /**
         * Creates a new key for a configuration option.
         *
         * @todo Replace by variable arguments length when we will be allowed to compile for J2SE 1.5.
         */
        public OptionKey(String option1, String option2, String option3) {
            this(new String[] {option1, option2, option3});
        }

        /**
         * Creates a new key for a configuration option.
         *
         * @todo Replace by variable arguments length when we will be allowed to compile for J2SE 1.5.
         */
        public OptionKey(String option1, String option2, String option3, String option4) {
            this(new String[] {option1, option2, option3, option4});
        }

        /**
         * Creates a new key for a configuration option.
         *
         * @todo Replace by variable arguments length when we will be allowed to compile for J2SE 1.5.
         */
        public OptionKey(String option1, String option2, String option3, String option4, String option5) {
            this(new String[] {option1, option2, option3, option4, option5});
        }
        /**
         * Creates a new key for a configuration option.
         *
         * @todo Replace by variable arguments length when we will be allowed to compile for J2SE 1.5.
         */
        public OptionKey(final String[] alternatives) {
            super(String.class);
            final Set options = new TreeSet(Arrays.asList(alternatives));
            this.wildcard = options.remove("*");
            this.options  = Collections.unmodifiableSet(options);
        }

        /**
         * Returns the set of available options.
         */
        public Set getOptions() {
            return options;
        }

        /**
         * Returns {@code true} if the specified object is one of the valid options. If the
         * options specified at construction time contains the {@code "*"} wildcard, then
         * this method returns {@code true} for every {@link String} object.
         */
        //@Override
        public boolean isCompatibleValue(final Object value) {
            return wildcard ? (value instanceof String) : options.contains(value);
        }
    }

    /**
     * Key for hints to be specified as a {@link javax.sql.DataSource}.
     * The file may also be specified as a {@link String} object.
     *
     * @since 2.4
     * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/metadata/src/main/java/org/geotools/factory/Hints.java $
     * @version $Id: Hints.java 29443 2008-02-25 10:04:01Z jgarnett $
     * @author Martin Desruisseaux
     */
    static final class DataSourceKey extends Key {
        /**
         * Creates a new key for {@link javax.sql.DataSource} value.
         */
        public DataSourceKey() {
            super(DataSource.class);
        }

        /**
         * Returns {@code true} if the specified object is a data source or data source name.
         */
        //@Override
        public boolean isCompatibleValue(final Object value) {
            return (value instanceof DataSource) || (value instanceof String);
        }
    }
}
