Module org.apache.sis.util
Package org.apache.sis.util.collection

# Interface TreeTable.Node

All Known Implementing Classes:
  `DefaultTreeTable​.Node`

Enclosing interface:
  `TreeTable`

public static interface TreeTable.Node
A node in a tree combined with a row in a table. A `TreeTable​.Node` can be seen as a tree node associated to a single user object (like ordinary trees), augmented with the capability to describe some aspects of the user object in predefined columns. The list of allowed columns is given by the `TreeTable​.getColumns()` method.

The following table summarizes the tree-related and table-related methods:

Tree-table methods
Tree-related methods | Table-related methods
--- | ---
`getParent()` `getChildren()` `newChild()` | `getValue(TableColumn)` `setValue(TableColumn, Object)` `isEditable(TableColumn)`

In addition, each `Node` can be associated to an arbitrary object by the `getUserObject()` method. This object is not used directly by the tree tables.

Since:
  0.3

- ## Method Summary

  Modifier and Type
  Method
  Description
  `boolean`
  `equals(Object other)`
  Returns `true` if the given object is a node with the same content than this node.
  `Collection<TreeTable​.Node>`
  `getChildren()`
  Returns the children of this node.
  `TreeTable​.Node`
  `getParent()`
  Returns the parent node, or `null` if this node is the root of the tree.
  `Object`
  `getUserObject()`
  Returns the user object associated to this node.
  `<V> V`
  `getValue(TableColumn<V> column)`
  Returns the value in the given column, or `null` if none.
  `int`
  `hashCode()`
  Returns a hash code value consistent with the `equals(Object)` implementation for this node.
  `boolean`
  `isEditable(TableColumn<?> column)`
  Determines whether the value in the specified column is editable.
  `boolean`
  `isLeaf()`
  Returns `true` if this node cannot have any children.
  `TreeTable​.Node`
  `newChild()`
  Creates a new child with the same columns than the other children, and adds it to the children collection.
  `<V> void`
  `setValue(TableColumn<V> column, V value)`
  Sets the value for the given column (optional operation).

