Package org.apache.sis.metadata

# Class ModifiableMetadata

Object
AbstractMetadata
ModifiableMetadata

All Implemented Interfaces:
  `Emptiable`, `LenientComparable`

Direct Known Subclasses:
  `ISOMetadata`

public abstract class ModifiableMetadata extends AbstractMetadata
Base class of metadata having an editable content. Newly created `ModifiableMetadata` are initially in editable state. The metadata can be populated using the setter methods provided by subclasses, then transition to the final state for making it safe to share by many consumers.

## Tip for subclass implementations

Subclasses can follow the pattern below for every `get` and `set` methods, with a different processing for singleton value or for collections.

>     public class MyMetadata {

>         // ==== Example for a singleton value =============================

>         private Foo property;

>         public Foo getProperty() {
>             return property;
>         }

>         public void setProperty(Foo newValue) {
>             checkWritePermission();
>             property = newValue;
>         }

>         // ==== Example for a collection ==================================

>         private Collection<Foo> properties;

>         public Collection<Foo> getProperties() {
>             return properties = nonNullCollection(properties, Foo.class);
>         }

>         public void setProperties(Collection<Foo> newValues) {
>             // the call to checkWritePermission() is implicit
>             properties = writeCollection(newValues, properties, Foo.class);
>         }
>     }

Since:
  0.3

Defined in the `sis-metadata` module

- ## Nested Class Summary

  Nested Classes
  Modifier and Type
  Class
  Description
  `static enum`
  `ModifiableMetadata​.State`
  Whether the metadata is still editable or has been made final.

- ## Constructor Summary

  Constructors
  Modifier
  Constructor
  Description
  `protected`
  `ModifiableMetadata()`
  Constructs an initially empty metadata.

- ## Method Summary

  Modifier and Type
  Method
  Description
  `protected void`
  `checkWritePermission(Object current)`
  Checks if changes in the metadata are allowed.
  `protected <E> Class<? extends Collection<E>>`
  `collectionType(Class<E> elementType)`
  Returns the type of collection to use for the given type.
  `protected final <E> Collection<E>`
  `copyCollection(Collection<? extends E> source, Class<E> elementType)`
  Creates a list or set with the content of the `source` collection, or returns `null` if the source is `null` or empty.
  `protected final <E> List<E>`
  `copyList(Collection<? extends E> source, Class<E> elementType)`
  Creates a list with the content of the `source` collection, or returns `null` if the source is `null` or empty.
  `protected final <K, V> Map<K,V>`
  `copyMap(Map<? extends K,? extends V> source, Class<K> keyType)`
  Creates a map with the content of the `source` map, or returns `null` if the source is `null` or empty.
  `protected final <E> Set<E>`
  `copySet(Collection<? extends E> source, Class<E> elementType)`
  Creates a set with the content of the `source` collection, or returns `null` if the source is `null` or empty.
  `ModifiableMetadata`
  `deepCopy(ModifiableMetadata​.State target)`
  Copies (if necessary) this metadata and all its children.
  `protected final <E> Collection<E>`
  `nonNullCollection(Collection<E> current, Class<E> elementType)`
  Returns the specified collection, or a new one if `current` is null.
  `protected final <E> List<E>`
  `nonNullList(List<E> current, Class<E> elementType)`
  Returns the specified list, or a new one if `current` is null.
  `protected final <K, V> Map<K,V>`
  `nonNullMap(Map<K,V> current, Class<K> keyType)`
  Returns the specified map, or a new one if `current` is null.
  `protected final <E> Set<E>`
  `nonNullSet(Set<E> current, Class<E> elementType)`
  Returns the specified set, or a new one if `current` is null.
  `protected final <E> Collection<E>`
  `singleton(E value, Class<E> elementType)`
  Creates a singleton list or set containing only the given value, if non-null.
  `ModifiableMetadata​.State`
  `state()`
  Tells whether this instance of metadata is editable.
  `boolean`
  `transitionTo(ModifiableMetadata​.State target)`
  Requests this metadata instance and (potentially) all its children to transition to a new state.
  `protected final <E> Collection<E>`
  `writeCollection(Collection<? extends E> source, Collection<E> target, Class<E> elementType)`
  Writes the content of the `source` collection into the `target` list or set, creating it if needed.
  `protected final <E> List<E>`
  `writeList(Collection<? extends E> source, List<E> target, Class<E> elementType)`
  Writes the content of the `source` collection into the `target` list, creating it if needed.
  `protected final <K, V> Map<K,V>`
  `writeMap(Map<? extends K,? extends V> source, Map<K,V> target, Class<K> keyType)`
  Writes the content of the `source` map into the `target` map, creating it if needed.
  `protected final <E> Set<E>`
  `writeSet(Collection<? extends E> source, Set<E> target, Class<E> elementType)`
  Writes the content of the `source` collection into the `target` set, creating it if needed.

  ### Methods inherited from class AbstractMetadata

  `asMap, asTreeTable, equals, equals, getInterface, getStandard, hashCode, isEmpty, prune, toString`

  ### Methods inherited from class Object

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

