Refactored uses statement handling in parser.
[yangtools.git] / yang / yang-parser-impl / src / main / java / org / opendaylight / yangtools / yang / parser / impl / YangParserImpl.java
index 6be46df39742b2aa35ff77b065c47dcdd2d9a1f3..f6d4d67cbbe1c3f2b2a53062546106aa6204f8c4 100644 (file)
@@ -15,10 +15,13 @@ 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.Collection;
 import java.util.Collections;
 import java.util.Date;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.LinkedHashMap;
 import java.util.LinkedHashSet;
 import java.util.List;
@@ -39,17 +42,19 @@ 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.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.parser.api.YangModelParser;
-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.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;
@@ -58,6 +63,7 @@ import org.opendaylight.yangtools.yang.parser.builder.impl.IdentityrefTypeBuilde
 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.GroupingSort;
 import org.opendaylight.yangtools.yang.parser.util.GroupingUtils;
 import org.opendaylight.yangtools.yang.parser.util.ModuleDependencySort;
 import org.opendaylight.yangtools.yang.parser.util.ParserUtils;
@@ -66,6 +72,7 @@ import org.opendaylight.yangtools.yang.validator.YangModelBasicValidator;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.base.Preconditions;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
@@ -75,6 +82,58 @@ public final class YangParserImpl implements YangModelParser {
 
     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());
@@ -94,7 +153,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);
 
@@ -106,7 +164,7 @@ public final class YangParserImpl implements YangModelParser {
                 }
             }
 
-            return new LinkedHashSet<Module>(buildWithContext(modules, context).values());
+            return new LinkedHashSet<>(buildWithContext(modules, context).values());
         }
         return Collections.emptySet();
     }
@@ -122,7 +180,7 @@ public final class YangParserImpl implements YangModelParser {
             Map<ModuleBuilder, InputStream> builderToStreamMap = Maps.newHashMap();
             final Map<String, TreeMap<Date, ModuleBuilder>> modules = resolveModuleBuildersWithContext(
                     yangModelStreams, builderToStreamMap, context);
-            return new LinkedHashSet<Module>(buildWithContext(modules, context).values());
+            return new LinkedHashSet<>(buildWithContext(modules, context).values());
         }
         return Collections.emptySet();
     }
@@ -185,26 +243,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<>(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;
     }
 
@@ -216,20 +275,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);
-
-        // LinkedHashMap must be used to preserve order
-        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()];
+        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;
             }
@@ -238,20 +309,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 = new TreeMap<>();
             }
             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;
     }
@@ -264,9 +357,10 @@ public final class YangParserImpl implements YangModelParser {
             final CommonTokenStream tokens = new CommonTokenStream(lexer);
             final YangParser parser = new YangParser(tokens);
             parser.removeErrorListeners();
-            parser.addErrorListener(new YangErrorListener());
-
+            YangErrorListener errorListener = new YangErrorListener();
+            parser.addErrorListener(errorListener);
             result = parser.yang();
+            errorListener.validate();
         } catch (IOException e) {
             LOG.warn("Exception while reading yang file: " + yangStream, e);
         }
@@ -275,20 +369,20 @@ public final class YangParserImpl implements YangModelParser {
 
     private Map<ModuleBuilder, Module> build(final Map<String, TreeMap<Date, ModuleBuilder>> modules) {
         // fix unresolved nodes
-        findUsesTargets(modules, null);
         resolveDirtyNodes(modules);
+        resolveAugmentsTargetPath(modules, null);
+        resolveUsesTargetGrouping(modules, null);
+        resolveUsesForGroupings(modules, null);
+        resolveUsesForNodes(modules, null);
         resolveAugments(modules);
-        resolveUses(modules, false);
         resolveDeviations(modules);
 
         // build
-        final Map<ModuleBuilder, Module> result = new LinkedHashMap<ModuleBuilder, Module>();
+        final Map<ModuleBuilder, Module> result = new LinkedHashMap<>();
         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);
             }
         }
@@ -298,20 +392,20 @@ public final class YangParserImpl implements YangModelParser {
     private Map<ModuleBuilder, Module> buildWithContext(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
             final SchemaContext context) {
         // fix unresolved nodes
-        findUsesTargets(modules, context);
         resolvedDirtyNodesWithContext(modules, context);
+        resolveAugmentsTargetPath(modules, context);
+        resolveUsesTargetGrouping(modules, context);
+        resolveUsesForGroupings(modules, context);
+        resolveUsesForNodes(modules, context);
         resolveAugmentsWithContext(modules, context);
-        resolveUses(modules, true);
         resolveDeviationsWithContext(modules, context);
 
         // build
-        final Map<ModuleBuilder, Module> result = new LinkedHashMap<ModuleBuilder, Module>();
+        final Map<ModuleBuilder, Module> result = new LinkedHashMap<>();
         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);
             }
         }
