Merge changes I14f284cc,I65bfdb56
[yangtools.git] / yang / yang-parser-impl / src / main / java / org / opendaylight / yangtools / yang / parser / impl / YangParserImpl.java
index 98fdc57f71355450d5c0d14a4abefd8528f97852..954f6c9261018b20eeab9ecbeb27744de8b94030 100644 (file)
@@ -17,20 +17,19 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.net.URI;
 import java.util.ArrayList;
+import java.util.Collection;
 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;
 
+import com.google.common.base.Preconditions;
 import org.antlr.v4.runtime.ANTLRInputStream;
 import org.antlr.v4.runtime.CommonTokenStream;
 import org.antlr.v4.runtime.tree.ParseTree;
@@ -38,44 +37,35 @@ 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.ModuleImport;
 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;
@@ -88,6 +78,60 @@ import com.google.common.collect.Sets;
 public final class YangParserImpl implements YangModelParser {
     private static final Logger LOG = LoggerFactory.getLogger(YangParserImpl.class);
 
+    private static final String FAIL_DEVIATION_TARGET = "Failed to find deviation target.";
+
+    @Override
+    public Set<Module> parseYangModels(final File yangFile, final File directory) {
+        Preconditions.checkState(yangFile.exists(), yangFile + " does not exists");
+        Preconditions.checkState(directory.exists(), directory + " does not exists");
+        Preconditions.checkState(directory.isDirectory(), directory + " is not a directory");
+
+        final String yangFileName = yangFile.getName();
+        final String[] fileList = directory.list();
+        Preconditions.checkNotNull(fileList, directory + " not found");
+
+        FileInputStream yangFileStream = null;
+        LinkedHashMap<InputStream, File> streamToFileMap = new LinkedHashMap<>();
+
+        try {
+            yangFileStream = new FileInputStream(yangFile);
+            streamToFileMap.put(yangFileStream, yangFile);
+        } catch(FileNotFoundException e) {
+            LOG.warn("Exception while reading yang file: " + yangFile.getName(), e);
+        }
+
+        for (String fileName : fileList) {
+            if (fileName.equals(yangFileName)) {
+                continue;
+            }
+            File dependency = new File(directory, fileName);
+            try {
+                if (dependency.isFile()) {
+                    streamToFileMap.put(new FileInputStream(dependency), dependency);
+                }
+            } catch(FileNotFoundException e) {
+                LOG.warn("Exception while reading yang file: " + fileName, e);
+            }
+        }
+
+        Map<InputStream, ModuleBuilder> parsedBuilders = parseModuleBuilders(
+                new ArrayList<>(streamToFileMap.keySet()), new HashMap<ModuleBuilder, InputStream>());
+        ModuleBuilder main = parsedBuilders.get(yangFileStream);
+
+        List<ModuleBuilder> moduleBuilders = new ArrayList<>();
+        moduleBuilders.add(main);
+        filterImports(main, new ArrayList<>(parsedBuilders.values()), moduleBuilders);
+
+        ModuleBuilder[] builders = new ModuleBuilder[moduleBuilders.size()];
+        moduleBuilders.toArray(builders);
+
+        // module dependency graph sorted
+        List<ModuleBuilder> sorted = ModuleDependencySort.sort(builders);
+
+        final LinkedHashMap<String, TreeMap<Date, ModuleBuilder>> modules = orderModules(sorted);
+        return new LinkedHashSet<>(build(modules).values());
+    }
+
     @Override
     public Set<Module> parseYangModels(final List<File> yangFiles) {
         return Sets.newLinkedHashSet(parseYangModelsMapped(yangFiles).values());
@@ -107,7 +151,6 @@ public final class YangParserImpl implements YangModelParser {
             }
 
             Map<ModuleBuilder, InputStream> builderToStreamMap = Maps.newHashMap();
-
             final Map<String, TreeMap<Date, ModuleBuilder>> modules = resolveModuleBuilders(
                     Lists.newArrayList(inputStreams.keySet()), builderToStreamMap);
 
@@ -198,26 +241,27 @@ public final class YangParserImpl implements YangModelParser {
         return new SchemaContextImpl(modules);
     }
 
-    private ModuleBuilder[] parseModuleBuilders(List<InputStream> inputStreams,
+    private Map<InputStream, ModuleBuilder> parseModuleBuilders(List<InputStream> inputStreams,
             Map<ModuleBuilder, InputStream> streamToBuilderMap) {
 
         final ParseTreeWalker walker = new ParseTreeWalker();
-        final List<ParseTree> trees = parseStreams(inputStreams);
-        final ModuleBuilder[] builders = new ModuleBuilder[trees.size()];
+        final Map<InputStream, ParseTree> trees = parseStreams(inputStreams);
+        final Map<InputStream, ModuleBuilder> builders = new LinkedHashMap<>();
 
         // validate yang
-        new YangModelBasicValidator(walker).validate(trees);
+        new YangModelBasicValidator(walker).validate(new ArrayList<ParseTree>(trees.values()));
 
-        YangParserListenerImpl yangModelParser = null;
-        for (int i = 0; i < trees.size(); i++) {
+        YangParserListenerImpl yangModelParser;
+        for(Map.Entry<InputStream, ParseTree> entry : trees.entrySet()) {
             yangModelParser = new YangParserListenerImpl();
-            walker.walk(yangModelParser, trees.get(i));
+            walker.walk(yangModelParser, entry.getValue());
             ModuleBuilder moduleBuilder = yangModelParser.getModuleBuilder();
 
             // We expect the order of trees and streams has to be the same
-            streamToBuilderMap.put(moduleBuilder, inputStreams.get(i));
-            builders[i] = moduleBuilder;
+            streamToBuilderMap.put(moduleBuilder, entry.getKey());
+            builders.put(entry.getKey(), moduleBuilder);
         }
+
         return builders;
     }
 
@@ -229,21 +273,32 @@ public final class YangParserImpl implements YangModelParser {
     private Map<String, TreeMap<Date, ModuleBuilder>> resolveModuleBuildersWithContext(
             final List<InputStream> yangFileStreams, final Map<ModuleBuilder, InputStream> streamToBuilderMap,
             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.
-        final LinkedHashMap<String, TreeMap<Date, ModuleBuilder>> modules = new LinkedHashMap<String, TreeMap<Date, ModuleBuilder>>();
+        Map<InputStream, ModuleBuilder> parsedBuilders = parseModuleBuilders(yangFileStreams, streamToBuilderMap);
+        ModuleBuilder[] builders = new ModuleBuilder[parsedBuilders.size()];
+        final ModuleBuilder[] moduleBuilders = new ArrayList<>(parsedBuilders.values()).toArray(builders);
 
         // module dependency graph sorted
-        List<ModuleBuilder> sorted = null;
+        List<ModuleBuilder> sorted;
         if (context == null) {
             sorted = ModuleDependencySort.sort(builders);
         } else {
             sorted = ModuleDependencySort.sortWithContext(context, builders);
         }
 
-        for (final ModuleBuilder builder : sorted) {
+        return orderModules(sorted);
+    }
+
+    /**
+     * Order modules by name and revision.
+     *
+     * @param modules
+     *            modules to order
+     * @return modules ordered by name and revision
+     */
+    private LinkedHashMap<String, TreeMap<Date, ModuleBuilder>> orderModules(List<ModuleBuilder> modules) {
+        // LinkedHashMap must be used to preserve order
+        LinkedHashMap<String, TreeMap<Date, ModuleBuilder>> result = new LinkedHashMap<>();
+        for (final ModuleBuilder builder : modules) {
             if (builder == null) {
                 continue;
             }
@@ -252,20 +307,42 @@ public final class YangParserImpl implements YangModelParser {
             if (builderRevision == null) {
                 builderRevision = new Date(0L);
             }
-            TreeMap<Date, ModuleBuilder> builderByRevision = modules.get(builderName);
+            TreeMap<Date, ModuleBuilder> builderByRevision = result.get(builderName);
             if (builderByRevision == null) {
                 builderByRevision = new TreeMap<Date, ModuleBuilder>();
             }
             builderByRevision.put(builderRevision, builder);
-            modules.put(builderName, builderByRevision);
+            result.put(builderName, builderByRevision);
         }
-        return modules;
+        return result;
     }
 
-    private List<ParseTree> parseStreams(final List<InputStream> yangStreams) {
-        final List<ParseTree> trees = new ArrayList<ParseTree>();
+    private void filterImports(ModuleBuilder main, List<ModuleBuilder> other, List<ModuleBuilder> filtered) {
+        for (ModuleImport mi : main.getModuleImports()) {
+            for (ModuleBuilder builder : other) {
+                if (mi.getModuleName().equals(builder.getModuleName())) {
+                    if (mi.getRevision() == null) {
+                        if (!filtered.contains(builder)) {
+                            filtered.add(builder);
+                            filterImports(builder, other, filtered);
+                        }
+                    } else {
+                        if (mi.getRevision().equals(builder.getRevision())) {
+                            if (!filtered.contains(builder)) {
+                                filtered.add(builder);
+                                filterImports(builder, other, filtered);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    private Map<InputStream, ParseTree> parseStreams(final List<InputStream> yangStreams) {
+        final Map<InputStream, ParseTree> trees = new HashMap<>();
         for (InputStream yangStream : yangStreams) {
-            trees.add(parseStream(yangStream));
+            trees.put(yangStream, parseStream(yangStream));
         }
         return trees;
     }
@@ -289,23 +366,20 @@ 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);
-            }
-        }
+        resolveAugmentsTargetPath(modules, null);
+        resolveUsesTargetGrouping(modules, null);
+        resolveDirtyNodes(modules);
         resolveAugments(modules);
+        resolveUses(modules, false);
+        resolvedUsesPostProcessing(modules, false);
         resolveDeviations(modules);
 
         // build
         final Map<ModuleBuilder, Module> result = new LinkedHashMap<ModuleBuilder, Module>();
         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
-            final Map<Date, Module> modulesByRevision = new HashMap<Date, Module>();
             for (Map.Entry<Date, ModuleBuilder> childEntry : entry.getValue().entrySet()) {
                 final ModuleBuilder moduleBuilder = childEntry.getValue();
                 final Module module = moduleBuilder.build();
-                modulesByRevision.put(childEntry.getKey(), module);
                 result.put(moduleBuilder, module);
             }
         }
@@ -313,44 +387,49 @@ 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);
-            }
-        }
+        resolveAugmentsTargetPath(modules, context);
+        resolveUsesTargetGrouping(modules, context);
+        resolvedDirtyNodesWithContext(modules, context);
         resolveAugmentsWithContext(modules, context);
+        resolveUses(modules, true);
+        resolvedUsesPostProcessing(modules, true);
         resolveDeviationsWithContext(modules, context);
 
         // build
         final Map<ModuleBuilder, Module> result = new LinkedHashMap<ModuleBuilder, Module>();
         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
-            final Map<Date, Module> modulesByRevision = new HashMap<Date, Module>();
             for (Map.Entry<Date, ModuleBuilder> childEntry : entry.getValue().entrySet()) {
                 final ModuleBuilder moduleBuilder = childEntry.getValue();
                 final Module module = moduleBuilder.build();
-                modulesByRevision.put(childEntry.getKey(), module);
                 result.put(moduleBuilder, module);
             }
         }
         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();
+                resolveIdentities(modules, module);
+                resolveDirtyNodes(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();
+                resolveIdentitiesWithContext(modules, module, context);
+                resolveDirtyNodesWithContext(modules, module, context);
+                resolveUnknownNodesWithContext(modules, module, context);
+            }
+        }
     }
 
     /**
@@ -372,7 +451,13 @@ public final class YangParserImpl implements YangModelParser {
                 } else if (nodeToResolve.getTypedef() instanceof IdentityrefTypeBuilder) {
                     // special handling for identityref types
                     IdentityrefTypeBuilder idref = (IdentityrefTypeBuilder) nodeToResolve.getTypedef();
-                    nodeToResolve.setType(new IdentityrefType(findFullQName(modules, module, idref), idref.getPath()));
+                    IdentitySchemaNodeBuilder identity = findBaseIdentity(modules, module, idref.getBaseString(),
+                            idref.getLine());
+                    if (identity == null) {
+                        throw new YangParseException(module.getName(), idref.getLine(), "Failed to find base identity");
+                    }
+                    idref.setBaseIdentity(identity);
+                    nodeToResolve.setType(idref.build());
                 } else {
                     resolveType(nodeToResolve, modules, module);
                 }
@@ -391,7 +476,10 @@ public final class YangParserImpl implements YangModelParser {
                 } else if (nodeToResolve.getTypedef() instanceof IdentityrefTypeBuilder) {
                     // special handling for identityref types
                     IdentityrefTypeBuilder idref = (IdentityrefTypeBuilder) nodeToResolve.getTypedef();
-                    nodeToResolve.setType(new IdentityrefType(findFullQName(modules, module, idref), idref.getPath()));
+                    IdentitySchemaNodeBuilder identity = findBaseIdentity(modules, module, idref.getBaseString(),
+                            idref.getLine());
+                    idref.setBaseIdentity(identity);
+                    nodeToResolve.setType(idref.build());
                 } else {
                     resolveTypeWithContext(nodeToResolve, modules, module, context);
                 }
@@ -400,74 +488,171 @@ 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.
+     * Correct augment target path.
      *
      * @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>();
+    private void resolveAugmentsTargetPath(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
+            SchemaContext context) {
+        // 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);
+        for (AugmentationSchemaBuilder augment : allAugments) {
+            setCorrectAugmentTargetPath(modules, augment, context);
+        }
+    }
+
+    private void setCorrectAugmentTargetPath(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
+            final AugmentationSchemaBuilder augment, final SchemaContext context) {
+        ModuleBuilder module = ParserUtils.getParentModule(augment);
+        SchemaPath oldSchemaPath = augment.getTargetPath();
+        List<QName> oldPath = oldSchemaPath.getPath();
+        List<QName> newPath = new ArrayList<>();
+        for (QName qn : oldPath) {
+            URI ns = module.getNamespace();
+            Date rev = module.getRevision();
+            String pref = module.getPrefix();
+            String localPrefix = qn.getPrefix();
+            if (localPrefix != null && !("".equals(localPrefix))) {
+                ModuleBuilder currentModule = ParserUtils.findModuleFromBuilders(modules, module, localPrefix,
+                        augment.getLine());
+                if (currentModule == null) {
+                    Module m = ParserUtils.findModuleFromContext(context, module, localPrefix, augment.getLine());
+                    if (m == null) {
+                        throw new YangParseException(module.getName(), augment.getLine(), "Module with prefix "
+                                + localPrefix + " not found.");
+                    }
+                    ns = m.getNamespace();
+                    rev = m.getRevision();
+                    pref = m.getPrefix();
+                } else {
+                    ns = currentModule.getNamespace();
+                    rev = currentModule.getRevision();
+                    pref = currentModule.getPrefix();
                 }
-                // then try to resolve first module again
-                resolveAugment(modules, module);
             }
+            newPath.add(new QName(ns, rev, pref, qn.getLocalName()));
         }
+        augment.setTargetNodeSchemaPath(new SchemaPath(newPath, augment.getTargetPath().isAbsolute()));
     }
 
     /**
-     * Tries to resolve augments in given module. If augment target node is not
-     * found, do nothing.
+     * Go through all augment definitions and perform augmentation. It is
+     * expected that modules are already sorted by their dependencies.
      *
      * @param modules
-     *            all available modules
-     * @param module
-     *            current module
+     *            all loaded modules
      */
-    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();
-                    }
+    private void resolveAugments(final Map<String, TreeMap<Date, ModuleBuilder>> modules) {
+        // 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()) {
+                allAugments.addAll(inner.getValue().getAllAugments());
+            }
+        }
+
+        checkAugmentMandatoryNodes(allAugments);
+
+        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");
+            }
+        }
+    }
+
+    /**
+     * Check augments for mandatory nodes. If the target node is in another
+     * module, then nodes added by the augmentation MUST NOT be mandatory nodes.
+     * If mandatory node is found, throw an exception.
+     *
+     * @param augments
+     *            augments to check
+     */
+    private void checkAugmentMandatoryNodes(Collection<AugmentationSchemaBuilder> augments) {
+        for (AugmentationSchemaBuilder augment : augments) {
+            String augmentPrefix = augment.getTargetPath().getPath().get(0).getPrefix();
+            ModuleBuilder module = ParserUtils.getParentModule(augment);
+            String modulePrefix = module.getPrefix();
 
-                    final ModuleBuilder dependentModule = findDependentModuleBuilder(modules, module, prefix,
-                            augmentBuilder.getLine());
-                    processAugmentation(augmentBuilder, path, module, dependentModule);
+            if (augmentPrefix == null || augmentPrefix.isEmpty() || augmentPrefix.equals(modulePrefix)) {
+                continue;
+            }
+
+            for (DataSchemaNodeBuilder childNode : augment.getChildNodeBuilders()) {
+                if (childNode.getConstraints().isMandatory()) {
+                    throw new YangParseException(augment.getModuleName(), augment.getLine(),
+                            "Error in augment parsing: cannot augment mandatory node");
                 }
+            }
+        }
+    }
 
