blob: c9c0064eec3f37cdf365c243f07c7e1935bcec1a [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.datamodel;
import java.util.Stack;
import org.onosproject.yangutils.datamodel.exceptions.DataModelException;
import static org.onosproject.yangutils.datamodel.ResolvableStatus.INTRA_FILE_RESOLVED;
import static org.onosproject.yangutils.datamodel.ResolvableStatus.LINKED;
import static org.onosproject.yangutils.datamodel.ResolvableStatus.RESOLVED;
import static org.onosproject.yangutils.datamodel.ResolvableStatus.UNRESOLVED;
/**
* Represents resolution object which will be resolved by linker.
*
* @param <T> type of resolution entity uses / type
*/
public class YangResolutionInfo<T> {
/**
* Information about the entity that needs to be resolved.
*/
private YangEntityToResolveInfo<T> entityToResolveInfo;
// Error Line number.
private int lineNumber;
// Error character position.
private int charPosition;
/*
* Stack for type/uses is maintained for hierarchical references, this is
* used during resolution.
*/
private Stack<YangEntityToResolveInfo<T>> partialResolvedStack;
// Module/Sub-module prefix.
private String resolutionInfoRootNodePrefix;
/**
* It is private to ensure the overloaded method be invoked to create an
* object.
*/
@SuppressWarnings("unused")
private YangResolutionInfo() {
}
/**
* Creates a resolution information object with all the inputs.
*
* @param dataNode current parsable data node
* @param holderNode parent YANG node
* @param lineNumber error line number
* @param charPositionInLine error character position in line
*/
public YangResolutionInfo(T dataNode, YangNode holderNode, int lineNumber, int charPositionInLine) {
setEntityToResolveInfo(new YangEntityToResolveInfo<T>());
getEntityToResolveInfo().setEntityToResolve(dataNode);
getEntityToResolveInfo().setHolderOfEntityToResolve(holderNode);
this.setLineNumber(lineNumber);
this.setCharPosition(charPositionInLine);
setPartialResolvedStack(new Stack<YangEntityToResolveInfo<T>>());
}
/**
* Resolve linking with all the ancestors node for a resolution info.
*
* @param resolutionInfoNodePrefix module/sub-module prefix
* @throws DataModelException DataModelException a violation of data model
* rules
*/
public void resolveLinkingForResolutionInfo(String resolutionInfoNodePrefix)
throws DataModelException {
this.resolutionInfoRootNodePrefix = resolutionInfoNodePrefix;
// Current node to resolve, it can be a YANG type or YANG uses.
T entityToResolve = getEntityToResolveInfo().getEntityToResolve();
// Check if linking is already done
if (entityToResolve instanceof Resolvable) {
Resolvable resolvable = (Resolvable) entityToResolve;
if (resolvable.getResolvableStatus() == RESOLVED) {
/**
* entity is already resolved, so nothing to do
*/
return;
}
} else {
throw new DataModelException("Data Model Exception: Entity to resolved is other than type/uses");
}
// Push the initial entity to resolve in stack.
addInPartialResolvedStack(getEntityToResolveInfo());
linkAndResolvePartialResolvedStack();
}
/**
* Resolves linking with ancestors.
*
* @throws DataModelException a violation of data model rules
*/
private void linkAndResolvePartialResolvedStack()
throws DataModelException {
while (getPartialResolvedStack().size() != 0) {
// Current node to resolve, it can be a YANG type or YANG uses.
T entityToResolve = getCurrentEntityToResolveFromStack();
// Check if linking is already done
if (entityToResolve instanceof Resolvable) {
Resolvable resolvable = (Resolvable) entityToResolve;
switch (resolvable.getResolvableStatus()) {
case RESOLVED: {
/*
* If the entity is already resolved in the stack, then
* pop it and continue with the remaining stack elements
* to resolve
*/
getPartialResolvedStack().pop();
break;
}
case LINKED: {
/*
* If the top of the stack is already linked then
* resolve the references and pop the entity and
* continue with remaining stack elements to resolve
*/
resolveTopOfStack();
getPartialResolvedStack().pop();
break;
}
case INTRA_FILE_RESOLVED: {
/*
* TODO: this needs to be deleted, when inter-file
* resolution is implmeneted
*/
getPartialResolvedStack().pop();
break;
}
case UNRESOLVED: {
linkTopOfStackReferenceUpdateStack();
if (resolvable.getResolvableStatus() == UNRESOLVED) {
// If current entity is still not resolved, then linking/resolution has failed.
DataModelException dataModelException =
new DataModelException("YANG file error: Unable to find base "
+ "typedef/grouping for given type/uses");
dataModelException.setLine(getLineNumber());
dataModelException.setCharPosition(getCharPosition());
throw dataModelException;
}
break;
}
default: {
throw new DataModelException("Data Model Exception: Unsupported, linker state");
}
}
} else {
throw new DataModelException("Data Model Exception: Entity to resolved is other than type/uses");
}
}
}
/**
* Resolve the current entity in the stack.
*/
private void resolveTopOfStack() {
((Resolvable) getCurrentEntityToResolveFromStack()).resolve();
if (((Resolvable) getCurrentEntityToResolveFromStack()).getResolvableStatus()
!= INTRA_FILE_RESOLVED) {
// Sets the resolution status in inside the type/uses.
((Resolvable) getCurrentEntityToResolveFromStack()).setResolvableStatus(RESOLVED);
}
}
/**
* Resolves linking for a node child and siblings.
*
* @throws DataModelException data model error
*/
private void linkTopOfStackReferenceUpdateStack()
throws DataModelException {
if (!isSelfFileReference()) {
/*
* TODO: use mojo utilities to load the referred module/sub-module
* and get the reference to the corresponding referred entity
*/
((Resolvable) getCurrentEntityToResolveFromStack()).setResolvableStatus(INTRA_FILE_RESOLVED);
return;
}
/**
* Try to resolve the top of the stack and update partial resolved stack
* if there is recursive references
*/
YangNode potentialAncestorWithReferredNode = getPartialResolvedStack().peek()
.getHolderOfEntityToResolve();
/**
* Traverse up in the ancestor tree to check if the referred node is
* defined
*/
while (potentialAncestorWithReferredNode != null) {
/**
* Check for the referred node defined in a ancestor scope
*/
YangNode potentialReferredNode = potentialAncestorWithReferredNode.getChild();
if (isReferredNodeInSiblingListProcessed(potentialReferredNode)) {
return;
}
potentialAncestorWithReferredNode = potentialAncestorWithReferredNode.getParent();
}
}
/**
* Check if the reference in self file or in external file.
*
* @return true if self file reference, false otherwise
* @throws DataModelException a violation of data model rules
*/
private boolean isSelfFileReference()
throws DataModelException {
String prefix;
if (getCurrentEntityToResolveFromStack() instanceof YangType) {
prefix = ((YangType<?>) getCurrentEntityToResolveFromStack()).getPrefix();
} else if (getCurrentEntityToResolveFromStack() instanceof YangUses) {
prefix = ((YangUses) getCurrentEntityToResolveFromStack()).getPrefix();
} else {
throw new DataModelException("Data Model Exception: Entity to resolved is other than type/uses");
}
if (prefix == null) {
return true;
}
return prefix.contentEquals(resolutionInfoRootNodePrefix);
}
/**
* Check for the referred node defined in a ancestor scope.
*
* @param potentialReferredNode potential referred node
* @return status of resolution and updating the partial resolved stack with
* the any recursive references
* @throws DataModelException data model errors
*/
private boolean isReferredNodeInSiblingListProcessed(YangNode potentialReferredNode)
throws DataModelException {
while (potentialReferredNode != null) {
// Check if the potential referred node is the actual referred node
if (isReferredNode(potentialReferredNode)) {
// Adds reference link of entity to the node under resolution.
addReferredEntityLink(potentialReferredNode);
/**
* resolve the reference and update the partial resolution stack
* with any further recursive references
*/
addUnresolvedRecursiveReferenceToStack(potentialReferredNode);
/*
* return true, since the reference is linked and any recursive
* unresolved references is added to the stack
*/
return true;
}
potentialReferredNode = potentialReferredNode.getNextSibling();
}
return false;
}
/**
* Check if the potential referred node is the actual referred node.
*
* @param potentialReferredNode typedef/grouping node
* @return true if node is of resolve type otherwise false
* @throws DataModelException a violation of data model rules
*/
private boolean isReferredNode(YangNode potentialReferredNode)
throws DataModelException {
if (getCurrentEntityToResolveFromStack() instanceof YangType) {
if (potentialReferredNode instanceof YangTypeDef) {
/*
* Check if name of node name matches with the entity being
* resolved
*/
return isNodeNameSameAsResolutionInfoName(potentialReferredNode);
}
} else if (getCurrentEntityToResolveFromStack() instanceof YangUses) {
if (potentialReferredNode instanceof YangGrouping) {
/*
* Check if name of node name matches with the entity being
* resolved
*/
return isNodeNameSameAsResolutionInfoName(potentialReferredNode);
}
} else {
throw new DataModelException("Data Model Exception: Entity to resolved is other than type/uses");
}
return false;
}
/**
* Check if node name is same as name in resolution info, i.e. name of
* typedef/grouping is same as name of type/uses.
*
* @param node typedef/grouping node
* @return true if node name is same as name in resolution info, otherwise
* false
* @throws DataModelException a violation of data model rules
*/
private boolean isNodeNameSameAsResolutionInfoName(YangNode node)
throws DataModelException {
if (getCurrentEntityToResolveFromStack() instanceof YangType) {
if (node.getName().contentEquals(
((YangType<?>) getCurrentEntityToResolveFromStack())
.getDataTypeName())) {
return true;
}
} else if (getCurrentEntityToResolveFromStack() instanceof YangUses) {
if (node.getName().contentEquals(
((YangUses) getCurrentEntityToResolveFromStack()).getName())) {
return true;
}
} else {
throw new DataModelException("Data Model Exception: Entity to resolved is other than type/uses");
}
return false;
}
/**
* Adds reference of grouping/typedef in uses/type.
*
* @param referredNode grouping/typedef node being referred
* @throws DataModelException a violation of data model rules
*/
private void addReferredEntityLink(YangNode referredNode)
throws DataModelException {
if (getCurrentEntityToResolveFromStack() instanceof YangType) {
YangDerivedInfo<?> derivedInfo = (YangDerivedInfo<?>)
((YangType<?>) getCurrentEntityToResolveFromStack()).getDataTypeExtendedInfo();
derivedInfo.setReferredTypeDef((YangTypeDef) referredNode);
} else if (getCurrentEntityToResolveFromStack() instanceof YangUses) {
((YangUses) getCurrentEntityToResolveFromStack())
.setRefGroup((YangGrouping) referredNode);
} else {
throw new DataModelException("Data Model Exception: Entity to resolved is other than type/uses");
}
// Sets the resolution status in inside the type/uses.
((Resolvable) getCurrentEntityToResolveFromStack()).setResolvableStatus(LINKED);
}
/**
* Checks if type/grouping has further reference to typedef/ unresolved
* uses. Add it to the partial resolve stack and return the status of
* addition to stack.
*
* @param referredNode grouping/typedef node
* @throws DataModelException a violation of data model rules
*/
private void addUnresolvedRecursiveReferenceToStack(YangNode referredNode)
throws DataModelException {
if (getCurrentEntityToResolveFromStack() instanceof YangType) {
/*
* Checks if typedef type is derived
*/
if (((YangTypeDef) referredNode).getTypeDefBaseType().getDataType()
== YangDataTypes.DERIVED) {
YangEntityToResolveInfo<YangType<?>> unResolvedEntityInfo = new YangEntityToResolveInfo<YangType<?>>();
unResolvedEntityInfo.setEntityToResolve(((YangTypeDef) referredNode)
.getTypeDefBaseType());
unResolvedEntityInfo.setHolderOfEntityToResolve(referredNode);
addInPartialResolvedStack((YangEntityToResolveInfo<T>) unResolvedEntityInfo);
}
} else if (getCurrentEntityToResolveFromStack() instanceof YangUses) {
/*
* Search if the grouping has any un resolved uses child, if so
* return true, else return false.
*/
addUnResolvedUsesToStack(referredNode);
} else {
throw new DataModelException("Data Model Exception: Entity to resolved is other than type/uses");
}
}
/**
* Return if there is any unresolved uses in grouping.
*
* @param node grouping/typedef node
*/
private void addUnResolvedUsesToStack(YangNode node) {
/**
* Search the grouping node's children for presence of uses node.
*/
YangNode curNode = node.getChild();
while (curNode != null) {
if (curNode instanceof YangUses) {
ResolvableStatus curResolveStatus = ((Resolvable) curNode).getResolvableStatus();
if (curResolveStatus == UNRESOLVED) {
/**
* The current uses is not resolved, add it to partial
* resolved stack
*/
YangEntityToResolveInfo<YangUses> unResolvedEntityInfo = new YangEntityToResolveInfo<YangUses>();
unResolvedEntityInfo.setEntityToResolve((YangUses) curNode);
unResolvedEntityInfo.setHolderOfEntityToResolve(node);
addInPartialResolvedStack((YangEntityToResolveInfo<T>) unResolvedEntityInfo);
}
}
curNode = curNode.getNextSibling();
}
return;
}
/**
* Returns error position.
*
* @return error position
*/
public int getCharPosition() {
return charPosition;
}
/**
* Sets error position.
*
* @param charPosition position of error
*/
public void setCharPosition(int charPosition) {
this.charPosition = charPosition;
}
/**
* Returns error character position in line.
*
* @return error character position in line
*/
public int getLineNumber() {
return lineNumber;
}
/**
* Sets error character position in line.
*
* @param lineNumber error character position in line
*/
public void setLineNumber(int lineNumber) {
this.lineNumber = lineNumber;
}
/**
* Returns stack of YANG type with partially resolved YANG construct
* hierarchy.
*
* @return partial resolved YANG construct stack
*/
private Stack<YangEntityToResolveInfo<T>> getPartialResolvedStack() {
return partialResolvedStack;
}
/**
* Sets stack of YANG type with partially resolved YANG construct hierarchy.
*
* @param partialResolvedStack partial resolved YANG construct stack
*/
private void setPartialResolvedStack(Stack<YangEntityToResolveInfo<T>> partialResolvedStack) {
this.partialResolvedStack = partialResolvedStack;
}
/**
* Sets stack of YANG type with partially resolved YANG construct hierarchy.
*
* @param partialResolvedInfo partial resolved YANG construct stack
*/
private void addInPartialResolvedStack(YangEntityToResolveInfo<T> partialResolvedInfo) {
getPartialResolvedStack().push(partialResolvedInfo);
}
/**
* Retrieves the next entity in the stack that needs to be resolved. It is
* assumed that the caller ensures that the stack is not empty.
*
* @return next entity in the stack that needs to be resolved
*/
private T getCurrentEntityToResolveFromStack() {
return getPartialResolvedStack().peek().getEntityToResolve();
}
/**
* Retrieves information about the entity that needs to be resolved.
*
* @return information about the entity that needs to be resolved
*/
public YangEntityToResolveInfo<T> getEntityToResolveInfo() {
return entityToResolveInfo;
}
/**
* Sets information about the entity that needs to be resolved.
*
* @param entityToResolveInfo information about the entity that needs to be
* resolved
*/
public void setEntityToResolveInfo(YangEntityToResolveInfo<T> entityToResolveInfo) {
this.entityToResolveInfo = entityToResolveInfo;
}
}