591d5c4f5c7de9518c9cbe00136197c93b45d8fd
[yangtools.git] / yang / yang-parser-impl / src / main / java / org / opendaylight / yangtools / yang / parser / impl / YangParserImpl.java
1 /*
2  * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.yangtools.yang.parser.impl;
9
10 import static org.opendaylight.yangtools.yang.parser.util.ParserUtils.*;
11 import static org.opendaylight.yangtools.yang.parser.util.TypeUtils.resolveType;
12 import static org.opendaylight.yangtools.yang.parser.util.TypeUtils.resolveTypeUnion;
13 import static org.opendaylight.yangtools.yang.parser.util.TypeUtils.resolveTypeUnionWithContext;
14 import static org.opendaylight.yangtools.yang.parser.util.TypeUtils.resolveTypeWithContext;
15
16 import java.io.File;
17 import java.io.FileInputStream;
18 import java.io.FileNotFoundException;
19 import java.io.IOException;
20 import java.io.InputStream;
21 import java.net.URI;
22 import java.util.*;
23
24 import org.antlr.v4.runtime.ANTLRInputStream;
25 import org.antlr.v4.runtime.CommonTokenStream;
26 import org.antlr.v4.runtime.tree.ParseTree;
27 import org.antlr.v4.runtime.tree.ParseTreeWalker;
28 import org.opendaylight.yangtools.antlrv4.code.gen.YangLexer;
29 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser;
30 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.YangContext;
31 import org.opendaylight.yangtools.yang.common.QName;
32 import org.opendaylight.yangtools.yang.model.api.*;
33 import org.opendaylight.yangtools.yang.model.parser.api.YangModelParser;
34 import org.opendaylight.yangtools.yang.parser.builder.api.AugmentationSchemaBuilder;
35 import org.opendaylight.yangtools.yang.parser.builder.api.Builder;
36 import org.opendaylight.yangtools.yang.parser.builder.api.DataNodeContainerBuilder;
37 import org.opendaylight.yangtools.yang.parser.builder.api.DataSchemaNodeBuilder;
38 import org.opendaylight.yangtools.yang.parser.builder.api.GroupingBuilder;
39 import org.opendaylight.yangtools.yang.parser.builder.api.SchemaNodeBuilder;
40 import org.opendaylight.yangtools.yang.parser.builder.api.TypeAwareBuilder;
41 import org.opendaylight.yangtools.yang.parser.builder.api.TypeDefinitionBuilder;
42 import org.opendaylight.yangtools.yang.parser.builder.api.UsesNodeBuilder;
43 import org.opendaylight.yangtools.yang.parser.builder.impl.*;
44 import org.opendaylight.yangtools.yang.parser.util.Comparators;
45 import org.opendaylight.yangtools.yang.parser.util.GroupingSort;
46 import org.opendaylight.yangtools.yang.parser.util.GroupingUtils;
47 import org.opendaylight.yangtools.yang.parser.util.ModuleDependencySort;
48 import org.opendaylight.yangtools.yang.parser.util.NamedFileInputStream;
49 import org.opendaylight.yangtools.yang.parser.util.ParserUtils;
50 import org.opendaylight.yangtools.yang.parser.util.YangParseException;
51 import org.opendaylight.yangtools.yang.validator.YangModelBasicValidator;
52 import org.slf4j.Logger;
53 import org.slf4j.LoggerFactory;
54
55 import com.google.common.base.Preconditions;
56
57
58 public final class YangParserImpl implements YangModelParser {
59     private static final Logger LOG = LoggerFactory.getLogger(YangParserImpl.class);
60
61     private static final String FAIL_DEVIATION_TARGET = "Failed to find deviation target.";
62
63     @Override
64     public Set<Module> parseYangModels(final File yangFile, final File directory) {
65         Preconditions.checkState(yangFile.exists(), yangFile + " does not exists");
66         Preconditions.checkState(directory.exists(), directory + " does not exists");
67         Preconditions.checkState(directory.isDirectory(), directory + " is not a directory");
68
69         final String yangFileName = yangFile.getName();
70         final String[] fileList = directory.list();
71         Preconditions.checkNotNull(fileList, directory + " not found");
72
73         FileInputStream yangFileStream = null;
74         LinkedHashMap<InputStream, File> streamToFileMap = new LinkedHashMap<>();
75         try {
76             yangFileStream = new FileInputStream(yangFile);
77             streamToFileMap.put(yangFileStream, yangFile);
78         } catch (FileNotFoundException e) {
79             LOG.warn("Exception while reading yang file: " + yangFile.getName(), e);
80         }
81
82         for (String fileName : fileList) {
83             if (fileName.equals(yangFileName)) {
84                 continue;
85             }
86             File dependency = new File(directory, fileName);
87             try {
88                 if (dependency.isFile()) {
89                     streamToFileMap.put(new FileInputStream(dependency), dependency);
90                 }
91             } catch (FileNotFoundException e) {
92                 LOG.warn("Exception while reading yang file: " + fileName, e);
93             }
94         }
95
96         Map<InputStream, ModuleBuilder> parsedBuilders = parseBuilders(new ArrayList<>(streamToFileMap.keySet()),
97                 new HashMap<ModuleBuilder, InputStream>());
98         ModuleBuilder main = parsedBuilders.get(yangFileStream);
99
100         List<ModuleBuilder> moduleBuilders = new ArrayList<>();
101         moduleBuilders.add(main);
102         filterImports(main, new ArrayList<>(parsedBuilders.values()), moduleBuilders);
103         Collection<ModuleBuilder> result = resolveSubmodules(moduleBuilders);
104
105         // module builders sorted by dependencies
106         ModuleBuilder[] builders = new ModuleBuilder[result.size()];
107         result.toArray(builders);
108         List<ModuleBuilder> sortedBuilders = ModuleDependencySort.sort(builders);
109         LinkedHashMap<String, TreeMap<Date, ModuleBuilder>> modules = orderModules(sortedBuilders);
110         Collection<Module> unsorted = build(modules).values();
111         return new LinkedHashSet<>(ModuleDependencySort.sort(unsorted.toArray(new Module[unsorted.size()])));
112     }
113
114     @Override
115     public Set<Module> parseYangModels(final List<File> yangFiles) {
116         Collection<Module> unsorted = parseYangModelsMapped(yangFiles).values();
117         return new LinkedHashSet<>(ModuleDependencySort.sort(unsorted.toArray(new Module[unsorted.size()])));
118     }
119
120     @Override
121     public Set<Module> parseYangModels(final List<File> yangFiles, final SchemaContext context) {
122         if (yangFiles == null) {
123             return Collections.emptySet();
124         }
125
126         final Map<InputStream, File> inputStreams = new HashMap<>();
127         for (final File yangFile : yangFiles) {
128             try {
129                 inputStreams.put(new FileInputStream(yangFile), yangFile);
130             } catch (FileNotFoundException e) {
131                 LOG.warn("Exception while reading yang file: " + yangFile.getName(), e);
132             }
133         }
134
135         List<InputStream> yangModelStreams = new ArrayList<>(inputStreams.keySet());
136         Map<ModuleBuilder, InputStream> builderToStreamMap = new HashMap<>();
137         Map<String, TreeMap<Date, ModuleBuilder>> modules = resolveModuleBuilders(yangModelStreams,
138                 builderToStreamMap, null);
139
140         for (InputStream is : inputStreams.keySet()) {
141             try {
142                 is.close();
143             } catch (IOException e) {
144                 LOG.debug("Failed to close stream.");
145             }
146         }
147
148         final Collection<Module> unsorted = buildWithContext(modules, context).values();
149         if (context != null) {
150             for (Module m : context.getModules()) {
151                 if (!unsorted.contains(m)) {
152                     unsorted.add(m);
153                 }
154             }
155         }
156         return new LinkedHashSet<>(ModuleDependencySort.sort(unsorted.toArray(new Module[unsorted.size()])));
157     }
158
159     @Override
160     public Set<Module> parseYangModelsFromStreams(final List<InputStream> yangModelStreams) {
161         Collection<Module> unsorted = parseYangModelsFromStreamsMapped(yangModelStreams).values();
162         return new LinkedHashSet<>(ModuleDependencySort.sort(unsorted.toArray(new Module[unsorted.size()])));
163     }
164
165     @Override
166     public Set<Module> parseYangModelsFromStreams(final List<InputStream> yangModelStreams, SchemaContext context) {
167         if (yangModelStreams == null) {
168             return Collections.emptySet();
169         }
170
171         final Map<ModuleBuilder, InputStream> builderToStreamMap = new HashMap<>();
172         final Map<String, TreeMap<Date, ModuleBuilder>> modules = resolveModuleBuilders(yangModelStreams,
173                 builderToStreamMap, context);
174         final Set<Module> unsorted = new LinkedHashSet<>(buildWithContext(modules, context).values());
175         if (context != null) {
176             for (Module m : context.getModules()) {
177                 if (!unsorted.contains(m)) {
178                     unsorted.add(m);
179                 }
180             }
181         }
182         return new LinkedHashSet<>(ModuleDependencySort.sort(unsorted.toArray(new Module[unsorted.size()])));
183     }
184
185     @Override
186     public Map<File, Module> parseYangModelsMapped(List<File> yangFiles) {
187         if (yangFiles == null) {
188             return Collections.emptyMap();
189         }
190
191         final Map<InputStream, File> inputStreams = new HashMap<>();
192         for (final File yangFile : yangFiles) {
193             try {
194
195                 inputStreams.put(new FileInputStream(yangFile), yangFile);
196             } catch (FileNotFoundException e) {
197                 LOG.warn("Exception while reading yang file: " + yangFile.getName(), e);
198             }
199         }
200
201         List<InputStream> yangModelStreams = new ArrayList<>(inputStreams.keySet());
202         Map<ModuleBuilder, InputStream> builderToStreamMap = new HashMap<>();
203         Map<String, TreeMap<Date, ModuleBuilder>> modules = resolveModuleBuilders(yangModelStreams, builderToStreamMap,
204                 null);
205
206         for (InputStream is : inputStreams.keySet()) {
207             try {
208                 is.close();
209             } catch (IOException e) {
210                 LOG.debug("Failed to close stream.");
211             }
212         }
213
214         Map<File, Module> result = new LinkedHashMap<>();
215         Map<ModuleBuilder, Module> builderToModuleMap = build(modules);
216         Set<ModuleBuilder> keyset = builderToModuleMap.keySet();
217         List<ModuleBuilder> sorted = ModuleDependencySort.sort(keyset.toArray(new ModuleBuilder[keyset.size()]));
218         for (ModuleBuilder key : sorted) {
219             result.put(inputStreams.get(builderToStreamMap.get(key)), builderToModuleMap.get(key));
220         }
221         return result;
222     }
223
224     @Override
225     public Map<InputStream, Module> parseYangModelsFromStreamsMapped(final List<InputStream> yangModelStreams) {
226         if (yangModelStreams == null) {
227             return Collections.emptyMap();
228         }
229
230         Map<ModuleBuilder, InputStream> builderToStreamMap = new HashMap<>();
231         Map<String, TreeMap<Date, ModuleBuilder>> modules = resolveModuleBuilders(yangModelStreams, builderToStreamMap,
232                 null);
233         Map<InputStream, Module> result = new LinkedHashMap<>();
234         Map<ModuleBuilder, Module> builderToModuleMap = build(modules);
235         Set<ModuleBuilder> keyset = builderToModuleMap.keySet();
236         List<ModuleBuilder> sorted = ModuleDependencySort.sort(keyset.toArray(new ModuleBuilder[keyset.size()]));
237         for (ModuleBuilder key : sorted) {
238             result.put(builderToStreamMap.get(key), builderToModuleMap.get(key));
239         }
240         return result;
241     }
242
243     @Override
244     public SchemaContext resolveSchemaContext(final Set<Module> modules) {
245         return new SchemaContextImpl(modules);
246     }
247
248     private Map<InputStream, ModuleBuilder> parseModuleBuilders(List<InputStream> inputStreams,
249             Map<ModuleBuilder, InputStream> streamToBuilderMap) {
250         Map<InputStream, ModuleBuilder> modules = parseBuilders(inputStreams, streamToBuilderMap);
251         Map<InputStream, ModuleBuilder> result = resolveSubmodules(modules);
252         return result;
253     }
254
255     private Map<InputStream, ModuleBuilder> parseBuilders(List<InputStream> inputStreams,
256             Map<ModuleBuilder, InputStream> streamToBuilderMap) {
257         final ParseTreeWalker walker = new ParseTreeWalker();
258         final Map<InputStream, ParseTree> trees = parseStreams(inputStreams);
259         final Map<InputStream, ModuleBuilder> builders = new LinkedHashMap<>();
260
261         // validate yang
262         new YangModelBasicValidator(walker).validate(new ArrayList<>(trees.values()));
263
264         YangParserListenerImpl yangModelParser;
265         for (Map.Entry<InputStream, ParseTree> entry : trees.entrySet()) {
266             InputStream is = entry.getKey();
267             String path = null;
268             if (is instanceof NamedFileInputStream) {
269                 NamedFileInputStream nis = (NamedFileInputStream)is;
270                 path = nis.getFileDestination();
271             }
272             yangModelParser = new YangParserListenerImpl(path);
273             walker.walk(yangModelParser, entry.getValue());
274             ModuleBuilder moduleBuilder = yangModelParser.getModuleBuilder();
275
276             // We expect the order of trees and streams has to be the same
277             streamToBuilderMap.put(moduleBuilder, entry.getKey());
278
279             builders.put(entry.getKey(), moduleBuilder);
280         }
281
282         return builders;
283     }
284
285     private Map<InputStream, ModuleBuilder> resolveSubmodules(Map<InputStream, ModuleBuilder> builders) {
286         Map<InputStream, ModuleBuilder> modules = new HashMap<>();
287         Set<ModuleBuilder> submodules = new HashSet<>();
288         for (Map.Entry<InputStream, ModuleBuilder> entry : builders.entrySet()) {
289             ModuleBuilder moduleBuilder = entry.getValue();
290             if (moduleBuilder.isSubmodule()) {
291                 submodules.add(moduleBuilder);
292             } else {
293                 modules.put(entry.getKey(), moduleBuilder);
294             }
295         }
296
297         Collection<ModuleBuilder> values = modules.values();
298         for (ModuleBuilder submodule : submodules) {
299             for (ModuleBuilder module : values) {
300                 if (module.getName().equals(submodule.getBelongsTo())) {
301                     addSubmoduleToModule(submodule, module);
302                 }
303             }
304         }
305         return modules;
306     }
307
308     private Collection<ModuleBuilder> resolveSubmodules(Collection<ModuleBuilder> builders) {
309         Collection<ModuleBuilder> modules = new HashSet<>();
310         Set<ModuleBuilder> submodules = new HashSet<>();
311         for (ModuleBuilder moduleBuilder : builders) {
312             if (moduleBuilder.isSubmodule()) {
313                 submodules.add(moduleBuilder);
314             } else {
315                 modules.add(moduleBuilder);
316             }
317         }
318
319         for (ModuleBuilder submodule : submodules) {
320             for (ModuleBuilder module : modules) {
321                 if (module.getName().equals(submodule.getBelongsTo())) {
322                     addSubmoduleToModule(submodule, module);
323                 }
324             }
325         }
326         return modules;
327     }
328
329     private void addSubmoduleToModule(ModuleBuilder submodule, ModuleBuilder module) {
330         submodule.setParent(module);
331         module.getDirtyNodes().addAll(submodule.getDirtyNodes());
332         module.getModuleImports().addAll(submodule.getModuleImports());
333         module.getAugments().addAll(submodule.getAugments());
334         module.getAugmentBuilders().addAll(submodule.getAugmentBuilders());
335         module.getAllAugments().addAll(submodule.getAllAugments());
336         module.getChildNodeBuilders().addAll(submodule.getChildNodeBuilders());
337         module.getChildNodes().addAll(submodule.getChildNodes());
338         module.getGroupings().addAll(submodule.getGroupings());
339         module.getGroupingBuilders().addAll(submodule.getGroupingBuilders());
340         module.getTypeDefinitions().addAll(submodule.getTypeDefinitions());
341         module.getTypeDefinitionBuilders().addAll(submodule.getTypeDefinitionBuilders());
342         module.getUsesNodes().addAll(submodule.getUsesNodes());
343         module.getUsesNodeBuilders().addAll(submodule.getUsesNodeBuilders());
344         module.getAllGroupings().addAll(submodule.getAllGroupings());
345         module.getAllUsesNodes().addAll(submodule.getAllUsesNodes());
346         module.getRpcs().addAll(submodule.getRpcs());
347         module.getAddedRpcs().addAll(submodule.getAddedRpcs());
348         module.getNotifications().addAll(submodule.getNotifications());
349         module.getAddedNotifications().addAll(submodule.getAddedNotifications());
350         module.getIdentities().addAll(submodule.getIdentities());
351         module.getAddedIdentities().addAll(submodule.getAddedIdentities());
352         module.getFeatures().addAll(submodule.getFeatures());
353         module.getAddedFeatures().addAll(submodule.getAddedFeatures());
354         module.getDeviations().addAll(submodule.getDeviations());
355         module.getDeviationBuilders().addAll(submodule.getDeviationBuilders());
356         module.getExtensions().addAll(submodule.getExtensions());
357         module.getAddedExtensions().addAll(submodule.getAddedExtensions());
358         module.getUnknownNodes().addAll(submodule.getUnknownNodes());
359         module.getAllUnknownNodes().addAll(submodule.getAllUnknownNodes());
360     }
361
362     private Map<String, TreeMap<Date, ModuleBuilder>> resolveModuleBuilders(final List<InputStream> yangFileStreams,
363             final Map<ModuleBuilder, InputStream> streamToBuilderMap, final SchemaContext context) {
364         Map<InputStream, ModuleBuilder> parsedBuilders = parseModuleBuilders(yangFileStreams, streamToBuilderMap);
365         ModuleBuilder[] builders = new ModuleBuilder[parsedBuilders.size()];
366         parsedBuilders.values().toArray(builders);
367
368         // module dependency graph sorted
369         List<ModuleBuilder> sorted;
370         if (context == null) {
371             sorted = ModuleDependencySort.sort(builders);
372         } else {
373             sorted = ModuleDependencySort.sortWithContext(context, builders);
374         }
375         return orderModules(sorted);
376     }
377
378     /**
379      * Order modules by name and revision.
380      *
381      * @param modules
382      *            modules to order
383      * @return modules ordered by name and revision
384      */
385     private LinkedHashMap<String, TreeMap<Date, ModuleBuilder>> orderModules(List<ModuleBuilder> modules) {
386         final LinkedHashMap<String, TreeMap<Date, ModuleBuilder>> result = new LinkedHashMap<>();
387         for (final ModuleBuilder builder : modules) {
388             if (builder == null) {
389                 continue;
390             }
391             final String builderName = builder.getName();
392             Date builderRevision = builder.getRevision();
393             if (builderRevision == null) {
394                 builderRevision = new Date(0L);
395             }
396             TreeMap<Date, ModuleBuilder> builderByRevision = result.get(builderName);
397             if (builderByRevision == null) {
398                 builderByRevision = new TreeMap<>();
399             }
400             builderByRevision.put(builderRevision, builder);
401             result.put(builderName, builderByRevision);
402         }
403         return result;
404     }
405
406     private void filterImports(ModuleBuilder main, List<ModuleBuilder> other, List<ModuleBuilder> filtered) {
407         Set<ModuleImport> imports = main.getModuleImports();
408
409         // if this is submodule, add parent to filtered and pick its imports
410         if (main.isSubmodule()) {
411             TreeMap<Date, ModuleBuilder> dependencies = new TreeMap<>();
412             for (ModuleBuilder mb : other) {
413                 if (mb.getName().equals(main.getBelongsTo())) {
414                     dependencies.put(mb.getRevision(), mb);
415                 }
416             }
417             ModuleBuilder parent = dependencies.get(dependencies.firstKey());
418             filtered.add(parent);
419             imports.addAll(parent.getModuleImports());
420         }
421
422         for (ModuleImport mi : imports) {
423             for (ModuleBuilder builder : other) {
424                 if (mi.getModuleName().equals(builder.getModuleName())) {
425                     if (mi.getRevision() == null) {
426                         if (!filtered.contains(builder)) {
427                             filtered.add(builder);
428                             filterImports(builder, other, filtered);
429                         }
430                     } else {
431                         if (mi.getRevision().equals(builder.getRevision())) {
432                             if (!filtered.contains(builder)) {
433                                 filtered.add(builder);
434                                 filterImports(builder, other, filtered);
435                             }
436                         }
437                     }
438                 }
439             }
440         }
441     }
442
443     private Map<InputStream, ParseTree> parseStreams(final List<InputStream> yangStreams) {
444         final Map<InputStream, ParseTree> trees = new HashMap<>();
445         for (InputStream yangStream : yangStreams) {
446             trees.put(yangStream, parseStream(yangStream));
447         }
448         return trees;
449     }
450
451     private ParseTree parseStream(final InputStream yangStream) {
452         ParseTree result = null;
453         try {
454             final ANTLRInputStream input = new ANTLRInputStream(yangStream);
455             final YangLexer lexer = new YangLexer(input);
456             final CommonTokenStream tokens = new CommonTokenStream(lexer);
457             final YangParser parser = new YangParser(tokens);
458             parser.removeErrorListeners();
459             YangErrorListener errorListener = new YangErrorListener();
460             parser.addErrorListener(errorListener);
461             result = parser.yang();
462             errorListener.validate();
463         } catch (IOException e) {
464             LOG.warn("Exception while reading yang file: " + yangStream, e);
465         }
466         return result;
467     }
468
469     public static YangContext parseStreamWithoutErrorListeners(final InputStream yangStream) {
470         YangContext result = null;
471         try {
472             final ANTLRInputStream input = new ANTLRInputStream(yangStream);
473             final YangLexer lexer = new YangLexer(input);
474             final CommonTokenStream tokens = new CommonTokenStream(lexer);
475             final YangParser parser = new YangParser(tokens);
476             parser.removeErrorListeners();
477             result = parser.yang();
478         } catch (IOException e) {
479             LOG.warn("Exception while reading yang file: " + yangStream, e);
480         }
481         return result;
482     }
483
484     /**
485      * Creates builder-to-module map based on given modules. Method first
486      * resolve unresolved type references, instantiate groupings through uses
487      * statements and perform augmentation.
488      *
489      * Node resolving must be performed in following order:
490      * <ol>
491      * <li>
492      * unresolved type references</li>
493      * <li>
494      * uses in groupings</li>
495      * <li>
496      * uses in other nodes</li>
497      * <li>
498      * augments</li>
499      * </ol>
500      *
501      * @param modules
502      *            all loaded modules
503      * @return modules mapped on their builders
504      */
505     private Map<ModuleBuilder, Module> build(final Map<String, TreeMap<Date, ModuleBuilder>> modules) {
506         resolveDirtyNodes(modules);
507         resolveAugmentsTargetPath(modules, null);
508         resolveUsesTargetGrouping(modules, null);
509         resolveUsesForGroupings(modules, null);
510         resolveUsesForNodes(modules, null);
511         resolveAugments(modules, null);
512         resolveDeviations(modules);
513
514         // build
515         final Map<ModuleBuilder, Module> result = new LinkedHashMap<>();
516         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
517             for (Map.Entry<Date, ModuleBuilder> childEntry : entry.getValue().entrySet()) {
518                 final ModuleBuilder moduleBuilder = childEntry.getValue();
519                 final Module module = moduleBuilder.build();
520                 result.put(moduleBuilder, module);
521             }
522         }
523         return result;
524     }
525
526     /**
527      * Creates builder-to-module map based on given modules. Method first
528      * resolve unresolved type references, instantiate groupings through uses
529      * statements and perform augmentation.
530      *
531      * Node resolving must be performed in following order:
532      * <ol>
533      * <li>
534      * unresolved type references</li>
535      * <li>
536      * uses in groupings</li>
537      * <li>
538      * uses in other nodes</li>
539      * <li>
540      * augments</li>
541      * </ol>
542      *
543      * @param modules
544      *            all loaded modules
545      * @param context
546      *            SchemaContext containing already resolved modules
547      * @return modules mapped on their builders
548      */
549     private Map<ModuleBuilder, Module> buildWithContext(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
550             final SchemaContext context) {
551         resolvedDirtyNodesWithContext(modules, context);
552         resolveAugmentsTargetPath(modules, context);
553         resolveUsesTargetGrouping(modules, context);
554         resolveUsesForGroupings(modules, context);
555         resolveUsesForNodes(modules, context);
556         resolveAugments(modules, context);
557         resolveDeviationsWithContext(modules, context);
558
559         // build
560         final Map<ModuleBuilder, Module> result = new LinkedHashMap<>();
561         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
562             for (Map.Entry<Date, ModuleBuilder> childEntry : entry.getValue().entrySet()) {
563                 final ModuleBuilder moduleBuilder = childEntry.getValue();
564                 final Module module = moduleBuilder.build();
565                 result.put(moduleBuilder, module);
566             }
567         }
568         return result;
569     }
570
571     /**
572      * Resolve all unresolved type references.
573      *
574      * @param modules
575      *            all loaded modules
576      */
577     private void resolveDirtyNodes(final Map<String, TreeMap<Date, ModuleBuilder>> modules) {
578         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
579             for (Map.Entry<Date, ModuleBuilder> childEntry : entry.getValue().entrySet()) {
580                 final ModuleBuilder module = childEntry.getValue();
581                 resolveUnknownNodes(modules, module);
582                 resolveIdentities(modules, module);
583                 resolveDirtyNodes(modules, module);
584             }
585         }
586     }
587
588     /**
589      * Resolve all unresolved type references.
590      *
591      * @param modules
592      *            all loaded modules
593      * @param context
594      *            SchemaContext containing already resolved modules
595      */
596     private void resolvedDirtyNodesWithContext(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
597             final SchemaContext context) {
598         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
599             for (Map.Entry<Date, ModuleBuilder> childEntry : entry.getValue().entrySet()) {
600                 final ModuleBuilder module = childEntry.getValue();
601                 resolveUnknownNodesWithContext(modules, module, context);
602                 resolveIdentitiesWithContext(modules, module, context);
603                 resolveDirtyNodesWithContext(modules, module, context);
604             }
605         }
606     }
607
608     /**
609      * Search for dirty nodes (node which contains UnknownType) and resolve
610      * unknown types.
611      *
612      * @param modules
613      *            all available modules
614      * @param module
615      *            current module
616      */
617     private void resolveDirtyNodes(final Map<String, TreeMap<Date, ModuleBuilder>> modules, final ModuleBuilder module) {
618         final Set<TypeAwareBuilder> dirtyNodes = module.getDirtyNodes();
619         if (!dirtyNodes.isEmpty()) {
620             for (TypeAwareBuilder nodeToResolve : dirtyNodes) {
621                 if (nodeToResolve instanceof UnionTypeBuilder) {
622                     // special handling for union types
623                     resolveTypeUnion((UnionTypeBuilder) nodeToResolve, modules, module);
624                 } else if (nodeToResolve.getTypedef() instanceof IdentityrefTypeBuilder) {
625                     // special handling for identityref types
626                     IdentityrefTypeBuilder idref = (IdentityrefTypeBuilder) nodeToResolve.getTypedef();
627                     IdentitySchemaNodeBuilder identity = findBaseIdentity(modules, module, idref.getBaseString(),
628                             idref.getLine());
629                     if (identity == null) {
630                         throw new YangParseException(module.getName(), idref.getLine(), "Failed to find base identity");
631                     }
632                     idref.setBaseIdentity(identity);
633                     nodeToResolve.setType(idref.build());
634                 } else {
635                     resolveType(nodeToResolve, modules, module);
636                 }
637             }
638         }
639     }
640
641     private void resolveDirtyNodesWithContext(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
642             final ModuleBuilder module, SchemaContext context) {
643         final Set<TypeAwareBuilder> dirtyNodes = module.getDirtyNodes();
644         if (!dirtyNodes.isEmpty()) {
645             for (TypeAwareBuilder nodeToResolve : dirtyNodes) {
646                 if (nodeToResolve instanceof UnionTypeBuilder) {
647                     // special handling for union types
648                     resolveTypeUnionWithContext((UnionTypeBuilder) nodeToResolve, modules, module, context);
649                 } else if (nodeToResolve.getTypedef() instanceof IdentityrefTypeBuilder) {
650                     // special handling for identityref types
651                     IdentityrefTypeBuilder idref = (IdentityrefTypeBuilder) nodeToResolve.getTypedef();
652                     IdentitySchemaNodeBuilder identity = findBaseIdentity(modules, module, idref.getBaseString(),
653                             idref.getLine());
654                     idref.setBaseIdentity(identity);
655                     nodeToResolve.setType(idref.build());
656                 } else {
657                     resolveTypeWithContext(nodeToResolve, modules, module, context);
658                 }
659             }
660         }
661     }
662
663     /**
664      * Traverse through augmentations of modules and fix their child nodes
665      * schema path.
666      *
667      * @param modules
668      *            all loaded modules
669      * @param context
670      *            SchemaContext containing already resolved modules
671      */
672     private void resolveAugmentsTargetPath(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
673             SchemaContext context) {
674         // collect augments from all loaded modules
675         final List<AugmentationSchemaBuilder> allAugments = new ArrayList<>();
676         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
677             for (Map.Entry<Date, ModuleBuilder> inner : entry.getValue().entrySet()) {
678                 allAugments.addAll(inner.getValue().getAllAugments());
679             }
680         }
681
682         for (AugmentationSchemaBuilder augment : allAugments) {
683             setCorrectAugmentTargetPath(modules, augment, context);
684         }
685     }
686
687     /**
688      * Find augment target and set correct schema path for all its child nodes.
689      *
690      * @param modules
691      *            all loaded modules
692      * @param augment
693      *            augment to resolve
694      * @param context
695      *            SchemaContext containing already resolved modules
696      */
697     private void setCorrectAugmentTargetPath(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
698             final AugmentationSchemaBuilder augment, final SchemaContext context) {
699         ModuleBuilder module = ParserUtils.getParentModule(augment);
700         SchemaPath oldSchemaPath = augment.getTargetPath();
701         List<QName> oldPath = oldSchemaPath.getPath();
702         List<QName> newPath = new ArrayList<>();
703
704         Builder parent = augment.getParent();
705         if (parent instanceof UsesNodeBuilder) {
706             DataNodeContainerBuilder usesParent = ((UsesNodeBuilder) parent).getParent();
707             newPath.addAll(usesParent.getPath().getPath());
708
709             URI ns;
710             Date revision;
711             String prefix;
712             QName baseQName = usesParent.getQName();
713             if (baseQName == null) {
714                 ModuleBuilder m = ParserUtils.getParentModule(usesParent);
715                 ns = m.getNamespace();
716                 revision = m.getRevision();
717                 prefix = m.getPrefix();
718             } else {
719                 ns = baseQName.getNamespace();
720                 revision = baseQName.getRevision();
721                 prefix = baseQName.getPrefix();
722             }
723
724             for (QName qn : oldSchemaPath.getPath()) {
725                 newPath.add(new QName(ns, revision, prefix, qn.getLocalName()));
726             }
727         } else {
728             for (QName qn : oldPath) {
729                 URI ns = module.getNamespace();
730                 Date rev = module.getRevision();
731                 String localPrefix = qn.getPrefix();
732                 if (localPrefix != null && !("".equals(localPrefix))) {
733                     ModuleBuilder currentModule = ParserUtils.findModuleFromBuilders(modules, module, localPrefix,
734                             augment.getLine());
735                     if (currentModule == null) {
736                         Module m = ParserUtils.findModuleFromContext(context, module, localPrefix, augment.getLine());
737                         if (m == null) {
738                             throw new YangParseException(module.getName(), augment.getLine(), "Module with prefix "
739                                     + localPrefix + " not found.");
740                         }
741                         ns = m.getNamespace();
742                         rev = m.getRevision();
743                     } else {
744                         ns = currentModule.getNamespace();
745                         rev = currentModule.getRevision();
746                     }
747                 }
748                 newPath.add(new QName(ns, rev, localPrefix, qn.getLocalName()));
749             }
750         }
751         augment.setTargetNodeSchemaPath(new SchemaPath(newPath, augment.getTargetPath().isAbsolute()));
752
753         for (DataSchemaNodeBuilder childNode : augment.getChildNodeBuilders()) {
754             correctPathForAugmentNodes(childNode, augment.getTargetNodeSchemaPath());
755         }
756     }
757
758     /**
759      * Set new schema path to node and all its child nodes based on given parent
760      * path. This method do not change the namespace.
761      *
762      * @param node
763      *            node which schema path should be updated
764      * @param parentPath
765      *            schema path of parent node
766      */
767     private void correctPathForAugmentNodes(DataSchemaNodeBuilder node, SchemaPath parentPath) {
768         SchemaPath newPath = ParserUtils.createSchemaPath(parentPath, node.getQName());
769         node.setPath(newPath);
770         if (node instanceof DataNodeContainerBuilder) {
771             for (DataSchemaNodeBuilder child : ((DataNodeContainerBuilder) node).getChildNodeBuilders()) {
772                 correctPathForAugmentNodes(child, node.getPath());
773             }
774         }
775         if (node instanceof ChoiceBuilder) {
776             for (ChoiceCaseBuilder child : ((ChoiceBuilder)node).getCases()) {
777                 correctPathForAugmentNodes(child, node.getPath());
778             }
779         }
780     }
781
782     /**
783      * Check augments for mandatory nodes. If the target node is in another
784      * module, then nodes added by the augmentation MUST NOT be mandatory nodes.
785      * If mandatory node is found, throw an exception.
786      *
787      * @param augments
788      *            augments to check
789      */
790     private void checkAugmentMandatoryNodes(Collection<AugmentationSchemaBuilder> augments) {
791         for (AugmentationSchemaBuilder augment : augments) {
792             String augmentPrefix = augment.getTargetPath().getPath().get(0).getPrefix();
793             ModuleBuilder module = ParserUtils.getParentModule(augment);
794             String modulePrefix = module.getPrefix();
795
796             if (augmentPrefix == null || augmentPrefix.isEmpty() || augmentPrefix.equals(modulePrefix)) {
797                 continue;
798             }
799
800             for (DataSchemaNodeBuilder childNode : augment.getChildNodeBuilders()) {
801                 if (childNode.getConstraints().isMandatory()) {
802                     throw new YangParseException(augment.getModuleName(), augment.getLine(),
803                             "Error in augment parsing: cannot augment mandatory node "
804                                     + childNode.getQName().getLocalName());
805                 }
806             }
807         }
808     }
809
810     /**
811      * Go through all augment definitions and resolve them.
812      *
813      * @param modules
814      *            all loaded modules
815      * @param context
816      *            SchemaContext containing already resolved modules
817      */
818     private void resolveAugments(final Map<String, TreeMap<Date, ModuleBuilder>> modules, final SchemaContext context) {
819         List<ModuleBuilder> all = new ArrayList<>();
820         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
821             for (Map.Entry<Date, ModuleBuilder> inner : entry.getValue().entrySet()) {
822                 all.add(inner.getValue());
823             }
824         }
825
826         List<ModuleBuilder> sorted;
827         if (context == null) {
828             sorted = ModuleDependencySort.sort(all.toArray(new ModuleBuilder[all.size()]));
829         } else {
830             sorted = ModuleDependencySort.sortWithContext(context, all.toArray(new ModuleBuilder[all.size()]));
831         }
832
833         for (ModuleBuilder mb : sorted) {
834             if (mb != null) {
835                 List<AugmentationSchemaBuilder> augments = mb.getAllAugments();
836                 checkAugmentMandatoryNodes(augments);
837                 Collections.sort(augments, Comparators.AUGMENT_COMP);
838                 for (AugmentationSchemaBuilder augment : augments) {
839                     if (!(augment.isResolved())) {
840                         boolean resolved = resolveAugment(augment, mb, modules, context);
841                         if (!resolved) {
842                             throw new YangParseException(augment.getModuleName(), augment.getLine(),
843                                     "Error in augment parsing: failed to find augment target: " + augment);
844                         }
845                     }
846                 }
847             }
848         }
849     }
850
851     /**
852      * Perform augmentation defined under uses statement.
853      *
854      * @param augment
855      *            augment to resolve
856      * @param module
857      *            current module
858      * @param modules
859      *            all loaded modules
860      * @param context
861      *            SchemaContext containing already resolved modules
862      * @return true if augment process succeed
863      */
864     private boolean resolveUsesAugment(final AugmentationSchemaBuilder augment, final ModuleBuilder module,
865             final Map<String, TreeMap<Date, ModuleBuilder>> modules, final SchemaContext context) {
866         if (augment.isResolved()) {
867             return true;
868         }
869
870         UsesNodeBuilder usesNode = (UsesNodeBuilder) augment.getParent();
871         DataNodeContainerBuilder parentNode = usesNode.getParent();
872         SchemaNodeBuilder targetNode;
873         if (parentNode instanceof ModuleBuilder) {
874             targetNode = findSchemaNodeInModule(augment.getTargetPath().getPath(), (ModuleBuilder) parentNode);
875         } else {
876             targetNode = findSchemaNode(augment.getTargetPath().getPath(), (SchemaNodeBuilder) parentNode);
877         }
878
879         fillAugmentTarget(augment, targetNode);
880         augment.setResolved(true);
881         return true;
882     }
883
884     /**
885      * Find augment target module and perform augmentation.
886      *
887      * @param augment
888      *            augment to resolve
889      * @param module
890      *            current module
891      * @param modules
892      *            all loaded modules
893      * @param context
894      *            SchemaContext containing already resolved modules
895      * @return true if augment process succeed
896      */
897     private boolean resolveAugment(final AugmentationSchemaBuilder augment, final ModuleBuilder module,
898             final Map<String, TreeMap<Date, ModuleBuilder>> modules, final SchemaContext context) {
899         if (augment.isResolved()) {
900             return true;
901         }
902
903         List<QName> targetPath = augment.getTargetPath().getPath();
904         ModuleBuilder targetModule = findTargetModule(targetPath.get(0), module, modules, context, augment.getLine());
905         if (targetModule == null) {
906             throw new YangParseException(module.getModuleName(), augment.getLine(), "Failed to resolve augment "
907                     + augment);
908         }
909
910         return processAugmentation(augment, targetModule);
911     }
912
913     /**
914      * Find module from loaded modules or from context based on given qname. If
915      * module is found in context, create wrapper over this module and add it to
916      * collection of loaded modules.
917      *
918      * @param qname
919      * @param module
920      *            current module
921      * @param modules
922      *            all loaded modules
923      * @param context
924      *            schema context
925      * @param line
926      *            current line
927      * @return
928      */
929     private ModuleBuilder findTargetModule(final QName qname, final ModuleBuilder module,
930             final Map<String, TreeMap<Date, ModuleBuilder>> modules, final SchemaContext context, final int line) {
931         ModuleBuilder targetModule = null;
932
933         String prefix = qname.getPrefix();
934         if (prefix == null || prefix.equals("")) {
935             targetModule = module;
936         } else {
937             targetModule = findModuleFromBuilders(modules, module, qname.getPrefix(), line);
938         }
939
940         if (targetModule == null && context != null) {
941             Module m = findModuleFromContext(context, module, prefix, line);
942             targetModule = new ModuleBuilder(m);
943             DataSchemaNode firstNode = m.getDataChildByName(qname.getLocalName());
944             DataSchemaNodeBuilder firstNodeWrapped = wrapChildNode(targetModule.getModuleName(), line, firstNode,
945                     targetModule.getPath(), firstNode.getQName());
946             targetModule.addChildNode(firstNodeWrapped);
947
948             TreeMap<Date, ModuleBuilder> map = new TreeMap<>();
949             map.put(targetModule.getRevision(), targetModule);
950             modules.put(targetModule.getModuleName(), map);
951         }
952
953         return targetModule;
954     }
955
956     /**
957      * Go through identity statements defined in current module and resolve
958      * their 'base' statement if present.
959      *
960      * @param modules
961      *            all modules
962      * @param module
963      *            module being resolved
964      */
965     private void resolveIdentities(final Map<String, TreeMap<Date, ModuleBuilder>> modules, final ModuleBuilder module) {
966         final Set<IdentitySchemaNodeBuilder> identities = module.getAddedIdentities();
967         for (IdentitySchemaNodeBuilder identity : identities) {
968             final String baseIdentityName = identity.getBaseIdentityName();
969             final int line = identity.getLine();
970             if (baseIdentityName != null) {
971                 IdentitySchemaNodeBuilder baseIdentity = findBaseIdentity(modules, module, baseIdentityName, line);
972                 if (baseIdentity == null) {
973                     throw new YangParseException(module.getName(), identity.getLine(), "Failed to find base identity");
974                 } else {
975                     identity.setBaseIdentity(baseIdentity);
976                 }
977             }
978         }
979     }
980
981     /**
982      * Go through identity statements defined in current module and resolve
983      * their 'base' statement. Method tries to find base identity in given
984      * modules. If base identity is not found, method will search it in context.
985      *
986      * @param modules
987      *            all loaded modules
988      * @param module
989      *            current module
990      * @param context
991      *            SchemaContext containing already resolved modules
992      */
993     private void resolveIdentitiesWithContext(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
994             final ModuleBuilder module, final SchemaContext context) {
995         final Set<IdentitySchemaNodeBuilder> identities = module.getAddedIdentities();
996         for (IdentitySchemaNodeBuilder identity : identities) {
997             final String baseIdentityName = identity.getBaseIdentityName();
998             final int line = identity.getLine();
999             if (baseIdentityName != null) {
1000                 IdentitySchemaNodeBuilder baseIdentity = findBaseIdentity(modules, module, baseIdentityName, line);
1001                 if (baseIdentity == null) {
1002                     IdentitySchemaNode baseId = findBaseIdentityFromContext(modules, module, baseIdentityName, line,
1003                             context);
1004                     identity.setBaseIdentity(baseId);
1005                 } else {
1006                     identity.setBaseIdentity(baseIdentity);
1007                 }
1008             }
1009         }
1010     }
1011
1012     /**
1013      * Find and add reference of uses target grouping.
1014      *
1015      * @param modules
1016      *            all loaded modules
1017      * @param context
1018      *            SchemaContext containing already resolved modules or null if
1019      *            context is not available
1020      */
1021     private void resolveUsesTargetGrouping(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
1022             final SchemaContext context) {
1023         final List<UsesNodeBuilder> allUses = new ArrayList<>();
1024         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
1025             for (Map.Entry<Date, ModuleBuilder> inner : entry.getValue().entrySet()) {
1026                 allUses.addAll(inner.getValue().getAllUsesNodes());
1027             }
1028         }
1029         for (UsesNodeBuilder usesNode : allUses) {
1030             ModuleBuilder module = ParserUtils.getParentModule(usesNode);
1031             final GroupingBuilder targetGroupingBuilder = GroupingUtils.getTargetGroupingFromModules(usesNode, modules,
1032                     module);
1033             if (targetGroupingBuilder == null) {
1034                 if (context == null) {
1035                     throw new YangParseException(module.getName(), usesNode.getLine(), "Referenced grouping '"
1036                             + usesNode.getGroupingPathAsString() + "' not found.");
1037                 } else {
1038                     GroupingDefinition targetGroupingDefinition = GroupingUtils.getTargetGroupingFromContext(usesNode,
1039                             module, context);
1040                     usesNode.setGroupingDefinition(targetGroupingDefinition);
1041                 }
1042             } else {
1043                 usesNode.setGrouping(targetGroupingBuilder);
1044             }
1045         }
1046     }
1047
1048     /**
1049      * Resolve uses statements defined in groupings.
1050      *
1051      * @param modules
1052      *            all loaded modules
1053      * @param context
1054      *            SchemaContext containing already resolved modules
1055      */
1056     private void resolveUsesForGroupings(final Map<String, TreeMap<Date, ModuleBuilder>> modules, final SchemaContext context) {
1057         final Set<GroupingBuilder> allGroupings = new HashSet<>();
1058         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
1059             for (Map.Entry<Date, ModuleBuilder> inner : entry.getValue().entrySet()) {
1060                 ModuleBuilder module = inner.getValue();
1061                 allGroupings.addAll(module.getAllGroupings());
1062             }
1063         }
1064         final List<GroupingBuilder> sorted = GroupingSort.sort(allGroupings);
1065         for (GroupingBuilder gb : sorted) {
1066             List<UsesNodeBuilder> usesNodes = new ArrayList<>(GroupingSort.getAllUsesNodes(gb));
1067             Collections.sort(usesNodes, new GroupingUtils.UsesComparator());
1068             for (UsesNodeBuilder usesNode : usesNodes) {
1069                 resolveUses(usesNode, modules, context);
1070             }
1071         }
1072     }
1073
1074     /**
1075      * Resolve uses statements.
1076      *
1077      * @param modules
1078      *            all loaded modules
1079      * @param context
1080      *            SchemaContext containing already resolved modules
1081      */
1082     private void resolveUsesForNodes(final Map<String, TreeMap<Date, ModuleBuilder>> modules, final SchemaContext context) {
1083         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
1084             for (Map.Entry<Date, ModuleBuilder> inner : entry.getValue().entrySet()) {
1085                 ModuleBuilder module = inner.getValue();
1086                 List<UsesNodeBuilder> usesNodes = module.getAllUsesNodes();
1087                 Collections.sort(usesNodes, new GroupingUtils.UsesComparator());
1088                 for (UsesNodeBuilder usesNode : usesNodes) {
1089                     resolveUses(usesNode, modules, context);
1090                 }
1091             }
1092         }
1093     }
1094
1095     /**
1096      * Find target grouping and copy its child nodes to current location with
1097      * new namespace.
1098      *
1099      * @param usesNode
1100      *            uses node to resolve
1101      * @param modules
1102      *            all loaded modules
1103      * @param context
1104      *            SchemaContext containing already resolved modules
1105      */
1106     private void resolveUses(UsesNodeBuilder usesNode,
1107             final Map<String, TreeMap<Date, ModuleBuilder>> modules, final SchemaContext context) {
1108         if (!usesNode.isResolved()) {
1109             DataNodeContainerBuilder parent = usesNode.getParent();
1110             ModuleBuilder module = ParserUtils.getParentModule(parent);
1111             GroupingBuilder target = GroupingUtils.getTargetGroupingFromModules(usesNode, modules, module);
1112             if (target == null) {
1113                 resolveUsesWithContext(usesNode);
1114                 usesNode.setResolved(true);
1115                 for (AugmentationSchemaBuilder augment : usesNode.getAugmentations()) {
1116                     resolveUsesAugment(augment, module, modules, context);
1117                 }
1118             } else {
1119                 parent.getChildNodeBuilders().addAll(target.instantiateChildNodes(parent));
1120                 parent.getTypeDefinitionBuilders().addAll(target.instantiateTypedefs(parent));
1121                 parent.getGroupingBuilders().addAll(target.instantiateGroupings(parent));
1122                 parent.getUnknownNodes().addAll(target.instantiateUnknownNodes(parent));
1123                 usesNode.setResolved(true);
1124                 for (AugmentationSchemaBuilder augment : usesNode.getAugmentations()) {
1125                     resolveUsesAugment(augment, module, modules, context);
1126                 }
1127             }
1128             GroupingUtils.performRefine(usesNode);
1129         }
1130     }
1131
1132     /**
1133      * Copy target grouping child nodes to current location with
1134      * new namespace.
1135      *
1136      * @param usesNode
1137      *            uses node to resolve
1138      * @param modules
1139      *            all loaded modules
1140      * @param context
1141      *            SchemaContext containing already resolved modules
1142      */
1143     private void resolveUsesWithContext(UsesNodeBuilder usesNode) {
1144         final int line = usesNode.getLine();
1145         DataNodeContainerBuilder parent = usesNode.getParent();
1146         ModuleBuilder module = ParserUtils.getParentModule(parent);
1147         SchemaPath parentPath;
1148         URI ns = null;
1149         Date rev = null;
1150         String pref = null;
1151         if (parent instanceof AugmentationSchemaBuilder || parent instanceof ModuleBuilder) {
1152             ns = module.getNamespace();
1153             rev = module.getRevision();
1154             pref = module.getPrefix();
1155             if (parent instanceof AugmentationSchemaBuilder) {
1156                 parentPath = ((AugmentationSchemaBuilder)parent).getTargetNodeSchemaPath();
1157             } else {
1158                 parentPath = ((ModuleBuilder)parent).getPath();
1159             }
1160         } else {
1161             ns = ((DataSchemaNodeBuilder) parent).getQName().getNamespace();
1162             rev = ((DataSchemaNodeBuilder) parent).getQName().getRevision();
1163             pref = ((DataSchemaNodeBuilder) parent).getQName().getPrefix();
1164             parentPath = ((DataSchemaNodeBuilder)parent).getPath();
1165         }
1166
1167         GroupingDefinition gd = usesNode.getGroupingDefinition();
1168
1169         Set<DataSchemaNodeBuilder> childNodes = wrapChildNodes(module.getModuleName(), line,
1170                 gd.getChildNodes(), parentPath, ns, rev, pref);
1171         parent.getChildNodeBuilders().addAll(childNodes);
1172         for (DataSchemaNodeBuilder childNode : childNodes) {
1173             setNodeAddedByUses(childNode);
1174         }
1175
1176         Set<TypeDefinitionBuilder> typedefs = wrapTypedefs(module.getModuleName(), line, gd, parentPath, ns,
1177                 rev, pref);
1178         parent.getTypeDefinitionBuilders().addAll(typedefs);
1179         for (TypeDefinitionBuilder typedef : typedefs) {
1180             setNodeAddedByUses(typedef);
1181         }
1182
1183         Set<GroupingBuilder> groupings = wrapGroupings(module.getModuleName(), line, usesNode
1184                 .getGroupingDefinition().getGroupings(), parentPath, ns, rev, pref);
1185         parent.getGroupingBuilders().addAll(groupings);
1186         for (GroupingBuilder gb : groupings) {
1187             setNodeAddedByUses(gb);
1188         }
1189
1190         List<UnknownSchemaNodeBuilder> unknownNodes = wrapUnknownNodes(module.getModuleName(), line,
1191                 gd.getUnknownSchemaNodes(), parentPath, ns, rev, pref);
1192         parent.getUnknownNodes().addAll(unknownNodes);
1193         for (UnknownSchemaNodeBuilder un : unknownNodes) {
1194             un.setAddedByUses(true);
1195         }
1196     }
1197
1198     /**
1199      * Try to find extension builder describing this unknown node and assign it
1200      * to unknown node builder.
1201      *
1202      * @param modules
1203      *            all loaded modules
1204      * @param module
1205      *            current module
1206      */
1207     private void resolveUnknownNodes(final Map<String, TreeMap<Date, ModuleBuilder>> modules, final ModuleBuilder module) {
1208         for (UnknownSchemaNodeBuilder usnb : module.getAllUnknownNodes()) {
1209             QName nodeType = usnb.getNodeType();
1210             try {
1211                 ModuleBuilder dependentModule = findModuleFromBuilders(modules, module, nodeType.getPrefix(),
1212                         usnb.getLine());
1213                 for (ExtensionBuilder extension : dependentModule.getAddedExtensions()) {
1214                     if (extension.getQName().getLocalName().equals(nodeType.getLocalName())) {
1215                         usnb.setNodeType(extension.getQName());
1216                         usnb.setExtensionBuilder(extension);
1217                         break;
1218                     }
1219                 }
1220             } catch (YangParseException e) {
1221                 throw new YangParseException(module.getName(), usnb.getLine(), "Failed to resolve node " + usnb
1222                         + ": no such extension definition found.", e);
1223             }
1224         }
1225     }
1226
1227     /**
1228      * Try to find extension builder describing this unknown node and assign it
1229      * to unknown node builder. If extension is not found in loaded modules, try
1230      * to find it in context.
1231      *
1232      * @param modules
1233      *            all loaded modules
1234      * @param module
1235      *            current module
1236      * @param context
1237      *            SchemaContext containing already resolved modules
1238      */
1239     private void resolveUnknownNodesWithContext(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
1240             final ModuleBuilder module, final SchemaContext context) {
1241         for (UnknownSchemaNodeBuilder usnb : module.getAllUnknownNodes()) {
1242             QName nodeType = usnb.getNodeType();
1243             try {
1244                 ModuleBuilder dependentModuleBuilder = findModuleFromBuilders(modules, module, nodeType.getPrefix(),
1245                         usnb.getLine());
1246
1247                 if (dependentModuleBuilder == null) {
1248                     Module dependentModule = findModuleFromContext(context, module, nodeType.getPrefix(),
1249                             usnb.getLine());
1250                     for (ExtensionDefinition e : dependentModule.getExtensionSchemaNodes()) {
1251                         if (e.getQName().getLocalName().equals(nodeType.getLocalName())) {
1252                             usnb.setNodeType(new QName(e.getQName().getNamespace(), e.getQName().getRevision(),
1253                                     nodeType.getPrefix(), e.getQName().getLocalName()));
1254                             usnb.setExtensionDefinition(e);
1255                             break;
1256                         }
1257                     }
1258                 } else {
1259                     for (ExtensionBuilder extension : dependentModuleBuilder.getAddedExtensions()) {
1260                         if (extension.getQName().getLocalName().equals(nodeType.getLocalName())) {
1261                             usnb.setExtensionBuilder(extension);
1262                             break;
1263                         }
1264                     }
1265                 }
1266
1267             } catch (YangParseException e) {
1268                 throw new YangParseException(module.getName(), usnb.getLine(), "Failed to resolve node " + usnb
1269                         + ": no such extension definition found.", e);
1270             }
1271
1272         }
1273     }
1274
1275     /**
1276      * Traverse through modules and resolve their deviation statements.
1277      *
1278      * @param modules
1279      *            all loaded modules
1280      */
1281     private void resolveDeviations(final Map<String, TreeMap<Date, ModuleBuilder>> modules) {
1282         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
1283             for (Map.Entry<Date, ModuleBuilder> inner : entry.getValue().entrySet()) {
1284                 ModuleBuilder b = inner.getValue();
1285                 resolveDeviation(modules, b);
1286             }
1287         }
1288     }
1289
1290     /**
1291      * Traverse through module and resolve its deviation statements.
1292      *
1293      * @param modules
1294      *            all loaded modules
1295      * @param module
1296      *            module in which resolve deviations
1297      */
1298     private void resolveDeviation(final Map<String, TreeMap<Date, ModuleBuilder>> modules, final ModuleBuilder module) {
1299         for (DeviationBuilder dev : module.getDeviationBuilders()) {
1300             int line = dev.getLine();
1301             SchemaPath targetPath = dev.getTargetPath();
1302             List<QName> path = targetPath.getPath();
1303             QName q0 = path.get(0);
1304             String prefix = q0.getPrefix();
1305             if (prefix == null) {
1306                 prefix = module.getPrefix();
1307             }
1308
1309             ModuleBuilder dependentModuleBuilder = findModuleFromBuilders(modules, module, prefix, line);
1310             processDeviation(dev, dependentModuleBuilder, path, module);
1311         }
1312     }
1313
1314     /**
1315      * Traverse through modules and resolve their deviation statements with
1316      * given context.
1317      *
1318      * @param modules
1319      *            all loaded modules
1320      * @param context
1321      *            already resolved context
1322      */
1323     private void resolveDeviationsWithContext(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
1324             final SchemaContext context) {
1325         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
1326             for (Map.Entry<Date, ModuleBuilder> inner : entry.getValue().entrySet()) {
1327                 ModuleBuilder b = inner.getValue();
1328                 resolveDeviationWithContext(modules, b, context);
1329             }
1330         }
1331     }
1332
1333     /**
1334      * Traverse through module and resolve its deviation statements with given
1335      * context.
1336      *
1337      * @param modules
1338      *            all loaded modules
1339      * @param module
1340      *            module in which resolve deviations
1341      * @param context
1342      *            already resolved context
1343      */
1344     private void resolveDeviationWithContext(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
1345             final ModuleBuilder module, final SchemaContext context) {
1346         for (DeviationBuilder dev : module.getDeviationBuilders()) {
1347             int line = dev.getLine();
1348             SchemaPath targetPath = dev.getTargetPath();
1349             List<QName> path = targetPath.getPath();
1350             QName q0 = path.get(0);
1351             String prefix = q0.getPrefix();
1352             if (prefix == null) {
1353                 prefix = module.getPrefix();
1354             }
1355
1356             ModuleBuilder dependentModuleBuilder = findModuleFromBuilders(modules, module, prefix, line);
1357             if (dependentModuleBuilder == null) {
1358                 Object currentParent = findModuleFromContext(context, module, prefix, line);
1359
1360                 for (QName q : path) {
1361                     if (currentParent == null) {
1362                         throw new YangParseException(module.getName(), line, FAIL_DEVIATION_TARGET);
1363                     }
1364                     String name = q.getLocalName();
1365                     if (currentParent instanceof DataNodeContainer) {
1366                         currentParent = ((DataNodeContainer) currentParent).getDataChildByName(name);
1367                     }
1368                 }
1369
1370                 if (currentParent == null) {
1371                     throw new YangParseException(module.getName(), line, FAIL_DEVIATION_TARGET);
1372                 }
1373                 if (currentParent instanceof SchemaNode) {
1374                     dev.setTargetPath(((SchemaNode) currentParent).getPath());
1375                 }
1376
1377             } else {
1378                 processDeviation(dev, dependentModuleBuilder, path, module);
1379             }
1380         }
1381     }
1382
1383     /**
1384      * Correct deviation target path in deviation builder.
1385      *
1386      * @param dev
1387      *            deviation
1388      * @param dependentModuleBuilder
1389      *            module containing deviation target
1390      * @param path
1391      *            current deviation target path
1392      * @param module
1393      *            current module
1394      */
1395     private void processDeviation(final DeviationBuilder dev, final ModuleBuilder dependentModuleBuilder,
1396             final List<QName> path, final ModuleBuilder module) {
1397         final int line = dev.getLine();
1398         Builder currentParent = dependentModuleBuilder;
1399
1400         for (QName q : path) {
1401             if (currentParent == null) {
1402                 throw new YangParseException(module.getName(), line, FAIL_DEVIATION_TARGET);
1403             }
1404             String name = q.getLocalName();
1405             if (currentParent instanceof DataNodeContainerBuilder) {
1406                 currentParent = ((DataNodeContainerBuilder) currentParent).getDataChildByName(name);
1407             }
1408         }
1409
1410         if (!(currentParent instanceof SchemaNodeBuilder)) {
1411             throw new YangParseException(module.getName(), line, FAIL_DEVIATION_TARGET);
1412         }
1413         dev.setTargetPath(((SchemaNodeBuilder) currentParent).getPath());
1414     }
1415
1416 }