Interface Element<T>

Type Parameters:
T - the class type of the wrapped object node that will be encapsulated in this Element

public interface Element<T>
An Element represents a node within a tree. It consists of the elementary unit of the HappyTree API, and it is directly handled through the TreeManager interface. An element can move from one tree to another, be removed, copied, and even encapsulate any object (node) within it, making this encapsulated object hierarchically available within a tree structure.

This interface is only responsible for handling the element itself and its children. Here, there is no direct relationship with the other available interfaces, except its respective session (TreeSession) which this element belongs to. Considering that an element corresponds to an elementary unit, it can only relate to itself and its children.

An element is always associated with a session. Even after its creation, the element already belongs to a session, but that does not mean that this element is already in the tree. It depends on its lifecycle state.

To be handled, an element must be in an appropriate state in its lifecycle, depending on the operation that is desired to perform. Its lifecycle consists of 3 states:

Element Lifecycle States
StateDescription
NOT_EXISTED When the element is created. At this time, the element has not been persisted inside any tree session yet.
ATTACHED When the element is created and after that, it is persisted within the tree session.
DETACHED When the element is already inside of the tree as ATTACHED but it has been changed since the API client gained access to it.

Considering that an element is previously attached to some session, each subsequent manipulation from the perspective of the element itself will turn its state to DETACHED, thus requiring an update by invoking TreeManager.updateElement(Element).

The API client has access to the following attributes:

Element Attributes
AttributesDescription
IDThe element identifier within the tree (must be unique within the tree).
ParentThe parent element identifier within the tree (if the parent element is not found or null then this element will be moved to the tree root level).
ChildrenThe children list and all descendants of the element.
Wrapped NodeThe encapsulated object node (the original object).
SessionThe session to which this element belongs.
LifecycleThe current state in the lifecycle.

The relationship between @Id and @Parent in the tree composition represents the main part of the HappyTree API. The tree is assembled strictly by this relation, in which the @Parent of an element references the @Id of the parent element. Therefore, the @Id of an element can never be null, and if the @Parent of an element is null or not found, that element is moved to the root level (first level) of the tree.

