“A Java API designed for handling objects with tree-like behavior. Transform linear Java objects into hierarchical tree structures. Add, remove, cut, copy, and export to JSON/XML with ease.”
HappyTree is a data structure API designed for handling Java objects that have tree-like behavior, whereas an @Id attribute of an object is referenced as a @Parent attribute of its children.
In certain circumstances there is a need to convert a list of Java objects that could represent a model layer in a business context, into an actual hierarchical tree structure in memory, where objects contain its children and each child contains its own children collection and so on.
When there is a need to put a collection (Set/List) of objects, where each object relates to another object of the same type through an identifier attribute in a tree-like manner, the HappyTree API is able to transform this structure into an actual tree structure in memory, where each object will be wrapped into a tree node object, called Element, and each element contains its children elements, where each child contains its own children and so on recursively.
From this point, the API client can handle those elements within a tree, such as adding/removing children from nodes, moving nodes to another point of the tree or even to another tree, copying nodes to other trees, converting trees into JSON/XML, etc.
The HappyTree API aims to provide a way of creating new trees, creating trees from existing collections of objects that have tree-like behavior, as well as handling these trees. It provides interfaces for the API client for three primary and clear objectives:
- Handle Java objects as if they were nodes within trees to perform operations such as copying, cutting, removing, creating, persisting/updating, etc. over those objects.
- Transform linear data structures of Java objects that have tree-like behavior into an actual tree.
- Create new trees from scratch.
The first purpose represents the basic operations of the trees, when the API client desires to change the state of the nodes (officially called Elements in the context of the API) in the trees, to move, copy, remove, create and update those nodes.
The second purpose is suitable for situations in which the API client needs to transform a collection of plain objects, of which there is a logical tree relation between them, into an actual tree. Here, each element contains its child elements, and each child contains its own children recursively.
The last one allows the API client to create new trees from scratch, persisting element by element to build the tree structure as desired.
For developers who feel the need to handle objects that have a tree-like behavior in their applications. There are several scenarios in which this API can be useful, such as:
- Handling directory structures.
- Handling organizational structures.
- Handling visual component structures.
- Handling product category structures.
- Handling comment/reply structures.
- And many other scenarios.
When the project into which this API was imported has a Java data model structure that logically represents a tree but its objects are only linearly referenced to each other, this API has precisely this purpose of transforming this linear structure into a physical tree structure. This process is known as the API Transformation Process, and it is one of the main core functionalities of the HappyTree API.
If, for example, the project has a collection of Java model objects representing directories in a file system, where each directory object has an identifier attribute which is referenced by the parent attribute from another directory object in the same collection, then this API can be used to transform this linear structure into an actual tree structure in memory.
Suppose we have something like this:
//Linear tree structure.
public class Directory {
//Own ID
private Integer directoryId;
//Super node reference
private Integer directoryParentId;
//Simple attribute
private String directoryName;
//getters and setters
}But we want this:
//Recursive tree structure wrapped through the Element object.
public interface Element<Directory> {
private Object directoryId;
private Object directoryParentId;
private Collection<Element<Directory>> children;
//Skeleton methods.
public void addChild(Element<Directory> child);
public void removeChild(Element<Directory> child);
public void wrap(Directory directory);
public Directory unwrap();
}If you want to do something described above, that is, using the HappyTree API to transform a linear structure (linear parent-child relationships through the identifiers) into a tree structure.
1 - You must first add the following annotations to the Java Class, usually a POJO, to which its objects will be inserted as a tree node and make it to implement the Serializable interface:
//Linear tree structure.
@Tree
public class Directory implements Serializable {
@Id
private Integer directoryId;
@Parent
private Integer directoryParentId;
//Simple attribute
private String directoryName;
public Directory() {}
public Integer getDirectoryId() {
return directoryId;
}
public void setDirectoryId(Integer directoryId) {
this.directoryId = directoryId;
}
public Integer getDirectoryParentId() {
return directoryParentId;
}
public void setDirectoryParentId(Integer directoryParentId) {
this.directoryParentId = directoryParentId;
}
public String getDirectoryName() {
return directoryName;
}
public void setDirectoryName(String directoryName) {
this.directoryName = directoryName;
}
}2 - After that, just group these objects into a collection and initialize a new session. The meaning of "session" here is a tree instance that will handle these objects, in other words, sessions are just trees semantically.
A tree can be created either through the API Transformation Process of a previous collection of objects that have tree behavior, disposed in a linear structure or it can be created freely from scratch, one by one.
For the case of the API Transformation Process, the initialization is something similar to the code below:
//Getting the collection of Directory objects from a private method.
Collection<Directory> directories = myObject.myMethodToGetDirectories();
//API usage to create a tree from the collection above.
TreeManager manager = HappyTree.createTreeManager();
TreeTransaction transaction = manager.getTransaction();
transaction.initializeSession("myFirstHappyTree", directories);To initialize an empty tree from scratch, the code snippet looks something like the code below:
TreeManager manager = HappyTree.createTreeManager();
TreeTransaction transaction = manager.getTransaction();
transaction.initializeSession("mySecondHappyTree", Directory.class);3 - Now, just handle it as you wish.
Element<Directory> administration = manager.getElementById(
administrationId);
Element<Directory> accessControl = manager.getElementById(
accessControlId);
/*
* The Access Control menu and its children will now be moved to the
* Administration menu.
*/
manager.cut(accessControl, administration);
Directory security = new Directory();
security.setDirectoryId(securityId);
security.setDirectoryParentId(administrationId);
Element<Directory> securityElement = manager.createElement(securityId,
administrationId, security);
//New inserted element (Security) in the tree.
securityElement = manager.persistElement(securityElement);
/*
* The Access Control menu has been moved to the new Security menu.
*/
accessControl = manager.cut(accessControl, securityElement);
/*
* If the Security element contains the Access Control element and the
* Administration element no longer contains the Access Control element,
* print the tree in JSON format.
*/
if (manager.containsElement(securityElement, accessControl)
&& !manager.containsElement(administration, accessControl)) {
Element<Directory> root = manager.root();
System.out.println(root.toJSON());
}
Once created, the trees start to work with Element type objects, which will encapsulate (wrap) their respective "original objects" (Directory) represented as nodes. From there, just use the TreeManager interface to handle these elements.
To import the HappyTree API into a project, simply add the following:
- Maven
<dependency>
<groupId>com.madzera</groupId>
<artifactId>happytree</artifactId>
<version>2.0.0</version>
</dependency>- Gradle
implementation 'com.madzera:happytree:2.0.0'Compared to v1.0.0, the groupId of this new version has changed:
- Maven
- <groupId>com.madzera.happytree</groupId>
<artifactId>happytree</artifactId>
- <version>1.0.0</version>
+ <groupId>com.madzera</groupId>
<artifactId>happytree</artifactId>
+ <version>2.0.0</version>- Gradle
- implementation 'com.madzera.happytree:happytree:1.0.0'
+ implementation 'com.madzera:happytree:2.0.0'HappyTree Official Documentation - PDF
Would you like to contribute? We are extremely grateful! Thank you!
See our Wiki or Contributing file.