# Class AbstractFeature

Object
AbstractFeature

All Implemented Interfaces:
  `Serializable`

public abstract class AbstractFeature extends Object implements Serializable
An instance of a feature type containing values for a real-world phenomena. Each feature instance can provide values for the following properties:

- Attributes
- Associations to other features
- Operations

`AbstractFeature` can be instantiated by calls to `DefaultFeatureType​.newInstance()`.

## Simple features

A feature is said “simple” if it complies to the following conditions:

- the feature allows only attributes and operations (no associations),
- the cardinality of all attributes is constrained to [1 … 1].

## Operations

Properties that are instances of `Operation` are usually not stored in `Feature` instances. Instead, the `Operation​.apply(…)` method is invoked every times that the property value is requested. `AbstractFeature` does not cache operation results. Those results are usually read-only, but may be writable under the conditions documented in `setOperationValue(String, Object)`.

## Limitations

- Multi-threading: `AbstractFeature` instances are not thread-safe. Synchronization, if needed, shall be done externally by the caller.
- Serialization: serialized objects of this class are not guaranteed to be compatible with future versions. Serialization should be used only for short term storage or RMI between applications running the same SIS version.

Since:
  0.5
See Also:
  - `DefaultFeatureType​.newInstance()`
  - Serialized Form

- ## Constructor Summary

  Constructors
  Modifier
  Constructor
  Description
  `protected`
  `AbstractFeature(DefaultFeatureType type)`
  Creates a new feature of the given type.

- ## Method Summary

  Modifier and Type
  Method
  Description
  `boolean`
  `equals(Object obj)`
  Compares this feature with the given object for equality.
  `Optional<?>`
  `getCharacteristicValue(String property, String characteristic)`
  Returns the explicit or default value of a characteristic of a property.
  `protected Object`
  `getOperationValue(String name)`
  Executes the parameterless operation of the given name and returns the value of its result.
  `Object`
  `getProperty(String name)`
  Returns the property (attribute, feature association or operation result) of the given name.
  `Object`
  `getPropertyValue(String name)`
  Returns the value for the property of the given name.
  `DefaultFeatureType`
  `getType()`
  Returns information about the feature (name, characteristics, etc.).
  `Object`
  `getValueOrFallback(String name, Object missingPropertyFallback)`
  Deprecated, for removal: This API element is subject to removal in a future version.
  Experience suggests that this method encourage bugs in user's code that stay unnoticed.
  `int`
  `hashCode()`
  Returns a hash code value for this feature.
  `DataQuality`
  `quality()`
  Evaluates the quality of this feature at this method invocation time.
  `protected void`
  `setOperationValue(String name, Object value)`
  Executes the parameterless operation of the given name and sets the value of its result.
  `void`
  `setProperty(Object property)`
  Sets the property (attribute or feature association).
  `abstract void`
  `setPropertyValue(String name, Object value)`
  Sets the value for the property of the given name.
  `String`
  `toString()`
  Formats this feature in a tabular format.

  ### Methods inherited from class Object

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

- ## Constructor Details

  - ### AbstractFeature

    protected AbstractFeature(DefaultFeatureType type)
    Creates a new feature of the given type.
    Parameters:
      `type` - information about the feature (name, characteristics, etc.).
    See Also:
      - `DefaultFeatureType​.newInstance()`

