Class AbstractIdentifiedObject

java.lang.Object
org.apache.sis.io.wkt.FormattableObject
org.apache.sis.referencing.AbstractIdentifiedObject
All Implemented Interfaces:
Serializable, Formattable, Deprecable, LenientComparable, org.opengis.referencing.IdentifiedObject
Direct Known Subclasses:
AbstractCoordinateOperation, AbstractCS, AbstractDatum, AbstractParameterDescriptor, AbstractReferenceSystem, DefaultCoordinateSystemAxis, DefaultEllipsoid, DefaultOperationMethod, DefaultPrimeMeridian

public class AbstractIdentifiedObject extends FormattableObject implements org.opengis.referencing.IdentifiedObject, Formattable, LenientComparable, Deprecable, Serializable
Base class for objects identified by a name or a code. Those objects are typically geodetic datum (e.g. "World Geodetic System 1984"), Coordinate Reference System (e.g. "WGS 84 / World Mercator") or map projection (e.g. "Mercator (variant A)"). Those names, or a code (e.g. "EPSG:3395"), can be used for fetching an object from a database. However, it is not sufficient to know the object name. We also need to know who define that name (the authority) since the same objects are often named differently depending on the providers, or conversely the same name is used for different objects depending on the provider.

The main information stored in an IdentifiedObject are:

  • a primary name, considered by the object creator as the preferred name,
  • an arbitrary number of aliases, for example a list of names used by other providers,
  • an arbitrary number of identifiers, typically primary keys in the provider database,
  • optional remarks.

Instantiation

This class is conceptually abstract, even if it is technically possible to instantiate it. Applications should instead instantiate the most specific subclass having a name starting by Default. However, exceptions to this rule may occur when it is not possible to identify the exact type.
Example: It is sometimes not possible to infer the exact coordinate system type from version 1 of Well Known Text format, for example when parsing a LOCAL_CS element. In such exceptional situation, a plain AbstractCS object may be instantiated.
IdentifiedObject instances are created in two main ways:
  • Using an ObjectFactory, in which case all properties can be explicitly specified.
  • Using an AuthorityFactory, in which case only a code (typically a primary key) is specified. The authority and authority code values are set to the authority name of the factory object, and the authority code supplied by the client, respectively. All other information are fetched from the database.

Immutability and thread safety