+    /**
+     * Search for augment target and perform augmentation.
+     *
+     * @param modules
+     *            all loaded modules
+     * @param augment
+     *            augment to resolve
+     * @return true if target node found, false otherwise
+     */
+    private boolean resolveAugment(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
+            final AugmentationSchemaBuilder augment) {
+        if (augment.isResolved()) {
+            return true;
+        }
+
+        int line = augment.getLine();
+        ModuleBuilder module = getParentModule(augment);
+        List<QName> path = augment.getTargetPath().getPath();
+        Builder augmentParent = augment.getParent();
+
+        Builder firstNodeParent;
+        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 = findModuleFromBuilders(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(augment.getModuleName(), line,
+                    "Failed to parse augment: Unresolved parent of augment: " + augmentParent);
         }
+
+        return processAugmentation(augment, firstNodeParent, path);
     }
 
     /**
@@ -482,74 +667,77 @@ 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();
-                    }
-
-                    // 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);
-                    }
-                }
+    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.getTargetNodeSchemaPath().getPath();
+        final QName firstNameInPath = path.get(0);
+        String prefix = firstNameInPath.getPrefix();
+        if (prefix == null) {
+            prefix = module.getPrefix();
+        }
+        Builder augmentParent = augment.getParent();
+        Builder currentParent;
+        if (augmentParent instanceof ModuleBuilder) {
+            // if augment is defined under module, first parent is target module
+            currentParent = findModuleFromBuilders(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);
         }
     }
 
@@ -566,25 +754,15 @@ public final class YangParserImpl implements YangModelParser {
         final Set<IdentitySchemaNodeBuilder> identities = module.getIdentities();
         for (IdentitySchemaNodeBuilder identity : identities) {
             final String baseIdentityName = identity.getBaseIdentityName();
+            final int line = identity.getLine();
             if (baseIdentityName != null) {
-                String baseIdentityPrefix = null;
-                String baseIdentityLocalName = null;
-                if (baseIdentityName.contains(":")) {
-                    final String[] splitted = baseIdentityName.split(":");
-                    baseIdentityPrefix = splitted[0];
-                    baseIdentityLocalName = splitted[1];
+                IdentitySchemaNodeBuilder baseIdentity = findBaseIdentity(modules, module, baseIdentityName, line);
+                if (baseIdentity == null) {
+                    throw new YangParseException(module.getName(), identity.getLine(), "Failed to find base identity");
                 } else {
-                    baseIdentityPrefix = module.getPrefix();
-                    baseIdentityLocalName = baseIdentityName;
-                }
-                final ModuleBuilder dependentModule = findDependentModuleBuilder(modules, module, baseIdentityPrefix,
-                        identity.getLine());
-
-                final Set<IdentitySchemaNodeBuilder> dependentModuleIdentities = dependentModule.getIdentities();
-                for (IdentitySchemaNodeBuilder idBuilder : dependentModuleIdentities) {
-                    if (idBuilder.getQName().getLocalName().equals(baseIdentityLocalName)) {
-                        identity.setBaseIdentity(idBuilder);
-                    }
+                    identity.setBaseIdentity(baseIdentity);
+                    IdentitySchemaNode built = identity.build();
+                    baseIdentity.addDerivedIdentity(built);
                 }
             }
         }
@@ -607,363 +785,187 @@ public final class YangParserImpl implements YangModelParser {
         final Set<IdentitySchemaNodeBuilder> identities = module.getIdentities();
         for (IdentitySchemaNodeBuilder identity : identities) {
             final String baseIdentityName = identity.getBaseIdentityName();
+            final int line = identity.getLine();
             if (baseIdentityName != null) {
-                String baseIdentityPrefix = null;
-                String baseIdentityLocalName = null;
-                if (baseIdentityName.contains(":")) {
-                    final String[] splitted = baseIdentityName.split(":");
-                    baseIdentityPrefix = splitted[0];
-                    baseIdentityLocalName = splitted[1];
-                } else {
-                    baseIdentityPrefix = module.getPrefix();
-                    baseIdentityLocalName = baseIdentityName;
-                }
-                final ModuleBuilder dependentModuleBuilder = findDependentModuleBuilder(modules, module,
-                        baseIdentityPrefix, identity.getLine());
-
-                if (dependentModuleBuilder == null) {
-                    final Module dependentModule = findModuleFromContext(context, module, baseIdentityPrefix,
-                            identity.getLine());
-                    final Set<IdentitySchemaNode> dependentModuleIdentities = dependentModule.getIdentities();
-                    for (IdentitySchemaNode idNode : dependentModuleIdentities) {
-                        if (idNode.getQName().getLocalName().equals(baseIdentityLocalName)) {
-                            identity.setBaseIdentity(idNode);
-                        }
+                IdentitySchemaNodeBuilder baseIdentity = findBaseIdentity(modules, module, baseIdentityName, line);
+                if (baseIdentity == null) {
+                    IdentitySchemaNode baseId = findBaseIdentityFromContext(modules, module, baseIdentityName, line,
+                            context);
+                    identity.setBaseIdentity(baseId);
+                    IdentitySchemaNode built = identity.build();
+                    if (baseId instanceof IdentitySchemaNodeBuilder.IdentitySchemaNodeImpl) {
+                        ((IdentitySchemaNodeBuilder.IdentitySchemaNodeImpl) baseId).toBuilder().addDerivedIdentity(
+                                built);
                     }
                 } else {
-                    final Set<IdentitySchemaNodeBuilder> dependentModuleIdentities = dependentModuleBuilder
-                            .getIdentities();
-                    for (IdentitySchemaNodeBuilder idBuilder : dependentModuleIdentities) {
-                        if (idBuilder.getQName().getLocalName().equals(baseIdentityLocalName)) {
-                            identity.setBaseIdentity(idBuilder);
-                        }
-                    }
+                    identity.setBaseIdentity(baseIdentity);
+                    IdentitySchemaNode built = identity.build();
+                    baseIdentity.addDerivedIdentity(built);
                 }
             }
         }
     }
 
     /**
-     * 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 resolveUsesTargetGrouping(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.getGroupingPathAsString() + "' 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. Augmentations have to be resolved already.
      *
      * @param modules
      *            all loaded modules
-     * @param module
-     *            current module
-     * @param context
-     *            SchemaContext containing already resolved modules
+     * @param resolveWithContext
+     *            boolean value which says whether
+     *            {@link GroupingUtils#collectUsesDataFromContext(UsesNodeBuilder)
+     *            collectUsesDataFromContext} should be used for processing of
+     *            individual uses node.
      */
