/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2005-2008, Open Source Geospatial Foundation (OSGeo)
*
* 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;
import java.awt.RenderingHints;
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import javax.naming.Name;
import javax.sql.DataSource;
import org.geotools.resources.i18n.ErrorKeys;
import org.geotools.resources.i18n.Errors;
import org.geotools.util.Utilities;
import org.geotools.util.logging.Logging;
import org.opengis.util.InternationalString;
/**
* 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 = &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 conjunction 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$
* @version $Id$
* @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();
/**
* {@code true} if {@link #scanSystemProperties} needs to be invoked.
*/
private static boolean needScan = true;
////////////////////////////////////////////////////////////////////////
//////// ////////
//////// Coordinate Reference Systems ////////
//////// ////////
////////////////////////////////////////////////////////////////////////
/**
* 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 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);
/**
* 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");
////////////////////////////////////////////////////////////////////////
//////// ////////
//////// ISO Geometries ////////
//////// ////////
////////////////////////////////////////////////////////////////////////
/**
* The {@link org.opengis.referencing.crs.CoordinateReferenceSystem} to use in
* ISO geometry factories.
*
* @see #JTS_SRID
* @since 2.5
*/
public static final Key CRS = new Key("org.opengis.referencing.crs.CoordinateReferenceSystem");
/**
* The {@link org.opengis.geometry.Precision} to use in ISO geometry factories.
*
* @see #JTS_PRECISION_MODEL
* @since 2.5
*/
public static final Key PRECISION = new Key("org.opengis.geometry.Precision");
/**
* The {@link org.opengis.geometry.PositionFactory} instance to use.
*
* @since 2.5
*/
public static final Key POSITION_FACTORY = new Key("org.opengis.geometry.PositionFactory");
/**
* The {@link org.opengis.geometry.coordinate.GeometryFactory} instance to use.
*
* @see #JTS_GEOMETRY_FACTORY
* @since 2.5
*/
public static final Key GEOMETRY_FACTORY = new Key("org.opengis.geometry.coordinate.GeometryFactory");
/**
* The {@link org.opengis.geometry.complex.ComplexFactory} instance to use.
*
* @since 2.5
*/
public static final Key COMPLEX_FACTORY = new Key("org.opengis.geometry.complex.ComplexFactory");
/**
* The {@link org.opengis.geometry.aggregate.AggregateFactory} instance to use.
*
* @since 2.5
*/
public static final Key AGGREGATE_FACTORY = new Key("org.opengis.geometry.aggregate.AggregateFactory");
/**
* The {@link org.opengis.geometry.primitive.PrimitiveFactory} instance to use.
*
* @since 2.5
*/
public static final Key PRIMITIVE_FACTORY = new Key("org.opengis.geometry.primitive.PrimitiveFactory");
/**
* If {@code true}, geometry will be validated on creation. A value of {@code false}
* may speedup geometry creation at the cost of less safety.
*
* @since 2.5
*/
public static final Key GEOMETRY_VALIDATE = new Key(Boolean.class);
////////////////////////////////////////////////////////////////////////
//////// ////////
//////// JTS Geometries ////////
//////// ////////
////////////////////////////////////////////////////////////////////////
/**
* The {@link com.vividsolutions.jts.geom.GeometryFactory} instance to use.
*
* @see #GEOMETRY_FACTORY
* @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
* @see #PRECISION
*/
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
* @see #CRS
*/
public static final Key JTS_SRID = new Key(Integer.class);
////////////////////////////////////////////////////////////////////////
//////// ////////
//////// Features ////////
//////// ////////
////////////////////////////////////////////////////////////////////////
/**
* The {@link org.opengis.feature.FeatureFactory} instance to use.
*
* @see CommonFactoryFinder.getFeatureFactory()
* @since 2.5
*/
public static ClassKey FEATURE_FACTORY = new ClassKey( "org.opengis.feature.FeatureFactory");
/**
* The {@link org.opengis.feature.type.FeatureTypeFactory} instance to use.
*
* @see CommonFactoryFinder.getFeatureTypeFactory()
* @since 2.4
*/
public static ClassKey FEATURE_TYPE_FACTORY = new ClassKey( "org.opengis.feature.type.FeatureTypeFactory");
/**
* 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");
/**
* Used to provide the <cite>type name</cite> for the returned
* {@link org.geotools.feature.FeatureTypeFactory}. Values should
* be instances of {@link String}.
* @deprecated This hint controls FeatureTypeBuilder which is now deprecated
* @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);
/**
* Asks a datastore having a vector pyramid (pre-generalized geometries)
* to return the geometry version whose points have been generalized
* less than the spefiedi distance (further generalization might be
* performed by the client in memory).<p>
* The geometries returned are supposed to be topologically valid.
*/
public static final Key GEOMETRY_DISTANCE = new Key(Double.class);
/**
* Asks a datastore to perform a topology preserving on the fly
* generalization of the geometries. The datastore will return
* geometries generalized at the specified distance.
*/
public static final Key GEOMETRY_GENERALIZATION = new Key(Double.class);
/**
* Asks a datastore to perform a non topology preserving on the fly
* generalization of the geometries (e.g., returning self crossing
* polygons as a result of the geoneralization is considered valid).
*/
public static final Key GEOMETRY_SIMPLIFICATION = new Key(Double.class);
/**
* The actual coordinate dimensions of the geometry (to be used in
* the GeometryDescriptor user map)
*/
public static final Key COORDINATE_DIMENSION = new Key(Integer.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");
////////////////////////////////////////////////////////////////////////
//////// ////////
//////// 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");
/**
* Key to control the maximum allowed number of tiles that we will load.
* If this number is exceeded, i.e. we request an area which is too large
* instead of getting stuck with opening thousands of files we throw an error.
*
* @since 2.5
*/
public static final Key MAX_ALLOWED_TILES = new Key(Integer.class);
/**
* Key to control the name of the attribute that contains the location for
* the tiles in the mosaic index.
*
* @since 2.5
*/
public static final Key MOSAIC_LOCATION_ATTRIBUTE = new Key(String.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);
/**
* Overview choosing policy. The value most be one of
* {link #org.geotools.coverage.grid.io.OverviewPolicy} enumeration.
*
* @since 2.5
*/
public static final Key OVERVIEW_POLICY = new Key(
"org.geotools.coverage.grid.io.OverviewPolicy");
/**
* Forces the {@linkplain org.opengis.coverage.processing.GridCoverageProcessor grid coverage
* processor} to perform operations on the specified view.
* <p>
* Some operation when called on a {@linkplain org.geotools.coverage.grid.GridCoverage2D grid
* coverage} tries to converts to {@linkplain org.geotools.coverage.grid.ViewType#GEOPHYSICS
* geophysics} view before to execute. The rationale behind this is that the other views are
* just the rendered version of a coverage data, and operations like interpolations have a
* physical meaning only when applied on the geophysics view (e.g. interpolate <cite>Sea
* Surface Temperature</cite> (SST) values, not the RGB values that colorize the temperature).
* <p>
* However, in some cases like when doing pure rendering of images, we might want to force
* operations to work on {@linkplain org.geotools.coverage.grid.ViewType#PHOTOGRAPHIC
* photographic} view directly, even performing color expansions as needed. This can be
* accomplished by setting this hint to the desired view. Be aware that interpolations
* after color expansions may produce colors that do not accuratly represent the geophysical
* value.
*
* @since 2.5
*/
public static final Key COVERAGE_PROCESSING_VIEW = new Key("org.geotools.coverage.grid.ViewType");
/**
* @deprecated Replaced by {@link #COVERAGE_PROCESSING_VIEW} key with a
* {@link org.geotools.coverage.grid.ViewType#PHOTOGRAPHIC} value.
*
* @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 recommend 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");
////////////////////////////////////////////////////////////////////////
//////// ////////
//////// Data stores ////////
//////// ////////
////////////////////////////////////////////////////////////////////////
/**
* The maximum number of associations traversed in a datastore query.
* <p>
* This maps directly to the {@code traversalXlinkDepth} parameter in a WFS query.
*/
public static final Hints.Key ASSOCIATION_TRAVERSAL_DEPTH = new Key(Integer.class);
/**
* The name of a property to traverse in a datastore query.
* <p>
* This maps directly to a {@code xlinkPropertyName} in a WFS query.
*/
public static final Hints.Key ASSOCIATION_PROPERTY = new Key("org.opengis.filter.expression.PropertyName");
////////////////////////////////////////////////////////////////////////
//////// ////////
//////// Caches ////////
//////// ////////
////////////////////////////////////////////////////////////////////////
/**
* 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 does 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 "fixed") for holding a fixed number of values specified by {@link #CACHE_LIMIT}.
* <li>{@code "all"} for holding values through strong references.</li>
* <li>{@code "none"} for disabling the cache.</li>
* <li>{@code "soft"} for holding the value throuhg(@linkplain java.lang.ref.SoftReference
* soft references}.
* </ul>
*
* @since 2.5
*/
public static final OptionKey CACHE_POLICY = new OptionKey("weak", "all", "fixed","none","default","soft");
/**
* The recommended maximum number of referencing objects to hold in a
* {@linkplain org.opengis.referencing.AuthorityFactory authority factory}.
*
* @since 2.5
*/
public static final IntegerKey CACHE_LIMIT = new IntegerKey(50);
/**
* The maximum number of active {@linkplain org.opengis.referencing.AuthorityFactory authority
* factories}. The default is the {@linkplain Runtime#availableProcessors number of available
* processors} plus one.
* <p>
* This hint is treated as an absolute <strong>limit</strong> for
* {@link org.geotools.referencing.factory.AbstractAuthorityMediator} instances such as
* {@link org.geotools.referencing.factory.epsg.HsqlDialectEpsgMediator}. As such this
* will be the absolute limit on the number of database connections the mediator will
* make use of.
* <p>
* When this limit it reached, code will be forced to block while waiting
* for a connection to become available.
* <p>
* When this value is non positive their is no limit to the number of
* active authority factories deployed.
*
* @since 2.5
*/
public static final IntegerKey AUTHORITY_MAX_ACTIVE =
new IntegerKey(Runtime.getRuntime().availableProcessors() + 1);
/**
* 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.
* <p>
* In practice this value indicates the number of database connections
* the application will hold open "just in case".
* <p>
* Recomendations:
* <ul>
* <li>Desktop Application: 1
* <li>Server Application: 2-3
* </ul>
* To agree with J2EE conventions you will want this value to be zero.
*
* @since 2.5
*/
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.
* As these 10 remain idle for AUTHORITY_
* <p>
* When the amount of time specified by AUTHORITY_IDLE_WAIT is non zero
* 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.5
*/
public static final IntegerKey AUTHORITY_MAX_IDLE = new IntegerKey(2);
/**
* When the evictor is run, if more time (in milliseconds) than the value in
* {@code AUTHORITY_MIN_EVICT_IDLETIME} has passed, then the worker is destroyed.
*
* @since 2.5
*/
public static final IntegerKey AUTHORITY_MIN_EVICT_IDLETIME = new IntegerKey(2 * 60 * 1000);
/**
* When the evictor is run, workers which have been idle for more than this
* value will be destroyed if and only if the number of idle workers exceeds
* AUTHORITY_MIN_IDLE.
*
* @since 2.5
*/
public static final IntegerKey AUTHORITY_SOFTMIN_EVICT_IDLETIME = new IntegerKey(10 * 1000);
/**
* Time in milliseconds to wait between eviction runs.
*
* @since 2.5
*/
public static final IntegerKey AUTHORITY_TIME_BETWEEN_EVICTION_RUNS = new IntegerKey(5 * 1000);
/**
* Tolerance used in comparisons between floating point values. Two floating points A and B are
* considered the same if A * (1 - tol) <= B <= A * (1 + tol).
* The default value is 0, meaning the two doubles have to be exactly the same (a bit to bit
* comparison will be performed).
*
* @since 2.6
*/
public static final DoubleKey COMPARISON_TOLERANCE = new DoubleKey(0.0);
/**
* Constructs an initially empty set of hints.
*
* @since 2.5
*/
public Hints() {
super(null);
}
/**
* 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(null); // Don't use 'super(key,value)' because it doesn't check validity.
super.put(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.
*
* @since 2.4
*/
public Hints(final RenderingHints.Key key1, final Object value1,
final RenderingHints.Key key2, final Object value2)
{
this (key1, value1);
super.put(key2, value2);
}
/**
* Constructs a new object with key/value pairs from an array.
*
* @param key1 The key for the first pair.
* @param value1 The value for the first pair.
* @param pairs An array containing additional pairs of keys and values.
*
* @since 2.4
*
* @deprecated The {@code Object[]} argument was a kind of substitution for variable-length
* arguments. In order to avoid confusion, it is safer to use the later.
*/
@Deprecated
public Hints(final RenderingHints.Key key1, final Object value1, final Object[] pairs) {
this(key1, value1);
fromPairs(pairs);
}
/**
* Constructs a new object from 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.
* @param pairs Additional pairs of keys and values.
*
* @since 2.4
*/
public Hints(final RenderingHints.Key key1, final Object value1,
final RenderingHints.Key key2, final Object value2,
final Object... pairs)
{
this(key1, value1, key2, value2);
fromPairs(pairs);
}
/**
* Utility method used to copy (key,value) pairs into this Hints map.
*
* @param pairs An array of Key/Value pairs.
* @throws IllegalArgumentException if a value is illegal.
*/
private void fromPairs(final Object[] pairs) throws IllegalArgumentException {
if ((pairs.length & 1) != 0) {
throw new IllegalArgumentException(
Errors.format(ErrorKeys.ODD_ARRAY_LENGTH_$1, pairs.length));
}
for (int i=0; i<pairs.length; i += 2) {
super.put(pairs[i], pairs[i+1]);
}
}
/**
* 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<? extends RenderingHints.Key, ?> hints) {
super(stripNonKeys(hints));
}
/**
* Constructs a new object with keys and values initialized from the
* specified hints (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.
*
* @since 2.5
*/
public Hints(final RenderingHints 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.
*
* @param hints The map of hints to filter.
* @return A map with filtered hints.
*/
static Map<RenderingHints.Key, Object> stripNonKeys(final Map<?,?> hints) {
if (hints == null) {
return null;
}
/*
* We cheat in the next line since the map may contains illegal key. However when this
* method will finish, we garantee that it will contains only RenderingHints.Key keys,
* provided there is no concurrent changes in an other thread.
*/
@SuppressWarnings("unchecked")
Map<RenderingHints.Key, Object> filtered = (Map) hints;
for (final Iterator it=hints.keySet().iterator(); it.hasNext();) {
final Object key = it.next();
if (!(key instanceof RenderingHints.Key)) {
if (filtered == hints) {
// Copies the map only if needed.
filtered = new HashMap<RenderingHints.Key, Object>(filtered);
}
filtered.remove(key);
}
}
return filtered;
}
/**
* Returns a new map of hints with the same content than this map.
*
* @since 2.5
*/
@Override
public Hints clone() {
return (Hints) super.clone();
}
/**
* 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
*/
@Override
public String toString() {
final String lineSeparator = System.getProperty("line.separator", "\n");
final StringBuilder buffer = new StringBuilder("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<Object,Object>(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();
}
/**
* Tries to find the name of the given key, using reflection.
*/
static String nameOf(final RenderingHints.Key key) {
if (key instanceof Key) {
return key.toString();
}
int t = 0;
while (true) {
final Class<?> type;
switch (t++) {
case 0: {
type = RenderingHints.class;
break;
}
case 1: {
try {
type = Class.forName("javax.media.jai.JAI");
break;
} catch (ClassNotFoundException e) {
continue;
} catch (NoClassDefFoundError e) {
// May occurs because of indirect JAI dependencies.
continue;
}
}
default: {
return key.toString();
}
}
final String name = nameOf(type, key);
if (name != null) {
return name;
}
}
}
/**
* If the given key is declared in the given class, returns its name.
* Otherwise returns {@code null}.
*/
private static String nameOf(final Class<?> type, final RenderingHints.Key key) {
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 == key) {
return f.getName();
}
}
}
return null;
}
/**
* 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$
* @version $Id$
* @author Martin Desruisseaux
*/
public static class Key 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<?> valueClass;
/**
* Constructs a new key for values of the given class.
*
* @param classe The base class for all valid values.
*/
public Key(final Class<?> 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.
*
* @return The class of values stored under this key.
*/
public Class<?> getValueClass() {
if (valueClass == null) {
try {
valueClass = Class.forName(className);
} catch (ClassNotFoundException exception) {
Logging.unexpectedException(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.
*/
@Override
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 String name = nameOf(type, this);
if (name != null) {
return name;
}
}
}
}
/**
* A key for value that may be specified either as instance of {@code T}, or as
* {@code Class<T>}.
*
* @since 2.4
* @source $URL$
* @version $Id$
* @author Martin Desruisseaux
*/
public static final class ClassKey extends Key {
/**
* Constructs a new key for values of the given class.
*
* @param classe The base class for all valid values.
*/
public ClassKey(final Class<?> 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 the value is an array of classes, invokes this method recursively
* in order to check the validity of each elements in the array.
*/
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;
}
/*
* If the value is a class, checks if it is assignable to the expected value class.
* As a special case, if the value is not assignable but is an abstract class while
* we expected an interface, we will accept this class anyway because the some sub-
* classes may implement the interface (we dont't really know). For example the
* AbstractAuthorityFactory class doesn't implements the CRSAuthorityFactory interface,
* but sub-classe of it do. We make this relaxation in order to preserve compatibility,
* but maybe we will make the check stricter in the future.
*/
if (value instanceof Class<?>) {
final Class<?> type = (Class<?>) value;
final Class<?> expected = getValueClass();
if (expected.isAssignableFrom(type)) {
return true;
}
if (expected.isInterface() && !type.isInterface()) {
final int modifiers = type.getModifiers();
if (Modifier.isAbstract(modifiers) && !Modifier.isFinal(modifiers)) {
return true;
}
}
return false;
}
return super.isCompatibleValue(value);
}
}
/**
* 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$
* @version $Id$
* @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$
* @version $Id$
* @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.
*
* @param number The default value.
*/
public IntegerKey(final int number) {
super(Integer.class);
this.number = number;
}
/**
* Returns the default value.
*
* @return 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(IntegerKey.class).finer(e.toString());
}
}
return false;
}
}
/**
* A hint used to capture a configuration setting as double.
* A default value is provided and may be checked with {@link #getDefault()}.
*
* @since 2.6
* @source $URL$
* @version $Id$
* @author Jody Garnett
*/
public static final class DoubleKey extends Key {
/**
* The default value.
*/
private final double number;
/**
* Creates a new key with the specified default value.
*
* @param number The default value.
*/
public DoubleKey(final double number) {
super(Integer.class);
this.number = number;
}
/**
* Returns the default value.
*
* @return The default value.
*/
public double getDefault(){
return number;
}
/**
* Returns the value from the specified hints as a double. 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 a double, or the default value if not hint
* was explicitly set.
*/
public double toValue(final Hints hints) {
if (hints != null) {
final Object value = hints.get(this);
if (value instanceof Number) {
return ((Number) value).doubleValue();
} else if (value instanceof CharSequence) {
return Double.parseDouble(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 Float || value instanceof Double) {
return true;
}
if (value instanceof String || value instanceof InternationalString) {
try {
Double.parseDouble(value.toString());
} catch (NumberFormatException e) {
Logging.getLogger(DoubleKey.class).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$
* @version $Id$
* @author Jody Garnett
*/
public static final class OptionKey extends Key {
/**
* The set of options allowed.
*/
private final Set<String> 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.
*
* @param alternatives The available options.
*/
public OptionKey(final String... alternatives) {
super(String.class);
final Set<String> options = new TreeSet<String>(Arrays.asList(alternatives));
this.wildcard = options.remove("*");
this.options = Collections.unmodifiableSet(options);
}
/**
* Returns the set of available options.
*
* @return The available options.
*/
public Set<String> 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} or {@link Name} object.
* <p>
* Different JNDI implementations build up their name differently (so we may need
* to look for "jdbc:EPSG" in JBoss and "jdbc/EPSG" in Websphere. The
* InitialContext.combineNames( String, String ) should be used to put together
* your nam
*
* @since 2.4
* @source $URL$
* @version $Id$
* @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) || (value instanceof Name);
}
}
}