- ## Method Details

  - ### getType

    public DefaultFeatureType getType()
    Returns information about the feature (name, characteristics, etc.).
    Warning: In a future SIS version, the return type may be changed to `org​.opengis​.feature​.FeatureType`. This change is pending GeoAPI revision.
    Returns:
      information about the feature.

  - ### getProperty

    public Object getProperty(String name) throws IllegalArgumentException
    Returns the property (attribute, feature association or operation result) of the given name. If the property type is a parameterless operation, then this method may return the result of executing the operation on this feature, at implementation choice.

    This method returns the property instance. If only the property value is desired, then `getPropertyValue(String)` is preferred since it gives to SIS a chance to avoid the creation of `AbstractAttribute` or `AbstractAssociation` instances.

    #### Note for subclass implementers

    The default implementation returns an instance that redirect all read and write operations to `getPropertyValue(String)` and `setPropertyValue(String, Object)` respectively. That default implementation is intended to make easier for developers to create their own customized `AbstractFacture` implementations, but has drawbacks: a new `Property` instance is created every time that this `getProperty(String)` method is invoked, and the returned `Property` implementation is not very efficient since it has to perform multiple lookups and type checks. Implementers are encouraged to override this method if they can provide a more efficient implementation. Note that this is already the case when using implementations created by `DefaultFeatureType​.newInstance()`.
    Warning: In a future SIS version, the return type may be changed to `org​.opengis​.feature​.Property`. This change is pending GeoAPI revision.
    Parameters:
      `name` - the property name.
    Returns:
      the property of the given name (never `null`).
    Throws:
      `IllegalArgumentException` - if the given argument is not a property name of this feature.
    See Also:
      - `getPropertyValue(String)`
      - `DefaultFeatureType​.getProperty(String)`

  - ### setProperty

    public void setProperty(Object property) throws IllegalArgumentException
    Sets the property (attribute or feature association). The given property shall comply to the following conditions:
    - It must be non-null.
    - Its name shall be the name of the property to set in this feature.
    - Its type shall be the same instance as the property type defined by the feature type for the above name. In other words, the following condition shall hold:

        assert property.getType() == getType().getProperty(property.getName());

    This method is useful for storing non-default `Attribute` or `FeatureAssociation` implementations in this feature. When default implementations are sufficient, the `setPropertyValue(String, Object)` method is preferred.

    #### Note for subclass implementers

    The default implementation verifies that the given property has the expected type and a null or empty map of characteristics, then delegates to `setPropertyValue(String, Object)`. That default implementation is intended to make easier for developers to create their own customized `AbstractFacture` implementations, but has drawbacks: the given `Property` instance is not stored (only its value is stored), and it cannot have custom characteristics. Implementers are encouraged to override this method if they can provide a better implementation. Note that this is already the case when using implementations created by `DefaultFeatureType​.newInstance()`.
    Warning: In a future SIS version, the argument may be changed to `org​.opengis​.feature​.Property`. This change is pending GeoAPI revision.
    Parameters:
      `property` - the property to set.
    Throws:
      `IllegalArgumentException` - if the name of the given property is not a property name of this feature.
      `IllegalArgumentException` - if the value of the given property is not valid.
      `IllegalArgumentException` - if the property cannot be set for another reason.
    See Also:
      - `setPropertyValue(String, Object)`

  - ### getPropertyValue

    public Object getPropertyValue(String name) throws IllegalArgumentException
    Returns the value for the property of the given name. This convenience method is equivalent to invoking `getProperty(String)` for the given name, then to perform one of the following actions depending on the property type and the multiplicity:

    Class of returned value
    Property type | max. occurs | Method invoked | Return type
    --- | --- | --- | ---
    `AttributeType` | 0 or 1 | `Attribute.getValue()` | `Object`
    `AttributeType` | 2 or more | `Attribute.getValues()` | `Collection<?>`
    `FeatureAssociationRole` | 0 or 1 | `FeatureAssociation.getValue()` | `Feature`
    `FeatureAssociationRole` | 2 or more | `FeatureAssociation.getValues()` | `Collection<Feature>`

    Note: “max. occurs” is the maximum number of occurrences and does not depend on the actual number of values. If an attribute allows more than one value, then this method will always return a collection for that attribute even if the collection is empty.

    #### Multi-valued properties and collections

    In the case of multi-valued properties (“max. occurs” > 1), the collection returned by this method may or may not be modifiable, at implementation choice. Generally the caller cannot add new elements into the returned collection anyway since `Collection<?>` does not allow such operations, and more specific casts (e.g. `Collection<String>` cannot be checked at runtime. If a type-safe modifiable collection is desired, the following approach can be used instead:

        Attribute<String> attribute = Features.cast((Attribute<?>) feature.getProperty(name), String.class);
        Collection<String> values = attribute.getValues();    // This collection is guaranteed to be "live".

    Parameters:
      `name` - the property name.
    Returns:
      value of the specified property, or the default value (which may be `null`} if none.
    Throws:
      `IllegalArgumentException` - if the given argument is not an attribute or association name of this feature.
    See Also:
      - `AbstractAttribute​.getValue()`

  - ### setPropertyValue

    public abstract void setPropertyValue(String name, Object value) throws IllegalArgumentException
    Sets the value for the property of the given name.

    #### Validation

    The number of validations performed by this method is implementation dependent. Usually, only the most basic constraints are verified. This is so for performance reasons and also because some rules may be temporarily broken while constructing a feature. A more exhaustive verification can be performed by invoking the `quality()` method.
    Parameters:
      `name` - the attribute name.
      `value` - the new value for the given attribute (may be `null`).
    Throws:
      `IllegalArgumentException` - if the given name is not an attribute or association name of this feature.
      `ClassCastException` - if the value is not assignable to the expected value class.
      `IllegalArgumentException` - if the given value is not valid for a reason other than its type.
    See Also:
      - `AbstractAttribute​.setValue(Object)`

  - ### getValueOrFallback

    @Deprecated(since="1.5", forRemoval=true) public Object getValueOrFallback(String name, Object missingPropertyFallback)
    Deprecated, for removal: This API element is subject to removal in a future version.
    Experience suggests that this method encourage bugs in user's code that stay unnoticed.
    Returns the value for the property of the given name if that property exists, or a fallback value otherwise. This method is equivalent to the following code, but potentially more efficient:

        return type.hasProperty(name) ? getPropertyValue(name) : missingPropertyFallback;

    Note that if a property of the given name exists but has no value, then this method returns the default value (which may be `null`). Property without value is not equivalent to non-existent property.
    Parameters:
      `name` - the property name.
      `missingPropertyFallback` - the (potentially `null`) value to return if no attribute or association of the given name exists.
    Returns:
      value or default value of the specified property, or `missingPropertyFallback` if no attribute or association of that name exists. This value may be `null`.
    Since:
      1.1

  - ### getOperationValue

    protected Object getOperationValue(String name)
    Executes the parameterless operation of the given name and returns the value of its result. This is a convenience method for sub-classes where some properties may be operations that depend on other properties of this `Feature` instance (for example a link to another property value). Invoking this method is equivalent to performing the following steps:

            public Object getPropertyValue(String name) {
                Operation operation = (Operation) type.getProperty(name);
                Property result = operation.apply(this, null);
                if (result instanceof Attribute<?>) {
                    return ...;                                      // the attribute value.
                } else if (result instanceof FeatureAssociation) {
                    return ...;                                      // the associated feature.
                } else {
                    return null;
                }
            }

    Parameters:
      `name` - the name of the operation to execute. The caller is responsible to ensure that the property type for that name is an instance of `AbstractOperation`.
    Returns:
      the result value of the given operation, or `null` if none.
    Since:
      0.8

  - ### setOperationValue

    protected void setOperationValue(String name, Object value)
    Executes the parameterless operation of the given name and sets the value of its result. This method is the complement of `getOperationValue(String)` for subclasses where some properties may be operations. Not all operations accept assignments, but the link and compound operations (for instances) do. Whether an operation is writable or not depends on whether the computed `Property` supports `AbstractAttribute​.setValue(Object)` or `AbstractAssociation​.setValue(Feature)`.
    Parameters:
      `name` - the name of the operation to execute. The caller is responsible to ensure that the property type for that name is an instance of `AbstractOperation`.
      `value` - the value to assign to the result of the named operation.
    Throws:
      `IllegalStateException` - if the operation of the given name does not accept assignment.
    Since:
      0.8

  - ### getCharacteristicValue

    public Optional<?> getCharacteristicValue(String property, String characteristic) throws IllegalArgumentException
    Returns the explicit or default value of a characteristic of a property. This is a shortcut for the following chain of method invocations (cast and null checks omitted for brevity), except that the actual implementation is potentially more efficient:

        return Optional.ofNullable(
                ((Attribute<?>) getProperty(property))
                .characteristics()
                .get(characteristic)
                .getValue());

    If the attribute has no characteristic of the given name, then this method fallbacks on the default value of the characteristics of the attribute type.
    Parameters:
      `property` - name of the property for which to get a characteristic.
      `characteristic` - name of the characteristic of the property of the given name.
    Returns:
      value of the specified characteristic on the specified property, or an empty value if the property is not an attribute or the attribute has no such characteristic.
    Throws:
      `IllegalArgumentException` - if the `property` argument is not the name of a property of this feature.
    Since:
      1.5

  - ### quality

    public DataQuality quality()
    Evaluates the quality of this feature at this method invocation time. The data quality reports may include information about whether the property values met the constraints defined by the property types, or any other criterion at implementation choice.

    The default implementation reports data quality with at least the following information:

    - The scope level is set to `ScopeCode​.FEATURE`.
    - The reports list contains at most one domain consistency element per property. Implementations are free to omit element for properties having nothing to report.
    - Each report may have one or more conformance result, as documented on `AbstractAttribute​.quality()` javadoc.
    This feature is valid if this method does not report any conformance result having a pass value of `false`.

    #### Example

    Given a feature with an attribute named “population”. If this attribute is mandatory ([1 … 1] multiplicity) but no value has been assigned to it, then this `quality()` method will return the following data quality report:

          Data quality
            ├─Scope
            │   └─Level………………………………………………… Feature
            └─Report
                ├─Measure identification
                │   └─Code………………………………………… population
                ├─Evaluation method type…… Direct internal
                └─Result
                    ├─Explanation……………………… Missing value for the “population” property.
                    └─Pass………………………………………… false

    Returns:
      reports on all constraint violations found.
    See Also:
      - `AbstractAttribute​.quality()`
      - `AbstractAssociation​.quality()`

  - ### toString

    public String toString()
    Formats this feature in a tabular format.
    Overrides:
      `toString` in class `Object`
    Returns:
      a string representation of this feature in a tabular format.
    See Also:
      - `FeatureFormat`

  - ### hashCode

    public int hashCode()
    Returns a hash code value for this feature. The default implementation performs the following algorithm:
    - Iterate over all properties returned by `type​.getProperty(true)` – thus including properties inherited from parent types (if any):
      - For each property type, get the value with `getPropertyValue(String)`.
      - Compute the hash code from the property name and value, ignoring the properties having a null value.
    Subclasses should override this method with a more efficient algorithm for their internal structure. There is no need to reproduce the same hash code value than the one computed by this default method.
    Overrides:
      `hashCode` in class `Object`
    Returns:
      a hash code value.
    Since:
      0.8

  - ### equals

    public boolean equals(Object obj)
    Compares this feature with the given object for equality. The default implementation performs the following algorithm:
    - Verify that both objects are non-null and of the same class.
    - Iterate over all properties returned by `type​.getProperty(true)` – thus including properties inherited from parent types (if any):
      - For each property type, get the value from both `FeatureType` by a call to `getPropertyValue(String)`.
      - Verify that the two values are either both null, or equal in the sense of `Object​.equals(Object)`.
    Subclasses should override this method with a more efficient algorithm for their internal structure.
    Overrides:
      `equals` in class `Object`
    Returns:
      `true` if both objects are equal.
    Since:
      0.8