All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.onosproject.yang.runtime.impl.DataTreeBuilderHelper Maven / Gradle / Ivy

There is a newer version: 2.6.1
Show newest version
/*
 * Copyright 2017-present Open Networking Foundation
 *
 * 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.yang.runtime.impl;

import org.onosproject.yang.compiler.datamodel.RpcNotificationContainer;
import org.onosproject.yang.compiler.datamodel.TraversalType;
import org.onosproject.yang.compiler.datamodel.YangAugment;
import org.onosproject.yang.compiler.datamodel.YangAugmentableNode;
import org.onosproject.yang.compiler.datamodel.YangCase;
import org.onosproject.yang.compiler.datamodel.YangChoice;
import org.onosproject.yang.compiler.datamodel.YangLeaf;
import org.onosproject.yang.compiler.datamodel.YangLeafList;
import org.onosproject.yang.compiler.datamodel.YangLeavesHolder;
import org.onosproject.yang.compiler.datamodel.YangList;
import org.onosproject.yang.compiler.datamodel.YangNode;
import org.onosproject.yang.compiler.datamodel.YangSchemaNode;
import org.onosproject.yang.model.Anydata;
import org.onosproject.yang.model.DataNode;
import org.onosproject.yang.model.InnerNode;
import org.onosproject.yang.model.LeafNode;
import org.onosproject.yang.model.LeafSchemaContext;
import org.onosproject.yang.model.LeafType;
import org.onosproject.yang.model.ModelConverterException;
import org.onosproject.yang.model.YangNamespace;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import static org.onosproject.yang.compiler.datamodel.TraversalType.CHILD;
import static org.onosproject.yang.compiler.datamodel.TraversalType.PARENT;
import static org.onosproject.yang.compiler.datamodel.TraversalType.ROOT;
import static org.onosproject.yang.compiler.datamodel.TraversalType.SIBLING;
import static org.onosproject.yang.compiler.datamodel.YangNodeType.ANYDATA_NODE;
import static org.onosproject.yang.compiler.datamodel.utils.DataModelUtils.nonEmpty;
import static org.onosproject.yang.model.DataNode.Type.MULTI_INSTANCE_LEAF_VALUE_NODE;
import static org.onosproject.yang.model.DataNode.Type.MULTI_INSTANCE_NODE;
import static org.onosproject.yang.model.DataNode.Type.SINGLE_INSTANCE_LEAF_VALUE_NODE;
import static org.onosproject.yang.model.DataNode.Type.SINGLE_INSTANCE_NODE;
import static org.onosproject.yang.runtime.RuntimeHelper.DEFAULT_CAPS;
import static org.onosproject.yang.runtime.RuntimeHelper.PERIOD;
import static org.onosproject.yang.runtime.RuntimeHelper.getCapitalCase;
import static org.onosproject.yang.runtime.impl.ModelConverterUtil.getAttributeOfObject;
import static org.onosproject.yang.runtime.impl.ModelConverterUtil.getAugmentObject;
import static org.onosproject.yang.runtime.impl.ModelConverterUtil.getClassLoaderForAugment;
import static org.onosproject.yang.runtime.impl.ModelConverterUtil.getInterfaceClassFromImplClass;
import static org.onosproject.yang.runtime.impl.ModelConverterUtil.getJavaName;
import static org.onosproject.yang.runtime.impl.ModelConverterUtil.getLeafListObject;
import static org.onosproject.yang.runtime.impl.ModelConverterUtil.getLeafObject;
import static org.onosproject.yang.runtime.impl.ModelConverterUtil.getParentObjectOfNode;
import static org.onosproject.yang.runtime.impl.ModelConverterUtil.isAugmentNode;
import static org.onosproject.yang.runtime.impl.ModelConverterUtil.isMultiInstanceNode;
import static org.onosproject.yang.runtime.impl.ModelConverterUtil.isNodeProcessCompleted;
import static org.onosproject.yang.runtime.impl.ModelConverterUtil.isNonProcessableNode;
import static org.onosproject.yang.runtime.impl.ModelConverterUtil.isTypeEmpty;
import static org.onosproject.yang.runtime.impl.YobConstants.E_FAIL_TO_LOAD_CLASS;


/**
 * Implements traversal of YANG node and its corresponding object, resulting
 * in building of the data tree.
 */
public class DataTreeBuilderHelper {

    private static final String TRUE = "true";
    private static final String AUGMENTATIONS = "augmentations";
    private static final String FALSE = "false";

    /**
     * Default YANG model registry.
     */
    private final DefaultYangModelRegistry reg;

    /**
     * Current instance of the data node builder where the tree is built.
     */
    private DataNode.Builder extBuilder;

    /**
     * YANG root object that is required for walking along with the YANG node.
     */
    private Object rootObj;

