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