@@ -322,9 +416,9 @@ public final class YangParserImpl implements YangModelParser {
         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);
+                resolveIdentities(modules, module);
+                resolveDirtyNodes(modules, module);
             }
         }
     }
@@ -334,9 +428,9 @@ public final class YangParserImpl implements YangModelParser {
         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);
+                resolveIdentitiesWithContext(modules, module, context);
+                resolveDirtyNodesWithContext(modules, module, context);
             }
         }
     }
@@ -344,7 +438,7 @@ public final class YangParserImpl implements YangModelParser {
     /**
      * Search for dirty nodes (node which contains UnknownType) and resolve
      * unknown types.
-     * 
+     *
      * @param modules
      *            all available modules
      * @param module
@@ -360,7 +454,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(null));
                 } else {
                     resolveType(nodeToResolve, modules, module);
                 }
@@ -379,7 +479,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(null));
                 } else {
                     resolveTypeWithContext(nodeToResolve, modules, module, context);
                 }
@@ -387,10 +490,105 @@ public final class YangParserImpl implements YangModelParser {
         }
     }
 
+    /**
+     * Correct augment target path.
+     *
+     * @param modules
+     *            all loaded modules
+     */
+    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()) {
+                allAugments.addAll(inner.getValue().getAllAugments());
+            }
+        }
+
+        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<>();
+
+        Builder parent = augment.getParent();
+        if (parent instanceof UsesNodeBuilder) {
+            DataNodeContainerBuilder usesParent = ((UsesNodeBuilder) parent).getParent();
+            newPath.addAll(usesParent.getPath().getPath());
+
+            URI ns;
+            Date revision;
+            String prefix;
+            QName baseQName = usesParent.getQName();
+            if (baseQName == null) {
+                ModuleBuilder m = ParserUtils.getParentModule(usesParent);
+                ns = m.getNamespace();
+                revision = m.getRevision();
+                prefix = m.getPrefix();
+            } else {
+                ns = baseQName.getNamespace();
+                revision = baseQName.getRevision();
+                prefix = baseQName.getPrefix();
+            }
+
+            for (QName qn : oldSchemaPath.getPath()) {
+                newPath.add(new QName(ns, revision, prefix, qn.getLocalName()));
+            }
+        } else {
+
+            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();
+                    }
+                }
+                newPath.add(new QName(ns, rev, pref, qn.getLocalName()));
+            }
+        }
+        augment.setTargetNodeSchemaPath(new SchemaPath(newPath, augment.getTargetPath().isAbsolute()));
+
+        for (DataSchemaNodeBuilder childNode : augment.getChildNodeBuilders()) {
+            correctPathForAugmentNodes(childNode, augment.getTargetNodeSchemaPath());
+        }
+    }
+
+    private void correctPathForAugmentNodes(DataSchemaNodeBuilder node, SchemaPath parentPath) {
+        node.setPath(ParserUtils.createSchemaPath(parentPath, node.getQName()));
+        if (node instanceof DataNodeContainerBuilder) {
+            for (DataSchemaNodeBuilder child : ((DataNodeContainerBuilder) node).getChildNodeBuilders()) {
+                correctPathForAugmentNodes(child, node.getPath());
+            }
+        }
+    }
+
     /**
      * Go through all augment definitions and perform augmentation. It is
      * expected that modules are already sorted by their dependencies.
-     * 
+     *
      * @param modules
      *            all loaded modules
      */
@@ -403,6 +601,8 @@ public final class YangParserImpl implements YangModelParser {
             }
         }
 
+        checkAugmentMandatoryNodes(allAugments);
+
         for (int i = 0; i < allAugments.size(); i++) {
             // pick one augment
             final AugmentationSchemaBuilder augment = allAugments.get(i);
@@ -430,27 +630,55 @@ public final class YangParserImpl implements YangModelParser {
         }
     }
 