Author:
Diego Madson de Andrade Nóbrega
See Also:
  • Method Summary

    Modifier and Type
    Method
    Description
    void
    addChild(Element<T> child)
    Adds a new child element into the current element.
    void
    Adds a list of child elements to be concatenated to the current children list.
    void
    apply(Consumer<Element<T>> action)
    Applies a function to be performed on this element and all its children recursively within the tree structure.
    void
    apply(Consumer<Element<T>> action, Predicate<Element<T>> condition)
    Applies a function to be performed within this element that satisfies the specified condition.
    Returns the TreeSession instance to which this element belongs.
    Obtains all child elements of the current element.
    Searches within the current element for an element according to the id parameter.
    Obtains the element identifier.
    Obtains the parent identifier of this element.
    Returns the current lifecycle state of this element.
    void
    Removes the specified child element from the children list of the current element.
    void
    Removes the element from the children list of the current element by the id parameter.
    void
    Removes a subset of elements within this one.
    search(Predicate<Element<T>> condition)
    Searches for elements that satisfy a specific condition within this element and its children recursively.
    void
    Sets the element identifier.
    void
    setParent(Object parent)
    Sets the parent identifier reference of this element.
    Converts the whole element structure into a JSON format.
    Converts the whole element structure into a well formatted JSON String.
    Converts the whole element structure into a well formatted XML String.
    Converts the whole element structure into an XML String.
    Returns a copy of the object node wrapped in this element.
    void
    wrap(T object)
    Encapsulates any object node within the element, as long as this object has the same class type as other objects that were encapsulated within the same tree session.
  • Method Details

    • getId

      Object getId()
      Obtains the element identifier.

      This identifier is unique within the tree session when this is attached to the tree.

      Returns:
      the element identifier
    • setId

      void setId(Object id)
      Sets the element identifier.

      By invoking this method, the element will not change its @Id immediately. It is necessary to update the element for the change to take effect.

      The @Id must be unique. If it is null, nothing happens because this method is used for updates, so the element keeps the old @Id.

      When the element is a root element, the @Id is not set.

      Parameters:
      id - the element identifier to be updated
    • getParent

      Object getParent()
      Obtains the parent identifier of this element.
      Returns:
      the parent element identifier
    • setParent

      void setParent(Object parent)
      Sets the parent identifier reference of this element.

      If the @Parent reference is null or references a nonexistent element, then it is certain that this element will be in the root level of the tree when it is going to be persisted or updated.

      When the element is a root element, the @Parent is not set.

      Parameters:
      parent - the parent element identifier
    • getChildren

      Collection<Element<T>> getChildren()
      Obtains all child elements of the current element. This includes all descendants recursively.
      Returns:
      all children of the current element
    • addChild

      void addChild(Element<T> child)
      Adds a new child element into the current element.

      If the child element contains children, they will also be added.

      Parameters:
      child - the element to be added as child
    • addChildren

      void addChildren(Collection<Element<T>> children)
      Adds a list of child elements to be concatenated to the current children list.

      If each element within the children list contains more children within it recursively, they will also be added.

      Parameters:
      children - the list of child elements to be concatenated to the current children list
    • getElementById

      Element<T> getElementById(Object id)
      Searches within the current element for an element according to the id parameter.

      The search is performed recursively within the children of this element starting by the current element itself. If the element is not found, then null is returned.

      Parameters:
      id - the element identifier to be searched for
      Returns:
      the found element within this one
    • removeChildren

      void removeChildren(Collection<Element<T>> children)
      Removes a subset of elements within this one.

      The element references are removed whether they are found in children list. When an element is removed, all of its children as well as the elements below the hierarchy are also removed, recursively.

      Parameters:
      children - the list of child elements to be removed
    • removeChild

      void removeChild(Element<T> child)
      Removes the specified child element from the children list of the current element.

      The child element reference is removed if it is found in the children list. When an element is removed, all of its children as well as the elements below in the hierarchy are also removed, recursively.

      Parameters:
      child - the element to be removed
    • removeChild

      void removeChild(Object id)
      Removes the element from the children list of the current element by the id parameter.

      If it exists, then the element and all of its children are removed as well.

      Parameters:
      id - the element identifier to be removed
    • wrap

      void wrap(T object)
      Encapsulates any object node within the element, as long as this object has the same class type as other objects that were encapsulated within the same tree session.

      As an element conceptually represents a node within a tree, the wrapped object node would be simulating precisely its position within the tree in question.

      The object node itself can be wrapped at two different times:

      1. Initializing a new empty tree session;
      2. Initializing a tree session from the API Transformation Process.

      To be effectively wrapped within this element in the tree session, the invocation of TreeManager.persistElement(Element) (for new elements) or TreeManager.updateElement(Element) (for changing object nodes) is required, depending on the context.

      The first one would be to create an empty tree. In this way, the wrapped node is determined after the tree is ready by invoking this method. This includes the choice of not determining it, leaving it with a null value.

      The latter happens in the API Transformation Process, that is, at the moment of initialization, before the tree is created, when a call to TreeTransaction.initializeSession(String, Collection) transforms a previous linear structure into a real hierarchical tree structure. Then, each Element object will wrap its respective node automatically.

      Therefore, each object node of this linear structure would be represented precisely by this encapsulated object within its respective Element object in its respective hierarchy within the tree. In this case, during the API Transformation Process lifecycle, the object node will be transformed, managed, and wrapped within its own element automatically.

      There are some requirements for this object node to be wrapped when initializing a session by the API Transformation Process:

      • The class of this node must be annotated by @Tree;
      • The parent attribute of this node must be annotated by @Parent;
      • The ID attribute of this node must be annotated by @Id;
      • The class of this node must implement Serializable;
      • The @Id value cannot be null;
      • The @Id and @Parent must be of the same class type.

      These requirements apply only when the session is initialized by the API Transformation Process.

      When the element is a root element, it is not possible to wrap the object in it. So, the wrapped object node is always null.

      Parameters:
      object - the object to be wrapped within this element
    • unwrap

      T unwrap()
      Returns a copy of the object node wrapped in this element.

      The object node itself cannot be changed. This method provides a way to access this object. To modify its state, consider using the wrap(Object) method and save the changes by invoking the TreeManager.updateElement(Element) method.

      Returns:
      a copy of the wrapped object node inside this element
    • attachedTo

      TreeSession attachedTo()
      Returns the TreeSession instance to which this element belongs. It represents the tree to which this element belongs.

      When an element is created, either through the API Transformation Process or normally through invoking TreeManager.createElement(Object, Object, Object), the current session of the transaction is always associated with the created element.

      Therefore, an element is always associated with a session.

      Returns:
      the session (tree) to which this element belongs
    • lifecycle

      String lifecycle()
      Returns the current lifecycle state of this element.

      The lifecycle is important to indicate which operations are allowed for this element through the TreeManager interface.

      Allowed Operations by Element Lifecycle State
      TreeManager OperationAllowed Lifecycle
      cut()ATTACHED
      copy()ATTACHED
      removeElement()ATTACHED
      containsElement()ATTACHED
      persistElement()NOT_EXISTED
      updateElement()ATTACHED; DETACHED

      For all operations where the lifecycle does not correspond, a TreeException will be thrown, except for TreeManager.containsElement(Element, Element) which just returns false when the elements are not ATTACHED.

      All operations provided by TreeManager change the element's state to ATTACHED, except TreeManager.createElement(Object, Object, Object) (NOT_EXISTED).

      Returns:
      String corresponding to the lifecycle state name of this element
    • toJSON

      String toJSON()
      Converts the whole element structure into a JSON format. This includes all children recursively.

      It is mandatory that the element as well as all its children have non-null wrapped object nodes. If there is at least one wrapped object node that is null, then an empty JSON object is returned "{}".

      The above restriction is not applied to the root element, as it is a special element created by the HappyTree API itself with no wrapped object node. Therefore, calling TreeManager.root() or TreeSession.tree() is allowed to print the tree structure in a JSON format, but the root element will not contain the @Id, @Parent, or the wrapped object node.

      The JSON representation consists of the attributes of the wrapped object node plus an attribute called children, which holds all the children of the node and so on recursively:

      • Object Node Attributes: all getters attributes from the class annotated with the @Tree annotation;
      • Children Attribute: the same structure but placed as child in a recursive way.

      For example, considering an element that wraps an object of type Directory, the JSON representation would look like (pretty print):

      {
      "identifier": 1,
      "parentIdentifier": null,
      "name": "Music"
      "children": [
              {
                      "identifier": 2,
                      "parentIdentifier": 1,
                      "name": "Country"
                      "children": [
                              {
                                      "identifier": 3,
                                      "parentIdentifier": 2,
                                      "name": "Bruce Springsteen",
                                      "children": []
                              },
                              {
                                      "identifier": 4,
                                      "parentIdentifier": 2,
                                      "name": "George Strait",
                                      "children": []
                              }
                      ]
              }
      ]
      }
      

      To convert the wrapped object into a JSON format, it is not necessary that the element be attached to any session. However, the element as well as all its children need to have their original objects nodes not null (except for the root element).

      For conversion, the Jackson library is used internally, so the wrapped object can be annotated with Jackson annotations to customize the conversion if necessary.

      Returns:
      the JSON representation of this element (minified)
    • toPrettyJSON

      String toPrettyJSON()
      Converts the whole element structure into a well formatted JSON String. This includes all children recursively.

      It is mandatory that the element as well as all its children have non-null wrapped object nodes. If there is at least one wrapped object node that is null, then an empty JSON object is returned "{}".

      The above restriction is not applied to the root element, as it is a special element created by the HappyTree API itself with no wrapped object node. Therefore, calling TreeManager.root() or TreeSession.tree() is allowed to print the tree structure in a JSON format, but the root element will not contain the @Id, @Parent, or the wrapped object node.

      Returns:
      a well-formatted JSON of this element
      See Also:
    • toXML

      String toXML()
      Converts the whole element structure into an XML String. This includes all children recursively.

      It is mandatory that the element as well as all its children have non-null wrapped object nodes. If there is at least one wrapped object node that is null, then an empty XML is returned.

      The above restriction is not applied to the root element, as it is a special element created by the HappyTree API itself with no wrapped object node. Therefore, calling TreeManager.root() or TreeSession.tree() is allowed to print the tree structure as an XML String, but the root element will not contain the @Id, @Parent, or the wrapped object node.

      The XML content consists of the attributes of the wrapped object node plus a tag called children, which holds all other elements that contain all other children of the node and so on recursively. The root tag of this XML is always element.

      For example, considering an element that wraps an object of type Directory, the XML content would look like (pretty print):

              <element>
                      <identifier>1</identifier>
                      <parentIdentifier>null</parentIdentifier>
                      <name>Music</name>
                      <children>
                              <element>
                                      <identifier>2</identifier>
                                      <parentIdentifier>1</parentIdentifier>
                                      <name>Country</name>
                                      <children>
                                              <element>
                                                      <identifier>3</identifier>
                                                      <parentIdentifier>2</parentIdentifier>
                                                      <name>Bruce Springsteen</name>
                                                      <children/>
                                              </element>
                                              <element>
                                                      <identifier>4</identifier>
                                                      <parentIdentifier>2</parentIdentifier>
                                                      <name>George Strait</name>
                                                      <children/>
                                              </element>
                                      </children>
                              </element>
                      </children>
              </element>
      

      To convert the wrapped object into an XML format, it is not necessary that the element be attached to any session. However, the element as well as all its children need to have their original objects nodes not null (except for the root element).

      For conversion, the Jackson library is used internally, so the wrapped object can be annotated with Jackson annotations to customize the conversion if necessary.

      Returns:
      the XML content of this element (minified)
    • toPrettyXML

      String toPrettyXML()
      Converts the whole element structure into a well formatted XML String. This includes all children recursively.

      It is mandatory that the element as well as all its children have non-null wrapped object nodes. If there is at least one wrapped object node that is null, then an empty XML is returned.

      The above restriction is not applied to the root element, as it is a special element created by the HappyTree API itself with no wrapped object node. Therefore, calling TreeManager.root() or TreeSession.tree() is allowed to print the tree structure as an XML String, but the root element will not contain the @Id, @Parent, or the wrapped object node.

      Returns:
      a well formatted XML of this element
      See Also:
    • search

      List<Element<T>> search(Predicate<Element<T>> condition)
      Searches for elements that satisfy a specific condition within this element and its children recursively. The method returns a list of elements that match the provided condition.

      This method traverses the subtree starting from this element (including the element itself) and evaluates each element against the specified condition. If an element satisfies the condition, it is included in the resulting list.

      The resulting list consists of all elements that match the specified condition, including their children. Therefore, the list will include the elements that satisfy the condition, while preserving each element's hierarchical structure.

      When this method is invoked by the root element, the search is performed on all elements in the tree, except for the root element itself, as it is a special element created by the HappyTree API itself, not having the @Id, @Parent neither a wrapped object node.

      Example usage:

      //Find all elements within this subtree which the object node has its
      //name starting with "A"
      List<Element<MyNodeType>> results = element.search(
          e -> e.unwrap().getName().startsWith("A")
      );
      
      Parameters:
      condition - the predicate function defining the search criteria
      Returns:
      a list of elements that satisfy the specified condition within this element's subtree
    • apply

      void apply(Consumer<Element<T>> action)
      Applies a function to be performed on this element and all its children recursively within the tree structure. The action applied to the elements is not automatically reflected on the tree session, thus requiring to invoke TreeManager.persistElement(Element) for new elements or TreeManager.updateElement(Element) to save the changes for already existing elements.

      This method traverses the entire subtree starting from this element (including the element itself) and applies the given action to all elements in the tree.

      The action function receives each Element as a parameter and can perform any operation on it, such as modifying the wrapped object, changing element properties, or performing calculations/verifications.

      When this method is invoked by the root element, the action is applied to all elements in the tree, except for the root element itself, as it is a special element created by the HappyTree API itself, not having the @Id, @Parent nor a wrapped object node.

      Example usage:

      //Transform all directory names to uppercase
      element.apply(e -> e.unwrap().transformNameToUpperCase());
      
      //Log all element IDs in the subtree
      element.apply(e -> System.out.println("Element ID: " + e.getId()));
      

      Note: The function is applied to all elements in the subtree (except for the root element itself). If you need conditional execution based on specific criteria, consider using apply(Consumer, Predicate) instead.

      Parameters:
      action - the function to apply to each element in the subtree
      See Also:
    • apply

      void apply(Consumer<Element<T>> action, Predicate<Element<T>> condition)
      Applies a function to be performed within this element that satisfies the specified condition. The action applied to the elements is not automatically reflected on the tree session, thus requiring to invoke the TreeManager.persistElement(Element) for new elements or TreeManager.updateElement(Element) to save the changes for already existing elements.

      This method traverses the entire subtree starting from this element (including the element itself) and applies the given action only to those elements that match the specified predicate condition.

      The condition predicate is evaluated for each element in the subtree, and the action is only performed on elements where the predicate returns true. This allows for fine-grained control over which elements are affected by the function.

      When this method is invoked by the root element, the action is applied to all elements that match the condition in the tree, except for the root element itself, as it is a special element created by the HappyTree API itself, not having the @Id, @Parent nor a wrapped object node.

      Example usage:

      //Transform only elements containing "photo" in their name
      element.apply(
          e -> e.unwrap().transformNameToUpperCase(),
          e -> e.unwrap().getName().toLowerCase().contains("photo")
      );
      
      //Apply action only to leaf elements (elements with no children)
      element.apply(
          e -> processLeafElement(e),
          e -> e.getChildren().isEmpty()
      );
      

      Note: Unlike apply(Consumer), the function is applied only for elements that satisfy the condition passed as parameter. Elements that don't match the condition remain unchanged, providing selective capabilities.

      Parameters:
      action - the function to apply to matching elements
      condition - the predicate that determines which elements should receive the action
      See Also: