Module org.apache.sis.metadata
Package org.apache.sis.metadata

# Class AbstractMetadata

Object
AbstractMetadata

All Implemented Interfaces:
  `Emptiable`, `LenientComparable`

Direct Known Subclasses:
  `ModifiableMetadata`

public abstract class AbstractMetadata extends Object implements LenientComparable, Emptiable
Provides basic operations using Java reflection for metadata implementations. All `AbstractMetadata` instances shall be associated to a `MetadataStandard`. The metadata standard is given by the `getStandard()` method and is typically a constant fixed by the subclass.

There is a large number of `AbstractMetadata` subclasses (not necessarily as direct children) for the same standard, where each subclass implement one Java interface defined by the metadata standard. This base class reduces the effort required to implement those metadata interfaces by providing `equals(Object)`, `hashCode()` and `toString()` implementations. Those methods are implemented using Java reflection for invoking the getter methods defined by the `MetadataStandard`.

`AbstractMetadata` subclasses may be read-only or read/write, at implementation choice. The methods that modify the metadata may throw `UnmodifiableMetadataException` if the metadata does not support the operation. Those methods are:

Metadata operations
Read-only operations | Read/write operations
--- | ---
`isEmpty()` `asMap()` with `get` operations `asTreeTable()` with `getValue` operations `equals(Object, ComparisonMode)` | `prune()` `asMap()` with `put` operations `asTreeTable()` with `setValue` operations

## Thread safety

Instances of this class are not synchronized for multi-threading. Synchronization, if needed, is caller's responsibility. Note that synchronization locks are not necessarily the metadata instances. For example, another common approach is to use a single lock for the whole metadata tree (including children).

Since:
  0.3
See Also:
  - `MetadataStandard`

- ## Constructor Summary

  Constructors
  Modifier
  Constructor
  Description
  `protected`
  `AbstractMetadata()`
  Creates an initially empty metadata.

- ## Method Summary

  Modifier and Type
  Method
  Description
  `Map<String,Object>`
  `asMap()`
  Returns a view of the property values in a `Map`.
  `TreeTable`
  `asTreeTable()`
  Returns the property types and values as a tree table.
  `final boolean`
  `equals(Object object)`
  Performs a strict comparison of this metadata with the given object.
  `boolean`
  `equals(Object object, ComparisonMode mode)`
  Compares this metadata with the specified object for equality.
  `Class<?>`
  `getInterface()`
  Returns the metadata interface implemented by this class.
  `abstract MetadataStandard`
  `getStandard()`
  Returns the metadata standard implemented by subclasses.
  `int`
  `hashCode()`
  Computes a hash code value for this metadata using Java reflection.
  `boolean`
  `isEmpty()`
  Returns `true` if this metadata contains only `null`, nil or empty properties.
  `void`
  `prune()`
  Removes all references to empty properties.
  `String`
  `toString()`
  Returns a string representation of this metadata.

  ### Methods inherited from class Object

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

- ## Constructor Details

  - ### AbstractMetadata

    protected AbstractMetadata()
    Creates an initially empty metadata.

