Merge branch release/0.3.0 into master

Return optionals rather than nulls
This commit is contained in:
Paul Campbell 2016-05-26 13:52:43 +01:00
commit 55981778b7
5 changed files with 124 additions and 96 deletions

View file

@ -1,6 +1,11 @@
CHANGELOG CHANGELOG
========= =========
0.3.0
------
* Return optionals rather than nulls
0.2.0 0.2.0
------ ------

View file

@ -2,7 +2,7 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>node</artifactId> <artifactId>node</artifactId>
<version>0.2.0</version> <version>0.3.0</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>Node</name> <name>Node</name>

View file

@ -32,9 +32,10 @@ public interface Node<T> {
/** /**
* Fetch the data held within the node. * Fetch the data held within the node.
* *
* @return the node's data * @return an Optional containing the node's data, or empty if the node has
* none
*/ */
T getData(); Optional<T> getData();
/** /**
* Set the data held within the node. * Set the data held within the node.
@ -52,13 +53,10 @@ public interface Node<T> {
/** /**
* Fetch the parent node. * Fetch the parent node.
* <p>
* If the node is a root node, i.e. has no parent, then this will return
* null.
* *
* @return the parent node * @return an Optional contain the parent node, or empty if a root node
*/ */
Node<T> getParent(); Optional<Node<T>> getParent();
/** /**
* Make the current node a direct child of the parent. * Make the current node a direct child of the parent.
@ -182,6 +180,8 @@ public interface Node<T> {
* @param name the name of the child * @param name the name of the child
* *
* @return the node * @return the node
*
* @throws NodeException if the node is not found
*/ */
Node<T> getChildByName(String name); Node<T> getChildByName(String name);

View file

@ -124,8 +124,8 @@ public class NodeItem<T> implements Node<T> {
} }
@Override @Override
public T getData() { public Optional<T> getData() {
return data; return Optional.ofNullable(data);
} }
@Override @Override
@ -139,8 +139,8 @@ public class NodeItem<T> implements Node<T> {
} }
@Override @Override
public Node<T> getParent() { public Optional<Node<T>> getParent() {
return parent; return Optional.ofNullable(parent);
} }
@Override @Override
@ -170,7 +170,12 @@ public class NodeItem<T> implements Node<T> {
} }
} }
children.add(child); children.add(child);
if (child.getParent() == null || !child.getParent().equals(this)) { // update the child's parent if they don't have one or it is not this
Optional<Node<T>> childParent = child.getParent();
boolean isOrphan = !childParent.isPresent();
boolean hasDifferentParent = !isOrphan && !childParent.get()
.equals(this);
if (isOrphan || hasDifferentParent) {
child.setParent(this); child.setParent(this);
} }
} }
@ -244,7 +249,8 @@ public class NodeItem<T> implements Node<T> {
throw new NullPointerException("child"); throw new NullPointerException("child");
} }
return children.stream() return children.stream()
.filter((Node<T> t) -> t.getData().equals(child)) .filter(n -> !n.isEmpty())
.filter(n -> n.getData().get().equals(child))
.findAny(); .findAny();
} }
@ -321,8 +327,8 @@ public class NodeItem<T> implements Node<T> {
addChild(nodeItem); addChild(nodeItem);
return; return;
} }
final Optional<Node<T>> childNamed = findChildByName( String nodeName = nodeItem.getName();
nodeItem.getName()); final Optional<Node<T>> childNamed = findChildByName(nodeName);
if (!childNamed.isPresent()) { // nothing with the same name exists if (!childNamed.isPresent()) { // nothing with the same name exists
addChild(nodeItem); addChild(nodeItem);
return; return;
@ -330,10 +336,10 @@ public class NodeItem<T> implements Node<T> {
// we have an existing node with the same name // we have an existing node with the same name
final Node<T> existing = childNamed.get(); final Node<T> existing = childNamed.get();
if (!existing.isEmpty()) { if (!existing.isEmpty()) {
throw new NodeException( throw new NodeException("A non-empty node named '" + nodeName
"A non-empty node with that name already exists here"); + "' already exists here");
} else { } else {
existing.setData(nodeItem.getData()); nodeItem.getData().ifPresent(existing::setData);
} }
return; return;
} }

View file

