[ONOS-4063 to 68] Intra YANG file Linking Implementation and Intra YANG file Linking Framework

Change-Id: I06e602c351ab54178bf90b8676af71a70e42371f
diff --git a/utils/yangutils/src/main/java/org/onosproject/yangutils/datamodel/YangResolutionInfo.java b/utils/yangutils/src/main/java/org/onosproject/yangutils/datamodel/YangResolutionInfo.java
new file mode 100644
index 0000000..de65bb4
--- /dev/null
+++ b/utils/yangutils/src/main/java/org/onosproject/yangutils/datamodel/YangResolutionInfo.java
@@ -0,0 +1,640 @@
+/*
+ * Copyright 2016 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;
+
+/**
+ * Resolution object which will be resolved by linker.
+ */
+public class YangResolutionInfo<T> {
+
+    // Prefix associated with the linking.
+    private String prefix;
+
+    // Parsable node for which resolution is to be performed.
+    private T entityToResolve;
+
+    // Holder of the YANG construct for which resolution has to be carried out.
+    private YangNode holderOfEntityToResolve;
+
+    // Error Line number.
+    private int lineNumber;
+
+    // Error character position.
+    private int charPosition;
+
+    // Status of resolution.
+    private boolean isResolved;
+
+    /*
+     * Stack for type/uses is maintained for hierarchical references, this
+     * is used during resolution.
+     */
+    private Stack<T> partialResolvedStack;
+
+    // Flag to indicate whether more references are detected.
+    private boolean isMoreReferenceDetected;
+
+    // Module/Sub-module prefix.
+    private String resolutionInfoRootNodePrefix;
+
+    /**
+     * Create a resolution information object.
+     */
+    private YangResolutionInfo() {
+
+    }
+
+    /**
+     * Creates a resolution information object with all the inputs.
+     *
+     * @param dataNode current parsable data node
+     * @param resolutionType type of resolution whether grouping/typedef
+     * @param holderNode parent YANG node
+     * @param prefix imported module prefix
+     * @param lineNumber error line number
+     * @param charPositionInLine error character position in line
+     */
+    public YangResolutionInfo(T dataNode, ResolutionType resolutionType,
+                              YangNode holderNode, String prefix, int lineNumber,
+                              int charPositionInLine) {
+        this.setHolderOfEntityToResolve(holderNode);
+        this.setEntityToResolve(dataNode);
+        this.setPrefix(prefix);
+        this.setLineNumber(lineNumber);
+        this.setCharPosition(charPositionInLine);
+        setPartialResolvedStack(new Stack<T>());
+    }
+
+    /**
+     * Creates a resolution information object with all the inputs except prefix.
+     *
+     * @param dataNode current parsable data node
+     * @param resolutionType type of resolution whether grouping/typedef
+     * @param holderNode parent YANG node
+     * @param lineNumber error line number
+     * @param charPositionInLine error character position in line
+     */
+    public YangResolutionInfo(T dataNode, ResolutionType resolutionType,
+                              YangNode holderNode, int lineNumber,
+                              int charPositionInLine) {
+        this.setHolderOfEntityToResolve(holderNode);
+        this.setEntityToResolve(dataNode);
+        this.setLineNumber(lineNumber);
+        this.setCharPosition(charPositionInLine);
+    }
+
+    /**
+     * 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 = getEntityToResolve();
+
+        // Check if linking is already done
+        if (entityToResolve instanceof Resolvable) {
+            Resolvable resolvable = (Resolvable) entityToResolve;
+            if (resolvable.getResolvableStatus() == ResolvableStatus.RESOLVED ||
+                    resolvable.getResolvableStatus() == ResolvableStatus.PARTIALLY_RESOLVED) {
+                return;
+            }
+        } else {
+            throw new DataModelException("Data Model Exception: Entity to resolved is other than type/uses");
+        }
+
+        // Push the initial YANG type to the stack.
+        getPartialResolvedStack().push(entityToResolve);
+
+        // Get holder of entity to resolve
+        YangNode curNode = getHolderOfEntityToResolve();
+
+        resolveLinkingWithAncestors(curNode);
+    }
+
+    /**
+     * Resolves linking with ancestors.
+     *
+     * @param curNode current node for which ancestors to be checked
+     * @throws DataModelException a violation of data model rules
+     */
+    private void resolveLinkingWithAncestors(YangNode curNode) throws DataModelException {
+
+        while (curNode != null) {
+            YangNode node = curNode.getChild();
+            if (resolveLinkingForNodesChildAndSibling(node, curNode)) {
+                return;
+            }
+            curNode = curNode.getParent();
+        }
+
+        // If curNode is null, it indicates an error condition in YANG file.
+        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;
+    }
+
+    /**
+     * Resolves linking for a node child and siblings.
+     *
+     * @param node current node
+     * @param parentNode parent node of current node
+     * @return flag to indicate whether resolution is done
+     * @throws DataModelException
+     */
+    private boolean resolveLinkingForNodesChildAndSibling(YangNode node, YangNode parentNode)
+            throws DataModelException {
+        while ((node != null)) {
+            isMoreReferenceDetected = false;
+            // Check if node is of type, typedef or grouping
+            if (isNodeOfResolveType(node)) {
+                if (resolveLinkingForNode(node, parentNode)) {
+                    return true;
+                }
+            }
+            if (isMoreReferenceDetected) {
+                /*
+                 * If more reference are present, tree traversal must start
+                 * from first child again, to check the availability of
+                 * typedef/grouping.
+                 */
+                node = parentNode.getChild();
+            } else {
+                node = node.getNextSibling();
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Resolves linking for a node.
+     *
+     * @param node current node
+     * @param parentNode parent node of current node
+     * @return flag to indicate whether resolution is done
+     * @throws DataModelException a violation of data model rules
+     */
+    private boolean resolveLinkingForNode(YangNode node, YangNode parentNode) throws
+            DataModelException {
+        /*
+         * Check if name of node name matches with the entity name
+         * under resolution.
+         */
+        if (isNodeNameSameAsResolutionInfoName(node)) {
+            // Add reference of entity to the node under resolution.
+            addReferredEntityLink(node);
+            // Check if referred entity has further reference to uses/type.
+            if (!(isMoreReferencePresent(node))) {
+                // Resolve all the entities in stack.
+                resolveStackAndAddToStack(node);
+                return true;
+            } else {
+                // Add referred type/uses to the stack.
+                addToPartialResolvedStack(node);
+                /*
+                 * Check whether referred type is resolved, partially resolved
+                 * or unresolved.
+                 */
+                if (isReferenceFullyResolved()) {
+                    // Resolve the stack which is complete.
+                    resolveCompleteStack();
+                    return true;
+                } else if (isReferencePartiallyResolved()) {
+                    /*
+                     * Update the resolution type to partially resolved for all
+                     * type/uses in stack
+                     */
+                    updateResolutionTypeToPartial();
+                    return true;
+                } else {
+                /*
+                 * Check if prefix is present to find that the derived
+                 * reference is for intra file or inter file, if it's
+                 * inter-file return and stop further processing.
+                 */
+                    if (isExternalPrefixPresent(node)) {
+                        /*
+                         * Update the resolution type to partially resolved for all
+                         * type/uses in stack
+                         */
+                        updateResolutionTypeToPartial();
+                        return true;
+                    } else {
+                    /*
+                     * If prefix is not present it indicates intra-file
+                     * dependency in this case set the node back to first
+                     * child, as referred entity may appear in any order
+                     * and continue with the resolution.
+                     */
+                        isMoreReferenceDetected = true;
+                        return false;
+                    }
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Update resolution type to partial for all type/uses in stack.
+     *
+     * @throws DataModelException a violation of data model rules
+     */
+    private void updateResolutionTypeToPartial() throws DataModelException {
+        // For all entries in stack calls for the resolution in type/uses.
+        for (T entity:getPartialResolvedStack()) {
+            if (!(entity instanceof Resolvable)) {
+                throw new DataModelException("Data Model Exception: Entity to resolved is other than type/uses");
+            }
+            if (((Resolvable) entity).getResolvableStatus() == ResolvableStatus.UNRESOLVED) {
+                // Set the resolution status in inside the type/uses.
+                ((Resolvable) entity).setResolvableStatus(ResolvableStatus.PARTIALLY_RESOLVED);
+            }
+        }
+    }
+
+    /**
+     * Add referred type/uses to the stack and resolve the stack.
+     *
+     * @param node typedef/grouping node
+     * @throws DataModelException a violation of data model rules
+     */
+    private void resolveStackAndAddToStack(YangNode node) throws DataModelException {
+        if (getEntityToResolve() instanceof YangType) {
+            // Add to the stack only for YANG typedef.
+            getPartialResolvedStack().push((T) ((YangTypeDef) node).getDataType());
+        }
+        // Don't add to stack in case of YANG grouping.
+
+        // Resolve the complete stack.
+        resolveCompleteStack();
+    }
+
+    /**
+     * Check if the referred type/uses is partially resolved.
+     *
+     * @return true if reference is partially resolved, otherwise false
+     */
+    private boolean isReferencePartiallyResolved() {
+        if (getPartialResolvedStack().peek() instanceof YangType) {
+            /*
+             * Checks if type is partially resolved.
+             */
+            if (((YangType) getPartialResolvedStack().peek()).getResolvableStatus() ==
+                    ResolvableStatus.PARTIALLY_RESOLVED) {
+                return true;
+            }
+        } else if (getPartialResolvedStack().peek() instanceof YangUses) {
+            if (((YangUses) getPartialResolvedStack().peek()).getResolvableStatus() ==
+                    ResolvableStatus.PARTIALLY_RESOLVED) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Check if the referred type/uses is resolved.
+     *
+     * @return true if reference is resolved, otherwise false
+     */
+    private boolean isReferenceFullyResolved() {
+        if (getPartialResolvedStack().peek() instanceof YangType) {
+            /*
+             * Checks if type is partially resolved.
+             */
+            if (((YangType) getPartialResolvedStack().peek()).getResolvableStatus() ==
+                    ResolvableStatus.RESOLVED) {
+                return true;
+            }
+        } else if (getPartialResolvedStack().peek() instanceof YangUses) {
+            if (((YangUses) getPartialResolvedStack().peek()).getResolvableStatus() ==
+                    ResolvableStatus.RESOLVED) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Check if node is of resolve type i.e. of type typedef or grouping.
+     *
+     * @param node typedef/grouping node
+     * @return true if node is of resolve type otherwise false
+     * @throws DataModelException a violation of data model rules
+     */
+    private boolean isNodeOfResolveType(YangNode node) throws DataModelException {
+        if (getPartialResolvedStack().peek() instanceof YangType && entityToResolve instanceof YangType) {
+            if (node instanceof YangTypeDef) {
+                return true;
+            }
+        } else if (getPartialResolvedStack().peek() instanceof YangUses && entityToResolve instanceof YangUses) {
+            if (node instanceof YangGrouping) {
+                return true;
+            }
+        } 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 (getPartialResolvedStack().peek() instanceof YangType) {
+            if (node.getName().equals(((YangType<?>) getPartialResolvedStack().peek()).getDataTypeName())) {
+                return true;
+            }
+        } else if (getPartialResolvedStack().peek() instanceof YangUses) {
+            if (node.getName().equals(((YangUses) getPartialResolvedStack().peek()).getName())) {
+                return true;
+            }
+        } else {
+            throw new DataModelException("Data Model Exception: Entity to resolved is other than type/uses");
+        }
+        return false;
+    }
+
+    /**
+     * Add reference of grouping/typedef in uses/type.
+     *
+     * @param node grouping/typedef node
+     * @throws DataModelException a violation of data model rules
+     */
+    private void addReferredEntityLink(YangNode node) throws DataModelException {
+        if (getPartialResolvedStack().peek() instanceof YangType) {
+            YangDerivedInfo<?> derivedInfo = (YangDerivedInfo<?>) ((YangType<?>) getPartialResolvedStack().peek())
+                    .getDataTypeExtendedInfo();
+            derivedInfo.setReferredTypeDef((YangTypeDef) node);
+        } else if (getPartialResolvedStack().peek() instanceof YangUses) {
+            ((YangUses) getPartialResolvedStack().peek()).setRefGroup((YangGrouping) node);
+        } else {
+            throw new DataModelException("Data Model Exception: Entity to resolved is other than type/uses");
+        }
+    }
+
+    /**
+     * Checks if typedef/grouping has further reference to type/typedef.
+     *
+     * @param node grouping/typedef node
+     * @return true if referred entity is resolved, otherwise false
+     * @throws DataModelException a violation of data model rules
+     */
+    private boolean isMoreReferencePresent(YangNode node) throws DataModelException {
+        if (getEntityToResolve() instanceof YangType) {
+            /*
+             * Checks if typedef type is built-in type
+             */
+            if ((((YangTypeDef) node).getDataType().getDataType() != YangDataTypes.DERIVED)) {
+                return false;
+            }
+        } else if (getEntityToResolve() instanceof YangUses) {
+            /*
+             * Search if the grouping has any uses child, if so return false,
+             * else return true.
+             */
+            if (getUsesInGrouping(node) == null) {
+                return false;
+            }
+        } else {
+            throw new DataModelException("Data Model Exception: Entity to resolved is other than type/uses");
+        }
+        return true;
+    }
+
+    /**
+     * Return if there is any uses in grouping.
+     *
+     * @param node grouping/typedef node
+     * @return if there is any uses in grouping, otherwise return null
+     */
+    private YangUses getUsesInGrouping(YangNode node) {
+        YangNode curNode = ((YangGrouping) node).getChild();
+        while (curNode != null) {
+            if (curNode instanceof YangUses) {
+                break;
+            }
+            curNode = curNode.getNextSibling();
+        }
+        return (YangUses) curNode;
+    }
+
+    /**
+     * Resolve the complete stack.
+     *
+     * @throws DataModelException a violation of data model rules
+     */
+    private void resolveCompleteStack() throws DataModelException {
+        // For all entries in stack calls for the resolution in type/uses.
+        for (T entity:getPartialResolvedStack()) {
+            if (!(entity instanceof Resolvable)) {
+                throw new DataModelException("Data Model Exception: Entity to resolved is other than type/uses");
+            }
+            ((Resolvable) entity).resolve();
+            // Set the resolution status in inside the type/uses.
+            ((Resolvable) entity).setResolvableStatus(ResolvableStatus.RESOLVED);
+        }
+        /*
+         * Set the resolution status in resolution info present in resolution
+         * list.
+         */
+        setIsResolved(true);
+    }
+
+    /**
+     * Add to partial resolved stack.
+     *
+     * @param node grouping/typedef node
+     * @throws DataModelException a violation of data model rules
+     */
+    private void addToPartialResolvedStack(YangNode node) throws DataModelException {
+        if (getPartialResolvedStack().peek() instanceof YangType) {
+            // Add to the stack only for YANG typedef.
+            getPartialResolvedStack().push((T) ((YangTypeDef) node).getDataType());
+        } else if (getPartialResolvedStack().peek() instanceof YangUses) {
+            getPartialResolvedStack().push((T) getUsesInGrouping(node));
+        } else {
+            throw new DataModelException("Data Model Exception: Entity to resolved is other than type/uses");
+        }
+    }
+
+    /**
+     * Check if prefix is associated with type/uses.
+     *
+     * @param node typedef/grouping node
+     * @return true if prefix is present, otherwise false
+     * @throws DataModelException a violation of data model rules
+     */
+    private boolean isExternalPrefixPresent(YangNode node) throws DataModelException {
+        if (getEntityToResolve() instanceof YangType) {
+            if (((YangTypeDef) node).getDataType().getPrefix() != null &&
+                    (!((YangTypeDef) node).getDataType().getPrefix().equals(resolutionInfoRootNodePrefix))) {
+                return true;
+            }
+        } else if (getEntityToResolve() instanceof YangUses) {
+            if (getUsesInGrouping(node).getPrefix() != null) {
+                return true;
+            }
+        } else {
+            throw new DataModelException("Data Model Exception: Entity to resolved is other than type/uses");
+        }
+        return false;
+    }
+
+    /**
+     * Returns prefix of imported module.
+     *
+     * @return prefix of imported module
+     */
+    public String getPrefix() {
+        return prefix;
+    }
+
+    /**
+     * Set prefix of imported module.
+     *
+     * @param prefix of imported module
+     */
+    public void setPrefix(String prefix) {
+        this.prefix = prefix;
+    }
+
+    /**
+     * Returns parsable entity which is to be resolved.
+     *
+     * @return parsable entity which is to be resolved
+     */
+    public T getEntityToResolve() {
+        return entityToResolve;
+    }
+
+    /**
+     * Set parsable entity to be resolved.
+     *
+     * @param entityToResolve YANG entity to be resolved
+     */
+    public void setEntityToResolve(T entityToResolve) {
+        this.entityToResolve = entityToResolve;
+    }
+
+    /**
+     * Returns parent YANG node holder for the entity to be resolved.
+     *
+     * @return parent YANG node holder
+     */
+    public YangNode getHolderOfEntityToResolve() {
+        return holderOfEntityToResolve;
+    }
+
+    /**
+     * Set parent YANG node holder for the entity to be resolved.
+     *
+     * @param holderOfEntityToResolve parent YANG node holder
+     */
+    public void setHolderOfEntityToResolve(YangNode holderOfEntityToResolve) {
+        this.holderOfEntityToResolve = holderOfEntityToResolve;
+    }
+
+    /**
+     * Returns error position.
+     *
+     * @return error position
+     */
+    public int getCharPosition() {
+        return charPosition;
+    }
+
+    /**
+     * Set 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;
+    }
+
+    /**
+     * Set error character position in line.
+     *
+     * @param lineNumber error character position in line
+     */
+    public void setLineNumber(int lineNumber) {
+        this.lineNumber = lineNumber;
+    }
+
+    /**
+     * Returns status of resolution.
+     *
+     * @return resolution status
+     */
+    public boolean isResolved() {
+        return isResolved;
+    }
+
+    /**
+     * Set status of resolution.
+     *
+     * @param isResolved resolution status
+     */
+    public void setIsResolved(boolean isResolved) {
+        this.isResolved = isResolved;
+    }
+
+    /**
+     * Returns stack of YANG type with partially resolved YANG construct hierarchy.
+     *
+     * @return partial resolved YANG construct stack
+     */
+    public Stack<T> getPartialResolvedStack() {
+        return partialResolvedStack;
+    }
+
+    /**
+     * Set stack of YANG type with partially resolved YANG construct hierarchy.
+     *
+     * @param partialResolvedStack partial resolved YANG construct stack
+     */
+    public void setPartialResolvedStack(Stack<T> partialResolvedStack) {
+        this.partialResolvedStack = partialResolvedStack;
+    }
+}