[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