This base class is immutable if the Citation, ReferenceIdentifier, GenericName and InternationalString instances given to the constructor are also immutable. Most SIS subclasses and related classes are immutable under similar conditions. This means that unless otherwise noted in the javadoc, IdentifiedObject instances created using only SIS factories and static constants can be shared by many objects and passed between threads without synchronization.
Since:
0.4
Version:
1.1
See Also:
  • Nested Class Summary

    Nested Classes
    Modifier and Type
    Class
    Description
    private final class 
    A writable view over the name of the enclosing object followed by all aliases which are instance of Identifier.
  • Field Summary

    Fields
    Modifier and Type
    Field
    Description
    private Collection<org.opengis.util.GenericName>
    An alternative name by which this object is identified, or null if none.
    private final boolean
    true if this object is deprecated.
    static final String
    Optional key which can be given to the constructor for specifying the object is deprecated.
    private int
    The cached hash code value, or 0 if not yet computed.
    private Set<org.opengis.referencing.ReferenceIdentifier>
    An identifier which references elsewhere the object's defining information.
    static final String
    Optional key which can be given to the constructor for specifying the locale to use for producing error messages.
    private org.opengis.referencing.ReferenceIdentifier
    The name for this object or code.
    private org.opengis.util.InternationalString
    Comments on or information about this object, or null if none.
    private static final long
    Serial number for inter-operability with different versions.

    Fields inherited from interface org.opengis.referencing.IdentifiedObject

    ALIAS_KEY, IDENTIFIERS_KEY, NAME_KEY, REMARKS_KEY
  • Constructor Summary

    Constructors
    Modifier
    Constructor
    Description
    (package private)
    Constructs a new object in which every attributes are set to a null value.
     
    Constructs an object from the given properties.
    protected
    AbstractIdentifiedObject(org.opengis.referencing.IdentifiedObject object)
    Constructs a new identified object with the same values than the specified one.
  • Method Summary

    Modifier and Type
    Method
    Description
    castOrCopy(org.opengis.referencing.IdentifiedObject object)
    Returns a SIS identified object implementation with the values of the given arbitrary implementation.
    protected long
    Invoked by hashCode() for computing the hash code when first needed.
    final boolean
    equals(Object object)
    Compares the specified object with this object for equality.
    boolean
    equals(Object object, ComparisonMode mode)
    Compares this object with the given object for equality.
    void
    formatTo(Formatter formatter, int flags, int width, int precision)
    Formats the name or identifier of this object using the provider formatter.
    protected String
    formatTo(Formatter formatter)
    Formats the inner part of the Well Known Text (WKT) representation for this object.
    Collection<org.opengis.util.GenericName>
    Returns alternative names by which this object is identified.
    org.opengis.util.InternationalString
    Returns a narrative explanation of the role of this object.
    (package private) final String
    The gml:id, which is mandatory.
    (package private) final Code
    Returns a single element from the Set<Identifier> collection, or null if none.
    Set<org.opengis.referencing.ReferenceIdentifier>
    Returns identifiers which references elsewhere the object's defining information.
    Class<? extends org.opengis.referencing.IdentifiedObject>
    Returns the GeoAPI interface implemented by this class.
    org.opengis.referencing.ReferenceIdentifier
    Returns the primary name by which this object is identified.
    (package private) final Collection<org.opengis.referencing.ReferenceIdentifier>
    Returns the name and all aliases which are also instance of Identifier.
    org.opengis.util.InternationalString
    Returns comments on or information about this object, including data source information.
    final int
    Returns a hash value for this identified object.
    illegalPropertyType(Map<String,?> properties, String key, Object value)
    Returns the exception to be thrown when a property is of illegal type.
    private boolean
    Returns true if the given object implements the same GeoAPI interface than this object.
    boolean
    Returns true if this object is deprecated.
    boolean
    Returns true if either the primary name or at least one alias matches the given string according heuristic rules.
    private void
    Invoked by JAXB at unmarhalling time for specifying the value of the gml:id attribute.
    private void
    setIdentifier(Code identifier)
    Invoked by JAXB at unmarshalling time for setting the identifier.
    private void
    setRemarks(org.opengis.util.InternationalString value)
    Invoked by JAXB for setting the remarks.

    Methods inherited from class org.apache.sis.io.wkt.FormattableObject

    print, toString, toString, toWKT

    Methods inherited from class java.lang.Object

    clone, finalize, getClass, notify, notifyAll, wait, wait, wait

    Methods inherited from interface org.opengis.referencing.IdentifiedObject

    toWKT
  • Field Details

    • serialVersionUID

      private static final long serialVersionUID
      Serial number for inter-operability with different versions.
      See Also:
    • LOCALE_KEY

      public static final String LOCALE_KEY
      Optional key which can be given to the constructor for specifying the locale to use for producing error messages. Notes:
      • The locale is not stored in any AbstractIdentifiedObject property; its value is ignored if no error occurred at construction time.
      • The locale is used on a best effort basis; not all error messages may be localized.
      See Also:
    • DEPRECATED_KEY

      public static final String DEPRECATED_KEY
      Optional key which can be given to the constructor for specifying the object is deprecated. If deprecated, then the replacement should be specified in the remarks.
      Example: "superseded by code XYZ".
      Since:
      0.6
      See Also:
    • name

      private org.opengis.referencing.ReferenceIdentifier name
      The name for this object or code. Shall never be null.

      Consider this field as final! This field is modified only at unmarshalling time by Names.add(Identifier).

      See Also:
    • alias

      private Collection<org.opengis.util.GenericName> alias
      An alternative name by which this object is identified, or null if none. We must be prepared to handle either null or an empty set for "no alias" because we may get both on unmarshalling.

      Consider this field as final! This field is modified only at unmarshalling time by Names.add(Identifier).

    • identifiers

      private Set<org.opengis.referencing.ReferenceIdentifier> identifiers
      An identifier which references elsewhere the object's defining information. Alternatively, an identifier by which this object can be referenced.

      Consider this field as final! This field is modified only at unmarshalling time by setIdentifier(Code)

      See Also:
    • remarks

      private org.opengis.util.InternationalString remarks
      Comments on or information about this object, or null if none.

      Consider this field as final! This field is modified only at unmarshalling time by setRemarks(InternationalString)

      See Also:
    • deprecated

      private final boolean deprecated
      true if this object is deprecated.
      Since:
      0.6
    • hashCode

      private transient int hashCode
      The cached hash code value, or 0 if not yet computed. This field is calculated only when first needed. We do not declare it volatile because it is not a big deal if this field is calculated many time, and the same value should be produced by all computations. The only possible outdated value is 0, which is okay.
  • Constructor Details

    • AbstractIdentifiedObject

      public AbstractIdentifiedObject(Map<String,?> properties) throws IllegalArgumentException
      Constructs an object from the given properties. Keys are strings from the table below. The map given in argument shall contain an entry at least for the "name" or "code" key. Other properties listed in the table below are optional. In particular, "authority", "code", "codespace" and "version" are convenience properties for building a name, and are ignored if the "name" property is already a ReferenceIdentifier object instead of a String.
      Recognized properties (non exhaustive list)
      Property name Value type Returned by
      "name" ReferenceIdentifier or String getName()
      "authority" String or Citation ImmutableIdentifier.getAuthority() on the name
      "code" String ImmutableIdentifier.getCode() on the name
      "codespace" String ImmutableIdentifier.getCodeSpace() on the name
      "version" String ImmutableIdentifier.getVersion() on the name
      "description" String ImmutableIdentifier.getDescription() on the name
      "alias" GenericName or CharSequence (optionally as array) getAlias()
      "identifiers" ReferenceIdentifier (optionally as array) getIdentifiers()
      "remarks" InternationalString or String getRemarks()
      "deprecated" Boolean isDeprecated()
      "locale" Locale (none)

      Localization

      All localizable attributes like "remarks" may have a language and country code suffix. For example, the "remarks_fr" property stands for remarks in French and the "remarks_fr_CA" property stands for remarks in French Canadian. They are convenience properties for building the InternationalString value.

      The "locale" property applies only in case of exception for formatting the error message, and is used only on a best effort basis. The locale is discarded after successful construction since localizations are applied by the InternationalString.toString(Locale) method.

      Properties map versus explicit arguments

      Generally speaking, information provided in the properties map are considered ignorable metadata while information provided in explicit arguments to the sub-class constructors have an impact on coordinate transformation results. See equals(Object, ComparisonMode) for more information.
      Parameters:
      properties - the properties to be given to this identified object.
      Throws:
      IllegalArgumentException - if a property has an invalid value.
    • AbstractIdentifiedObject

      protected AbstractIdentifiedObject(org.opengis.referencing.IdentifiedObject object)
      Constructs a new identified object with the same values than the specified one. This copy constructor provides a way to convert an arbitrary implementation into a SIS one or a user-defined one (as a subclass), usually in order to leverage some implementation-specific API.

      This constructor performs a shallow copy, i.e. the properties are not cloned.

      Parameters:
      object - the object to shallow copy.
    • AbstractIdentifiedObject

      AbstractIdentifiedObject()
      Constructs a new object in which every attributes are set to a null value. This is not a valid object. This constructor is strictly reserved to JAXB, which will assign values to the fields using reflection.
  • Method Details

    • illegalPropertyType

      private static IllegalArgumentException illegalPropertyType(Map<String,?> properties, String key, Object value)
      Returns the exception to be thrown when a property is of illegal type.
    • castOrCopy

      public static AbstractIdentifiedObject castOrCopy(org.opengis.referencing.IdentifiedObject object)
      Returns a SIS identified object implementation with the values of the given arbitrary implementation. This method performs the first applicable action in the following choices:
      • If the given object is null, then this method returns null.
      • Otherwise if the given object is an instance of CoordinateReferenceSystem, CoordinateSystem, CoordinateSystemAxis, Datum, Ellipsoid, PrimeMeridian, OperationMethod, CoordinateOperation, ParameterDescriptor or ParameterDescriptorGroup, then this method delegates to the castOrCopy(…) method of the corresponding SIS subclass. Note that if the given object implements more than one of the above-cited interfaces, then the castOrCopy(…) method to be used is unspecified.
      • Otherwise if the given object is already an instance of AbstractIdentifiedObject, then it is returned unchanged.
      • Otherwise a new AbstractIdentifiedObject instance is created using the copy constructor and returned. Note that this is a shallow copy operation, because the other properties contained in the given object are not recursively copied.
      Parameters:
      object - the object to get as a SIS implementation, or null if none.
      Returns:
      a SIS implementation containing the values of the given object (may be the given object itself), or null if the argument was null.
    • getInterface

      public Class<? extends org.opengis.referencing.IdentifiedObject> getInterface()
      Returns the GeoAPI interface implemented by this class. This information is part of the data compared by equals(Object, ComparisonMode).

      The default implementation returns IdentifiedObject.class. Subclasses implementing a more specific GeoAPI interface shall override this method.

      Invariants

      The following invariants must hold for all AbstractIdentifiedObject instances:
      • getInterface().isInstance(this) shall return true.
      • If A.getClass() == B.getClass() is true, then A.getInterface() == B.getInterface() shall be true. Note that the converse does not need to hold.
      Returns:
      the GeoAPI interface implemented by this class.
    • getName

      public org.opengis.referencing.ReferenceIdentifier getName()
      Returns the primary name by which this object is identified.
      Specified by:
      getName in interface org.opengis.referencing.IdentifiedObject
      Returns:
      the primary name.
      See Also:
    • getAlias

      public Collection<org.opengis.util.GenericName> getAlias()
      Returns alternative names by which this object is identified.
      Specified by:
      getAlias in interface org.opengis.referencing.IdentifiedObject
      Returns:
      the aliases, or an empty collection if there is none.
      See Also:
    • getIdentifiers

      public Set<org.opengis.referencing.ReferenceIdentifier> getIdentifiers()
      Returns identifiers which references elsewhere the object's defining information. Alternatively, identifiers by which this object can be referenced.
      Specified by:
      getIdentifiers in interface org.opengis.referencing.IdentifiedObject
      Returns:
      this object identifiers, or an empty set if there is none.
      See Also:
    • getDescription

      public org.opengis.util.InternationalString getDescription()
      Returns a narrative explanation of the role of this object.

      Default value

      The default implementation returns the description provided by this object's name.
      Returns:
      a narrative explanation of the role of this object, or null if none.
      Since:
      0.6
      See Also:
    • getRemarks

      public org.opengis.util.InternationalString getRemarks()
      Returns comments on or information about this object, including data source information. If this object is deprecated, then the remarks should give indication about the replacement (e.g. "superceded by …").
      Specified by:
      getRemarks in interface Deprecable
      Specified by:
      getRemarks in interface org.opengis.referencing.IdentifiedObject
      Returns:
      the remarks, or null if none.
    • isDeprecated

      public boolean isDeprecated()
      Returns true if this object is deprecated. Deprecated objects exist in some authority factories like the EPSG database. If this method returns true, then the remarks should give indication about the replacement (e.g. "superceded by …").
      Specified by:
      isDeprecated in interface Deprecable
      Returns:
      true if this object is deprecated.
    • isHeuristicMatchForName

      public boolean isHeuristicMatchForName(String name)
      Returns true if either the primary name or at least one alias matches the given string according heuristic rules. The default implementation returns true if the given name is equal, ignoring aspects documented below, to one of the following names: The comparison ignores the following aspects:
      • Lower/upper cases.
      • Some Latin diacritical signs (e.g. "Réunion" and "Reunion" are considered equal).
      • All characters that are not letters or digits (e.g. "Mercator (1SP)" and "Mercator_1SP" are considered equal).
      • Namespaces or scopes, because this method is typically invoked with either the value of another IdentifiedObject.getName().getCode() or with the Well Known Text (WKT) projection or parameter name.

      Usage

      This method is invoked by SIS when comparing in IGNORE_METADATA mode two objects that can be differentiated only by some identifier (name or alias), like coordinate system axes, datum, parameters and operation methods. See equals(Object, ComparisonMode) for more information.

      This method is also invoked when searching a parameter or operation method for a given name. For example, the same projection is known as "Mercator (variant A)" (the primary name according EPSG) and "Mercator (1SP)" (the legacy name prior EPSG 7.6). Since the latter is still in frequent use, SIS accepts it as an alias of the Mercator (variant A) projection.

      Overriding by subclasses

      Some subclasses add more flexibility to the comparisons:

      Future evolutions

      This method implements recommendations from the WKT 2 specification §B.5.2, together with heuristic rules learned from experience while trying to provide inter-operability with different data producers. Those rules may be adjusted in any future SIS version according experience gained while working with more data producers.
      Parameters:
      name - the name to compare with the object name or aliases.
      Returns:
      true if the primary name or at least one alias matches the specified name.
      See Also:
    • equals

      public boolean equals(Object object, ComparisonMode mode)
      Compares this object with the given object for equality. The strictness level is controlled by the second argument, from stricter to more permissive values:
      Description of comparison modes
      ModeDescription
      STRICT: Verifies if the two objects are of the same class and compares all public properties, including SIS-specific (non standard) properties.
      BY_CONTRACT: Verifies if the two objects implement the same GeoAPI interface and compares all properties defined by that interface (name, identifiers, remarks, etc). The two objects do not need to be instances of the same implementation class and SIS-specific properties are ignored.
      IGNORE_METADATA: Compares only the properties relevant to coordinate transformations. Generally speaking, the content of the properties map given at construction time is considered ignorable metadata while the explicit arguments given to the constructor (if any) are considered non-ignorable. Note that there is some exceptions to this rule of thumb — see When object name matter below.
      APPROXIMATE: Same as IGNORE_METADATA, with some tolerance threshold on numerical values.
      ALLOW_VARIANT: Same as APPROXIMATE, but ignores coordinate system axes.
      DEBUG: Special mode for figuring out why two objects expected to be equal are not.
      The main guideline is that if sourceCRS.equals(targetCRS, IGNORE_METADATA) returns true, then the transformation from sourceCRS to targetCRS should be the identity transform even if the two CRS do not have the same name.

      When object name matter

      Some subclasses (especially DefaultCoordinateSystemAxis, AbstractDatum and DefaultParameterDescriptor) will compare the name even in IGNORE_METADATA mode, because objects of those types with different names have completely different meaning. For example, nothing differentiate the "semi_major" and "semi_minor" parameters except the name. The name comparison may be lenient however, i.e. the rules may accept a name matching an alias. See isHeuristicMatchForName(String) for more information.

      Conformance to the equals(Object) method contract

      ComparisonMode.STRICT is the only mode compliant with the Object.equals(Object) contract. For all other modes, the comparison is not guaranteed to be symmetric neither transitive. See LenientComparable for more information.
      Specified by:
      equals in interface LenientComparable
      Parameters:
      object - the object to compare to this.
      mode - the strictness level of the comparison.
      Returns:
      true if both objects are equal according the given comparison mode.
      See Also:
    • implementsSameInterface

      private boolean implementsSameInterface(Object object)
      Returns true if the given object implements the same GeoAPI interface than this object.
    • equals

      public final boolean equals(Object object)
      Compares the specified object with this object for equality. This method is implemented as below (omitting assertions): Subclasses shall override equals(Object, ComparisonMode) instead of this method.
      Specified by:
      equals in interface LenientComparable
      Overrides:
      equals in class Object
      Parameters:
      object - the other object (may be null).
      Returns:
      true if both objects are equal.
      See Also:
    • hashCode

      public final int hashCode()
      Returns a hash value for this identified object. Two AbstractIdentifiedObject instances for which equals(Object) returns true shall have the same hash code value, if the hash codes are computed on the same JVM instance for both objects. The hash code value is not guaranteed to be stable between different versions of the Apache SIS library, or between libraries running on different JVM.

      Implementation note

      This method invokes computeHashCode() when first needed, then caches the result. Subclasses shall override computeHashCode() instead of this method.
      Overrides:
      hashCode in class Object
      Returns:
      the hash code value. This value may change in any future Apache SIS version.
    • computeHashCode

      protected long computeHashCode()
      Invoked by hashCode() for computing the hash code when first needed. This method is invoked at most once in normal execution, or an arbitrary number of times if Java assertions are enabled. The hash code value shall never change during the whole lifetime of this object in a JVM. The hash code value does not need to be the same in two different executions of the JVM.

      Overriding

      Subclasses can override this method for using more properties in hash code calculation. All computeHashCode() methods shall invoke super.computeHashCode(), not hashCode(). Example:
      Returns:
      the hash code value. This value may change in any future Apache SIS version.
    • formatTo

      protected String formatTo(Formatter formatter)
      Formats the inner part of the Well Known Text (WKT) representation for this object. The default implementation writes the following elements: Keywords and metadata (scope, extent, identifier and remarks) shall not be formatted here. For example if this formattable element is for a GeodeticCRS[…] element, then subclasses shall write the content starting at the insertion point shown below:

      WKT example

      Java code example

      Formatting non-standard WKT

      If the implementation cannot represent this object without violating some WKT constraints, it can uses its own (non-standard) keywords but shall declare that it did so by invoking one of the Formatter.setInvalidWKT(…) methods.

      Alternatively, the implementation may also have no WKT keyword for this object. In such case, this method shall return null.

      Specified by:
      formatTo in class FormattableObject
      Parameters:
      formatter - the formatter where to format the inner content of this WKT element.
      Returns:
      the CamelCase keyword for the WKT element, or null if unknown.
      See Also:
    • formatTo

      public void formatTo(Formatter formatter, int flags, int width, int precision)
      Formats the name or identifier of this object using the provider formatter. This method is invoked when an IdentifiedObject object is formatted using the "%s" conversion specifier of Formatter. Users don't need to invoke this method explicitly.

      If the alternate flags is present (as in "%#s"), then this method will format the identifier (if present) instead of the object name.

      Specified by:
      formatTo in interface Formattable
      Parameters:
      formatter - the formatter in which to format this identified object.
      flags - whether to apply left alignment, use upper-case letters and/or use alternate form.
      width - minimal number of characters to write, padding with ' ' if necessary.
      precision - maximal number of characters to write, or -1 if no limit.
      Since:
      1.1
      See Also:
    • getID

      final String getID()
      The gml:id, which is mandatory. The current implementation searches for the first identifier, regardless its authority. If no identifier is found, then the name or aliases are used. If none of the above is found (which should not occur for valid objects), then this method returns null.

      If an identifier or a name has been found, this method returns the concatenation of the following elements separated by hyphens:

      • The code space in lower case, retaining only characters that are valid for Unicode identifiers.
      • The object type as defined in OGC's URN (see DefinitionURI)
      • The object code, retaining only characters that are valid for Unicode identifiers.
      Example: "epsg-crs-4326".

      The returned ID needs to be unique only in the XML document being marshalled. Consecutive invocations of this method do not need to return the same value, since it may depends on the marshalling context.

    • setID

      private void setID(String id)
      Invoked by JAXB at unmarhalling time for specifying the value of the gml:id attribute. That GML identifier is not actually stored in this AbstractIdentifiedObject since we rather generate it dynamically from the ISO 19111 identifiers. But we still need to declare that identifier to our unmarshaller context, in case it is referenced from elsewhere in the XML document.
    • getIdentifier

      final Code getIdentifier()
      Returns a single element from the Set<Identifier> collection, or null if none. We have to define this method because ISO 19111 defines the identifiers property as a collection while GML 3.2 defines it as a singleton.

      This method searches for the following identifiers, in preference order:

      • The first identifier having a code that begin with "urn:".
      • The first identifier having a code that begin with "http:".
      • The first identifier, converted to the "urn: syntax if possible.
    • setIdentifier

      private void setIdentifier(Code identifier)
      Invoked by JAXB at unmarshalling time for setting the identifier.
    • getNames

      final Collection<org.opengis.referencing.ReferenceIdentifier> getNames()
      Returns the name and all aliases which are also instance of Identifier. The latter happen often in SIS implementation since many aliases are instance of NamedIdentifier.

      The returned collection is live: adding elements in that collection will modify this AbstractIdentifiedObject instance. This is needed for unmarshalling with JAXB and should not be used in other context.

      Why there is no setNames(…) method

      Some JAXB implementations never invoke setter method for collections. Instead, they invoke the getter and add directly the identifiers in the returned collection. Whether JAXB will perform or not a final call to setNames(…) is JAXB-implementation dependent (JDK7 does but JDK6 and JDK8 early access do not). It seems a more portable approach (at least for JAXB reference implementations) to design our class without setter method, in order to have the same behavior on all supported JDK versions.
      See Also:
    • setRemarks

      private void setRemarks(org.opengis.util.InternationalString value)
      Invoked by JAXB for setting the remarks.
      See Also: