| /* |
| * 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.utils; |
| |
| import org.onosproject.yangutils.datamodel.CollisionDetector; |
| import org.onosproject.yangutils.datamodel.ResolvableType; |
| import org.onosproject.yangutils.datamodel.YangAtomicPath; |
| import org.onosproject.yangutils.datamodel.YangAugment; |
| import org.onosproject.yangutils.datamodel.YangBase; |
| import org.onosproject.yangutils.datamodel.YangEntityToResolveInfoImpl; |
| import org.onosproject.yangutils.datamodel.YangEnumeration; |
| import org.onosproject.yangutils.datamodel.YangIdentityRef; |
| import org.onosproject.yangutils.datamodel.YangIfFeature; |
| import org.onosproject.yangutils.datamodel.YangImport; |
| import org.onosproject.yangutils.datamodel.YangLeaf; |
| import org.onosproject.yangutils.datamodel.YangLeafList; |
| import org.onosproject.yangutils.datamodel.YangLeafRef; |
| import org.onosproject.yangutils.datamodel.YangLeavesHolder; |
| import org.onosproject.yangutils.datamodel.YangModule; |
| import org.onosproject.yangutils.datamodel.YangNode; |
| import org.onosproject.yangutils.datamodel.YangReferenceResolver; |
| import org.onosproject.yangutils.datamodel.YangResolutionInfo; |
| import org.onosproject.yangutils.datamodel.YangRpc; |
| import org.onosproject.yangutils.datamodel.YangType; |
| import org.onosproject.yangutils.datamodel.YangUnion; |
| import org.onosproject.yangutils.datamodel.YangUses; |
| import org.onosproject.yangutils.datamodel.exceptions.DataModelException; |
| import org.onosproject.yangutils.datamodel.utils.builtindatatype.YangDataTypes; |
| |
| import java.io.FileInputStream; |
| import java.io.IOException; |
| import java.io.ObjectInputStream; |
| import java.util.ArrayList; |
| import java.util.Iterator; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| /** |
| * Represents utilities for data model tree. |
| */ |
| public final class DataModelUtils { |
| public static final String TRUE = "true"; |
| public static final String FALSE = "false"; |
| |
| /** |
| * Creates a new data model tree utility. |
| */ |
| private DataModelUtils() { |
| } |
| |
| /** |
| * Detects the colliding identifier name in a given YANG node and its child. |
| * |
| * @param identifierName name for which collision detection is to be checked |
| * @param dataType type of YANG node asking for detecting collision |
| * @param node instance of calling node |
| * @throws DataModelException a violation of data model rules |
| */ |
| public static void detectCollidingChildUtil(String identifierName, YangConstructType dataType, YangNode node) |
| throws DataModelException { |
| if (dataType == YangConstructType.USES_DATA || dataType == YangConstructType.GROUPING_DATA) { |
| detectCollidingForUsesGrouping(identifierName, dataType, node); |
| } else { |
| if (node instanceof YangLeavesHolder) { |
| YangLeavesHolder leavesHolder = (YangLeavesHolder) node; |
| detectCollidingLeaf(leavesHolder.getListOfLeaf(), identifierName); |
| detectCollidingLeafList(leavesHolder.getListOfLeafList(), identifierName); |
| } |
| node = node.getChild(); |
| while (node != null) { |
| Parsable parsable = (Parsable) node; |
| if (node instanceof CollisionDetector |
| && parsable.getYangConstructType() != YangConstructType.USES_DATA |
| && parsable.getYangConstructType() != YangConstructType.GROUPING_DATA) { |
| ((CollisionDetector) node).detectSelfCollision(identifierName, dataType); |
| } |
| node = node.getNextSibling(); |
| } |
| } |
| } |
| |
| /** |
| * Detects colliding of uses and grouping only with uses and grouping respectively. |
| * |
| * @param identifierName name for which collision detection is to be checked |
| * @param dataType type of YANG node asking for detecting collision |
| * @param node node instance of calling node |
| * @throws DataModelException a violation of data model rules |
| */ |
| public static void detectCollidingForUsesGrouping(String identifierName, YangConstructType dataType, YangNode node) |
| throws DataModelException { |
| |
| node = node.getChild(); |
| while (node != null) { |
| Parsable parsable = (Parsable) node; |
| if (node instanceof CollisionDetector |
| && parsable.getYangConstructType() == dataType) { |
| ((CollisionDetector) node).detectSelfCollision(identifierName, dataType); |
| } |
| node = node.getNextSibling(); |
| } |
| } |
| |
| /** |
| * Detects the colliding identifier name in a given leaf node. |
| * |
| * @param listOfLeaf List of leaves to detect collision |
| * @param identifierName name for which collision detection is to be checked |
| * @throws DataModelException a violation of data model rules |
| */ |
| private static void detectCollidingLeaf(List<YangLeaf> listOfLeaf, String identifierName) |
| throws DataModelException { |
| |
| if (listOfLeaf == null) { |
| return; |
| } |
| for (YangLeaf leaf : listOfLeaf) { |
| if (leaf.getName().equals(identifierName)) { |
| throw new DataModelException("YANG file error: Duplicate input identifier detected, same as leaf \"" |
| + leaf.getName() + "\""); |
| } |
| } |
| } |
| |
| /** |
| * Detects the colliding identifier name in a given leaf-list node. |
| * |
| * @param listOfLeafList list of leaf-lists to detect collision |
| * @param identifierName name for which collision detection is to be checked |
| * @throws DataModelException a violation of data model rules |
| */ |
| private static void detectCollidingLeafList(List<YangLeafList> listOfLeafList, String identifierName) |
| throws DataModelException { |
| |
| if (listOfLeafList == null) { |
| return; |
| } |
| for (YangLeafList leafList : listOfLeafList) { |
| if (leafList.getName().equals(identifierName)) { |
| throw new DataModelException("YANG file error: Duplicate input identifier detected, same as leaf " + |
| "list \"" + leafList.getName() + "\""); |
| } |
| } |
| } |
| |
| /** |
| * Add a resolution information. |
| * |
| * @param resolutionInfo information about the YANG construct which has to be resolved |
| * @throws DataModelException a violation of data model rules |
| */ |
| public static void addResolutionInfo(YangResolutionInfo resolutionInfo) |
| throws DataModelException { |
| |
| /* get the module node to add maintain the list of nested reference */ |
| YangNode curNode = resolutionInfo.getEntityToResolveInfo() |
| .getHolderOfEntityToResolve(); |
| while (!(curNode instanceof YangReferenceResolver)) { |
| curNode = curNode.getParent(); |
| if (curNode == null) { |
| throw new DataModelException("Internal datamodel error: Datamodel tree is not correct"); |
| } |
| } |
| YangReferenceResolver resolutionNode = (YangReferenceResolver) curNode; |
| |
| if (resolutionInfo.getEntityToResolveInfo() |
| .getEntityToResolve() instanceof YangType) { |
| resolutionNode.addToResolutionList(resolutionInfo, |
| ResolvableType.YANG_DERIVED_DATA_TYPE); |
| } else if (resolutionInfo.getEntityToResolveInfo() |
| .getEntityToResolve() instanceof YangUses) { |
| resolutionNode.addToResolutionList(resolutionInfo, |
| ResolvableType.YANG_USES); |
| } else if (resolutionInfo.getEntityToResolveInfo() |
| .getEntityToResolve() instanceof YangAugment) { |
| resolutionNode.addToResolutionList(resolutionInfo, |
| ResolvableType.YANG_AUGMENT); |
| } else if (resolutionInfo.getEntityToResolveInfo() |
| .getEntityToResolve() instanceof YangIfFeature) { |
| resolutionNode.addToResolutionList(resolutionInfo, |
| ResolvableType.YANG_IF_FEATURE); |
| } else if (resolutionInfo.getEntityToResolveInfo() |
| .getEntityToResolve() instanceof YangLeafRef) { |
| resolutionNode.addToResolutionList(resolutionInfo, |
| ResolvableType.YANG_LEAFREF); |
| } else if (resolutionInfo.getEntityToResolveInfo().getEntityToResolve() instanceof YangBase) { |
| resolutionNode.addToResolutionList(resolutionInfo, ResolvableType.YANG_BASE); |
| } else if (resolutionInfo.getEntityToResolveInfo().getEntityToResolve() instanceof YangIdentityRef) { |
| resolutionNode.addToResolutionList(resolutionInfo, ResolvableType.YANG_IDENTITYREF); |
| } |
| } |
| |
| /** |
| * Resolve linking for a resolution list. |
| * |
| * @param resolutionList resolution list for which linking to be done |
| * @param dataModelRootNode module/sub-module node |
| * @throws DataModelException a violation of data model rules |
| */ |
| public static void resolveLinkingForResolutionList(List<YangResolutionInfo> resolutionList, |
| YangReferenceResolver dataModelRootNode) |
| throws DataModelException { |
| |
| for (YangResolutionInfo resolutionInfo : resolutionList) { |
| resolutionInfo.resolveLinkingForResolutionInfo(dataModelRootNode); |
| } |
| } |
| |
| /** |
| * Links type/uses referring to typedef/uses of inter YANG file. |
| * |
| * @param resolutionList resolution list for which linking to be done |
| * @param dataModelRootNode module/sub-module node |
| * @throws DataModelException a violation of data model rules |
| */ |
| public static void linkInterFileReferences(List<YangResolutionInfo> resolutionList, |
| YangReferenceResolver dataModelRootNode) |
| throws DataModelException { |
| /* |
| * Run through the resolution list, find type/uses referring to inter |
| * file typedef/grouping, ask for linking. |
| */ |
| for (YangResolutionInfo resolutionInfo : resolutionList) { |
| resolutionInfo.linkInterFile(dataModelRootNode); |
| } |
| } |
| |
| /** |
| * Checks if there is any rpc defined in the module or sub-module. |
| * |
| * @param rootNode root node of the data model |
| * @return status of rpc's existence |
| */ |
| public static boolean isRpcChildNodePresent(YangNode rootNode) { |
| YangNode childNode = rootNode.getChild(); |
| while (childNode != null) { |
| if (childNode instanceof YangRpc) { |
| return true; |
| } |
| childNode = childNode.getNextSibling(); |
| } |
| return false; |
| } |
| |
| /** |
| * Returns referred node in a given set. |
| * |
| * @param yangNodeSet YANG node set |
| * @param refNodeName name of the node which is referred |
| * @return referred node's reference |
| */ |
| public static YangNode findReferredNode(Set<YangNode> yangNodeSet, String refNodeName) { |
| /* |
| * Run through the YANG files to see which YANG file matches the |
| * referred node name. |
| */ |
| for (YangNode yangNode : yangNodeSet) { |
| if (yangNode.getName().equals(refNodeName)) { |
| return yangNode; |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Returns the contained data model parent node. |
| * |
| * @param currentNode current node which parent contained node is required |
| * @return parent node in which the current node is an attribute |
| */ |
| public static YangNode getParentNodeInGenCode(YangNode currentNode) { |
| |
| /* |
| * TODO: recursive parent lookup to support choice/augment/uses. TODO: |
| * need to check if this needs to be updated for |
| * choice/case/augment/grouping |
| */ |
| return currentNode.getParent(); |
| } |
| |
| /** |
| * Returns de-serializes YANG data-model nodes. |
| * |
| * @param serializableInfoSet YANG file info set |
| * @return de-serializes YANG data-model nodes |
| * @throws IOException when fails do IO operations |
| */ |
| public static List<YangNode> deSerializeDataModel(List<String> serializableInfoSet) throws IOException { |
| |
| List<YangNode> nodes = new ArrayList<>(); |
| for (String fileInfo : serializableInfoSet) { |
| YangNode node = null; |
| try { |
| FileInputStream fileInputStream = new FileInputStream(fileInfo); |
| ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream); |
| node = (YangNode) objectInputStream.readObject(); |
| nodes.add(node); |
| objectInputStream.close(); |
| fileInputStream.close(); |
| } catch (IOException | ClassNotFoundException e) { |
| throw new IOException(fileInfo + " not found."); |
| } |
| } |
| return nodes; |
| } |
| |
| /** |
| * Clones the list of leaves and list of leaf list in the leaves holder. |
| * |
| * @param leavesHolder YANG node potentially containing leaves or leaf lists |
| * @param yangUses instance of YANG uses |
| * @throws CloneNotSupportedException clone is not supported |
| * @throws DataModelException data model error |
| */ |
| public static void cloneLeaves(YangLeavesHolder leavesHolder, YangUses yangUses) |
| throws CloneNotSupportedException, DataModelException { |
| List<YangLeaf> currentListOfLeaves = leavesHolder.getListOfLeaf(); |
| if (currentListOfLeaves != null) { |
| List<YangLeaf> clonedLeavesList = new LinkedList<YangLeaf>(); |
| for (YangLeaf leaf : currentListOfLeaves) { |
| YangLeaf clonedLeaf = leaf.clone(); |
| if (yangUses.getCurrentGroupingDepth() == 0) { |
| YangEntityToResolveInfoImpl resolveInfo = |
| resolveLeafrefUnderGroupingForLeaf(clonedLeaf, leavesHolder, yangUses); |
| if (resolveInfo != null) { |
| yangUses.addEntityToResolve(resolveInfo); |
| } |
| } |
| clonedLeaf.setContainedIn(leavesHolder); |
| clonedLeavesList.add(clonedLeaf); |
| } |
| leavesHolder.setListOfLeaf(clonedLeavesList); |
| } |
| |
| List<YangLeafList> currentListOfLeafList = leavesHolder.getListOfLeafList(); |
| if (currentListOfLeafList != null) { |
| List<YangLeafList> clonedListOfLeafList = new LinkedList<YangLeafList>(); |
| for (YangLeafList leafList : currentListOfLeafList) { |
| YangLeafList clonedLeafList = leafList.clone(); |
| if (yangUses.getCurrentGroupingDepth() == 0) { |
| YangEntityToResolveInfoImpl resolveInfo = |
| resolveLeafrefUnderGroupingForLeafList(clonedLeafList, leavesHolder); |
| if (resolveInfo != null) { |
| yangUses.addEntityToResolve(resolveInfo); |
| } |
| } |
| clonedLeafList.setContainedIn(leavesHolder); |
| clonedListOfLeafList.add(clonedLeafList); |
| } |
| leavesHolder.setListOfLeafList(clonedListOfLeafList); |
| } |
| } |
| |
| /** |
| * Resolves leafref in leaf, which are under grouping by adding it to the resolution list. |
| * |
| * @param clonedLeaf cloned leaf in uses from grouping |
| * @param leafParentHolder holder of the leaf from uses |
| * @param yangUses YANG uses |
| * @return entity of leafref which has to be resolved |
| * @throws DataModelException data model error |
| */ |
| public static YangEntityToResolveInfoImpl resolveLeafrefUnderGroupingForLeaf(YangLeaf clonedLeaf, |
| YangLeavesHolder leafParentHolder, |
| YangUses yangUses) throws |
| DataModelException { |
| if (clonedLeaf.getDataType().getDataTypeExtendedInfo() instanceof YangLeafRef) { |
| YangLeafRef leafrefForCloning = (YangLeafRef) clonedLeaf.getDataType().getDataTypeExtendedInfo(); |
| // Conversion of prefixes in absolute path while cloning them. |
| convertThePrefixesDuringChange(leafrefForCloning, yangUses); |
| leafrefForCloning.setParentNodeOfLeafref((YangNode) leafParentHolder); |
| YangEntityToResolveInfoImpl yangEntityToResolveInfo = new YangEntityToResolveInfoImpl(); |
| yangEntityToResolveInfo.setEntityToResolve(leafrefForCloning); |
| yangEntityToResolveInfo.setHolderOfEntityToResolve((YangNode) leafParentHolder); |
| yangEntityToResolveInfo.setLineNumber(leafrefForCloning.getLineNumber()); |
| yangEntityToResolveInfo.setCharPosition(leafrefForCloning.getCharPosition()); |
| return yangEntityToResolveInfo; |
| } |
| return null; |
| } |
| |
| /** |
| * Converts the prefixes in all the nodes of the leafref with respect to the uses node. |
| * |
| * @param leafrefForCloning leafref that is to be cloned |
| * @param yangUses instance of YANG uses where cloning is done |
| * @throws DataModelException data model error |
| */ |
| private static void convertThePrefixesDuringChange(YangLeafRef leafrefForCloning, YangUses yangUses) throws |
| DataModelException { |
| List<YangAtomicPath> atomicPathList = leafrefForCloning.getAtomicPath(); |
| if (atomicPathList != null && !atomicPathList.isEmpty()) { |
| Iterator<YangAtomicPath> atomicPathIterator = atomicPathList.listIterator(); |
| while (atomicPathIterator.hasNext()) { |
| YangAtomicPath atomicPath = atomicPathIterator.next(); |
| Map<String, String> prefixesAndItsImportNameNode = leafrefForCloning.getPrefixAndItsImportedModule(); |
| if (!prefixesAndItsImportNameNode.isEmpty() || prefixesAndItsImportNameNode != null) { |
| String prefixInPath = atomicPath.getNodeIdentifier().getPrefix(); |
| String importedNodeName = prefixesAndItsImportNameNode.get(prefixInPath); |
| assignCurrentLeafrefWithNewPrefixes(importedNodeName, atomicPath, yangUses); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Assigns leafref with new prefixes while cloning. |
| * |
| * @param importedNodeName imported node name from grouping |
| * @param atomicPath atomic path in leafref |
| * @param node instance of YANG uses where cloning is done |
| * @throws DataModelException data model error |
| */ |
| private static void assignCurrentLeafrefWithNewPrefixes(String importedNodeName, YangAtomicPath atomicPath, |
| YangNode node) throws DataModelException { |
| while (!(node instanceof YangReferenceResolver)) { |
| node = node.getParent(); |
| if (node == null) { |
| throw new DataModelException("Internal datamodel error: Datamodel tree is not correct"); |
| } |
| } |
| if (node instanceof YangModule) { |
| List<YangImport> importInUsesList = ((YangModule) node).getImportList(); |
| if (importInUsesList != null && !importInUsesList.isEmpty()) { |
| Iterator<YangImport> importInUsesListIterator = importInUsesList.listIterator(); |
| while (importInUsesListIterator.hasNext()) { |
| YangImport importInUsesNode = importInUsesListIterator.next(); |
| if (importInUsesNode.getModuleName().equals(importedNodeName)) { |
| atomicPath.getNodeIdentifier().setPrefix(importInUsesNode.getPrefixId()); |
| } |
| } |
| } |
| } |
| } |
| |
| /** |
| * Resolves leafref in leaf-list, which are under grouping by adding it to the resolution list. |
| * |
| * @param clonedLeafList cloned leaf-list in uses from grouping |
| * @param leafListParentHolder holder of the leaf-list from uses |
| * @return entity of leafref which has to be resolved |
| * @throws DataModelException data model error |
| */ |
| public static YangEntityToResolveInfoImpl resolveLeafrefUnderGroupingForLeafList(YangLeafList clonedLeafList, |
| YangLeavesHolder |
| leafListParentHolder) |
| throws DataModelException { |
| if (clonedLeafList.getDataType().getDataTypeExtendedInfo() instanceof YangLeafRef) { |
| YangLeafRef leafrefForCloning = (YangLeafRef) clonedLeafList.getDataType().getDataTypeExtendedInfo(); |
| leafrefForCloning.setParentNodeOfLeafref((YangNode) leafListParentHolder); |
| YangEntityToResolveInfoImpl yangEntityToResolveInfo = new YangEntityToResolveInfoImpl(); |
| yangEntityToResolveInfo.setEntityToResolve(leafrefForCloning); |
| yangEntityToResolveInfo.setHolderOfEntityToResolve((YangNode) leafListParentHolder); |
| yangEntityToResolveInfo.setLineNumber(leafrefForCloning.getLineNumber()); |
| yangEntityToResolveInfo.setCharPosition(leafrefForCloning.getCharPosition()); |
| return yangEntityToResolveInfo; |
| } |
| return null; |
| } |
| |
| /** |
| * Clones the union or enum leaves. If there is any cloned leaves whose type is union/enum then the corresponding |
| * type info needs to be updated to the cloned new type node. |
| * |
| * @param leavesHolder cloned leaves holder, for whom the leaves reference needs to be updated |
| * @throws DataModelException when fails to do data model operations |
| */ |
| public static void updateClonedLeavesUnionEnumRef(YangLeavesHolder leavesHolder) throws DataModelException { |
| List<YangLeaf> currentListOfLeaves = leavesHolder.getListOfLeaf(); |
| if (currentListOfLeaves != null) { |
| for (YangLeaf leaf : currentListOfLeaves) { |
| if (leaf.getDataType().getDataType() == YangDataTypes.ENUMERATION |
| || leaf.getDataType().getDataType() == YangDataTypes.UNION) { |
| try { |
| updateClonedTypeRef(leaf.getDataType(), leavesHolder); |
| } catch (DataModelException e) { |
| throw e; |
| } |
| } |
| } |
| |
| } |
| |
| List<YangLeafList> currentListOfLeafList = leavesHolder.getListOfLeafList(); |
| if (currentListOfLeafList != null) { |
| for (YangLeafList leafList : currentListOfLeafList) { |
| if (leafList.getDataType().getDataType() == YangDataTypes.ENUMERATION |
| || leafList.getDataType().getDataType() == YangDataTypes.UNION) { |
| try { |
| updateClonedTypeRef(leafList.getDataType(), leavesHolder); |
| } catch (DataModelException e) { |
| throw e; |
| } |
| } |
| } |
| } |
| } |
| |
| /** |
| * Updates the types extended info pointer to point to the cloned type node. |
| * |
| * @param dataType data type, whose extended info needs to be pointed to the cloned type |
| * @param leavesHolder the leaves holder having the cloned type |
| */ |
| private static void updateClonedTypeRef(YangType dataType, YangLeavesHolder leavesHolder) |
| throws DataModelException { |
| if (!(leavesHolder instanceof YangNode)) { |
| throw new DataModelException("Data model error: cloned leaves holder is not a node"); |
| } |
| YangNode potentialTypeNode = ((YangNode) leavesHolder).getChild(); |
| while (potentialTypeNode != null) { |
| String dataTypeName = null; |
| if (dataType.getDataType() == YangDataTypes.ENUMERATION) { |
| YangEnumeration enumNode = (YangEnumeration) dataType.getDataTypeExtendedInfo(); |
| dataTypeName = enumNode.getName(); |
| } else if (dataType.getDataType() == YangDataTypes.UNION) { |
| YangUnion unionNode = (YangUnion) dataType.getDataTypeExtendedInfo(); |
| dataTypeName = unionNode.getName(); |
| } |
| if (potentialTypeNode.getName().contentEquals(dataTypeName)) { |
| dataType.setDataTypeExtendedInfo((Object) potentialTypeNode); |
| return; |
| } |
| potentialTypeNode = potentialTypeNode.getNextSibling(); |
| } |
| |
| throw new DataModelException("Data model error: cloned leaves type is not found"); |
| } |
| } |