/* * Geotools 2 - OpenSource mapping toolkit * (C) 2003, Geotools Project Managment Committee (PMC) * (C) 2001, Institut de Recherche pour le Développement * * 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; either * version 2.1 of the License, or (at your option) any later version. * * 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. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * * This package contains documentation from OpenGIS specifications. * OpenGIS consortium's work is fully acknowledged here. */ package org.geotools.cs; // OpenGIS legacy dependencies import java.io.ObjectStreamException; import java.io.Serializable; import java.lang.ref.Reference; import java.lang.ref.WeakReference; import java.rmi.RemoteException; import java.rmi.server.UnicastRemoteObject; import java.util.Locale; import java.util.Map; import org.geotools.referencing.NamedIdentifier; import org.geotools.resources.RemoteProxy; import org.geotools.resources.Utilities; import org.geotools.resources.i18n.ErrorKeys; import org.geotools.resources.i18n.Errors; import org.geotools.units.Unit; import org.geotools.util.SimpleInternationalString; import org.geotools.util.WeakHashSet; import org.opengis.cs.CS_AngularUnit; import org.opengis.cs.CS_Info; import org.opengis.cs.CS_LinearUnit; import org.opengis.cs.CS_Unit; import org.opengis.metadata.Identifier; import org.opengis.util.InternationalString; /** * A base class for metadata applicable to coordinate system objects. * The metadata items "Abbreviation", "Alias", "Authority", "AuthorityCode", * "Name" and "Remarks" were specified in the Simple Features interfaces, * so they have been kept here. * * This specification does not dictate what the contents of these items * should be. However, the following guidelines are suggested: *
get
methods except {@link #getName}.
* For example, the method {@link #getAuthorityCode} returns the value
* of property "authorityCode"
. May be null
* if there are no properties for this object.
*/
private final Map properties;
/**
* OpenGIS object returned by {@link #cachedOpenGIS}.
* It may be a hard or a weak reference.
*/
private transient Object proxy;
/**
* Creates an object with the specified name. If name
* implements the {@link java.util.Map} interface, then its values
* will be copied for the following keys:
* null
for a default
* locale.
* If no string is available for the specified locale, an arbitrary
* locale is used.
*
* @see org.opengis.cs.CS_Info#getName()
*/
public Identifier getName() {
return name;
}
/**
* Gets the authority name, or null
if unspecified.
* An Authority is an organization that maintains definitions of Authority
* Codes. For example the European Petroleum Survey Group (EPSG) maintains
* a database of coordinate systems, and other spatial referencing objects,
* where each object has a code number ID. For example, the EPSG code for
* a WGS84 Lat/Lon coordinate system is '4326'.
*
* @param locale The desired locale, or null
for the default
* locale.
* If no string is available for the specified locale, an arbitrary
* locale is used.
*
* @see org.opengis.cs.CS_Info#getAuthority()
*/
public String getAuthority(final Locale locale) {
return getProperty("authority");
}
/**
* Gets the authority-specific identification code, or null
* if unspecified. The AuthorityCode is a compact string defined by an
* Authority to reference a particular spatial reference object.
* For example, the European Survey Group (EPSG) authority uses 32 bit
* integers to reference coordinate systems, so all their code strings
* will consist of a few digits. The EPSG code for WGS84 Lat/Lon is '4326'
*
* @param locale The desired locale, or null
for the default
* locale.
* If no string is available for the specified locale, an arbitrary
* locale is used.
*
* @see org.opengis.cs.CS_Info#getAuthorityCode()
*
* @deprecated Replaced by {@link NamedIdentifier#getCode}.
*/
public String getAuthorityCode(final Locale locale) {
return getProperty("authorityCode");
}
/**
* Gets the alias, or null
if there is none.
*
* @param locale The desired locale, or null
for the default
* locale.
* If no string is available for the specified locale, an arbitrary
* locale is used.
*
* @see org.opengis.cs.CS_Info#getAlias()
*
* @deprecated Replaced by {@link org.geotools.referencing.AbstractIdentifiedObject#getIdentifiers}.
*/
public String getAlias(final Locale locale) {
return getProperty("alias");
}
/** For compatibility with GeoAPI interfaces. */
public java.util.Collection getAlias() {
return java.util.Collections.EMPTY_SET;
}
/**
* Gets the abbreviation, or null
if there is none.
*
* @param locale The desired locale, or null
for the default
* locale.
* If no string is available for the specified locale, an arbitrary
* locale is used.
*
* @see org.opengis.cs.CS_Info#getAbbreviation()
*
* @deprecated No replacement.
*/
public String getAbbreviation(final Locale locale) {
return getProperty("abbreviation");
}
/**
* Gets the provider-supplied remarks,
* or null
if there is none.
*
* @param locale The desired locale, or null
for the default
* locale.
* If no string is available for the specified locale, an arbitrary
* locale is used.
*
* @see org.opengis.cs.CS_Info#getRemarks()
*/
public InternationalString getRemarks() {
return remarks;
}
/**
* Gets the property for the specified key,
* or null
if there is none.
*
* @param key The key.
*/
private String getProperty(final String key) {
return (properties!=null) ? (String) properties.get(key) : null;
}
/**
* Compare two objects for equality.
*
* @param object1 The first object to compare (may be null
).
* @param object2 The second object to compare (may be null
).
* @return true
if both objects are equal.
*/
static boolean equals(final Object object1, final Object object2) {
return Utilities.equals(object1, object2);
}
/**
* Compare two objects for equality.
*
* @param object1 The first object to compare (may be null
).
* @param object2 The second object to compare (may be null
).
* @param compareNames true
for performing a strict comparaison, or
* false
for comparing only properties relevant to transformations.
* @return true
if both objects are equal.
*/
static boolean equals(final Info object1, final Info object2, final boolean compareNames) {
return (object1==object2) || (object1!=null && object1.equals(object2, compareNames));
}
/**
* Compare this object with the specified object for equality.
*
* If compareNames
is true
, then all available properties
* are compared including {@linkplain #getName name}, {@linkplain #getAlias alias},
* {@linkplain #getAuthorityCode authority code}, etc.
*
* If compareNames
is false
, then this method compare
* only the properties needed for computing transformations. In other words,
* sourceCS.equals(targetCS, false)
returns true
if
* the transformation from sourceCS
to targetCS
is
* the identity transform, no matter what {@link #getName} saids.
*
* @param object The object to compare to this
.
* @param compareNames true
for performing a strict comparaison, or
* false
for comparing only properties relevant to transformations.
* @return true
if both objects are equal.
*/
public boolean equals(final Info object, final boolean compareNames) {
if (object!=null && object.getClass().equals(getClass())) {
if (!compareNames) {
return true;
}
return equals(name, object.name) &&
equals(properties, object.properties);
}
return false;
}
/**
* Compares the specified object with this info for equality.
* The default implementation invokes {@link #equals(Info, boolean)}.
*
* @param object The other object (may be null
).
* @return true
if both objects are equal.
*/
public final boolean equals(final Object object) {
return (object instanceof Info) && equals((Info)object, true);
}
/**
* Returns a hash value for this info. {@linkplain #getName Name},
* {@linkplain #getAlias alias}, {@linkplain #getAuthorityCode authority code}
* and the like are not taken in account. In other words, two info objects
* will return the same hash value if they are equal in the sense of
* {@link #equals equals}(Info, false)
.
*
* @return The hash code value. This value doesn't need to be the same
* in past or future versions of this class.
*/
public int hashCode() {
// Subclasses need to override this!!!!
return (int)serialVersionUID ^ getClass().hashCode();
}
/**
* Returns a Well Known Text (WKT) for this info.
* "Well known text" are part of OpenGIS's specification.
*
* @see org.opengis.cs.CS_Info#getWKT()
*/
public String toString() {
final String WKT = getProperty("WKT");
return (WKT!=null) ? WKT : toString(null);
}
/**
* Returns a Well Known Text (WKT) for this info.
*
* @param context The contextual unit. Most subclasses will
* ignore this argument, except {@link PrimeMeridian}.
*/
final String toString(final Unit context) {
final Locale locale = null;
final StringBuffer buffer = new StringBuffer(40);
buffer.append("[\"");
buffer.append(getWKTName(locale));
buffer.append('"');
buffer.insert(0, addString(buffer, context));
if (properties!=null) {
final String authority = getAuthority(locale);
if (authority!=null) {
buffer.append(", AUTHORITY[\"");
buffer.append(authority);
final String code = getAuthorityCode(locale);
if (code!=null) {
buffer.append("\",\"");
buffer.append(code);
}
buffer.append("\"]");
}
}
buffer.append(']');
return buffer.toString();
}
/**
* Returns the name to place into the WKT.
* To be overriden by {@link Projection} only.
*/
String getWKTName(final Locale locale) {
return name.getCode();
}
/**
* Adds more information inside the "[...]" part of {@link #toString}.
* The default implementation adds nothing. Subclasses will override
* this method in order to complete string representation.
*
* @param buffer The buffer to add the string to.
* @param context The contextual unit. Most subclasses will
* ignore this argument, except {@link PrimeMeridian}.
* @return The WKT code name (e.g. "GEOGCS").
*/
String addString(final StringBuffer buffer, final Unit context) {
return Utilities.getShortClassName(this);
}
/**
* Adds a unit in WKT form.
*/
final void addUnit(final StringBuffer buffer, final Unit unit) {
if (unit!=null) {
buffer.append("UNIT[\"");
buffer.append(unit.getLocalizedName());
buffer.append('"');
Unit base=null;
if (Unit.METRE.canConvert(unit)) {
base = Unit.METRE;
} else if (Unit.RADIAN.canConvert(unit)) {
base = Unit.RADIAN;
} else if (Unit.SECOND.canConvert(unit)) {
base = Unit.SECOND;
}
if (unit!=null) {
buffer.append(',');
buffer.append(base.convert(1, unit));
}
buffer.append(']');
}
}
/**
* Makes sure an argument is non-null. This is a
* convenience method for subclass constructors.
*
* @param name Argument name.
* @param object User argument.
* @throws IllegalArgumentException if object
is null.
*/
protected static void ensureNonNull(final String name, final Object object)
throws IllegalArgumentException
{
if (object==null) {
throw new IllegalArgumentException(Errors.format(
ErrorKeys.NULL_ARGUMENT_$1, name));
}
}
/**
* Makes sure an array element is non-null.
*
* @param name Argument name.
* @param array User argument.
* @param index Element to check.
* @throws IllegalArgumentException if array[i]
is null.
*/
static void ensureNonNull(final String name, final Object[] array, final int index)
throws IllegalArgumentException
{
if (array[index]==null) {
throw new IllegalArgumentException(Errors.format(
ErrorKeys.NULL_ARGUMENT_$1, name+'['+index+']'));
}
}
/**
* Makes sure that the specified unit is a temporal one.
*
* @param unit Unit to check.
* @throws IllegalArgumentException if unit
is not a temporal
* unit.
*/
static void ensureTimeUnit(final Unit unit) throws IllegalArgumentException {
if (!Unit.SECOND.canConvert(unit)) {
throw new IllegalArgumentException(Errors.format(
ErrorKeys.NON_TEMPORAL_UNIT_$1, unit));
}
}
/**
* Makes sure that the specified unit is a linear one.
*
* @param unit Unit to check.
* @throws IllegalArgumentException if unit
is not a linear
* unit.
*/
static void ensureLinearUnit(final Unit unit) throws IllegalArgumentException {
if (!Unit.METRE.canConvert(unit)) {
throw new IllegalArgumentException(Errors.format(
ErrorKeys.NON_LINEAR_UNIT_$1, unit));
}
}
/**
* Makes sure that the specified unit is an angular one.
*
* @param unit Unit to check.
* @throws IllegalArgumentException if unit
is not an angular
* unit.
*/
static void ensureAngularUnit(final Unit unit) throws IllegalArgumentException {
if (!Unit.DEGREE.canConvert(unit)) {
throw new IllegalArgumentException(Errors.format(
ErrorKeys.NON_ANGULAR_UNIT_$1, unit));
}
}
/**
* Serialize a single instance of this object.
* This is an optimisation for speeding up RMI.
*/
Object writeReplace() throws ObjectStreamException {
return pool.canonicalize(this);
}
/**
* Returns a reference to a unique instance of this Info
.
* This method is automatically invoked during deserialization.
*
* NOTE ABOUT ACCESS-MODIFIER: This method can't be private,
* because it would prevent it from being invoked from subclasses
* in this package (e.g. {@link CoordinateSystem}). This method
* will not be invoked for classes outside this package,
* unless we give it protected
access. TODO: Would
* it be a good idea?
*/
Object readResolve() throws ObjectStreamException {
return pool.canonicalize(this);
}
/**
* Returns an OpenGIS interface for this info.
* The returned object is suitable for RMI use.
*
* Note: The returned type is a generic {@link Object} in order
* to avoid premature class loading of OpenGIS interface.
*
* @param adapters The originating {@link Adapters}.
* @return The OpenGIS interface for this info.
* @throws RemoteException if the object can't be exported.
*/
Object toOpenGIS(final Object adapters) throws RemoteException {
return new Export(adapters);
}
/**
* Returns an OpenGIS interface for this info. This method first looks in the cache.
* If no interface was previously cached, then this method creates a new adapter and
* caches the result.
*
* @param adapters The originating {@link Adapters}.
* @return The OpenGIS interface for this info.
* @throws RemoteException if the object can't be exported.
*/
final synchronized Object cachedOpenGIS(final Object adapters) throws RemoteException {
if (proxy!=null) {
if (proxy instanceof Reference) {
final Object ref = ((Reference) proxy).get();
if (ref!=null) {
return ref;
}
} else {
return proxy;
}
}
final Object opengis = toOpenGIS(adapters);
proxy = new WeakReference(opengis);
return opengis;
}
/** For compatibility with GeoAPI interfaces. */
public java.util.Set/*