Interface TreeManager


public interface TreeManager
Provides ways to handle elements within a tree session. This makes it possible to create, persist, update, cut, copy, remove, retrieve elements, and perform any other operations within the tree.

This interface works by directly handling objects represented by Element, where each one behaves similarly to a node within the tree. So, in practical terms, this interface allows the API client to handle business objects with tree-like behavior.

Operations here are performed on elements within other elements or on "root" elements. Root elements are those that are not inside other elements, but are at the "top" of the tree hierarchy in question. When the tree is created, its root is also created automatically, and it can be empty or have multiple children.

The operations are also done for cases where it is desirable to reallocate elements to other tree sessions. In this case, the uniqueness of each element as well as the same class type of the nodes must be respected, related to the tree in which elements will be placed.

It is important to note that there are two distinct and well-defined contexts in the HappyTree API: the inside and outside contexts. When the API client obtains an element and its children through the manager, the API client is actually working with identical copies of each node (element). When the API client makes any changes to any of the elements, a change in the element's lifecycle is made, and this change is not immediately reflected in the tree in question. This context is called outside the tree. An inside context represents the client's action to perform the persist/update of the element by using this interface, so the change is actually reflected in the tree, but made through this manager itself. The persist/update operations work by invoking persistElement(Element) and updateElement(Element) respectively.

Conceptually, this interface handles trees through a transaction. This transaction is represented by the TreeTransaction interface, and the relation between both interfaces is 1:1. Therefore, this interface will always be linked to a single transaction, which can contain none or many sessions, but it is only possible to handle one session at a time.

The API client must be sure to handle elements through their respective transactions. A simple swap of a tree, by a transaction, makes this manager ready to deal with a totally different tree.

For operations to work, the following validations are made:

  • The transaction must have a session bound, always.
  • It is mandatory that the session be activated.
  • According to the method, the elements must be in the proper state.
  • For each tree session, each element inside must have a unique @Id.
  • An element cannot be handled within trees that have different types of wrapped object nodes.
  • It is not possible to handle root elements for the copy(Element, Element), cut(Element, Element), persistElement(Element) and removeElement(Element) methods.

If one of these validations fails, an exception will be thrown.