+    /**
+     * 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();
+
+            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 "
+                                    + childNode.getQName().getLocalName());
+                }
+            }
+        }
+    }
+
     /**
      * Search for augment target and perform augmentation.
-     * 
+     *
      * @param modules
      *            all loaded modules
-     * @param augmentBuilder
+     * @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 augmentBuilder) {
-        if (augmentBuilder.isResolved()) {
+            final AugmentationSchemaBuilder augment) {
+        if (augment.isResolved()) {
             return true;
         }
 
-        int line = augmentBuilder.getLine();
-        ModuleBuilder module = getParentModule(augmentBuilder);
-        List<QName> path = augmentBuilder.getTargetPath().getPath();
-        Builder augmentParent = augmentBuilder.getParent();
+        int line = augment.getLine();
+        ModuleBuilder module = getParentModule(augment);
+        List<QName> path = augment.getTargetPath().getPath();
+        Builder augmentParent = augment.getParent();
 
-        Builder firstNodeParent = null;
+        Builder firstNodeParent;
         if (augmentParent instanceof ModuleBuilder) {
             // if augment is defined under module, parent of first node is
             // target module
@@ -459,23 +687,23 @@ public final class YangParserImpl implements YangModelParser {
             if (prefix == null) {
                 prefix = module.getPrefix();
             }
-            firstNodeParent = findDependentModuleBuilder(modules, module, prefix, line);
+            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(augmentBuilder.getModuleName(), line,
+            throw new YangParseException(augment.getModuleName(), line,
                     "Failed to parse augment: Unresolved parent of augment: " + augmentParent);
         }
 
-        return processAugmentation(augmentBuilder, firstNodeParent, path);
+        return processAugmentation(augment, firstNodeParent, path);
     }
 
     /**
      * Go through all augment definitions and resolve them. This method works in
      * same way as {@link #resolveAugments(Map)} except that if target node is
      * not found in loaded modules, it search for target node in given context.
-     * 
+     *
      * @param modules
      *            all loaded modules
      * @param context
@@ -515,7 +743,7 @@ public final class YangParserImpl implements YangModelParser {
 
     /**
      * Search for augment target and perform augmentation.
-     * 
+     *
      * @param modules
      *            all loaded modules
      * @param augment
@@ -531,18 +759,17 @@ public final class YangParserImpl implements YangModelParser {
         }
         int line = augment.getLine();
         ModuleBuilder module = getParentModule(augment);
-        List<QName> path = augment.getTargetPath().getPath();
+        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 = null;
-
+        Builder currentParent;
         if (augmentParent instanceof ModuleBuilder) {
             // if augment is defined under module, first parent is target module
-            currentParent = findDependentModuleBuilder(modules, module, prefix, line);
+            currentParent = findModuleFromBuilders(modules, module, prefix, line);
         } else if (augmentParent instanceof UsesNodeBuilder) {
             currentParent = augmentParent.getParent();
         } else {
@@ -561,7 +788,7 @@ public final class YangParserImpl implements YangModelParser {
     /**
      * Go through identity statements defined in current module and resolve
      * their 'base' statement if present.
-     * 
+     *
      * @param modules
      *            all modules
      * @param module
@@ -571,25 +798,13 @@ 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);
                 }
             }
         }
@@ -599,7 +814,7 @@ public final class YangParserImpl implements YangModelParser {
      * Go through identity statements defined in current module and resolve
      * their 'base' statement. Method tries to find base identity in given
      * modules. If base identity is not found, method will search it in context.
-     * 
+     *
      * @param modules
      *            all loaded modules
      * @param module
@@ -612,37 +827,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) {
+                    IdentitySchemaNode baseId = findBaseIdentityFromContext(modules, module, baseIdentityName, line,
+                            context);
+                    identity.setBaseIdentity(baseId);
                 } 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);
-                        }
-                    }
-                } else {
-                    final Set<IdentitySchemaNodeBuilder> dependentModuleIdentities = dependentModuleBuilder
-                            .getIdentities();
-                    for (IdentitySchemaNodeBuilder idBuilder : dependentModuleIdentities) {
-                        if (idBuilder.getQName().getLocalName().equals(baseIdentityLocalName)) {
-                            identity.setBaseIdentity(idBuilder);
-                        }
-                    }
+                    identity.setBaseIdentity(baseIdentity);
                 }
             }
         }
@@ -650,14 +843,15 @@ public final class YangParserImpl implements YangModelParser {
 
     /**
      * Find and add reference of uses target grouping.
-     * 
+     *
      * @param modules
      *            all loaded modules
      * @param context
      *            SchemaContext containing already resolved modules or null if
      *            context is not available
      */
-    private void findUsesTargets(final Map<String, TreeMap<Date, ModuleBuilder>> modules, final SchemaContext context) {
+    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()) {
@@ -683,77 +877,89 @@ public final class YangParserImpl implements YangModelParser {
         }
     }
 