-    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, final boolean resolveWithContext) {
+        for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
+            for (Map.Entry<Date, ModuleBuilder> inner : entry.getValue().entrySet()) {
+                ModuleBuilder module = inner.getValue();
+                boolean dataCollected = module.isAllUsesDataCollected();
+
+                List<UsesNodeBuilder> usesNodes;
+                while (!dataCollected) {
+                    usesNodes = new ArrayList<>(module.getAllUsesNodes());
+                    for (UsesNodeBuilder usesNode : usesNodes) {
+                        if (!usesNode.isDataCollected()) {
+                            if (resolveWithContext && usesNode.getGroupingBuilder() == null) {
+                                GroupingUtils.collectUsesDataFromContext(usesNode);
+                            } else {
+                                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.
+     * Update uses parent and perform refinement.
      *
-     * @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();
-        }
-        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
+     * @param modules
+     *            all loaded modules
+     * @param resolveWithContext
+     *            boolean value which says whether
+     *            {@link GroupingUtils#collectUsesDataFromContext(UsesNodeBuilder)
+     *            collectUsesDataFromContext} should be used for processing of
+     *            individual uses node.
      */
-    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();
-        }
-        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);
+    private void resolvedUsesPostProcessing(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
+            final boolean resolveWithContext) {
+        // new loop 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());
+            }
         }
-    }
 
-    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) {
+            GroupingUtils.updateUsesParent(usesNode);
+            GroupingUtils.performRefine(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 (!resolveWithContext) {
+            for (UsesNodeBuilder usesNode : allModulesUses) {
+                if (usesNode.isCopy()) {
+                    usesNode.getParent().getUsesNodes().remove(usesNode);
                 }
-                if (newChild instanceof GroupingMember) {
-                    ((GroupingMember) newChild).setAddedByUses(true);
-                }
-
-                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);
-        }
-        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);
-        }
-        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);
-        }
-        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 = findModuleFromBuilders(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.", e);
             }
         }
     }
 
     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 = findModuleFromBuilders(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.", e);
             }
