blob: 23e190c1e263b31b4341596cca330661fecc0584 [file] [log] [blame]
/*
* 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.linker.impl;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.regex.Pattern;
import org.onosproject.yangutils.datamodel.TraversalType;
import org.onosproject.yangutils.datamodel.YangAtomicPath;
import org.onosproject.yangutils.datamodel.YangAugment;
import org.onosproject.yangutils.datamodel.YangAugmentableNode;
import org.onosproject.yangutils.datamodel.YangCase;
import org.onosproject.yangutils.datamodel.YangChoice;
import org.onosproject.yangutils.datamodel.YangGrouping;
import org.onosproject.yangutils.datamodel.YangIdentityRef;
import org.onosproject.yangutils.datamodel.YangImport;
import org.onosproject.yangutils.datamodel.YangInclude;
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.YangNode;
import org.onosproject.yangutils.datamodel.YangNodeIdentifier;
import org.onosproject.yangutils.datamodel.YangReferenceResolver;
import org.onosproject.yangutils.datamodel.YangType;
import org.onosproject.yangutils.datamodel.YangTypeDef;
import org.onosproject.yangutils.datamodel.YangUses;
import org.onosproject.yangutils.datamodel.exceptions.DataModelException;
import org.onosproject.yangutils.datamodel.utils.ResolvableStatus;
import org.onosproject.yangutils.datamodel.utils.YangConstructType;
import org.onosproject.yangutils.linker.exceptions.LinkerException;
import org.onosproject.yangutils.translator.exception.TranslatorException;
import static org.onosproject.yangutils.datamodel.TraversalType.CHILD;
import static org.onosproject.yangutils.datamodel.TraversalType.PARENT;
import static org.onosproject.yangutils.datamodel.TraversalType.ROOT;
import static org.onosproject.yangutils.datamodel.TraversalType.SIBILING;
import static org.onosproject.yangutils.datamodel.utils.DataModelUtils.addResolutionInfo;
import static org.onosproject.yangutils.datamodel.utils.GeneratedLanguage.JAVA_GENERATION;
import static org.onosproject.yangutils.datamodel.utils.YangConstructType.getYangConstructType;
import static org.onosproject.yangutils.datamodel.utils.builtindatatype.YangDataTypes.DERIVED;
import static org.onosproject.yangutils.datamodel.utils.builtindatatype.YangDataTypes.IDENTITYREF;
import static org.onosproject.yangutils.translator.tojava.YangDataModelFactory.getYangCaseNode;
import static org.onosproject.yangutils.utils.UtilConstants.COLON;
import static org.onosproject.yangutils.utils.UtilConstants.EMPTY_STRING;
/**
* Represent utilities for YANG linker.
*/
public final class YangLinkerUtils {
private static final int IDENTIFIER_LENGTH = 64;
private static final Pattern IDENTIFIER_PATTERN = Pattern.compile("[a-zA-Z_][a-zA-Z0-9_.-]*");
private static final String XML = "xml";
private YangLinkerUtils() {
}
/**
* Detects collision between target nodes leaf/leaf-list or child node with augmented leaf/leaf-list or child node.
*
* @param targetNode target node
* @param augment augment node
*/
private static void detectCollision(YangNode targetNode, YangAugment augment) {
YangNode targetNodesChild = targetNode.getChild();
YangNode augmentsChild = augment.getChild();
YangNode parent = targetNode;
if (targetNode instanceof YangAugment) {
parent = targetNode.getParent();
} else {
while (parent.getParent() != null) {
parent = parent.getParent();
}
}
if (targetNode instanceof YangChoice) {
addCaseNodeToChoiceTarget(augment);
} else {
detectCollisionInLeaveHolders(targetNode, augment);
while (augmentsChild != null) {
detectCollisionInChildNodes(targetNodesChild, augmentsChild, targetNode.getName(), parent.getName());
augmentsChild = augmentsChild.getNextSibling();
}
}
}
/*Detects collision between leaves/leaf-lists*/
private static void detectCollisionInLeaveHolders(YangNode targetNode, YangAugment augment) {
YangLeavesHolder targetNodesLeavesHolder = (YangLeavesHolder) targetNode;
YangNode parent = targetNode;
if (targetNode instanceof YangAugment) {
parent = targetNode.getParent();
} else {
while (parent.getParent() != null) {
parent = parent.getParent();
}
}
if (augment.getListOfLeaf() != null && augment.getListOfLeaf().size() != 0
&& targetNodesLeavesHolder.getListOfLeaf() != null) {
for (YangLeaf leaf : augment.getListOfLeaf()) {
for (YangLeaf targetLeaf : targetNodesLeavesHolder.getListOfLeaf()) {
if (targetLeaf.getName().equals(leaf.getName())) {
throw new LinkerException("target node " + targetNode.getName()
+ " contains augmented leaf " + leaf.getName() + " in module "
+ parent.getName() + " in " + targetNode.getLineNumber()
+ " at " + targetNode.getCharPosition() + " in " + targetNode.getFileName());
}
}
}
} else if (augment.getListOfLeafList() != null
&& augment.getListOfLeafList().size() != 0
&& augment.getListOfLeafList() != null) {
for (YangLeafList leafList : augment.getListOfLeafList()) {
for (YangLeafList targetLeafList : targetNodesLeavesHolder.getListOfLeafList()) {
if (targetLeafList.getName().equals(leafList.getName())) {
throw new LinkerException("target node " + targetNode.getName()
+ " contains augmented leaf-list" + leafList.getName() + " in module "
+ parent.getName() + " in " + targetNode.getLineNumber()
+ " at " + targetNode.getCharPosition() + " in " + targetNode.getFileName());
}
}
}
}
}
/*Detects collision for child nodes.*/
private static void detectCollisionInChildNodes(YangNode targetNodesChild, YangNode augmentsChild, String
targetName, String parentName) {
while (augmentsChild != null) {
while (targetNodesChild != null) {
if (targetNodesChild.getName().equals(augmentsChild.getName())) {
throw new LinkerException("target node " + targetName
+ " contains augmented child node " + augmentsChild.getName() + " in module "
+ parentName + " in " + targetNodesChild.getLineNumber()
+ " at " + targetNodesChild.getCharPosition() + " in " + targetNodesChild.getFileName());
}
targetNodesChild = targetNodesChild.getNextSibling();
}
augmentsChild = augmentsChild.getNextSibling();
}
}
/**
* Adds a case node in augment when augmenting a choice node.
*
* @param augment augment node
*/
private static void addCaseNodeToChoiceTarget(YangAugment augment) {
try {
YangNode child = augment.getChild();
List<YangNode> childNodes = new ArrayList<>();
while (child != null) {
childNodes.add(child);
child = child.getNextSibling();
}
augment.setChild(null);
for (YangNode node : childNodes) {
YangCase javaCase = getYangCaseNode(JAVA_GENERATION);
javaCase.setName(node.getName());
augment.addChild(javaCase);
node.setParent(javaCase);
node.setNextSibling(null);
node.setPreviousSibling(null);
javaCase.addChild(node);
}
if (augment.getListOfLeaf() != null) {
for (YangLeaf leaf : augment.getListOfLeaf()) {
YangCase javaCase = getYangCaseNode(JAVA_GENERATION);
javaCase.setName(leaf.getName());
javaCase.addLeaf(leaf);
augment.addChild(javaCase);
}
augment.getListOfLeaf().clear();
}
if (augment.getListOfLeafList() != null) {
for (YangLeafList leafList : augment.getListOfLeafList()) {
YangCase javaCase = getYangCaseNode(JAVA_GENERATION);
javaCase.setName(leafList.getName());
javaCase.addLeafList(leafList);
augment.addChild(javaCase);
}
augment.getListOfLeafList().clear();
}
} catch (DataModelException e) {
throw new TranslatorException("Failed to add child nodes to case node of augment " + augment.getName()
+ " in " + augment.getLineNumber() + " at "
+ augment.getCharPosition() + " in " + augment.getFileName());
}
}
//Detect collision between augment and choice children.
private void detectCollisionForChoiceNode(YangNode choice, YangNode augment) {
YangNode choiceChild = choice.getChild();
YangNode augmentChild = augment.getChild();
List<YangNode> choiceChildren = new ArrayList<>();
List<YangNode> augmentChildren = new ArrayList<>();
while (choiceChild != null) {
choiceChildren.add(choiceChild);
}
while (augmentChild != null) {
augmentChildren.add(augmentChild);
}
for (YangNode cChild : choiceChildren) {
for (YangNode aChild : augmentChildren) {
if (cChild.getName().equals(aChild.getName())) {
throw new LinkerException("case node " + aChild.getName() + "already present in choice " +
choice.getName() + " in " + cChild.getLineNumber() + " at " + cChild.getCharPosition()
+ " in " + cChild.getFileName());
}
}
}
}
/**
* Detects collision between target nodes and its all leaf/leaf-list or child node with augmented leaf/leaf-list or
* child node.
*
* @param targetNode target node
* @param augment augment node
*/
static void detectCollisionForAugmentedNode(YangNode targetNode, YangAugment augment) {
// Detect collision for target node and augment node.
detectCollision(targetNode, augment);
List<YangAugment> yangAugmentedInfo = ((YangAugmentableNode) targetNode).getAugmentedInfoList();
// Detect collision for target augment node and current augment node.
for (YangAugment info : yangAugmentedInfo) {
detectCollision(info, augment);
}
}
/**
* Returns list of path names that are needed from augment.
*
* @param augment instance of YANG augment
* @param remainingAncestors ancestor count to move in augment path
* @return list of path names needed in leafref
*/
static List<String> getPathWithAugment(YangAugment augment, int remainingAncestors) {
List<String> listOfPathName = new ArrayList<>();
for (YangAtomicPath atomicPath : augment.getTargetNode()) {
if (atomicPath.getNodeIdentifier().getPrefix() != null && !atomicPath.getNodeIdentifier().getPrefix()
.equals(EMPTY_STRING)) {
listOfPathName.add(atomicPath.getNodeIdentifier().getPrefix() + ":" +
atomicPath.getNodeIdentifier().getName());
} else {
listOfPathName.add(atomicPath.getNodeIdentifier().getName());
}
}
for (int countOfAncestor = 0; countOfAncestor < remainingAncestors; countOfAncestor++) {
listOfPathName.remove(listOfPathName.size() - 1);
}
return listOfPathName;
}
/**
* Skips the invalid nodes which cannot have data from YANG.
*
* @param currentParent current parent node reference
* @param leafref instance of YANG leafref
* @return parent node which can hold data
* @throws LinkerException a violation of linker rules
*/
static YangNode skipInvalidDataNodes(YangNode currentParent, YangLeafRef leafref)
throws LinkerException {
while (currentParent instanceof YangChoice || currentParent instanceof YangCase) {
if (currentParent.getParent() == null) {
throw new LinkerException("YANG file error: The target node, in the leafref path " +
leafref.getPath() + ", is invalid." + " in " + leafref.getLineNumber()
+ " at " + leafref.getCharPosition() + " in " + leafref.getFileName());
}
currentParent = currentParent.getParent();
}
return currentParent;
}
/**
* Checks and return valid node identifier.
*
* @param nodeIdentifierString string from yang file
* @param yangConstruct yang construct for creating error message
* @return valid node identifier
*/
static YangNodeIdentifier getValidNodeIdentifier(String nodeIdentifierString,
YangConstructType yangConstruct) {
String[] tmpData = nodeIdentifierString.split(Pattern.quote(COLON));
if (tmpData.length == 1) {
YangNodeIdentifier nodeIdentifier = new YangNodeIdentifier();
nodeIdentifier.setName(getValidIdentifier(tmpData[0], yangConstruct));
return nodeIdentifier;
} else if (tmpData.length == 2) {
YangNodeIdentifier nodeIdentifier = new YangNodeIdentifier();
nodeIdentifier.setPrefix(getValidIdentifier(tmpData[0], yangConstruct));
nodeIdentifier.setName(getValidIdentifier(tmpData[1], yangConstruct));
return nodeIdentifier;
} else {
throw new LinkerException("YANG file error : " +
getYangConstructType(yangConstruct) + " name " + nodeIdentifierString +
" is not valid.");
}
}
/**
* Validates identifier and returns concatenated string if string contains plus symbol.
*
* @param identifier string from yang file
* @param yangConstruct yang construct for creating error message=
* @return concatenated string after removing double quotes
*/
public static String getValidIdentifier(String identifier, YangConstructType yangConstruct) {
if (identifier.length() > IDENTIFIER_LENGTH) {
throw new LinkerException("YANG file error : " +
getYangConstructType(yangConstruct) + " name " + identifier + " is " +
"greater than 64 characters.");
} else if (!IDENTIFIER_PATTERN.matcher(identifier).matches()) {
throw new LinkerException("YANG file error : " +
getYangConstructType(yangConstruct) + " name " + identifier + " is not " +
"valid.");
} else if (identifier.toLowerCase().startsWith(XML)) {
throw new LinkerException("YANG file error : " +
getYangConstructType(yangConstruct) + " identifier " + identifier +
" must not start with (('X'|'x') ('M'|'m') ('L'|'l')).");
} else {
return identifier;
}
}
/**
* Updates the priority for all the input files.
*
* @param yangNodeSet set of YANG files info
*/
public static void updateFilePriority(Set<YangNode> yangNodeSet) {
for (YangNode yangNode : yangNodeSet) {
updateFilePriorityOfNode(yangNode);
}
}
/**
* Updates priority of the node.
*
* @param yangNode YANG node information
*/
private static void updateFilePriorityOfNode(YangNode yangNode) {
int curNodePriority = yangNode.getPriority();
if (yangNode instanceof YangReferenceResolver) {
List<YangImport> yangImportList = ((YangReferenceResolver) yangNode).getImportList();
Iterator<YangImport> importInfoIterator = yangImportList.iterator();
// Run through the imported list to update priority.
while (importInfoIterator.hasNext()) {
YangImport yangImport = importInfoIterator.next();
YangNode importedNode = yangImport.getImportedNode();
if (curNodePriority >= importedNode.getPriority()) {
importedNode.setPriority(curNodePriority + 1);
updateFilePriorityOfNode(importedNode);
}
}
List<YangInclude> yangIncludeList = ((YangReferenceResolver) yangNode).getIncludeList();
Iterator<YangInclude> includeInfoIterator = yangIncludeList.iterator();
// Run through the imported list to update priority.
while (includeInfoIterator.hasNext()) {
YangInclude yangInclude = includeInfoIterator.next();
YangNode includedNode = yangInclude.getIncludedNode();
if (curNodePriority >= includedNode.getPriority()) {
includedNode.setPriority(curNodePriority + 1);
updateFilePriorityOfNode(includedNode);
}
}
}
}
/**
* Add the unresolved data under the root leve grouping to be resolved, since it will be used in interfile uses.
*
* @param referenceResolver module / sub-module
*/
public static void resolveGroupingInDefinationScope(YangReferenceResolver referenceResolver) {
YangNode potentialInterFileGrouping = ((YangNode) referenceResolver).getChild();
while (potentialInterFileGrouping != null) {
if (potentialInterFileGrouping instanceof YangGrouping) {
addGroupingResolvableEntitiesToResolutionList((YangGrouping) potentialInterFileGrouping);
}
potentialInterFileGrouping = potentialInterFileGrouping.getNextSibling();
}
}
/**
* Add the interfile grouping resolvable entities to reesolution list.
*
* @param interFileGrouping interfile grouping
*/
private static void addGroupingResolvableEntitiesToResolutionList(YangGrouping interFileGrouping) {
YangNode curNode = interFileGrouping;
TraversalType curTraversal = ROOT;
addResolvableLeavesToResolutionList((YangLeavesHolder) curNode);
curTraversal = CHILD;
curNode = interFileGrouping.getChild();
if (curNode == null) {
return;
}
while (curNode != interFileGrouping) {
if (curTraversal != PARENT) {
if (curNode instanceof YangGrouping || curNode instanceof YangUses) {
if (curNode.getNextSibling() != null) {
curTraversal = SIBILING;
curNode = curNode.getNextSibling();
} else {
curTraversal = PARENT;
curNode = curNode.getParent();
}
continue;
}
if (curNode instanceof YangLeavesHolder) {
addResolvableLeavesToResolutionList((YangLeavesHolder) curNode);
} else if (curNode instanceof YangTypeDef) {
List<YangType<?>> typeList = ((YangTypeDef) curNode).getTypeList();
if (!typeList.isEmpty()) {
YangType<?> type = typeList.get(0);
if (type.getDataType() == DERIVED) {
if (type.getResolvableStatus() != ResolvableStatus.RESOLVED) {
type.setTypeForInterFileGroupingResolution(true);
// Add resolution information to the list
YangResolutionInfoImpl resolutionInfo =
new YangResolutionInfoImpl<YangType>(type, curNode, type.getLineNumber(),
type.getCharPosition());
try {
addResolutionInfo(resolutionInfo);
} catch (DataModelException e) {
String errorInfo = "Error in file: " + curNode.getName() + " in " +
curNode.getFileName() + " at " +
"line: " + e.getLineNumber() +
" at position: " + e.getCharPositionInLine()
+ e.getLocalizedMessage();
throw new LinkerException("Failed to add type info in grouping to resolution "
+ errorInfo);
}
}
}
}
}
}
if (curTraversal != PARENT && curNode.getChild() != null) {
curTraversal = CHILD;
curNode = curNode.getChild();
} else if (curNode.getNextSibling() != null) {
curTraversal = SIBILING;
curNode = curNode.getNextSibling();
} else {
curTraversal = PARENT;
curNode = curNode.getParent();
}
}
}
/**
* Add resolvable leaves type info to resolution list.
*
* @param leavesHolder leaves holder node
*/
private static void addResolvableLeavesToResolutionList(YangLeavesHolder leavesHolder) {
if (leavesHolder.getListOfLeaf() != null && !leavesHolder.getListOfLeaf().isEmpty()) {
for (YangLeaf leaf : leavesHolder.getListOfLeaf()) {
YangType type = leaf.getDataType();
if (type.getDataType() == DERIVED) {
type.setTypeForInterFileGroupingResolution(true);
// Add resolution information to the list
YangResolutionInfoImpl resolutionInfo =
new YangResolutionInfoImpl<>(type, (YangNode) leavesHolder,
type.getLineNumber(), type.getCharPosition());
try {
addResolutionInfo(resolutionInfo);
} catch (DataModelException e) {
throw new LinkerException("Failed to add leaf type info in grouping, to resolution ");
}
} else if (type.getDataType() == IDENTITYREF) {
YangIdentityRef identityRef = (YangIdentityRef) type.getDataTypeExtendedInfo();
identityRef.setIdentityForInterFileGroupingResolution(true);
// Add resolution information to the list
YangResolutionInfoImpl resolutionInfo =
new YangResolutionInfoImpl<YangIdentityRef>(identityRef, (YangNode) leavesHolder,
identityRef.getLineNumber(), identityRef.getCharPosition());
try {
addResolutionInfo(resolutionInfo);
} catch (DataModelException e) {
throw new LinkerException("Failed to add leaf identity ref info in grouping, to resolution ");
}
}
}
}
if (leavesHolder.getListOfLeafList() != null && !leavesHolder.getListOfLeafList().isEmpty()) {
for (YangLeafList leafList : leavesHolder.getListOfLeafList()) {
YangType type = leafList.getDataType();
if (type.getDataType() == DERIVED) {
type.setTypeForInterFileGroupingResolution(true);
// Add resolution information to the list
YangResolutionInfoImpl resolutionInfo =
new YangResolutionInfoImpl<YangType>(type, (YangNode) leavesHolder,
type.getLineNumber(), type.getCharPosition());
try {
addResolutionInfo(resolutionInfo);
} catch (DataModelException e) {
throw new LinkerException("Failed to add leaf type info in grouping, to resolution ");
}
} else if (type.getDataType() == IDENTITYREF) {
YangIdentityRef identityRef = (YangIdentityRef) type.getDataTypeExtendedInfo();
identityRef.setIdentityForInterFileGroupingResolution(true);
// Add resolution information to the list
YangResolutionInfoImpl resolutionInfo =
new YangResolutionInfoImpl<YangIdentityRef>(identityRef, (YangNode) leavesHolder,
identityRef.getLineNumber(), identityRef.getCharPosition());
try {
addResolutionInfo(resolutionInfo);
} catch (DataModelException e) {
throw new LinkerException("Failed to add leaf identity ref info in grouping, to resolution ");
}
}
}
}
}
}