[ONOS-5734] Define DataNode , InnerNode and LeafNode implementation
Change-Id: I38f941347839cd73d87a7affa1be74997a97ab1f
diff --git a/model/src/main/java/org/onosproject/yang/model/DataNode.java b/model/src/main/java/org/onosproject/yang/model/DataNode.java
index d927455..71ca3fd 100644
--- a/model/src/main/java/org/onosproject/yang/model/DataNode.java
+++ b/model/src/main/java/org/onosproject/yang/model/DataNode.java
@@ -21,33 +21,12 @@
* exchange between YANG runtime, protocol and store will be based on this
* node, agnostic of schema.
*/
-public interface DataNode {
+public abstract class DataNode {
- /**
- * Returns the node schema identifier.
- *
- * @return node schema identifier
- */
- SchemaId identifier();
-
- /**
- * Returns the type of node.
- *
- * @return node type
- */
- Type type();
-
- /**
- * Returns the key to identify a branching node.
- *
- * @return key to identify a branching node
- */
- NodeKey key();
-
- /**
+ /*
* Represents type of node in data store.
*/
- enum Type {
+ public enum Type {
/**
* Single instance node.
@@ -69,4 +48,236 @@
*/
MULTI_INSTANCE_LEAF_VALUE_NODE
}
+
+ /**
+ * Type of node in data store.
+ */
+ protected Type type;
+
+ /**
+ * Identifies a node uniquely among its siblings.
+ */
+ protected NodeKey key;
+
+ /**
+ * Returns the type of node.
+ *
+ * @return node type
+ */
+ public Type type() {
+ return type;
+ }
+
+ /**
+ * Returns the key to identify a branching node.
+ *
+ * @return key to identify a branching node
+ */
+ public NodeKey key() {
+ return key;
+ }
+
+ /**
+ * Creates an instance of data node.
+ *
+ * @param builder data node builder
+ */
+ protected DataNode(Builder builder) {
+ type = builder.type;
+ key = builder.key;
+ }
+
+ /**
+ * Returns data node builder for a given data node.
+ * It contains all the attributes from the data node. It is to provide
+ * mutability of data node using builder pattern.
+ *
+ * @return data node builder
+ */
+ public abstract Builder copyBuilder();
+
+ /**
+ * Represents the implementation of data node builder class.
+ *
+ * @param <B> type of data node builder
+ */
+ public static abstract class Builder<B extends Builder<B>> {
+
+ /**
+ * Type of node in data store.
+ */
+ protected Type type;
+
+ /**
+ * Identifies a node uniquely among its siblings.
+ */
+ protected NodeKey key;
+
+ /**
+ * Node key builder.
+ */
+ protected NodeKey.NodeKeyBuilder keyBuilder;
+
+ /**
+ * Parent data node
+ */
+ protected InnerNode.Builder parent;
+
+ /**
+ * Creates an instance of data node builder.
+ */
+ protected Builder() {
+ }
+
+ /**
+ * Creates an instance of data node builder using old data node.
+ *
+ * @param node data node which
+ */
+ protected Builder(DataNode node) {
+ type = node.type;
+ key = node.key;
+ }
+
+ /**
+ * Sets node key in builder object.
+ * when serializers have an instance of key present with them they can
+ * directly set the key value using this method.
+ *
+ * @param key node key identifier
+ * @return data node builder object
+ */
+ public B key(NodeKey key) {
+ this.key = key;
+ return (B) this;
+ }
+
+ /**
+ * Sets parent node's builder.
+ *
+ * @param node parent node builder
+ * @return builder object
+ */
+ protected B parent(InnerNode.Builder node) {
+ parent = node;
+ return (B) this;
+ }
+
+ /**
+ * Sets node type in builder object.
+ *
+ * @param type node type
+ * @return data node builder
+ */
+ public B type(Type type) {
+ this.type = type;
+ return (B) this;
+ }
+
+ /**
+ * Creates a child builder of type inner node and set a back reference
+ * of parent node. it is used while creating a data tree.
+ *
+ * @param name name of inner node
+ * @param nameSpace namespace of inner node
+ * @return child node builder
+ */
+ public abstract InnerNode.Builder createChildBuilder(
+ String name, String nameSpace);
+
+ /**
+ * Creates a child build of type leaf node and set a back reference
+ * of parent node. it is used while creating a data tree. the value
+ * of leaf is set while creation.
+ *
+ * @param name name of leaf node
+ * @param nameSpace namespace of leaf node
+ * @param value value for leaf node
+ * @return child node builder
+ */
+ public abstract LeafNode.Builder createChildBuilder(
+ String name, String nameSpace, Object value);
+
+ /**
+ * Deletes child node for a given node key from parent node.
+ * <p>
+ * for deleting a node from data tree , caller should parse resource
+ * identifier to reach to the child node. while parsing the resource
+ * identifier caller need to create a new data node using copy
+ * builder. this copy builder can be used further to create child
+ * nodes copy builders.
+ *
+ * @param key node key for child node
+ * @return data node builder
+ */
+ public abstract InnerNode.Builder deleteChild(NodeKey key);
+
+ /**
+ * Returns a child node builder for a given node key. it contains all
+ * the attribute of child node. it is used to modify the data tree
+ * while delete or update operations.
+ * <p>
+ * this method provides copy builder of child node when a
+ * update/delete request comes. it sets a back reference of parent
+ * node as well in child node's copy builder.
+ *
+ * @param key data node key
+ * @return child node
+ */
+ public abstract InnerNode.Builder getChildBuilder(NodeKey key);
+
+
+ /**
+ * Add key leaf for list node key. It can be used while handling a
+ * list node when in your yang file you have multiple key leaves.
+ * <p>
+ * this method is used for adding multiple key leaves in you list
+ * node. these keys will be added to key builder which is built while
+ * while node building. To use this method caller should know about
+ * schema of list and key leaves.
+ *
+ * @param name name of leaf node
+ * @param nameSpace namespace of leaf node
+ * @param val value of leaf
+ * @return data node builder
+ */
+ public abstract InnerNode.Builder addKeyLeaf(String name, String nameSpace,
+ Object val);
+
+ /**
+ * Add key value to leaf list key. this can be used while handling a
+ * leaf list where you need to add multiple values.
+ *
+ * @param val value
+ * @return data node builder
+ */
+ public abstract LeafNode.Builder addLeafListValue(Object val);
+
+ /**
+ * Builds data node.
+ *
+ * @return data node
+ */
+ public abstract DataNode build();
+
+ /**
+ * Returns parent node builder after building and adding the child
+ * node to parent's child node map.
+ * <p>
+ * This method is used when caller has reached to the depth of the
+ * subtree and then he wants to go back to its parent node so he
+ * should build the node and then it should add it to parent node's
+ * map. this method provides both the functionalities of build and
+ * add to parent . Also it returns back the parent pointer so caller
+ * can do further operations on data tree.
+ *
+ * @return parent's builder object
+ */
+ public InnerNode.Builder exitNode() {
+ if (parent != null) {
+ parent.addNode(build());
+ }
+ return parent;
+ }
+ }
}
diff --git a/model/src/main/java/org/onosproject/yang/model/InnerNode.java b/model/src/main/java/org/onosproject/yang/model/InnerNode.java
index c4db08f..32478d7 100644
--- a/model/src/main/java/org/onosproject/yang/model/InnerNode.java
+++ b/model/src/main/java/org/onosproject/yang/model/InnerNode.java
@@ -14,23 +14,177 @@
* limitations under the License.
*/
-
package org.onosproject.yang.model;
import java.util.LinkedHashMap;
+import java.util.Map;
+
+import static org.onosproject.yang.model.ModelConstants.LEAF_IS_TERMINAL;
/**
* Abstraction of an entity which represents an inner node in data store.
*/
-public interface InnerNode extends DataNode {
+public final class InnerNode extends DataNode {
+
+ /**
+ * Map containing info of all child data nodes with respect to their node
+ * keys.
+ */
+ private Map<NodeKey, DataNode> childNodes = new LinkedHashMap<>();
/**
* Returns the children nodes to the current node.
- * <p>
- * Children nodes are identified based on the node key
+ * Children nodes are identified based on the node key.
*
* @return read only linked map of children nodes
*/
- LinkedHashMap<NodeKey, DataNode> childNodes();
+ public Map<NodeKey, DataNode> childNodes() {
+ return childNodes;
+ }
+ /**
+ * Creates an instance of inner node.
+ *
+ * @param builder inner node builder
+ */
+ public InnerNode(Builder builder) {
+ super(builder);
+ childNodes = builder.childNodes;
+ }
+
+ /**
+ * Returns inner node builder instance.
+ *
+ * @param name name of node
+ * @param nameSpace namespace of node
+ * @return inner node builder instance
+ */
+ public static Builder builder(String name, String nameSpace) {
+ return new Builder(name, nameSpace);
+ }
+
+ /**
+ * Returns inner node copy builder.
+ *
+ * @return inner node copy builder
+ */
+ @Override
+ public Builder copyBuilder() {
+ return new Builder(this);
+ }
+
+ /**
+ * Builder with get and set functions to build inner node,
+ * builder will be used both to create inner node from scratch or from a
+ * given inner node.
+ */
+ public static class Builder extends DataNode.Builder<Builder> {
+
+ /**
+ * Map containing info of all child data nodes with respect to their
+ * node keys.
+ */
+ private Map<NodeKey, DataNode> childNodes = new LinkedHashMap<>();
+
+ /**
+ * Creates an instance of data node builder.
+ *
+ * @param name name of node
+ * @param namespace namespace of node
+ */
+ protected Builder(String name, String namespace) {
+ keyBuilder = NodeKey.builder().schemaId(name, namespace);
+ }
+
+ /**
+ * Creates an instance of inner node builder.
+ *
+ * @param node old inner node
+ */
+ public Builder(InnerNode node) {
+ super(node);
+ childNodes = node.childNodes;
+ }
+
+ /**
+ * Adds node to the builder.
+ *
+ * @param node node to be added
+ * @return inner node builder
+ */
+ public Builder addNode(DataNode node) {
+ childNodes.put(node.key(), node);
+ return this;
+ }
+
+ /**
+ * Builds a inner node object.
+ *
+ * @return inner node
+ */
+ @Override
+ public InnerNode build() {
+ if (type == null) {
+ throw new IllegalStateException("node should have a type.");
+ }
+ if (key == null) {
+ key = keyBuilder.build();
+ }
+ return new InnerNode(this);
+ }
+
+ @Override
+ public InnerNode.Builder createChildBuilder(String name, String nameSpace) {
+ return InnerNode.builder(name, nameSpace)
+ .parent(this);
+ }
+
+ @Override
+ public LeafNode.Builder createChildBuilder(String name, String nameSpace,
+ Object value) {
+ return LeafNode.builder(name, nameSpace)
+ .parent(this)
+ .value(value);
+ }
+
+ @Override
+ public InnerNode.Builder deleteChild(NodeKey key) {
+ childNodes.remove(key);
+ return this;
+ }
+
+ @Override
+ public Builder getChildBuilder(NodeKey nodeKey) {
+ DataNode node = childNodes.get(nodeKey);
+ if (node == null) {
+ throw new IllegalArgumentException(
+ "Invalid key: no child nodes found for given key: " +
+ nodeKey);
+ }
+ return (Builder) node.copyBuilder().parent(this);
+ }
+
+ @Override
+ public Builder addKeyLeaf(String name, String nameSpace, Object val) {
+ ListKey.ListKeyBuilder listKeyBuilder;
+ if (!(keyBuilder instanceof ListKey.ListKeyBuilder)) {
+ if (keyBuilder instanceof LeafListKey.LeafListKeyBuilder) {
+ throw new ModelException(LEAF_IS_TERMINAL);
+ }
+
+ listKeyBuilder = new ListKey.ListKeyBuilder(keyBuilder);
+ } else {
+ listKeyBuilder = (ListKey.ListKeyBuilder) keyBuilder;
+ }
+
+ listKeyBuilder.addKeyLeaf(name, nameSpace, val);
+ keyBuilder = listKeyBuilder;
+ return this;
+ }
+
+ @Override
+ public LeafNode.Builder addLeafListValue(Object val) {
+ throw new IllegalStateException("node is not of leaf list type.");
+ }
+ }
}
diff --git a/model/src/main/java/org/onosproject/yang/model/KeyLeaf.java b/model/src/main/java/org/onosproject/yang/model/KeyLeaf.java
index 6d782e4..5bc0243 100644
--- a/model/src/main/java/org/onosproject/yang/model/KeyLeaf.java
+++ b/model/src/main/java/org/onosproject/yang/model/KeyLeaf.java
@@ -16,15 +16,20 @@
package org.onosproject.yang.model;
+import java.util.Objects;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+import static java.util.Objects.hash;
+
/**
* Represents the List's key leaf value.
*/
public class KeyLeaf {
+
private SchemaId leafSchema;
private Object leafVal;
private KeyLeaf() {
-
}
/**
@@ -65,4 +70,28 @@
public String leafValAsString() {
return leafVal.toString();
}
+
+ @Override
+ public int hashCode() {
+ return hash(leafSchema, leafVal);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null) {
+ return false;
+ }
+
+ KeyLeaf that = (KeyLeaf) obj;
+ return Objects.equals(leafSchema, that.leafSchema) &&
+ Objects.equals(leafVal, that.leafVal);
+ }
+
+ @Override
+ public String toString() {
+ return toStringHelper(getClass())
+ .add("schemaId", leafSchema)
+ .add("leafValue", leafVal)
+ .toString();
+ }
}
diff --git a/model/src/main/java/org/onosproject/yang/model/LeafListKey.java b/model/src/main/java/org/onosproject/yang/model/LeafListKey.java
index 904d6bd..591dcb6 100644
--- a/model/src/main/java/org/onosproject/yang/model/LeafListKey.java
+++ b/model/src/main/java/org/onosproject/yang/model/LeafListKey.java
@@ -16,6 +16,10 @@
package org.onosproject.yang.model;
+import java.util.Objects;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+
/**
* Representation of an entity which identifies a uniquely branching
* leaf-list entry corresponding to a multi instance leaf schema.
@@ -52,6 +56,36 @@
return val.toString();
}
+ @Override
+ public int hashCode() {
+ return Objects.hash(schemaId, val);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null) {
+ return false;
+ }
+
+ if (!getClass().equals(obj.getClass())) {
+ return false;
+ }
+
+ LeafListKey that = (LeafListKey) obj;
+ return Objects.equals(val, that.val) &&
+ Objects.equals(schemaId, that.schemaId);
+ }
+
+ @Override
+ public String toString() {
+ return toStringHelper(getClass())
+ .add("value", val)
+ .toString();
+ }
+
+ /**
+ * Represents Leaf list key builder.
+ */
public static class LeafListKeyBuilder
extends NodeKeyBuilder<LeafListKeyBuilder> {
diff --git a/model/src/main/java/org/onosproject/yang/model/LeafNode.java b/model/src/main/java/org/onosproject/yang/model/LeafNode.java
index e6af9d2..ca46b9a 100644
--- a/model/src/main/java/org/onosproject/yang/model/LeafNode.java
+++ b/model/src/main/java/org/onosproject/yang/model/LeafNode.java
@@ -17,23 +17,176 @@
package org.onosproject.yang.model;
+import static org.onosproject.yang.model.ModelConstants.NON_KEY_LEAF;
+
/**
* Abstraction of an entity which represents leaf data tree node.
*/
-public interface LeafNode extends DataNode {
+public final class LeafNode extends DataNode {
+
+ /**
+ * Leaf node value.
+ */
+ private Object value;
/**
* Returns value contained in leaf node.
*
* @return value contained in leaf node
*/
- Object value();
+ public Object value() {
+ return value;
+ }
/**
* Returns value as string, for usage in serializers.
*
* @return string representation of value
*/
- String asString();
+ public String asString() {
+ return String.valueOf(value);
+ }
+ /**
+ * Creates an instance of leaf node.
+ *
+ * @param builder leaf node builder
+ */
+ public LeafNode(Builder builder) {
+ super(builder);
+ value = builder.value;
+ }
+
+ /**
+ * Returns data node builder instance.
+ *
+ * @param name name of node
+ * @param nameSpace namespace of node
+ * @return data node builder instance
+ */
+ public static Builder builder(String name, String nameSpace) {
+ return new Builder(name, nameSpace);
+ }
+
+ /**
+ * Returns data node copy builder.
+ *
+ * @return data node copy builder
+ */
+ @Override
+ public Builder copyBuilder() {
+ return new Builder(this);
+ }
+
+ /**
+ * Builder with get and set functions to build leaf node,
+ * builder will be used both to create leaf node from scratch or from a
+ * given leaf node.
+ */
+ public final static class Builder extends DataNode.Builder<Builder> {
+
+ /**
+ * Leaf node value.
+ */
+ private Object value;
+
+ /**
+ * Creates an instance of data node builder.
+ *
+ * @param name name of node
+ * @param namespace namespace of node
+ */
+ protected Builder(String name, String namespace) {
+ keyBuilder = NodeKey.builder().schemaId(name, namespace);
+ }
+
+ /**
+ * Creates an instance of leaf node copy builder.
+ *
+ * @param node old leaf node
+ */
+ public Builder(LeafNode node) {
+ super(node);
+ value = node.value;
+ }
+
+ /**
+ * Sets value of leaf node builder.
+ *
+ * @param value value
+ * @return leaf node builder
+ */
+ public Builder value(Object value) {
+ this.value = value;
+ return this;
+ }
+
+ @Override
+ public InnerNode.Builder createChildBuilder(String name, String nameSpace) {
+ throw new IllegalStateException("leaf node can't have a child " +
+ "node");
+ }
+
+ @Override
+ public LeafNode.Builder createChildBuilder(String name, String nameSpace,
+ Object value) {
+ throw new IllegalStateException("leaf node can't have a child " +
+ "node");
+ }
+
+ @Override
+ public InnerNode.Builder deleteChild(NodeKey key) {
+ throw new IllegalStateException("leaf node can't have a child " +
+ "node");
+ }
+
+ @Override
+ public InnerNode.Builder getChildBuilder(NodeKey key) {
+ throw new IllegalStateException("leaf node can't have a child " +
+ "node");
+ }
+
+
+ @Override
+ public InnerNode.Builder addKeyLeaf(String name, String nameSpace, Object val) {
+ throw new IllegalStateException("leaf node can't have a key " +
+ "leaves node");
+ }
+
+ @Override
+ public Builder addLeafListValue(Object val) {
+ LeafListKey.LeafListKeyBuilder leafListKeyBuilder;
+ if (!(keyBuilder instanceof LeafListKey.LeafListKeyBuilder)) {
+ if (keyBuilder instanceof ListKey.ListKeyBuilder) {
+ throw new ModelException(NON_KEY_LEAF);
+ }
+
+ leafListKeyBuilder = new LeafListKey.LeafListKeyBuilder();
+ NodeKey key = keyBuilder.build();
+ leafListKeyBuilder.schemaId(key.schemaId());
+ } else {
+ leafListKeyBuilder = (LeafListKey.LeafListKeyBuilder) keyBuilder;
+ }
+
+ leafListKeyBuilder.value(val);
+ keyBuilder = leafListKeyBuilder;
+ return this;
+ }
+
+ /**
+ * Builds a leaf node object.
+ *
+ * @return leaf node
+ */
+ @Override
+ public LeafNode build() {
+ if (type == null) {
+ throw new IllegalStateException("node should have a type.");
+ }
+ if (key == null) {
+ key = keyBuilder.build();
+ }
+ return new LeafNode(this);
+ }
+ }
}
diff --git a/model/src/main/java/org/onosproject/yang/model/ListKey.java b/model/src/main/java/org/onosproject/yang/model/ListKey.java
index 87107fc..ebc9057 100644
--- a/model/src/main/java/org/onosproject/yang/model/ListKey.java
+++ b/model/src/main/java/org/onosproject/yang/model/ListKey.java
@@ -19,6 +19,9 @@
import java.util.LinkedList;
import java.util.List;
+import java.util.Objects;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
/**
* Represents an entity which identifies a unique branching node
@@ -54,6 +57,38 @@
return 0;
}
+ @Override
+ public int hashCode() {
+ return Objects.hash(schemaId, keyLeafs);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null) {
+ return false;
+ }
+
+ if (!getClass().equals(obj.getClass())) {
+ return false;
+ }
+
+ ListKey that = (ListKey) obj;
+ List<KeyLeaf> thatList = that.keyLeafs;
+ return keyLeafs.size() == thatList.size() &&
+ keyLeafs.containsAll(thatList) &&
+ Objects.equals(schemaId, that.schemaId);
+ }
+
+ @Override
+ public String toString() {
+ return toStringHelper(getClass())
+ .add("value", keyLeafs)
+ .toString();
+ }
+
+ /**
+ * Represents list key builder.
+ */
public static class ListKeyBuilder extends NodeKeyBuilder<ListKeyBuilder> {
private List<KeyLeaf> keyLeafs = new LinkedList<>();
@@ -61,7 +96,6 @@
* used to construct the key from scratch.
*/
public ListKeyBuilder() {
-
}
/**
diff --git a/model/src/main/java/org/onosproject/yang/model/NodeKey.java b/model/src/main/java/org/onosproject/yang/model/NodeKey.java
index 8e83e49..21dc12f 100644
--- a/model/src/main/java/org/onosproject/yang/model/NodeKey.java
+++ b/model/src/main/java/org/onosproject/yang/model/NodeKey.java
@@ -17,7 +17,11 @@
package org.onosproject.yang.model;
+import java.util.Objects;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
import static com.google.common.base.Preconditions.checkNotNull;
+import static java.util.Objects.hash;
import static org.onosproject.yang.model.ModelConstants.INCOMPLETE_SCHEMA_INFO;
/**
@@ -26,7 +30,7 @@
*/
public class NodeKey<E extends NodeKey> implements Comparable<E> {
- private SchemaId schemaId;
+ protected SchemaId schemaId;
/**
* Create object from builder.
@@ -49,10 +53,50 @@
@Override
public int compareTo(NodeKey o) {
- //TODO: implement me
- return 0;
+ checkNotNull(o);
+ return schemaId.compareTo(o.schemaId());
}
+ /**
+ * Returns node key builder.
+ *
+ * @return node key builder
+ */
+ public static NodeKeyBuilder builder() {
+ return new NodeKeyBuilder();
+ }
+
+ @Override
+ public int hashCode() {
+ return hash(schemaId);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null) {
+ return false;
+ }
+
+ if (!getClass().equals(obj.getClass())) {
+ return false;
+ }
+
+ NodeKey that = (NodeKey) obj;
+ return Objects.equals(schemaId, that.schemaId);
+ }
+
+ @Override
+ public String toString() {
+ return toStringHelper(getClass())
+ .add("schemaId", schemaId)
+ .toString();
+ }
+
+ /**
+ * Builder for node key.
+ *
+ * @param <B> node key type
+ */
public static class NodeKeyBuilder<B extends NodeKeyBuilder<B>> {
private SchemaId schemaId;
@@ -60,7 +104,6 @@
* Create the node key from scratch.
*/
public NodeKeyBuilder() {
-
}
/**
diff --git a/model/src/main/java/org/onosproject/yang/model/ResourceId.java b/model/src/main/java/org/onosproject/yang/model/ResourceId.java
index 7f99c8c..1e75b54 100644
--- a/model/src/main/java/org/onosproject/yang/model/ResourceId.java
+++ b/model/src/main/java/org/onosproject/yang/model/ResourceId.java
@@ -19,6 +19,9 @@
import java.util.LinkedList;
import java.util.List;
+import static com.google.common.base.MoreObjects.toStringHelper;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static java.util.Objects.hash;
import static org.onosproject.yang.model.ModelConstants.LEAF_IS_TERMINAL;
import static org.onosproject.yang.model.ModelConstants.NON_KEY_LEAF;
import static org.onosproject.yang.model.ModelConstants.NO_KEY_SET;
@@ -30,6 +33,10 @@
*/
public class ResourceId {
+
+ /**
+ * List of node keys.
+ */
private List<NodeKey> nodeKeyList;
/**
@@ -60,6 +67,29 @@
return new Builder();
}
+ @Override
+ public int hashCode() {
+ return hash(nodeKeyList);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null) {
+ return false;
+ }
+ ResourceId that = (ResourceId) obj;
+ List<NodeKey> thatList = that.nodeKeyList;
+ return nodeKeyList.size() == thatList.size() &&
+ nodeKeyList.containsAll(thatList);
+ }
+
+ @Override
+ public String toString() {
+ return toStringHelper(getClass())
+ .add("nodeKeyList", nodeKeyList)
+ .toString();
+ }
+
/**
* Builder to construct resource identifier.
*/
@@ -144,9 +174,7 @@
* @return built resource identifier
*/
public ResourceId build() {
- if (curKeyBuilder == null) {
- throw new ModelException(NO_KEY_SET);
- }
+ checkNotNull(curKeyBuilder, NO_KEY_SET);
nodeKeyList.add(curKeyBuilder.build());
return new ResourceId(this);
}
diff --git a/model/src/main/java/org/onosproject/yang/model/SchemaId.java b/model/src/main/java/org/onosproject/yang/model/SchemaId.java
index e25f8b4..082c357 100644
--- a/model/src/main/java/org/onosproject/yang/model/SchemaId.java
+++ b/model/src/main/java/org/onosproject/yang/model/SchemaId.java
@@ -16,25 +16,27 @@
package org.onosproject.yang.model;
+import java.util.Objects;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+import static com.google.common.base.Preconditions.checkNotNull;
import static org.onosproject.yang.model.ModelConstants.INCOMPLETE_SCHEMA_INFO;
/**
* Representation of an entity which identifies a schema node in the schema /
* data tree.
*/
-public class SchemaId {
+public class SchemaId implements Comparable<SchemaId> {
private String name;
private String nameSpace;
private SchemaId() {
-
}
public SchemaId(String name, String nameSpace) {
- if (name == null || nameSpace == null) {
- throw new ModelException(INCOMPLETE_SCHEMA_INFO);
- }
+ checkNotNull(name, INCOMPLETE_SCHEMA_INFO);
+ checkNotNull(nameSpace, INCOMPLETE_SCHEMA_INFO);
this.name = name;
this.nameSpace = nameSpace;
}
@@ -58,4 +60,38 @@
String namespace() {
return nameSpace;
}
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(name, nameSpace);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null) {
+ return false;
+ }
+ SchemaId that = (SchemaId) obj;
+ return Objects.equals(name, that.name) &&
+ Objects.equals(nameSpace, that.nameSpace);
+ }
+
+ @Override
+ public int compareTo(SchemaId o) {
+ checkNotNull(o);
+ if (name.equals(o.name)) {
+ if (nameSpace.equals(o.nameSpace)) {
+ return 0;
+ }
+ }
+ return -1;
+ }
+
+ @Override
+ public String toString() {
+ return toStringHelper(getClass())
+ .add("name", name)
+ .add("nameSpace", nameSpace)
+ .toString();
+ }
}
diff --git a/model/src/test/java/org/onosproject/yang/model/DataTreeOperationTest.java b/model/src/test/java/org/onosproject/yang/model/DataTreeOperationTest.java
new file mode 100644
index 0000000..680e757
--- /dev/null
+++ b/model/src/test/java/org/onosproject/yang/model/DataTreeOperationTest.java
@@ -0,0 +1,535 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.yang.model;
+
+import org.junit.Test;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertThat;
+import static org.onosproject.yang.model.DataNode.Type.MULTI_INSTANCE_LEAF_VALUE_NODE;
+import static org.onosproject.yang.model.DataNode.Type.MULTI_INSTANCE_NODE;
+import static org.onosproject.yang.model.DataNode.Type.SINGLE_INSTANCE_LEAF_VALUE_NODE;
+import static org.onosproject.yang.model.DataNode.Type.SINGLE_INSTANCE_NODE;
+
+/**
+ * Unit test case to verify data tree operations.
+ */
+public class DataTreeOperationTest {
+
+ private static final String PARENT = "parent";
+ private static final String PARENT_NAMESPACE = "parent";
+
+ private static final String C1 = "c1";
+ private static final String C1_NAMESPACE = "parent/c1";
+
+ private static final String C2 = "c2";
+ private static final String C2_NAMESPACE = "parent/c2";
+
+ private static final String C3 = "c3";
+ private static final String C3_NAMESPACE = "parent/c1/c3";
+
+ private static final String L1 = "l1";
+ private static final String L1_NAMESPACE = "parent/c1/l1";
+
+ private static final String L2 = "l2";
+ private static final String L2_NAMESPACE = "parent/c2/l2";
+
+ private static final String C4 = "c4";
+ private static final String C4_NAMESPACE = "parent/c1/c3/c4";
+
+ private static final String L3 = "l3";
+ private static final String L3_NAMESPACE = "parent/c2/l3";
+
+ private static final String LIST = "list";
+ private static final String LIST_NAMESPACE = "parent/list";
+
+ private static final String KL1 = "kl1";
+ private static final String KL1_NAMESPACE = "parent/kl1";
+
+ private static final String KL2 = "kl2";
+ private static final String KL2_NAMESPACE = "parent/kl2";
+
+
+ /**
+ * Creates a data tree.
+ *
+ * @return data tree
+ */
+ private DataNode createDataTree() {
+
+ /*
+ * parent
+ * |------C1
+ * | |-----C3
+ * | |-----l1
+ * |
+ * |------C2
+ * | |-----l2
+ */
+
+ return InnerNode.builder(PARENT, PARENT_NAMESPACE)
+ //Parent
+ .type(SINGLE_INSTANCE_NODE)
+ //C1
+ .createChildBuilder(C1, C1_NAMESPACE)
+ .type(SINGLE_INSTANCE_NODE)
+
+ //C1's child nodes C3
+ .createChildBuilder(C3, C3_NAMESPACE)
+ .type(SINGLE_INSTANCE_NODE)
+
+ //build c3 and traverse back to c1
+ .exitNode()
+
+ //C1's child leaf L1
+ .createChildBuilder(L1, L1_NAMESPACE, 10)
+ .type(SINGLE_INSTANCE_LEAF_VALUE_NODE)
+
+ //Builder l1 and traverse back to c1
+ .exitNode()
+
+ //build c1 and add it to parent and traverse back to parent node
+ .exitNode()
+
+ //create c2 parent's child node
+ .createChildBuilder(C2, C2_NAMESPACE)
+ .type(SINGLE_INSTANCE_NODE)
+ //C2's leaf l2
+
+ .createChildBuilder(L2, L2_NAMESPACE, "string")
+ .type(MULTI_INSTANCE_LEAF_VALUE_NODE)
+
+ //build l2 and add it to c2 and traverse back to c2.
+ .exitNode()
+
+ //build c2 and traverse back to parent node
+ .exitNode()
+ //build parent node
+ .build();
+ }
+
+ /**
+ * Unit test case for creating a data tree.
+ */
+ @Test
+ public void testCreate() {
+
+ /*
+ * parent
+ * |------C1
+ * | |-----C3
+ * | |-----l1
+ * |
+ * |------C2
+ * | |-----l2
+ */
+
+ DataNode node = createDataTree();
+
+ //validate parent.
+ validateNode(node, PARENT, PARENT_NAMESPACE, 2);
+
+ //Validate c1
+ Iterator<Map.Entry<NodeKey, DataNode>> itp = ((InnerNode) node)
+ .childNodes()
+ .entrySet()
+ .iterator();
+ node = itp.next().getValue();
+ validateNode(node, C1, C1_NAMESPACE, 2);
+
+ Iterator<Map.Entry<NodeKey, DataNode>> itc1 = ((InnerNode) node)
+ .childNodes()
+ .entrySet()
+ .iterator();
+
+ //validate c3
+ node = itc1.next().getValue();
+ validateNode(node, C3, C3_NAMESPACE, 0);
+
+ //Validate c2
+ node = itp.next().getValue();
+ validateNode(node, C2, C2_NAMESPACE, 1);
+ }
+
+ /**
+ * Unit test case to add a new child to current data tree.
+ */
+ @Test
+ public void testAddChildNode() {
+
+ /*
+ * parent
+ * |------C1
+ * | |-----C3
+ * | | |----c4
+ * | |-----l1
+ * |
+ * |------C2
+ * | |-----l2
+ */
+
+ DataNode node = createDataTree();
+ /*
+ * RSC path == /parent/c1/c3.
+ * adding c4 to c3 node.
+ */
+ ResourceId id = ResourceId.builder()
+ .addBranchPointSchema(PARENT, PARENT_NAMESPACE)
+ .addBranchPointSchema(C1, C1_NAMESPACE)
+ .addBranchPointSchema(C3, C3_NAMESPACE).build();
+
+ List<NodeKey> keys = id.nodeKeys();
+
+ node.copyBuilder()
+
+ //Reach to c1 by fetching it from the map.
+ .getChildBuilder(keys.get(1))
+
+ // now you have c1's builder and get c3 from c1's map and
+ // then get its builder.
+ .getChildBuilder(keys.get(2))
+
+ //add c4 in c3.
+ .createChildBuilder(C4, C4_NAMESPACE).type(SINGLE_INSTANCE_NODE)
+
+ //build c3 and return to c1.
+ .exitNode()
+
+ //build c1 and return to parent.
+ .exitNode()
+
+ //build parent node.
+ .build();
+
+ //validate parent.
+ validateNode(node, PARENT, PARENT_NAMESPACE, 2);
+
+ //Validate c1
+ Iterator<Map.Entry<NodeKey, DataNode>> itp = ((InnerNode) node)
+ .childNodes()
+ .entrySet()
+ .iterator();
+ node = itp.next().getValue();
+ validateNode(node, C1, C1_NAMESPACE, 2);
+
+ Iterator<Map.Entry<NodeKey, DataNode>> itc1 = ((InnerNode) node)
+ .childNodes()
+ .entrySet()
+ .iterator();
+
+ //validate c3
+ node = itc1.next().getValue();
+ validateNode(node, C3, C3_NAMESPACE, 1);
+
+ Iterator<Map.Entry<NodeKey, DataNode>> itc3 = ((InnerNode) node)
+ .childNodes()
+ .entrySet()
+ .iterator();
+
+ //validate c3
+ node = itc3.next().getValue();
+ validateNode(node, C4, C4_NAMESPACE, 0);
+ }
+
+ /**
+ * Unit test case to add a leaf node to current data tree.
+ */
+ @Test
+ public void testAddLeafNode() {
+
+ /*
+ * parent
+ * |------C1
+ * | |-----C3
+ * | |-----l1
+ * |
+ * |------C2
+ * | |----l2
+ * | |----l3
+ */
+ DataNode node = createDataTree();
+ /*
+ * RSC path == /parent/c1/c3.
+ * adding c4 to c3 node.
+ */
+ ResourceId id = ResourceId.builder()
+ .addBranchPointSchema(PARENT, PARENT_NAMESPACE)
+ .addBranchPointSchema(C2, C2_NAMESPACE).build();
+
+ List<NodeKey> keys = id.nodeKeys();
+
+ node.copyBuilder()
+
+ //Reach to c2 by fetching it from the map.
+ .getChildBuilder(keys.get(1))
+ //add l3 in c2.
+ .createChildBuilder(L3, L3_NAMESPACE, 15)
+ .type(MULTI_INSTANCE_LEAF_VALUE_NODE)
+ .addLeafListValue(16)
+
+ //build l3 and return to c2.
+ .exitNode()
+
+ //build c2 and return to parent.
+ .exitNode()
+
+ //build parent node.
+ .build();
+
+ //validate parent.
+ validateNode(node, PARENT, PARENT_NAMESPACE, 2);
+
+ //Validate c1
+ Iterator<Map.Entry<NodeKey, DataNode>> itp = ((InnerNode) node)
+ .childNodes()
+ .entrySet()
+ .iterator();
+ node = itp.next().getValue();
+ validateNode(node, C1, C1_NAMESPACE, 2);
+
+ //Validate c1
+ node = itp.next().getValue();
+ validateNode(node, C2, C2_NAMESPACE, 2);
+
+ Iterator<Map.Entry<NodeKey, DataNode>> itc2 = ((InnerNode) node)
+ .childNodes()
+ .entrySet()
+ .iterator();
+
+ //validate l2
+ node = itc2.next().getValue();
+ validateNode(node, L2, L2_NAMESPACE, 0);
+
+ //validate l3
+ node = itc2.next().getValue();
+ validateNode(node, L3, L3_NAMESPACE, 0);
+
+ //validate for leaf list key
+ assertThat(16, is(((LeafListKey) node.key()).value()));
+ }
+
+ /**
+ * Unit test case for adding a list node in current data tree.
+ */
+ @Test
+ public void testAddListNode() {
+ /*
+ * parent
+ * |------C1
+ * | |-----C3
+ * | |-------list1
+ * | |-----l1
+ * |
+ * |------C2
+ * | |----l2
+ */
+ DataNode node = createDataTree();
+ /*
+ * RSC path == /parent/c1/c3.
+ * adding c4 to c3 node.
+ */
+ ResourceId id = ResourceId.builder()
+ .addBranchPointSchema(PARENT, PARENT_NAMESPACE)
+ .addBranchPointSchema(C1, C1_NAMESPACE)
+ .addBranchPointSchema(C3, C3_NAMESPACE).build();
+
+ List<NodeKey> keys = id.nodeKeys();
+
+ node.copyBuilder()
+
+ //Reach to c1 by fetching it from the map.
+ .getChildBuilder(keys.get(1))
+
+ //reach to c3
+ .getChildBuilder(keys.get(2))
+
+ //add list in c3.
+ .createChildBuilder(LIST, LIST_NAMESPACE)
+ .type(MULTI_INSTANCE_NODE)
+
+ //Add key leaf1
+ .addKeyLeaf(KL1, KL1_NAMESPACE, 15)
+
+ //add key leaf 3
+ .addKeyLeaf(KL2, KL2_NAMESPACE, 16)
+
+ //build list and return to c3.
+ .exitNode()
+
+ //build c3 and return to c1.
+ .exitNode()
+
+ //build c1 and return to parent.
+ .exitNode()
+
+ //build parent node.
+ .build();
+
+ //validate parent.
+ validateNode(node, PARENT, PARENT_NAMESPACE, 2);
+
+ //Validate c1
+ Iterator<Map.Entry<NodeKey, DataNode>> itp = ((InnerNode) node)
+ .childNodes()
+ .entrySet()
+ .iterator();
+ node = itp.next().getValue();
+ validateNode(node, C1, C1_NAMESPACE, 2);
+
+ Iterator<Map.Entry<NodeKey, DataNode>> itc1 = ((InnerNode) node)
+ .childNodes()
+ .entrySet()
+ .iterator();
+
+ //validate c2
+ node = itc1.next().getValue();
+ validateNode(node, C3, C3_NAMESPACE, 1);
+
+ //validate c3
+ Iterator<Map.Entry<NodeKey, DataNode>> itc2 = ((InnerNode) node)
+ .childNodes()
+ .entrySet()
+ .iterator();
+
+ //validate l2
+ node = itc2.next().getValue();
+ validateNode(node, LIST, LIST_NAMESPACE, 0);
+
+ //validate for leaf list key
+ assertThat(2, is(((ListKey) node.key()).keyLeafs().size()));
+ }
+
+ /**
+ * Unit test case to remove one leaf node from data tree.
+ */
+ @Test
+ public void testRemoveLeafNode() {
+
+ DataNode node = createDataTree();
+ /*
+ * RSC path == /parent/c1/c3.
+ * adding c4 to c3 node.
+ */
+ ResourceId id = ResourceId.builder()
+ .addBranchPointSchema(PARENT, PARENT_NAMESPACE)
+ .addBranchPointSchema(C1, C1_NAMESPACE)
+ .addBranchPointSchema(L1, L1_NAMESPACE).build();
+
+ List<NodeKey> keys = id.nodeKeys();
+
+ node.copyBuilder()
+
+ // copy c1
+ .getChildBuilder(keys.get(1))
+
+ //delete l1 from c1
+ .deleteChild(keys.get(2))
+
+ //traverse back to parent node and build c1
+ .exitNode()
+
+ //build parent node
+ .build();
+
+ //validate parent.
+ validateNode(node, PARENT, PARENT_NAMESPACE, 2);
+
+ //Validate c1
+ Iterator<Map.Entry<NodeKey, DataNode>> itp = ((InnerNode) node)
+ .childNodes()
+ .entrySet()
+ .iterator();
+ node = itp.next().getValue();
+ validateNode(node, C1, C1_NAMESPACE, 1);
+
+ Iterator<Map.Entry<NodeKey, DataNode>> itc1 = ((InnerNode) node)
+ .childNodes()
+ .entrySet()
+ .iterator();
+
+ //validate c3
+ node = itc1.next().getValue();
+ validateNode(node, C3, C3_NAMESPACE, 0);
+ }
+
+ /**
+ * Unit test case to remove one child node from data tree.
+ */
+ @Test
+ public void testRemoveChildNode() {
+
+ DataNode node = createDataTree();
+ /*
+ * RSC path == /parent/c1/c3.
+ * adding c4 to c3 node.
+ */
+ ResourceId id = ResourceId.builder()
+ .addBranchPointSchema(PARENT, PARENT_NAMESPACE)
+ .addBranchPointSchema(C1, C1_NAMESPACE).build();
+
+ List<NodeKey> keys = id.nodeKeys();
+
+ node.copyBuilder()
+
+ //delete l1 from c1
+ .deleteChild(keys.get(1))
+
+ //build parent node
+ .build();
+
+ //validate parent.
+ validateNode(node, PARENT, PARENT_NAMESPACE, 1);
+
+ //Validate c1
+ Iterator<Map.Entry<NodeKey, DataNode>> itp = ((InnerNode) node)
+ .childNodes()
+ .entrySet()
+ .iterator();
+ node = itp.next().getValue();
+
+ validateNode(node, C2, C2_NAMESPACE, 1);
+ }
+
+ /**
+ * Validates each node.
+ *
+ * @param node data node
+ * @param name name of node
+ * @param namespace namespace of node
+ * @param size number of children
+ */
+ private void validateNode(DataNode node, String name, String namespace,
+ int size) {
+
+ String nodeName = node.key().schemaId().name();
+ String nodeNamespace = node.key().schemaId().namespace();
+
+ //validate parent node.
+ assertThat(true, is(nodeName.equals(name)));
+ assertThat(true, is(nodeNamespace.equals(namespace)));
+
+ if (node instanceof InnerNode) {
+ InnerNode in = (InnerNode) node;
+ Map<NodeKey, DataNode> children = in.childNodes();
+ assertThat(true, is(children.size() == size));
+ }
+ }
+}
\ No newline at end of file