    /**
     * YANG root node that is required for walking along with the YANG object.
     */
    private YangSchemaNode rootSchema;

    /**
     * Yang node for which exit builder is being processed.
     */
    private YangSchemaNode exitBuilderSchema;

    /**
     * Creates an instance of data tree builder helper.
     *
     * @param reg default model registry
     */
    public DataTreeBuilderHelper(DefaultYangModelRegistry reg) {
        this.reg = reg;
    }

    /**
     * Sets exit builder.
     *
     * @param extBuilder data node builder
     */
    public void setExtBuilder(DataNode.Builder extBuilder) {
        this.extBuilder = extBuilder;
    }

    /**
     * Creates data tree from the root object, by traversing through YANG data
     * model node, and simultaneously checking the object nodes presence and
     * walking the object.
     *
     * @param curSchema current root node schema
     * @param builder   data node builder
     * @param obj       current root object
     * @return data node builder
     */
    DataNode.Builder getDataTree(YangSchemaNode curSchema, DataNode.Builder builder,
                                 Object obj) {
        extBuilder = builder;
        rootObj = obj;
        rootSchema = curSchema;
        YangNode curNode = (YangNode) curSchema;
        TraversalType curTraversal = ROOT;
        DataTreeNodeInfo listNodeInfo = null;
        DataTreeNodeInfo augmentNodeInfo = null;

        do {
            /*
             * Processes the node, if it is being visited for the first time in
             * the schema, also if the schema node is being retraced in a multi
             * instance node.
             */
            if (curTraversal != PARENT || isMultiInstanceNode(curNode)) {

                if (curTraversal == PARENT && isMultiInstanceNode(curNode)) {
                    /*
                     * If the schema is being retraced for a multi-instance
                     * node, it has already entered for this multi-instance
                     * node. Now this re-processes the same schema node for
                     * any additional list object.
                     */
                    listNodeInfo = getCurNodeInfoAndTraverseBack(curNode);
                }

                if (curTraversal == ROOT && !isAugmentNode(curNode)) {
                    /*
                     * In case of RPC output, the root node is augmentative,
                     * so when the root traversal is coming for augment this
                     * flow is skipped. This adds only the root node in the
                     * data tree.
                     */
                    processApplicationRootNode();
                } else {
                    /*
                     * Gets the object corresponding to current schema node.
                     * If object exists, this adds the corresponding data node
                     * to the tree and returns the object. Else returns null.
                     */
                    Object processedObject = processCurSchemaNode(curNode,
                                                                  listNodeInfo);
                    /*
                     * Clears the list info of processed node. The next time
                     * list info is taken newly and accordingly.
                     */
                    listNodeInfo = null;
                    if (processedObject == null && !isAugmentNode(curNode)) {
                        /*
                         * Checks the presence of next sibling of the node, by
                         * breaking the complete chain of the current node,
                         * when the object value is not present, or when the
                         * list entries are completely retraced. The augment
                         * may have sibling, so this doesn't process for
                         * augment.
                         */
                        ModelConverterTraversalInfo traverseInfo =
                                getProcessableInfo(curNode);
                        curNode = traverseInfo.getYangNode();
                        curTraversal = traverseInfo.getTraverseType();
                        continue;
                        /*
                         * Irrespective of root or parent, sets the traversal
                         * type as parent, when augment node doesn't have any
                         * value. So, the other sibling augments can be
                         * processed, if present.
                         */
                    } else if (processedObject == null &&
                            isAugmentNode(curNode)) {
                        curTraversal = PARENT;
                        /*
                         * The second content in the list will be having
                         * parent traversal, in such case it cannot go to its
                         * child in the flow, so it is made as child
                         * traversal and proceeded to continue.
                         */
                    } else if (curTraversal == PARENT &&
                            isMultiInstanceNode(curNode)) {
                        curTraversal = CHILD;
                    }
                }
            }
            /*
             * Checks for the sibling augment when the first augment node is
             * getting completed. From the current augment node the previous
             * node info is taken for augment and the traversal is changed to
             * child, so as to check for the presence of sibling augment.
             */
            if (curTraversal == PARENT && isAugmentNode(curNode)) {
                curNode = ((YangAugment) curNode).getAugmentedNode();
                augmentNodeInfo = getParentInfo();
                curTraversal = CHILD;
            }
            /*
             * Creates an augment iterator for the first time or takes the
             * previous augment iterator for more than one time, whenever an
             * augmentative node arrives. If augment is present it goes back
             * for processing. If its null, the augmentative nodes process is
             * continued.
             */
            if (curTraversal != PARENT &&
                    curNode instanceof YangAugmentableNode) {
                YangNode augmentNode = getAugmentInsideSchemaNode(
                        curNode, augmentNodeInfo);
                if (augmentNode != null) {
                    curNode = augmentNode;
                    continue;
                }
            }
            /*
             * Processes the child, after processing the node. If complete
             * child depth is over, it takes up sibling and processes it.
             * Once child and sibling is over, it is traversed back to the
             * parent, without processing. In multi instance case, before
             * going to parent or schema sibling, its own list sibling is
             * processed. Skips the processing of RPC,notification and
             * augment, as these nodes are dealt in a different flow.
             */
            if (curTraversal != PARENT && curNode.getChild() != null) {
                augmentNodeInfo = null;
                listNodeInfo = null;
                curTraversal = CHILD;
                curNode = curNode.getChild();
                if (isNonProcessableNode(curNode)) {
                    ModelConverterTraversalInfo traverseInfo = getProcessableInfo(curNode);
                    curNode = traverseInfo.getYangNode();
                    curTraversal = traverseInfo.getTraverseType();
                }
            } else if (curNode.getNextSibling() != null) {
                if (isNodeProcessCompleted(curNode, curTraversal)) {
                    break;
                }
                if (isMultiInstanceNode(curNode)) {
                    listNodeInfo = getCurNodeInfoAndTraverseBack(curNode);
                    augmentNodeInfo = null;
                    continue;
                }
                curTraversal = SIBLING;
                augmentNodeInfo = null;
                traverseToParent(curNode);
                //here if current node does have a sibling node but current
                // node is equal to the root schema then it will fail because
                // our current root object does not have info about its sibling.
                if (!curNode.equals(rootSchema)) {
                    if (curNode instanceof YangAugment) {
                        curTraversal = PARENT;
                        continue;
                    }
                    if (isNonProcessableNode(curNode)) {
                        ModelConverterTraversalInfo traverseInfo = getProcessableInfo(curNode);
                        curNode = traverseInfo.getYangNode();
                        curTraversal = traverseInfo.getTraverseType();
                    } else {
                        curNode = curNode.getNextSibling();
                    }
                }
            } else {
                if (isNodeProcessCompleted(curNode, curTraversal)) {
                    break;
                }
                if (isMultiInstanceNode(curNode)) {
                    listNodeInfo = getCurNodeInfoAndTraverseBack(curNode);
                    augmentNodeInfo = null;
                    continue;
                }
                curTraversal = PARENT;
                traverseToParent(curNode);
                curNode = getParentSchemaNode(curNode);
            }
        } while (curNode != null && !curNode.equals(curSchema));
        return extBuilder;
    }

