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