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); }