-
-
\ No newline at end of file
diff --git a/.idea/encodings.xml b/.idea/encodings.xml
deleted file mode 100644
index c0bce70..0000000
--- a/.idea/encodings.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/findbugs-idea.xml b/.idea/findbugs-idea.xml
deleted file mode 100644
index 9548eb2..0000000
--- a/.idea/findbugs-idea.xml
+++ /dev/null
@@ -1,213 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
deleted file mode 100644
index 31e175c..0000000
--- a/.idea/inspectionProfiles/Project_Default.xml
+++ /dev/null
@@ -1,60 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml
deleted file mode 100644
index 3b31283..0000000
--- a/.idea/inspectionProfiles/profiles_settings.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
deleted file mode 100644
index 935c7b9..0000000
--- a/.idea/modules.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
deleted file mode 100644
index 35eb1dd..0000000
--- a/.idea/vcs.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/node.iml b/node.iml
deleted file mode 100644
index bb89d96..0000000
--- a/node.iml
+++ /dev/null
@@ -1,180 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
From bc5320b5736826e26c396fdcc295b989c9955e90 Mon Sep 17 00:00:00 2001
From: Paul Campbell
Date: Sun, 21 Aug 2016 10:49:05 +0100
Subject: [PATCH 06/41] .gitignore: ignore intellij project files
---
.gitignore | 17 ++---------------
1 file changed, 2 insertions(+), 15 deletions(-)
diff --git a/.gitignore b/.gitignore
index 4ea327c..b311b8d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -17,21 +17,8 @@ nbactions.xml
.project
# intellij
-.idea/libraries/
-.idea/workspace.xml
-.idea/uiDesigner.xml
-.idea/compiler.xml
-.idea/misc.xml
-.idea/checkstyle.xml
-.idea/artifacts/
-.idea/dataSources*
-.idea/tasks.xml
-.idea/dictionaries/
-.idea/shelf/
-.idea/dynamic.xml
-.idea/sqlDataSources.xml
-.idea/gradle.xml
-.idea/mongoSettings.xml
+.idea/
+*.iml
# Spring
spring.log
From f945af160a6ac2b688c70e86d079bc727195e055 Mon Sep 17 00:00:00 2001
From: Paul Campbell
Date: Sun, 21 Aug 2016 11:29:42 +0100
Subject: [PATCH 07/41] NodeItem: deprecate dynamic node names
With the aim of moving towards immutable objects, node shouldn't have
dynamic names. I've not found a use for them in my own projects.
---
src/main/java/net/kemitix/node/NodeItem.java | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/src/main/java/net/kemitix/node/NodeItem.java b/src/main/java/net/kemitix/node/NodeItem.java
index ec71f11..899bf5c 100644
--- a/src/main/java/net/kemitix/node/NodeItem.java
+++ b/src/main/java/net/kemitix/node/NodeItem.java
@@ -52,7 +52,10 @@ public class NodeItem implements Node {
*
* @param data the data or null
* @param nameSupplier the name supplier function
+ *
+ * @deprecated dynamic names don't work on immutable objects
*/
+ @Deprecated
public NodeItem(
final T data, final Function, String> nameSupplier) {
this(data);
@@ -89,7 +92,10 @@ public class NodeItem implements Node {
* @param data the data or null
* @param nameSupplier the name supplier function
* @param parent the parent node
+ *
+ * @deprecated dynamic names don't work on immutable objects
*/
+ @Deprecated
public NodeItem(
final T data, final Function, String> nameSupplier,
final Node parent) {
From 39721e77c1636441c473e8f197e81d3d4c6c8f29 Mon Sep 17 00:00:00 2001
From: Paul Campbell
Date: Sun, 21 Aug 2016 11:36:46 +0100
Subject: [PATCH 08/41] pom.xml: add dependency on net.trajano.commons to
utility class testing
---
pom.xml | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/pom.xml b/pom.xml
index dd9ddbf..3071a0d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -17,6 +17,7 @@
3.4.14.2.0
+ 2.1.0
@@ -69,5 +70,11 @@
${assertj.version}test
+
+ net.trajano.commons
+ commons-testing
+ ${trajano-commons-testing.version}
+ test
+
From 51e8194db76037a10619f3c493b1064ea1bd75f7 Mon Sep 17 00:00:00 2001
From: Paul Campbell
Date: Sun, 21 Aug 2016 11:37:15 +0100
Subject: [PATCH 09/41] Nodes: add utility class to create Node instances
---
src/main/java/net/kemitix/node/Nodes.java | 66 +++++++++++++++++++
src/test/java/net/kemitix/node/NodesTest.java | 62 +++++++++++++++++
2 files changed, 128 insertions(+)
create mode 100644 src/main/java/net/kemitix/node/Nodes.java
create mode 100644 src/test/java/net/kemitix/node/NodesTest.java
diff --git a/src/main/java/net/kemitix/node/Nodes.java b/src/main/java/net/kemitix/node/Nodes.java
new file mode 100644
index 0000000..24dc50e
--- /dev/null
+++ b/src/main/java/net/kemitix/node/Nodes.java
@@ -0,0 +1,66 @@
+package net.kemitix.node;
+
+/**
+ * Utility class for {@link Node} items.
+ *
+ * @author pcampbell
+ */
+public final class Nodes {
+
+ private Nodes() {
+ }
+
+ /**
+ * Creates a new unnamed root node.
+ *
+ * @param data the data the node will contain
+ * @param the type of the data
+ *
+ * @return the new node
+ */
+ public static Node unnamedRoot(final T data) {
+ return new NodeItem<>(data);
+ }
+
+ /**
+ * Creates a new named root node.
+ *
+ * @param data the data the node will contain
+ * @param name the name of the node
+ * @param the type of the data
+ *
+ * @return the new node
+ */
+ public static Node namedRoot(final T data, final String name) {
+ return new NodeItem<>(data, name);
+ }
+
+ /**
+ * Creates a new unnamed child node.
+ *
+ * @param data the data the node will contain
+ * @param parent the parent of the node
+ * @param the type of the data
+ *
+ * @return the new node
+ */
+ public static Node unnamedChild(final T data, final Node parent) {
+ return new NodeItem<>(data, parent);
+ }
+
+ /**
+ * Creates a new named child node.
+ *
+ * @param data the data the node will contain
+ * @param name the name of the node
+ * @param parent the parent of the node
+ * @param the type of the data
+ *
+ * @return the new node
+ */
+ public static Node namedChild(
+ final T data, final String name, final Node parent) {
+ return new NodeItem<>(data, name, parent);
+ }
+
+}
diff --git a/src/test/java/net/kemitix/node/NodesTest.java b/src/test/java/net/kemitix/node/NodesTest.java
new file mode 100644
index 0000000..ace4f79
--- /dev/null
+++ b/src/test/java/net/kemitix/node/NodesTest.java
@@ -0,0 +1,62 @@
+package net.kemitix.node;
+
+import lombok.val;
+import org.assertj.core.api.SoftAssertions;
+import org.junit.Test;
+
+import static net.trajano.commons.testing.UtilityClassTestUtil
+ .assertUtilityClassWellDefined;
+
+/**
+ * Tests for {@link Nodes}.
+ *
+ * @author pcampbell
+ */
+public class NodesTest {
+
+ @Test
+ public void shouldBeValidUtilityClass() throws Exception {
+ assertUtilityClassWellDefined(Nodes.class);
+ }
+
+ @Test
+ public void shouldCreateUnnamedRoot() throws Exception {
+ val node = Nodes.unnamedRoot("data");
+ SoftAssertions softly = new SoftAssertions();
+ softly.assertThat(node.getData()).contains("data");
+ softly.assertThat(node.getName()).isNull();
+ softly.assertAll();
+ }
+
+ @Test
+ public void shouldCreateNamedRoot() throws Exception {
+ val node = Nodes.namedRoot("data", "name");
+ SoftAssertions softly = new SoftAssertions();
+ softly.assertThat(node.getData()).contains("data");
+ softly.assertThat(node.getName()).isEqualTo("name");
+ softly.assertAll();
+ }
+
+ @Test
+ public void shouldCreateUnnamedChild() throws Exception {
+ val parent = Nodes.unnamedRoot("root");
+ val node = Nodes.unnamedChild("data", parent);
+ SoftAssertions softly = new SoftAssertions();
+ softly.assertThat(node.getData()).contains("data");
+ softly.assertThat(node.getName()).isNull();
+ softly.assertThat(node.getParent()).contains(parent);
+ softly.assertAll();
+ }
+
+ @Test
+ public void shouldCreateNamedChild() throws Exception {
+ val parent = Nodes.unnamedRoot("root");
+ val node = Nodes.namedChild("data", "child", parent);
+ SoftAssertions softly = new SoftAssertions();
+ softly.assertThat(node.getData()).contains("data");
+ softly.assertThat(node.getName()).isEqualTo("child");
+ softly.assertThat(node.getParent()).contains(parent);
+ softly.assertAll();
+ }
+
+}
From e9b43cb73c87a628b1a06eba5b67a96b8c9557ae Mon Sep 17 00:00:00 2001
From: Paul Campbell
Date: Sun, 21 Aug 2016 12:04:24 +0100
Subject: [PATCH 10/41] [BREAKING] NodeItem: require instantiation using Nodes
This breaks the existing API where NodeItem could be instantiated directly.
---
src/main/java/net/kemitix/node/NodeItem.java | 8 +-
.../java/net/kemitix/node/NodeItemTest.java | 225 +++++++++---------
2 files changed, 117 insertions(+), 116 deletions(-)
diff --git a/src/main/java/net/kemitix/node/NodeItem.java b/src/main/java/net/kemitix/node/NodeItem.java
index 899bf5c..f54b6eb 100644
--- a/src/main/java/net/kemitix/node/NodeItem.java
+++ b/src/main/java/net/kemitix/node/NodeItem.java
@@ -32,7 +32,7 @@ public class NodeItem implements Node {
* @param data the data or null
* @param name the name
*/
- public NodeItem(final T data, final String name) {
+ NodeItem(final T data, final String name) {
this(data);
this.name = name;
}
@@ -42,7 +42,7 @@ public class NodeItem implements Node {
*
* @param data the data or null
*/
- public NodeItem(final T data) {
+ NodeItem(final T data) {
this.data = data;
this.nameSupplier = (n) -> null;
}
@@ -68,7 +68,7 @@ public class NodeItem implements Node {
* @param data the data or null
* @param parent the parent node
*/
- public NodeItem(final T data, final Node parent) {
+ NodeItem(final T data, final Node parent) {
this.data = data;
setParent(parent);
}
@@ -80,7 +80,7 @@ public class NodeItem implements Node {
* @param name the name
* @param parent the parent node
*/
- public NodeItem(final T data, final String name, final Node parent) {
+ NodeItem(final T data, final String name, final Node parent) {
this.data = data;
this.name = name;
setParent(parent);
diff --git a/src/test/java/net/kemitix/node/NodeItemTest.java b/src/test/java/net/kemitix/node/NodeItemTest.java
index 700b053..87b5a69 100644
--- a/src/test/java/net/kemitix/node/NodeItemTest.java
+++ b/src/test/java/net/kemitix/node/NodeItemTest.java
@@ -33,7 +33,7 @@ public class NodeItemTest {
//given
val data = "this node data";
//when
- node = new NodeItem<>(data);
+ node = Nodes.unnamedRoot(data);
//then
assertThat(node.getData()).as("can get the data from a node").
contains(data);
@@ -42,7 +42,7 @@ public class NodeItemTest {
@Test
public void canCreateAnEmptyAndUnnamedNode() {
//when
- node = new NodeItem<>(null);
+ node = Nodes.unnamedRoot(null);
//then
SoftAssertions softly = new SoftAssertions();
softly.assertThat(node.isEmpty()).as("node is empty").isTrue();
@@ -64,7 +64,7 @@ public class NodeItemTest {
@Test
public void canSetName() {
//given
- node = new NodeItem<>(null);
+ node = Nodes.unnamedRoot(null);
//when
node.setName("named");
//then
@@ -77,7 +77,7 @@ public class NodeItemTest {
@Test
public void shouldHaveNullForDefaultParent() {
//given
- node = new NodeItem<>("data");
+ node = Nodes.unnamedRoot("data");
//then
assertThat(node.getParent()).as(
"node created without a parent has no parent").isEmpty();
@@ -89,9 +89,9 @@ public class NodeItemTest {
@Test
public void shouldReturnNodeParent() {
//given
- val parent = new NodeItem("parent");
+ val parent = Nodes.unnamedRoot("parent");
//when
- node = new NodeItem<>("subject", parent);
+ node = Nodes.unnamedChild("subject", parent);
//then
assertThat(node.getParent()).as(
"node created with a parent can return the parent")
@@ -105,8 +105,8 @@ public class NodeItemTest {
@Test
public void setParentShouldThrowNodeExceptionWhenParentIsAChild() {
//given
- node = new NodeItem<>("subject");
- val child = new NodeItem("child", node);
+ node = Nodes.unnamedRoot("subject");
+ val child = Nodes.unnamedChild("child", node);
exception.expect(NodeException.class);
exception.expectMessage("Parent is a descendant");
//when
@@ -121,9 +121,9 @@ public class NodeItemTest {
@SuppressWarnings("unchecked")
public void shouldAddNewNodeAsChildToParent() {
//given
- val parent = new NodeItem("parent");
+ val parent = Nodes.unnamedRoot("parent");
//when
- node = new NodeItem<>("subject", parent);
+ node = Nodes.unnamedChild("subject", parent);
//then
assertThat(parent.getChildren()).as(
"when a node is created with a parent, the parent has the new"
@@ -136,8 +136,8 @@ public class NodeItemTest {
@Test
public void shouldReturnSetParent() {
//given
- node = new NodeItem<>("subject");
- val parent = new NodeItem("parent");
+ node = Nodes.unnamedRoot("subject");
+ val parent = Nodes.unnamedRoot("parent");
//when
node.setParent(parent);
//then
@@ -152,7 +152,7 @@ public class NodeItemTest {
@Test
public void shouldThrowNPEWhenSetParentNull() {
//given
- node = new NodeItem<>("subject");
+ node = Nodes.unnamedRoot("subject");
exception.expect(NullPointerException.class);
exception.expectMessage("parent");
//when
@@ -166,7 +166,7 @@ public class NodeItemTest {
@Test
public void setParentShouldThrowNodeExceptionWhenParentIsSelf() {
//given
- node = new NodeItem<>("subject");
+ node = Nodes.unnamedRoot("subject");
exception.expect(NodeException.class);
exception.expectMessage("Parent is a descendant");
//when
@@ -180,9 +180,9 @@ public class NodeItemTest {
@Test
public void shouldUpdateOldParentWhenNodeSetToNewParent() {
//given
- node = new NodeItem<>("subject");
+ node = Nodes.unnamedRoot("subject");
val child = node.createChild("child");
- val newParent = new NodeItem("newParent");
+ val newParent = Nodes.unnamedRoot("newParent");
//when
child.setParent(newParent);
//then
@@ -201,9 +201,9 @@ public class NodeItemTest {
@Test
public void shouldRemoveNodeFromOldParentWhenAddedAsChildToNewParent() {
//given
- node = new NodeItem<>("subject");
+ node = Nodes.unnamedRoot("subject");
val child = node.createChild("child");
- val newParent = new NodeItem("newParent");
+ val newParent = Nodes.unnamedRoot("newParent");
//when
newParent.addChild(child);
//then
@@ -223,7 +223,7 @@ public class NodeItemTest {
@Test
public void shouldThrowNPEWhenAddingNullAsChild() {
//given
- node = new NodeItem<>("subject");
+ node = Nodes.unnamedRoot("subject");
exception.expect(NullPointerException.class);
exception.expectMessage("child");
//when
@@ -237,8 +237,8 @@ public class NodeItemTest {
@SuppressWarnings("unchecked")
public void shouldReturnAddedChild() {
//given
- node = new NodeItem<>("subject");
- val child = new NodeItem("child");
+ node = Nodes.unnamedRoot("subject");
+ val child = Nodes.unnamedRoot("child");
//when
node.addChild(child);
//then
@@ -253,7 +253,7 @@ public class NodeItemTest {
@Test
public void addChildShouldThrowNodeExceptionWhenAddingANodeAsOwnChild() {
//given
- node = new NodeItem<>("subject");
+ node = Nodes.unnamedRoot("subject");
exception.expect(NodeException.class);
exception.expectMessage("Child is an ancestor");
//then
@@ -266,7 +266,7 @@ public class NodeItemTest {
@Test
public void addChildShouldThrowNodeExceptionWhenAddingSelfAsChild() {
//given
- node = new NodeItem<>("subject");
+ node = Nodes.unnamedRoot("subject");
exception.expect(NodeException.class);
exception.expectMessage("Child is an ancestor");
//when
@@ -280,8 +280,8 @@ public class NodeItemTest {
@Test
public void addChildShouldThrowNodeExceptionWhenChildIsParent() {
//given
- val parent = new NodeItem("parent");
- node = new NodeItem<>("subject", parent);
+ val parent = Nodes.unnamedRoot("parent");
+ node = Nodes.unnamedChild("subject", parent);
exception.expect(NodeException.class);
exception.expectMessage("Child is an ancestor");
//when
@@ -295,9 +295,9 @@ public class NodeItemTest {
@Test
public void addChildShouldThrowNodeExceptionWhenAddingGrandParentAsChild() {
//given
- val grandParent = new NodeItem("grandparent");
- val parent = new NodeItem("parent", grandParent);
- node = new NodeItem<>("subject", parent);
+ val grandParent = Nodes.unnamedRoot("grandparent");
+ val parent = Nodes.unnamedChild("parent", grandParent);
+ node = Nodes.unnamedChild("subject", parent);
exception.expect(NodeException.class);
exception.expectMessage("Child is an ancestor");
//when
@@ -310,8 +310,8 @@ public class NodeItemTest {
@Test
public void shouldSetParentOnChildWhenAddedAsChild() {
//given
- node = new NodeItem<>("subject");
- val child = new NodeItem("child");
+ node = Nodes.unnamedRoot("subject");
+ val child = Nodes.unnamedRoot("child");
//when
node.addChild(child);
//then
@@ -327,11 +327,11 @@ public class NodeItemTest {
public void shouldWalkTreeToNode() {
//given
val grandparent = "grandparent";
- val grandParentNode = new NodeItem(grandparent);
+ val grandParentNode = Nodes.unnamedRoot(grandparent);
val parent = "parent";
- val parentNode = new NodeItem(parent, grandParentNode);
+ val parentNode = Nodes.unnamedChild(parent, grandParentNode);
val subject = "subject";
- node = new NodeItem<>(subject, parentNode);
+ node = Nodes.unnamedChild(subject, parentNode);
//when
val result = grandParentNode.findInPath(Arrays.asList(parent, subject));
//then
@@ -352,9 +352,9 @@ public class NodeItemTest {
public void shouldNotFindNonExistentChildNode() {
//given
val parent = "parent";
- val parentNode = new NodeItem(parent);
+ val parentNode = Nodes.unnamedRoot(parent);
val subject = "subject";
- node = new NodeItem<>(subject, parentNode);
+ node = Nodes.unnamedChild(subject, parentNode);
//when
val result = parentNode.findInPath(Arrays.asList(subject, "no child"));
//then
@@ -369,7 +369,7 @@ public class NodeItemTest {
@Test
public void shouldThrowNEWhenWalkTreeNull() {
//given
- node = new NodeItem<>("subject");
+ node = Nodes.unnamedRoot("subject");
exception.expect(NullPointerException.class);
exception.expectMessage("path");
//when
@@ -383,7 +383,7 @@ public class NodeItemTest {
@Test
public void shouldReturnEmptyForEmptyWalkTreePath() {
//given
- node = new NodeItem<>("subject");
+ node = Nodes.unnamedRoot("subject");
//when
val result = node.findInPath(Collections.emptyList());
//then
@@ -396,7 +396,7 @@ public class NodeItemTest {
@Test
public void shouldCreateDescendantNodes() {
//given
- node = new NodeItem<>("subject");
+ node = Nodes.unnamedRoot("subject");
val alphaData = "alpha";
val betaData = "beta";
val gammaData = "gamma";
@@ -445,7 +445,7 @@ public class NodeItemTest {
@Test
public void createDescendantLineShouldThrowNPEWhenDescendantsAreNull() {
//given
- node = new NodeItem<>("subject");
+ node = Nodes.unnamedRoot("subject");
exception.expect(NullPointerException.class);
exception.expectMessage("descendants");
//when
@@ -458,7 +458,7 @@ public class NodeItemTest {
@Test
public void shouldChangeNothingWhenCreateDescendantEmpty() {
//given
- node = new NodeItem<>("subject");
+ node = Nodes.unnamedRoot("subject");
//when
node.createDescendantLine(Collections.emptyList());
//then
@@ -473,9 +473,9 @@ public class NodeItemTest {
@Test
public void shouldFindExistingChildNode() {
//given
- node = new NodeItem<>("subject");
+ node = Nodes.unnamedRoot("subject");
val childData = "child";
- val child = new NodeItem(childData, node);
+ val child = Nodes.unnamedChild(childData, node);
//when
val found = node.findOrCreateChild(childData);
//then
@@ -490,7 +490,7 @@ public class NodeItemTest {
@Test
public void shouldFindCreateNewChildNode() {
//given
- node = new NodeItem<>("subject");
+ node = Nodes.unnamedRoot("subject");
val childData = "child";
//when
val found = node.findOrCreateChild(childData);
@@ -506,7 +506,7 @@ public class NodeItemTest {
@Test
public void findOrCreateChildShouldThrowNPEFWhenChildIsNull() {
//given
- node = new NodeItem<>("subject");
+ node = Nodes.unnamedRoot("subject");
exception.expect(NullPointerException.class);
exception.expectMessage("child");
//when
@@ -519,9 +519,9 @@ public class NodeItemTest {
@Test
public void shouldGetChild() {
//given
- node = new NodeItem<>("subject");
+ node = Nodes.unnamedRoot("subject");
val childData = "child";
- val child = new NodeItem(childData);
+ val child = Nodes.unnamedRoot(childData);
node.addChild(child);
//when
val found = node.findChild(childData);
@@ -541,7 +541,7 @@ public class NodeItemTest {
@Test
public void getChildShouldThrowNPEWhenThereIsNoChild() {
//given
- node = new NodeItem<>("data");
+ node = Nodes.unnamedRoot("data");
exception.expect(NullPointerException.class);
exception.expectMessage("child");
//when
@@ -555,7 +555,7 @@ public class NodeItemTest {
@Test
public void shouldCreateChild() {
//given
- node = new NodeItem<>("subject");
+ node = Nodes.unnamedRoot("subject");
val childData = "child";
//when
val child = node.createChild(childData);
@@ -580,7 +580,7 @@ public class NodeItemTest {
@Test
public void createChildShouldThrowNPEWhenChildIsNull() {
//given
- node = new NodeItem<>("subject");
+ node = Nodes.unnamedRoot("subject");
exception.expect(NullPointerException.class);
exception.expectMessage("child");
//when
@@ -599,7 +599,7 @@ public class NodeItemTest {
public void getNameShouldUseParentNameSupplier() {
//given
val root = new NodeItem("root", n -> n.getData().get());
- node = new NodeItem<>("child", root);
+ node = Nodes.unnamedChild("child", root);
//then
assertThat(node.getName()).isEqualTo("child");
}
@@ -622,7 +622,7 @@ public class NodeItemTest {
node = new NodeItem<>("root", n -> n.getData().get());
val child = new NodeItem("child", Object::toString);
node.addChild(child);
- val grandChild = new NodeItem<>("grandchild", child);
+ val grandChild = Nodes.unnamedChild("grandchild", child);
//then
assertThat(node.getName()).isEqualTo("root");
assertThat(child.getName()).isNotEqualTo("child");
@@ -631,8 +631,8 @@ public class NodeItemTest {
@Test
public void getNameShouldWorkWithoutNameSupplier() {
- node = new NodeItem<>(null, "root");
- val namedchild = new NodeItem<>("named", "Alice", node);
+ node = Nodes.namedRoot(null, "root");
+ val namedchild = Nodes.namedChild("named", "Alice", node);
//then
assertThat(node.getName()).isEqualTo("root");
assertThat(namedchild.getName()).isEqualTo("Alice");
@@ -640,22 +640,22 @@ public class NodeItemTest {
@Test
public void canCreateRootNodeWithoutData() {
- node = new NodeItem<>(null, "empty");
+ node = Nodes.namedRoot(null, "empty");
assertThat(node.getData()).isEmpty();
}
@Test
public void canCreateRootNodeWithoutDataButWithNameSupplier() {
- node = new NodeItem<>(null);
+ node = Nodes.unnamedRoot(null);
assertThat(node.getData()).isEmpty();
}
@Test
public void getChildNamedFindsChild() {
//given
- node = new NodeItem<>(null, "root");
- val alpha = new NodeItem(null, "alpha");
- val beta = new NodeItem(null, "beta");
+ node = Nodes.namedRoot("root data", "root");
+ val alpha = Nodes.namedRoot("alpha data", "alpha");
+ val beta = Nodes.namedRoot("beta data", "beta");
node.addChild(alpha);
node.addChild(beta);
//when
@@ -667,9 +667,9 @@ public class NodeItemTest {
@Test
public void getChildNamedFindsNothing() {
//given
- node = new NodeItem<>(null, "root");
- val alpha = new NodeItem(null, "alpha");
- val beta = new NodeItem(null, "beta");
+ node = Nodes.namedRoot("root data", "root");
+ val alpha = Nodes.namedRoot("alpha data", "alpha");
+ val beta = Nodes.namedRoot("beta data", "beta");
node.addChild(alpha);
node.addChild(beta);
exception.expect(NodeException.class);
@@ -681,10 +681,10 @@ public class NodeItemTest {
@Test
public void nodeNamesAreUniqueWithinAParent() {
//given
- node = new NodeItem<>(null, "root");
- val alpha = new NodeItem(null, "alpha");
+ node = Nodes.namedRoot("root data", "root");
+ val alpha = Nodes.namedRoot("alpha data", "alpha");
node.addChild(alpha);
- val beta = new NodeItem(null, "alpha");
+ val beta = Nodes.namedRoot("beta data", "alpha");
exception.expect(NodeException.class);
exception.expectMessage("Node with that name already exists here");
//when
@@ -694,8 +694,8 @@ public class NodeItemTest {
@Test
public void canPlaceNodeInTreeByPathNames() {
//given
- node = new NodeItem<>(null, "root"); // create a root
- val four = new NodeItem("data", "four");
+ node = Nodes.namedRoot("root data", "root"); // create a root
+ val four = Nodes.namedRoot("data", "four");
//when
node.insertInPath(four, "one", "two", "three");
//then
@@ -717,9 +717,9 @@ public class NodeItemTest {
@SuppressWarnings("unchecked")
public void canPlaceInTreeUnderExistingNode() {
//given
- node = new NodeItem<>(null, "root");
- val child = new NodeItem("child data", "child");
- val grandchild = new NodeItem("grandchild data", "grandchild");
+ node = Nodes.namedRoot(null, "root");
+ val child = Nodes.namedRoot("child data", "child");
+ val grandchild = Nodes.namedRoot("grandchild data", "grandchild");
//when
node.insertInPath(child); // as root/child
node.insertInPath(grandchild, "child"); // as root/child/grandchild
@@ -734,9 +734,9 @@ public class NodeItemTest {
@SuppressWarnings("unchecked")
public void canPlaceInTreeAboveExistingNode() {
//given
- node = new NodeItem<>(null, "root");
- val child = new NodeItem("child data", "child");
- val grandchild = new NodeItem("grandchild data", "grandchild");
+ node = Nodes.namedRoot(null, "root");
+ val child = Nodes.namedRoot("child data", "child");
+ val grandchild = Nodes.namedRoot("grandchild data", "grandchild");
//when
node.insertInPath(grandchild, "child");
node.insertInPath(child);
@@ -752,7 +752,7 @@ public class NodeItemTest {
@Test
public void removingParentFromNodeWithNoParentIsNoop() {
//given
- node = new NodeItem<>(null);
+ node = Nodes.unnamedRoot(null);
//when
node.removeParent();
}
@@ -760,8 +760,8 @@ public class NodeItemTest {
@Test
public void removingParentFromNodeWithParentRemovesParent() {
//given
- node = new NodeItem<>(null);
- val child = new NodeItem(null, node);
+ node = Nodes.unnamedRoot(null);
+ val child = Nodes.unnamedChild("child data", node);
//when
child.removeParent();
//then
@@ -775,22 +775,22 @@ public class NodeItemTest {
exception.expect(NodeException.class);
exception.expectMessage(
"A non-empty node named 'grandchild' already exists here");
- node = new NodeItem<>(null);
- val child = new NodeItem(null, "child", node);
- new NodeItem<>("data", "grandchild", child);
+ node = Nodes.unnamedRoot(null);
+ val child = Nodes.namedChild("child data", "child", node);
+ Nodes.namedChild("data", "grandchild", child);
// root -> child -> grandchild
// only grandchild has data
//when
// attempt to add another node called 'grandchild' to 'child'
- node.insertInPath(new NodeItem<>("cuckoo", "grandchild"), "child");
+ node.insertInPath(Nodes.namedRoot("cuckoo", "grandchild"), "child");
}
@Test
@SuppressWarnings("unchecked")
public void placeNodeInTreeWhenAddedNodeIsUnnamed() {
//given
- node = new NodeItem<>(null);
- final Node newNode = new NodeItem<>(null);
+ node = Nodes.unnamedRoot(null);
+ final Node newNode = Nodes.unnamedRoot(null);
//when
node.insertInPath(newNode);
//then
@@ -801,12 +801,12 @@ public class NodeItemTest {
@SuppressWarnings("unchecked")
public void placeNodeInTreeWhenEmptyChildWithTargetNameExists() {
//given
- node = new NodeItem<>(null);
- final NodeItem child = new NodeItem<>(null, "child");
- final NodeItem target = new NodeItem<>(null, "target");
+ node = Nodes.unnamedRoot(null);
+ final Node child = Nodes.namedRoot(null, "child");
+ final Node target = Nodes.namedRoot(null, "target");
node.addChild(child);
child.addChild(target);
- final NodeItem addMe = new NodeItem<>("I'm new", "target");
+ val addMe = Nodes.namedRoot("I'm new", "target");
assertThat(addMe.getParent()).isEmpty();
assertThat(child.getChildByName("target").isEmpty()).as(
"target starts empty").isTrue();
@@ -823,7 +823,7 @@ public class NodeItemTest {
//given
exception.expect(NullPointerException.class);
exception.expectMessage("name");
- node = new NodeItem<>(null);
+ node = Nodes.unnamedRoot(null);
//when
node.findChildByName(null);
}
@@ -831,7 +831,7 @@ public class NodeItemTest {
@Test
public void isNamedNull() {
//given
- node = new NodeItem<>(null);
+ node = Nodes.unnamedRoot(null);
//then
assertThat(node.isNamed()).isFalse();
}
@@ -839,7 +839,7 @@ public class NodeItemTest {
@Test
public void isNamedEmpty() {
//given
- node = new NodeItem<>(null, "");
+ node = Nodes.namedRoot(null, "");
//then
assertThat(node.isNamed()).isFalse();
}
@@ -847,7 +847,7 @@ public class NodeItemTest {
@Test
public void isNamedNamed() {
//given
- node = new NodeItem<>(null, "named");
+ node = Nodes.namedRoot(null, "named");
//then
assertThat(node.isNamed()).isTrue();
}
@@ -858,7 +858,7 @@ public class NodeItemTest {
// provider
//given
node = new NodeItem<>("data", n -> n.getData().get());
- final NodeItem child = new NodeItem<>("other", node);
+ val child = Nodes.unnamedChild("other", node);
assertThat(node.getName()).as("initial root name").isEqualTo("data");
assertThat(child.getName()).as("initial child name").isEqualTo("other");
//when
@@ -872,7 +872,7 @@ public class NodeItemTest {
@SuppressWarnings("unchecked")
public void removeChildRemovesTheChild() {
//given
- node = new NodeItem<>(null);
+ node = Nodes.unnamedRoot(null);
Node child = node.createChild("child");
assertThat(node.getChildren()).containsExactly(child);
//then
@@ -885,13 +885,14 @@ public class NodeItemTest {
@Test
public void drawTreeIsCorrect() {
//given
- node = new NodeItem<>(null, "root");
- val bob = new NodeItem(null, "bob", node);
- val alice = new NodeItem(null, "alice", node);
- new NodeItem<>(null, "dave", alice);
- new NodeItem<>(null, bob); // has no name and no children so no included
- val kim = new NodeItem(null, node); // nameless mother
- new NodeItem<>(null, "lucy", kim);
+ node = Nodes.namedRoot(null, "root");
+ val bob = Nodes.namedChild("bob data", "bob", node);
+ val alice = Nodes.namedChild("alice data", "alice", node);
+ Nodes.namedChild("dave data", "dave", alice);
+ Nodes.unnamedChild("bob's child's data",
+ bob); // has no name and no children so no included
+ val kim = Nodes.unnamedChild("kim data", node); // nameless mother
+ Nodes.namedChild("lucy data", "lucy", kim);
//when
val tree = node.drawTree(0);
//then
@@ -907,7 +908,7 @@ public class NodeItemTest {
@Test
public void canChangeNodeData() {
//given
- node = new NodeItem<>("initial");
+ node = Nodes.unnamedRoot("initial");
//when
node.setData("updated");
//then
@@ -918,7 +919,7 @@ public class NodeItemTest {
@SuppressWarnings("unchecked")
public void canCreateNamedChild() {
//given
- node = new NodeItem<>(null);
+ node = Nodes.unnamedRoot(null);
//when
Node child = node.createChild("child data", "child name");
//then
@@ -930,10 +931,10 @@ public class NodeItemTest {
@Test
public void canGetChildWhenFound() {
//given
- node = new NodeItem<>("data");
- Node child = new NodeItem<>("child data", "child name", node);
+ node = Nodes.unnamedRoot("data");
+ val child = Nodes.namedChild("child data", "child name", node);
//when
- Node found = node.getChild("child data");
+ val found = node.getChild("child data");
//then
assertThat(found).isSameAs(child);
}
@@ -943,7 +944,7 @@ public class NodeItemTest {
//given
exception.expect(NodeException.class);
exception.expectMessage("Child not found");
- node = new NodeItem<>("data");
+ node = Nodes.unnamedRoot("data");
//when
node.getChild("child data");
}
@@ -952,9 +953,9 @@ public class NodeItemTest {
@SuppressWarnings("unchecked")
public void constructorWithNameSupplierAndParentBeChildOfParent() {
//given
- node = new NodeItem<>(null);
+ node = Nodes.unnamedRoot(null);
//when
- NodeItem child = new NodeItem<>(null, node);
+ val child = Nodes.unnamedChild("child data", node);
//then
assertThat(child.getParent()).contains(node);
assertThat(node.getChildren()).containsExactly(child);
@@ -965,7 +966,7 @@ public class NodeItemTest {
public void removeParentCopiesRootNameSupplier() {
//given
node = new NodeItem<>("root data", n -> "root supplier");
- val child = new NodeItem<>("child data", node);
+ val child = Nodes.unnamedChild("child data", node);
assertThat(child.getName()).isEqualTo("root supplier");
//when
child.removeParent();
@@ -990,7 +991,7 @@ public class NodeItemTest {
public void setNameToNullRevertsToParentNameSupplier() {
//given
node = new NodeItem<>(null, n -> "root supplier");
- val child = new NodeItem(null, "child name", node);
+ val child = Nodes.namedChild("child data", "child name", node);
assertThat(child.getName()).isEqualTo("child name");
//when
child.setName(null);
@@ -1034,8 +1035,8 @@ public class NodeItemTest {
return "";
};
node = new NodeItem<>(null, pathNameSupplier);
- val child = new NodeItem("child", node);
- val grandchild = new NodeItem("grandchild", child);
+ val child = Nodes.unnamedChild("child", node);
+ val grandchild = Nodes.unnamedChild("grandchild", child);
//then
assertThat(grandchild.getName()).isEqualTo("/child/grandchild");
}
@@ -1043,8 +1044,8 @@ public class NodeItemTest {
@Test
public void canSafelyHandleFindChildWhenAChildHasNoData() {
//given
- node = new NodeItem<>(null);
- new NodeItem<>(null, node);
+ node = Nodes.unnamedRoot(null);
+ Nodes.unnamedChild(null, node);
//when
node.findChild("data");
}
From 2da1d9aa3dfd747146fa46390f780a9b2a3c1393 Mon Sep 17 00:00:00 2001
From: Paul Campbell
Date: Sun, 21 Aug 2016 12:27:50 +0100
Subject: [PATCH 11/41] NodeItem: add deprecated to implementation of
deprecated interface method
---
src/main/java/net/kemitix/node/NodeItem.java | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/main/java/net/kemitix/node/NodeItem.java b/src/main/java/net/kemitix/node/NodeItem.java
index f54b6eb..3afbe8b 100644
--- a/src/main/java/net/kemitix/node/NodeItem.java
+++ b/src/main/java/net/kemitix/node/NodeItem.java
@@ -235,6 +235,7 @@ public class NodeItem implements Node {
* @return the found or created child node
*/
@Override
+ @Deprecated
public Node findOrCreateChild(final T child) {
if (child == null) {
throw new NullPointerException("child");
From 1b0b0222815a284703f1e1a79e77f35b1992f7c4 Mon Sep 17 00:00:00 2001
From: Paul Campbell
Date: Sun, 21 Aug 2016 12:36:40 +0100
Subject: [PATCH 12/41] NodeItem.findChild(): rewrite stream filter
---
src/main/java/net/kemitix/node/NodeItem.java | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/main/java/net/kemitix/node/NodeItem.java b/src/main/java/net/kemitix/node/NodeItem.java
index 3afbe8b..9e34a83 100644
--- a/src/main/java/net/kemitix/node/NodeItem.java
+++ b/src/main/java/net/kemitix/node/NodeItem.java
@@ -255,10 +255,10 @@ public class NodeItem implements Node {
if (child == null) {
throw new NullPointerException("child");
}
- return children.stream()
- .filter(n -> !n.isEmpty())
- .filter(n -> n.getData().get().equals(child))
- .findAny();
+ return children.stream().filter(node -> {
+ final Optional d = node.getData();
+ return d.isPresent() && d.get().equals(child);
+ }).findAny();
}
@Override
From 7a10498a31b51f78e3af567a9d5cd62070cd0b0c Mon Sep 17 00:00:00 2001
From: Paul Campbell
Date: Sun, 21 Aug 2016 12:42:21 +0100
Subject: [PATCH 13/41] NodeItem: remove dynamic name support for nodes
---
src/main/java/net/kemitix/node/NodeItem.java | 32 ----
.../java/net/kemitix/node/NodeItemTest.java | 168 ------------------
2 files changed, 200 deletions(-)
diff --git a/src/main/java/net/kemitix/node/NodeItem.java b/src/main/java/net/kemitix/node/NodeItem.java
index 9e34a83..1a83c73 100644
--- a/src/main/java/net/kemitix/node/NodeItem.java
+++ b/src/main/java/net/kemitix/node/NodeItem.java
@@ -47,21 +47,6 @@ public class NodeItem implements Node {
this.nameSupplier = (n) -> null;
}
- /**
- * Creates root node with a name supplier.
- *
- * @param data the data or null
- * @param nameSupplier the name supplier function
- *
- * @deprecated dynamic names don't work on immutable objects
- */
- @Deprecated
- public NodeItem(
- final T data, final Function, String> nameSupplier) {
- this(data);
- this.nameSupplier = nameSupplier;
- }
-
/**
* Creates a node with a parent.
*
@@ -86,23 +71,6 @@ public class NodeItem implements Node {
setParent(parent);
}
- /**
- * Creates a node with a name supplier and a parent.
- *
- * @param data the data or null
- * @param nameSupplier the name supplier function
- * @param parent the parent node
- *
- * @deprecated dynamic names don't work on immutable objects
- */
- @Deprecated
- public NodeItem(
- final T data, final Function, String> nameSupplier,
- final Node parent) {
- this(data, nameSupplier);
- setParent(parent);
- }
-
private String generateName() {
return getNameSupplier().apply(this);
}
diff --git a/src/test/java/net/kemitix/node/NodeItemTest.java b/src/test/java/net/kemitix/node/NodeItemTest.java
index 87b5a69..247e7fb 100644
--- a/src/test/java/net/kemitix/node/NodeItemTest.java
+++ b/src/test/java/net/kemitix/node/NodeItemTest.java
@@ -8,13 +8,9 @@ import org.junit.rules.ExpectedException;
import static org.assertj.core.api.Assertions.assertThat;
-import java.time.LocalDate;
-import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.Collections;
import java.util.Optional;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.function.Function;
/**
* Test for {@link NodeItem}.
@@ -50,17 +46,6 @@ public class NodeItemTest {
softly.assertAll();
}
- @Test
- public void canCreateNodeWithParentAndCustomNameSupplier() {
- //given
- node = new NodeItem<>(null, n -> "root name supplier");
- //when
- val child = new NodeItem(null, n -> "overridden", node);
- //then
- assertThat(child.getName()).isEqualTo("overridden");
- assertThat(child.getParent()).contains(node);
- }
-
@Test
public void canSetName() {
//given
@@ -587,69 +572,12 @@ public class NodeItemTest {
node.createChild(null);
}
- @Test
- public void getNameShouldBeCorrect() {
- //given
- node = new NodeItem<>("subject", n -> n.getData().get());
- //then
- assertThat(node.getName()).isEqualTo("subject");
- }
-
- @Test
- public void getNameShouldUseParentNameSupplier() {
- //given
- val root = new NodeItem("root", n -> n.getData().get());
- node = Nodes.unnamedChild("child", root);
- //then
- assertThat(node.getName()).isEqualTo("child");
- }
-
- @Test
- public void getNameShouldReturnNameForNonStringData() {
- val root = new NodeItem(LocalDate.parse("2016-05-23"), n -> {
- if (n.isEmpty()) {
- return null;
- }
- return n.getData().get().format(DateTimeFormatter.BASIC_ISO_DATE);
-
- });
- //then
- assertThat(root.getName()).isEqualTo("20160523");
- }
-
- @Test
- public void getNameShouldUseClosestNameSupplier() {
- node = new NodeItem<>("root", n -> n.getData().get());
- val child = new NodeItem("child", Object::toString);
- node.addChild(child);
- val grandChild = Nodes.unnamedChild("grandchild", child);
- //then
- assertThat(node.getName()).isEqualTo("root");
- assertThat(child.getName()).isNotEqualTo("child");
- assertThat(grandChild.getName()).isNotEqualTo("grandchild");
- }
-
- @Test
- public void getNameShouldWorkWithoutNameSupplier() {
- node = Nodes.namedRoot(null, "root");
- val namedchild = Nodes.namedChild("named", "Alice", node);
- //then
- assertThat(node.getName()).isEqualTo("root");
- assertThat(namedchild.getName()).isEqualTo("Alice");
- }
-
@Test
public void canCreateRootNodeWithoutData() {
node = Nodes.namedRoot(null, "empty");
assertThat(node.getData()).isEmpty();
}
- @Test
- public void canCreateRootNodeWithoutDataButWithNameSupplier() {
- node = Nodes.unnamedRoot(null);
- assertThat(node.getData()).isEmpty();
- }
-
@Test
public void getChildNamedFindsChild() {
//given
@@ -852,22 +780,6 @@ public class NodeItemTest {
assertThat(node.isNamed()).isTrue();
}
- @Test
- public void removeParentNodeProvidesSameNameSupplier() {
- // once a node has it's parent removed it should provide a default name
- // provider
- //given
- node = new NodeItem<>("data", n -> n.getData().get());
- val child = Nodes.unnamedChild("other", node);
- assertThat(node.getName()).as("initial root name").isEqualTo("data");
- assertThat(child.getName()).as("initial child name").isEqualTo("other");
- //when
- child.removeParent();
- //then
- assertThat(node.getName()).as("final root name").isEqualTo("data");
- assertThat(child.getName()).as("final child name").isEqualTo("other");
- }
-
@Test
@SuppressWarnings("unchecked")
public void removeChildRemovesTheChild() {
@@ -961,86 +873,6 @@ public class NodeItemTest {
assertThat(node.getChildren()).containsExactly(child);
}
- @Test
- @SuppressWarnings("unchecked")
- public void removeParentCopiesRootNameSupplier() {
- //given
- node = new NodeItem<>("root data", n -> "root supplier");
- val child = Nodes.unnamedChild("child data", node);
- assertThat(child.getName()).isEqualTo("root supplier");
- //when
- child.removeParent();
- //then
- assertThat(child.getName()).isEqualTo("root supplier");
- }
-
- @Test
- @SuppressWarnings("unchecked")
- public void removeParentDoesNotReplaceLocalNameSupplier() {
- //given
- node = new NodeItem<>("root data", n -> "root supplier");
- val child = new NodeItem<>("child data", n -> "local supplier", node);
- assertThat(child.getName()).isEqualTo("local supplier");
- //when
- child.removeParent();
- //then
- assertThat(child.getName()).isEqualTo("local supplier");
- }
-
- @Test
- public void setNameToNullRevertsToParentNameSupplier() {
- //given
- node = new NodeItem<>(null, n -> "root supplier");
- val child = Nodes.namedChild("child data", "child name", node);
- assertThat(child.getName()).isEqualTo("child name");
- //when
- child.setName(null);
- //then
- assertThat(child.getName()).isEqualTo("root supplier");
- }
-
- @Test
- public void getNameWithNameSupplierIsRecalculatedEachCall() {
- val counter = new AtomicInteger(0);
- node = new NodeItem<>(null,
- n -> Integer.toString(counter.incrementAndGet()));
- //then
- assertThat(node.getName()).isNotEqualTo(node.getName());
- }
-
- @Test
- public void isNamedWithNameSupplierIsRecalculatedEachCall() {
- val counter = new AtomicInteger(0);
- node = new NodeItem<>(null, n -> {
- // alternate between even numbers and nulls: null, 2, null, 4, null
- final int i = counter.incrementAndGet();
- if (i % 2 == 0) {
- return Integer.toString(i);
- }
- return null;
- });
- //then
- assertThat(node.isNamed()).isFalse();
- assertThat(node.isNamed()).isTrue();
- }
-
- @Test
- public void canUseNameSupplierToBuildFullPath() {
- //given
- final Function, String> pathNameSupplier = node -> {
- Optional> parent = node.getParent();
- if (parent.isPresent()) {
- return parent.get().getName() + "/" + node.getData().get();
- }
- return "";
- };
- node = new NodeItem<>(null, pathNameSupplier);
- val child = Nodes.unnamedChild("child", node);
- val grandchild = Nodes.unnamedChild("grandchild", child);
- //then
- assertThat(grandchild.getName()).isEqualTo("/child/grandchild");
- }
-
@Test
public void canSafelyHandleFindChildWhenAChildHasNoData() {
//given
From cf6bc727173e4a55d1744009d90ad757cc5cbe7c Mon Sep 17 00:00:00 2001
From: Paul Campbell
Date: Sun, 21 Aug 2016 15:39:02 +0100
Subject: [PATCH 14/41] NodeItem: hide package class - use Node interface only
---
src/main/java/net/kemitix/node/NodeItem.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/main/java/net/kemitix/node/NodeItem.java b/src/main/java/net/kemitix/node/NodeItem.java
index 1a83c73..e688355 100644
--- a/src/main/java/net/kemitix/node/NodeItem.java
+++ b/src/main/java/net/kemitix/node/NodeItem.java
@@ -14,7 +14,7 @@ import java.util.function.Function;
*
* @author pcampbell
*/
-public class NodeItem implements Node {
+class NodeItem implements Node {
private T data;
From be0685538f8c032544f1e6246289502dc2a146a1 Mon Sep 17 00:00:00 2001
From: Paul Campbell
Date: Sun, 21 Aug 2016 19:08:59 +0100
Subject: [PATCH 15/41] AbstractNodeItem: added
---
.../net/kemitix/node/AbstractNodeItem.java | 157 ++++++++++++++++++
1 file changed, 157 insertions(+)
create mode 100644 src/main/java/net/kemitix/node/AbstractNodeItem.java
diff --git a/src/main/java/net/kemitix/node/AbstractNodeItem.java b/src/main/java/net/kemitix/node/AbstractNodeItem.java
new file mode 100644
index 0000000..6aa90e7
--- /dev/null
+++ b/src/main/java/net/kemitix/node/AbstractNodeItem.java
@@ -0,0 +1,157 @@
+package net.kemitix.node;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+
+/**
+ * An abstract node item, providing default implementations for most read-only
+ * operations.
+ *
+ * @param the type of data stored in each node
+ *
+ * @author pcampbell
+ */
+abstract class AbstractNodeItem implements Node {
+
+ private T data;
+
+ private String name;
+
+ private Node parent;
+
+ private final Set> children;
+
+ protected AbstractNodeItem(
+ final T data, final String name, final Node parent,
+ final Set> children) {
+ this.data = data;
+ this.name = name;
+ this.parent = parent;
+ this.children = children;
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public Optional getData() {
+ return Optional.ofNullable(data);
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return data == null;
+ }
+
+ @Override
+ public Optional> getParent() {
+ return Optional.ofNullable(parent);
+ }
+
+ @Override
+ public Set> getChildren() {
+ return new HashSet<>(children);
+ }
+
+ /**
+ * Fetches the node for the child if present.
+ *
+ * @param child the child's data to search for
+ *
+ * @return an {@link Optional} containing the child node if found
+ */
+ @Override
+ public Optional> findChild(final T child) {
+ if (child == null) {
+ throw new NullPointerException("child");
+ }
+ return children.stream().filter(node -> {
+ final Optional d = node.getData();
+ return d.isPresent() && d.get().equals(child);
+ }).findAny();
+ }
+
+ @Override
+ public Node getChild(final T child) {
+ return findChild(child).orElseThrow(
+ () -> new NodeException("Child not found"));
+ }
+
+ /**
+ * Checks if the node is an ancestor.
+ *
+ * @param node the potential ancestor
+ *
+ * @return true if the node is an ancestor
+ */
+ @Override
+ public boolean isDescendantOf(final Node node) {
+ return parent != null && (node.equals(parent) || parent.isDescendantOf(
+ node));
+ }
+
+ /**
+ * Walks the node tree using the path to select each child.
+ *
+ * @param path the path to the desired child
+ *
+ * @return the child or null
+ */
+ @Override
+ public Optional> findInPath(final List path) {
+ if (path == null) {
+ throw new NullPointerException("path");
+ }
+ if (path.size() > 0) {
+ Optional> found = findChild(path.get(0));
+ if (found.isPresent()) {
+ if (path.size() > 1) {
+ return found.get().findInPath(path.subList(1, path.size()));
+ }
+ return found;
+ }
+ }
+ return Optional.empty();
+ }
+
+ @Override
+ public Optional> findChildByName(final String named) {
+ if (named == null) {
+ throw new NullPointerException("name");
+ }
+ return children.stream()
+ .filter(n -> n.getName().equals(named))
+ .findAny();
+ }
+
+ @Override
+ public Node getChildByName(final String named) {
+ return findChildByName(named).orElseThrow(
+ () -> new NodeException("Named child not found"));
+ }
+
+ @Override
+ public String drawTree(final int depth) {
+ final StringBuilder sb = new StringBuilder();
+ final String unnamed = "(unnamed)";
+ if (isNamed()) {
+ sb.append(String.format("[%1$" + (depth + name.length()) + "s]\n",
+ name));
+ } else if (!children.isEmpty()) {
+ sb.append(
+ String.format("[%1$" + (depth + unnamed.length()) + "s]\n",
+ unnamed));
+ }
+ getChildren().forEach(c -> sb.append(c.drawTree(depth + 1)));
+ return sb.toString();
+ }
+
+ @Override
+ public boolean isNamed() {
+ return name != null && name.length() > 0;
+ }
+}
From 421ad743f01fb42d05ea2cadb8c07ae343a2d5d3 Mon Sep 17 00:00:00 2001
From: Paul Campbell
Date: Sun, 21 Aug 2016 19:09:12 +0100
Subject: [PATCH 16/41] ImmutableNodeItem: added
---
.../net/kemitix/node/ImmutableNodeItem.java | 95 ++++
.../kemitix/node/ImmutableNodeItemTest.java | 449 ++++++++++++++++++
2 files changed, 544 insertions(+)
create mode 100644 src/main/java/net/kemitix/node/ImmutableNodeItem.java
create mode 100644 src/test/java/net/kemitix/node/ImmutableNodeItemTest.java
diff --git a/src/main/java/net/kemitix/node/ImmutableNodeItem.java b/src/main/java/net/kemitix/node/ImmutableNodeItem.java
new file mode 100644
index 0000000..7b80a59
--- /dev/null
+++ b/src/main/java/net/kemitix/node/ImmutableNodeItem.java
@@ -0,0 +1,95 @@
+package net.kemitix.node;
+
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Represents an immutable tree of nodes.
+ *
+ *
Due to the use of generics the data within a node may not be immutable.
+ * (We can't create a defensive copy.) So if a user were to use {@code
+ * getData()} they could then modify the original data within the node. This
+ * wouldn't affect the integrity of the node tree structure, however.
+ *
+ * @param the type of data stored in each node
+ *
+ * @author pcampbell
+ */
+final class ImmutableNodeItem extends AbstractNodeItem {
+
+ private static final String IMMUTABLE_OBJECT = "Immutable object";
+
+ private ImmutableNodeItem(
+ final T data, final String name, final Node parent,
+ final Set> children) {
+ super(data, name, parent, children);
+ }
+
+ static ImmutableNodeItem newRoot(
+ final T data, final String name, final Set> children) {
+ return new ImmutableNodeItem<>(data, name, null, children);
+ }
+
+ static ImmutableNodeItem newChild(
+ final T data, final String name, final Node parent,
+ final Set> children) {
+ return new ImmutableNodeItem<>(data, name, parent, children);
+ }
+
+ @Override
+ public void setName(final String name) {
+ throw new UnsupportedOperationException(IMMUTABLE_OBJECT);
+ }
+
+ @Override
+ public void setData(final T data) {
+ throw new UnsupportedOperationException(IMMUTABLE_OBJECT);
+ }
+
+ @Override
+ public void setParent(final Node parent) {
+ throw new UnsupportedOperationException(IMMUTABLE_OBJECT);
+ }
+
+ @Override
+ public void addChild(final Node child) {
+ throw new UnsupportedOperationException(IMMUTABLE_OBJECT);
+ }
+
+ @Override
+ public Node createChild(final T child) {
+ throw new UnsupportedOperationException(IMMUTABLE_OBJECT);
+ }
+
+ @Override
+ public Node createChild(final T child, final String name) {
+ throw new UnsupportedOperationException(IMMUTABLE_OBJECT);
+ }
+
+ @Override
+ public void createDescendantLine(final List descendants) {
+ throw new UnsupportedOperationException(IMMUTABLE_OBJECT);
+ }
+
+ @Override
+ public Node findOrCreateChild(final T child) {
+ return findChild(child).orElseThrow(
+ () -> new UnsupportedOperationException(IMMUTABLE_OBJECT));
+ }
+
+ @Override
+ public void insertInPath(final Node node, final String... path) {
+ throw new UnsupportedOperationException(IMMUTABLE_OBJECT);
+ }
+
+ @Override
+ public void removeChild(final Node node) {
+ throw new UnsupportedOperationException(IMMUTABLE_OBJECT);
+ }
+
+ @Override
+ public void removeParent() {
+ throw new UnsupportedOperationException(IMMUTABLE_OBJECT);
+ }
+
+}
diff --git a/src/test/java/net/kemitix/node/ImmutableNodeItemTest.java b/src/test/java/net/kemitix/node/ImmutableNodeItemTest.java
new file mode 100644
index 0000000..9475ff5
--- /dev/null
+++ b/src/test/java/net/kemitix/node/ImmutableNodeItemTest.java
@@ -0,0 +1,449 @@
+package net.kemitix.node;
+
+import lombok.val;
+import org.assertj.core.api.SoftAssertions;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Optional;
+
+/**
+ * Test for {@link ImmutableNodeItem}.
+ *
+ * @author pcampbell
+ */
+public class ImmutableNodeItemTest {
+
+ private static final String IMMUTABLE_OBJECT = "Immutable object";
+
+ @Rule
+ public ExpectedException exception = ExpectedException.none();
+
+ private Node immutableNode;
+
+ private void expectImmutableException() {
+ exception.expect(UnsupportedOperationException.class);
+ exception.expectMessage(IMMUTABLE_OBJECT);
+ }
+
+ @Test
+ public void getDataReturnsData() {
+ //given
+ val data = "this immutableNode data";
+ //when
+ immutableNode = Nodes.asImmutable(Nodes.unnamedRoot(data));
+ //then
+ assertThat(immutableNode.getData()).as(
+ "can get the data from a immutableNode").
+ contains(data);
+ }
+
+ @Test
+ public void canCreateAnEmptyAndUnnamedNode() {
+ //when
+ immutableNode = Nodes.asImmutable(Nodes.unnamedRoot(null));
+ //then
+ SoftAssertions softly = new SoftAssertions();
+ softly.assertThat(immutableNode.isEmpty())
+ .as("immutableNode is empty")
+ .isTrue();
+ softly.assertThat(immutableNode.isNamed())
+ .as("immutableNode is unnamed")
+ .isFalse();
+ softly.assertAll();
+ }
+
+ @Test
+ public void shouldThrowExceptionOnSetName() {
+ //given
+ immutableNode = Nodes.asImmutable(Nodes.unnamedRoot(null));
+ expectImmutableException();
+ //when
+ immutableNode.setName("named");
+ }
+
+ @Test
+ public void rootNodeShouldHaveNoParent() {
+ //given
+ immutableNode = Nodes.asImmutable(Nodes.unnamedRoot("data"));
+ //then
+ assertThat(immutableNode.getParent()).as(
+ "immutableNode created without a parent has no parent")
+ .isEmpty();
+ }
+
+ @Test
+ public void shouldContainImmutableCopyOfChild() {
+ //given
+ val parent = Nodes.unnamedRoot("root");
+ val child = Nodes.namedChild("child", "child", parent);
+ //when
+ immutableNode = Nodes.asImmutable(parent);
+ //then
+ val immutableChild = immutableNode.getChildByName("child");
+ assertThat(immutableChild).isNotSameAs(child);
+ assertThat(immutableChild.getName()).isEqualTo("child");
+ }
+
+ @Test
+ public void childShouldHaveImmutableParent() {
+ //given
+ val parent = Nodes.namedRoot("parent", "root");
+ Nodes.namedChild("subject", "child", parent);
+ //when
+ immutableNode = Nodes.asImmutable(parent);
+ //then
+ // get the immutable node's child's parent
+ val immutableChild = immutableNode.getChildByName("child");
+ final Optional> optionalParent
+ = immutableChild.getParent();
+ if (optionalParent.isPresent()) {
+ val p = optionalParent.get();
+ assertThat(p).hasFieldOrPropertyWithValue("name", "root")
+ .hasFieldOrPropertyWithValue("data",
+ Optional.of("parent"));
+ }
+ }
+
+ @Test
+ public void shouldNotBeAbleToAddChildToImmutableTree() {
+ //given
+ immutableNode = Nodes.asImmutable(Nodes.unnamedRoot("root"));
+ expectImmutableException();
+ //when
+ Nodes.unnamedChild("child", immutableNode);
+ }
+
+ @Test
+ public void shouldThrowExceptionWhenSetParent() {
+ //given
+ immutableNode = Nodes.asImmutable(Nodes.unnamedRoot("subject"));
+ expectImmutableException();
+ //when
+ immutableNode.setParent(null);
+ }
+
+ @Test
+ public void shouldThrowExceptionWhenAddingChild() {
+ //given
+ immutableNode = Nodes.asImmutable(Nodes.unnamedRoot("subject"));
+ expectImmutableException();
+ //when
+ immutableNode.addChild(Nodes.unnamedRoot("child"));
+ }
+
+ /**
+ * Test that we can walk a tree to the target node.
+ */
+ @Test
+ public void shouldWalkTreeToNode() {
+ //given
+ val root = Nodes.unnamedRoot("root");
+ Nodes.namedChild("child", "child", Nodes.unnamedChild("parent", root));
+ immutableNode = Nodes.asImmutable(root);
+ //when
+ val result = immutableNode.findInPath(Arrays.asList("parent", "child"));
+ //then
+ assertThat(result.isPresent()).isTrue();
+ if (result.isPresent()) {
+ assertThat(result.get().getName()).isEqualTo("child");
+ }
+ }
+
+ /**
+ * Test that we get an empty {@link Optional} when walking a path that
+ * doesn't exist.
+ */
+ @Test
+ public void shouldNotFindNonExistentChildNode() {
+ //given
+ val root = Nodes.unnamedRoot("root");
+ Nodes.unnamedChild("child", Nodes.unnamedChild("parent", root));
+ immutableNode = Nodes.asImmutable(root);
+ //when
+ val result = immutableNode.findInPath(
+ Arrays.asList("parent", "no child"));
+ //then
+ assertThat(result.isPresent()).isFalse();
+ }
+
+ /**
+ * Test that when we pass null we get an exception.
+ */
+ @Test
+ public void shouldThrowNEWhenWalkTreeNull() {
+ //given
+ immutableNode = Nodes.asImmutable(Nodes.unnamedRoot("subject"));
+ exception.expect(NullPointerException.class);
+ exception.expectMessage("path");
+ //when
+ immutableNode.findInPath(null);
+ }
+
+ /**
+ * Test that when we pass an empty path we get and empty {@link Optional} as
+ * a result.
+ */
+ @Test
+ public void shouldReturnEmptyForEmptyWalkTreePath() {
+ //given
+ immutableNode = Nodes.asImmutable(Nodes.unnamedRoot("subject"));
+ //when
+ val result = immutableNode.findInPath(Collections.emptyList());
+ //then
+ assertThat(result).isEmpty();
+ }
+
+ /**
+ * Test that we can find a child of a immutableNode.
+ */
+ @Test
+ public void shouldFindExistingChildNode() {
+ //given
+ val root = Nodes.unnamedRoot("root");
+ Nodes.unnamedChild("child", root);
+ immutableNode = Nodes.asImmutable(root);
+ //when
+ val result = immutableNode.findChild("child");
+ //then
+ assertThat(result.isPresent()).isTrue();
+ if (result.isPresent()) {
+ assertThat(result.get().getData()).contains("child");
+ }
+ }
+
+ /**
+ * Test that if we pass null we get an exception.
+ */
+ @Test
+ public void findOrCreateChildShouldThrowNPEFWhenChildIsNull() {
+ //given
+ immutableNode = Nodes.asImmutable(Nodes.unnamedRoot("subject"));
+ exception.expect(NullPointerException.class);
+ exception.expectMessage("child");
+ //when
+ immutableNode.findOrCreateChild(null);
+ }
+
+ /**
+ * Test that we throw an exception when passed null.
+ */
+ @Test
+ public void getChildShouldThrowNPEWhenThereIsNoChild() {
+ //given
+ immutableNode = Nodes.asImmutable(Nodes.unnamedRoot("data"));
+ exception.expect(NullPointerException.class);
+ exception.expectMessage("child");
+ //when
+ immutableNode.findChild(null);
+ }
+
+ @Test
+ public void getChildNamedFindsChild() {
+ //given
+ val root = Nodes.namedRoot("root data", "root");
+ val alpha = Nodes.namedRoot("alpha data", "alpha");
+ val beta = Nodes.namedRoot("beta data", "beta");
+ root.addChild(alpha);
+ root.addChild(beta);
+ immutableNode = Nodes.asImmutable(root);
+ //when
+ val result = immutableNode.getChildByName("alpha");
+ //then
+ assertThat(result.getName()).isEqualTo(alpha.getName());
+ }
+
+ @Test
+ public void getChildNamedFindsNothing() {
+ //given
+ val root = Nodes.namedRoot("root data", "root");
+ val alpha = Nodes.namedRoot("alpha data", "alpha");
+ val beta = Nodes.namedRoot("beta data", "beta");
+ root.addChild(alpha);
+ root.addChild(beta);
+ exception.expect(NodeException.class);
+ exception.expectMessage("Named child not found");
+ immutableNode = Nodes.asImmutable(root);
+ //when
+ immutableNode.getChildByName("gamma");
+ }
+
+ @Test
+ public void removingParentThrowsException() {
+ //given
+ immutableNode = Nodes.asImmutable(Nodes.unnamedRoot(null));
+ expectImmutableException();
+ //when
+ immutableNode.removeParent();
+ }
+
+ @Test
+ public void findChildNamedShouldThrowNPEWhenNameIsNull() {
+ //given
+ exception.expect(NullPointerException.class);
+ exception.expectMessage("name");
+ immutableNode = Nodes.asImmutable(Nodes.unnamedRoot(null));
+ //when
+ immutableNode.findChildByName(null);
+ }
+
+ @Test
+ public void isNamedNull() {
+ //given
+ immutableNode = Nodes.asImmutable(Nodes.unnamedRoot(null));
+ //then
+ assertThat(immutableNode.isNamed()).isFalse();
+ }
+
+ @Test
+ public void isNamedEmpty() {
+ //given
+ immutableNode = Nodes.asImmutable(Nodes.namedRoot(null, ""));
+ //then
+ assertThat(immutableNode.isNamed()).isFalse();
+ }
+
+ @Test
+ public void isNamedNamed() {
+ //given
+ immutableNode = Nodes.asImmutable(Nodes.namedRoot(null, "named"));
+ //then
+ assertThat(immutableNode.isNamed()).isTrue();
+ }
+
+ @Test
+ public void removeChildThrowsExceptions() {
+ //given
+ immutableNode = Nodes.asImmutable(Nodes.unnamedRoot(null));
+ expectImmutableException();
+ //then
+ immutableNode.removeChild(null);
+ }
+
+ @Test
+ public void drawTreeIsCorrect() {
+ //given
+ val root = Nodes.namedRoot("root data", "root");
+ val bob = Nodes.namedChild("bob data", "bob", root);
+ val alice = Nodes.namedChild("alice data", "alice", root);
+ Nodes.namedChild("dave data", "dave", alice);
+ Nodes.unnamedChild("bob's child's data",
+ bob); // has no name and no children so no included
+ val kim = Nodes.unnamedChild("kim data", root); // nameless mother
+ Nodes.namedChild("lucy data", "lucy", kim);
+ immutableNode = Nodes.asImmutable(root);
+ //when
+ val tree = immutableNode.drawTree(0);
+ //then
+ String[] lines = tree.split("\n");
+ assertThat(lines).contains("[root]", "[ alice]", "[ dave]",
+ "[ (unnamed)]", "[ lucy]", "[ bob]");
+ assertThat(lines).containsSubsequence("[root]", "[ alice]", "[ dave]");
+ assertThat(lines).containsSubsequence("[root]", "[ (unnamed)]",
+ "[ lucy]");
+ assertThat(lines).containsSubsequence("[root]", "[ bob]");
+ }
+
+ @Test
+ public void setDataShouldThrowException() {
+ //given
+ immutableNode = Nodes.asImmutable(Nodes.unnamedRoot("initial"));
+ expectImmutableException();
+ //when
+ immutableNode.setData("updated");
+ }
+
+ @Test
+ public void createChildThrowsException() {
+ //given
+ immutableNode = Nodes.asImmutable(Nodes.unnamedRoot(null));
+ expectImmutableException();
+ //when
+ immutableNode.createChild("child data", "child name");
+ }
+
+ @Test
+ public void canGetChildWhenFound() {
+ //given
+ val root = Nodes.unnamedRoot("data");
+ val child = Nodes.namedChild("child data", "child name", root);
+ immutableNode = Nodes.asImmutable(root);
+ //when
+ val found = immutableNode.getChild("child data");
+ //then
+ assertThat(found.getName()).isEqualTo(child.getName());
+ }
+
+ @Test
+ public void canGetChildWhenNotFound() {
+ //given
+ exception.expect(NodeException.class);
+ exception.expectMessage("Child not found");
+ immutableNode = Nodes.asImmutable(Nodes.unnamedRoot("data"));
+ //when
+ immutableNode.getChild("child data");
+ }
+
+ @Test
+ public void canSafelyHandleFindChildWhenAChildHasNoData() {
+ //given
+ val root = Nodes.unnamedRoot("");
+ Nodes.unnamedChild(null, root);
+ immutableNode = Nodes.asImmutable(root);
+ //when
+ immutableNode.findChild("data");
+ }
+
+ @Test
+ public void createChildShouldThrowException() {
+ //given
+ immutableNode = Nodes.asImmutable(Nodes.unnamedRoot(""));
+ expectImmutableException();
+ //when
+ immutableNode.createChild("child");
+ }
+
+ @Test
+ public void createDescendantLineShouldThrowException() {
+ //given
+ immutableNode = Nodes.asImmutable(Nodes.unnamedRoot(""));
+ expectImmutableException();
+ //when
+ immutableNode.createDescendantLine(
+ Arrays.asList("child", "grandchild"));
+ }
+
+ @Test
+ public void insertInPathShouldThrowException() {
+ //given
+ immutableNode = Nodes.asImmutable(Nodes.unnamedRoot(""));
+ expectImmutableException();
+ //when
+ immutableNode.insertInPath(null, "");
+ }
+
+ @Test
+ public void findOrCreateChildShouldReturnChildWhenChildIsFound() {
+ //given
+ val root = Nodes.unnamedRoot("");
+ Nodes.namedChild("child", "child", root);
+ immutableNode = Nodes.asImmutable(root);
+ //when
+ val found = immutableNode.findOrCreateChild("child");
+ assertThat(found).extracting(Node::getName).contains("child");
+ }
+
+ @Test
+ public void findOrCreateChildShouldThrowExceptionWhenChildNotFound() {
+ //given
+ immutableNode = Nodes.asImmutable(Nodes.unnamedRoot(""));
+ expectImmutableException();
+ //when
+ immutableNode.findOrCreateChild("child");
+ }
+}
From f1a73366bd3dffb4e54dc8074c82568442f09430 Mon Sep 17 00:00:00 2001
From: Paul Campbell
Date: Sun, 21 Aug 2016 19:09:47 +0100
Subject: [PATCH 17/41] Nodes.asImmutable: added to create an immutable copy of
a node tree
---
src/main/java/net/kemitix/node/Nodes.java | 43 ++++++++++++++++++++++-
1 file changed, 42 insertions(+), 1 deletion(-)
diff --git a/src/main/java/net/kemitix/node/Nodes.java b/src/main/java/net/kemitix/node/Nodes.java
index 24dc50e..ecc5f39 100644
--- a/src/main/java/net/kemitix/node/Nodes.java
+++ b/src/main/java/net/kemitix/node/Nodes.java
@@ -1,5 +1,9 @@
package net.kemitix.node;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
/**
* Utility class for {@link Node} items.
*
@@ -52,7 +56,7 @@ public final class Nodes {
* Creates a new named child node.
*
* @param data the data the node will contain
- * @param name the name of the node
+ * @param name the name of the node
* @param parent the parent of the node
* @param the type of the data
*
@@ -63,4 +67,41 @@ public final class Nodes {
return new NodeItem<>(data, name, parent);
}
+ /**
+ * Creates an immutable copy of an existing node tree.
+ *
+ * @param root the root node of the source tree
+ * @param the type of the data
+ *
+ * @return the immutable copy of the tree
+ */
+ public static Node asImmutable(final Node root) {
+ if (root.getParent().isPresent()) {
+ throw new IllegalArgumentException("source must be the root node");
+ }
+ final Set> children = getImmutableChildren(root);
+ return ImmutableNodeItem.newRoot(root.getData().orElse(null),
+ root.getName(), children);
+ }
+
+ private static Set> getImmutableChildren(final Node source) {
+ return source.getChildren()
+ .stream()
+ .map(Nodes::asImmutableChild)
+ .collect(Collectors.toSet());
+ }
+
+ private static Node asImmutableChild(
+ final Node source) {
+ final Optional> sourceParent = source.getParent();
+ if (sourceParent.isPresent()) {
+ return ImmutableNodeItem.newChild(source.getData().orElse(null),
+ source.getName(), sourceParent.get(),
+ getImmutableChildren(source));
+ } else {
+ throw new IllegalArgumentException(
+ "source must not be the root node");
+ }
+ }
+
}
From 08b934a6d6f9d19e7946b7ec8e5645ecaf9168da Mon Sep 17 00:00:00 2001
From: Paul Campbell
Date: Mon, 22 Aug 2016 08:44:48 +0100
Subject: [PATCH 18/41] pom.xml: upgrade kemitix-parent to 2.0.0
---
pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pom.xml b/pom.xml
index 3071a0d..9c0e8b4 100644
--- a/pom.xml
+++ b/pom.xml
@@ -11,7 +11,7 @@
net.kemitixkemitix-parent
- 0.6.0
+ 2.0.0
From acce849819baeb3df94c12e7eb9e09c036f817c4 Mon Sep 17 00:00:00 2001
From: Paul Campbell
Date: Mon, 22 Aug 2016 08:45:00 +0100
Subject: [PATCH 19/41] checkstyle.xml: removed
---
checkstyle.xml | 192 -------------------------------------------------
1 file changed, 192 deletions(-)
delete mode 100644 checkstyle.xml
diff --git a/checkstyle.xml b/checkstyle.xml
deleted file mode 100644
index e54bdb6..0000000
--- a/checkstyle.xml
+++ /dev/null
@@ -1,192 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
From e108d61ac51c0c66eb62a575fe0acafa017b793c Mon Sep 17 00:00:00 2001
From: Paul Campbell
Date: Mon, 22 Aug 2016 08:48:31 +0100
Subject: [PATCH 20/41] pom.xml: move lombok to compile scope
---
pom.xml | 1 -
1 file changed, 1 deletion(-)
diff --git a/pom.xml b/pom.xml
index 9c0e8b4..a502cc3 100644
--- a/pom.xml
+++ b/pom.xml
@@ -50,7 +50,6 @@
org.projectlomboklombok1.16.8
- testjunit
From d99fcdcebeb8e8661af754923d5100591ca45185 Mon Sep 17 00:00:00 2001
From: Paul Campbell
Date: Mon, 22 Aug 2016 08:51:18 +0100
Subject: [PATCH 21/41] NodeItem: replace manual null checks with @NonNull
---
src/main/java/net/kemitix/node/NodeItem.java | 42 +++++---------------
1 file changed, 10 insertions(+), 32 deletions(-)
diff --git a/src/main/java/net/kemitix/node/NodeItem.java b/src/main/java/net/kemitix/node/NodeItem.java
index e688355..3f6f6bc 100644
--- a/src/main/java/net/kemitix/node/NodeItem.java
+++ b/src/main/java/net/kemitix/node/NodeItem.java
@@ -1,5 +1,7 @@
package net.kemitix.node;
+import lombok.NonNull;
+
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
@@ -128,10 +130,7 @@ class NodeItem implements Node {
* @param child the node to add
*/
@Override
- public void addChild(final Node child) {
- if (child == null) {
- throw new NullPointerException("child");
- }
+ public void addChild(@NonNull final Node child) {
if (this.equals(child) || isDescendantOf(child)) {
throw new NodeException("Child is an ancestor");
}
@@ -162,10 +161,7 @@ class NodeItem implements Node {
* @return the new child node
*/
@Override
- public Node createChild(final T child) {
- if (child == null) {
- throw new NullPointerException("child");
- }
+ public Node createChild(@NonNull final T child) {
return new NodeItem<>(child, this);
}
@@ -184,10 +180,7 @@ class NodeItem implements Node {
* @param descendants the line of descendants from the current node
*/
@Override
- public void createDescendantLine(final List descendants) {
- if (descendants == null) {
- throw new NullPointerException("descendants");
- }
+ public void createDescendantLine(@NonNull final List descendants) {
if (!descendants.isEmpty()) {
findOrCreateChild(descendants.get(0)).createDescendantLine(
descendants.subList(1, descendants.size()));
@@ -204,10 +197,7 @@ class NodeItem implements Node {
*/
@Override
@Deprecated
- public Node findOrCreateChild(final T child) {
- if (child == null) {
- throw new NullPointerException("child");
- }
+ public Node findOrCreateChild(@NonNull final T child) {
return findChild(child).orElseGet(() -> createChild(child));
}
@@ -219,10 +209,7 @@ class NodeItem implements Node {
* @return an {@link Optional} containing the child node if found
*/
@Override
- public Optional> findChild(final T child) {
- if (child == null) {
- throw new NullPointerException("child");
- }
+ public Optional> findChild(@NonNull final T child) {
return children.stream().filter(node -> {
final Optional d = node.getData();
return d.isPresent() && d.get().equals(child);
@@ -257,10 +244,7 @@ class NodeItem implements Node {
* @param parent the new parent node
*/
@Override
- public final void setParent(final Node parent) {
- if (parent == null) {
- throw new NullPointerException("parent");
- }
+ public final void setParent(@NonNull final Node parent) {
if (this.equals(parent) || parent.isDescendantOf(this)) {
throw new NodeException("Parent is a descendant");
}
@@ -279,10 +263,7 @@ class NodeItem implements Node {
* @return the child or null
*/
@Override
- public Optional> findInPath(final List path) {
- if (path == null) {
- throw new NullPointerException("path");
- }
+ public Optional> findInPath(@NonNull final List path) {
if (path.size() > 0) {
Optional> found = findChild(path.get(0));
if (found.isPresent()) {
@@ -330,10 +311,7 @@ class NodeItem implements Node {
}
@Override
- public Optional> findChildByName(final String named) {
- if (named == null) {
- throw new NullPointerException("name");
- }
+ public Optional> findChildByName(@NonNull final String named) {
return children.stream()
.filter((Node t) -> t.getName().equals(named))
.findAny();
From b2130442e50a67f7bd58e8e7b8c702ad58acb713 Mon Sep 17 00:00:00 2001
From: Paul Campbell
Date: Mon, 22 Aug 2016 08:52:42 +0100
Subject: [PATCH 22/41] AbstractNodeItem: replace manual null checks with
@NonNull
---
.../java/net/kemitix/node/AbstractNodeItem.java | 17 +++++------------
1 file changed, 5 insertions(+), 12 deletions(-)
diff --git a/src/main/java/net/kemitix/node/AbstractNodeItem.java b/src/main/java/net/kemitix/node/AbstractNodeItem.java
index 6aa90e7..4815d93 100644
--- a/src/main/java/net/kemitix/node/AbstractNodeItem.java
+++ b/src/main/java/net/kemitix/node/AbstractNodeItem.java
@@ -1,5 +1,7 @@
package net.kemitix.node;
+import lombok.NonNull;
+
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
@@ -65,10 +67,7 @@ abstract class AbstractNodeItem implements Node {
* @return an {@link Optional} containing the child node if found
*/
@Override
- public Optional> findChild(final T child) {
- if (child == null) {
- throw new NullPointerException("child");
- }
+ public Optional> findChild(@NonNull final T child) {
return children.stream().filter(node -> {
final Optional d = node.getData();
return d.isPresent() && d.get().equals(child);
@@ -102,10 +101,7 @@ abstract class AbstractNodeItem implements Node {
* @return the child or null
*/
@Override
- public Optional> findInPath(final List path) {
- if (path == null) {
- throw new NullPointerException("path");
- }
+ public Optional> findInPath(@NonNull final List path) {
if (path.size() > 0) {
Optional> found = findChild(path.get(0));
if (found.isPresent()) {
@@ -119,10 +115,7 @@ abstract class AbstractNodeItem implements Node {
}
@Override
- public Optional> findChildByName(final String named) {
- if (named == null) {
- throw new NullPointerException("name");
- }
+ public Optional> findChildByName(@NonNull final String named) {
return children.stream()
.filter(n -> n.getName().equals(named))
.findAny();
From 2a435a849e74add511897a2545ccf37433ab368d Mon Sep 17 00:00:00 2001
From: Paul Campbell
Date: Mon, 22 Aug 2016 08:58:58 +0100
Subject: [PATCH 23/41] NodeItem: refactor formatting name by depth
Remove duplicate string literals.
---
src/main/java/net/kemitix/node/NodeItem.java | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/src/main/java/net/kemitix/node/NodeItem.java b/src/main/java/net/kemitix/node/NodeItem.java
index 3f6f6bc..3afd79a 100644
--- a/src/main/java/net/kemitix/node/NodeItem.java
+++ b/src/main/java/net/kemitix/node/NodeItem.java
@@ -331,17 +331,18 @@ class NodeItem implements Node {
final StringBuilder sb = new StringBuilder();
final String unnamed = "(unnamed)";
if (isNamed()) {
- sb.append(String.format("[%1$" + (depth + name.length()) + "s]\n",
- name));
+ sb.append(formatByDepth(name, depth));
} else if (!children.isEmpty()) {
- sb.append(
- String.format("[%1$" + (depth + unnamed.length()) + "s]\n",
- unnamed));
+ sb.append(formatByDepth(unnamed, depth));
}
getChildren().stream().forEach(c -> sb.append(c.drawTree(depth + 1)));
return sb.toString();
}
+ private String formatByDepth(final String value, final int depth) {
+ return String.format("[%1$" + (depth + value.length()) + "s]\n", value);
+ }
+
@Override
public boolean isNamed() {
String currentName = getName();
From 6253a3226f6d7bd1a6f9559a215a762a5bc741b5 Mon Sep 17 00:00:00 2001
From: Paul Campbell
Date: Mon, 22 Aug 2016 08:59:52 +0100
Subject: [PATCH 24/41] NodeItem: simplify stream().forEach()
---
src/main/java/net/kemitix/node/NodeItem.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/main/java/net/kemitix/node/NodeItem.java b/src/main/java/net/kemitix/node/NodeItem.java
index 3afd79a..caa23e0 100644
--- a/src/main/java/net/kemitix/node/NodeItem.java
+++ b/src/main/java/net/kemitix/node/NodeItem.java
@@ -335,7 +335,7 @@ class NodeItem implements Node {
} else if (!children.isEmpty()) {
sb.append(formatByDepth(unnamed, depth));
}
- getChildren().stream().forEach(c -> sb.append(c.drawTree(depth + 1)));
+ getChildren().forEach(c -> sb.append(c.drawTree(depth + 1)));
return sb.toString();
}
From 3d94eaeb32647c8360d4185809a15b6384220ed2 Mon Sep 17 00:00:00 2001
From: Paul Campbell
Date: Mon, 22 Aug 2016 09:01:34 +0100
Subject: [PATCH 25/41] AbstractNodeItem: refactor formatting name by depth
Remove duplicate string literals.
---
src/main/java/net/kemitix/node/AbstractNodeItem.java | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/src/main/java/net/kemitix/node/AbstractNodeItem.java b/src/main/java/net/kemitix/node/AbstractNodeItem.java
index 4815d93..f177c73 100644
--- a/src/main/java/net/kemitix/node/AbstractNodeItem.java
+++ b/src/main/java/net/kemitix/node/AbstractNodeItem.java
@@ -132,17 +132,18 @@ abstract class AbstractNodeItem implements Node {
final StringBuilder sb = new StringBuilder();
final String unnamed = "(unnamed)";
if (isNamed()) {
- sb.append(String.format("[%1$" + (depth + name.length()) + "s]\n",
- name));
+ sb.append(formatByDepth(name, depth));
} else if (!children.isEmpty()) {
- sb.append(
- String.format("[%1$" + (depth + unnamed.length()) + "s]\n",
- unnamed));
+ sb.append(formatByDepth(unnamed, depth));
}
getChildren().forEach(c -> sb.append(c.drawTree(depth + 1)));
return sb.toString();
}
+ private String formatByDepth(final String value, final int depth) {
+ return String.format("[%1$" + (depth + value.length()) + "s]\n", value);
+ }
+
@Override
public boolean isNamed() {
return name != null && name.length() > 0;
From 5c129b54b8af4a127ef3eb30b7b209f97990ada2 Mon Sep 17 00:00:00 2001
From: Paul Campbell
Date: Mon, 22 Aug 2016 09:04:16 +0100
Subject: [PATCH 26/41] NodeItem: put trailing comments on their own line
---
src/main/java/net/kemitix/node/NodeItem.java | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/src/main/java/net/kemitix/node/NodeItem.java b/src/main/java/net/kemitix/node/NodeItem.java
index caa23e0..e5db2fd 100644
--- a/src/main/java/net/kemitix/node/NodeItem.java
+++ b/src/main/java/net/kemitix/node/NodeItem.java
@@ -279,13 +279,15 @@ class NodeItem implements Node {
@Override
public void insertInPath(final Node nodeItem, final String... path) {
if (path.length == 0) {
- if (!nodeItem.isNamed()) { // nothing to conflict with
+ if (!nodeItem.isNamed()) {
+ // nothing to conflict with
addChild(nodeItem);
return;
}
String nodeName = nodeItem.getName();
final Optional> childNamed = findChildByName(nodeName);
- if (!childNamed.isPresent()) { // nothing with the same name exists
+ if (!childNamed.isPresent()) {
+ // nothing with the same name exists
addChild(nodeItem);
return;
}
From 37247e93bc28ae8b0a36df8a7c1797c548192121 Mon Sep 17 00:00:00 2001
From: Paul Campbell
Date: Sun, 4 Sep 2016 18:44:29 +0100
Subject: [PATCH 27/41] node: change javadoc element order to Atclause order
The default configuration of the AtclauseOrder is a bit strange.
---
src/main/java/net/kemitix/node/AbstractNodeItem.java | 4 ++--
src/main/java/net/kemitix/node/ImmutableNodeItem.java | 4 ++--
src/main/java/net/kemitix/node/Node.java | 4 ++--
src/main/java/net/kemitix/node/NodeItem.java | 4 ++--
4 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/src/main/java/net/kemitix/node/AbstractNodeItem.java b/src/main/java/net/kemitix/node/AbstractNodeItem.java
index f177c73..f7efaf9 100644
--- a/src/main/java/net/kemitix/node/AbstractNodeItem.java
+++ b/src/main/java/net/kemitix/node/AbstractNodeItem.java
@@ -11,9 +11,9 @@ import java.util.Set;
* An abstract node item, providing default implementations for most read-only
* operations.
*
- * @param the type of data stored in each node
+ * @author Paul Campbell
*
- * @author pcampbell
+ * @param the type of data stored in each node
*/
abstract class AbstractNodeItem implements Node {
diff --git a/src/main/java/net/kemitix/node/ImmutableNodeItem.java b/src/main/java/net/kemitix/node/ImmutableNodeItem.java
index 7b80a59..dbc85ec 100644
--- a/src/main/java/net/kemitix/node/ImmutableNodeItem.java
+++ b/src/main/java/net/kemitix/node/ImmutableNodeItem.java
@@ -11,9 +11,9 @@ import java.util.Set;
* getData()} they could then modify the original data within the node. This
* wouldn't affect the integrity of the node tree structure, however.
*
- * @param the type of data stored in each node
+ * @author Paul Campbell
*
- * @author pcampbell
+ * @param the type of data stored in each node
*/
final class ImmutableNodeItem extends AbstractNodeItem {
diff --git a/src/main/java/net/kemitix/node/Node.java b/src/main/java/net/kemitix/node/Node.java
index 7cda730..f1fa7ba 100644
--- a/src/main/java/net/kemitix/node/Node.java
+++ b/src/main/java/net/kemitix/node/Node.java
@@ -7,9 +7,9 @@ import java.util.Set;
/**
* An interface for tree node items.
*
- * @param the type of data held in each node
+ * @author Paul Campbell
*
- * @author pcampbell
+ * @param the type of data held in each node
*/
public interface Node {
diff --git a/src/main/java/net/kemitix/node/NodeItem.java b/src/main/java/net/kemitix/node/NodeItem.java
index e5db2fd..812adf4 100644
--- a/src/main/java/net/kemitix/node/NodeItem.java
+++ b/src/main/java/net/kemitix/node/NodeItem.java
@@ -12,9 +12,9 @@ import java.util.function.Function;
/**
* Represents a tree of nodes.
*
- * @param the type of data stored in each node
+ * @author Paul Campbell
*
- * @author pcampbell
+ * @param the type of data stored in each node
*/
class NodeItem implements Node {
From 9f3aec202af31a3ef83f59da503e3ac0de099c40 Mon Sep 17 00:00:00 2001
From: Paul Campbell
Date: Sun, 4 Sep 2016 18:59:22 +0100
Subject: [PATCH 28/41] {Abstract}NodeItem: reduce nested if statements
---
.../net/kemitix/node/AbstractNodeItem.java | 15 ++++-----
src/main/java/net/kemitix/node/NodeItem.java | 31 +++++++++----------
2 files changed, 22 insertions(+), 24 deletions(-)
diff --git a/src/main/java/net/kemitix/node/AbstractNodeItem.java b/src/main/java/net/kemitix/node/AbstractNodeItem.java
index f7efaf9..3097d4a 100644
--- a/src/main/java/net/kemitix/node/AbstractNodeItem.java
+++ b/src/main/java/net/kemitix/node/AbstractNodeItem.java
@@ -102,14 +102,15 @@ abstract class AbstractNodeItem implements Node {
*/
@Override
public Optional> findInPath(@NonNull final List path) {
- if (path.size() > 0) {
- Optional> found = findChild(path.get(0));
- if (found.isPresent()) {
- if (path.size() > 1) {
- return found.get().findInPath(path.subList(1, path.size()));
- }
- return found;
+ if (path.isEmpty()) {
+ return Optional.empty();
+ }
+ Optional> found = findChild(path.get(0));
+ if (found.isPresent()) {
+ if (path.size() > 1) {
+ return found.get().findInPath(path.subList(1, path.size()));
}
+ return found;
}
return Optional.empty();
}
diff --git a/src/main/java/net/kemitix/node/NodeItem.java b/src/main/java/net/kemitix/node/NodeItem.java
index 812adf4..2ed9b02 100644
--- a/src/main/java/net/kemitix/node/NodeItem.java
+++ b/src/main/java/net/kemitix/node/NodeItem.java
@@ -2,19 +2,15 @@ package net.kemitix.node;
import lombok.NonNull;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Optional;
-import java.util.Set;
+import java.util.*;
import java.util.function.Function;
/**
* Represents a tree of nodes.
*
- * @author Paul Campbell
- *
* @param the type of data stored in each node
+ *
+ * @author Paul Campbell
*/
class NodeItem implements Node {
@@ -147,7 +143,7 @@ class NodeItem implements Node {
Optional> childParent = child.getParent();
boolean isOrphan = !childParent.isPresent();
boolean hasDifferentParent = !isOrphan && !childParent.get()
- .equals(this);
+ .equals(this);
if (isOrphan || hasDifferentParent) {
child.setParent(this);
}
@@ -264,14 +260,15 @@ class NodeItem implements Node {
*/
@Override
public Optional> findInPath(@NonNull final List path) {
- if (path.size() > 0) {
- Optional> found = findChild(path.get(0));
- if (found.isPresent()) {
- if (path.size() > 1) {
- return found.get().findInPath(path.subList(1, path.size()));
- }
- return found;
+ if (path.isEmpty()) {
+ return Optional.empty();
+ }
+ Optional> found = findChild(path.get(0));
+ if (found.isPresent()) {
+ if (path.size() > 1) {
+ return found.get().findInPath(path.subList(1, path.size()));
}
+ return found;
}
return Optional.empty();
}
@@ -315,8 +312,8 @@ class NodeItem implements Node {
@Override
public Optional> findChildByName(@NonNull final String named) {
return children.stream()
- .filter((Node t) -> t.getName().equals(named))
- .findAny();
+ .filter((Node t) -> t.getName().equals(named))
+ .findAny();
}
@Override
From 02d07605c0f6e818298869b4c55d37d3d32bd98d Mon Sep 17 00:00:00 2001
From: Paul Campbell
Date: Sun, 4 Sep 2016 19:20:16 +0100
Subject: [PATCH 29/41] NodeFindInPathTestsCategory: added
---
.../net/kemitix/node/NodeFindInPathTestsCategory.java | 9 +++++++++
1 file changed, 9 insertions(+)
create mode 100644 src/test/java/net/kemitix/node/NodeFindInPathTestsCategory.java
diff --git a/src/test/java/net/kemitix/node/NodeFindInPathTestsCategory.java b/src/test/java/net/kemitix/node/NodeFindInPathTestsCategory.java
new file mode 100644
index 0000000..979190f
--- /dev/null
+++ b/src/test/java/net/kemitix/node/NodeFindInPathTestsCategory.java
@@ -0,0 +1,9 @@
+package net.kemitix.node;
+
+/**
+ * Category marker for tests relating to implementations of Node.findInPath(...).
+ *
+ * @author Paul Campbell
+ */
+public interface NodeFindInPathTestsCategory {
+}
From 6987f927fe4e29a0c8f1594e0a0558554f8b620f Mon Sep 17 00:00:00 2001
From: Paul Campbell
Date: Sun, 4 Sep 2016 19:21:18 +0100
Subject: [PATCH 30/41] {Immutable}NodeItemTest: categorise findInPath tests
---
.../java/net/kemitix/node/ImmutableNodeItemTest.java | 9 +++++++--
src/test/java/net/kemitix/node/NodeItemTest.java | 9 +++++++--
2 files changed, 14 insertions(+), 4 deletions(-)
diff --git a/src/test/java/net/kemitix/node/ImmutableNodeItemTest.java b/src/test/java/net/kemitix/node/ImmutableNodeItemTest.java
index 9475ff5..91eee8a 100644
--- a/src/test/java/net/kemitix/node/ImmutableNodeItemTest.java
+++ b/src/test/java/net/kemitix/node/ImmutableNodeItemTest.java
@@ -4,14 +4,15 @@ import lombok.val;
import org.assertj.core.api.SoftAssertions;
import org.junit.Rule;
import org.junit.Test;
+import org.junit.experimental.categories.Category;
import org.junit.rules.ExpectedException;
-import static org.assertj.core.api.Assertions.assertThat;
-
import java.util.Arrays;
import java.util.Collections;
import java.util.Optional;
+import static org.assertj.core.api.Assertions.assertThat;
+
/**
* Test for {@link ImmutableNodeItem}.
*
@@ -141,6 +142,7 @@ public class ImmutableNodeItemTest {
* Test that we can walk a tree to the target node.
*/
@Test
+ @Category(NodeFindInPathTestsCategory.class)
public void shouldWalkTreeToNode() {
//given
val root = Nodes.unnamedRoot("root");
@@ -160,6 +162,7 @@ public class ImmutableNodeItemTest {
* doesn't exist.
*/
@Test
+ @Category(NodeFindInPathTestsCategory.class)
public void shouldNotFindNonExistentChildNode() {
//given
val root = Nodes.unnamedRoot("root");
@@ -176,6 +179,7 @@ public class ImmutableNodeItemTest {
* Test that when we pass null we get an exception.
*/
@Test
+ @Category(NodeFindInPathTestsCategory.class)
public void shouldThrowNEWhenWalkTreeNull() {
//given
immutableNode = Nodes.asImmutable(Nodes.unnamedRoot("subject"));
@@ -190,6 +194,7 @@ public class ImmutableNodeItemTest {
* a result.
*/
@Test
+ @Category(NodeFindInPathTestsCategory.class)
public void shouldReturnEmptyForEmptyWalkTreePath() {
//given
immutableNode = Nodes.asImmutable(Nodes.unnamedRoot("subject"));
diff --git a/src/test/java/net/kemitix/node/NodeItemTest.java b/src/test/java/net/kemitix/node/NodeItemTest.java
index 247e7fb..2702ae0 100644
--- a/src/test/java/net/kemitix/node/NodeItemTest.java
+++ b/src/test/java/net/kemitix/node/NodeItemTest.java
@@ -4,14 +4,15 @@ import lombok.val;
import org.assertj.core.api.SoftAssertions;
import org.junit.Rule;
import org.junit.Test;
+import org.junit.experimental.categories.Category;
import org.junit.rules.ExpectedException;
-import static org.assertj.core.api.Assertions.assertThat;
-
import java.util.Arrays;
import java.util.Collections;
import java.util.Optional;
+import static org.assertj.core.api.Assertions.assertThat;
+
/**
* Test for {@link NodeItem}.
*
@@ -309,6 +310,7 @@ public class NodeItemTest {
* Test that we can walk a tree to the target node.
*/
@Test
+ @Category(NodeFindInPathTestsCategory.class)
public void shouldWalkTreeToNode() {
//given
val grandparent = "grandparent";
@@ -334,6 +336,7 @@ public class NodeItemTest {
* doesn't exist.
*/
@Test
+ @Category(NodeFindInPathTestsCategory.class)
public void shouldNotFindNonExistentChildNode() {
//given
val parent = "parent";
@@ -352,6 +355,7 @@ public class NodeItemTest {
* Test that when we pass null we get an exception.
*/
@Test
+ @Category(NodeFindInPathTestsCategory.class)
public void shouldThrowNEWhenWalkTreeNull() {
//given
node = Nodes.unnamedRoot("subject");
@@ -366,6 +370,7 @@ public class NodeItemTest {
* a result.
*/
@Test
+ @Category(NodeFindInPathTestsCategory.class)
public void shouldReturnEmptyForEmptyWalkTreePath() {
//given
node = Nodes.unnamedRoot("subject");
From 91c57f098e3feca06c2ca91d4af6c35db5838166 Mon Sep 17 00:00:00 2001
From: Paul Campbell
Date: Sun, 4 Sep 2016 19:21:41 +0100
Subject: [PATCH 31/41] {Abstract}NodeItem: rewrite findInPath to avoid
recursion
---
.../java/net/kemitix/node/AbstractNodeItem.java | 15 +++++++++------
src/main/java/net/kemitix/node/NodeItem.java | 15 +++++++++------
2 files changed, 18 insertions(+), 12 deletions(-)
diff --git a/src/main/java/net/kemitix/node/AbstractNodeItem.java b/src/main/java/net/kemitix/node/AbstractNodeItem.java
index 3097d4a..1fe6eab 100644
--- a/src/main/java/net/kemitix/node/AbstractNodeItem.java
+++ b/src/main/java/net/kemitix/node/AbstractNodeItem.java
@@ -105,14 +105,17 @@ abstract class AbstractNodeItem implements Node {
if (path.isEmpty()) {
return Optional.empty();
}
- Optional> found = findChild(path.get(0));
- if (found.isPresent()) {
- if (path.size() > 1) {
- return found.get().findInPath(path.subList(1, path.size()));
+ Node current = this;
+ for (T item : path) {
+ final Optional> child = current.findChild(item);
+ if (child.isPresent()) {
+ current = child.get();
+ } else {
+ current = null;
+ break;
}
- return found;
}
- return Optional.empty();
+ return Optional.ofNullable(current);
}
@Override
diff --git a/src/main/java/net/kemitix/node/NodeItem.java b/src/main/java/net/kemitix/node/NodeItem.java
index 2ed9b02..519867f 100644
--- a/src/main/java/net/kemitix/node/NodeItem.java
+++ b/src/main/java/net/kemitix/node/NodeItem.java
@@ -263,14 +263,17 @@ class NodeItem implements Node {
if (path.isEmpty()) {
return Optional.empty();
}
- Optional> found = findChild(path.get(0));
- if (found.isPresent()) {
- if (path.size() > 1) {
- return found.get().findInPath(path.subList(1, path.size()));
+ Node current = this;
+ for (T item : path) {
+ final Optional> child = current.findChild(item);
+ if (child.isPresent()) {
+ current = child.get();
+ } else {
+ current = null;
+ break;
}
- return found;
}
- return Optional.empty();
+ return Optional.ofNullable(current);
}
@Override
From e28b140db82465965ae188ed3c5274a97847ee04 Mon Sep 17 00:00:00 2001
From: Paul Campbell
Date: Sun, 4 Sep 2016 19:28:20 +0100
Subject: [PATCH 32/41] NodeItem: simplify inserting child into path
---
src/main/java/net/kemitix/node/NodeItem.java | 13 ++++---------
1 file changed, 4 insertions(+), 9 deletions(-)
diff --git a/src/main/java/net/kemitix/node/NodeItem.java b/src/main/java/net/kemitix/node/NodeItem.java
index 519867f..067ef5e 100644
--- a/src/main/java/net/kemitix/node/NodeItem.java
+++ b/src/main/java/net/kemitix/node/NodeItem.java
@@ -301,15 +301,10 @@ class NodeItem implements Node {
}
return;
}
- String item = path[0];
- final Optional> childNamed = findChildByName(item);
- Node child;
- if (!childNamed.isPresent()) {
- child = new NodeItem<>(null, item, this);
- } else {
- child = childNamed.get();
- }
- child.insertInPath(nodeItem, Arrays.copyOfRange(path, 1, path.length));
+ val item = path[0];
+ findChildByName(item)
+ .orElseGet(() -> new NodeItem<>(null, item, this))
+ .insertInPath(nodeItem, Arrays.copyOfRange(path, 1, path.length));
}
@Override
From 40f49fd832db73cf8fd04260e4b4d249b8334dd6 Mon Sep 17 00:00:00 2001
From: Paul Campbell
Date: Sun, 4 Sep 2016 19:40:08 +0100
Subject: [PATCH 33/41] NodeItem: refactored insertInPath to be easier to
understand
---
src/main/java/net/kemitix/node/NodeItem.java | 59 +++++++++++---------
1 file changed, 34 insertions(+), 25 deletions(-)
diff --git a/src/main/java/net/kemitix/node/NodeItem.java b/src/main/java/net/kemitix/node/NodeItem.java
index 067ef5e..6c5f241 100644
--- a/src/main/java/net/kemitix/node/NodeItem.java
+++ b/src/main/java/net/kemitix/node/NodeItem.java
@@ -1,6 +1,7 @@
package net.kemitix.node;
import lombok.NonNull;
+import lombok.val;
import java.util.*;
import java.util.function.Function;
@@ -279,32 +280,40 @@ class NodeItem implements Node {
@Override
public void insertInPath(final Node nodeItem, final String... path) {
if (path.length == 0) {
- if (!nodeItem.isNamed()) {
- // nothing to conflict with
- addChild(nodeItem);
- return;
- }
- String nodeName = nodeItem.getName();
- final Optional> childNamed = findChildByName(nodeName);
- if (!childNamed.isPresent()) {
- // nothing with the same name exists
- addChild(nodeItem);
- return;
- }
- // we have an existing node with the same name
- final Node existing = childNamed.get();
- if (!existing.isEmpty()) {
- throw new NodeException("A non-empty node named '" + nodeName
- + "' already exists here");
- } else {
- nodeItem.getData().ifPresent(existing::setData);
- }
- return;
+ insertChild(nodeItem);
+ } else {
+ val item = path[0];
+ findChildByName(item)
+ .orElseGet(() -> new NodeItem<>(null, item, this))
+ .insertInPath(nodeItem, Arrays.copyOfRange(path, 1, path.length));
+ }
+ }
+
+ private void insertChild(final Node nodeItem) {
+ if (nodeItem.isNamed()) {
+ insertNamedChild(nodeItem);
+ } else {
+ // nothing to conflict with
+ addChild(nodeItem);
+ }
+ }
+
+ private void insertNamedChild(final Node nodeItem) {
+ val childByName = findChildByName(nodeItem.getName());
+ if (childByName.isPresent()) {
+ // we have an existing node with the same name
+ val existing = childByName.get();
+ if (existing.isEmpty()) {
+ // place any data in the new node into the existing empty node
+ nodeItem.getData().ifPresent(existing::setData);
+ } else {
+ throw new NodeException("A non-empty node named '" + nodeItem.getName()
+ + "' already exists here");
+ }
+ } else {
+ // nothing with the same name exists
+ addChild(nodeItem);
}
- val item = path[0];
- findChildByName(item)
- .orElseGet(() -> new NodeItem<>(null, item, this))
- .insertInPath(nodeItem, Arrays.copyOfRange(path, 1, path.length));
}
@Override
From 1da9d44a8b70f879bb17b35c242b2d7d645c066d Mon Sep 17 00:00:00 2001
From: Paul Campbell
Date: Sun, 4 Sep 2016 19:45:16 +0100
Subject: [PATCH 34/41] Node: add javadoc @deprecated to findOrCreateChild
---
src/main/java/net/kemitix/node/Node.java | 3 +++
src/main/java/net/kemitix/node/NodeItem.java | 3 +++
2 files changed, 6 insertions(+)
diff --git a/src/main/java/net/kemitix/node/Node.java b/src/main/java/net/kemitix/node/Node.java
index f1fa7ba..395ebb7 100644
--- a/src/main/java/net/kemitix/node/Node.java
+++ b/src/main/java/net/kemitix/node/Node.java
@@ -113,6 +113,9 @@ public interface Node {
* @param child the child's data to search or create with
*
* @return the found or created child node
+ *
+ * @deprecated use node.findChild(child).orElseGet(() ->
+ * node.createChild(child));
*/
@Deprecated
Node findOrCreateChild(T child);
diff --git a/src/main/java/net/kemitix/node/NodeItem.java b/src/main/java/net/kemitix/node/NodeItem.java
index 6c5f241..c41a3d7 100644
--- a/src/main/java/net/kemitix/node/NodeItem.java
+++ b/src/main/java/net/kemitix/node/NodeItem.java
@@ -191,6 +191,9 @@ class NodeItem implements Node {
* @param child the child's data to search or create with
*
* @return the found or created child node
+ *
+ * @deprecated use node.findChild(child).orElseGet(() -> node.createChild
+ * (child));
*/
@Override
@Deprecated
From cd1afc6778b071fb3faba39d761f518d2ff66c20 Mon Sep 17 00:00:00 2001
From: Paul Campbell
Date: Sun, 4 Sep 2016 19:46:30 +0100
Subject: [PATCH 35/41] NodeItem: avoid import .*
---
src/main/java/net/kemitix/node/NodeItem.java | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/src/main/java/net/kemitix/node/NodeItem.java b/src/main/java/net/kemitix/node/NodeItem.java
index c41a3d7..7f0cd75 100644
--- a/src/main/java/net/kemitix/node/NodeItem.java
+++ b/src/main/java/net/kemitix/node/NodeItem.java
@@ -3,7 +3,11 @@ package net.kemitix.node;
import lombok.NonNull;
import lombok.val;
-import java.util.*;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
import java.util.function.Function;
/**
From e4c4fdf4bc2bb12cfeeddc6002a1b992855ef5ab Mon Sep 17 00:00:00 2001
From: Paul Campbell
Date: Sun, 4 Sep 2016 19:47:19 +0100
Subject: [PATCH 36/41] NodeItem,: use strange javadoc element ordering
---
src/main/java/net/kemitix/node/NodeItem.java | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/main/java/net/kemitix/node/NodeItem.java b/src/main/java/net/kemitix/node/NodeItem.java
index 7f0cd75..2ee8b97 100644
--- a/src/main/java/net/kemitix/node/NodeItem.java
+++ b/src/main/java/net/kemitix/node/NodeItem.java
@@ -13,9 +13,9 @@ import java.util.function.Function;
/**
* Represents a tree of nodes.
*
- * @param the type of data stored in each node
- *
* @author Paul Campbell
+ *
+ * @param the type of data stored in each node
*/
class NodeItem implements Node {
From 9ec2668802d34aed90d84384e31a3256b57e5839 Mon Sep 17 00:00:00 2001
From: Paul Campbell
Date: Sun, 4 Sep 2016 19:49:13 +0100
Subject: [PATCH 37/41] NodeItem: wrap lines at 80 columns
---
src/main/java/net/kemitix/node/NodeItem.java | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/src/main/java/net/kemitix/node/NodeItem.java b/src/main/java/net/kemitix/node/NodeItem.java
index 2ee8b97..979fddd 100644
--- a/src/main/java/net/kemitix/node/NodeItem.java
+++ b/src/main/java/net/kemitix/node/NodeItem.java
@@ -292,7 +292,8 @@ class NodeItem implements Node {
val item = path[0];
findChildByName(item)
.orElseGet(() -> new NodeItem<>(null, item, this))
- .insertInPath(nodeItem, Arrays.copyOfRange(path, 1, path.length));
+ .insertInPath(nodeItem,
+ Arrays.copyOfRange(path, 1, path.length));
}
}
@@ -314,8 +315,8 @@ class NodeItem implements Node {
// place any data in the new node into the existing empty node
nodeItem.getData().ifPresent(existing::setData);
} else {
- throw new NodeException("A non-empty node named '" + nodeItem.getName()
- + "' already exists here");
+ throw new NodeException("A non-empty node named '"
+ + nodeItem.getName() + "' already exists here");
}
} else {
// nothing with the same name exists
From 69be86ba074ccc8408d87185bf3baeb05bee5d8c Mon Sep 17 00:00:00 2001
From: Paul Campbell
Date: Sun, 4 Sep 2016 22:13:10 +0100
Subject: [PATCH 38/41] NodeItem: reduce complexity of addChild
---
src/main/java/net/kemitix/node/NodeItem.java | 38 +++++++++++---------
1 file changed, 22 insertions(+), 16 deletions(-)
diff --git a/src/main/java/net/kemitix/node/NodeItem.java b/src/main/java/net/kemitix/node/NodeItem.java
index 979fddd..f9ebf47 100644
--- a/src/main/java/net/kemitix/node/NodeItem.java
+++ b/src/main/java/net/kemitix/node/NodeItem.java
@@ -132,28 +132,34 @@ class NodeItem implements Node {
*/
@Override
public void addChild(@NonNull final Node child) {
- if (this.equals(child) || isDescendantOf(child)) {
- throw new NodeException("Child is an ancestor");
- }
- if (child.isNamed()) {
- final Optional> existingChild = findChildByName(
- child.getName());
- if (existingChild.isPresent() && existingChild.get() != child) {
- throw new NodeException(
- "Node with that name already exists here");
- }
- }
+ verifyChildIsNotAnAncestor(child);
+ verifyChildWithSameNameDoesNotAlreadyExist(child);
children.add(child);
// update the child's parent if they don't have one or it is not this
- Optional> childParent = child.getParent();
- boolean isOrphan = !childParent.isPresent();
- boolean hasDifferentParent = !isOrphan && !childParent.get()
- .equals(this);
- if (isOrphan || hasDifferentParent) {
+ val childParent = child.getParent();
+ if (!childParent.isPresent() || !childParent.get().equals(this)) {
child.setParent(this);
}
}
+ private void verifyChildWithSameNameDoesNotAlreadyExist(
+ final @NonNull Node child) {
+ if (child.isNamed()) {
+ findChildByName(child.getName())
+ .filter(existingChild -> existingChild != child)
+ .ifPresent(existingChild -> {
+ throw new NodeException(
+ "Node with that name already exists here");
+ });
+ }
+ }
+
+ private void verifyChildIsNotAnAncestor(final @NonNull Node child) {
+ if (this.equals(child) || isDescendantOf(child)) {
+ throw new NodeException("Child is an ancestor");
+ }
+ }
+
/**
* Creates a new node and adds it as a child of the current node.
*
From 29a5ceca827430896bab8952961100d1c9dd88d5 Mon Sep 17 00:00:00 2001
From: Paul Campbell
Date: Sun, 4 Sep 2016 22:19:22 +0100
Subject: [PATCH 39/41] NodeItem: remove support for dynamic names
---
src/main/java/net/kemitix/node/NodeItem.java | 26 --------------------
1 file changed, 26 deletions(-)
diff --git a/src/main/java/net/kemitix/node/NodeItem.java b/src/main/java/net/kemitix/node/NodeItem.java
index f9ebf47..12a7cef 100644
--- a/src/main/java/net/kemitix/node/NodeItem.java
+++ b/src/main/java/net/kemitix/node/NodeItem.java
@@ -8,7 +8,6 @@ import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
-import java.util.function.Function;
/**
* Represents a tree of nodes.
@@ -23,8 +22,6 @@ class NodeItem implements Node {
private final Set> children = new HashSet<>();
- private Function, String> nameSupplier;
-
private Node parent;
private String name;
@@ -47,7 +44,6 @@ class NodeItem implements Node {
*/
NodeItem(final T data) {
this.data = data;
- this.nameSupplier = (n) -> null;
}
/**
@@ -74,24 +70,8 @@ class NodeItem implements Node {
setParent(parent);
}
- private String generateName() {
- return getNameSupplier().apply(this);
- }
-
- private Function, String> getNameSupplier() {
- if (nameSupplier != null) {
- return nameSupplier;
- }
- // no test for parent as root nodes will always have a default name
- // supplier
- return ((NodeItem) parent).getNameSupplier();
- }
-
@Override
public String getName() {
- if (name == null) {
- return generateName();
- }
return name;
}
@@ -380,14 +360,8 @@ class NodeItem implements Node {
public void removeParent() {
if (parent != null) {
Node oldParent = parent;
- Function, String> supplier = getNameSupplier();
parent = null;
oldParent.removeChild(this);
- if (this.nameSupplier == null) {
- // this is now a root node, so must provide a default name
- // supplier
- this.nameSupplier = supplier;
- }
}
}
From 4f4266fe3e87bf8226825afb01789664d6b094fc Mon Sep 17 00:00:00 2001
From: Paul Campbell
Date: Tue, 13 Sep 2016 07:39:05 +0100
Subject: [PATCH 40/41] pom.xml: version set to 0.4.0
---
pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pom.xml b/pom.xml
index a502cc3..4835b9d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -2,7 +2,7 @@
4.0.0node
- 0.4.0-SNAPSHOT
+ 0.4.0jarNode
From 4f713542831ceeaed81563c5c5abbbe3d794e1f4 Mon Sep 17 00:00:00 2001
From: Paul Campbell
Date: Tue, 13 Sep 2016 07:39:05 +0100
Subject: [PATCH 41/41] CHANGELOG
---
CHANGELOG | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/CHANGELOG b/CHANGELOG
index 66ec612..5ee231c 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,6 +1,13 @@
CHANGELOG
=========
+0.4.0
+------
+
+* Upgrade kemitix-parent to 2.0.0
+* Add ImmutableTree implementation
+* Switch to static factory constructors
+
0.3.0
------