Skip to content

Commit

Permalink
Added Tree Interface, GenericTree abstract class, and tests
Browse files Browse the repository at this point in the history
  • Loading branch information
CodeByDrescher committed Nov 14, 2024
1 parent 6be39a1 commit eecf5d6
Show file tree
Hide file tree
Showing 4 changed files with 256 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package org.vcell.util.trees;

public class GenericStringTree extends GenericTree<String> {
@Override
protected boolean isInvalidType(Object o) {
return !(o instanceof String);
}
}
121 changes: 121 additions & 0 deletions vcell-util/src/main/java/org/vcell/util/trees/GenericTree.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
package org.vcell.util.trees;

import java.util.Arrays;
import java.util.List;

public abstract class GenericTree <T> implements Tree<T> {
private final Node<T> root;

public GenericTree(){
this(null);
}

private GenericTree(T rootValue){
this.root = new Node<>(rootValue);
}


@Override
@SafeVarargs
final public boolean hasElement(T... path) {
try {
Node<T> targetNode = getTargetNode(path);
return targetNode.getData().equals(path[path.length - 1]);
} catch (Exception e) {
return false;
}
}

@Override
@SafeVarargs
final public List<T> getChildren(T... path) {
Node<T> targetNode = getTargetNode(path);
return targetNode.getChildren().stream().map(Node::getData).toList();
}

@Override
@SafeVarargs
final public boolean addElement(T ... path){
return this.addElement(false, path);
}

@Override
@SafeVarargs
final public boolean addElement(boolean makeParents, T ... path){
T[] parentPath = Arrays.copyOf(path, path.length - 1);
Node<T> targetNode = this.getTargetNode(makeParents, parentPath);
for (Node<T> child : targetNode.getChildren()) if (child.getData().equals(path[parentPath.length])) return false;
targetNode.addChild(new Node<>(path[parentPath.length]));
return true;
}

@Override
@SafeVarargs
final public T swapElements(T replacement, T ... path){
return this.swapElements(false, replacement, path);
}

@Override
@SafeVarargs
final public T swapElements(boolean removeChildren, T replacement, T ... path){
Node<T> targetNode = getTargetNode(path);
T oldValue = targetNode.getData();
if (!targetNode.getChildren().isEmpty() && removeChildren){
Node<T> parent = targetNode.getParent();
parent.removeChild(targetNode);
parent.addChild(new Node<>(replacement));
} else {
targetNode.setData(replacement);
}
return oldValue;
}

@Override
@SafeVarargs
final public T removeElement(T... path) {
return this.removeElement(false, path);
}

@Override
@SafeVarargs
final public T removeElement(boolean removeChildren, T... path) {
Node<T> targetNode = getTargetNode(path);
if (!targetNode.getChildren().isEmpty() && !removeChildren)
throw new IllegalArgumentException("Path element to remove has children; set flag to remove if desired behavior.");

T oldValue = targetNode.getData();
targetNode.getParent().removeChild(targetNode);
return oldValue;
}

protected abstract boolean isInvalidType(Object o);

protected Node<T> getChild(Node<T> node, T value){
for (Node<T> child : node.getChildren()) {
if (child.getData().equals(value)) return child;
}
return null;
}

@SafeVarargs
private Node<T> getTargetNode(T ... path) {
return this.getTargetNode(false, path);
}

@SafeVarargs
private Node<T> getTargetNode(boolean makeParents, T ... path){
Node<T> currentNode = this.root;
for (int i = 0; i < path.length; i++) { // Need index for exception statement
T element = path[i];
if (isInvalidType(element)) throw new IllegalArgumentException(element.toString() + " -> is not the correct type for this tree!");
Node<T> childNode = getChild(currentNode, element);
if (childNode == null){
if (!makeParents) throw new IllegalArgumentException("Path element `" + element + "` at index (" + i + ") not found");
childNode = new Node<>(element);
currentNode.addChild(childNode);
}
currentNode = childNode;
}
return currentNode;
}
}
74 changes: 74 additions & 0 deletions vcell-util/src/main/java/org/vcell/util/trees/Tree.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package org.vcell.util.trees;

