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