-    /**
-     * Copy data from uses target. Augmentations have to be resolved already.
-     * 
-     * @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 resolveUses(final Map<String, TreeMap<Date, ModuleBuilder>> modules, final boolean resolveWithContext) {
+    private void resolveUsesForGroupings(final Map<String, TreeMap<Date, ModuleBuilder>> modules, final SchemaContext context) {
+        final Set<GroupingBuilder> allGroupings = new HashSet<>();
         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 (resolveWithContext && usesNode.getGroupingBuilder() == null) {
-                                GroupingUtils.collectUsesDataFromContext(usesNode);
-                            } else {
-                                GroupingUtils.collectUsesData(usesNode);
-                            }
-                        }
-                    }
-                    dataCollected = module.isAllUsesDataCollected();
-                }
+                allGroupings.addAll(module.getAllGroupings());
+            }
+        }
+        final List<GroupingBuilder> sorted = GroupingSort.sort(allGroupings);
+        for (GroupingBuilder gb : sorted) {
+            List<UsesNodeBuilder> usesNodes = new ArrayList<>(GroupingSort.getAllUsesNodes(gb));
+            Collections.sort(usesNodes, new GroupingUtils.UsesComparator());
+            for (UsesNodeBuilder usesNode : usesNodes) {
+                resolveUses(usesNode, modules, context);
             }
         }
-        resolvedUsesPostProcessing(modules, resolveWithContext);
     }
 
-    /**
-     * Update uses parent and perform refinement.
-     * 
-     * @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 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<>();
+    private void resolveUsesForNodes(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()) {
-                allModulesUses.addAll(inner.getValue().getAllUsesNodes());
+                ModuleBuilder module = inner.getValue();
+                List<UsesNodeBuilder> usesNodes = module.getAllUsesNodes();
+                Collections.sort(usesNodes, new GroupingUtils.UsesComparator());
+                for (UsesNodeBuilder usesNode : usesNodes) {
+                    resolveUses(usesNode, modules, context);
+                }
             }
         }
+    }
 
-        for (UsesNodeBuilder usesNode : allModulesUses) {
-            GroupingUtils.updateUsesParent(usesNode);
-            GroupingUtils.performRefine(usesNode);
-        }
-        for (UsesNodeBuilder usesNode : allModulesUses) {
-            GroupingUtils.fixUsesNodesPath(usesNode);
-        }
+    private void resolveUses(UsesNodeBuilder usesNode,
+            final Map<String, TreeMap<Date, ModuleBuilder>> modules, final SchemaContext context) {
+        if (!usesNode.isResolved()) {
+            final int line = usesNode.getLine();
+            DataNodeContainerBuilder parent = usesNode.getParent();
+            ModuleBuilder module = ParserUtils.getParentModule(parent);
+            GroupingBuilder target = GroupingUtils.getTargetGroupingFromModules(usesNode, modules, module);
+            if (target == null) {
+                URI ns = null;
+                Date rev = null;
+                String prefix = null;
+                if (parent instanceof AugmentationSchemaBuilder || parent instanceof ModuleBuilder) {
+                    ns = module.getNamespace();
+                    rev = module.getRevision();
+                    prefix = module.getPrefix();
+                } else {
+                    ns = ((DataSchemaNodeBuilder) parent).getQName().getNamespace();
+                    rev = ((DataSchemaNodeBuilder) parent).getQName().getRevision();
+                    prefix = ((DataSchemaNodeBuilder) parent).getQName().getPrefix();
+                }
 
-        if (!resolveWithContext) {
-            for (UsesNodeBuilder usesNode : allModulesUses) {
-                if (usesNode.isCopy()) {
-                    usesNode.getParent().getUsesNodes().remove(usesNode);
+                Set<DataSchemaNodeBuilder> childNodes = GroupingUtils.getTargetGroupingDefinitionNodesWithNewNamespace(
+                        usesNode, ns, rev, prefix, module.getName(), line);
+                parent.getChildNodeBuilders().addAll(childNodes);
+                Set<TypeDefinitionBuilder> typedefs = GroupingUtils
+                        .getTargetGroupingDefinitionTypedefsWithNewNamespace(usesNode, ns, rev, prefix,
+                                module.getName(), line);
+                parent.getTypeDefinitionBuilders().addAll(typedefs);
+                Set<GroupingBuilder> groupings = GroupingUtils.getTargetGroupingDefinitionGroupingsWithNewNamespace(
+                        usesNode, ns, rev, prefix, module.getName(), line);
+                parent.getGroupingBuilders().addAll(groupings);
+                List<UnknownSchemaNodeBuilder> unknownNodes = GroupingUtils
+                        .getTargetGroupingDefinitionUnknownNodesWithNewNamespace(usesNode, ns, rev, prefix,
+                                module.getName(), line);
+                parent.getUnknownNodeBuilders().addAll(unknownNodes);
+                usesNode.setResolved(true);
+
+                for (AugmentationSchemaBuilder augment : usesNode.getAugmentations()) {
+                    processAugmentationOnContext(augment, augment.getTargetPath().getPath(), module, prefix, context);
+                }
+                GroupingUtils.performRefine(usesNode);
+            } else {
+                parent.getChildNodeBuilders().addAll(target.instantiateChildNodes(parent));
+                parent.getTypeDefinitionBuilders().addAll(target.instantiateTypedefs(parent));
+                parent.getGroupingBuilders().addAll(target.instantiateGroupings(parent));
+                parent.getUnknownNodeBuilders().addAll(target.instantiateUnknownNodes(parent));
+                usesNode.setResolved(true);
+
+                for (AugmentationSchemaBuilder augment : usesNode.getAugmentations()) {
+                    processAugmentation(augment, parent, augment.getTargetPath().getPath());
                 }
+                GroupingUtils.performRefine(usesNode);
             }
         }
     }
@@ -762,7 +968,7 @@ public final class YangParserImpl implements YangModelParser {
         for (UnknownSchemaNodeBuilder usnb : module.getAllUnknownNodes()) {
             QName nodeType = usnb.getNodeType();
             try {
-                ModuleBuilder dependentModule = findDependentModuleBuilder(modules, module, nodeType.getPrefix(),
+                ModuleBuilder dependentModule = findModuleFromBuilders(modules, module, nodeType.getPrefix(),
                         usnb.getLine());
                 for (ExtensionBuilder extension : dependentModule.getExtensions()) {
                     if (extension.getQName().getLocalName().equals(nodeType.getLocalName())) {
@@ -783,8 +989,8 @@ public final class YangParserImpl implements YangModelParser {
         for (UnknownSchemaNodeBuilder usnb : module.getAllUnknownNodes()) {
             QName nodeType = usnb.getNodeType();
             try {
-                ModuleBuilder dependentModuleBuilder = findDependentModuleBuilder(modules, module,
-                        nodeType.getPrefix(), usnb.getLine());
+                ModuleBuilder dependentModuleBuilder = findModuleFromBuilders(modules, module, nodeType.getPrefix(),
+                        usnb.getLine());
 
                 if (dependentModuleBuilder == null) {
                     Module dependentModule = findModuleFromContext(context, module, nodeType.getPrefix(),
@@ -816,7 +1022,7 @@ public final class YangParserImpl implements YangModelParser {
 
     /**
      * Traverse through modules and resolve their deviation statements.
-     * 
+     *
      * @param modules
      *            all loaded modules
      */