- ## Constructor Details

  - ### ModifiableMetadata

    protected ModifiableMetadata()
    Constructs an initially empty metadata. The initial state is `ModifiableMetadata​.State​.EDITABLE`.

- ## Method Details

  - ### state

    public ModifiableMetadata.State state()
    Tells whether this instance of metadata is editable. This is initially `ModifiableMetadata​.State​.EDITABLE` for new `ModifiableMetadata` instances, but can be changed by a call to `transitionTo(State)`.

    `ModifiableMetadata​.State​.FINAL` implies that all properties are also final. This recursivity does not necessarily apply to other states. For example `ModifiableMetadata​.State​.EDITABLE` does not imply that all `ModifiableMetadata` children are also editable.

    API note: the `ModifiableMetadata` state is not a metadata per se, but rather an information about this particular instance of a metadata class. Two metadata instances may be in different states but still have the same metadata content. For this reason, this method does not have `get` prefix for avoiding confusion with getter and setter methods of metadata properties.
    Returns:
      the state (editable, completable or final) of this `ModifiableMetadata` instance.
    Since:
      1.0

  - ### transitionTo

    public boolean transitionTo(ModifiableMetadata.State target)
    Requests this metadata instance and (potentially) all its children to transition to a new state. The action performed by this method depends on the source state and the given target state, as listed in the following table:

    State transitions
    Current state | Target state | Action
    --- | --- | ---
    Any | Same | Does nothing and returns `false`.
    `ModifiableMetadata.State.EDITABLE` | `ModifiableMetadata.State.COMPLETABLE` | Marks this metadata and all children as completable.
    Any | `ModifiableMetadata.State.FINAL` | Marks this metadata and all children as unmodifiable.
    `ModifiableMetadata.State.FINAL` | Any other | Throws `UnmodifiableMetadataException`.

    The effect of invoking this method may be recursive. For example transitioning to `ModifiableMetadata​.State​.FINAL` implies transitioning all children `ModifiableMetadata` instances to the final state too.
    Parameters:
      `target` - the desired new state (editable, completable or final).
    Returns:
      `true` if the state of this `ModifiableMetadata` changed as a result of this method call.
    Throws:
      `UnmodifiableMetadataException` - if a transition to a less restrictive state (e.g. from `ModifiableMetadata​.State​.FINAL` to `ModifiableMetadata​.State​.EDITABLE`) was attempted.
    Since:
      1.0

  - ### deepCopy

    public ModifiableMetadata deepCopy(ModifiableMetadata.State target)
    Copies (if necessary) this metadata and all its children. Changes in the returned metadata will not affect this `ModifiableMetadata` instance, and conversely. The returned metadata will be in the state specified by the `target` argument. The state of this `ModifiableMetadata` instance stay unchanged.

    As a special case, this method returns `this` if and only if the specified target is `ModifiableMetadata​.State​.FINAL` and this `ModifiableMetadata` instance is already in final state. In that particular case, copies are not needed for protecting metadata against changes because neither `this` or the returned value can be modified.

    This method is typically invoked for getting a modifiable metadata from an unmodifiable one:

    >     Metadata source  = ...;          // Any implementation.
    >     DefaultMetadata md = DefaultMetadata.castOrCopy(source);
    >     md = (DefaultMetadata) md.deepCopy(DefaultMetadata.State.EDITABLE);

    #### Alternative

    If unconditional copy is desired, or if the metadata to copy may be arbitrary implementations of GeoAPI interfaces (i.e. not necessarily a `ModifiableMetadata` subclass), then the following code can be used instead:

    >     MetadataCopier copier = new MetadataCopier(MetadataStandard.ISO_19115);
    >     Metadata source = ...;                           // Any implementation.
    >     Metadata copy = copier.copy(Metadata.class, source);

    The `Metadata` type in above example can be replaced by any other ISO 19115 type. Types from other standards can also be used if the `MetadataStandard​.ISO_19115` constant is replaced accordingly.
    Parameters:
      `target` - the desired state (editable, completable or final).
    Returns:
      a copy (except in above-cited special case) of this metadata in the specified state.
    Since:
      1.1
    See Also:
      - `MetadataCopier`
      - `DefaultMetadata​.deepCopy(Metadata)`

  - ### checkWritePermission

    protected void checkWritePermission(Object current) throws UnmodifiableMetadataException
    Checks if changes in the metadata are allowed. All `setFoo(…)` methods in subclasses shall invoke this method (directly or indirectly) before to apply any change. The current property value should be specified in argument.
    Parameters:
      `current` - the current value, or `null` if none.
    Throws:
      `UnmodifiableMetadataException` - if this metadata is unmodifiable.
    Since:
      1.0
    See Also:
      - `state()`

  - ### writeList

    protected final <E> List<E> writeList(Collection<? extends E> source, List<E> target, Class<E> elementType) throws UnmodifiableMetadataException
    Writes the content of the `source` collection into the `target` list, creating it if needed. This method performs the following steps:
    - Invokes `checkWritePermission(Object)` in order to ensure that this metadata is modifiable.
    - If `source` is null or empty, returns `null` (meaning that the metadata property is not provided).
    - If `target` is null, creates a new `List`.
    - Copies the content of the given `source` into the target.
    Type Parameters:
      `E` - the type represented by the `Class` argument.
    Parameters:
      `source` - the source list, or `null`.
      `target` - the target list, or `null` if not yet created.
      `elementType` - the base type of elements to put in the list.
    Returns:
      a list (possibly the `target` instance) containing the `source` elements, or `null` if the source was null.
    Throws:
      `UnmodifiableMetadataException` - if this metadata is unmodifiable.
    See Also:
      - `nonNullList(List, Class)`

  - ### writeSet

    protected final <E> Set<E> writeSet(Collection<? extends E> source, Set<E> target, Class<E> elementType) throws UnmodifiableMetadataException
    Writes the content of the `source` collection into the `target` set, creating it if needed. This method performs the following steps:
    - Invokes `checkWritePermission(Object)` in order to ensure that this metadata is modifiable.
    - If `source` is null or empty, returns `null` (meaning that the metadata property is not provided).
    - If `target` is null, creates a new `Set`.
    - Copies the content of the given `source` into the target.
    Type Parameters:
      `E` - the type represented by the `Class` argument.
    Parameters:
      `source` - the source set, or `null`.
      `target` - the target set, or `null` if not yet created.
      `elementType` - the base type of elements to put in the set.
    Returns:
      a set (possibly the `target` instance) containing the `source` elements, or `null` if the source was null.
    Throws:
      `UnmodifiableMetadataException` - if this metadata is unmodifiable.
    See Also:
      - `nonNullSet(Set, Class)`

  - ### writeCollection

    protected final <E> Collection<E> writeCollection(Collection<? extends E> source, Collection<E> target, Class<E> elementType) throws UnmodifiableMetadataException
    Writes the content of the `source` collection into the `target` list or set, creating it if needed. This method performs the following steps:
    - Invokes `checkWritePermission(Object)` in order to ensure that this metadata is modifiable.
    - If `source` is null or empty, returns `null` (meaning that the metadata property is not provided).
    - If `target` is null, creates a new `Set` or a new `List` depending on the value returned by `collectionType(Class)`.
    - Copies the content of the given `source` into the target.

    #### Choosing a collection type

    Implementations shall invoke `writeList` or `writeSet` methods instead of this method when the collection type is enforced by ISO specification. When the type is not enforced by the specification, some freedom are allowed at implementer choice. The default implementation invokes `collectionType(Class)` in order to get a hint about whether a `List` or a `Set` should be used.
    Type Parameters:
      `E` - the type represented by the `Class` argument.
    Parameters:
      `source` - the source collection, or `null`.
      `target` - the target collection, or `null` if not yet created.
      `elementType` - the base type of elements to put in the collection.
    Returns:
      a collection (possibly the `target` instance) containing the `source` elements, or `null` if the source was null.
    Throws:
      `UnmodifiableMetadataException` - if this metadata is unmodifiable.

  - ### writeMap

    protected final <K, V> Map<K,V> writeMap(Map<? extends K,? extends V> source, Map<K,V> target, Class<K> keyType) throws UnmodifiableMetadataException
    Writes the content of the `source` map into the `target` map, creating it if needed. This method performs the following steps:
    - Invokes `checkWritePermission(Object)` in order to ensure that this metadata is modifiable.
    - If `source` is null or empty, returns `null` (meaning that the metadata property is not provided).
    - If `target` is null, creates a new `Map`.
    - Copies the content of the given `source` into the target.
    Type Parameters:
      `K` - the type of keys represented by the `Class` argument.
      `V` - the type of values in the map.
    Parameters:
      `source` - the source map, or `null`.
      `target` - the target map, or `null` if not yet created.
      `keyType` - the base type of keys to put in the map.
    Returns:
      a map (possibly the `target` instance) containing the `source` entries, or `null` if the source was null.
    Throws:
      `UnmodifiableMetadataException` - if this metadata is unmodifiable.
    Since:
      1.0
    See Also:
      - `nonNullMap(Map, Class)`

  - ### copyList

    protected final <E> List<E> copyList(Collection<? extends E> source, Class<E> elementType)
    Creates a list with the content of the `source` collection, or returns `null` if the source is `null` or empty. This is a convenience method for copying fields in subclass copy constructors.
    Type Parameters:
      `E` - the type represented by the `Class` argument.
    Parameters:
      `source` - the source collection, or `null`.
      `elementType` - the base type of elements to put in the list.
    Returns:
      a list containing the `source` elements, or `null` if the source was null or empty.

  - ### copySet

    protected final <E> Set<E> copySet(Collection<? extends E> source, Class<E> elementType)
    Creates a set with the content of the `source` collection, or returns `null` if the source is `null` or empty. This is a convenience method for copying fields in subclass copy constructors.
    Type Parameters:
      `E` - the type represented by the `Class` argument.
    Parameters:
      `source` - the source collection, or `null`.
      `elementType` - the base type of elements to put in the set.
    Returns:
      a set containing the `source` elements, or `null` if the source was null or empty.

  - ### copyCollection

    protected final <E> Collection<E> copyCollection(Collection<? extends E> source, Class<E> elementType)
    Creates a list or set with the content of the `source` collection, or returns `null` if the source is `null` or empty. This is a convenience method for copying fields in subclass copy constructors.

    The collection type is selected as described in the `nonNullCollection(Collection, Class)`.

    Type Parameters:
      `E` - the type represented by the `Class` argument.
    Parameters:
      `source` - the source collection, or `null`.
      `elementType` - the base type of elements to put in the collection.
    Returns:
      a collection containing the `source` elements, or `null` if the source was null or empty.

  - ### copyMap

    protected final <K, V> Map<K,V> copyMap(Map<? extends K,? extends V> source, Class<K> keyType)
    Creates a map with the content of the `source` map, or returns `null` if the source is `null` or empty. This is a convenience method for copying fields in subclass copy constructors.
    Type Parameters:
      `K` - the type of keys represented by the `Class` argument.
      `V` - the type of values in the map.
    Parameters:
      `source` - the source map, or `null`.
      `keyType` - the base type of keys to put in the map.
    Returns:
      a map containing the `source` entries, or `null` if the source was null or empty.
    Since:
      1.0

  - ### singleton

    protected final <E> Collection<E> singleton(E value, Class<E> elementType)
    Creates a singleton list or set containing only the given value, if non-null. This is a convenience method for initializing fields in subclass constructors.

    The collection type is selected as described in the `nonNullCollection(Collection, Class)`.

    Type Parameters:
      `E` - the type represented by the `Class` argument.
    Parameters:
      `value` - the singleton value to put in the returned collection, or `null`.
      `elementType` - the element type (used only if `value` is non-null).
    Returns:
      a new modifiable collection containing the given value, or `null` if the given value was null.

  - ### nonNullList

    protected final <E> List<E> nonNullList(List<E> current, Class<E> elementType)
    Returns the specified list, or a new one if `current` is null. This is a convenience method for implementation of `getFoo()` methods.
    Type Parameters:
      `E` - the type represented by the `Class` argument.
    Parameters:
      `current` - the existing list, or `null` if the list has not yet been created.
      `elementType` - the element type (used only if `current` is null).
    Returns:
      `current`, or a new list if `current` is null.

  - ### nonNullSet

    protected final <E> Set<E> nonNullSet(Set<E> current, Class<E> elementType)
    Returns the specified set, or a new one if `current` is null. This is a convenience method for implementation of `getFoo()` methods.
    Type Parameters:
      `E` - the type represented by the `Class` argument.
    Parameters:
      `current` - the existing set, or `null` if the set has not yet been created.
      `elementType` - the element type (used only if `current` is null).
    Returns:
      `current`, or a new set if `current` is null.

  - ### nonNullCollection

    protected final <E> Collection<E> nonNullCollection(Collection<E> current, Class<E> elementType)
    Returns the specified collection, or a new one if `current` is null. This is a convenience method for implementation of `getFoo()` methods.

    #### Choosing a collection type

    Implementations shall invoke `nonNullList(…)` or `nonNullSet(…)` instead of this method when the collection type is enforced by ISO specification. When the type is not enforced by the specification, some freedom are allowed at implementer choice. The default implementation invokes `collectionType(Class)` in order to get a hint about whether a `List` or a `Set` should be used.
    Type Parameters:
      `E` - the type represented by the `Class` argument.
    Parameters:
      `current` - the existing collection, or `null` if the collection has not yet been created.
      `elementType` - the element type (used only if `current` is null).
    Returns:
      `current`, or a new collection if `current` is null.

  - ### nonNullMap

    protected final <K, V> Map<K,V> nonNullMap(Map<K,V> current, Class<K> keyType)
    Returns the specified map, or a new one if `current` is null. This is a convenience method for implementation of `getFoo()` methods.
    Type Parameters:
      `K` - the type of keys represented by the `Class` argument.
      `V` - the type of values in the map.
    Parameters:
      `current` - the existing map, or `null` if the map has not yet been created.
      `keyType` - the key type (used only if `current` is null).
    Returns:
      `current`, or a new map if `current` is null.
    Since:
      1.0

  - ### collectionType

    protected <E> Class<? extends Collection<E>> collectionType(Class<E> elementType)
    Returns the type of collection to use for the given type. The current implementation can return only two values: `Set.class` if the property should not accept duplicated values, or `List.class` otherwise. Future SIS versions may accept other types.

    The default implementation returns `Set.class` if the element type is assignable to `CodeList`, `Enum`, `String`, `Charset`, `Locale` or `Currency`, and `List.class` otherwise. Subclasses can override this method for choosing different kind of collections. Note however that `Set` should be used only with immutable element types, for hash code stability.

    Type Parameters:
      `E` - the type of elements in the collection to be created.
    Parameters:
      `elementType` - the type of elements in the collection to be created.
    Returns:
      `List​.class` or `Set​.class` depending on whether the property shall accept duplicated values or not.