| /* |
| * 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.yangutils.datamodel; |
| |
| import org.onosproject.yangutils.datamodel.exceptions.DataModelException; |
| import org.onosproject.yangutils.parser.Parsable; |
| import org.onosproject.yangutils.translator.tojava.TraversalType; |
| |
| import static org.onosproject.yangutils.translator.tojava.TraversalType.CHILD; |
| import static org.onosproject.yangutils.translator.tojava.TraversalType.PARENT; |
| import static org.onosproject.yangutils.translator.tojava.TraversalType.SIBILING; |
| |
| /** |
| * Represents base class of a node in data model tree. |
| */ |
| public abstract class YangNode |
| implements Cloneable { |
| |
| /** |
| * Type of node. |
| */ |
| private YangNodeType nodeType; |
| |
| /** |
| * Parent reference. |
| */ |
| private YangNode parent; |
| |
| /** |
| * First child reference. |
| */ |
| private YangNode child; |
| |
| /** |
| * Next sibling reference. |
| */ |
| private YangNode nextSibling; |
| |
| /** |
| * Previous sibling reference. |
| */ |
| private YangNode previousSibling; |
| |
| /** |
| * Returns the nodes name. |
| * |
| * @return nodes name |
| */ |
| public abstract String getName(); |
| |
| /** |
| * Sets the nodes name. |
| * |
| * @param name nodes name |
| */ |
| public abstract void setName(String name); |
| |
| /** |
| * Creates a YANG node object. |
| */ |
| @SuppressWarnings("unused") |
| private YangNode() { |
| |
| } |
| |
| /** |
| * Creates a specific type of node. |
| * |
| * @param type of YANG node |
| */ |
| protected YangNode(YangNodeType type) { |
| setNodeType(type); |
| } |
| |
| /** |
| * Returns the node type. |
| * |
| * @return node type |
| */ |
| public YangNodeType getNodeType() { |
| return nodeType; |
| } |
| |
| /** |
| * Sets the node type. |
| * |
| * @param nodeType type of node |
| */ |
| private void setNodeType(YangNodeType nodeType) { |
| this.nodeType = nodeType; |
| } |
| |
| /** |
| * Returns the parent of node. |
| * |
| * @return parent of node |
| */ |
| public YangNode getParent() { |
| return parent; |
| } |
| |
| /** |
| * Sets the parent of node. |
| * |
| * @param parent node |
| */ |
| public void setParent(YangNode parent) { |
| this.parent = parent; |
| } |
| |
| /** |
| * Returns the first child of node. |
| * |
| * @return first child of node |
| */ |
| public YangNode getChild() { |
| return child; |
| } |
| |
| /** |
| * Sets the first instance of a child node. |
| * |
| * @param child is only child to be set |
| */ |
| public void setChild(YangNode child) { |
| this.child = child; |
| } |
| |
| /** |
| * Returns the next sibling of node. |
| * |
| * @return next sibling of node |
| */ |
| public YangNode getNextSibling() { |
| return nextSibling; |
| } |
| |
| /** |
| * Sets the next sibling of node. |
| * |
| * @param sibling YANG node |
| */ |
| private void setNextSibling(YangNode sibling) { |
| nextSibling = sibling; |
| } |
| |
| /** |
| * Returns the previous sibling. |
| * |
| * @return previous sibling node |
| */ |
| public YangNode getPreviousSibling() { |
| return previousSibling; |
| } |
| |
| /** |
| * Sets the previous sibling. |
| * |
| * @param previousSibling points to predecessor sibling |
| */ |
| private void setPreviousSibling(YangNode previousSibling) { |
| this.previousSibling = previousSibling; |
| } |
| |
| /** |
| * Adds a child node, the children sibling list will be sorted based on node |
| * type. |
| * |
| * @param newChild refers to a child to be added |
| * @throws DataModelException due to violation in data model rules |
| */ |
| public void addChild(YangNode newChild) |
| throws DataModelException { |
| if (newChild.getNodeType() == null) { |
| throw new DataModelException("Abstract node cannot be inserted into a tree"); |
| } |
| |
| if (newChild.getParent() == null) { |
| newChild.setParent(this); |
| } else if (newChild.getParent() != this) { |
| throw new DataModelException("Node is already part of a tree"); |
| } |
| |
| if (newChild.getChild() != null) { |
| throw new DataModelException("Child to be added is not atomic, it already has a child"); |
| } |
| |
| if (newChild.getNextSibling() != null) { |
| throw new DataModelException("Child to be added is not atomic, it already has a next sibling"); |
| } |
| |
| if (newChild.getPreviousSibling() != null) { |
| throw new DataModelException("Child to be added is not atomic, it already has a previous sibling"); |
| } |
| |
| /* First child to be added */ |
| if (getChild() == null) { |
| setChild(newChild); |
| return; |
| } |
| |
| YangNode curNode; |
| curNode = getChild(); |
| |
| /* |
| * Get the predecessor child of new child |
| */ |
| while (curNode.getNextSibling() != null) { |
| |
| curNode = curNode.getNextSibling(); |
| } |
| |
| /* If the new node needs to be the last child */ |
| if (curNode.getNextSibling() == null) { |
| curNode.setNextSibling(newChild); |
| newChild.setPreviousSibling(curNode); |
| } |
| } |
| |
| /** |
| * Clone the current node contents and create a new node. |
| * |
| * @return cloned node |
| * @throws CloneNotSupportedException clone is not supported by the referred |
| * node |
| */ |
| @Override |
| public YangNode clone() |
| throws CloneNotSupportedException { |
| YangNode clonedNode = (YangNode) super.clone(); |
| clonedNode.setParent(null); |
| clonedNode.setChild(null); |
| clonedNode.setNextSibling(null); |
| clonedNode.setPreviousSibling(null); |
| return clonedNode; |
| } |
| |
| /** |
| * Clone the subtree from the specified source node to the mentioned target |
| * node. The source and target root node cloning is carried out by the |
| * caller. |
| * |
| * @param srcRootNode source node for sub tree cloning |
| * @param dstRootNode destination node where the sub tree needs to be cloned |
| * @throws DataModelException data model error |
| */ |
| public static void cloneSubTree(YangNode srcRootNode, YangNode dstRootNode) |
| throws DataModelException { |
| |
| YangNode nextNodeToClone = srcRootNode; |
| TraversalType curTraversal; |
| |
| YangNode clonedTreeCurNode = dstRootNode; |
| YangNode newNode = null; |
| |
| nextNodeToClone = nextNodeToClone.getChild(); |
| if (nextNodeToClone == null) { |
| return; |
| } else { |
| /** |
| * Root level cloning is taken care in the caller. |
| */ |
| curTraversal = CHILD; |
| } |
| |
| /** |
| * Caller ensures the cloning of the root nodes |
| */ |
| try { |
| while (nextNodeToClone != srcRootNode) { |
| if (nextNodeToClone == null) { |
| throw new DataModelException("Internal error: Cloning failed, source tree null pointer reached"); |
| } |
| if (curTraversal != PARENT) { |
| newNode = nextNodeToClone.clone(); |
| detectCollisionWhileCloning(clonedTreeCurNode, newNode, curTraversal); |
| } |
| |
| if (curTraversal == CHILD) { |
| |
| /** |
| * add the new node to the cloned tree. |
| */ |
| clonedTreeCurNode.addChild(newNode); |
| |
| /** |
| * update the cloned tree's traversal current node as the |
| * new node. |
| */ |
| clonedTreeCurNode = newNode; |
| } else if (curTraversal == SIBILING) { |
| |
| clonedTreeCurNode.addNextSibling(newNode); |
| clonedTreeCurNode = newNode; |
| } else if (curTraversal == PARENT) { |
| clonedTreeCurNode = clonedTreeCurNode.getParent(); |
| } |
| |
| if (curTraversal != PARENT && nextNodeToClone.getChild() != null) { |
| curTraversal = CHILD; |
| |
| /** |
| * update the traversal's current node. |
| */ |
| nextNodeToClone = nextNodeToClone.getChild(); |
| |
| } else if (nextNodeToClone.getNextSibling() != null) { |
| |
| curTraversal = SIBILING; |
| |
| nextNodeToClone = nextNodeToClone.getNextSibling(); |
| } else { |
| curTraversal = PARENT; |
| nextNodeToClone = nextNodeToClone.getParent(); |
| } |
| } |
| } catch (CloneNotSupportedException e) { |
| throw new DataModelException("Failed to clone the tree"); |
| } |
| |
| } |
| |
| /** |
| * Detects collision when the grouping is deep copied to the uses's parent. |
| * |
| * @param currentNode parent/previous sibling node for the new node |
| * @param newNode node which has to be added |
| * @param addAs traversal type of the node |
| * @throws DataModelException data model error |
| */ |
| private static void detectCollisionWhileCloning(YangNode currentNode, YangNode newNode, TraversalType addAs) |
| throws DataModelException { |
| if (!(currentNode instanceof CollisionDetector) |
| || !(newNode instanceof Parsable)) { |
| throw new DataModelException("Node in data model tree does not support collision detection"); |
| } |
| |
| CollisionDetector collisionDetector = (CollisionDetector) currentNode; |
| Parsable parsable = (Parsable) newNode; |
| if (addAs == TraversalType.CHILD) { |
| collisionDetector.detectCollidingChild(newNode.getName(), parsable.getYangConstructType()); |
| } else if (addAs == TraversalType.SIBILING) { |
| currentNode = currentNode.getParent(); |
| if (!(currentNode instanceof CollisionDetector)) { |
| throw new DataModelException("Node in data model tree does not support collision detection"); |
| } |
| collisionDetector = (CollisionDetector) currentNode; |
| collisionDetector.detectCollidingChild(newNode.getName(), parsable.getYangConstructType()); |
| } else { |
| throw new DataModelException("Errored tree cloning"); |
| } |
| |
| } |
| |
| /** |
| * Add a new next sibling. |
| * |
| * @param newSibling new sibling to be added |
| * @throws DataModelException data model error |
| */ |
| private void addNextSibling(YangNode newSibling) |
| throws DataModelException { |
| |
| if (newSibling.getNodeType() == null) { |
| throw new DataModelException("Cloned abstract node cannot be inserted into a tree"); |
| } |
| |
| if (newSibling.getParent() == null) { |
| /** |
| * Since the siblings needs to have a common parent, set the parent |
| * as the current node's parent |
| */ |
| newSibling.setParent(getParent()); |
| |
| } else { |
| throw new DataModelException("Node is already part of a tree, and cannot be added as a sibling"); |
| } |
| |
| if (newSibling.getPreviousSibling() == null) { |
| newSibling.setPreviousSibling(this); |
| setNextSibling(newSibling); |
| } else { |
| throw new DataModelException("New sibling to be added is not atomic, it already has a previous sibling"); |
| } |
| |
| if (newSibling.getChild() != null) { |
| throw new DataModelException("Sibling to be added is not atomic, it already has a child"); |
| } |
| |
| if (newSibling.getNextSibling() != null) { |
| throw new DataModelException("Sibling to be added is not atomic, it already has a next sibling"); |
| } |
| } |
| } |