@@ -831,7 +1037,7 @@ public final class YangParserImpl implements YangModelParser {
 
     /**
      * Traverse through module and resolve its deviation statements.
-     * 
+     *
      * @param modules
      *            all loaded modules
      * @param module
@@ -848,7 +1054,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);
         }
     }
@@ -856,7 +1062,7 @@ public final class YangParserImpl implements YangModelParser {
     /**
      * Traverse through modules and resolve their deviation statements with
      * given context.
-     * 
+     *
      * @param modules
      *            all loaded modules
      * @param context
@@ -875,7 +1081,7 @@ public final class YangParserImpl implements YangModelParser {
     /**
      * Traverse through module and resolve its deviation statements with given
      * context.
-     * 
+     *
      * @param modules
      *            all loaded modules
      * @param module
@@ -894,19 +1100,16 @@ public final class YangParserImpl implements YangModelParser {
             if (prefix == null) {
                 prefix = module.getPrefix();
             }
-            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;
+                Object currentParent = findModuleFromContext(context, module, prefix, line);
 
-                for (int i = 0; i < path.size(); i++) {
+                for (QName q : path) {
                     if (currentParent == null) {
                         throw new YangParseException(module.getName(), line, FAIL_DEVIATION_TARGET);
                     }
-                    QName q = path.get(i);
-                    name = q.getLocalName();
+                    String name = q.getLocalName();
                     if (currentParent instanceof DataNodeContainer) {
                         currentParent = ((DataNodeContainer) currentParent).getDataChildByName(name);
                     }
@@ -927,7 +1130,7 @@ public final class YangParserImpl implements YangModelParser {
 
     /**
      * Correct deviation target path in deviation builder.
-     * 
+     *
      * @param dev
      *            deviation
      * @param dependentModuleBuilder
@@ -942,11 +1145,10 @@ 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, FAIL_DEVIATION_TARGET);
             }
-            QName q = path.get(i);
             String name = q.getLocalName();
             if (currentParent instanceof DataNodeContainerBuilder) {
                 currentParent = ((DataNodeContainerBuilder) currentParent).getDataChildByName(name);