    /**
     * Returns parent schema node of current node.
     *
     * @param curNode current schema node
     * @return parent schema node
     */
    private YangNode getParentSchemaNode(YangNode curNode) {
        if (curNode instanceof YangAugment) {
            /*
             * If curNode is augment, either next augment or augmented node
             * has to be processed. So traversal type is changed to parent,
             * but node is not changed.
             */
            return curNode;
        }
        if (!curNode.equals(rootSchema)) {
            return curNode.getParent();
        }
        return curNode;
    }

    /**
     * Processes root YANG node and adds it as a child to the data tree
     * builder which is created earlier.
     */
    private void processApplicationRootNode() {
        DataTreeNodeInfo nodeInfo = new DataTreeNodeInfo();
        nodeInfo.setYangObject(rootObj);
        if (rootSchema instanceof YangList) {
            nodeInfo.type(MULTI_INSTANCE_NODE);
        } else {
            nodeInfo.type(SINGLE_INSTANCE_NODE);
        }
        extBuilder.appInfo(nodeInfo);
        exitBuilderSchema = rootSchema;
        processLeaves((YangNode) rootSchema, nodeInfo);
        processLeavesList((YangNode) rootSchema, nodeInfo);
    }

    /**
     * Traverses to parent, based on the schema node that requires to be
     * traversed. Skips traversal of parent for choice and case node, as they
     * don't get added to the data tree.
     *
     * @param curNode current YANG node
     */
    private void traverseToParent(YangNode curNode) {
        if (curNode instanceof YangCase || curNode instanceof YangChoice
                || curNode instanceof YangAugment) {
            return;
        }
        if (!curNode.equals(rootSchema)) {
            extBuilder = extBuilder.exitNode();
        }
    }

    /**
     * Returns the current data tree builder info of the data node builder, and
     * then traverses back to parent. In case of multi instance node the
     * previous node info is used for iterating through the list.
     *
     * @param curNode current YANG node
     * @return current data tree builder info
     */
    private DataTreeNodeInfo getCurNodeInfoAndTraverseBack(YangNode curNode) {
        DataTreeNodeInfo appInfo = getParentInfo();
        if (!curNode.equals(rootSchema)) {
            extBuilder = extBuilder.exitNode();
        }
        return appInfo;
    }

    /**
     * Returns augment node for an augmented node. From the list of augment
     * nodes it has, one of the nodes is taken and provided linearly. If the
     * node is not augmented or the all the augment nodes are processed, then
     * it returns null.
     *
     * @param curNode         current YANG node
     * @param augmentNodeInfo previous augment node info
     * @return YANG augment node
     */
    private YangNode getAugmentInsideSchemaNode(YangNode curNode,
                                                DataTreeNodeInfo augmentNodeInfo) {
        if (augmentNodeInfo == null) {
            List augmentList = ((YangAugmentableNode) curNode)
                    .getAugmentedInfoList();
            if (nonEmpty(augmentList)) {
                DataTreeNodeInfo parentNodeInfo = getParentInfo();
                Iterator augmentItr = augmentList.listIterator();
                parentNodeInfo.setAugmentIterator(augmentItr);
                return augmentItr.next();
            }
        } else if (augmentNodeInfo.getAugmentIterator() != null) {
            if (augmentNodeInfo.getAugmentIterator().hasNext()) {
                return augmentNodeInfo.getAugmentIterator().next();
            }
        }
        return null;
    }

    /**
     * Processes the current YANG node and if necessary adds it to the data tree
     * builder by extracting the information from the corresponding class object.
     *
     * @param curNode      current YANG node
     * @param listNodeInfo previous node info for list
     * @return object of the schema node
     */
    private Object processCurSchemaNode(YangNode curNode,
                                        DataTreeNodeInfo listNodeInfo) {
        DataTreeNodeInfo curNodeInfo = new DataTreeNodeInfo();
        Object nodeObj = null;
        DataTreeNodeInfo parentNodeInfo = getParentInfo();
        if (curNode instanceof YangAugment) {
            YangNode augmented = ((YangAugment) curNode).getAugmentedNode();
            String name;
            if (augmented instanceof YangCase) {
                name = augmented.getJavaAttributeName();
                Object obj;
                try {
                    obj = getAttributeOfObject(
                            parentNodeInfo.getYangObject(), name);
                } catch (NoSuchMethodException e) {
                    throw new ModelConverterException(
                            "Not processable case node with augment in " +
                                    "data tree", e);
                }
                parentNodeInfo = new DataTreeNodeInfo();
                parentNodeInfo.setYangObject(obj);
                parentNodeInfo.type(SINGLE_INSTANCE_NODE);
            }
        }

        switch (curNode.getYangSchemaNodeType()) {
            case YANG_ANYDATA_NODE:
            case YANG_SINGLE_INSTANCE_NODE:
                curNodeInfo.type(SINGLE_INSTANCE_NODE);
                nodeObj = processSingleInstanceNode(curNode, curNodeInfo,
                                                    parentNodeInfo);
                break;
            case YANG_MULTI_INSTANCE_NODE:
                curNodeInfo.type(MULTI_INSTANCE_NODE);
                nodeObj = processMultiInstanceNode(
                        curNode, curNodeInfo, listNodeInfo, parentNodeInfo);
                break;
            case YANG_CHOICE_NODE:
                nodeObj = processChoiceNode(curNode, parentNodeInfo);
                break;
            case YANG_NON_DATA_NODE:
                if (curNode instanceof YangCase) {
                    nodeObj = processCaseNode(curNode, parentNodeInfo);
                }
                break;
            case YANG_AUGMENT_NODE:
                nodeObj = processAugmentNode(curNode, parentNodeInfo);
                break;
            default:
                throw new ModelConverterException(
                        "Non processable schema node has arrived for adding " +
                                "it in data tree");
        }
        // Processes leaf/leaf-list only when object has value, else it skips.
        if (nodeObj != null) {
            processLeaves(curNode, parentNodeInfo);
            processLeavesList(curNode, parentNodeInfo);
        }
        return nodeObj;
    }

    /**
     * Processes single instance node which is added to the data tree.
     *
     * @param curNode        current YANG node
     * @param curNodeInfo    current data tree node info
     * @param parentNodeInfo parent data tree node info
     * @return object of the current node
     */
    private Object processSingleInstanceNode(YangNode curNode,
                                             DataTreeNodeInfo curNodeInfo,
                                             DataTreeNodeInfo parentNodeInfo) {
        Object childObj = getChildObject(curNode, parentNodeInfo);
        if (childObj != null) {
            curNodeInfo.setYangObject(childObj);
            curNodeInfo.type(SINGLE_INSTANCE_NODE);
            processChildNode(curNode, curNodeInfo);
        }
        return childObj;
    }

    /**
     * Processes multi instance node which has to be added to the data tree.
     * For the first instance in the list, iterator is created and added to
     * the list. For second instance or more the iterator from first instance
     * is taken and iterated through to get the object of parent.
     *
     * @param curNode        current list node
     * @param curNodeInfo    current node info for list
     * @param listNodeInfo   previous instance node info of list
     * @param parentNodeInfo parent node info of list
     * @return object of the current instance
     */
    private Object processMultiInstanceNode(YangNode curNode,
                                            DataTreeNodeInfo curNodeInfo,
                                            DataTreeNodeInfo listNodeInfo,
                                            DataTreeNodeInfo parentNodeInfo) {
        Object childObj = null;
        /*
         * When YANG list comes to this flow for first time, its data node
         * will be null. When it comes for the second or more content, then
         * the list would have been already set for that node. According to
         * set or not set this flow will be proceeded.
         */
        if (listNodeInfo == null) {
            List childObjList = (List) getChildObject(
                    curNode, parentNodeInfo);
            if (nonEmpty(childObjList)) {
                Iterator listItr = childObjList.iterator();
                if (!listItr.hasNext()) {
                    return null;
                    //TODO: Handle the subtree filtering with no list entries.
                }
                childObj = listItr.next();
                /*
                 * For that node the iterator is set. So the next time for
                 * the list this iterator will be taken.
                 */
                curNodeInfo.setListIterator(listItr);
            }
        } else {
            /*
             * If the list value comes for second or more time, that list
             * node will be having data tree builder info, where iterator can be
             * retrieved and check if any more contents are present. If
             * present those will be processed.
             */
            curNodeInfo.setListIterator(listNodeInfo.getListIterator());
            if (listNodeInfo.getListIterator().hasNext()) {
                childObj = listNodeInfo.getListIterator().next();
            }
        }
        if (childObj != null) {
            curNodeInfo.setYangObject(childObj);
            processChildNode(curNode, curNodeInfo);
        }
        return childObj;
    }

    /**
     * Processes choice node which adds a map to the parent node info of
     * choice name and the case object. The object taken for choice node is
     * of case object with choice name. Also, this Skips the addition of choice
     * to data tree.
     *
     * @param curNode        current choice node
     * @param parentNodeInfo parent data tree info
     * @return object of the choice node
     */
    Object processChoiceNode(YangNode curNode,
                             DataTreeNodeInfo parentNodeInfo) {
        /*
         * Retrieves the parent data tree info, to take the object of parent,
         * so as
         * to check the child attribute from the object.
         */
        Object childObj = getChildObject(curNode, parentNodeInfo);
        if (childObj != null) {
            Map choiceCaseMap = parentNodeInfo
                    .getChoiceCaseMap();
            if (choiceCaseMap == null) {
                choiceCaseMap = new HashMap<>();
                parentNodeInfo.setChoiceCaseMap(choiceCaseMap);
            }
            choiceCaseMap.put(curNode.getName(), childObj);
        }
        return childObj;
    }

    /**
     * Processes case node from the map contents that is filled by choice
     * nodes. Object of choice is taken when choice name and case class name
     * matches. When the case node is not present in the map it returns null.
     *
     * @param curNode        current case node
     * @param parentNodeInfo choice parent node info
     * @return object of the case node
     */
    Object processCaseNode(YangNode curNode,
                           DataTreeNodeInfo parentNodeInfo) {
        Object childObj = null;
        if (parentNodeInfo.getChoiceCaseMap() != null) {
            childObj = getCaseObjectFromChoice(parentNodeInfo,
                                               curNode);
        }
        if (childObj != null) {
            /*
             * Sets the case object in parent info, so that rest of the case
             * children can use it as parent. Case is not added in data tree.
             */
            parentNodeInfo.setCaseObject(childObj);
        }
        return childObj;
    }

    /**
     * Processes augment node, which is not added in the data tree, but binds
     * itself to the parent data tree node info, so rest of its child nodes can
     * use for adding themselves to the data tree. If there is no augment node
     * added in map or if the augment module is not registered,
     * then it returns null.
     *
     * @param curNode        current augment node
     * @param parentNodeInfo augment parent node info
     * @return object of the augment node
     */
    Object processAugmentNode(YangNode curNode,
                              DataTreeNodeInfo parentNodeInfo) {
        String className = curNode.getJavaClassNameOrBuiltInType();
        String pkgName = curNode.getJavaPackage();
        Object parentObj = getParentObjectOfNode(parentNodeInfo,
                                                 curNode.getParent());

        YangNode augmented = ((YangAugment) curNode).getAugmentedNode();
        if (augmented instanceof YangChoice) {
            String name = augmented.getJavaAttributeName();
            try {
                return getAttributeOfObject(
                        parentNodeInfo.getYangObject(), name);
            } catch (NoSuchMethodException e) {
                throw new ModelConverterException(
                        "Not processable case node with augment in " +
                                "data tree", e);
            }
        }

        Map augmentMap;
        try {
            augmentMap = (Map) getAugmentObject(parentObj,
                                                AUGMENTATIONS);
            if (augmentMap != null && !augmentMap.isEmpty()) {
            /*
             * Gets the registered module class. Loads the class and gets the
             * augment class.
             */
                curNode = getRootNode(curNode);
                Class moduleClass = getClassLoaderForAugment(curNode, reg);
                if (moduleClass == null) {
                    return null;
                }
                Class augmentClass = moduleClass.getClassLoader().loadClass(
                        pkgName + PERIOD + DEFAULT_CAPS + className);
                Object childObj = augmentMap.get(augmentClass);
                parentNodeInfo.setAugmentObject(childObj);
                return childObj;
            }
        } catch (ClassNotFoundException | NoSuchMethodException e) {
            throw new ModelConverterException(e);
        }
        return null;
    }

    /**
     * Returns the root node from the current node.
     *
     * @param curNode current YANG node
     * @return root node
     */
    private YangNode getRootNode(YangNode curNode) {
        while (curNode.getParent() != null) {
            curNode = curNode.getParent();
        }
        return curNode;
    }

    /**
     * Returns the data tree info from the parent node, so that its own bounded
     * object can be taken out.
     *
     * @return parent node data tree node info
     */
    private DataTreeNodeInfo getParentInfo() {
        return (DataTreeNodeInfo) extBuilder.appInfo();
    }

    /**
     * Returns the child object from the parent object. Uses java name of the
     * current node to search the attribute in the parent object.
     *
     * @param curNode        current YANG node
     * @param parentNodeInfo parent data tree node info
     * @return object of the child node
     */
    Object getChildObject(YangNode curNode,
                          DataTreeNodeInfo parentNodeInfo) {
        // check current node parent linking status if current node
        if (curNode.getParent() != null && curNode.getParent()
                .getNodeType() == ANYDATA_NODE) {
            return getAnydataChildObject(curNode, parentNodeInfo);
        }
        String nodeJavaName = curNode.getJavaAttributeName();
        Object parentObj = getParentObjectOfNode(parentNodeInfo,
                                                 curNode.getParent());
        try {
            return getAttributeOfObject(parentObj, nodeJavaName);
        } catch (NoSuchMethodException e) {
            throw new ModelConverterException(e);
        }
    }

    /**
     * Returns the child object from the parent object for anydata. Uses java
     * name of the current node to search the attribute in the parent object.
     *
     * @param curNode current YANG node
     * @param info    parent data tree node info
     * @return object of the child node
     */
    private Object getAnydataChildObject(YangNode curNode,
                                         DataTreeNodeInfo info) {
        // Getting the curNode anydata parent object
        Anydata parentObj = (Anydata) getParentObjectOfNode(
                info, curNode.getParent());
        YangSchemaNode node = reg.getForNameSpace(
                curNode.getNameSpace().getModuleNamespace(), false);
        // Getting the module class
        Class moduleClass = reg.getRegisteredClass(node);
        if (moduleClass == null) {
            throw new ModelConverterException(E_FAIL_TO_LOAD_CLASS + node
                    .getJavaClassNameOrBuiltInType());
        }

        // Forming the default class name for the curNode object creation.
        String className = curNode.getJavaPackage() + PERIOD + DEFAULT_CAPS +
                getCapitalCase(curNode.getJavaAttributeName());
        Class childClass;
        try {
            childClass = moduleClass.getClassLoader().loadClass(className);
        } catch (ClassNotFoundException e) {
            throw new ModelConverterException(E_FAIL_TO_LOAD_CLASS + className, e);
        }
        if (curNode.getType().equals(SINGLE_INSTANCE_NODE)) {
            return parentObj.anydata(childClass).get(0);
        }
        return parentObj.anydata(childClass);
    }

    /**
     * Adds the child node to the data tree by taking operation type from the
     * object. Also, binds the object to the data node through data tree node
     * info.
     *
     * @param curNode     current YANG node
     * @param curNodeInfo current data tree info
     */
    private void processChildNode(YangNode curNode, DataTreeNodeInfo curNodeInfo) {
        if (extBuilder == null) {
            InnerNode.Builder builder = InnerNode.builder(
                    curNode.getName(), curNode.getNameSpace().getModuleNamespace());
            if (curNode instanceof YangList) {
                builder.type(MULTI_INSTANCE_NODE);
            } else {
                builder.type(SINGLE_INSTANCE_NODE);
            }
            extBuilder = builder;
        } else {
            extBuilder = extBuilder.createChildBuilder(
                    curNode.getName(), curNode.getNameSpace()
                            .getModuleNamespace()).type(curNodeInfo.type());
        }
        exitBuilderSchema = curNode;
        extBuilder.appInfo(curNodeInfo);
    }

    /**
     * Processes every leaf in a YANG node. Iterates through the leaf, takes
     * value from the leaf and adds it to the data tree with value. If value is
     * not present, and select leaf is set, adds it to the data tree without
     * value.
     *
     * @param yangNode leaves holder node
     * @param info     data node info
     */
    private void processLeaves(YangNode yangNode, DataTreeNodeInfo info) {
        if (yangNode instanceof YangLeavesHolder) {
            List leavesList = ((YangLeavesHolder) yangNode)
                    .getListOfLeaf();
            if (leavesList != null) {
                for (YangLeaf yangLeaf : leavesList) {
                    DataTreeNodeInfo parentInfo = getParentInfo();
                    if (yangNode instanceof YangAugment) {
                        if (info != null) {
                            parentInfo = info;
                        }
                    }
                    if (parentInfo == null) {
                        parentInfo = info;
                    }
                    Object parentObj = getParentObjectOfNode(parentInfo,
                                                             yangNode);
                    Object leafType;
                    try {
                        leafType = getAttributeOfObject(parentObj,
                                                        getJavaName(yangLeaf));
                    } catch (NoSuchMethodException e) {
                        throw new ModelConverterException(e);
                    }
                    Object obj = getLeafObject(yangNode, yangLeaf, parentObj,
                                               leafType, false);
                    if (obj != null) {
                        if (isTypeEmpty(yangLeaf.getDataType())) {
                            String empty = String.valueOf(obj);
                            if (!empty.equals(TRUE)) {
                                continue;
                            }
                            obj = null;
                        }
                        createLeafNode(yangLeaf, obj);
                    }
                }
            }
        }
    }

    /**
     * Processes every leaf-list in a YANG node for adding the value in data
     * tree.
     *
     * @param yangNode list of leaf-list holder node
     * @param info     data node info
     */
    private void processLeavesList(YangNode yangNode, DataTreeNodeInfo info) {
        if (yangNode instanceof YangLeavesHolder) {
            List listOfLeafList =
                    ((YangLeavesHolder) yangNode).getListOfLeafList();

            if (listOfLeafList != null) {
                for (YangLeafList yangLeafList : listOfLeafList) {
                    addToBuilder(yangNode, yangLeafList, info);
                }
            }
        }
    }

    /**
     * Processes the list of objects of the leaf list and adds the leaf list
     * value to the builder.
     *
     * @param yangNode YANG node
     * @param leafList YANG leaf list
     * @param info     data node info
     */
    private List addToBuilder(
            YangNode yangNode, YangLeafList leafList,
            DataTreeNodeInfo info) {
        DataTreeNodeInfo dnbNodeInfo = getParentInfo();
        if (yangNode instanceof YangAugment) {
            if (info != null) {
                dnbNodeInfo = info;
            }
        }
        if (dnbNodeInfo == null) {
            dnbNodeInfo = info;
        }
        Object parentObj = getParentObjectOfNode(dnbNodeInfo, yangNode);
        List obj;
        try {
            obj = (List) getAttributeOfObject(parentObj,
                                                      getJavaName(leafList));
        } catch (NoSuchMethodException e) {
            throw new ModelConverterException(e);
        }
        if (obj != null) {
            Set objects = getLeafListObject(yangNode, leafList,
                                                    parentObj, obj);
            if (!objects.isEmpty()) {
                Object o = objects.iterator().next();
                if (isTypeEmpty(leafList.getDataType())) {
                    objects.clear();
                    String empty = String.valueOf(o);
                    if (!empty.equals(TRUE)) {
                        return null;
                    }
                    objects.add(null);
                }
                return addLeafList(objects, leafList);
            }
        }
        return null;
    }