@ -36,7 +36,7 @@ public class NodeItemTest {
node = new NodeItem<>(data); node = new NodeItem<>(data);
//then //then
assertThat(node.getData()).as("can get the data from a node"). assertThat(node.getData()).as("can get the data from a node").
isSameAs(data); contains(data);
} }
@Test @Test
@ -55,9 +55,10 @@ public class NodeItemTest {
//given //given
node = new NodeItem<>(null, n -> "root name supplier"); node = new NodeItem<>(null, n -> "root name supplier");
//when //when
val child = new NodeItem<>(null, n -> "overridden", node); val child = new NodeItem<String>(null, n -> "overridden", node);
//then //then
assertThat(child.getName()).isEqualTo("overridden"); assertThat(child.getName()).isEqualTo("overridden");
assertThat(child.getParent()).contains(node);
} }
@Test @Test
@ -76,10 +77,10 @@ public class NodeItemTest {
@Test @Test
public void shouldHaveNullForDefaultParent() { public void shouldHaveNullForDefaultParent() {
//given //given
node = new NodeItem<>("data", Node::getData); node = new NodeItem<>("data");
//then //then
assertThat(node.getParent()).as( assertThat(node.getParent()).as(
"node created without a parent has null as parent").isNull(); "node created without a parent has no parent").isEmpty();
} }
/** /**
@ -88,13 +89,13 @@ public class NodeItemTest {
@Test @Test
public void shouldReturnNodeParent() { public void shouldReturnNodeParent() {
//given //given
val parent = new NodeItem<String>("parent", Node::getData); val parent = new NodeItem<String>("parent");
//when //when
node = new NodeItem<>("subject", parent); node = new NodeItem<>("subject", parent);
//then //then
assertThat(node.getParent()).as( assertThat(node.getParent()).as(
"node created with a parent can return the parent") "node created with a parent can return the parent")
.isSameAs(parent); .contains(parent);
} }
/** /**
@ -104,7 +105,7 @@ public class NodeItemTest {
@Test @Test
public void setParentShouldThrowNodeExceptionWhenParentIsAChild() { public void setParentShouldThrowNodeExceptionWhenParentIsAChild() {
//given //given
node = new NodeItem<>("subject", Node::getData); node = new NodeItem<>("subject");
val child = new NodeItem<String>("child", node); val child = new NodeItem<String>("child", node);
exception.expect(NodeException.class); exception.expect(NodeException.class);
exception.expectMessage("Parent is a descendant"); exception.expectMessage("Parent is a descendant");
@ -120,7 +121,7 @@ public class NodeItemTest {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public void shouldAddNewNodeAsChildToParent() { public void shouldAddNewNodeAsChildToParent() {
//given //given
val parent = new NodeItem<String>("parent", Node::getData); val parent = new NodeItem<String>("parent");
//when //when
node = new NodeItem<>("subject", parent); node = new NodeItem<>("subject", parent);
//then //then
@ -135,14 +136,14 @@ public class NodeItemTest {
@Test @Test
public void shouldReturnSetParent() { public void shouldReturnSetParent() {
//given //given
node = new NodeItem<>("subject", Node::getData); node = new NodeItem<>("subject");
val parent = new NodeItem<String>("parent", Node::getData); val parent = new NodeItem<String>("parent");
//when //when
node.setParent(parent); node.setParent(parent);
//then //then
assertThat(node.getParent()).as( assertThat(node.getParent()).as(
"when a node is assigned a new parent that parent can be " "when a node is assigned a new parent that parent can be "
+ "returned").isSameAs(parent); + "returned").contains(parent);
} }
/** /**
@ -151,7 +152,7 @@ public class NodeItemTest {
@Test @Test
public void shouldThrowNPEWhenSetParentNull() { public void shouldThrowNPEWhenSetParentNull() {
//given //given
node = new NodeItem<>("subject", Node::getData); node = new NodeItem<>("subject");
exception.expect(NullPointerException.class); exception.expect(NullPointerException.class);
exception.expectMessage("parent"); exception.expectMessage("parent");
//when //when
@ -165,7 +166,7 @@ public class NodeItemTest {
@Test @Test
public void setParentShouldThrowNodeExceptionWhenParentIsSelf() { public void setParentShouldThrowNodeExceptionWhenParentIsSelf() {
//given //given
node = new NodeItem<>("subject", Node::getData); node = new NodeItem<>("subject");
exception.expect(NodeException.class); exception.expect(NodeException.class);
exception.expectMessage("Parent is a descendant"); exception.expectMessage("Parent is a descendant");
//when //when
@ -179,15 +180,15 @@ public class NodeItemTest {
@Test @Test
public void shouldUpdateOldParentWhenNodeSetToNewParent() { public void shouldUpdateOldParentWhenNodeSetToNewParent() {
//given //given
node = new NodeItem<>("subject", Node::getData); node = new NodeItem<>("subject");
val child = node.createChild("child"); val child = node.createChild("child");
val newParent = new NodeItem<String>("newParent", Node::getData); val newParent = new NodeItem<String>("newParent");
//when //when
child.setParent(newParent); child.setParent(newParent);
//then //then
assertThat(child.getParent()).as( assertThat(child.getParent()).as(
"when a node is assigned a new parent, the old parent is " "when a node is assigned a new parent, the old parent is "
+ "replaced").isSameAs(newParent); + "replaced").contains(newParent);
assertThat(node.findChild("child").isPresent()).as( assertThat(node.findChild("child").isPresent()).as(
"when a node is assigned a new parent, the old parent no " "when a node is assigned a new parent, the old parent no "
+ "longer has the node among it's children").isFalse(); + "longer has the node among it's children").isFalse();
@ -200,16 +201,16 @@ public class NodeItemTest {
@Test @Test
public void shouldRemoveNodeFromOldParentWhenAddedAsChildToNewParent() { public void shouldRemoveNodeFromOldParentWhenAddedAsChildToNewParent() {
//given //given
node = new NodeItem<>("subject", Node::getData); node = new NodeItem<>("subject");
val child = node.createChild("child"); val child = node.createChild("child");
val newParent = new NodeItem<String>("newParent", Node::getData); val newParent = new NodeItem<String>("newParent");
//when //when
newParent.addChild(child); newParent.addChild(child);
//then //then
assertThat(child.getParent()).as( assertThat(child.getParent()).as(
"when a node with an existing parent is added as a child " "when a node with an existing parent is added as a child "
+ "to another node, then the old parent is replaced") + "to another node, then the old parent is replaced")
.isSameAs(newParent); .contains(newParent);
assertThat(node.findChild("child").isPresent()).as( assertThat(node.findChild("child").isPresent()).as(
"when a node with an existing parent is added as a child to " "when a node with an existing parent is added as a child to "
+ "another node, then the old parent no longer has " + "another node, then the old parent no longer has "
@ -222,7 +223,7 @@ public class NodeItemTest {
@Test @Test
public void shouldThrowNPEWhenAddingNullAsChild() { public void shouldThrowNPEWhenAddingNullAsChild() {
//given //given
node = new NodeItem<>("subject", Node::getData); node = new NodeItem<>("subject");
exception.expect(NullPointerException.class); exception.expect(NullPointerException.class);
exception.expectMessage("child"); exception.expectMessage("child");
//when //when
@ -236,8 +237,8 @@ public class NodeItemTest {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public void shouldReturnAddedChild() { public void shouldReturnAddedChild() {
//given //given
node = new NodeItem<>("subject", Node::getData); node = new NodeItem<>("subject");
val child = new NodeItem<String>("child", Node::getData); val child = new NodeItem<String>("child");
//when //when
node.addChild(child); node.addChild(child);
//then //then
@ -252,7 +253,7 @@ public class NodeItemTest {
@Test @Test
public void addChildShouldThrowNodeExceptionWhenAddingANodeAsOwnChild() { public void addChildShouldThrowNodeExceptionWhenAddingANodeAsOwnChild() {
//given //given
node = new NodeItem<>("subject", Node::getData); node = new NodeItem<>("subject");
exception.expect(NodeException.class); exception.expect(NodeException.class);
exception.expectMessage("Child is an ancestor"); exception.expectMessage("Child is an ancestor");
//then //then
@ -265,7 +266,7 @@ public class NodeItemTest {
@Test @Test
public void addChildShouldThrowNodeExceptionWhenAddingSelfAsChild() { public void addChildShouldThrowNodeExceptionWhenAddingSelfAsChild() {
//given //given
node = new NodeItem<>("subject", Node::getData); node = new NodeItem<>("subject");
exception.expect(NodeException.class); exception.expect(NodeException.class);
exception.expectMessage("Child is an ancestor"); exception.expectMessage("Child is an ancestor");
//when //when
@ -279,7 +280,7 @@ public class NodeItemTest {
@Test @Test
public void addChildShouldThrowNodeExceptionWhenChildIsParent() { public void addChildShouldThrowNodeExceptionWhenChildIsParent() {
//given //given
val parent = new NodeItem<String>("parent", Node::getData); val parent = new NodeItem<String>("parent");
node = new NodeItem<>("subject", parent); node = new NodeItem<>("subject", parent);
exception.expect(NodeException.class); exception.expect(NodeException.class);
exception.expectMessage("Child is an ancestor"); exception.expectMessage("Child is an ancestor");
@ -294,7 +295,7 @@ public class NodeItemTest {
@Test @Test
public void addChildShouldThrowNodeExceptionWhenAddingGrandParentAsChild() { public void addChildShouldThrowNodeExceptionWhenAddingGrandParentAsChild() {
//given //given
val grandParent = new NodeItem<String>("grandparent", Node::getData); val grandParent = new NodeItem<String>("grandparent");
val parent = new NodeItem<String>("parent", grandParent); val parent = new NodeItem<String>("parent", grandParent);
node = new NodeItem<>("subject", parent); node = new NodeItem<>("subject", parent);
exception.expect(NodeException.class); exception.expect(NodeException.class);
@ -309,14 +310,14 @@ public class NodeItemTest {
@Test @Test
public void shouldSetParentOnChildWhenAddedAsChild() { public void shouldSetParentOnChildWhenAddedAsChild() {
//given //given
val child = new NodeItem<String>("child", Node::getData); node = new NodeItem<>("subject");
node = new NodeItem<>("subject", Node::getData); val child = new NodeItem<String>("child");
//when //when
node.addChild(child); node.addChild(child);
//then //then
assertThat(child.getParent()).as( assertThat(child.getParent()).as(
"when a node is added as a child, the child has the node as " "when a node is added as a child, the child has the node as "
+ "its parent").isSameAs(node); + "its parent").contains(node);
} }
/** /**
@ -326,7 +327,7 @@ public class NodeItemTest {
public void shouldWalkTreeToNode() { public void shouldWalkTreeToNode() {
//given //given
val grandparent = "grandparent"; val grandparent = "grandparent";
val grandParentNode = new NodeItem<String>(grandparent, Node::getData); val grandParentNode = new NodeItem<String>(grandparent);
val parent = "parent"; val parent = "parent";
val parentNode = new NodeItem<String>(parent, grandParentNode); val parentNode = new NodeItem<String>(parent, grandParentNode);
val subject = "subject"; val subject = "subject";
@ -351,7 +352,7 @@ public class NodeItemTest {
public void shouldNotFindNonExistentChildNode() { public void shouldNotFindNonExistentChildNode() {
//given //given
val parent = "parent"; val parent = "parent";
val parentNode = new NodeItem<String>(parent, Node::getData); val parentNode = new NodeItem<String>(parent);
val subject = "subject"; val subject = "subject";
node = new NodeItem<>(subject, parentNode); node = new NodeItem<>(subject, parentNode);
//when //when
@ -368,7 +369,7 @@ public class NodeItemTest {
@Test @Test
public void shouldThrowNEWhenWalkTreeNull() { public void shouldThrowNEWhenWalkTreeNull() {
//given //given
node = new NodeItem<>("subject", Node::getData); node = new NodeItem<>("subject");
exception.expect(NullPointerException.class); exception.expect(NullPointerException.class);
exception.expectMessage("path"); exception.expectMessage("path");
//when //when
@ -382,7 +383,7 @@ public class NodeItemTest {
@Test @Test
public void shouldReturnEmptyForEmptyWalkTreePath() { public void shouldReturnEmptyForEmptyWalkTreePath() {
//given //given
node = new NodeItem<>("subject", Node::getData); node = new NodeItem<>("subject");
//when //when
val result = node.findInPath(Collections.emptyList()); val result = node.findInPath(Collections.emptyList());
//then //then
@ -395,7 +396,7 @@ public class NodeItemTest {
@Test @Test
public void shouldCreateDescendantNodes() { public void shouldCreateDescendantNodes() {
//given //given
node = new NodeItem<>("subject", Node::getData); node = new NodeItem<>("subject");
val alphaData = "alpha"; val alphaData = "alpha";
val betaData = "beta"; val betaData = "beta";
val gammaData = "gamma"; val gammaData = "gamma";
@ -411,7 +412,7 @@ public class NodeItemTest {
val alpha = alphaOptional.get(); val alpha = alphaOptional.get();
assertThat(alpha.getParent()).as( assertThat(alpha.getParent()).as(
"when creating a descendant line, the first element has " "when creating a descendant line, the first element has "
+ "the current node as its parent").isSameAs(node); + "the current node as its parent").contains(node);
val betaOptional = alpha.findChild(betaData); val betaOptional = alpha.findChild(betaData);
assertThat(betaOptional.isPresent()).as( assertThat(betaOptional.isPresent()).as(
"when creating a descendant line, the second element is " "when creating a descendant line, the second element is "
@ -421,7 +422,7 @@ public class NodeItemTest {
assertThat(beta.getParent()).as( assertThat(beta.getParent()).as(
"when creating a descendant line, the second element " "when creating a descendant line, the second element "
+ "has the first as its parent") + "has the first as its parent")
.isSameAs(alpha); .contains(alpha);
val gammaOptional = beta.findChild(gammaData); val gammaOptional = beta.findChild(gammaData);
assertThat(gammaOptional.isPresent()).as( assertThat(gammaOptional.isPresent()).as(
"when creating a descendant line, the third element " "when creating a descendant line, the third element "
@ -431,7 +432,7 @@ public class NodeItemTest {
assertThat(gamma.getParent()).as( assertThat(gamma.getParent()).as(
"when creating a descendant line, the third " "when creating a descendant line, the third "
+ "element has the second as its parent") + "element has the second as its parent")
.isSameAs(beta); .contains(beta);
} }
} }
} }
@ -444,7 +445,7 @@ public class NodeItemTest {
@Test @Test
public void createDescendantLineShouldThrowNPEWhenDescendantsAreNull() { public void createDescendantLineShouldThrowNPEWhenDescendantsAreNull() {
//given //given
node = new NodeItem<>("subject", Node::getData); node = new NodeItem<>("subject");
exception.expect(NullPointerException.class); exception.expect(NullPointerException.class);
exception.expectMessage("descendants"); exception.expectMessage("descendants");
//when //when
@ -457,7 +458,7 @@ public class NodeItemTest {
@Test @Test
public void shouldChangeNothingWhenCreateDescendantEmpty() { public void shouldChangeNothingWhenCreateDescendantEmpty() {
//given //given
node = new NodeItem<>("subject", Node::getData); node = new NodeItem<>("subject");
//when //when
node.createDescendantLine(Collections.emptyList()); node.createDescendantLine(Collections.emptyList());
//then //then
@ -472,7 +473,7 @@ public class NodeItemTest {
@Test @Test
public void shouldFindExistingChildNode() { public void shouldFindExistingChildNode() {
//given //given
node = new NodeItem<>("subject", Node::getData); node = new NodeItem<>("subject");
val childData = "child"; val childData = "child";
val child = new NodeItem<String>(childData, node); val child = new NodeItem<String>(childData, node);
//when //when
@ -489,14 +490,14 @@ public class NodeItemTest {
@Test @Test
public void shouldFindCreateNewChildNode() { public void shouldFindCreateNewChildNode() {
//given //given
node = new NodeItem<>("subject", Node::getData); node = new NodeItem<>("subject");
val childData = "child"; val childData = "child";
//when //when
val found = node.findOrCreateChild(childData); val found = node.findOrCreateChild(childData);
//then //then
assertThat(found.getData()).as( assertThat(found.getData()).as(
"when searching for a child by data, a new node is created") "when searching for a non-existent child by data, a new node "
.isSameAs(childData); + "is created").contains(childData);
} }
/** /**
@ -505,7 +506,7 @@ public class NodeItemTest {
@Test @Test
public void findOrCreateChildShouldThrowNPEFWhenChildIsNull() { public void findOrCreateChildShouldThrowNPEFWhenChildIsNull() {
//given //given
node = new NodeItem<>("subject", Node::getData); node = new NodeItem<>("subject");
exception.expect(NullPointerException.class); exception.expect(NullPointerException.class);
exception.expectMessage("child"); exception.expectMessage("child");
//when //when
@ -518,9 +519,9 @@ public class NodeItemTest {
@Test @Test
public void shouldGetChild() { public void shouldGetChild() {
//given //given
node = new NodeItem<>("subject", Node::getData); node = new NodeItem<>("subject");
val childData = "child"; val childData = "child";
val child = new NodeItem<String>(childData, Node::getData); val child = new NodeItem<String>(childData);
node.addChild(child); node.addChild(child);
//when //when
val found = node.findChild(childData); val found = node.findChild(childData);
@ -540,7 +541,7 @@ public class NodeItemTest {
@Test @Test
public void getChildShouldThrowNPEWhenThereIsNoChild() { public void getChildShouldThrowNPEWhenThereIsNoChild() {
//given //given
node = new NodeItem<>("data", Node::getData); node = new NodeItem<>("data");
exception.expect(NullPointerException.class); exception.expect(NullPointerException.class);
exception.expectMessage("child"); exception.expectMessage("child");
//when //when
@ -554,14 +555,14 @@ public class NodeItemTest {
@Test @Test
public void shouldCreateChild() { public void shouldCreateChild() {
//given //given
node = new NodeItem<>("subject", Node::getData); node = new NodeItem<>("subject");
val childData = "child"; val childData = "child";
//when //when
val child = node.createChild(childData); val child = node.createChild(childData);
//then //then
assertThat(child.getParent()).as( assertThat(child.getParent()).as(
"when creating a child node, the child has the current node " "when creating a child node, the child has the current node "
+ "as its parent").isSameAs(node); + "as its parent").contains(node);
val foundChild = node.findChild(childData); val foundChild = node.findChild(childData);
assertThat(foundChild.isPresent()).as( assertThat(foundChild.isPresent()).as(
"when creating a child node, the child can be found by its " "when creating a child node, the child can be found by its "
@ -579,7 +580,7 @@ public class NodeItemTest {
@Test @Test
public void createChildShouldThrowNPEWhenChildIsNull() { public void createChildShouldThrowNPEWhenChildIsNull() {
//given //given
node = new NodeItem<>("subject", Node::getData); node = new NodeItem<>("subject");
exception.expect(NullPointerException.class); exception.expect(NullPointerException.class);
exception.expectMessage("child"); exception.expectMessage("child");
//when //when
@ -589,7 +590,7 @@ public class NodeItemTest {
@Test @Test
public void getNameShouldBeCorrect() { public void getNameShouldBeCorrect() {
//given //given
node = new NodeItem<>("subject", Node::getData); node = new NodeItem<>("subject", n -> n.getData().get());
//then //then
assertThat(node.getName()).isEqualTo("subject"); assertThat(node.getName()).isEqualTo("subject");
} }
@ -597,7 +598,7 @@ public class NodeItemTest {
@Test @Test
public void getNameShouldUseParentNameSupplier() { public void getNameShouldUseParentNameSupplier() {
//given //given
val root = new NodeItem<String>("root", Node::getData); val root = new NodeItem<String>("root", n -> n.getData().get());
node = new NodeItem<>("child", root); node = new NodeItem<>("child", root);
//then //then
assertThat(node.getName()).isEqualTo("child"); assertThat(node.getName()).isEqualTo("child");
@ -605,15 +606,20 @@ public class NodeItemTest {
@Test @Test
public void getNameShouldReturnNameForNonStringData() { public void getNameShouldReturnNameForNonStringData() {
val root = new NodeItem<LocalDate>(LocalDate.parse("2016-05-23"), val root = new NodeItem<LocalDate>(LocalDate.parse("2016-05-23"), n -> {
n -> n.getData().format(DateTimeFormatter.BASIC_ISO_DATE)); if (n.isEmpty()) {
return null;
}
return n.getData().get().format(DateTimeFormatter.BASIC_ISO_DATE);
});
//then //then
assertThat(root.getName()).isEqualTo("20160523"); assertThat(root.getName()).isEqualTo("20160523");
} }
@Test @Test
public void getNameShouldUseClosestNameSupplier() { public void getNameShouldUseClosestNameSupplier() {
node = new NodeItem<>("root", Node::getData); node = new NodeItem<>("root", n -> n.getData().get());
val child = new NodeItem<String>("child", Object::toString); val child = new NodeItem<String>("child", Object::toString);
node.addChild(child); node.addChild(child);
val grandChild = new NodeItem<>("grandchild", child); val grandChild = new NodeItem<>("grandchild", child);
@ -635,13 +641,13 @@ public class NodeItemTest {
@Test @Test
public void canCreateRootNodeWithoutData() { public void canCreateRootNodeWithoutData() {
node = new NodeItem<>(null, "empty"); node = new NodeItem<>(null, "empty");
assertThat(node.getData()).isNull(); assertThat(node.getData()).isEmpty();
} }
@Test @Test
public void canCreateRootNodeWithoutDataButWithNameSupplier() { public void canCreateRootNodeWithoutDataButWithNameSupplier() {
node = new NodeItem<>(null, Node::getData); node = new NodeItem<>(null);
assertThat(node.getData()).isNull(); assertThat(node.getData()).isEmpty();
} }
@Test @Test
@ -693,14 +699,14 @@ public class NodeItemTest {
//when //when
node.insertInPath(four, "one", "two", "three"); node.insertInPath(four, "one", "two", "three");
//then //then
val three = four.getParent(); val three = four.getParent().get();
assertThat(four.getParent()).as("add node to a tree").isNotNull(); assertThat(four.getParent()).as("add node to a tree").isNotNull();
assertThat(three.getName()).isEqualTo("three"); assertThat(three.getName()).isEqualTo("three");
val two = three.getParent(); val two = three.getParent().get();
assertThat(two.getName()).isEqualTo("two"); assertThat(two.getName()).isEqualTo("two");
val one = two.getParent(); val one = two.getParent().get();
assertThat(one.getName()).isEqualTo("one"); assertThat(one.getName()).isEqualTo("one");
assertThat(one.getParent()).isSameAs(node); assertThat(one.getParent().get()).isSameAs(node);
assertThat(node.getChildByName("one") assertThat(node.getChildByName("one")
.getChildByName("two") .getChildByName("two")
.getChildByName("three") .getChildByName("three")
@ -736,7 +742,7 @@ public class NodeItemTest {
node.insertInPath(child); node.insertInPath(child);
//then //then
assertThat(node.getChildByName("child").getData()).as("data in tree") assertThat(node.getChildByName("child").getData()).as("data in tree")
.isSameAs( .contains(
"child data"); "child data");
assertThat( assertThat(
node.getChildByName("child").getChildByName("grandchild")).as( node.getChildByName("child").getChildByName("grandchild")).as(
@ -755,11 +761,12 @@ public class NodeItemTest {
public void removingParentFromNodeWithParentRemovesParent() { public void removingParentFromNodeWithParentRemovesParent() {
//given //given
node = new NodeItem<>(null); node = new NodeItem<>(null);
NodeItem<String> child = new NodeItem<>(null, node); val child = new NodeItem<String>(null, node);
//when //when
child.removeParent(); child.removeParent();
//then //then
assertThat(child.getParent()).isNull(); assertThat(child.getParent()).isEmpty();
assertThat(node.getChildren()).isEmpty();
} }
@Test @Test
@ -767,7 +774,7 @@ public class NodeItemTest {
//given //given
exception.expect(NodeException.class); exception.expect(NodeException.class);
exception.expectMessage( exception.expectMessage(
"A non-empty node with that name already exists here"); "A non-empty node named 'grandchild' already exists here");
node = new NodeItem<>(null); node = new NodeItem<>(null);
val child = new NodeItem<String>(null, "child", node); val child = new NodeItem<String>(null, "child", node);
new NodeItem<>("data", "grandchild", child); new NodeItem<>("data", "grandchild", child);
@ -800,7 +807,7 @@ public class NodeItemTest {
node.addChild(child); node.addChild(child);
child.addChild(target); child.addChild(target);
final NodeItem<String> addMe = new NodeItem<>("I'm new", "target"); final NodeItem<String> addMe = new NodeItem<>("I'm new", "target");
assertThat(addMe.getParent()).isNull(); assertThat(addMe.getParent()).isEmpty();
assertThat(child.getChildByName("target").isEmpty()).as( assertThat(child.getChildByName("target").isEmpty()).as(
"target starts empty").isTrue(); "target starts empty").isTrue();
//when //when
@ -808,7 +815,7 @@ public class NodeItemTest {
node.insertInPath(addMe, "child"); node.insertInPath(addMe, "child");
//then //then
assertThat(child.getChildByName("target").getData()).as( assertThat(child.getChildByName("target").getData()).as(
"target now contains data").isEqualTo("I'm new"); "target now contains data").contains("I'm new");
} }
@Test @Test
@ -850,7 +857,7 @@ public class NodeItemTest {
// once a node has it's parent removed it should provide a default name // once a node has it's parent removed it should provide a default name
// provider // provider
//given //given
node = new NodeItem<>("data", Node::getData); // name provider: getData node = new NodeItem<>("data", n -> n.getData().get());
final NodeItem<String> child = new NodeItem<>("other", node); final NodeItem<String> child = new NodeItem<>("other", node);
assertThat(node.getName()).as("initial root name").isEqualTo("data"); assertThat(node.getName()).as("initial root name").isEqualTo("data");
assertThat(child.getName()).as("initial child name").isEqualTo("other"); assertThat(child.getName()).as("initial child name").isEqualTo("other");
@ -872,6 +879,7 @@ public class NodeItemTest {
node.removeChild(child); node.removeChild(child);
//then //then
assertThat(node.getChildren()).isEmpty(); assertThat(node.getChildren()).isEmpty();
assertThat(child.getParent()).isEmpty();
} }
@Test @Test
@ -903,7 +911,7 @@ public class NodeItemTest {
//when //when
node.setData("updated"); node.setData("updated");
//then //then
assertThat(node.getData()).isEqualTo("updated"); assertThat(node.getData()).contains("updated");
} }
@Test @Test
@ -915,7 +923,7 @@ public class NodeItemTest {
Node<String> child = node.createChild("child data", "child name"); Node<String> child = node.createChild("child data", "child name");
//then //then
assertThat(child.getName()).isEqualTo("child name"); assertThat(child.getName()).isEqualTo("child name");
assertThat(child.getParent()).isSameAs(node); assertThat(child.getParent()).contains(node);
assertThat(node.getChildren()).containsExactly(child); assertThat(node.getChildren()).containsExactly(child);
} }
@ -946,9 +954,9 @@ public class NodeItemTest {
//given //given
node = new NodeItem<>(null); node = new NodeItem<>(null);
//when //when
NodeItem<String> child = new NodeItem<>(null, Node::getData, node); NodeItem<String> child = new NodeItem<>(null, node);
//then //then
assertThat(child.getParent()).isSameAs(node); assertThat(child.getParent()).contains(node);
assertThat(node.getChildren()).containsExactly(child); assertThat(node.getChildren()).containsExactly(child);
} }
@ -1019,11 +1027,11 @@ public class NodeItemTest {
public void canUseNameSupplierToBuildFullPath() { public void canUseNameSupplierToBuildFullPath() {
//given //given
final Function<Node<String>, String> pathNameSupplier = node -> { final Function<Node<String>, String> pathNameSupplier = node -> {
Node<String> parent = node.getParent(); Optional<Node<String>> parent = node.getParent();
if (parent == null) { if (parent.isPresent()) {
return ""; return parent.get().getName() + "/" + node.getData().get();
} }
return parent.getName() + "/" + node.getData(); return "";
}; };
node = new NodeItem<>(null, pathNameSupplier); node = new NodeItem<>(null, pathNameSupplier);
val child = new NodeItem<String>("child", node); val child = new NodeItem<String>("child", node);
@ -1031,4 +1039,13 @@ public class NodeItemTest {
//then //then
assertThat(grandchild.getName()).isEqualTo("/child/grandchild"); assertThat(grandchild.getName()).isEqualTo("/child/grandchild");
} }
@Test
public void canSafelyHandleFindChildWhenAChildHasNoData() {
//given
node = new NodeItem<>(null);
new NodeItem<>(null, node);
//when
node.findChild("data");
}
} }