Operations by Element State
Element State/operationAttachedDetached Not Existed
CutXX
CopyXX
RemoveXX
UpdateX
PersistXX
Author:
Diego Madson de Andrade Nóbrega
See Also:
  • Method Details

    • cut

      <T> Element<T> cut(Element<T> from, Element<T> to) throws TreeException
      Cuts the from element into the to element, whether for the same session or not. With this, the element to be cut can be cut into the same tree session or to another tree in another session. All children of the from element will be cut as well.

      If the to parameter element is null, then the from element with all children will be moved to the root level of the same tree.

      When cutting to the target element, the from parameter element cannot have a duplicate @Id in the tree where the to parameter element is located. This also includes the children IDs of the from element.

      It is imperative that both trees of the from and to elements be activated.

      Parameters:
      from - the source element
      to - the target element
      Returns:
      a copy of from element after cut
      Throws:
      TreeException - when:
      • The transaction has no selected session to work;
      • The current session or the session which the to element belongs is not active;
      • The from element does not belong in the correct current session;
      • The from and to elements have different types of wrapped nodes;
      • The from element is represented by the root of the tree (it is not possible to handle root elements);
      • The from or to element or at least one of their children have a DETACHED or NOT_EXISTED state in the lifecycle;
      • The from element has an already existing @Id in the target tree (if the to element is in another tree).
      IllegalArgumentException - when the from parameter is null
    • cut

      <T> Element<T> cut(Object from, Object to) throws TreeException
      Cuts the from element into the to element, inside of the same session. Both parameters represent the @Id of elements.

      If the to parameter is null or if its respective element is not found, then the from element with all children will be moved to the root level of the tree.

      If it is not possible to find the from element id passed through the parameter, then null is returned.

      Using this cut(Object, Object) operation, an element can only be cut into the same tree. To cut elements to other trees, consider using cut(Element, Element) where the target element is linked to another tree.

      Parameters:
      from - the source element
      to - the target element
      Returns:
      a copy of the element represented by the from parameter, after cut
      Throws:
      TreeException - when:
      • The transaction has no selected session to work;
      • The current session is not active;
      • The from element does not belong in the correct current session;
      IllegalArgumentException - when the from parameter is null
    • copy

      <T> Element<T> copy(Element<T> from, Element<T> to) throws TreeException
      Copies the respective from element into the to element in another tree session. The entire structure of the copied element will be pasted inside the to element. The element to be copied and all its children cannot have the same identifier as any element in the to element tree.

      Ensure that the current session is referencing the session to which the from element belongs, otherwise an exception with the description "Element not defined in this session" will be thrown.

      Also, this method should only be used when the client desires to copy the element between different trees. It is not possible to copy elements inside the same tree, because it will throw a duplicate @Id exception.

      The following steps are done internally inside the core API to copy an element:

      1. Validates the current session and the input;
      2. Copies the whole element structure with all the children;
      3. Invokes TreeTransaction.sessionCheckout(String) so that the target tree can be worked on;
      4. Pastes the elements;
      5. Invokes TreeTransaction.sessionCheckout(String) back to the source tree, as before.

      It is mandatory that both from and to elements be attached in different trees, and both trees must be activated.

      Parameters:
      from - the source element
      to - the target element
      Returns:
      a copy of the copied element in the target tree session
      Throws:
      TreeException - when:
      • The transaction has no selected session to work;
      • The current session (to which the from element belongs) or the session which the to element belongs is not active;
      • The from element does not belong in the correct current session (it occurs when the current session is not the same to which from element belongs);
      • The from and to elements have different types of wrapped nodes;
      • The from element is represented by the root of the tree (it is not possible to handle root elements);
      • The from or to element or at least one of their children have a DETACHED or NOT_EXISTED state in the lifecycle;
      • The from element has an already existing @Id in the target tree.
      IllegalArgumentException - when the from or to parameters are null
    • removeElement

      <T> Element<T> removeElement(Element<T> element) throws TreeException
      Removes the corresponding element from the tree session and returns the removed element itself. After being removed, the element and all its children will have the NOT_EXISTED state in the lifecycle. In the case of reinserting this removed element, it must be persisted again.

      The element to be removed must be attached (ATTACHED state) in the tree and cannot have changes. All children will be removed as well, if they are in the ATTACHED state in their lifecycle. If there is at least a single child element that is not ATTACHED, then none will be removed and this method will return null.

      In the case where the element or its children are not attached, then it is necessary to attach them in the current tree session by invoking persistElement(Element) if it is a new element or invoking updateElement(Element) if it is a changed element. After that, the element in question becomes attached again and can then be removed.

      If the element parameter is null then this method also will return null.

      Parameters:
      element - the element to be removed with all its children
      Returns:
      the removed element itself, but now with the NOT_EXISTED state in the lifecycle
      Throws:
      TreeException - when:
      • The transaction has no selected session to work;
      • The current session is not active;
      • The from element does not belong in the correct current session;
      • The element has a different type of wrapped node related to the current session;
      • The element is represented by the root of the tree (it is not possible to handle root elements);
      • The element or at least one of its children have a DETACHED or NOT_EXISTED state in the lifecycle;
    • removeElement

      <T> Element<T> removeElement(Object id) throws TreeException

      Removes the element by its @Id. All children of the found element are removed as well and returns the removed element itself. Note that removing an element means that this element will be permanently eliminated from inside the tree, having its state as NOT_EXISTED in its lifecycle.

      If the @Id cannot be found or is null, then this method will return null.

      Be sure to be in the correct tree session, so as not to remove an element with the same @Id but in another tree.

      Parameters:
      id - the identifier of the element to be removed
      Returns:
      the removed element itself, but with the NOT_EXISTED state in the lifecycle
      Throws:
      TreeException - when:
      • The transaction has no selected session to work;
      • The current session is not active;
    • getElementById

      <T> Element<T> getElementById(Object id) throws TreeException
      Obtains an element by @Id in the current tree session.

      If the @Id is null or it cannot be found in the tree, then this method will return null.

      The id corresponds to the @Id annotated attribute of the wrapped object node in the API Transformation Process or just an ID which the API client chooses. When a tree is being built by a previous collection of objects (API Transformation Process), the core API will bind the elements by this @Id annotated attribute.

      Therefore:

      • The @Id can never be null;
      • The @Id is always unique in the tree.

      The element and its children returned by this method represent elements with ATTACHED state in relation to the current tree session. An attached element means that the element and its children are "mirror" pieces of the tree.

      If the element or its children change states, then it is necessary to attach them again in the current tree session by invoking updateElement(Element) to be able to handle them, for operations like cut(Element, Element) or copy(Element, Element) for example.

      Parameters:
      id - the element identifier
      Returns:
      an identical copy of the found element
      Throws:
      TreeException - when the transaction has no selected session to work or the current session is not active
    • containsElement

      <T> boolean containsElement(Element<T> parent, Element<T> descendant) throws TreeException
      Verifies whether the parent element contains inside of it the descendant element in this current session.

      If both parent and descendant elements are null or their (including the children) state are not ATTACHED to this tree session, then false is returned.

      Parameters:
      parent - the element which will contain the descendant element
      descendant - the element which is inside of parent element
      Returns:
      true value if the parent element contains the descendant element, false otherwise
      Throws:
      TreeException - when the transaction has no selected session to work or the current session is not active
    • containsElement

      boolean containsElement(Object parent, Object descendant) throws TreeException
      Verifies whether the parent element contains inside of it the descendant element in this current session.

      The parent and descendant identifiers are used to bring the respective elements. If both parent and descendant elements are null or not found, then false is returned.

      When the elements are found, then it means that they were already caught from a tree and they are automatically with the ATTACHED state, so this method returns true when the descendant element is inside of the parent element.

      Parameters:
      parent - the parent identifier which will contain the descendant element
      descendant - the descendant identifier which will be inside of the parent element
      Returns:
      true value if the parent element contains the descendant element, false otherwise
      Throws:
      TreeException - when the transaction has no selected session to work or the current session is not active
    • containsElement

      boolean containsElement(Element<?> element) throws TreeException
      Verifies that the current tree session has the specified element.

      If the element is null, not found or if the element or at least one of its children is not in the ATTACHED state in the lifecycle then false is returned.

      Parameters:
      element - the specified element to be searched
      Returns:
      true value if the tree contains the element, false otherwise
      Throws:
      TreeException - when the transaction has no selected session to work or the current session is not active
    • containsElement

      boolean containsElement(Object id) throws TreeException
      Verifies that the current tree session has the specified element by the given @Id.

      If the element is not found in the tree or the id is null then false is returned.

      Parameters:
      id - the identifier of the element to be searched
      Returns:
      true value if the tree contains the respective element, false otherwise
      Throws:
      TreeException - when the transaction has no selected session to work or the current session is not active
    • createElement

      <T> Element<T> createElement(Object id, Object parent, T wrappedNode) throws TreeException
      Creates an element with the id, parent and the wrapped object node. Only the id is mandatory. When the parent is null or not found, then this element will be moved to inside of the root level (first level) of the tree, when persisted.

      Creating a new element does not mean that it will be automatically in the tree of the current session. When creating a new element, it is "outside" of the tree yet, having the NOT_EXISTED state in the lifecycle. The element needs to be attached in the tree right after the creation moment time, by invoking persistElement(Element). So, after that, the element becomes attached and finally can be handled by cut(Element, Element) or copy(Element, Element) operations for example.

      Ensure that the parameterized type when creating an element is the same type of the object related to the current session.

      Type Parameters:
      T - the class type of wrapped object node that will be encapsulated into the Element object
      Parameters:
      id - the identifier of the new element
      parent - the parent identifier of this new element
      wrappedNode - the object to be encapsulated in this element
      Returns:
      a new element with the NOT_EXISTED state in lifecycle
      Throws:
      TreeException - when the transaction has no selected session to work or the current session is not active
      IllegalArgumentException - when the id parameter is null
    • persistElement

      <T> Element<T> persistElement(Element<T> newElement) throws TreeException
      Persists a new element into the current tree session. The new element to be persisted must have a unique identifier in the tree session. If the @Parent of this new element is defined with null or if it is simply not found, then this new element will be persisted in root level of the tree (first level).

      Also, the new element must be essentially new, created using the createElement(Object, Object, Object) method to guarantee a coherent detached state from the current tree. After creating the element, it will have the NOT_EXISTED state in the lifecycle, even its children (in case of creating children inside this element).

      This method also allows new chained children elements to be persisted at once. If the element has duplicate identifier or there is a descendant in this element to be created that has duplicate identifier in relation to the tree, then an exception will be thrown.

      The NOT_EXISTED state represents an "outside tree session" element, which means that this element has not been inside any tree. When the element is persisted by this operation, its state changes to ATTACHED in the lifecycle. This also happens with its children when they are persisted at once.

      Element States
      Element StatusWayspersistElement()
      NOT_EXISTED createElement(Object, Object, Object) Yes
      ATTACHED getElementById(Object) No
      DETACHED getElementById(Object) - Element.setId(Object)No
      Parameters:
      newElement - the element to be persisted
      Returns:
      a copy of the new persisted element with the ATTACHED state in lifecycle
      Throws:
      TreeException - when:
      • The transaction has no selected session to work;
      • The current session is not active;
      • The newElement does not belong in the correct current session;
      • The newElement has a different type of wrapped node related to the current session;
      • The newElement or at least one of its descendants have a DETACHED or ATTACHED state in the lifecycle;
      • The newElement has an already existing identifier in this session.
      IllegalArgumentException - when the newElement or its @Id is null
    • updateElement

      <T> Element<T> updateElement(Element<T> element) throws TreeException
      Updates the state of the element to the tree. Synchronizes a previous changed element.

      To be updated, an element should be previously captured by invoking getElementById(Object) for example, and its state should be as DETACHED.

      A DETACHED element represents an element, or one of its descendants, that has undergone some change, whether it be the @Id, the @Parent, or the wrapped node. Make sure to avoid duplicate @Id in the current session when changing an @Id. To move the element to the root level (first level), just set the @Parent as null or reference an inexistent parent element.

      This operation is for DETACHED elements. Trying to update a NOT_EXISTED element in the tree throws an exception. For ATTACHED elements, nothing happens because the element is already attached, and the same copy is returned.

      Therefore, it is only possible to update an element through persistElement(Element) or by the API Transformation Process, passing a previous list of elements to be transformed into a tree by the TreeTransaction.initializeSession(String, java.util.Collection) method.

      This operation also works for the root element, allowing the update when the root element adds or removes children elements directly.

      When updating an element, its children list will also be automatically updated recursively. After updating, all elements have their states as ATTACHED in the lifecycle.

      Element States
      Element StatusWaysupdateElement()
      NOT_EXISTED createElement(Object, Object, Object) No
      ATTACHED getElementById(Object) Yes
      DETACHED getElementById(Object) - Element.setId(Object)Yes
      Parameters:
      element - the element to be updated
      Returns:
      a copy of the updated element with the ATTACHED state in the lifecycle
      Throws:
      TreeException - when:
      • The transaction has no selected session to work;
      • The current session is not active;
      • The element does not belong in the correct current session;
      • The element has a different type of wrapped node related to the current session;
      • The element or at least one of its descendants have a NOT_EXISTED state in the lifecycle;
      • The element has an already existing @Id in this session.
      IllegalArgumentException - when the element or its @Id is null
    • getTransaction

      TreeTransaction getTransaction()
      Returns the TreeTransaction instance associated with this manager.

      The manager is closely related to the transaction. Every operation defined in this interface needs to check the transaction and verify whether there is a session to be managed.

      If there is no session to be handled or the session is not active inside the transaction, an error occurs. The API client using this method should know what session (tree) is preferred to work with. This transaction object has this objective to provide and handle the sessions.

      TREEMANAGER (invokes) -> TREETRANSACTION (to store) -> TREESESSION (that contains) -> ELEMENT
      Returns:
      the transaction associated to this manager
    • root

      <T> Element<T> root() throws TreeException
      Returns the root of the tree in this current session.

      The root structure is also an object of Element type which represents the top of the tree and encompasses all other elements.

      It contains a collection of children, which in turn contains a collection of children, and so on recursively. All this structure is returned in this method as Element type.

      The root element is like a 'special' element created exclusively by the core API. All elements that the API client handles are under the root element. Thus it is not possible to create a root element. Because of that, every object node with a null @Parent or an unknown (not found) @Parent will be attached directly as an immediate root child (first level).

      Unlike regular elements, the root element has no metadata associated with it, such as @Id, @Parent and wrapped object node. Therefore, calling the Element.getId(), Element.getParent() or Element.unwrap() methods on the root element will always return null.

      For being a special element, write operations like copy, cut or remove cannot be applied directly to the root element, otherwise an exception will be thrown and the execution aborted.

      Below, an example of a tree structure with its root element and children:

      
                                                              ELEMENT(ROOT)
                                    /\
                          ELEMENT(A)  ELEMENT(B)
                              /\         /\
                         E(A1) E(A2) E(B1) E(B2)
      
      
      The creation of the root element is responsibility of the core API. It occurs at the moment of initialization of a new session when the TreeTransaction.initializeSession(String, java.util.Collection) or TreeTransaction.initializeSession(String, Class) is invoked.
      Returns:
      the root element
      Throws:
      TreeException - when the transaction has no selected session to work or if the current session is not active
      See Also:
    • search

      <T> List<Element<T>> search(Predicate<Element<T>> condition) throws TreeException
      Searches for elements that satisfy a specific condition within the entire tree structure. The method returns a list of elements that match the provided condition.

      This method traverses the complete tree starting from the root element 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 matching elements while preserving each element's hierarchy.

      Example usage:

      //Find all elements which the object node has its name starting with "A"
      List<Element<MyNodeType>> results = manager.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
      Throws:
      TreeException - when the transaction has no selected session to work or if the current session is not active
    • apply

      <T> void apply(Consumer<Element<T>> action) throws TreeException
      Applies a function to be performed on all elements within the entire tree structure. The action applied to every element in the tree is automatically reflected on the tree session (if there are any changes), not being necessary to invoke the persistElement(Element) nor updateElement(Element) to save the changes.

      This method traverses the complete tree starting from the root element and applies the given action to all elements in the tree (except for the root element itself). Unlike apply(Consumer, Predicate), this method operates on the entire tree structure rather than just a subset of elements that match the condition.

      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.

      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 in the entire tree
      manager.apply(e -> e.unwrap().transformNameToUpperCase());
      
      //Log all element IDs in the tree
      manager.apply(e -> System.out.println("Element ID: " + e.getId()));
      

      Note: The function is applied to all elements in the entire tree (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 tree
      Throws:
      TreeException - when the transaction has no selected session to work or if the current session is not active
      See Also:
    • apply

      <T> void apply(Consumer<Element<T>> action, Predicate<Element<T>> condition) throws TreeException
      Applies a function to be performed on elements that satisfy a specific condition within the entire tree structure. The action applied to every matching element in the tree is automatically reflected on the tree session (if there are any changes), not being necessary to invoke the persistElement(Element) nor updateElement(Element) to save the changes.

      This method traverses the complete tree starting from the root element and applies the given action only to elements that satisfy the specified condition (not including the root element itself). Unlike apply(Consumer), this method allows for selective application of the action based on custom criteria defined by the condition.

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

      The condition is a predicate function that takes an Element as input and returns a boolean value indicating whether the action should be applied to that element. Only elements for which the condition evaluates to true will have the action applied.

      Example usage:

      //Transform names to uppercase for directories only
      manager.apply(
          e -> e.unwrap().transformNameToUpperCase(),
          e -> e.unwrap().isDirectory()
      );
      
      //Log IDs of elements with names starting with "A"
      manager.apply(
          e -> System.out.println("Element ID: " + e.getId()),
          e -> e.unwrap().getName().startsWith("A")
      );
      

      Note: The function is applied only to elements that satisfy the specified condition (not including the root element). If you want to apply an action to all elements in the tree, consider using apply(Consumer) instead.

      Parameters:
      action - the function to apply to each element that meets the condition
      condition - the predicate function to determine which elements should have the action applied
      Throws:
      TreeException - when the transaction has no selected session to work or if the current session is not active
      See Also: