Minor code refactoring in YangParserImpl.
[yangtools.git] / yang / yang-parser-impl / src / main / java / org / opendaylight / yangtools / yang / parser / impl / YangParserImpl.java
index 330c673bfeb0362294104a1142bf24e17de5ff90..7cd05c0534ae12df6e133cf64020247b50834c6c 100644 (file)
@@ -15,19 +15,15 @@ import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
-import java.net.URI;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Date;
 import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
-import java.util.NoSuchElementException;
 import java.util.Set;
 import java.util.TreeMap;
 
@@ -38,44 +34,33 @@ import org.antlr.v4.runtime.tree.ParseTreeWalker;
 import org.opendaylight.yangtools.antlrv4.code.gen.YangLexer;
 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser;
 import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
-import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
-import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ExtensionDefinition;
 import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
 import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
-import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.Module;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
-import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
-import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
 import org.opendaylight.yangtools.yang.model.parser.api.YangModelParser;
-import org.opendaylight.yangtools.yang.model.util.ExtendedType;
 import org.opendaylight.yangtools.yang.model.util.IdentityrefType;
 import org.opendaylight.yangtools.yang.parser.builder.api.AugmentationSchemaBuilder;
 import org.opendaylight.yangtools.yang.parser.builder.api.Builder;
 import org.opendaylight.yangtools.yang.parser.builder.api.DataNodeContainerBuilder;
-import org.opendaylight.yangtools.yang.parser.builder.api.DataSchemaNodeBuilder;
 import org.opendaylight.yangtools.yang.parser.builder.api.GroupingBuilder;
-import org.opendaylight.yangtools.yang.parser.builder.api.GroupingMember;
 import org.opendaylight.yangtools.yang.parser.builder.api.SchemaNodeBuilder;
 import org.opendaylight.yangtools.yang.parser.builder.api.TypeAwareBuilder;
-import org.opendaylight.yangtools.yang.parser.builder.api.TypeDefinitionBuilder;
 import org.opendaylight.yangtools.yang.parser.builder.api.UsesNodeBuilder;
 import org.opendaylight.yangtools.yang.parser.builder.impl.DeviationBuilder;
+import org.opendaylight.yangtools.yang.parser.builder.impl.ExtensionBuilder;
 import org.opendaylight.yangtools.yang.parser.builder.impl.IdentitySchemaNodeBuilder;
 import org.opendaylight.yangtools.yang.parser.builder.impl.IdentityrefTypeBuilder;
 import org.opendaylight.yangtools.yang.parser.builder.impl.ModuleBuilder;
 import org.opendaylight.yangtools.yang.parser.builder.impl.UnionTypeBuilder;
 import org.opendaylight.yangtools.yang.parser.builder.impl.UnknownSchemaNodeBuilder;
+import org.opendaylight.yangtools.yang.parser.util.GroupingUtils;
 import org.opendaylight.yangtools.yang.parser.util.ModuleDependencySort;
-import org.opendaylight.yangtools.yang.parser.util.RefineHolder;
-import org.opendaylight.yangtools.yang.parser.util.RefineUtils;
+import org.opendaylight.yangtools.yang.parser.util.ParserUtils;
 import org.opendaylight.yangtools.yang.parser.util.YangParseException;
 import org.opendaylight.yangtools.yang.validator.YangModelBasicValidator;
 import org.slf4j.Logger;
@@ -231,8 +216,7 @@ public final class YangParserImpl implements YangModelParser {
             final SchemaContext context) {
         final ModuleBuilder[] builders = parseModuleBuilders(yangFileStreams, streamToBuilderMap);
 
-        // Linked Hash Map MUST be used because Linked Hash Map preserves ORDER
-        // of items stored in map.
+        // LinkedHashMap must be used to preserve order
         final LinkedHashMap<String, TreeMap<Date, ModuleBuilder>> modules = new LinkedHashMap<String, TreeMap<Date, ModuleBuilder>>();
 
         // module dependency graph sorted
@@ -289,13 +273,10 @@ public final class YangParserImpl implements YangModelParser {
 
     private Map<ModuleBuilder, Module> build(final Map<String, TreeMap<Date, ModuleBuilder>> modules) {
         // fix unresolved nodes
-        for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
-            for (Map.Entry<Date, ModuleBuilder> childEntry : entry.getValue().entrySet()) {
-                final ModuleBuilder moduleBuilder = childEntry.getValue();
-                fixUnresolvedNodes(modules, moduleBuilder);
-            }
-        }
+        findUsesTargets(modules, null);
+        resolveDirtyNodes(modules);
         resolveAugments(modules);
+        resolveUses(modules);
         resolveDeviations(modules);
 
         // build
@@ -313,15 +294,12 @@ public final class YangParserImpl implements YangModelParser {
     }
 
     private Map<ModuleBuilder, Module> buildWithContext(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
-            SchemaContext context) {
+            final SchemaContext context) {
         // fix unresolved nodes
-        for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
-            for (Map.Entry<Date, ModuleBuilder> childEntry : entry.getValue().entrySet()) {
-                final ModuleBuilder moduleBuilder = childEntry.getValue();
-                fixUnresolvedNodesWithContext(modules, moduleBuilder, context);
-            }
-        }
+        findUsesTargets(modules, context);
+        resolvedDirtyNodesWithContext(modules, context);
         resolveAugmentsWithContext(modules, context);
+        resolveUsesWithContext(modules, context);
         resolveDeviationsWithContext(modules, context);
 
         // build
@@ -338,19 +316,27 @@ public final class YangParserImpl implements YangModelParser {
         return result;
     }
 
-    private void fixUnresolvedNodes(final Map<String, TreeMap<Date, ModuleBuilder>> modules, final ModuleBuilder builder) {
-        resolveDirtyNodes(modules, builder);
-        resolveIdentities(modules, builder);
-        resolveUsesNodes(modules, builder);
-        resolveUnknownNodes(modules, builder);
+    private void resolveDirtyNodes(final Map<String, TreeMap<Date, ModuleBuilder>> modules) {
+        for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
+            for (Map.Entry<Date, ModuleBuilder> childEntry : entry.getValue().entrySet()) {
+                final ModuleBuilder module = childEntry.getValue();
+                resolveDirtyNodes(modules, module);
+                resolveIdentities(modules, module);
+                resolveUnknownNodes(modules, module);
+            }
+        }
     }
 
-    private void fixUnresolvedNodesWithContext(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
-            final ModuleBuilder builder, final SchemaContext context) {
-        resolveDirtyNodesWithContext(modules, builder, context);
-        resolveIdentitiesWithContext(modules, builder, context);
-        resolveUsesNodesWithContext(modules, builder, context);
-        resolveUnknownNodesWithContext(modules, builder, context);
+    private void resolvedDirtyNodesWithContext(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
+            final SchemaContext context) {
+        for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
+            for (Map.Entry<Date, ModuleBuilder> childEntry : entry.getValue().entrySet()) {
+                final ModuleBuilder module = childEntry.getValue();
+                resolveDirtyNodesWithContext(modules, module, context);
+                resolveIdentitiesWithContext(modules, module, context);
+                resolveUnknownNodesWithContext(modules, module, context);
+            }
+        }
     }
 
     /**
@@ -400,76 +386,87 @@ public final class YangParserImpl implements YangModelParser {
     }
 
     /**
-     * Go through all augment definitions and resolve them. It is expected that
-     * modules are already sorted by their dependencies. This method also finds
-     * augment target node and add child nodes to it.
+     * Go through all augment definitions and perform augmentation. It is
+     * expected that modules are already sorted by their dependencies.
      *
      * @param modules
-     *            all available modules
+     *            all loaded modules
      */
     private void resolveAugments(final Map<String, TreeMap<Date, ModuleBuilder>> modules) {
-        final List<ModuleBuilder> allModulesList = new ArrayList<ModuleBuilder>();
-        final Set<ModuleBuilder> allModulesSet = new HashSet<ModuleBuilder>();
+        // collect augments from all loaded modules
+        final List<AugmentationSchemaBuilder> allAugments = new ArrayList<>();
         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
             for (Map.Entry<Date, ModuleBuilder> inner : entry.getValue().entrySet()) {
-                allModulesList.add(inner.getValue());
-                allModulesSet.add(inner.getValue());
+                allAugments.addAll(inner.getValue().getAllAugments());
             }
         }
 
-        for (int i = 0; i < allModulesList.size(); i++) {
-            final ModuleBuilder module = allModulesList.get(i);
-            // try to resolve augments in module
-            resolveAugment(modules, module);
-            // while all augments are not resolved
-            final Iterator<ModuleBuilder> allModulesIterator = allModulesSet.iterator();
-            while (!(module.getAugmentsResolved() == module.getAllAugments().size())) {
-                ModuleBuilder nextModule = null;
-                // try resolve other module augments
-                try {
-                    nextModule = allModulesIterator.next();
-                    resolveAugment(modules, nextModule);
-                } catch (NoSuchElementException e) {
-                       
-                       
-                    throw new YangParseException("Failed to resolve augments in module '" + module.getName() + "'.", e);
-                }
-                // then try to resolve first module again
-                resolveAugment(modules, module);
+        for (int i = 0; i < allAugments.size(); i++) {
+            // pick one augment
+            final AugmentationSchemaBuilder augment = allAugments.get(i);
+            // create collection of others
+            List<AugmentationSchemaBuilder> others = new ArrayList<>(allAugments);
+            others.remove(augment);
+
+            // try to resolve it
+            boolean resolved = resolveAugment(modules, augment);
+            // while not resolved
+            int j = 0;
+            while (!(resolved) && j < others.size()) {
+                // try to resolve next augment
+                resolveAugment(modules, others.get(j));
+                // then try to resolve first again
+                resolved = resolveAugment(modules, augment);
+                j++;
+
+            }
+
+            if (!resolved) {
+                throw new YangParseException(augment.getModuleName(), augment.getLine(),
+                        "Error in augment parsing: failed to find augment target");
             }
         }
     }
 
     /**
-     * Tries to resolve augments in given module. If augment target node is not
-     * found, do nothing.
+     * Search for augment target and perform augmentation.
      *
      * @param modules
-     *            all available modules
-     * @param module
-     *            current module
+     *            all loaded modules
+     * @param augmentBuilder
+     *            augment to resolve
+     * @return true if target node found, false otherwise
      */
-    private void resolveAugment(final Map<String, TreeMap<Date, ModuleBuilder>> modules, final ModuleBuilder module) {
-        if (module.getAugmentsResolved() < module.getAllAugments().size()) {
-            for (AugmentationSchemaBuilder augmentBuilder : module.getAllAugments()) {
-
-                if (!augmentBuilder.isResolved()) {
-                    final SchemaPath augmentTargetSchemaPath = augmentBuilder.getTargetPath();
-                    final List<QName> path = augmentTargetSchemaPath.getPath();
-
-                    final QName qname = path.get(0);
-                    String prefix = qname.getPrefix();
-                    if (prefix == null) {
-                        prefix = module.getPrefix();
-                    }
-
-                    final ModuleBuilder dependentModule = findDependentModuleBuilder(modules, module, prefix,
-                            augmentBuilder.getLine());
-                    processAugmentation(augmentBuilder, path, module, dependentModule);
-                }
-
+    private boolean resolveAugment(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
+            final AugmentationSchemaBuilder augmentBuilder) {
+        if (augmentBuilder.isResolved()) {
+            return true;
+        }
+
+        int line = augmentBuilder.getLine();
+        ModuleBuilder module = getParentModule(augmentBuilder);
+        List<QName> path = augmentBuilder.getTargetPath().getPath();
+        Builder augmentParent = augmentBuilder.getParent();
+
+        Builder firstNodeParent = null;
+        if (augmentParent instanceof ModuleBuilder) {
+            // if augment is defined under module, parent of first node is
+            // target module
+            final QName firstNameInPath = path.get(0);
+            String prefix = firstNameInPath.getPrefix();
+            if (prefix == null) {
+                prefix = module.getPrefix();
             }
+            firstNodeParent = findDependentModuleBuilder(modules, module, prefix, line);
+        } else if (augmentParent instanceof UsesNodeBuilder) {
+            firstNodeParent = augmentParent.getParent();
+        } else {
+            // augment can be defined only under module or uses
+            throw new YangParseException(augmentBuilder.getModuleName(), line,
+                    "Failed to parse augment: Unresolved parent of augment: " + augmentParent);
         }
+
+        return processAugmentation(augmentBuilder, firstNodeParent, path);
     }
 
     /**
@@ -484,74 +481,78 @@ public final class YangParserImpl implements YangModelParser {
      */
     private void resolveAugmentsWithContext(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
             final SchemaContext context) {
-        final List<ModuleBuilder> allModulesList = new ArrayList<ModuleBuilder>();
-        final Set<ModuleBuilder> allModulesSet = new HashSet<ModuleBuilder>();
+        // collect augments from all loaded modules
+        final List<AugmentationSchemaBuilder> allAugments = new ArrayList<>();
         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
             for (Map.Entry<Date, ModuleBuilder> inner : entry.getValue().entrySet()) {
-                allModulesList.add(inner.getValue());
-                allModulesSet.add(inner.getValue());
+                allAugments.addAll(inner.getValue().getAllAugments());
             }
         }
 
-        for (int i = 0; i < allModulesList.size(); i++) {
-            final ModuleBuilder module = allModulesList.get(i);
-            // try to resolve augments in module
-            resolveAugmentWithContext(modules, module, context);
-            // while all augments are not resolved
-            final Iterator<ModuleBuilder> allModulesIterator = allModulesSet.iterator();
-            while (!(module.getAugmentsResolved() == module.getAllAugments().size())) {
-                ModuleBuilder nextModule = null;
-                // try resolve other module augments
-                try {
-                    nextModule = allModulesIterator.next();
-                    resolveAugmentWithContext(modules, nextModule, context);
-                } catch (NoSuchElementException e) {
-                    throw new YangParseException("Failed to resolve augments in module '" + module.getName() + "'.", e);
-                }
-                // then try to resolve first module again
-                resolveAugmentWithContext(modules, module, context);
+        for (int i = 0; i < allAugments.size(); i++) {
+            // pick augment from list
+            final AugmentationSchemaBuilder augment = allAugments.get(i);
+            // try to resolve it
+            boolean resolved = resolveAugmentWithContext(modules, augment, context);
+            // while not resolved
+            int j = i + 1;
+            while (!(resolved) && j < allAugments.size()) {
+                // try to resolve next augment
+                resolveAugmentWithContext(modules, allAugments.get(j), context);
+                // then try to resolve first again
+                resolved = resolveAugmentWithContext(modules, augment, context);
+                j++;
+            }
+
+            if (!resolved) {
+                throw new YangParseException(augment.getModuleName(), augment.getLine(),
+                        "Error in augment parsing: failed to find augment target");
             }
         }
     }
 
     /**
-     * Tries to resolve augments in given module. If augment target node is not
-     * found, do nothing.
+     * Search for augment target and perform augmentation.
      *
      * @param modules
-     *            all available modules
-     * @param module
-     *            current module
+     *            all loaded modules
+     * @param augment
+     *            augment to resolve
+     * @param context
+     *            SchemaContext containing already resolved modules
+     * @return true if target node found, false otherwise
      */
-    private void resolveAugmentWithContext(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
-            final ModuleBuilder module, final SchemaContext context) {
-        if (module.getAugmentsResolved() < module.getAllAugments().size()) {
-
-            for (AugmentationSchemaBuilder augmentBuilder : module.getAllAugments()) {
-                final int line = augmentBuilder.getLine();
-
-                if (!augmentBuilder.isResolved()) {
-                    final List<QName> path = augmentBuilder.getTargetPath().getPath();
-                    final QName qname = path.get(0);
-                    String prefix = qname.getPrefix();
-                    if (prefix == null) {
-                        prefix = module.getPrefix();
-                    }
+    private boolean resolveAugmentWithContext(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
+            final AugmentationSchemaBuilder augment, final SchemaContext context) {
+        if (augment.isResolved()) {
+            return true;
+        }
+        int line = augment.getLine();
+        ModuleBuilder module = getParentModule(augment);
+        List<QName> path = augment.getTargetPath().getPath();
+        final QName firstNameInPath = path.get(0);
+        String prefix = firstNameInPath.getPrefix();
+        if (prefix == null) {
+            prefix = module.getPrefix();
+        }
+        Builder augmentParent = augment.getParent();
+        Builder currentParent = null;
 
-                    // try to find augment target module in loaded modules...
-                    final ModuleBuilder dependentModuleBuilder = findDependentModuleBuilder(modules, module, prefix,
-                            line);
-                    if (dependentModuleBuilder == null) {
-                        // perform augmentation on module from context and
-                        // continue to next augment
-                        processAugmentationOnContext(augmentBuilder, path, module, prefix, line, context);
-                        continue;
-                    } else {
-                        processAugmentation(augmentBuilder, path, module, dependentModuleBuilder);
-                    }
-                }
+        if (augmentParent instanceof ModuleBuilder) {
+            // if augment is defined under module, first parent is target module
+            currentParent = findDependentModuleBuilder(modules, module, prefix, line);
+        } else if (augmentParent instanceof UsesNodeBuilder) {
+            currentParent = augmentParent.getParent();
+        } else {
+            // augment can be defined only under module or uses
+            throw new YangParseException(augment.getModuleName(), augment.getLine(),
+                    "Error in augment parsing: Unresolved parent of augment: " + augmentParent);
+        }
 
-            }
+        if (currentParent == null) {
+            return processAugmentationOnContext(augment, path, module, prefix, context);
+        } else {
+            return processAugmentation(augment, currentParent, path);
         }
     }
 
@@ -646,326 +647,194 @@ public final class YangParserImpl implements YangModelParser {
     }
 
     /**
-     * Go through uses statements defined in current module and resolve their
-     * refine statements.
+     * Find and add reference of uses target grouping.
      *
      * @param modules
-     *            all modules
-     * @param module
-     *            module being resolved
+     *            all loaded modules
+     * @param context
+     *            SchemaContext containing already resolved modules or null if
+     *            context is not available
      */
-    private void resolveUsesNodes(final Map<String, TreeMap<Date, ModuleBuilder>> modules, final ModuleBuilder module) {
-        final List<UsesNodeBuilder> allModuleUses = module.getAllUsesNodes();
-        for (UsesNodeBuilder usesNode : allModuleUses) {
-            // process uses operation
-            final GroupingBuilder targetGrouping = getTargetGroupingFromModules(usesNode, modules, module);
-            usesNode.setGroupingPath(targetGrouping.getPath());
-            processUsesNode(module, usesNode, targetGrouping);
-            // refine
-            for (RefineHolder refine : usesNode.getRefines()) {
-                DataSchemaNodeBuilder nodeToRefine = null;
-                for (DataSchemaNodeBuilder dsnb : usesNode.getTargetChildren()) {
-                    if (refine.getName().equals(dsnb.getQName().getLocalName())) {
-                        nodeToRefine = dsnb;
-                        break;
-                    }
-                }
-                if (nodeToRefine == null) {
-                    throw new YangParseException(refine.getModuleName(), refine.getLine(), "Refine target node '"
-                            + refine.getName() + "' not found");
-                }
-                if (nodeToRefine instanceof GroupingMember) {
-                    ((GroupingMember) nodeToRefine).setAddedByUses(true);
-                }
-                RefineUtils.performRefine(nodeToRefine, refine);
-                usesNode.addRefineNode(nodeToRefine);
+    private void findUsesTargets(final Map<String, TreeMap<Date, ModuleBuilder>> modules, final SchemaContext context) {
+        final List<UsesNodeBuilder> allUses = new ArrayList<>();
+        for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
+            for (Map.Entry<Date, ModuleBuilder> inner : entry.getValue().entrySet()) {
+                allUses.addAll(inner.getValue().getAllUsesNodes());
             }
         }
-        for (UsesNodeBuilder usesNode : allModuleUses) {
-            final GroupingBuilder targetGrouping = getTargetGroupingFromModules(usesNode, modules, module);
-            processUsesTarget(module, usesNode, targetGrouping);
+        for (UsesNodeBuilder usesNode : allUses) {
+            ModuleBuilder module = ParserUtils.getParentModule(usesNode);
+            final GroupingBuilder targetGroupingBuilder = GroupingUtils.getTargetGroupingFromModules(usesNode, modules,
+                    module);
+            if (targetGroupingBuilder == null) {
+                if (context == null) {
+                    throw new YangParseException(module.getName(), usesNode.getLine(), "Referenced grouping '"
+                            + usesNode.getGroupingName() + "' not found.");
+                } else {
+                    GroupingDefinition targetGroupingDefinition = GroupingUtils.getTargetGroupingFromContext(usesNode,
+                            module, context);
+                    usesNode.setGroupingDefinition(targetGroupingDefinition);
+                }
+            } else {
+                usesNode.setGrouping(targetGroupingBuilder);
+            }
         }
     }
 
     /**
-     * Tries to search target grouping in given modules and resolve refine
-     * nodes. If grouping is not found in modules, method tries to find it in
-     * modules from context.
+     * Copy data from uses target, update uses parent and perform refinement.
+     * Augmentations have to be resolved already.
      *
      * @param modules
      *            all loaded modules
-     * @param module
-     *            current module
-     * @param context
-     *            SchemaContext containing already resolved modules
      */
-    private void resolveUsesNodesWithContext(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
-            final ModuleBuilder module, final SchemaContext context) {
-        final List<UsesNodeBuilder> moduleUses = module.getAllUsesNodes();
-        for (UsesNodeBuilder usesNode : moduleUses) {
-            final GroupingBuilder targetGroupingBuilder = getTargetGroupingFromModules(usesNode, modules, module);
-            if (targetGroupingBuilder == null) {
-                final GroupingDefinition targetGrouping = getTargetGroupingFromContext(usesNode, module, context);
-                usesNode.setGroupingPath(targetGrouping.getPath());
-                processUsesNode(usesNode, targetGrouping);
-                for (RefineHolder refine : usesNode.getRefines()) {
-                    DataSchemaNodeBuilder nodeToRefine = null;
-                    for (DataSchemaNodeBuilder dsnb : usesNode.getTargetChildren()) {
-                        if (refine.getName().equals(dsnb.getQName().getLocalName())) {
-                            nodeToRefine = dsnb;
-                            break;
-                        }
-                    }
-                    if (nodeToRefine == null) {
-                        throw new YangParseException(refine.getModuleName(), refine.getLine(), "Refine target node '"
-                                + refine.getName() + "' not found");
-                    }
-                    if (nodeToRefine instanceof GroupingMember) {
-                        ((GroupingMember) nodeToRefine).setAddedByUses(true);
-                    }
-                    RefineUtils.performRefine(nodeToRefine, refine);
-                    usesNode.addRefineNode(nodeToRefine);
-                }
-            } else {
-                usesNode.setGroupingPath(targetGroupingBuilder.getPath());
-                processUsesNode(module, usesNode, targetGroupingBuilder);
-                for (RefineHolder refine : usesNode.getRefines()) {
-                    DataSchemaNodeBuilder nodeToRefine = null;
-                    for (DataSchemaNodeBuilder dsnb : usesNode.getTargetChildren()) {
-                        if (refine.getName().equals(dsnb.getQName().getLocalName())) {
-                            nodeToRefine = dsnb;
-                            break;
+    private void resolveUses(final Map<String, TreeMap<Date, ModuleBuilder>> modules) {
+        for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
+            for (Map.Entry<Date, ModuleBuilder> inner : entry.getValue().entrySet()) {
+                ModuleBuilder module = inner.getValue();
+                List<UsesNodeBuilder> usesNodes = null;
+                boolean dataCollected = module.isAllUsesDataCollected();
+
+                while (!dataCollected) {
+                    usesNodes = new ArrayList<>(module.getAllUsesNodes());
+                    for (UsesNodeBuilder usesNode : usesNodes) {
+                        if (!usesNode.isDataCollected()) {
+                            GroupingUtils.collectUsesData(usesNode);
                         }
                     }
-                    if (nodeToRefine == null) {
-                        throw new YangParseException(refine.getModuleName(), refine.getLine(), "Refine target node '"
-                                + refine.getName() + "' not found");
-                    }
-                    if (nodeToRefine instanceof GroupingMember) {
-                        ((GroupingMember) nodeToRefine).setAddedByUses(true);
-                    }
-                    RefineUtils.performRefine(nodeToRefine, refine);
-                    usesNode.addRefineNode(nodeToRefine);
+                    dataCollected = module.isAllUsesDataCollected();
                 }
             }
         }
-    }
 
-    /**
-     * Add nodes defined in target grouping to current context.
-     *
-     * @param module
-     *            current module
-     * @param usesNode
-     * @param targetGrouping
-     */
-    private void processUsesNode(final ModuleBuilder module, final UsesNodeBuilder usesNode,
-            final GroupingBuilder targetGrouping) {
-        DataNodeContainerBuilder parent = usesNode.getParent();
-        URI namespace = null;
-        Date revision = null;
-        String prefix = null;
-        if (parent instanceof ModuleBuilder || parent instanceof AugmentationSchemaBuilder) {
-            namespace = module.getNamespace();
-            revision = module.getRevision();
-            prefix = module.getPrefix();
-        } else {
-            QName parentQName = parent.getQName();
-            namespace = parentQName.getNamespace();
-            revision = parentQName.getRevision();
-            prefix = parentQName.getPrefix();
+        // new cycle is must because in collecting data process new uses could
+        // be created
+        final List<UsesNodeBuilder> allModulesUses = new ArrayList<>();
+        for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
+            for (Map.Entry<Date, ModuleBuilder> inner : entry.getValue().entrySet()) {
+                allModulesUses.addAll(inner.getValue().getAllUsesNodes());
+            }
         }
-        SchemaPath parentPath = parent.getPath();
-        // child nodes
-        Set<DataSchemaNodeBuilder> newChildren = processUsesDataSchemaNode(usesNode,
-                targetGrouping.getChildNodeBuilders(), parentPath, namespace, revision, prefix);
-        usesNode.getTargetChildren().addAll(newChildren);
-        // groupings
-        Set<GroupingBuilder> newGroupings = processUsesGroupings(targetGrouping.getGroupingBuilders(), parentPath,
-                namespace, revision, prefix);
-        usesNode.getTargetGroupings().addAll(newGroupings);
-        // typedefs
-        Set<TypeDefinitionBuilder> newTypedefs = processUsesTypedefs(targetGrouping.getTypeDefinitionBuilders(),
-                parentPath, namespace, revision, prefix);
-        usesNode.getTargetTypedefs().addAll(newTypedefs);
-        // unknown nodes
-        List<UnknownSchemaNodeBuilder> newUnknownNodes = processUsesUnknownNodes(
-                targetGrouping.getUnknownNodeBuilders(), parentPath, namespace, revision, prefix);
-        usesNode.getTargetUnknownNodes().addAll(newUnknownNodes);
-    }
 
-    /**
-     * Check if target grouping contains uses nodes and if it does, merge
-     * current uses with them.
-     *
-     * @param module
-     * @param usesNode
-     * @param targetGrouping
-     */
-    private void processUsesTarget(final ModuleBuilder module, final UsesNodeBuilder usesNode,
-            final GroupingBuilder targetGrouping) {
-        DataNodeContainerBuilder parent = usesNode.getParent();
-        URI namespace = null;
-        Date revision = null;
-        String prefix = null;
-        if (parent instanceof ModuleBuilder || parent instanceof AugmentationSchemaBuilder) {
-            namespace = module.getNamespace();
-            revision = module.getRevision();
-            prefix = module.getPrefix();
-        } else {
-            QName parentQName = parent.getQName();
-            namespace = parentQName.getNamespace();
-            revision = parentQName.getRevision();
-            prefix = parentQName.getPrefix();
+        for (UsesNodeBuilder usesNode : allModulesUses) {
+            GroupingUtils.updateUsesParent(usesNode);
+            GroupingUtils.performRefine(usesNode);
         }
-        SchemaPath parentPath = parent.getPath();
-
-        for (UsesNodeBuilder unb : targetGrouping.getUses()) {
-            Set<DataSchemaNodeBuilder> newChildren = processUsesDataSchemaNode(usesNode, unb.getTargetChildren(),
-                    parentPath, namespace, revision, prefix);
-            usesNode.getTargetChildren().addAll(newChildren);
-
-            Set<GroupingBuilder> newGroupings = processUsesGroupings(unb.getTargetGroupings(), parentPath, namespace,
-                    revision, prefix);
-            usesNode.getTargetGroupings().addAll(newGroupings);
-
-            Set<TypeDefinitionBuilder> newTypedefs = processUsesTypedefs(unb.getTargetTypedefs(), parentPath,
-                    namespace, revision, prefix);
-            usesNode.getTargetTypedefs().addAll(newTypedefs);
-
-            List<UnknownSchemaNodeBuilder> newUnknownNodes = processUsesUnknownNodes(unb.getTargetUnknownNodes(),
-                    parentPath, namespace, revision, prefix);
-            usesNode.getTargetUnknownNodes().addAll(newUnknownNodes);
+        for (UsesNodeBuilder usesNode : allModulesUses) {
+            GroupingUtils.fixUsesNodesPath(usesNode);
         }
-    }
 
-    private void processUsesNode(final UsesNodeBuilder usesNode, final GroupingDefinition targetGrouping) {
-        final String moduleName = usesNode.getModuleName();
-        final int line = usesNode.getLine();
-        DataNodeContainerBuilder parent = usesNode.getParent();
-        URI namespace = null;
-        Date revision = null;
-        String prefix = null;
-        if (parent instanceof ModuleBuilder) {
-            ModuleBuilder m = (ModuleBuilder) parent;
-            namespace = m.getNamespace();
-            revision = m.getRevision();
-            prefix = m.getPrefix();
-        } else {
-            QName parentQName = parent.getQName();
-            namespace = parentQName.getNamespace();
-            revision = parentQName.getRevision();
-            prefix = parentQName.getPrefix();
+        for (UsesNodeBuilder usesNode : allModulesUses) {
+            if (usesNode.isCopy()) {
+                usesNode.getParent().getUsesNodes().remove(usesNode);
+            }
         }
-        SchemaPath parentPath = parent.getPath();
-
-        final Set<DataSchemaNodeBuilder> newChildren = new HashSet<>();
-        for (DataSchemaNode child : targetGrouping.getChildNodes()) {
-            if (child != null) {
-                DataSchemaNodeBuilder newChild = null;
-                QName newQName = new QName(namespace, revision, prefix, child.getQName().getLocalName());
-                if (child instanceof AnyXmlSchemaNode) {
-                    newChild = createAnyXml((AnyXmlSchemaNode) child, newQName, moduleName, line);
-                } else if (child instanceof ChoiceNode) {
-                    newChild = createChoice((ChoiceNode) child, newQName, moduleName, line);
-                } else if (child instanceof ContainerSchemaNode) {
-                    newChild = createContainer((ContainerSchemaNode) child, newQName, moduleName, line);
-                } else if (child instanceof LeafListSchemaNode) {
-                    newChild = createLeafList((LeafListSchemaNode) child, newQName, moduleName, line);
-                } else if (child instanceof LeafSchemaNode) {
-                    newChild = createLeafBuilder((LeafSchemaNode) child, newQName, moduleName, line);
-                } else if (child instanceof ListSchemaNode) {
-                    newChild = createList((ListSchemaNode) child, newQName, moduleName, line);
-                }
+    }
 
-                if (newChild == null) {
-                    throw new YangParseException(moduleName, line,
-                            "Unknown member of target grouping while resolving uses node.");
-                }
-                if (newChild instanceof GroupingMember) {
-                    ((GroupingMember) newChild).setAddedByUses(true);
+    /**
+     * Copy data from uses target, update uses parent and perform refinement.
+     * Augmentations have to be resolved already.
+     *
+     * @param modules
+     *            all loaded modules
+     * @param context
+     *            SchemaContext containing already resolved modules
+     */
+    private void resolveUsesWithContext(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
+            final SchemaContext context) {
+        for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
+            for (Map.Entry<Date, ModuleBuilder> inner : entry.getValue().entrySet()) {
+                ModuleBuilder module = inner.getValue();
+                List<UsesNodeBuilder> usesNodes = null;
+                boolean dataCollected = module.isAllUsesDataCollected();
+
+                while (!dataCollected) {
+                    usesNodes = new ArrayList<>(module.getAllUsesNodes());
+                    for (UsesNodeBuilder usesNode : usesNodes) {
+                        if (!usesNode.isDataCollected()) {
+                            if (usesNode.getGroupingBuilder() == null) {
+                                GroupingUtils.collectUsesDataFromContext(usesNode);
+                            } else {
+                                GroupingUtils.collectUsesData(usesNode);
+                            }
+                        }
+                    }
+                    dataCollected = module.isAllUsesDataCollected();
                 }
-
-                newChild.setPath(createSchemaPath(parentPath, newQName));
-                newChildren.add(newChild);
             }
         }
-        usesNode.getTargetChildren().addAll(newChildren);
-
-        final Set<GroupingBuilder> newGroupings = new HashSet<>();
-        for (GroupingDefinition g : targetGrouping.getGroupings()) {
-            QName newQName = new QName(namespace, revision, prefix, g.getQName().getLocalName());
-            GroupingBuilder newGrouping = createGrouping(g, newQName, moduleName, line);
-            newGrouping.setAddedByUses(true);
-            newGrouping.setPath(createSchemaPath(parentPath, newQName));
-            newGroupings.add(newGrouping);
+
+        // new cycle is must because in collecting data process new uses could
+        // be created
+        final List<UsesNodeBuilder> allModulesUses = new ArrayList<>();
+        for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
+            for (Map.Entry<Date, ModuleBuilder> inner : entry.getValue().entrySet()) {
+                allModulesUses.addAll(inner.getValue().getAllUsesNodes());
+            }
         }
-        usesNode.getTargetGroupings().addAll(newGroupings);
-
-        final Set<TypeDefinitionBuilder> newTypedefs = new HashSet<>();
-        for (TypeDefinition<?> td : targetGrouping.getTypeDefinitions()) {
-            QName newQName = new QName(namespace, revision, prefix, td.getQName().getLocalName());
-            TypeDefinitionBuilder newType = createTypedef((ExtendedType) td, newQName, moduleName, line);
-            newType.setAddedByUses(true);
-            newType.setPath(createSchemaPath(parentPath, newQName));
-            newTypedefs.add(newType);
+
+        for (UsesNodeBuilder usesNode : allModulesUses) {
+            GroupingUtils.updateUsesParent(usesNode);
+            GroupingUtils.performRefine(usesNode);
         }
-        usesNode.getTargetTypedefs().addAll(newTypedefs);
-
-        final List<UnknownSchemaNodeBuilder> newUnknownNodes = new ArrayList<>();
-        for (UnknownSchemaNode un : targetGrouping.getUnknownSchemaNodes()) {
-            QName newQName = new QName(namespace, revision, prefix, un.getQName().getLocalName());
-            UnknownSchemaNodeBuilder newNode = createUnknownSchemaNode(un, newQName, moduleName, line);
-            newNode.setAddedByUses(true);
-            newNode.setPath(createSchemaPath(parentPath, newQName));
-            newUnknownNodes.add(newNode);
+        for (UsesNodeBuilder usesNode : allModulesUses) {
+            GroupingUtils.fixUsesNodesPath(usesNode);
         }
-        usesNode.getTargetUnknownNodes().addAll(newUnknownNodes);
     }
 
     private void resolveUnknownNodes(final Map<String, TreeMap<Date, ModuleBuilder>> modules, final ModuleBuilder module) {
         for (UnknownSchemaNodeBuilder usnb : module.getAllUnknownNodes()) {
             QName nodeType = usnb.getNodeType();
-            if (nodeType.getNamespace() == null || nodeType.getRevision() == null) {
-                try {
-                    ModuleBuilder dependentModule = findDependentModuleBuilder(modules, module, nodeType.getPrefix(),
-                            usnb.getLine());
-                    QName newNodeType = new QName(dependentModule.getNamespace(), dependentModule.getRevision(),
-                            nodeType.getPrefix(), nodeType.getLocalName());
-                    usnb.setNodeType(newNodeType);
-                } catch (YangParseException e) {
-                    LOG.debug(module.getName(), usnb.getLine(), "Failed to find unknown node type: " + nodeType);
+            try {
+                ModuleBuilder dependentModule = findDependentModuleBuilder(modules, module, nodeType.getPrefix(),
+                        usnb.getLine());
+                for (ExtensionBuilder extension : dependentModule.getExtensions()) {
+                    if (extension.getQName().getLocalName().equals(nodeType.getLocalName())) {
+                        usnb.setNodeType(extension.getQName());
+                        usnb.setExtensionBuilder(extension);
+                        break;
+                    }
                 }
+            } catch (YangParseException e) {
+                throw new YangParseException(module.getName(), usnb.getLine(), "Failed to resolve node " + usnb
+                        + ": no such extension definition found.");
             }
         }
     }
 
     private void resolveUnknownNodesWithContext(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
             final ModuleBuilder module, final SchemaContext context) {
-        for (UnknownSchemaNodeBuilder unknownNodeBuilder : module.getAllUnknownNodes()) {
-            QName nodeType = unknownNodeBuilder.getNodeType();
-            if (nodeType.getNamespace() == null || nodeType.getRevision() == null) {
-                try {
-                    ModuleBuilder dependentModuleBuilder = findDependentModuleBuilder(modules, module,
-                            nodeType.getPrefix(), unknownNodeBuilder.getLine());
-
-                    QName newNodeType = null;
-                    if (dependentModuleBuilder == null) {
-                        Module dependentModule = findModuleFromContext(context, module, nodeType.getPrefix(),
-                                unknownNodeBuilder.getLine());
-                        newNodeType = new QName(dependentModule.getNamespace(), dependentModule.getRevision(),
-                                nodeType.getPrefix(), nodeType.getLocalName());
-                    } else {
-                        newNodeType = new QName(dependentModuleBuilder.getNamespace(),
-                                dependentModuleBuilder.getRevision(), nodeType.getPrefix(), nodeType.getLocalName());
-                    }
+        for (UnknownSchemaNodeBuilder usnb : module.getAllUnknownNodes()) {
+            QName nodeType = usnb.getNodeType();
+            try {
+                ModuleBuilder dependentModuleBuilder = findDependentModuleBuilder(modules, module,
+                        nodeType.getPrefix(), usnb.getLine());
 
-                    unknownNodeBuilder.setNodeType(newNodeType);
-                } catch (YangParseException e) {
-                    LOG.debug(module.getName(), unknownNodeBuilder.getLine(), "Failed to find unknown node type: "
-                            + nodeType);
+                if (dependentModuleBuilder == null) {
+                    Module dependentModule = findModuleFromContext(context, module, nodeType.getPrefix(),
+                            usnb.getLine());
+                    for (ExtensionDefinition e : dependentModule.getExtensionSchemaNodes()) {
+                        if (e.getQName().getLocalName().equals(nodeType.getLocalName())) {
+                            usnb.setNodeType(new QName(e.getQName().getNamespace(), e.getQName().getRevision(),
+                                    nodeType.getPrefix(), e.getQName().getLocalName()));
+                            usnb.setExtensionDefinition(e);
+                            break;
+                        }
+                    }
+                } else {
+                    for (ExtensionBuilder extension : dependentModuleBuilder.getExtensions()) {
+                        if (extension.getQName().getLocalName().equals(nodeType.getLocalName())) {
+                            usnb.setExtensionBuilder(extension);
+                            break;
+                        }
+                    }
                 }
+
+            } catch (YangParseException e) {
+                throw new YangParseException(module.getName(), usnb.getLine(), "Failed to resolve node " + usnb
+                        + ": no such extension definition found.");
             }
+
         }
     }