    /**
     * Adds set of leaf list values in the builder and traverses back to the
     * holder.
     *
     * @param leafListVal set of values
     * @param leafList    YANG leaf list
     * @return node builders for leaf list or null
     */
    List addLeafList(Set leafListVal,
                                       YangLeafList leafList) {
        LeafType ltype;
        if (extBuilder != null) {
            for (Object val : leafListVal) {
                if (val != null) {
                    ltype = leafList.getLeafType(val.toString());
                } else {
                    ltype = leafList.getLeafType(null);
                }
                String valNamespace = getValNamespace(val, leafList);
                DataNode.Builder leaf = extBuilder.createChildBuilder(
                        leafList.getName(), leafList.getNameSpace()
                                .getModuleNamespace(), val, valNamespace)
                        .leafType(ltype);
                leaf.type(MULTI_INSTANCE_LEAF_VALUE_NODE);
                leaf.addLeafListValue(val);
                extBuilder = leaf.exitNode();
            }
            return null;
        }
        //In case of root node leaf lists.
        List builders = new ArrayList<>();
        for (Object val : leafListVal) {
            if (val != null) {
                ltype = leafList.getLeafType(val.toString());
            } else {
                ltype = leafList.getLeafType(null);
            }
            String valNamespace = getValNamespace(val, leafList);
            DataNode.Builder leaf = LeafNode.builder(
                    leafList.getName(), leafList.getNameSpace()
                            .getModuleNamespace()).value(val)
                    .valueNamespace(valNamespace).leafType(ltype);
            leaf.type(MULTI_INSTANCE_LEAF_VALUE_NODE);
            leaf.addLeafListValue(val);
            builders.add(leaf);
        }
        return builders;
    }

    /**
     * Returns case object from the map that is bound to the parent node
     * info. For any case node, only when the key and value is matched the
     * object of the case is provided. If a match is not found, null is
     * returned.
     *
     * @param parentNodeInfo parent data tree node info
     * @param caseNode       case schema node
     * @return object of the case node
     */
    private Object getCaseObjectFromChoice(DataTreeNodeInfo parentNodeInfo,
                                           YangSchemaNode caseNode) {
        String javaName = getCapitalCase(
                caseNode.getJavaClassNameOrBuiltInType());
        YangNode parent = ((YangNode) caseNode).getParent();
        String choiceName;
        if (parent instanceof YangAugment) {
            choiceName = ((YangAugment) parent).getAugmentedNode().getName();
        } else {
            choiceName = ((YangNode) caseNode).getParent().getName();
        }
        Map mapObj = parentNodeInfo.getChoiceCaseMap();
        Object caseObj = mapObj.get(choiceName);
        Class interfaceClass = getInterfaceClassFromImplClass(caseObj);
        return interfaceClass.getSimpleName().equals(javaName) ? caseObj : null;
    }

    /**
     * Creates data leaf node.
     *
     * @param yangLeaf YANG leaf
     * @param val      value for the leaf
     * @return datanode builder created
     */
    DataNode.Builder createLeafNode(YangLeaf yangLeaf, Object val) {
        String valNamespace = getValNamespace(val, yangLeaf);
        LeafType ltype;
        if (val != null) {
            ltype = yangLeaf.getLeafType(val.toString());
        } else {
            ltype = yangLeaf.getLeafType(null);
        }
        if (extBuilder != null) {
            //Add leaf to key leaves.
            if (yangLeaf.isKeyLeaf()) {
                extBuilder.addKeyLeaf(yangLeaf.getName(), yangLeaf
                        .getNameSpace().getModuleNamespace(), val);
            }
            //build leaf node and add to parent node.
            DataNode.Builder leaf = extBuilder.createChildBuilder(
                    yangLeaf.getName(), yangLeaf.getNameSpace()
                            .getModuleNamespace(), val, valNamespace)
                    .leafType(ltype);
            leaf.type(SINGLE_INSTANCE_LEAF_VALUE_NODE);

            extBuilder = leaf.exitNode();
            return leaf;
        }
        return LeafNode.builder(yangLeaf.getName(), yangLeaf.getNameSpace()
                .getModuleNamespace())
                .type(SINGLE_INSTANCE_LEAF_VALUE_NODE)
                .value(val).valueNamespace(valNamespace).leafType(ltype);
    }

    /**
     * Returns the node info which can be processed, by eliminating the nodes
     * which need not to be processed at normal conditions such as RPC,
     * notification and augment.
     *
     * @param curNode current node
     * @return info of node which needs processing
     */
    private ModelConverterTraversalInfo getProcessableInfo(YangNode curNode) {
        if (curNode.getNextSibling() != null) {
            YangNode sibling = curNode.getNextSibling();
            while (isNonProcessableNode(sibling)) {
                sibling = sibling.getNextSibling();
            }
            if (sibling != null) {
                return new ModelConverterTraversalInfo(sibling, SIBLING);
            }
        }
        YangNode parent = curNode.getParent();
        if (!(parent instanceof RpcNotificationContainer)) {
            return new ModelConverterTraversalInfo(curNode.getParent(), PARENT);
        }
        return new ModelConverterTraversalInfo((YangNode) exitBuilderSchema, PARENT);
    }

    public static String getValNamespace(Object val, LeafSchemaContext lsc) {
        String valNamespace = null;
        if (val != null) {
            YangNamespace yn = lsc.getValueNamespace(val.toString());
            if (yn != null) {
                valNamespace = yn.getModuleNamespace();
            }
        }
        return valNamespace;
    }
}