Implemented support for generating YangModuleInfo implementation.
[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     private Map<ModuleBuilder, Module> build(final Map<String, TreeMap<Date, ModuleBuilder>> modules) {
485         // fix unresolved nodes
486         resolveDirtyNodes(modules);
487         resolveAugmentsTargetPath(modules, null);
488         resolveUsesTargetGrouping(modules, null);
489         resolveUsesForGroupings(modules, null);
490         resolveUsesForNodes(modules, null);
491         resolveAugments(modules, null);
492         resolveDeviations(modules);
493
494         // build
495         final Map<ModuleBuilder, Module> result = new LinkedHashMap<>();
496         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
497             for (Map.Entry<Date, ModuleBuilder> childEntry : entry.getValue().entrySet()) {
498                 final ModuleBuilder moduleBuilder = childEntry.getValue();
499                 final Module module = moduleBuilder.build();
500                 result.put(moduleBuilder, module);
501             }
502         }
503         return result;
504     }
505
506     private Map<ModuleBuilder, Module> buildWithContext(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
507             final SchemaContext context) {
508         // fix unresolved nodes
509         resolvedDirtyNodesWithContext(modules, context);
510         resolveAugmentsTargetPath(modules, context);
511         resolveUsesTargetGrouping(modules, context);
512         resolveUsesForGroupings(modules, context);
513         resolveUsesForNodes(modules, context);
514         resolveAugments(modules, context);
515         resolveDeviationsWithContext(modules, context);
516
517         // build
518         final Map<ModuleBuilder, Module> result = new LinkedHashMap<>();
519         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
520             for (Map.Entry<Date, ModuleBuilder> childEntry : entry.getValue().entrySet()) {
521                 final ModuleBuilder moduleBuilder = childEntry.getValue();
522                 final Module module = moduleBuilder.build();
523                 result.put(moduleBuilder, module);
524             }
525         }
526         return result;
527     }
528
529     private void resolveDirtyNodes(final Map<String, TreeMap<Date, ModuleBuilder>> modules) {
530         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
531             for (Map.Entry<Date, ModuleBuilder> childEntry : entry.getValue().entrySet()) {
532                 final ModuleBuilder module = childEntry.getValue();
533                 resolveUnknownNodes(modules, module);
534                 resolveIdentities(modules, module);
535                 resolveDirtyNodes(modules, module);
536             }
537         }
538     }
539
540     private void resolvedDirtyNodesWithContext(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
541             final SchemaContext context) {
542         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
543             for (Map.Entry<Date, ModuleBuilder> childEntry : entry.getValue().entrySet()) {
544                 final ModuleBuilder module = childEntry.getValue();
545                 resolveUnknownNodesWithContext(modules, module, context);
546                 resolveIdentitiesWithContext(modules, module, context);
547                 resolveDirtyNodesWithContext(modules, module, context);
548             }
549         }
550     }
551
552     /**
553      * Search for dirty nodes (node which contains UnknownType) and resolve
554      * unknown types.
555      *
556      * @param modules
557      *            all available modules
558      * @param module
559      *            current module
560      */
561     private void resolveDirtyNodes(final Map<String, TreeMap<Date, ModuleBuilder>> modules, final ModuleBuilder module) {
562         final Set<TypeAwareBuilder> dirtyNodes = module.getDirtyNodes();
563         if (!dirtyNodes.isEmpty()) {
564             for (TypeAwareBuilder nodeToResolve : dirtyNodes) {
565                 if (nodeToResolve instanceof UnionTypeBuilder) {
566                     // special handling for union types
567                     resolveTypeUnion((UnionTypeBuilder) nodeToResolve, modules, module);
568                 } else if (nodeToResolve.getTypedef() instanceof IdentityrefTypeBuilder) {
569                     // special handling for identityref types
570                     IdentityrefTypeBuilder idref = (IdentityrefTypeBuilder) nodeToResolve.getTypedef();
571                     IdentitySchemaNodeBuilder identity = findBaseIdentity(modules, module, idref.getBaseString(),
572                             idref.getLine());
573                     if (identity == null) {
574                         throw new YangParseException(module.getName(), idref.getLine(), "Failed to find base identity");
575                     }
576                     idref.setBaseIdentity(identity);
577                     nodeToResolve.setType(idref.build());
578                 } else {
579                     resolveType(nodeToResolve, modules, module);
580                 }
581             }
582         }
583     }
584
585     private void resolveDirtyNodesWithContext(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
586             final ModuleBuilder module, SchemaContext context) {
587         final Set<TypeAwareBuilder> dirtyNodes = module.getDirtyNodes();
588         if (!dirtyNodes.isEmpty()) {
589             for (TypeAwareBuilder nodeToResolve : dirtyNodes) {
590                 if (nodeToResolve instanceof UnionTypeBuilder) {
591                     // special handling for union types
592                     resolveTypeUnionWithContext((UnionTypeBuilder) nodeToResolve, modules, module, context);
593                 } else if (nodeToResolve.getTypedef() instanceof IdentityrefTypeBuilder) {
594                     // special handling for identityref types
595                     IdentityrefTypeBuilder idref = (IdentityrefTypeBuilder) nodeToResolve.getTypedef();
596                     IdentitySchemaNodeBuilder identity = findBaseIdentity(modules, module, idref.getBaseString(),
597                             idref.getLine());
598                     idref.setBaseIdentity(identity);
599                     nodeToResolve.setType(idref.build());
600                 } else {
601                     resolveTypeWithContext(nodeToResolve, modules, module, context);
602                 }
603             }
604         }
605     }
606
607     /**
608      * Correct augment target path.
609      *
610      * @param modules
611      *            all loaded modules
612      */
613     private void resolveAugmentsTargetPath(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
614             SchemaContext context) {
615         // collect augments from all loaded modules
616         final List<AugmentationSchemaBuilder> allAugments = new ArrayList<>();
617         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
618             for (Map.Entry<Date, ModuleBuilder> inner : entry.getValue().entrySet()) {
619                 allAugments.addAll(inner.getValue().getAllAugments());
620             }
621         }
622
623         for (AugmentationSchemaBuilder augment : allAugments) {
624             setCorrectAugmentTargetPath(modules, augment, context);
625         }
626     }
627
628     private void setCorrectAugmentTargetPath(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
629             final AugmentationSchemaBuilder augment, final SchemaContext context) {
630         ModuleBuilder module = ParserUtils.getParentModule(augment);
631         SchemaPath oldSchemaPath = augment.getTargetPath();
632         List<QName> oldPath = oldSchemaPath.getPath();
633         List<QName> newPath = new ArrayList<>();
634
635         Builder parent = augment.getParent();
636         if (parent instanceof UsesNodeBuilder) {
637             DataNodeContainerBuilder usesParent = ((UsesNodeBuilder) parent).getParent();
638             newPath.addAll(usesParent.getPath().getPath());
639
640             URI ns;
641             Date revision;
642             String prefix;
643             QName baseQName = usesParent.getQName();
644             if (baseQName == null) {
645                 ModuleBuilder m = ParserUtils.getParentModule(usesParent);
646                 ns = m.getNamespace();
647                 revision = m.getRevision();
648                 prefix = m.getPrefix();
649             } else {
650                 ns = baseQName.getNamespace();
651                 revision = baseQName.getRevision();
652                 prefix = baseQName.getPrefix();
653             }
654
655             for (QName qn : oldSchemaPath.getPath()) {
656                 newPath.add(new QName(ns, revision, prefix, qn.getLocalName()));
657             }
658         } else {
659
660             for (QName qn : oldPath) {
661                 URI ns = module.getNamespace();
662                 Date rev = module.getRevision();
663                 String pref = module.getPrefix();
664                 String localPrefix = qn.getPrefix();
665                 if (localPrefix != null && !("".equals(localPrefix))) {
666                     ModuleBuilder currentModule = ParserUtils.findModuleFromBuilders(modules, module, localPrefix,
667                             augment.getLine());
668                     if (currentModule == null) {
669                         Module m = ParserUtils.findModuleFromContext(context, module, localPrefix, augment.getLine());
670                         if (m == null) {
671                             throw new YangParseException(module.getName(), augment.getLine(), "Module with prefix "
672                                     + localPrefix + " not found.");
673                         }
674                         ns = m.getNamespace();
675                         rev = m.getRevision();
676                         pref = m.getPrefix();
677                     } else {
678                         ns = currentModule.getNamespace();
679                         rev = currentModule.getRevision();
680                         pref = currentModule.getPrefix();
681                     }
682                 }
683                 newPath.add(new QName(ns, rev, pref, qn.getLocalName()));
684             }
685         }
686         augment.setTargetNodeSchemaPath(new SchemaPath(newPath, augment.getTargetPath().isAbsolute()));
687
688         for (DataSchemaNodeBuilder childNode : augment.getChildNodeBuilders()) {
689             correctPathForAugmentNodes(childNode, augment.getTargetNodeSchemaPath());
690         }
691     }
692
693     private void correctPathForAugmentNodes(DataSchemaNodeBuilder node, SchemaPath parentPath) {
694         SchemaPath newPath = ParserUtils.createSchemaPath(parentPath, node.getQName());
695         node.setPath(newPath);
696         if (node instanceof DataNodeContainerBuilder) {
697             for (DataSchemaNodeBuilder child : ((DataNodeContainerBuilder) node).getChildNodeBuilders()) {
698                 correctPathForAugmentNodes(child, node.getPath());
699             }
700         }
701         if (node instanceof ChoiceBuilder) {
702             for (ChoiceCaseBuilder child : ((ChoiceBuilder)node).getCases()) {
703                 correctPathForAugmentNodes(child, node.getPath());
704             }
705         }
706     }
707
708     /**
709      * Check augments for mandatory nodes. If the target node is in another
710      * module, then nodes added by the augmentation MUST NOT be mandatory nodes.
711      * If mandatory node is found, throw an exception.
712      *
713      * @param augments
714      *            augments to check
715      */
716     private void checkAugmentMandatoryNodes(Collection<AugmentationSchemaBuilder> augments) {
717         for (AugmentationSchemaBuilder augment : augments) {
718             String augmentPrefix = augment.getTargetPath().getPath().get(0).getPrefix();
719             ModuleBuilder module = ParserUtils.getParentModule(augment);
720             String modulePrefix = module.getPrefix();
721
722             if (augmentPrefix == null || augmentPrefix.isEmpty() || augmentPrefix.equals(modulePrefix)) {
723                 continue;
724             }
725
726             for (DataSchemaNodeBuilder childNode : augment.getChildNodeBuilders()) {
727                 if (childNode.getConstraints().isMandatory()) {
728                     throw new YangParseException(augment.getModuleName(), augment.getLine(),
729                             "Error in augment parsing: cannot augment mandatory node "
730                                     + childNode.getQName().getLocalName());
731                 }
732             }
733         }
734     }
735
736     /**
737      * Go through all augment definitions and resolve them. This method works in
738      * same way as {@link #resolveAugments(Map)} except that if target node is
739      * not found in loaded modules, it search for target node in given context.
740      *
741      * @param modules
742      *            all loaded modules
743      * @param context
744      *            SchemaContext containing already resolved modules
745      */
746     private void resolveAugments(final Map<String, TreeMap<Date, ModuleBuilder>> modules, final SchemaContext context) {
747         List<ModuleBuilder> all = new ArrayList<>();
748         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
749             for (Map.Entry<Date, ModuleBuilder> inner : entry.getValue().entrySet()) {
750                 all.add(inner.getValue());
751             }
752         }
753
754         List<ModuleBuilder> sorted;
755         if (context == null) {
756             sorted = ModuleDependencySort.sort(all.toArray(new ModuleBuilder[all.size()]));
757         } else {
758             sorted = ModuleDependencySort.sortWithContext(context, all.toArray(new ModuleBuilder[all.size()]));
759         }
760
761         for (ModuleBuilder mb : sorted) {
762             if (mb != null) {
763                 List<AugmentationSchemaBuilder> augments = mb.getAllAugments();
764                 checkAugmentMandatoryNodes(augments);
765                 Collections.sort(augments, Comparators.AUGMENT_COMP);
766                 for (AugmentationSchemaBuilder augment : augments) {
767                     if (!(augment.isResolved())) {
768                         boolean resolved = resolveAugment(augment, mb, modules, context);
769                         if (!resolved) {
770                             throw new YangParseException(augment.getModuleName(), augment.getLine(),
771                                     "Error in augment parsing: failed to find augment target: " + augment);
772                         }
773                     }
774                 }
775             }
776         }
777     }
778
779     private boolean resolveUsesAugment(final AugmentationSchemaBuilder augment, final ModuleBuilder module,
780             final Map<String, TreeMap<Date, ModuleBuilder>> modules, final SchemaContext context) {
781         if (augment.isResolved()) {
782             return true;
783         }
784
785         UsesNodeBuilder usesNode = (UsesNodeBuilder) augment.getParent();
786         DataNodeContainerBuilder parentNode = usesNode.getParent();
787         SchemaNodeBuilder targetNode;
788         if (parentNode instanceof ModuleBuilder) {
789             targetNode = findSchemaNodeInModule(augment.getTargetPath().getPath(), (ModuleBuilder)parentNode);
790         } else {
791             targetNode = findSchemaNode(augment.getTargetPath().getPath(), (SchemaNodeBuilder)parentNode);
792         }
793
794         fillAugmentTarget(augment, targetNode);
795         augment.setResolved(true);
796         return true;
797     }
798
799     private boolean resolveAugment(final AugmentationSchemaBuilder augment, final ModuleBuilder module,
800             final Map<String, TreeMap<Date, ModuleBuilder>> modules, final SchemaContext context) {
801         if (augment.isResolved()) {
802             return true;
803         }
804
805         List<QName> targetPath = augment.getTargetPath().getPath();
806         ModuleBuilder targetModule = findTargetModule(targetPath.get(0), module, modules, context, augment.getLine());
807         if (targetModule == null) {
808             throw new YangParseException(module.getModuleName(), augment.getLine(), "Failed to resolve augment "
809                     + augment);
810         }
811
812         return processAugmentation(augment, targetModule);
813     }
814
815     /**
816      * Find module from loaded modules or from context based on given qname. If
817      * module is found in context, create wrapper over this module and add it to
818      * collection of loaded modules.
819      *
820      * @param qname
821      * @param module
822      *            current module
823      * @param modules
824      *            all loaded modules
825      * @param context
826      *            schema context
827      * @param line
828      *            current line
829      * @return
830      */
831     private ModuleBuilder findTargetModule(final QName qname, final ModuleBuilder module,
832             final Map<String, TreeMap<Date, ModuleBuilder>> modules, final SchemaContext context, final int line) {
833         ModuleBuilder targetModule = null;
834
835         String prefix = qname.getPrefix();
836         if (prefix == null || prefix.equals("")) {
837             targetModule = module;
838         } else {
839             targetModule = findModuleFromBuilders(modules, module, qname.getPrefix(), line);
840         }
841
842         if (targetModule == null && context != null) {
843             Module m = findModuleFromContext(context, module, prefix, line);
844             targetModule = new ModuleBuilder(m);
845             DataSchemaNode firstNode = m.getDataChildByName(qname.getLocalName());
846             DataSchemaNodeBuilder firstNodeWrapped = wrapChildNode(targetModule.getModuleName(), line, firstNode,
847                     targetModule.getPath(), firstNode.getQName());
848             targetModule.addChildNode(firstNodeWrapped);
849
850             TreeMap<Date, ModuleBuilder> map = new TreeMap<>();
851             map.put(targetModule.getRevision(), targetModule);
852             modules.put(targetModule.getModuleName(), map);
853         }
854
855         return targetModule;
856     }
857
858     /**
859      * Go through identity statements defined in current module and resolve
860      * their 'base' statement if present.
861      *
862      * @param modules
863      *            all modules
864      * @param module
865      *            module being resolved
866      */
867     private void resolveIdentities(final Map<String, TreeMap<Date, ModuleBuilder>> modules, final ModuleBuilder module) {
868         final Set<IdentitySchemaNodeBuilder> identities = module.getAddedIdentities();
869         for (IdentitySchemaNodeBuilder identity : identities) {
870             final String baseIdentityName = identity.getBaseIdentityName();
871             final int line = identity.getLine();
872             if (baseIdentityName != null) {
873                 IdentitySchemaNodeBuilder baseIdentity = findBaseIdentity(modules, module, baseIdentityName, line);
874                 if (baseIdentity == null) {
875                     throw new YangParseException(module.getName(), identity.getLine(), "Failed to find base identity");
876                 } else {
877                     identity.setBaseIdentity(baseIdentity);
878                 }
879             }
880         }
881     }
882
883     /**
884      * Go through identity statements defined in current module and resolve
885      * their 'base' statement. Method tries to find base identity in given
886      * modules. If base identity is not found, method will search it in context.
887      *
888      * @param modules
889      *            all loaded modules
890      * @param module
891      *            current module
892      * @param context
893      *            SchemaContext containing already resolved modules
894      */
895     private void resolveIdentitiesWithContext(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
896             final ModuleBuilder module, final SchemaContext context) {
897         final Set<IdentitySchemaNodeBuilder> identities = module.getAddedIdentities();
898         for (IdentitySchemaNodeBuilder identity : identities) {
899             final String baseIdentityName = identity.getBaseIdentityName();
900             final int line = identity.getLine();
901             if (baseIdentityName != null) {
902                 IdentitySchemaNodeBuilder baseIdentity = findBaseIdentity(modules, module, baseIdentityName, line);
903                 if (baseIdentity == null) {
904                     IdentitySchemaNode baseId = findBaseIdentityFromContext(modules, module, baseIdentityName, line,
905                             context);
906                     identity.setBaseIdentity(baseId);
907                 } else {
908                     identity.setBaseIdentity(baseIdentity);
909                 }
910             }
911         }
912     }
913
914     /**
915      * Find and add reference of uses target grouping.
916      *
917      * @param modules
918      *            all loaded modules
919      * @param context
920      *            SchemaContext containing already resolved modules or null if
921      *            context is not available
922      */
923     private void resolveUsesTargetGrouping(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
924             final SchemaContext context) {
925         final List<UsesNodeBuilder> allUses = new ArrayList<>();
926         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
927             for (Map.Entry<Date, ModuleBuilder> inner : entry.getValue().entrySet()) {
928                 allUses.addAll(inner.getValue().getAllUsesNodes());
929             }
930         }
931         for (UsesNodeBuilder usesNode : allUses) {
932             ModuleBuilder module = ParserUtils.getParentModule(usesNode);
933             final GroupingBuilder targetGroupingBuilder = GroupingUtils.getTargetGroupingFromModules(usesNode, modules,
934                     module);
935             if (targetGroupingBuilder == null) {
936                 if (context == null) {
937                     throw new YangParseException(module.getName(), usesNode.getLine(), "Referenced grouping '"
938                             + usesNode.getGroupingPathAsString() + "' not found.");
939                 } else {
940                     GroupingDefinition targetGroupingDefinition = GroupingUtils.getTargetGroupingFromContext(usesNode,
941                             module, context);
942                     usesNode.setGroupingDefinition(targetGroupingDefinition);
943                 }
944             } else {
945                 usesNode.setGrouping(targetGroupingBuilder);
946             }
947         }
948     }
949
950     private void resolveUsesForGroupings(final Map<String, TreeMap<Date, ModuleBuilder>> modules, final SchemaContext context) {
951         final Set<GroupingBuilder> allGroupings = new HashSet<>();
952         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
953             for (Map.Entry<Date, ModuleBuilder> inner : entry.getValue().entrySet()) {
954                 ModuleBuilder module = inner.getValue();
955                 allGroupings.addAll(module.getAllGroupings());
956             }
957         }
958         final List<GroupingBuilder> sorted = GroupingSort.sort(allGroupings);
959         for (GroupingBuilder gb : sorted) {
960             List<UsesNodeBuilder> usesNodes = new ArrayList<>(GroupingSort.getAllUsesNodes(gb));
961             Collections.sort(usesNodes, new GroupingUtils.UsesComparator());
962             for (UsesNodeBuilder usesNode : usesNodes) {
963                 resolveUses(usesNode, modules, context);
964             }
965         }
966     }
967
968     private void resolveUsesForNodes(final Map<String, TreeMap<Date, ModuleBuilder>> modules, final SchemaContext context) {
969         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
970             for (Map.Entry<Date, ModuleBuilder> inner : entry.getValue().entrySet()) {
971                 ModuleBuilder module = inner.getValue();
972                 List<UsesNodeBuilder> usesNodes = module.getAllUsesNodes();
973                 Collections.sort(usesNodes, new GroupingUtils.UsesComparator());
974                 for (UsesNodeBuilder usesNode : usesNodes) {
975                     resolveUses(usesNode, modules, context);
976                 }
977             }
978         }
979     }
980
981     private void resolveUses(UsesNodeBuilder usesNode,
982             final Map<String, TreeMap<Date, ModuleBuilder>> modules, final SchemaContext context) {
983         if (!usesNode.isResolved()) {
984             DataNodeContainerBuilder parent = usesNode.getParent();
985             ModuleBuilder module = ParserUtils.getParentModule(parent);
986             GroupingBuilder target = GroupingUtils.getTargetGroupingFromModules(usesNode, modules, module);
987             if (target == null) {
988                 resolveUsesWithContext(usesNode);
989                 usesNode.setResolved(true);
990                 for (AugmentationSchemaBuilder augment : usesNode.getAugmentations()) {
991                     resolveUsesAugment(augment, module, modules, context);
992                 }
993             } else {
994                 parent.getChildNodeBuilders().addAll(target.instantiateChildNodes(parent));
995                 parent.getTypeDefinitionBuilders().addAll(target.instantiateTypedefs(parent));
996                 parent.getGroupingBuilders().addAll(target.instantiateGroupings(parent));
997                 parent.getUnknownNodes().addAll(target.instantiateUnknownNodes(parent));
998                 usesNode.setResolved(true);
999                 for (AugmentationSchemaBuilder augment : usesNode.getAugmentations()) {
1000                     resolveUsesAugment(augment, module, modules, context);
1001                 }
1002             }
1003             GroupingUtils.performRefine(usesNode);
1004         }
1005     }
1006
1007     private void resolveUsesWithContext(UsesNodeBuilder usesNode) {
1008         final int line = usesNode.getLine();
1009         DataNodeContainerBuilder parent = usesNode.getParent();
1010         ModuleBuilder module = ParserUtils.getParentModule(parent);
1011         SchemaPath parentPath;
1012         URI ns = null;
1013         Date rev = null;
1014         String pref = null;
1015         if (parent instanceof AugmentationSchemaBuilder || parent instanceof ModuleBuilder) {
1016             ns = module.getNamespace();
1017             rev = module.getRevision();
1018             pref = module.getPrefix();
1019             if (parent instanceof AugmentationSchemaBuilder) {
1020                 parentPath = ((AugmentationSchemaBuilder)parent).getTargetNodeSchemaPath();
1021             } else {
1022                 parentPath = ((ModuleBuilder)parent).getPath();
1023             }
1024         } else {
1025             ns = ((DataSchemaNodeBuilder) parent).getQName().getNamespace();
1026             rev = ((DataSchemaNodeBuilder) parent).getQName().getRevision();
1027             pref = ((DataSchemaNodeBuilder) parent).getQName().getPrefix();
1028             parentPath = ((DataSchemaNodeBuilder)parent).getPath();
1029         }
1030
1031         GroupingDefinition gd = usesNode.getGroupingDefinition();
1032
1033         Set<DataSchemaNodeBuilder> childNodes = wrapChildNodes(module.getModuleName(), line,
1034                 gd.getChildNodes(), parentPath, ns, rev, pref);
1035         parent.getChildNodeBuilders().addAll(childNodes);
1036         for (DataSchemaNodeBuilder childNode : childNodes) {
1037             setNodeAddedByUses(childNode);
1038         }
1039
1040         Set<TypeDefinitionBuilder> typedefs = wrapTypedefs(module.getModuleName(), line, gd, parentPath, ns,
1041                 rev, pref);
1042         parent.getTypeDefinitionBuilders().addAll(typedefs);
1043         for (TypeDefinitionBuilder typedef : typedefs) {
1044             setNodeAddedByUses(typedef);
1045         }
1046
1047         Set<GroupingBuilder> groupings = wrapGroupings(module.getModuleName(), line, usesNode
1048                 .getGroupingDefinition().getGroupings(), parentPath, ns, rev, pref);
1049         parent.getGroupingBuilders().addAll(groupings);
1050         for (GroupingBuilder gb : groupings) {
1051             setNodeAddedByUses(gb);
1052         }
1053
1054         List<UnknownSchemaNodeBuilder> unknownNodes = wrapUnknownNodes(module.getModuleName(), line,
1055                 gd.getUnknownSchemaNodes(), parentPath, ns, rev, pref);
1056         parent.getUnknownNodes().addAll(unknownNodes);
1057         for (UnknownSchemaNodeBuilder un : unknownNodes) {
1058             un.setAddedByUses(true);
1059         }
1060     }
1061
1062     private void resolveUnknownNodes(final Map<String, TreeMap<Date, ModuleBuilder>> modules, final ModuleBuilder module) {
1063         for (UnknownSchemaNodeBuilder usnb : module.getAllUnknownNodes()) {
1064             QName nodeType = usnb.getNodeType();
1065             try {
1066                 ModuleBuilder dependentModule = findModuleFromBuilders(modules, module, nodeType.getPrefix(),
1067                         usnb.getLine());
1068                 for (ExtensionBuilder extension : dependentModule.getAddedExtensions()) {
1069                     if (extension.getQName().getLocalName().equals(nodeType.getLocalName())) {
1070                         usnb.setNodeType(extension.getQName());
1071                         usnb.setExtensionBuilder(extension);
1072                         break;
1073                     }
1074                 }
1075             } catch (YangParseException e) {
1076                 throw new YangParseException(module.getName(), usnb.getLine(), "Failed to resolve node " + usnb
1077                         + ": no such extension definition found.", e);
1078             }
1079         }
1080     }
1081
1082     private void resolveUnknownNodesWithContext(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
1083             final ModuleBuilder module, final SchemaContext context) {
1084         for (UnknownSchemaNodeBuilder usnb : module.getAllUnknownNodes()) {
1085             QName nodeType = usnb.getNodeType();
1086             try {
1087                 ModuleBuilder dependentModuleBuilder = findModuleFromBuilders(modules, module, nodeType.getPrefix(),
1088                         usnb.getLine());
1089
1090                 if (dependentModuleBuilder == null) {
1091                     Module dependentModule = findModuleFromContext(context, module, nodeType.getPrefix(),
1092                             usnb.getLine());
1093                     for (ExtensionDefinition e : dependentModule.getExtensionSchemaNodes()) {
1094                         if (e.getQName().getLocalName().equals(nodeType.getLocalName())) {
1095                             usnb.setNodeType(new QName(e.getQName().getNamespace(), e.getQName().getRevision(),
1096                                     nodeType.getPrefix(), e.getQName().getLocalName()));
1097                             usnb.setExtensionDefinition(e);
1098                             break;
1099                         }
1100                     }
1101                 } else {
1102                     for (ExtensionBuilder extension : dependentModuleBuilder.getAddedExtensions()) {
1103                         if (extension.getQName().getLocalName().equals(nodeType.getLocalName())) {
1104                             usnb.setExtensionBuilder(extension);
1105                             break;
1106                         }
1107                     }
1108                 }
1109
1110             } catch (YangParseException e) {
1111                 throw new YangParseException(module.getName(), usnb.getLine(), "Failed to resolve node " + usnb
1112                         + ": no such extension definition found.", e);
1113             }
1114
1115         }
1116     }
1117
1118     /**
1119      * Traverse through modules and resolve their deviation statements.
1120      *
1121      * @param modules
1122      *            all loaded modules
1123      */
1124     private void resolveDeviations(final Map<String, TreeMap<Date, ModuleBuilder>> modules) {
1125         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
1126             for (Map.Entry<Date, ModuleBuilder> inner : entry.getValue().entrySet()) {
1127                 ModuleBuilder b = inner.getValue();
1128                 resolveDeviation(modules, b);
1129             }
1130         }
1131     }
1132
1133     /**
1134      * Traverse through module and resolve its deviation statements.
1135      *
1136      * @param modules
1137      *            all loaded modules
1138      * @param module
1139      *            module in which resolve deviations
1140      */
1141     private void resolveDeviation(final Map<String, TreeMap<Date, ModuleBuilder>> modules, final ModuleBuilder module) {
1142         for (DeviationBuilder dev : module.getDeviationBuilders()) {
1143             int line = dev.getLine();
1144             SchemaPath targetPath = dev.getTargetPath();
1145             List<QName> path = targetPath.getPath();
1146             QName q0 = path.get(0);
1147             String prefix = q0.getPrefix();
1148             if (prefix == null) {
1149                 prefix = module.getPrefix();
1150             }
1151
1152             ModuleBuilder dependentModuleBuilder = findModuleFromBuilders(modules, module, prefix, line);
1153             processDeviation(dev, dependentModuleBuilder, path, module);
1154         }
1155     }
1156
1157     /**
1158      * Traverse through modules and resolve their deviation statements with
1159      * given context.
1160      *
1161      * @param modules
1162      *            all loaded modules
1163      * @param context
1164      *            already resolved context
1165      */
1166     private void resolveDeviationsWithContext(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
1167             final SchemaContext context) {
1168         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
1169             for (Map.Entry<Date, ModuleBuilder> inner : entry.getValue().entrySet()) {
1170                 ModuleBuilder b = inner.getValue();
1171                 resolveDeviationWithContext(modules, b, context);
1172             }
1173         }
1174     }
1175
1176     /**
1177      * Traverse through module and resolve its deviation statements with given
1178      * context.
1179      *
1180      * @param modules
1181      *            all loaded modules
1182      * @param module
1183      *            module in which resolve deviations
1184      * @param context
1185      *            already resolved context
1186      */
1187     private void resolveDeviationWithContext(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
1188             final ModuleBuilder module, final SchemaContext context) {
1189         for (DeviationBuilder dev : module.getDeviationBuilders()) {
1190             int line = dev.getLine();
1191             SchemaPath targetPath = dev.getTargetPath();
1192             List<QName> path = targetPath.getPath();
1193             QName q0 = path.get(0);
1194             String prefix = q0.getPrefix();
1195             if (prefix == null) {
1196                 prefix = module.getPrefix();
1197             }
1198
1199             ModuleBuilder dependentModuleBuilder = findModuleFromBuilders(modules, module, prefix, line);
1200             if (dependentModuleBuilder == null) {
1201                 Object currentParent = findModuleFromContext(context, module, prefix, line);
1202
1203                 for (QName q : path) {
1204                     if (currentParent == null) {
1205                         throw new YangParseException(module.getName(), line, FAIL_DEVIATION_TARGET);
1206                     }
1207                     String name = q.getLocalName();
1208                     if (currentParent instanceof DataNodeContainer) {
1209                         currentParent = ((DataNodeContainer) currentParent).getDataChildByName(name);
1210                     }
1211                 }
1212
1213                 if (currentParent == null) {
1214                     throw new YangParseException(module.getName(), line, FAIL_DEVIATION_TARGET);
1215                 }
1216                 if (currentParent instanceof SchemaNode) {
1217                     dev.setTargetPath(((SchemaNode) currentParent).getPath());
1218                 }
1219
1220             } else {
1221                 processDeviation(dev, dependentModuleBuilder, path, module);
1222             }
1223         }
1224     }
1225
1226     /**
1227      * Correct deviation target path in deviation builder.
1228      *
1229      * @param dev
1230      *            deviation
1231      * @param dependentModuleBuilder
1232      *            module containing deviation target
1233      * @param path
1234      *            current deviation target path
1235      * @param module
1236      *            current module
1237      */
1238     private void processDeviation(final DeviationBuilder dev, final ModuleBuilder dependentModuleBuilder,
1239             final List<QName> path, final ModuleBuilder module) {
1240         final int line = dev.getLine();
1241         Builder currentParent = dependentModuleBuilder;
1242
1243         for (QName q : path) {
1244             if (currentParent == null) {
1245                 throw new YangParseException(module.getName(), line, FAIL_DEVIATION_TARGET);
1246             }
1247             String name = q.getLocalName();
1248             if (currentParent instanceof DataNodeContainerBuilder) {
1249                 currentParent = ((DataNodeContainerBuilder) currentParent).getDataChildByName(name);
1250             }
1251         }
1252
1253         if (!(currentParent instanceof SchemaNodeBuilder)) {
1254             throw new YangParseException(module.getName(), line, FAIL_DEVIATION_TARGET);
1255         }
1256         dev.setTargetPath(((SchemaNodeBuilder) currentParent).getPath());
1257     }
1258
1259 }