blob: 06fde023e4e69bad960ab531b38eb6acdd8f7e91 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.sis.util.collection;
import java.util.Collection;
import java.util.List;
/**
* Defines the structure (list of columns) of a table and provides the root of the tree
* containing the data. {@code TreeTable} can be seen as a table in which the first
* column contains a tree. Every row in this table is a {@link Node} instance, and each
* node can have an arbitrary number of {@linkplain Node#getChildren() children} nodes.
*
* <p>Below is an example of what a two-columns {@code TreeTable} instance may look like
* when {@linkplain TreeTableFormat formatted as a text}:</p>
*
* {@preformat text
* Citation
*   ├─Title…………………………………………………………… Open Geospatial Consortium
*   ├─Presentation Forms………………………… document digital
*   ├─Cited Responsible Parties
*   │   ├─Organisation Name………………… Open Geospatial Consortium
*   │   ├─Role…………………………………………………… resource provider
*   │   └─Contact Info
*   │       └─Online Resource
*   │           ├─Linkage……………………… http://www.opengeospatial.org/
*   │           └─Function…………………… information
*   └─Identifiers
*       └─Code…………………………………………………… OGC
* }
*
* <p>In many cases, the columns are known in advance as hard-coded static constants.
* Those column constants are typically documented close to the class producing the
* {@code TreeTable} instance. Using directly those static constants provides type
* safety, as in the following example:</p>
*
* {@preformat java
* TreeTable table = ...; // Put here a TreeTable instance.
* TreeTable.Node node = table.getRoot();
* CharSequence name = node.getValue(TableColumn.NAME);
* Class<?> type = node.getValue(TableColumn.TYPE);
* }
*
* In the above example, the type of value returned by the {@link Node#getValue(TableColumn)}
* method is determined by the column constant. However this approach is possible only when
* the table structure is known in advance. If a method needs to work with arbitrary tables,
* then that method can get the list of columns by a call to {@link #getColumns()}. However
* this column list does not provide the above type-safety.
*
* @author Martin Desruisseaux (Geomatys)
* @version 0.3
* @since 0.3
* @module
*/
public interface TreeTable {
/**
* Returns the table columns, in the order they shall be rendered by default.
* This method returns the union of all table columns in every nodes of this
* tree. However any {@link Node} instance can return {@code null} for a
* particular column if the node doesn't have that column.
*
* @return the union of all table columns in every tree node.
*
* @see Node#getValue(TableColumn)
* @see Node#setValue(TableColumn, Object)
*/
List<TableColumn<?>> getColumns();
/**
* Returns the root node of the tree.
*
* @return the root node of the tree.
*/
Node getRoot();
/**
* A node in a tree combined with a row in a table. A {@code TreeTable.Node} can be seen as a
* tree node associated to a single {@linkplain #getUserObject() user object} (like ordinary trees),
* augmented with the capability to describe some aspects of the user object in pre-defined columns.
* The list of allowed columns is given by the {@link TreeTable#getColumns()} method.
*
* <p>The following table summarizes the tree-related and table-related methods:</p>
*
* <table class="sis">
* <caption>Tree-table methods</caption>
* <tr>
* <th>Tree-related methods</th>
* <th>Table-related methods</th>
* </tr>
* <tr><td><ul>
* <li>{@link #getParent()}</li>
* <li>{@link #getChildren()}</li>
* <li>{@link #newChild()}</li>
* </ul></td>
* <td><ul>
* <li>{@link #getValue(TableColumn)}</li>
* <li>{@link #setValue(TableColumn, Object)}</li>
* <li>{@link #isEditable(TableColumn)}</li>
* </ul></td></tr>
* </table>
*
* In addition, each {@code Node} can be associated to an arbitrary object by the
* {@link #getUserObject()} method. This object is not used directly by the tree tables.
*
* @author Martin Desruisseaux (IRD, Geomatys)
* @version 0.8
* @since 0.3
* @module
*/
public interface Node {
/**
* Returns the parent node, or {@code null} if this node is the root of the tree.
*
* <p>There is intentionally no {@code setParent(Node)} method, as children and parent managements
* are highly implementation-dependant. If the {@linkplain #getChildren() children collection} is
* modifiable, then implementations are encouraged to update automatically the parent when a child
* is <em>added to</em> or <em>removed from</em> that collection.</p>
*
* @return the parent, or {@code null} if none.
* @category tree
*/
Node getParent();
/**
* Returns {@code true} if this node can not have any children. The {@linkplain #getChildren() children
* collection} of a leaf node can only be empty, and adding {@linkplain #newChild() new child}
* is an unsupported operation.
*
* <p>This value is provided as a tip for graphical user interfaces, in order to determine if
* a node is expandable (even if empty). {@link TreeTableFormat} does not use this value.</p>
*
* @return {@code true} if this node can not have any children.
*/
boolean isLeaf();
/**
* 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 <cite>live</cite>,
* i.e. any modification to the returned collection are reflected immediately in the tree.
* This allows addition or removal of child nodes as below:
*
* {@preformat java
* TreeTable.Node newNode = new ...; // Create a new node here.
* parent.getChildren().add(newNode);
* }
*
* The collection is often a {@link List}, but not necessarily. For some implementations like the
* {@linkplain org.apache.sis.metadata.AbstractMetadata#asTreeTable() metadata tree table view},
* compliance to the {@code List} contract is impractical or inefficient.
*
* @return the children, or an empty collection if none.
* @category tree
*/
Collection<Node> getChildren();
/**
* Creates a new child with the same columns than the other children, and adds it to
* the {@linkplain #getChildren() 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.
*
* @return the new child.
* @throws UnsupportedOperationException if this node can not add new children.
*/
Node newChild() throws UnsupportedOperationException;
/**
* Returns the value in the given column, or {@code null} if none.
*
* @param <V> the base type of values in the given column.
* @param column identifier of the column from which to get the value.
* @return the value in the given column, or {@code null} if none.
*
* @see TreeTable#getColumns()
* @category table
*/
<V> V getValue(TableColumn<V> column);
/**
* Sets the value for the given column (optional operation).
* The {@link #isEditable(TableColumn)} method can be invoked before this setter method
* for determining if the given column is modifiable.
*
* @param <V> the base type of values in the given column.
* @param column identifier of the column into which to set the value.
* @param value the value to set.
* @throws IllegalArgumentException if the given column is not a legal column for this node.
* @throws UnsupportedOperationException if values in the given column can not be modified.
*
* @see TreeTable#getColumns()
* @see #isEditable(TableColumn)
* @category table
*/
<V> void setValue(TableColumn<V> column, V value) throws IllegalArgumentException, UnsupportedOperationException;
/**
* Determines whether the value in the specified column is editable. If the given
* column is not a legal column for this {@code Node} instance, then this method
* returns {@code false}.
*
* @param column the column to query.
* @return {@code true} if the given column is a legal column for this {@code Node}
* implementation and the corresponding value is editable, or {@code false}
* otherwise.
* @category table
*/
boolean isEditable(TableColumn<?> column);
/**
* 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.
*
* <div class="note"><b>Example:</b>
* If a {@code CityLocation} class is defined as a (<var>city name</var>, <var>latitude</var>,
* <var>longitude</var>) tuple, then a {@code TreeTable.Node} could be defined to have 3 columns for the
* above 3 tuple components, and the user object could be the original {@code CityLocation} instance.</div>
*
* @return any object stored at this node by the user, or {@code null} if none.
* @category tree
*/
Object getUserObject();
/**
* Returns {@code true} if the given object is a node with the same content than this node.
* For this method, the meaning of <cite>same content</cite> is defined as below:
*
* <ul>
* <li>The given object is also a {@code Node}.</li>
* <li>The list returned by {@link TreeTable#getColumns()} is equals for both nodes.</li>
* <li>The objects returned by {@link #getValue(TableColumn)} are equal for each column.</li>
* <li>The list returned by {@linkplain #getChildren() children} is equals for both node.</li>
* </ul>
*
* The node returned by {@link #getParent()} shall <strong>not</strong> be taken in account.
* It is necessary to ignore the parent for consistency with {@linkplain DefaultTreeTable#clone() clone}
* and for avoiding infinite recursivity when comparing the children.
* A third reason is given in the <cite>purpose</cite> example below.
*
* <div class="note"><b>Purpose of this method: example with ISO metadata</b><br>
* 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 {@code Platform} instance, so we have a cyclic graph:
*
* <table class="compact">
* <caption>Metadata tree example</caption>
* <tr><th>Node 1:</th><td>{@code  }{@linkplain org.apache.sis.metadata.iso.acquisition.DefaultAcquisitionInformation Acquisition information}</td></tr>
* <tr><th>Node 2:</th><td>{@code   └─}{@linkplain org.apache.sis.metadata.iso.acquisition.DefaultPlatform Platform}</td></tr>
* <tr><th>Node 3:</th><td>{@code      └─}{@linkplain org.apache.sis.metadata.iso.acquisition.DefaultInstrument Instrument}</td></tr>
* <tr><th>Node 4:</th><td>{@code         └─}{@linkplain org.apache.sis.metadata.iso.acquisition.DefaultPlatform Platform} (same instance than above)</td></tr>
* <tr><th>Node 5:</th><td>{@code            └─}<i>etc…</i></td></tr>
* </table>
*
* The {@link org.apache.sis.metadata.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 {@code TreeTable} generating nodes with identical content as we bounce between
* {@code Platform} and {@code Instrument} metadata objects. To break this loop, we need to know when the
* <em>content</em> of a node (in this example, the wrapped metadata object) has already been visited.
* The parent shall <strong>not</strong> be taken in account since node 2 and 4 have different parents
* despite having the same {@code Platform} content.
*
* <p>In this use case, the {@code Node.equals(Object)} implementation needs only to compare the wrapped
* metadata (usually given by the {@linkplain #getUserObject() user object}) since the node content,
* including the list of children, is fully determined by those metadata. An identity comparison
* (with {@code ==}) is sufficient for the purpose of avoiding infinite recursivity.</p></div>
*
* <div class="section">Flexibility in implementations</div>
* 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 {@code ==} operator) between values
* instead than using {@link Object#equals(Object)}. This flexibility means that even if all above conditions
* are true, this is not a guarantee that this method will return {@code true}.
*
* <p>It is okay to <em>not</em> override this method at all since the identity comparison inherited from
* {@link Object#equals(Object)} is consistent with this method contract. Alternatively, {@code Node}
* implementations having a content fully determined by the wrapped {@linkplain #getUserObject() user
* object} need only the following implementation:</p>
*
* {@preformat java
* &#64;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 {@code null} user objects or by invoking
* {@link Object#equals(Object)} instead than performing identity comparisons. Note however that since this
* method purpose is to detect cyclic graphs (see above example), user objects should be compared with
* {@code equals(Object)} only if their implementations are known to be safe against infinite recursivity.
*
* @param other the other object to compare with this node.
* @return whether the two objects are nodes with equal values and equal children, ignoring parents.
*
* @since 0.8
*/
@Override
boolean equals(Object other);
/**
* Returns a hash code value consistent with the {@code equals(Object)} implementation for this node.
* If the {@link #equals(Object)} method has not been overridden, then this {@code hashCode()} method
* should not be overridden neither. Otherwise if this node content ({@linkplain #getValue values} and
* {@linkplain #getChildren() children}) is fully generated from the {@linkplain #getUserObject() user
* object}, then the {@code equals(…)} and {@code hashCode()} methods may be implemented like below:
*
* {@preformat java
* &#64;Override
* public boolean equals(Object obj) {
* return (obj instanceof MyNode) && ((MyNode) obj).getUserObject() == getUserObject();
* }
*
* &#64;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.
*
* @return a hash code for this node, potentially based on values and children but ignoring parent.
*
* @since 0.8
*/
@Override
int hashCode();
}
}