import java.util.ArrayList;
import java.util.List;

public interface Tree <T>{
boolean hasElement(T ... path);
List<T> getChildren(T ... path);
boolean addElement(T ... path);
boolean addElement(boolean makeParents, T ... path);
T swapElements(T replacement, T ... path);
T swapElements(boolean removeChildren, T replacement, T ... path);
T removeElement(T ... path);
T removeElement(boolean removeChildren, T ... path);



class Node<T>{
private T data;
private Node<T> parent;
private List<Node<T>> children;

Node(T data){
this(data, new ArrayList<>());
}

Node(T data, List<Node<T>> children){
this.data = data;
this.parent = null;
this.children = children;
}

T getData(){
return data;
}

Node<T> getParent(){
return parent;
}

List<Node<T>> getChildren(){
return children;
}

void setData(T data){
this.data = data;
}

void setChildren(List<Node<T>> children){
this.children = children;
}

private void setParent(Node<T> parent){
this.parent = parent;
}

boolean hasChild(Node<T> childToFind){
for (Node<T> child : this.children){
if (child.equals(childToFind)) return true;
}
return false;
}

void addChild(Node<T> childToAdd){
if (this.hasChild(childToAdd)) return;
this.children.add(childToAdd);
childToAdd.setParent(this);
}

void removeChild(Node<T> childToRemove){
if (this.hasChild(childToRemove)) this.children.remove(childToRemove);
}
}
}
53 changes: 53 additions & 0 deletions vcell-util/src/test/java/org/vcell/util/GenericTreeTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package org.vcell.util;

import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Assertions;
import org.vcell.util.trees.GenericStringTree;
import org.vcell.util.trees.GenericTree;
import org.vcell.util.trees.Tree;

import java.util.List;

@Tag("Fast")
public class GenericTreeTest {
@Test
public void exerciseStringTree(){
Tree<String> testTree = new GenericStringTree();

testTree.addElement(true, "hello", "world");
Assertions.assertTrue(testTree.hasElement( "hello", "world"));
Assertions.assertThrows(IllegalArgumentException.class,
() -> testTree.addElement("hello", "my", "baby"));
Assertions.assertFalse(testTree.hasElement("hello", "my"));
testTree.addElement("hello", "my");
testTree.addElement( "hello", "my", "baby");
Assertions.assertTrue(testTree.hasElement("hello", "my", "baby"));

List<String> children = testTree.getChildren("hello");
Assertions.assertEquals(2, children.size());
Assertions.assertTrue(children.contains("world"));
Assertions.assertTrue(children.contains("my"));

testTree.swapElements("honey", "hello", "my", "baby");
Assertions.assertTrue(testTree.hasElement("hello", "my", "honey"));
testTree.addElement("hello", "my", "honey", "gal");
testTree.swapElements("ragtime", "hello", "my", "honey");
Assertions.assertTrue(testTree.hasElement("hello", "my", "ragtime", "gal"));
testTree.swapElements(true, "baby", "hello", "my", "ragtime");
Assertions.assertFalse(testTree.hasElement("hello", "my", "ragtime", "gal"));
Assertions.assertFalse(testTree.hasElement("hello", "my", "ragtime"));
Assertions.assertTrue(testTree.hasElement("hello", "my", "baby"));

testTree.swapElements("ragtime", "hello", "my", "baby");
testTree.addElement("hello", "my", "ragtime", "gal");
Assertions.assertThrows(IllegalArgumentException.class, () -> testTree.removeElement("hello", "my", "baby"));
testTree.removeElement("hello", "my", "ragtime", "gal");
Assertions.assertFalse(testTree.hasElement("hello", "my", "ragtime", "gal"));
Assertions.assertTrue(testTree.hasElement("hello", "my", "ragtime"));
testTree.removeElement(true, "hello", "my");
Assertions.assertFalse(testTree.hasElement("hello", "my", "ragtime"));
Assertions.assertFalse(testTree.hasElement("hello", "my"));
Assertions.assertTrue(testTree.hasElement("hello"));
}
}

0 comments on commit eecf5d6

Please sign in to comment.