- ## Method Details

  - ### getParent

    TreeTable.Node getParent()
    Returns the parent node, or `null` if this node is the root of the tree.

    There is intentionally no `setParent(Node)` method, as children and parent managements are highly implementation-dependant. If the children collection is modifiable, then implementations are encouraged to update automatically the parent when a child is added to or removed from that collection.

    Returns:
      the parent, or `null` if none.

  - ### isLeaf

    boolean isLeaf()
    Returns `true` if this node cannot have any children. The children collection of a leaf node can only be empty, and adding new child is an unsupported operation.

    This value is provided as a tip for graphical user interfaces, in order to determine if a node is expandable (even if empty). `TreeTableFormat` does not use this value.

    Returns:
      `true` if this node cannot have any children.

  - ### getChildren

    Collection<TreeTable.Node> getChildren()
    Returns the children of this node. The returned collection may or may not be modifiable, at implementation choice. If the collection is modifiable, then it shall be live, i.e. any modification to the returned collection are reflected immediately in the tree. This allows addition or removal of child nodes as below:

        TreeTable.Node newNode = new ...; // Create a new node here.
        parent.getChildren().add(newNode);

    The collection is often a `List`, but not necessarily. For some implementations like the metadata tree table view, compliance to the `List` contract is impractical or inefficient.
    Returns:
      the children, or an empty collection if none.

  - ### newChild

    TreeTable.Node newChild() throws UnsupportedOperationException
    Creates a new child with the same columns than the other children, and adds it to the children collection. The new child is typically added at the end of the collection, but this is not mandatory: implementations can add the child at whatever position they see fit.
    Returns:
      the new child.
    Throws:
      `UnsupportedOperationException` - if this node cannot add new children.

  - ### getValue

    <V> V getValue(TableColumn<V> column)
    Returns the value in the given column, or `null` if none.
    Type Parameters:
      `V` - the base type of values in the given column.
    Parameters:
      `column` - identifier of the column from which to get the value.
    Returns:
      the value in the given column, or `null` if none.
    See Also:
      - `TreeTable​.getColumns()`

  - ### setValue

    <V> void setValue(TableColumn<V> column, V value) throws IllegalArgumentException, UnsupportedOperationException
    Sets the value for the given column (optional operation). The `isEditable(TableColumn)` method can be invoked before this setter method for determining if the given column is modifiable.
    Type Parameters:
      `V` - the base type of values in the given column.
    Parameters:
      `column` - identifier of the column into which to set the value.
      `value` - the value to set.
    Throws:
      `IllegalArgumentException` - if the given column is not a legal column for this node.
      `UnsupportedOperationException` - if values in the given column cannot be modified.
    See Also:
      - `TreeTable​.getColumns()`
      - `isEditable(TableColumn)`

  - ### isEditable

    boolean isEditable(TableColumn<?> column)
    Determines whether the value in the specified column is editable. If the given column is not a legal column for this `Node` instance, then this method returns `false`.
    Parameters:
      `column` - the column to query.
    Returns:
      `true` if the given column is a legal column for this `Node` implementation and the corresponding value is editable, or `false` otherwise.

  - ### getUserObject

    Object getUserObject()
    Returns the user object associated to this node. The user object is for information purpose only and does not appear in the rendered tree. It is typically a Java object whose content is splitted into the various table columns.

    #### Example

    If a `CityLocation` class is defined as a (city name, latitude, longitude) tuple, then a `TreeTable​.Node` could be defined to have 3 columns for the above 3 tuple components, and the user object could be the original `CityLocation` instance.
    Returns:
      any object stored at this node by the user, or `null` if none.

  - ### equals

    boolean equals(Object other)
    Returns `true` if the given object is a node with the same content than this node. For this method, the meaning of same content is defined as below:
    - The given object is also a `Node`.
    - The list returned by `TreeTable​.getColumns()` is equal for both nodes.
    - The objects returned by `getValue(TableColumn)` are equal for each column.
    - The list returned by children is equal for both node.
    The node returned by `getParent()` shall not be taken in account. It is necessary to ignore the parent for consistency with clone and for avoiding infinite recursivity when comparing the children. A third reason is given in the purpose example below.

    #### Purpose of this method: example with ISO metadata

    Consider the following tree made of ISO 19115 metadata objects: a platform containing a list of instruments, and an instrument containing a reference to the platform on which the instrument is installed. In this example, nodes 2 and 4 contain a reference to the same `Platform` instance, so we have a cyclic graph:

    Metadata tree example
    Node 1: | `` Acquisition information
    Node 2: | `└─`Platform
    Node 3: | `└─`Instrument
    Node 4: | `└─`Platform (same instance than above)
    Node 5: | `└─`etc…

    The `AbstractMetadata​.asTreeTable()` method gives a view in which each node has its content fully generated from wrapped metadata object. Consequently, a naive walk over the above tree causes an infinite loop with `TreeTable` generating nodes with identical content as we bounce between `Platform` and `Instrument` metadata objects. To break this loop, we need to know when the content of a node (in this example, the wrapped metadata object) has already been visited. The parent shall not be taken in account since node 2 and 4 have different parents despite having the same `Platform` content.

    In this use case, the `Node​.equals(Object)` implementation needs only to compare the wrapped metadata (usually given by the user object) since the node content, including the list of children, is fully determined by those metadata. An identity comparison (with `==`) is sufficient for the purpose of avoiding infinite recursivity.

    #### Flexibility in implementations

    The above list specifies minimal conditions that must be true when two nodes are considered equal. Implementations should not relax those conditions, but are free to make them more restrictive. In particular, many implementations will require that the two nodes are instances of the same class. Some implementations may also perform identity comparisons (with the `==` operator) between values instead of using `Object​.equals(Object)`. This flexibility means that even if all above conditions are true, this is not a guarantee that this method will return `true`.

    It is okay to not override this method at all since the identity comparison inherited from `Object​.equals(Object)` is consistent with this method contract. Alternatively, `Node` implementations having a content fully determined by the wrapped user object need only the following implementation:

            @Override
            public boolean equals(Object obj) {
                return (obj instanceof MyNode) && ((MyNode) obj).getUserObject() == getUserObject();
            }

    Implementation details may vary, for example in the way to compare `null` user objects or by invoking `Object​.equals(Object)` instead of performing identity comparisons. Note however that since this method purpose is to detect cyclic graphs (see above example), user objects should be compared with `equals(Object)` only if their implementations are known to be safe against infinite recursivity.
    Overrides:
      `equals` in class `Object`
    Parameters:
      `other` - the other object to compare with this node.
    Returns:
      whether the two objects are nodes with equal values and equal children, ignoring parents.
    Since:
      0.8

  - ### hashCode

    int hashCode()
    Returns a hash code value consistent with the `equals(Object)` implementation for this node. If the `equals(Object)` method has not been overridden, then this `hashCode()` method should not be overridden neither. Otherwise if this node content (values and children) is fully generated from the user object, then the `equals(…)` and `hashCode()` methods may be implemented like below:

            @Override
            public boolean equals(Object obj) {
                return (obj instanceof MyNode) && ((MyNode) obj).getUserObject() == getUserObject();
            }

            @Override
            public int hashCode() {
                return System.identityHashCode(getUserObject());
            }

    Otherwise this method should compute a hash code based on values and children of this node, ignoring parent.
    Overrides:
      `hashCode` in class `Object`
    Returns:
      a hash code for this node, potentially based on values and children but ignoring parent.
    Since:
      0.8