/* * 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.yms.app.ydt; import org.onosproject.yangutils.datamodel.YangSchemaNode; import org.onosproject.yangutils.datamodel.YangSchemaNodeContextInfo; import org.onosproject.yangutils.datamodel.YangSchemaNodeIdentifier; import org.onosproject.yangutils.datamodel.exceptions.DataModelException; import org.onosproject.yms.app.ydt.exceptions.YdtException; import org.onosproject.yms.ydt.YdtContext; import org.onosproject.yms.ydt.YdtContextOperationType; import org.onosproject.yms.ydt.YdtExtendedInfoType; import org.onosproject.yms.ydt.YdtType; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import static org.onosproject.yms.app.ydt.YdtConstants.errorMsg; /** * Represents implementation of interfaces to build and obtain YANG data tree * which is data (sub)instance representation, abstract of protocol. */ public abstract class YdtNode<T> implements YdtExtendedContext, Cloneable { // YDT formatted error string private static final String FMT_NON_LIST_STR = "List of key cannot be created for leaf and leaf-list %s node."; private static final String FMT_VAL_N = "Value cannot be set in non leaf %s node."; private static final String FMT_VAL_NS = "ValueSet cannot be set in non leaf-list %s node."; private static final String FMT_VAL_IN = "Value cannot be invoke from non leaf %s node."; private static final String FMT_VAL_INS = "ValueSet cannot be invoke from non leaf-list %s node"; // YDT error string private static final String E_EXIST = "Node is already part of a tree"; private static final String E_ATOMIC = "Child to be added is not atomic, it already has a child"; private static final String E_SIB = "Child to be added is not atomic, it already has a next sibling"; private static final String E_PRE = "Child to be added is not atomic, it already has a previous " + "sibling"; private static final String E_SUPPORT = "Requested node type not supported"; /* * Parent reference. */ private YdtNode parent; /* * First child reference. */ private YdtNode child; /* * Next sibling reference. */ private YdtNode nextSibling; /* * Previous sibling reference. */ private YdtNode previousSibling; /* * Last child reference. */ private YdtNode lastChild; /* * Type of node. */ private final YdtType ydtType; /* * Flag to keep the track of context switch, * if set then traverse back to parent in YDT app tree else no need. */ private boolean isContextSwitch; /* * YDT extended information. */ private T ydtExtendedInfo; /* * YDT extended information type. */ private YdtExtendedInfoType ydtExtendedInfoType; /* * Ydt map to keep the track of node added under current parent node. */ final Map<YangSchemaNodeIdentifier, YdtNode<T>> ydtNodeMap = new HashMap<>(); /* * Ydt map to keep the track of multi instance node added under current * parent node. */ private final Map<YangSchemaNodeIdentifier, List<YdtNode<YdtMultiInstanceNode>>> ydtMultiInsMap = new HashMap<>(); /* * Reference for data-model schema node. */ private YangSchemaNode yangSchemaNode; /* * Reference for ydt node operation type. */ private YdtContextOperationType ydtContextOperationType; /* * Ydt map to keep the track of application information object * with respective type. */ private final Map<AppType, Object> ydtAppInfoMap = new HashMap<>(); private YdtContext clonedNode; /** * Creates a specific type of node. * * @param type of YDT node * @param node schema node */ YdtNode(YdtType type, YangSchemaNode node) { ydtType = type; yangSchemaNode = node; } /** * Creates a specific type of node. * * @param type of YDT node */ YdtNode(YdtType type) { ydtType = type; } /** * Returns the cloned ydt node. * * @return clonedNode cloned ydt node */ public YdtContext getClonedNode() { return clonedNode; } /** * Sets the cloned node. * * @param clonedNode cloned ydt node */ public void setClonedNode(YdtContext clonedNode) { this.clonedNode = clonedNode; } @Override public String getName() { return yangSchemaNode.getName(); } @Override public String getNamespace() { return yangSchemaNode.getNameSpace().getModuleNamespace(); } @Override public String getModuleNameAsNameSpace() { return yangSchemaNode.getNameSpace().getModuleName(); } @Override public <T> T getYdtContextExtendedInfo() { return (T) ydtExtendedInfo; } @Override public YdtExtendedInfoType getYdtExtendedInfoType() { return ydtExtendedInfoType; } @Override public YdtType getYdtType() { return ydtType; } @Override public YdtNode getParent() { return parent; } @Override public YdtNode getFirstChild() { return child; } @Override public YdtNode getNextSibling() { return nextSibling; } public YangSchemaNode getYangSchemaNode() { return yangSchemaNode; } @Override public YdtNode getLastChild() { return lastChild; } @Override public Object getAppInfo(AppType appType) { return ydtAppInfoMap.get(appType); } @Override public void addAppInfo(AppType appType, Object object) { ydtAppInfoMap.put(appType, object); } /** * Returns child schema node context information. It is used by YMS to * obtain the child schema corresponding to data node identifier. * * @param id represents a identifier of YANG data tree node * @return YANG data node context information * @throws YdtException when user requested node schema doesn't exist */ public YangSchemaNodeContextInfo getSchemaNodeContextInfo( YangSchemaNodeIdentifier id) throws YdtException { try { return getYangSchemaNode().getChildSchema(id); } catch (DataModelException e) { throw new YdtException(e.getLocalizedMessage()); } } /** * Adds the given value to the non single instance leaf node. * <p> * This default implementation throws an exception stating that * the value cannot be added. Subclasses may override this method * to provide the correct behavior for their specific implementation. * * @param value value in a single instance node * @throws YdtException when fails to add value for non single instance * leaf node */ public void addValue(String value) throws YdtException { throw new YdtException(errorMsg(FMT_VAL_N, getName())); } /** * Creates the list of key element's of multi instance node. * This will not be applicable on leaf and leaf-list node. * * @throws YdtException when user requested multi instance node is missing * any of the key element in request or requested * node is of type other then multi instance node */ public void createKeyNodeList() throws YdtException { throw new YdtException(errorMsg(FMT_NON_LIST_STR, getName())); } /** * Adds the given value to the non single instance leaf node. * <p> * This default implementation throws an exception stating that * the value cannot be added. Subclasses may override this method * to provide the correct behavior for their specific implementation. * This will be applicable in case of call from SBI so no need * to validate the value. * * @param value value in a single instance leaf node * @param isKeyLeaf true, for key leaf; false non key leaf * @throws YdtException when fails to add value for non single instance * leaf node */ public void addValueWithoutValidation(String value, boolean isKeyLeaf) throws YdtException { throw new YdtException(errorMsg(FMT_VAL_N, getName())); } /** * Adds the given valueSet to the non multi instance leaf node. * <p> * This default implementation throws an exception stating that * the value cannot be added. Subclasses may override this method * to provide the correct behavior for their specific implementation. * * @param valueSet valueSet in a multi instance leaf node * @throws YdtException when fails to add value set for non multi instance * leaf node */ public void addValueSet(Set<String> valueSet) throws YdtException { throw new YdtException(errorMsg(FMT_VAL_NS, getName())); } /** * Adds the given valueSet to the non multi instance leaf node. * <p> * This default implementation throws an exception stating that * the value cannot be added. Subclasses may override this method * to provide the correct behavior for their specific implementation. * This will be applicable in case of call from SBI so no need * to validate the value. * * @param valueSet valueSet in a multi instance leaf node * @throws YdtException when fails to add value set for non multi instance * leaf node */ public void addValueSetWithoutValidation(Set<String> valueSet) throws YdtException { throw new YdtException(errorMsg(FMT_VAL_NS, getName())); } /** * Validates requested node allowed to have duplicate entry or not. * <p> * This default implementation throws an exception stating that * the duplicate entry found. Subclasses may override this method * to provide the correct behavior for their specific implementation. * * @throws YdtException when fails to process valid duplicate entry in YDT */ void validDuplicateEntryProcessing() throws YdtException { } /** * Returns already existing YdtNode in Ydt tree with same nodeIdentifier. * * @param id represents a identifier of YANG data tree node * @return YDT node * @throws YdtException when user requested node already part of YDT tree. */ public YdtNode getCollidingChild(YangSchemaNodeIdentifier id) throws YdtException { // Find the key in YDT map for getting the colliding node. YdtNode collidingChild = ydtNodeMap.get(id); /* * If colliding child exist then process colliding node in respective * YDT node type. */ if (collidingChild != null) { collidingChild.validDuplicateEntryProcessing(); return collidingChild; } return null; } /** * Sets the parent of node. * * @param parent node */ public void setParent(YdtNode parent) { this.parent = parent; } /** * Sets the first instance of a child node. * * @param child is only child to be set */ public void setChild(YdtNode child) { this.child = child; } /** * Sets the next sibling of node. * * @param sibling YANG node */ public void setNextSibling(YdtNode sibling) { nextSibling = sibling; } /** * Returns the previous sibling of a node. * * @return previous sibling of a node */ public YdtNode getPreviousSibling() { return previousSibling; } /** * Sets the previous sibling. * * @param previousSibling points to predecessor sibling */ public void setPreviousSibling(YdtNode previousSibling) { this.previousSibling = previousSibling; } @Override public String getValue() throws YdtException { throw new YdtException(errorMsg(FMT_VAL_IN, getName())); } @Override public Set<String> getValueSet() throws YdtException { throw new YdtException(errorMsg(FMT_VAL_INS, getName())); } /** * Sets the data-model node reference for of a given node. * * @param yangSchemaNode YANG data node */ public void setYangSchemaNode(YangSchemaNode yangSchemaNode) { this.yangSchemaNode = yangSchemaNode; } /** * Sets the last instance of a child node. * * @param child is last child to be set */ public void setLastChild(YdtNode child) { lastChild = child; } /** * Adds a child node. * The children sibling list will be sorted based on node * type. This will add single child or sub-tree based on isAtomic flag. * * @param newChild refers to a new child to be added * @param isAtomic boolean flag to maintain atomicity of the current node * @throws YdtException in case of violation of any YDT rule */ public void addChild(YdtContext newChild, boolean isAtomic) throws YdtException { if (!(newChild instanceof YdtNode)) { throw new YdtException(errorMsg(E_SUPPORT)); } YdtNode node = (YdtNode) newChild; if (node.getParent() == null) { node.setParent(this); } else if (!node.getParent().equals(this)) { throw new YdtException(E_EXIST); } if (node.getFirstChild() != null && isAtomic) { throw new YdtException(E_ATOMIC); } if (node.getNextSibling() != null) { throw new YdtException(E_SIB); } if (node.getPreviousSibling() != null) { throw new YdtException(E_PRE); } // If new node needs to be added as first child. if (getFirstChild() == null) { setChild(node); setLastChild(node); return; } // If new node needs to be added as last child. YdtNode curNode = getLastChild(); curNode.setNextSibling(node); node.setPreviousSibling(curNode); setLastChild(node); } @Override public YdtContextOperationType getYdtContextOperationType() { return ydtContextOperationType; } /** * Sets type of yang data tree node operation. * * @param opType type of yang data tree node operation */ public void setYdtContextOperationType(YdtContextOperationType opType) { ydtContextOperationType = opType; } /** * Updates ydt maps of current context parent node. * * @param node ydt node for which map need to be updated */ void updateYdtMap(YdtNode node) { YangSchemaNodeIdentifier id = node.getYangSchemaNode() .getYangSchemaNodeIdentifier(); /* * If node to be added is of type multi instance node(list) then multi * instance node to be updated */ if (node.getYdtType() == YdtType.MULTI_INSTANCE_NODE) { updateMultiInsMap(id, node); } /* * If entry for multi instance node is already there with same id then * existing entry will be overwritten by the new entry. */ ydtNodeMap.put(id, node); } /** * Updates ydt multi instance map of current context parent node. * * @param id object node identifier * @param node ydt node for which map need to be updated */ private void updateMultiInsMap(YangSchemaNodeIdentifier id, YdtNode node) { List<YdtNode<YdtMultiInstanceNode>> list = ydtMultiInsMap.get(id); if (list == null) { list = new ArrayList<>(); ydtMultiInsMap.put(id, list); } list.add(node); } /** * Returns the flag for node if context switch. * * @return isContextSwitch flag of a node */ public boolean getAppContextSwitch() { return isContextSwitch; } /** * Sets the flag to keep the track of context switch. * If it is set then when YDT get traverToParent then * traverse back to parent in YDT application tree. */ public void setAppContextSwitch() { isContextSwitch = true; } /** * Validates all multi Instance nodes inside current context. * * @throws YdtException when fails to validate multi instance node */ public void validateMultiInstanceNode() throws YdtException { // Set for checking whether input string is unique or not. Set<String> keyStringSet = new HashSet<>(); if (ydtMultiInsMap.size() != 0) { /* * Iterating over values in map and find multi instance node list * only. */ for (List<YdtNode<YdtMultiInstanceNode>> ydtNodeList : ydtMultiInsMap.values()) { try { ydtNodeList.get(0).validateInstances(keyStringSet, ydtNodeList); } catch (YdtException e) { throw new YdtException(e.getLocalizedMessage()); } } } } /** * Validates the given list of instances by verifying the allowed * instance count and key element uniqueness. * <p> * This default implementation do nothing if requested node is of type * other then multiInstanceNode. Subclasses may override this method * to provide the correct behavior for their specific implementation. * * @param keyStringSet set to validate the key element uniqueness * @param ydtNodeList list of instance's of same list * @throws YdtException when user requested multi instance node instance's * count doesn't fit into the allowed instance's limit * or doesn't have unique key's */ void validateInstances(Set<String> keyStringSet, List<YdtNode<YdtMultiInstanceNode>> ydtNodeList) throws YdtException { } /** * Clones the current node contents and create a new node. * * @return cloned node * @throws CloneNotSupportedException clone is not supported * by the referred node */ public YdtNode clone() throws CloneNotSupportedException { YdtNode clonedNode = (YdtNode) super.clone(); clonedNode.setPreviousSibling(null); clonedNode.setNextSibling(null); clonedNode.setParent(null); clonedNode.setChild(null); clonedNode.setLastChild(null); return clonedNode; } }