+
         }
     }
 
@@ -1001,7 +1003,7 @@ public final class YangParserImpl implements YangModelParser {
                 prefix = module.getPrefix();
             }
 
-            ModuleBuilder dependentModuleBuilder = findDependentModuleBuilder(modules, module, prefix, line);
+            ModuleBuilder dependentModuleBuilder = findModuleFromBuilders(modules, module, prefix, line);
             processDeviation(dev, dependentModuleBuilder, path, module);
         }
     }
@@ -1049,16 +1051,15 @@ public final class YangParserImpl implements YangModelParser {
             }
             String name = null;
 
-            ModuleBuilder dependentModuleBuilder = findDependentModuleBuilder(modules, module, prefix, line);
+            ModuleBuilder dependentModuleBuilder = findModuleFromBuilders(modules, module, prefix, line);
             if (dependentModuleBuilder == null) {
                 Module dependentModule = findModuleFromContext(context, module, prefix, line);
                 Object currentParent = dependentModule;
 
-                for (int i = 0; i < path.size(); i++) {
+                for (QName q : path) {
                     if (currentParent == null) {
-                        throw new YangParseException(module.getName(), line, "Failed to find deviation target.");
+                        throw new YangParseException(module.getName(), line, FAIL_DEVIATION_TARGET);
                     }
-                    QName q = path.get(i);
                     name = q.getLocalName();
                     if (currentParent instanceof DataNodeContainer) {
                         currentParent = ((DataNodeContainer) currentParent).getDataChildByName(name);
@@ -1066,7 +1067,7 @@ public final class YangParserImpl implements YangModelParser {
                 }
 
                 if (currentParent == null) {
-                    throw new YangParseException(module.getName(), line, "Failed to find deviation target.");
+                    throw new YangParseException(module.getName(), line, FAIL_DEVIATION_TARGET);
                 }
                 if (currentParent instanceof SchemaNode) {
                     dev.setTargetPath(((SchemaNode) currentParent).getPath());
@@ -1095,19 +1096,18 @@ public final class YangParserImpl implements YangModelParser {
         final int line = dev.getLine();
         Builder currentParent = dependentModuleBuilder;
 
-        for (int i = 0; i < path.size(); i++) {
+        for (QName q : path) {
             if (currentParent == null) {
-                throw new YangParseException(module.getName(), line, "Failed to find deviation target.");
+                throw new YangParseException(module.getName(), line, FAIL_DEVIATION_TARGET);
             }
-            QName q = path.get(i);
             String name = q.getLocalName();
             if (currentParent instanceof DataNodeContainerBuilder) {
                 currentParent = ((DataNodeContainerBuilder) currentParent).getDataChildByName(name);
             }
         }
 
-        if (currentParent == null || !(currentParent instanceof SchemaNodeBuilder)) {
-            throw new YangParseException(module.getName(), line, "Failed to find deviation target.");
+        if (!(currentParent instanceof SchemaNodeBuilder)) {
+            throw new YangParseException(module.getName(), line, FAIL_DEVIATION_TARGET);
         }
         dev.setTargetPath(((SchemaNodeBuilder) currentParent).getPath());
     }