From b18020708bb21a1e44bd0de0bda57739b2901c6b Mon Sep 17 00:00:00 2001 From: Paul Campbell Date: Tue, 24 May 2016 10:59:16 +0100 Subject: [PATCH] Node: may have names and a functional name supplier Names, where present, must be unique for each parent. Node: * String getName() * void setName(String name) * boolean isNamed() NodeItem - replaces all constructor: * (data) * (data, name) * (data, nameSupplier) * (data, parent) * (data, nameSupplier, parent) The name supplier takes a node and generates a string at the time the node is constructed. The root node has a default name supplier that returns null which means that the node is considered unnamed. Other nodes may provide their own name supplier that would be used be new nodes created within their subtree. --- src/main/java/net/kemitix/node/Node.java | 21 ++++ src/main/java/net/kemitix/node/NodeItem.java | 111 +++++++++++++++++-- 2 files changed, 121 insertions(+), 11 deletions(-) diff --git a/src/main/java/net/kemitix/node/Node.java b/src/main/java/net/kemitix/node/Node.java index 2d9c67e..66f9910 100644 --- a/src/main/java/net/kemitix/node/Node.java +++ b/src/main/java/net/kemitix/node/Node.java @@ -13,6 +13,20 @@ import java.util.Set; */ public interface Node { + /** + * Fetch the name of the node. + * + * @return the name of the node + */ + String getName(); + + /** + * Sets the explicit name for a node. + * + * @param name the new name + */ + void setName(String name); + /** * Fetch the data held within the node. * @@ -112,4 +126,11 @@ public interface Node { */ Optional> walkTree(final List path); + /** + * Returns true if the Node has a name. + * + * @return true if the node has a name + */ + boolean isNamed(); + } diff --git a/src/main/java/net/kemitix/node/NodeItem.java b/src/main/java/net/kemitix/node/NodeItem.java index c3a3b45..6815350 100644 --- a/src/main/java/net/kemitix/node/NodeItem.java +++ b/src/main/java/net/kemitix/node/NodeItem.java @@ -4,6 +4,7 @@ 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. @@ -16,33 +17,108 @@ public class NodeItem implements Node { private final T data; - private Node parent; - private final Set> children = new HashSet<>(); + private Function, String> nameSupplier; + + private Node parent; + + private String name; + /** - * Creates a root node. + * Create unnamed root node. * - * @param data the value of the node + * @param data the data or null */ public NodeItem(final T data) { - this(data, null); + this.data = data; + this.nameSupplier = (n) -> null; + } + + /** + * Create named root node. + * + * @param data the data or null + * @param name the name + */ + public NodeItem(final T data, final String name) { + this(data); + this.name = name; + } + + /** + * Creates root node with a name supplier. + * + * @param data the data or null + * @param nameSupplier the name supplier function + */ + public NodeItem( + final T data, final Function, String> nameSupplier) { + this(data); + this.nameSupplier = nameSupplier; + name = generateName(); } /** * Creates a node with a parent. * - * @param data the value of the node + * @param data the data or null * @param parent the parent node */ public NodeItem(final T data, final Node parent) { - if (data == null) { - throw new NullPointerException("data"); - } this.data = data; - if (parent != null) { - setParent(parent); + setParent(parent); + this.name = generateName(); + } + + /** + * Creates a named node with a parent. + * + * @param data the data or null + * @param name the name + * @param parent the parent node + */ + public NodeItem(final T data, final String name, final Node parent) { + this.data = data; + this.name = name; + 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 + */ + public NodeItem( + final T data, final Function, String> nameSupplier, + final Node parent) { + this(data, nameSupplier); + 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() { + return name; + } + + @Override + public void setName(final String name) { + this.name = name; } @Override @@ -98,6 +174,14 @@ public class NodeItem implements Node { if (this.equals(child) || isChildOf(child)) { throw new NodeException("Child is an ancestor"); } + if (child.isNamed()) { + final Optional> existingChild = findChildNamed( + child.getName()); + if (existingChild.isPresent() && existingChild.get() != child) { + throw new NodeException( + "Node with that name already exists here"); + } + } children.add(child); if (child.getParent() == null || !child.getParent().equals(this)) { child.setParent(this); @@ -202,6 +286,11 @@ public class NodeItem implements Node { public Node createChild(final T child) { if (child == null) { throw new NullPointerException("child"); + @Override + public boolean isNamed() { + return name != null && name.length() > 0; + } + } return new NodeItem<>(child, this); }