- ## Method Details

  - ### getStandard

    public abstract MetadataStandard getStandard()
    Returns the metadata standard implemented by subclasses. Subclasses will typically return a hard-coded constant such as `MetadataStandard​.ISO_19115`.

    #### Note for implementers

    Implementation of this method shall not depend on the object state, since this method may be indirectly invoked by copy constructors.
    Returns:
      the metadata standard implemented.

  - ### getInterface

    public Class<?> getInterface()
    Returns the metadata interface implemented by this class. It should be one of the interfaces defined in the metadata standard implemented by this class.
    Returns:
      the standard interface implemented by this implementation class.
    See Also:
      - `MetadataStandard​.getInterface(Class)`

  - ### isEmpty

    public boolean isEmpty()
    Returns `true` if this metadata contains only `null`, nil or empty properties. A non-null and non-nil property is considered empty in any of the following cases:
    - An empty character sequences.
    - An empty collection or an empty array.
    - A collection or array containing only `null`, nil or empty elements.
    - Another metadata object containing only `null`, nil or empty properties.
    Note that empty properties can be removed by calling the `prune()` method.

    #### Note for implementers

    The default implementation uses Java reflection indirectly, by iterating over all entries returned by `MetadataStandard​.asValueMap(Object, Class, KeyNamePolicy, ValueExistencePolicy)`. Subclasses that override this method should usually not invoke `super​.isEmpty()`, because the Java reflection will discover and process the properties defined in the subclasses - which is usually not the intent when overriding a method.
    Specified by:
      `isEmpty` in interface `Emptiable`
    Returns:
      `true` if this metadata is empty.
    See Also:
      - `DefaultGeographicBoundingBox​.isEmpty()`

  - ### prune

    public void prune()
    Removes all references to empty properties. The default implementation iterates over all non null properties, and sets to `null` the properties for which `isEmpty()` returned `true`.
    Throws:
      `UnmodifiableMetadataException` - if this metadata is not modifiable.

  - ### asMap

    public Map<String,Object> asMap()
    Returns a view of the property values in a `Map`. The map is backed by this metadata object, so changes in the underlying metadata object are immediately reflected in the map and conversely.

    #### Supported operations

    The map supports the `put(…)` and `remove(…)` operations if the underlying metadata object contains setter methods. The `remove(…)` method is implemented by a call to `put(…, null)`.

    #### Keys and values

    The keys are case-insensitive and can be either the JavaBeans property name, the getter method name or the UML identifier. The value given to a call to the `put(…)` method shall be an instance of the type expected by the corresponding setter method, or an instance of a type convertible to the expected type.

    #### Multi-values entries

    Calls to `put(…)` replace the previous value, with one noticeable exception: if the metadata property associated to the given key is a `Collection` but the given value is a single element (not a collection), then the given value is added to the existing collection. In other words, the returned map behaves as a multi-values map for the properties that allow multiple values. If the intent is to unconditionally discard all previous values, then make sure that the given value is a collection when the associated metadata property expects such collection.

    #### Default implementation

    The default implementation is equivalent to the following:

        return getStandard().asValueMap(this, null,
                KeyNamePolicy.JAVABEANS_PROPERTY,
                ValueExistencePolicy.NON_EMPTY);

    Returns:
      a view of this metadata object as a map.
    See Also:
      - `MetadataStandard​.asValueMap(Object, Class, KeyNamePolicy, ValueExistencePolicy)`

  - ### asTreeTable

    public TreeTable asTreeTable()
    Returns the property types and values as a tree table. The tree table is backed by the metadata object using Java reflection, so changes in the underlying metadata object are immediately reflected in the tree table and conversely.

    The returned `TreeTable` instance contains the following columns:

    - `TableColumn​.IDENTIFIER`
      The UML identifier if any, or the Java Beans property name otherwise, of a metadata property. For example in a tree table view of `DefaultCitation`, there is a node having the `"title"` identifier.
    - `TableColumn​.INDEX`
      If the metadata property is a collection, then the zero-based index of the element in that collection. Otherwise `null`. For example, in a tree table view of `DefaultCitation`, if the `"alternateTitle"` collection contains two elements, then there is a node with index 0 for the first element and another node with index 1 for the second element.
      Note: The `(IDENTIFIER, INDEX)` pair can be used as a primary key for uniquely identifying a node in a list of children. That uniqueness is guaranteed only for the children of a given node; the same keys may appear in the children of any other nodes.
    - `TableColumn​.NAME`
      A human-readable name for the node, derived from the identifier and the index. This is the column shown in the default `toString()` implementation and may be localizable.
    - `TableColumn​.TYPE`
      The base type of the value (usually an interface).
    - `TableColumn​.VALUE`
      The metadata value for the node. Values in this column are writable if the underlying metadata class have a setter method for the property represented by the node.
    - `TableColumn​.REMARKS`
      Remarks or warning on the property value. This is rarely present. It is provided when the value may look surprising, for example the longitude values in a geographic bounding box crossing the anti-meridian.

    #### Write operations

    Only the `VALUE` column may be writable, with one exception: newly created children need to have their `IDENTIFIER` set before any other operation. For example, the following code adds a title to a citation:

        TreeTable.Node node = ...;                               // The node for a DefaultCitation.
        TreeTable.Node child = node.newChild();
        child.setValue(TableColumn.IDENTIFIER, "title");
        child.setValue(TableColumn.VALUE, "Le petit prince");
        // Nothing else to do - the child node has been added.

    Nodes can be removed by invoking the `Iterator​.remove()` method on the children iterator.

    #### Default implementation

    The default implementation is equivalent to the following:

        return getStandard().asTreeTable(this, null, ValueExistencePolicy.COMPACT);

    Returns:
      a tree table representation of the specified metadata.
    See Also:
      - `MetadataStandard​.asTreeTable(Object, Class, ValueExistencePolicy)`

  - ### equals

    public boolean equals(Object object, ComparisonMode mode)
    Compares this metadata with the specified object for equality. The default implementation uses Java reflection. Subclasses may override this method for better performances, or for comparing "hidden" properties not specified by the GeoAPI (or other standard) interface.
    Specified by:
      `equals` in interface `LenientComparable`
    Parameters:
      `object` - the object to compare with this metadata.
      `mode` - the strictness level of the comparison.
    Returns:
      `true` if the given object is equal to this metadata.
    See Also:
      - `Utilities​.deepEquals(Object, Object, ComparisonMode)`

  - ### equals

    public final boolean equals(Object object)
    Performs a strict comparison of this metadata with the given object. This method is implemented as below:

            public final boolean equals(final Object object) {
                return equals(object, ComparisonMode.STRICT);
            }

    If a subclass needs to override the behavior of this method, then override `equals(Object, ComparisonMode)` instead.
    Specified by:
      `equals` in interface `LenientComparable`
    Overrides:
      `equals` in class `Object`
    Parameters:
      `object` - the object to compare with this metadata for equality.
    Returns:
      `true` if the given object is strictly equals to this metadata.
    See Also:
      - `ComparisonMode​.STRICT`

  - ### hashCode

    public int hashCode()
    Computes a hash code value for this metadata using Java reflection. The hash code is defined as the sum of hash code values of all non-empty properties, excluding cyclic dependencies. For acyclic metadata, this method contract is compatible with the `Set​.hashCode()` one and ensures that the hash code value is insensitive to the ordering of properties.

    #### Implementation note

    This method does not cache the value because current implementation has no notification mechanism for tracking changes in children properties. If this metadata is known to be immutable, then subclasses may consider caching the hash code value if performance is important.
    Overrides:
      `hashCode` in class `Object`
    See Also:
      - `MetadataStandard​.hashCode(Object)`

  - ### toString

    public String toString()
    Returns a string representation of this metadata. The default implementation is as below:

        return asTreeTable().toString();

    Note that this make extensive use of Unicode characters and is better rendered with a monospaced font.
    Overrides:
      `toString` in class `Object`