Merge pull request #5 from kemitix/tidy-interface

Tidy interface
This commit is contained in:
Paul Campbell 2016-05-24 15:22:29 +01:00
commit c0bbadf119
3 changed files with 156 additions and 61 deletions

View file

@ -34,6 +34,13 @@ public interface Node<T> {
*/
T getData();
/**
* Set the data held within the node.
*
* @param data the node's data
*/
void setData(T data);
/**
* Returns true if the node is empty (has no data).
*
@ -56,7 +63,7 @@ public interface Node<T> {
*
* @param parent the new parent node
*/
void setParent(final Node<T> parent);
void setParent(Node<T> parent);
/**
* Fetches the child nodes.
@ -70,16 +77,26 @@ public interface Node<T> {
*
* @param child the node to add
*/
void addChild(final Node<T> child);
void addChild(Node<T> child);
/**
* Creates a new node and adds it as a child of the current node.
* Creates a new unnamed node and adds it as a child of the current node.
*
* @param child the child node's data
*
* @return the new child node
*/
Node<T> createChild(final T child);
Node<T> createChild(T child);
/**
* Creates a new named node and adds it as a child of the current node.
*
* @param child the child node's data
* @param name the name
*
* @return the new child node
*/
Node<T> createChild(T child, String name);
/**
* Populates the tree with the path of nodes, each being a child of the
@ -87,7 +104,7 @@ public interface Node<T> {
*
* @param descendants the line of descendants from the current node
*/
void createDescendantLine(final List<T> descendants);
void createDescendantLine(List<T> descendants);
/**
* Looks for a child node and returns it, creating a new child node if one
@ -97,7 +114,8 @@ public interface Node<T> {
*
* @return the found or created child node
*/
Node<T> findOrCreateChild(final T child);
@Deprecated
Node<T> findOrCreateChild(T child);
/**
* Fetches the node for the child if present.
@ -106,7 +124,18 @@ public interface Node<T> {
*
* @return an {@link Optional} containing the child node if found
*/
Optional<Node<T>> getChild(final T child);
Optional<Node<T>> findChild(T child);
/**
* Fetches the node for the child if present.
*
* @param child the child's data to search for
*
* @return the child node if found
*
* @throws NodeException if the node is not found
*/
Node<T> getChild(T child);
/**
* Checks if the node is an ancestor.
@ -115,7 +144,7 @@ public interface Node<T> {
*
* @return true if the node is an ancestor
*/
boolean isChildOf(final Node<T> node);
boolean isDescendantOf(Node<T> node);
/**
* Walks the node tree using the path to select each child.
@ -124,7 +153,7 @@ public interface Node<T> {
*
* @return the child or null
*/
Optional<Node<T>> walkTree(final List<T> path);
Optional<Node<T>> findInPath(List<T> path);
/**
* Places the node in the tree under by the path. Intervening empty
@ -133,7 +162,7 @@ public interface Node<T> {
* @param node the node to place
* @param path the path to contain the new node
*/
void placeNodeIn(Node<T> node, String... path);
void insertInPath(Node<T> node, String... path);
/**
* Searches for a child with the name given.
@ -142,7 +171,7 @@ public interface Node<T> {
*
* @return an Optional containing the child found or empty
*/
Optional<Node<T>> findChildNamed(String name);
Optional<Node<T>> findChildByName(String name);
/**
* Returns the child with the given name. If one can't be found a
@ -152,7 +181,7 @@ public interface Node<T> {
*
* @return the node
*/
Node<T> getChildNamed(String name);
Node<T> getChildByName(String name);
/**
* Draw a representation of the tree.

View file

@ -16,7 +16,7 @@ import java.util.function.Function;
*/
public class NodeItem<T> implements Node<T> {
private final T data;
private T data;
private final Set<Node<T>> children = new HashSet<>();
@ -127,6 +127,11 @@ public class NodeItem<T> implements Node<T> {
return data;
}
@Override
public void setData(final T data) {
this.data = data;
}
@Override
public boolean isEmpty() {
return data == null;
@ -152,11 +157,11 @@ public class NodeItem<T> implements Node<T> {
if (child == null) {
throw new NullPointerException("child");
}
if (this.equals(child) || isChildOf(child)) {
if (this.equals(child) || isDescendantOf(child)) {
throw new NodeException("Child is an ancestor");
}
if (child.isNamed()) {
final Optional<Node<T>> existingChild = findChildNamed(
final Optional<Node<T>> existingChild = findChildByName(
child.getName());
if (existingChild.isPresent() && existingChild.get() != child) {
throw new NodeException(
@ -184,6 +189,14 @@ public class NodeItem<T> implements Node<T> {
return new NodeItem<>(child, this);
}
@Override
@SuppressWarnings("hiddenfield")
public Node<T> createChild(final T child, final String name) {
Node<T> node = createChild(child);
node.setName(name);
return node;
}
/**
* Populates the tree with the path of nodes, each being a child of the
* previous node in the path.
@ -214,7 +227,7 @@ public class NodeItem<T> implements Node<T> {
if (child == null) {
throw new NullPointerException("child");
}
return getChild(child).orElseGet(() -> createChild(child));
return findChild(child).orElseGet(() -> createChild(child));
}
/**
@ -225,7 +238,7 @@ public class NodeItem<T> implements Node<T> {
* @return an {@link Optional} containing the child node if found
*/
@Override
public Optional<Node<T>> getChild(final T child) {
public Optional<Node<T>> findChild(final T child) {
if (child == null) {
throw new NullPointerException("child");
}
@ -234,6 +247,15 @@ public class NodeItem<T> implements Node<T> {
.findAny();
}
@Override
public Node<T> getChild(final T child) {
Optional<Node<T>> optional = findChild(child);
if (optional.isPresent()) {
return optional.get();
}
throw new NodeException("Child not found");
}
/**
* Checks if the node is an ancestor.
*
@ -242,8 +264,8 @@ public class NodeItem<T> implements Node<T> {
* @return true if the node is an ancestor
*/
@Override
public boolean isChildOf(final Node<T> node) {
return parent != null && (node.equals(parent) || parent.isChildOf(
public boolean isDescendantOf(final Node<T> node) {
return parent != null && (node.equals(parent) || parent.isDescendantOf(
node));
}
@ -257,7 +279,7 @@ public class NodeItem<T> implements Node<T> {
if (parent == null) {
throw new NullPointerException("parent");
}
if (this.equals(parent) || parent.isChildOf(this)) {
if (this.equals(parent) || parent.isDescendantOf(this)) {
throw new NodeException("Parent is a descendant");
}
if (this.parent != null) {
@ -275,15 +297,15 @@ public class NodeItem<T> implements Node<T> {
* @return the child or null
*/
@Override
public Optional<Node<T>> walkTree(final List<T> path) {
public Optional<Node<T>> findInPath(final List<T> path) {
if (path == null) {
throw new NullPointerException("path");
}
if (path.size() > 0) {
Optional<Node<T>> found = getChild(path.get(0));
Optional<Node<T>> found = findChild(path.get(0));
if (found.isPresent()) {
if (path.size() > 1) {
return found.get().walkTree(path.subList(1, path.size()));
return found.get().findInPath(path.subList(1, path.size()));
}
return found;
}
@ -292,13 +314,13 @@ public class NodeItem<T> implements Node<T> {
}
@Override
public void placeNodeIn(final Node<T> nodeItem, final String... path) {
public void insertInPath(final Node<T> nodeItem, final String... path) {
if (path.length == 0) {
if (!nodeItem.isNamed()) { // nothing to conflict with
addChild(nodeItem);
return;
}
final Optional<Node<T>> childNamed = findChildNamed(
final Optional<Node<T>> childNamed = findChildByName(
nodeItem.getName());
if (!childNamed.isPresent()) { // nothing with the same name exists
addChild(nodeItem);
@ -317,18 +339,18 @@ public class NodeItem<T> implements Node<T> {
return;
}
String item = path[0];
final Optional<Node<T>> childNamed = findChildNamed(item);
final Optional<Node<T>> childNamed = findChildByName(item);
Node<T> child;
if (!childNamed.isPresent()) {
child = new NodeItem<>(null, item, this);
} else {
child = childNamed.get();
}
child.placeNodeIn(nodeItem, Arrays.copyOfRange(path, 1, path.length));
child.insertInPath(nodeItem, Arrays.copyOfRange(path, 1, path.length));
}
@Override
public Optional<Node<T>> findChildNamed(final String named) {
public Optional<Node<T>> findChildByName(final String named) {
if (named == null) {
throw new NullPointerException("name");
}
@ -338,8 +360,8 @@ public class NodeItem<T> implements Node<T> {
}
@Override
public Node<T> getChildNamed(final String named) {
final Optional<Node<T>> optional = findChildNamed(named);
public Node<T> getChildByName(final String named) {
final Optional<Node<T>> optional = findChildByName(named);
if (optional.isPresent()) {
return optional.get();
}

View file

@ -186,7 +186,7 @@ public class NodeItemTest {
assertThat(child.getParent()).as(
"when a node is assigned a new parent, the old parent is "
+ "replaced").isSameAs(newParent);
assertThat(node.getChild("child").isPresent()).as(
assertThat(node.findChild("child").isPresent()).as(
"when a node is assigned a new parent, the old parent no "
+ "longer has the node among it's children").isFalse();
}
@ -208,7 +208,7 @@ public class NodeItemTest {
"when a node with an existing parent is added as a child "
+ "to another node, then the old parent is replaced")
.isSameAs(newParent);
assertThat(node.getChild("child").isPresent()).as(
assertThat(node.findChild("child").isPresent()).as(
"when a node with an existing parent is added as a child to "
+ "another node, then the old parent no longer has "
+ "the node among it's children").isFalse();
@ -330,7 +330,7 @@ public class NodeItemTest {
val subject = "subject";
node = new NodeItem<>(subject, parentNode);
//when
val result = grandParentNode.walkTree(Arrays.asList(parent, subject));
val result = grandParentNode.findInPath(Arrays.asList(parent, subject));
//then
assertThat(result.isPresent()).as(
"when we walk the tree to a node it is found").isTrue();
@ -353,7 +353,7 @@ public class NodeItemTest {
val subject = "subject";
node = new NodeItem<>(subject, parentNode);
//when
val result = parentNode.walkTree(Arrays.asList(subject, "no child"));
val result = parentNode.findInPath(Arrays.asList(subject, "no child"));
//then
assertThat(result.isPresent()).as(
"when we walk the tree to a node that doesn't exists, nothing"
@ -370,7 +370,7 @@ public class NodeItemTest {
exception.expect(NullPointerException.class);
exception.expectMessage("path");
//when
node.walkTree(null);
node.findInPath(null);
}
/**
@ -382,7 +382,7 @@ public class NodeItemTest {
//given
node = new NodeItem<>("subject", Node::getData);
//when
val result = node.walkTree(Collections.emptyList());
val result = node.findInPath(Collections.emptyList());
//then
assertThat(result).isEmpty();
}
@ -401,7 +401,7 @@ public class NodeItemTest {
node.createDescendantLine(
Arrays.asList(alphaData, betaData, gammaData));
//then
val alphaOptional = node.getChild(alphaData);
val alphaOptional = node.findChild(alphaData);
assertThat(alphaOptional.isPresent()).as(
"when creating a descendant line, the first element is found")
.isTrue();
@ -410,7 +410,7 @@ public class NodeItemTest {
assertThat(alpha.getParent()).as(
"when creating a descendant line, the first element has "
+ "the current node as its parent").isSameAs(node);
val betaOptional = alpha.getChild(betaData);
val betaOptional = alpha.findChild(betaData);
assertThat(betaOptional.isPresent()).as(
"when creating a descendant line, the second element is "
+ "found").isTrue();
@ -420,7 +420,7 @@ public class NodeItemTest {
"when creating a descendant line, the second element "
+ "has the first as its parent")
.isSameAs(alpha);
val gammaOptional = beta.getChild(gammaData);
val gammaOptional = beta.findChild(gammaData);
assertThat(gammaOptional.isPresent()).as(
"when creating a descendant line, the third element "
+ "is found").isTrue();
@ -521,7 +521,7 @@ public class NodeItemTest {
val child = new NodeItem<String>(childData, Node::getData);
node.addChild(child);
//when
val found = node.getChild(childData);
val found = node.findChild(childData);
//then
assertThat(found.isPresent()).as(
"when retrieving a child by its data, it is found").isTrue();
@ -542,7 +542,7 @@ public class NodeItemTest {
exception.expect(NullPointerException.class);
exception.expectMessage("child");
//when
node.getChild(null);
node.findChild(null);
}
/**
@ -560,7 +560,7 @@ public class NodeItemTest {
assertThat(child.getParent()).as(
"when creating a child node, the child has the current node "
+ "as its parent").isSameAs(node);
val foundChild = node.getChild(childData);
val foundChild = node.findChild(childData);
assertThat(foundChild.isPresent()).as(
"when creating a child node, the child can be found by its "
+ "data").isTrue();
@ -651,7 +651,7 @@ public class NodeItemTest {
node.addChild(alpha);
node.addChild(beta);
//when
val result = node.getChildNamed("alpha");
val result = node.getChildByName("alpha");
//then
assertThat(result).isSameAs(alpha);
}
@ -667,7 +667,7 @@ public class NodeItemTest {
exception.expect(NodeException.class);
exception.expectMessage("Named child not found");
//when
node.getChildNamed("gamma");
node.getChildByName("gamma");
}
@Test
@ -689,7 +689,7 @@ public class NodeItemTest {
node = new NodeItem<>(null, "root"); // create a root
val four = new NodeItem<String>("data", "four");
//when
node.placeNodeIn(four, "one", "two", "three");
node.insertInPath(four, "one", "two", "three");
//then
val three = four.getParent();
assertThat(four.getParent()).as("add node to a tree").isNotNull();
@ -699,10 +699,10 @@ public class NodeItemTest {
val one = two.getParent();
assertThat(one.getName()).isEqualTo("one");
assertThat(one.getParent()).isSameAs(node);
assertThat(node.getChildNamed("one")
.getChildNamed("two")
.getChildNamed("three")
.getChildNamed("four")).isSameAs(four);
assertThat(node.getChildByName("one")
.getChildByName("two")
.getChildByName("three")
.getChildByName("four")).isSameAs(four);
}
@Test
@ -713,11 +713,11 @@ public class NodeItemTest {
val child = new NodeItem<String>("child data", "child");
val grandchild = new NodeItem<String>("grandchild data", "grandchild");
//when
node.placeNodeIn(child); // as root/child
node.placeNodeIn(grandchild, "child"); // as root/child/grandchild
node.insertInPath(child); // as root/child
node.insertInPath(grandchild, "child"); // as root/child/grandchild
//then
assertThat(node.getChildNamed("child")).as("child").isSameAs(child);
assertThat(node.getChildNamed("child").getChildNamed("grandchild")).as(
assertThat(node.getChildByName("child")).as("child").isSameAs(child);
assertThat(node.getChildByName("child").getChildByName("grandchild")).as(
"grandchild").isSameAs(grandchild);
}
@ -729,11 +729,11 @@ public class NodeItemTest {
val child = new NodeItem<String>("child data", "child");
val grandchild = new NodeItem<String>("grandchild data", "grandchild");
//when
node.placeNodeIn(grandchild, "child");
node.placeNodeIn(child);
node.insertInPath(grandchild, "child");
node.insertInPath(child);
//then
assertThat(node.getChildNamed("child")).as("child").isSameAs(child);
assertThat(node.getChildNamed("child").getChildNamed("grandchild")).as(
assertThat(node.getChildByName("child")).as("child").isSameAs(child);
assertThat(node.getChildByName("child").getChildByName("grandchild")).as(
"grandchild").isSameAs(grandchild);
}
@ -758,7 +758,7 @@ public class NodeItemTest {
// only grandchild has data
//when
// attempt to add another node called 'grandchild' to 'child'
node.placeNodeIn(new NodeItem<>("cuckoo", "grandchild"), "child");
node.insertInPath(new NodeItem<>("cuckoo", "grandchild"), "child");
}
@Test
@ -768,7 +768,7 @@ public class NodeItemTest {
node = new NodeItem<>(null);
final Node<String> newNode = new NodeItem<>(null);
//when
node.placeNodeIn(newNode);
node.insertInPath(newNode);
//then
assertThat(node.getChildren()).containsOnly(newNode);
}
@ -786,7 +786,7 @@ public class NodeItemTest {
assertThat(addMe.getParent()).isNull();
//when
// addMe should replace target as the sole descendant of child
node.placeNodeIn(addMe, "child");
node.insertInPath(addMe, "child");
//then
assertThat(child.getChildren()).as("child only contains new node")
.containsOnly(addMe);
@ -801,7 +801,7 @@ public class NodeItemTest {
exception.expectMessage("name");
node = new NodeItem<>(null);
//when
node.findChildNamed(null);
node.findChildByName(null);
}
@Test
@ -878,4 +878,48 @@ public class NodeItemTest {
"[ lucy]");
assertThat(lines).containsSubsequence("[root]", "[ bob]");
}
@Test
public void canChangeNodeData() {
//given
node = new NodeItem<>("initial");
//when
node.setData("updated");
//then
assertThat(node.getData()).isEqualTo("updated");
}
@Test
@SuppressWarnings("unchecked")
public void canCreateNamedChild() {
//given
node = new NodeItem<>(null);
//when
Node<String> child = node.createChild("child data", "child name");
//then
assertThat(child.getName()).isEqualTo("child name");
assertThat(child.getParent()).isSameAs(node);
assertThat(node.getChildren()).containsExactly(child);
}
@Test
public void canGetChildWhenFound() {
//given
node = new NodeItem<>("data");
Node<String> child = new NodeItem<>("child data", "child name", node);
//when
Node<String> found = node.getChild("child data");
//then
assertThat(found).isSameAs(child);
}
@Test
public void canGetChildWhenNotFound() {
//given
exception.expect(NodeException.class);
exception.expectMessage("Child not found");
node = new NodeItem<>("data");
//when
node.getChild("child data");
}
}