Clean up Node interface (#60)
* NoteItem Remove @NonNull from private method parameters * When creating node with null children treat as no children * NodeItem use Lombok Getter/Setter * Extract NodeTreeDraw * Remove getData() * Remove getChild() * Remove getParent() * Remove getChildByName() * Merge NodeTreeDraw into Nodes * Remove methodcound suppression * [changelog] Updated
This commit is contained in:
parent
04e0e83748
commit
82f0c25b1d
9 changed files with 207 additions and 357 deletions
|
@ -9,16 +9,29 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
** Added
|
||||
|
||||
- Add kemitix-maven-tiles
|
||||
- Add kemitix-maven-tiles 2.4.1
|
||||
|
||||
** Changed
|
||||
|
||||
- Replace Jenkins with Github Actions (#57)
|
||||
- [checkstyle] suppress npath complexity issues
|
||||
- [coverage] lower requirements
|
||||
- Clean up changelog and readme, and remove external build dependencies (#38)
|
||||
- Pinned pitest-junit5-plugin at 0.9 (#59)
|
||||
- Moved: Node.drawTree to Nodes (#60)
|
||||
|
||||
** Removed
|
||||
|
||||
- Removed from Node: getChildByName, getParent, getChild, getData (#60
|
||||
|
||||
** Dependencies
|
||||
|
||||
- Bump kemitix-checkstyle-ruleset from 4.0.1 to 5.4.0 (#59)
|
||||
- Bump kemitix-parent from 5.2.0 to 5.3.0 (#56)
|
||||
- Bump lombok from 1.18.10 to 1.18.12 (#55)
|
||||
- Bump assertj-core from 3.13.2 to 3.15.0 (#54)
|
||||
- Bump junit from 4.12 to 4.13 (#53)
|
||||
- Bump tiles-maven-plugin from 2.15 to 2.16 (#52)
|
||||
- Bump hamcrest-core from 2.1 to 2.2 (#50)
|
||||
- Bump lombok from 1.18.8 to 1.18.10 (#49)
|
||||
- Bump assertj-core from 3.12.2 to 3.13.2 (#48)
|
||||
|
@ -29,7 +42,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
- Bump lombok from 1.18.4 to 1.18.6 (#41)
|
||||
- Bump tiles-maven-plugin from 2.12 to 2.13 (#40)
|
||||
- Bump hamcrest-core from 1.3 to 2.1 (#37)
|
||||
- Clean up changelog and readme, and remove external build dependencies (#38)
|
||||
|
||||
* [0.7.0] - 2017-02-18
|
||||
|
||||
|
|
|
@ -58,13 +58,6 @@ public interface Node<T> {
|
|||
*/
|
||||
Optional<T> findData();
|
||||
|
||||
/**
|
||||
* Fetch the data held within the node.
|
||||
*
|
||||
* @return the node's data, or throws an {@link EmptyNodeException}
|
||||
*/
|
||||
T getData();
|
||||
|
||||
/**
|
||||
* Set the data held within the node.
|
||||
*
|
||||
|
@ -93,13 +86,6 @@ public interface Node<T> {
|
|||
*/
|
||||
Optional<Node<T>> findParent();
|
||||
|
||||
/**
|
||||
* Fetch the parent node.
|
||||
*
|
||||
* @return the parent node, or throws an {@link OrphanedNodeException}
|
||||
*/
|
||||
Node<T> getParent();
|
||||
|
||||
/**
|
||||
* Make the current node a direct child of the parent.
|
||||
*
|
||||
|
@ -157,15 +143,6 @@ public interface Node<T> {
|
|||
*/
|
||||
Optional<Node<T>> findChild(T child);
|
||||
|
||||
/**
|
||||
* Fetches the node for the child if present.
|
||||
*
|
||||
* @param child the child's data to search for
|
||||
*
|
||||
* @return the child node if found
|
||||
*/
|
||||
Node<T> getChild(T child);
|
||||
|
||||
/**
|
||||
* Checks if the node is an ancestor.
|
||||
*
|
||||
|
@ -202,25 +179,6 @@ public interface Node<T> {
|
|||
*/
|
||||
Optional<Node<T>> findChildByName(String name);
|
||||
|
||||
/**
|
||||
* Returns the child with the given name. If one can't be found a
|
||||
* NodeException is thrown.
|
||||
*
|
||||
* @param name the name of the child
|
||||
*
|
||||
* @return the node
|
||||
*/
|
||||
Node<T> getChildByName(String name);
|
||||
|
||||
/**
|
||||
* Draw a representation of the tree.
|
||||
*
|
||||
* @param depth current depth for recursion
|
||||
*
|
||||
* @return a representation of the tree
|
||||
*/
|
||||
String drawTree(int depth);
|
||||
|
||||
/**
|
||||
* Returns true if the Node has a name. Where a name supplier is used, the
|
||||
* generated name is used.
|
||||
|
|
|
@ -21,7 +21,9 @@
|
|||
|
||||
package net.kemitix.node;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import lombok.Setter;
|
||||
import lombok.val;
|
||||
|
||||
import java.util.*;
|
||||
|
@ -38,15 +40,17 @@ import static net.kemitix.node.HeadTail.tail;
|
|||
*
|
||||
* @author Paul Campbell (pcampbell@kemitix.net)
|
||||
*/
|
||||
@SuppressWarnings("methodcount")
|
||||
class NodeItem<T> implements Node<T> {
|
||||
|
||||
private final Set<Node<T>> children = new HashSet<>();
|
||||
|
||||
@Setter
|
||||
private T data;
|
||||
|
||||
private Node<T> parent;
|
||||
|
||||
@Setter
|
||||
@Getter
|
||||
private String name;
|
||||
|
||||
/**
|
||||
|
@ -57,13 +61,20 @@ class NodeItem<T> implements Node<T> {
|
|||
* @param parent the parent of the node, or null for a root node
|
||||
* @param children the children of the node - must not be null
|
||||
*/
|
||||
NodeItem(final T data, final String name, final Node<T> parent, @NonNull final Set<Node<T>> children) {
|
||||
NodeItem(
|
||||
final T data,
|
||||
final String name,
|
||||
final Node<T> parent,
|
||||
final Set<Node<T>> children
|
||||
) {
|
||||
this.data = data;
|
||||
this.name = name;
|
||||
if (parent != null) {
|
||||
doSetParent(parent);
|
||||
}
|
||||
this.children.addAll(children);
|
||||
if (children != null) {
|
||||
this.children.addAll(children);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -75,34 +86,11 @@ class NodeItem<T> implements Node<T> {
|
|||
this.parent = newParent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setName(final String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<T> findData() {
|
||||
return Optional.ofNullable(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T getData() {
|
||||
if (isEmpty()) {
|
||||
throw new EmptyNodeException(getName());
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setData(final T data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return data == null;
|
||||
|
@ -118,14 +106,6 @@ class NodeItem<T> implements Node<T> {
|
|||
return Optional.ofNullable(parent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node<T> getParent() {
|
||||
if (parent == null) {
|
||||
throw new OrphanedNodeException(getName());
|
||||
}
|
||||
return parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make the current node a direct child of the parent.
|
||||
*
|
||||
|
@ -137,7 +117,7 @@ class NodeItem<T> implements Node<T> {
|
|||
}
|
||||
|
||||
@SuppressWarnings("npathcomplexity")
|
||||
private void doSetParent(@NonNull final Node<T> newParent) {
|
||||
private void doSetParent(final Node<T> newParent) {
|
||||
if (this.equals(newParent) || newParent.isDescendantOf(this)) {
|
||||
throw new NodeException("Parent is a descendant");
|
||||
}
|
||||
|
@ -178,7 +158,7 @@ class NodeItem<T> implements Node<T> {
|
|||
}
|
||||
}
|
||||
|
||||
private void verifyChildIsNotAnAncestor(final @NonNull Node<T> child) {
|
||||
private void verifyChildIsNotAnAncestor(final Node<T> child) {
|
||||
if (this.equals(child) || isDescendantOf(child)) {
|
||||
throw new NodeException("Child is an ancestor");
|
||||
}
|
||||
|
@ -237,11 +217,6 @@ class NodeItem<T> implements Node<T> {
|
|||
.findFirst();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node<T> getChild(final T child) {
|
||||
return findChild(child).orElseThrow(() -> new NodeException("Child not found"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the node is an ancestor.
|
||||
*
|
||||
|
@ -323,33 +298,6 @@ class NodeItem<T> implements Node<T> {
|
|||
.findAny();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node<T> getChildByName(final String named) {
|
||||
final Optional<Node<T>> optional = findChildByName(named);
|
||||
if (optional.isPresent()) {
|
||||
return optional.get();
|
||||
}
|
||||
throw new NodeException("Named child not found");
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("movevariableinsideif")
|
||||
public String drawTree(final int depth) {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
final String unnamed = "(unnamed)";
|
||||
if (isNamed()) {
|
||||
sb.append(formatByDepth(name, depth));
|
||||
} else if (!children.isEmpty()) {
|
||||
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() {
|
||||
String currentName = getName();
|
||||
|
|
|
@ -85,7 +85,7 @@ public final class Nodes {
|
|||
*/
|
||||
public static <T> Node<T> namedChild(
|
||||
final T data, final String name, final Node<T> parent
|
||||
) {
|
||||
) {
|
||||
return new NodeItem<>(data, name, parent, new HashSet<>());
|
||||
}
|
||||
|
||||
|
@ -104,23 +104,49 @@ public final class Nodes {
|
|||
}
|
||||
final Set<Node<T>> children = getImmutableChildren(root);
|
||||
return ImmutableNodeItem.newRoot(root.findData()
|
||||
.orElse(null), root.getName(), children);
|
||||
.orElse(null), root.getName(), children);
|
||||
}
|
||||
|
||||
private static <T> Set<Node<T>> getImmutableChildren(final Node<T> source) {
|
||||
return source.getChildren()
|
||||
.stream()
|
||||
.map(Nodes::asImmutableChild)
|
||||
.collect(Collectors.toSet());
|
||||
.stream()
|
||||
.map(Nodes::asImmutableChild)
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
private static <T> Node<T> asImmutableChild(
|
||||
final Node<T> source
|
||||
) {
|
||||
) {
|
||||
return ImmutableNodeItem.newChild(source.findData()
|
||||
.orElse(null), source.getName(), source.findParent()
|
||||
.orElse(null),
|
||||
getImmutableChildren(source)
|
||||
);
|
||||
.orElse(null), source.getName(), source.findParent()
|
||||
.orElse(null),
|
||||
getImmutableChildren(source)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw a representation of the tree.
|
||||
*
|
||||
* @param depth current depth for recursion
|
||||
* @return a representation of the tree
|
||||
*/
|
||||
@SuppressWarnings("movevariableinsideif")
|
||||
public static <T> String drawTree(
|
||||
final Node<T> node,
|
||||
final int depth
|
||||
) {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
final String unnamed = "(unnamed)";
|
||||
if (node.isNamed()) {
|
||||
sb.append(formatByDepth(node.getName(), depth));
|
||||
} else if (!node.getChildren().isEmpty()) {
|
||||
sb.append(formatByDepth(unnamed, depth));
|
||||
}
|
||||
node.getChildren().forEach(c -> sb.append(drawTree(c, depth + 1)));
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static String formatByDepth(final String value, final int depth) {
|
||||
return String.format("[%1$" + (depth + value.length()) + "s]\n", value);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import java.util.Arrays;
|
|||
import java.util.Collections;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
|
@ -33,18 +34,6 @@ public class ImmutableNodeItemTest {
|
|||
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
|
||||
|
@ -81,14 +70,15 @@ public class ImmutableNodeItemTest {
|
|||
@Test
|
||||
public void shouldContainImmutableCopyOfChild() {
|
||||
//given
|
||||
val parent = Nodes.unnamedRoot("root");
|
||||
val child = Nodes.namedChild("child", "child", parent);
|
||||
Node<String> parent = Nodes.unnamedRoot("root");
|
||||
Node<String> 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");
|
||||
Optional<Node<String>> immutableChild =
|
||||
immutableNode.findChildByName("child");
|
||||
assertThat(immutableChild).isNotEqualTo(Optional.of(child));
|
||||
assertThat(immutableChild.map(Node::getName)).contains("child");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -100,13 +90,14 @@ public class ImmutableNodeItemTest {
|
|||
immutableNode = Nodes.asImmutable(parent);
|
||||
//then
|
||||
// get the immutable node's child's parent
|
||||
val immutableChild = immutableNode.getChildByName("child");
|
||||
final Optional<Node<String>> optionalParent = immutableChild.findParent();
|
||||
if (optionalParent.isPresent()) {
|
||||
val p = optionalParent.get();
|
||||
assertThat(p).hasFieldOrPropertyWithValue("name", "root")
|
||||
.hasFieldOrPropertyWithValue("data", "parent");
|
||||
}
|
||||
Optional<Node<String>> foundParent =
|
||||
immutableNode.findChildByName("child")
|
||||
.flatMap(Node::findParent);
|
||||
assertThat(foundParent).isNotEmpty();
|
||||
foundParent.ifPresent(p ->
|
||||
assertThat(p)
|
||||
.hasFieldOrPropertyWithValue("name", "root")
|
||||
.hasFieldOrPropertyWithValue("data", "parent"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -215,10 +206,9 @@ public class ImmutableNodeItemTest {
|
|||
val result = immutableNode.findChild("child");
|
||||
//then
|
||||
assertThat(result.isPresent()).isTrue();
|
||||
if (result.isPresent()) {
|
||||
assertThat(result.get()
|
||||
.getData()).contains("child");
|
||||
}
|
||||
result.map(resultNode ->
|
||||
assertThat(resultNode.findData())
|
||||
.contains("child"));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -244,9 +234,10 @@ public class ImmutableNodeItemTest {
|
|||
root.addChild(beta);
|
||||
immutableNode = Nodes.asImmutable(root);
|
||||
//when
|
||||
val result = immutableNode.getChildByName("alpha");
|
||||
Optional<Node<String>> result = immutableNode.findChildByName("alpha");
|
||||
//then
|
||||
assertThat(result.getName()).isEqualTo(alpha.getName());
|
||||
assertThat(result.map(Node::getName))
|
||||
.contains(alpha.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -257,11 +248,10 @@ public class ImmutableNodeItemTest {
|
|||
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");
|
||||
//then
|
||||
assertThat(immutableNode.findChildByName("gamma"))
|
||||
.isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -316,27 +306,6 @@ public class ImmutableNodeItemTest {
|
|||
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
|
||||
|
@ -355,28 +324,6 @@ public class ImmutableNodeItemTest {
|
|||
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
|
||||
|
@ -437,15 +384,16 @@ public class ImmutableNodeItemTest {
|
|||
Nodes.namedChild("eight", "eight", n6);
|
||||
val immutableRoot = Nodes.asImmutable(node);
|
||||
//when
|
||||
val result = immutableRoot.stream()
|
||||
.collect(Collectors.toList());
|
||||
val result = immutableRoot.stream().collect(Collectors.toList());
|
||||
//then
|
||||
assertThat(result).as("full tree")
|
||||
.hasSize(9);
|
||||
assertThat(result).as("full tree").hasSize(9);
|
||||
// and
|
||||
assertThat(immutableRoot.getChild("one")
|
||||
.stream()
|
||||
.collect(Collectors.toList())).as("sub-tree")
|
||||
.hasSize(4);
|
||||
assertThat(immutableRoot
|
||||
.findChild("one")
|
||||
.map(Node::stream)
|
||||
.map(Stream::count)
|
||||
)
|
||||
.as("sub-tree")
|
||||
.contains(4L);
|
||||
}
|
||||
}
|
||||
|
|
4
src/test/java/net/kemitix/node/IsNamedCategory.java
Normal file
4
src/test/java/net/kemitix/node/IsNamedCategory.java
Normal file
|
@ -0,0 +1,4 @@
|
|||
package net.kemitix.node;
|
||||
|
||||
public interface IsNamedCategory {
|
||||
}
|
|
@ -27,27 +27,6 @@ public class NodeItemTest {
|
|||
|
||||
private Node<String> node;
|
||||
|
||||
@Test
|
||||
public void getDataReturnsData() {
|
||||
//given
|
||||
val data = "this node data";
|
||||
//when
|
||||
node = Nodes.unnamedRoot(data);
|
||||
//then
|
||||
assertThat(node.getData()).as("can get the data from a node")
|
||||
.contains(data);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getDataWhenEmptyThrowsException() throws Exception {
|
||||
//given
|
||||
node = Nodes.unnamedRoot(null);
|
||||
assertThat(node.isEmpty()).isTrue();
|
||||
exception.expect(EmptyNodeException.class);
|
||||
//when
|
||||
node.getData();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findDataWhenFullReturnsData() {
|
||||
//given
|
||||
|
@ -69,26 +48,6 @@ public class NodeItemTest {
|
|||
assertThat(result).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getParentWhenRootThrowsException() {
|
||||
//given
|
||||
node = Nodes.unnamedRoot(null);
|
||||
exception.expect(OrphanedNodeException.class);
|
||||
//when
|
||||
node.getParent();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getParentWhenChildReturnsRoot() {
|
||||
//given
|
||||
val root = Nodes.unnamedRoot("root");
|
||||
node = Nodes.unnamedChild("child", root);
|
||||
//when
|
||||
val result = node.getParent();
|
||||
//then
|
||||
assertThat(result).isSameAs(root);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findParentWhenRootReturnsEmptyOptional() {
|
||||
//given
|
||||
|
@ -607,34 +566,6 @@ public class NodeItemTest {
|
|||
assertThat(node.findData()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getChildNamedFindsChild() {
|
||||
//given
|
||||
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
|
||||
val result = node.getChildByName("alpha");
|
||||
//then
|
||||
assertThat(result).isSameAs(alpha);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getChildNamedFindsNothing() {
|
||||
//given
|
||||
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);
|
||||
exception.expectMessage("Named child not found");
|
||||
//when
|
||||
node.getChildByName("gamma");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nodeNamesAreUniqueWithinAParent() {
|
||||
//given
|
||||
|
@ -656,23 +587,38 @@ public class NodeItemTest {
|
|||
//when
|
||||
node.insertInPath(four, "one", "two", "three");
|
||||
//then
|
||||
val three = four.getParent();
|
||||
assertThat(four.getParent()).as("add node to a tree")
|
||||
.isNotNull();
|
||||
assertThat(three.getName()).isEqualTo("three");
|
||||
val two = three.getParent();
|
||||
assertThat(two.getName()).isEqualTo("two");
|
||||
val one = two.getParent();
|
||||
assertThat(one.getName()).isEqualTo("one");
|
||||
assertThat(one.getParent()).isSameAs(node);
|
||||
assertThat(node.getChildByName("one")
|
||||
.getChildByName("two")
|
||||
.getChildByName("three")
|
||||
.getChildByName("four")).isSameAs(four);
|
||||
assertThat(four.findParent())
|
||||
.as("add node to a tree")
|
||||
.isNotEmpty();
|
||||
|
||||
val three = four.findParent();
|
||||
assertThat(three).isNotEmpty();
|
||||
three.map(threeNode ->
|
||||
assertThat(threeNode.getName())
|
||||
.isEqualTo("three"));
|
||||
|
||||
val two = three.flatMap(Node::findParent);
|
||||
assertThat(two).isNotEmpty();
|
||||
two.map(twoNode ->
|
||||
assertThat(twoNode.getName())
|
||||
.isEqualTo("two"));
|
||||
|
||||
val one = two.flatMap(Node::findParent);
|
||||
assertThat(one).isNotEmpty();
|
||||
one.ifPresent(oneNode ->
|
||||
SoftAssertions.assertSoftly(softly -> {
|
||||
assertThat(oneNode.getName()).isEqualTo("one");
|
||||
assertThat(oneNode.findParent()).contains(node);
|
||||
}));
|
||||
Optional<Node<String>> fourNode = node.findChildByName("one")
|
||||
.flatMap(oneChild -> oneChild.findChildByName("two"))
|
||||
.flatMap(twoChild -> twoChild.findChildByName("three"))
|
||||
.flatMap(threeChild -> threeChild.findChildByName("four"));
|
||||
assertThat(fourNode).isNotEmpty();
|
||||
assertThat(fourNode).contains(four);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void canPlaceInTreeUnderExistingNode() {
|
||||
//given
|
||||
node = Nodes.namedRoot(null, "root");
|
||||
|
@ -682,15 +628,17 @@ public class NodeItemTest {
|
|||
node.insertInPath(child); // as root/child
|
||||
node.insertInPath(grandchild, "child"); // as root/child/grandchild
|
||||
//then
|
||||
assertThat(node.getChildByName("child")).as("child")
|
||||
.isSameAs(child);
|
||||
assertThat(node.getChildByName("child")
|
||||
.getChildByName("grandchild")).as("grandchild")
|
||||
.isSameAs(grandchild);
|
||||
assertThat(node.findChildByName("child"))
|
||||
.as("child")
|
||||
.contains(child);
|
||||
Optional<Node<String>> grandNode = node.findChildByName("child")
|
||||
.flatMap(childNode -> childNode.findChildByName("grandchild"));
|
||||
assertThat(grandNode)
|
||||
.as("grandchild")
|
||||
.contains(grandchild);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void canPlaceInTreeAboveExistingNode() {
|
||||
//given
|
||||
node = Nodes.namedRoot(null, "root");
|
||||
|
@ -700,12 +648,14 @@ public class NodeItemTest {
|
|||
node.insertInPath(grandchild, "child");
|
||||
node.insertInPath(child);
|
||||
//then
|
||||
assertThat(node.getChildByName("child")
|
||||
.getData()).as("data in tree")
|
||||
.contains("child data");
|
||||
assertThat(node.getChildByName("child")
|
||||
.getChildByName("grandchild")).as("grandchild")
|
||||
.isSameAs(grandchild);
|
||||
assertThat(node.findChildByName("child").flatMap(Node::findData))
|
||||
.as("data in tree")
|
||||
.contains("child data");
|
||||
assertThat(
|
||||
node.findChildByName("child").flatMap(childNode ->
|
||||
childNode.findChildByName("grandchild")))
|
||||
.as("grandchild")
|
||||
.contains(grandchild);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -756,7 +706,6 @@ public class NodeItemTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void placeNodeInTreeWhenEmptyChildWithTargetNameExists() {
|
||||
//given
|
||||
node = Nodes.unnamedRoot(null);
|
||||
|
@ -764,18 +713,18 @@ public class NodeItemTest {
|
|||
final Node<String> target = Nodes.namedRoot(null, "target");
|
||||
node.addChild(child);
|
||||
child.addChild(target);
|
||||
val addMe = Nodes.namedRoot("I'm new", "target");
|
||||
Node<String> addMe = Nodes.namedRoot("I'm new", "target");
|
||||
assertThat(addMe.findParent()).isEmpty();
|
||||
assertThat(child.getChildByName("target")
|
||||
.isEmpty()).as("target starts empty")
|
||||
.isTrue();
|
||||
assertThat(child.findChildByName("target").flatMap(Node::findData))
|
||||
.as("target starts empty")
|
||||
.isEmpty();
|
||||
//when
|
||||
// addMe should replace target as the sole descendant of child
|
||||
node.insertInPath(addMe, "child");
|
||||
//then
|
||||
assertThat(child.getChildByName("target")
|
||||
.getData()).as("target now contains data")
|
||||
.contains("I'm new");
|
||||
assertThat(child.findChildByName("target").flatMap(Node::findData))
|
||||
.as("target now contains data")
|
||||
.contains("I'm new");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -789,14 +738,16 @@ public class NodeItemTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
@Category(IsNamedCategory.class)
|
||||
public void isNamedNull() {
|
||||
//given
|
||||
node = Nodes.unnamedRoot(null);
|
||||
node = Nodes.namedRoot(null, null);
|
||||
//then
|
||||
assertThat(node.isNamed()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Category(IsNamedCategory.class)
|
||||
public void isNamedEmpty() {
|
||||
//given
|
||||
node = Nodes.namedRoot(null, "");
|
||||
|
@ -805,6 +756,7 @@ public class NodeItemTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
@Category(IsNamedCategory.class)
|
||||
public void isNamedNamed() {
|
||||
//given
|
||||
node = Nodes.namedRoot(null, "named");
|
||||
|
@ -826,26 +778,6 @@ public class NodeItemTest {
|
|||
assertThat(child.findParent()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void drawTreeIsCorrect() {
|
||||
//given
|
||||
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
|
||||
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 canChangeNodeData() {
|
||||
//given
|
||||
|
@ -853,7 +785,7 @@ public class NodeItemTest {
|
|||
//when
|
||||
node.setData("updated");
|
||||
//then
|
||||
assertThat(node.getData()).contains("updated");
|
||||
assertThat(node.findData()).contains("updated");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -869,27 +801,6 @@ public class NodeItemTest {
|
|||
assertThat(node.getChildren()).containsExactly(child);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void canGetChildWhenFound() {
|
||||
//given
|
||||
node = Nodes.unnamedRoot("data");
|
||||
val child = Nodes.namedChild("child data", "child name", node);
|
||||
//when
|
||||
val found = node.getChild("child data");
|
||||
//then
|
||||
assertThat(found).isSameAs(child);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void canGetChildWhenNotFound() {
|
||||
//given
|
||||
exception.expect(NodeException.class);
|
||||
exception.expectMessage("Child not found");
|
||||
node = Nodes.unnamedRoot("data");
|
||||
//when
|
||||
node.getChild("child data");
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void constructorWithNameSupplierAndParentBeChildOfParent() {
|
||||
|
@ -969,4 +880,17 @@ public class NodeItemTest {
|
|||
assertThat(resultChild1).containsExactlyInAnyOrder(root);
|
||||
assertThat(resultChild3).containsExactlyInAnyOrder(child2, root);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenNodeItemChildrenAreNullThenAsNoChildren() {
|
||||
//when
|
||||
NodeItem<String> nodeItem = new NodeItem<>(
|
||||
"data",
|
||||
"name",
|
||||
null,
|
||||
null);
|
||||
//then
|
||||
assertThat(nodeItem.getChildren())
|
||||
.isEmpty();
|
||||
}
|
||||
}
|
||||
|
|
30
src/test/java/net/kemitix/node/NodeTreeDrawTest.java
Normal file
30
src/test/java/net/kemitix/node/NodeTreeDrawTest.java
Normal file
|
@ -0,0 +1,30 @@
|
|||
package net.kemitix.node;
|
||||
|
||||
import lombok.val;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class NodeTreeDrawTest {
|
||||
|
||||
@Test
|
||||
public void drawTreeIsCorrect() {
|
||||
//given
|
||||
final Node<String> 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 = Nodes.drawTree(node, 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]");
|
||||
}
|
||||
|
||||
}
|
|
@ -23,7 +23,7 @@ public class NodesTest {
|
|||
public void shouldCreateUnnamedRoot() throws Exception {
|
||||
val node = Nodes.unnamedRoot("data");
|
||||
SoftAssertions softly = new SoftAssertions();
|
||||
softly.assertThat(node.getData()).contains("data");
|
||||
softly.assertThat(node.findData()).contains("data");
|
||||
softly.assertThat(node.getName()).isEmpty();
|
||||
softly.assertAll();
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ public class NodesTest {
|
|||
public void shouldCreateNamedRoot() throws Exception {
|
||||
val node = Nodes.namedRoot("data", "name");
|
||||
SoftAssertions softly = new SoftAssertions();
|
||||
softly.assertThat(node.getData()).contains("data");
|
||||
softly.assertThat(node.findData()).contains("data");
|
||||
softly.assertThat(node.getName()).isEqualTo("name");
|
||||
softly.assertAll();
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ public class NodesTest {
|
|||
val parent = Nodes.unnamedRoot("root");
|
||||
val node = Nodes.unnamedChild("data", parent);
|
||||
SoftAssertions softly = new SoftAssertions();
|
||||
softly.assertThat(node.getData()).contains("data");
|
||||
softly.assertThat(node.findData()).contains("data");
|
||||
softly.assertThat(node.getName()).isEmpty();
|
||||
softly.assertThat(node.findParent()).contains(parent);
|
||||
softly.assertAll();
|
||||
|
@ -53,7 +53,7 @@ public class NodesTest {
|
|||
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.findData()).contains("data");
|
||||
softly.assertThat(node.getName()).isEqualTo("child");
|
||||
softly.assertThat(node.findParent()).contains(parent);
|
||||
softly.assertAll();
|
||||
|
|
Loading…
Reference in a new issue