-
Notifications
You must be signed in to change notification settings - Fork 20
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added Tree Interface, GenericTree abstract class, and tests
- Loading branch information
1 parent
6be39a1
commit eecf5d6
Showing
4 changed files
with
256 additions
and
0 deletions.
There are no files selected for viewing
8 changes: 8 additions & 0 deletions
8
vcell-util/src/main/java/org/vcell/util/trees/GenericStringTree.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
121
vcell-util/src/main/java/org/vcell/util/trees/GenericTree.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
53
vcell-util/src/test/java/org/vcell/util/GenericTreeTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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")); | ||
} | ||
} |