Refactored base yang-java types.
[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.*;
12
13 import java.io.File;
14 import java.io.FileInputStream;
15 import java.io.FileNotFoundException;
16 import java.io.IOException;
17 import java.io.InputStream;
18 import java.util.ArrayList;
19 import java.util.Collections;
20 import java.util.Date;
21 import java.util.HashMap;
22 import java.util.LinkedHashMap;
23 import java.util.LinkedHashSet;
24 import java.util.List;
25 import java.util.Map;
26 import java.util.Map.Entry;
27 import java.util.Set;
28 import java.util.TreeMap;
29
30 import org.antlr.v4.runtime.ANTLRInputStream;
31 import org.antlr.v4.runtime.CommonTokenStream;
32 import org.antlr.v4.runtime.tree.ParseTree;
33 import org.antlr.v4.runtime.tree.ParseTreeWalker;
34 import org.opendaylight.yangtools.antlrv4.code.gen.YangLexer;
35 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser;
36 import org.opendaylight.yangtools.yang.common.QName;
37 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
38 import org.opendaylight.yangtools.yang.model.api.ExtensionDefinition;
39 import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
40 import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
41 import org.opendaylight.yangtools.yang.model.api.Module;
42 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
43 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
44 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
45 import org.opendaylight.yangtools.yang.model.parser.api.YangModelParser;
46 import org.opendaylight.yangtools.yang.model.util.IdentityrefType;
47 import org.opendaylight.yangtools.yang.parser.builder.api.AugmentationSchemaBuilder;
48 import org.opendaylight.yangtools.yang.parser.builder.api.Builder;
49 import org.opendaylight.yangtools.yang.parser.builder.api.DataNodeContainerBuilder;
50 import org.opendaylight.yangtools.yang.parser.builder.api.GroupingBuilder;
51 import org.opendaylight.yangtools.yang.parser.builder.api.SchemaNodeBuilder;
52 import org.opendaylight.yangtools.yang.parser.builder.api.TypeAwareBuilder;
53 import org.opendaylight.yangtools.yang.parser.builder.api.UsesNodeBuilder;
54 import org.opendaylight.yangtools.yang.parser.builder.impl.DeviationBuilder;
55 import org.opendaylight.yangtools.yang.parser.builder.impl.ExtensionBuilder;
56 import org.opendaylight.yangtools.yang.parser.builder.impl.IdentitySchemaNodeBuilder;
57 import org.opendaylight.yangtools.yang.parser.builder.impl.IdentityrefTypeBuilder;
58 import org.opendaylight.yangtools.yang.parser.builder.impl.ModuleBuilder;
59 import org.opendaylight.yangtools.yang.parser.builder.impl.UnionTypeBuilder;
60 import org.opendaylight.yangtools.yang.parser.builder.impl.UnknownSchemaNodeBuilder;
61 import org.opendaylight.yangtools.yang.parser.util.GroupingUtils;
62 import org.opendaylight.yangtools.yang.parser.util.ModuleDependencySort;
63 import org.opendaylight.yangtools.yang.parser.util.ParserUtils;
64 import org.opendaylight.yangtools.yang.parser.util.YangParseException;
65 import org.opendaylight.yangtools.yang.validator.YangModelBasicValidator;
66 import org.slf4j.Logger;
67 import org.slf4j.LoggerFactory;
68
69 import com.google.common.collect.Lists;
70 import com.google.common.collect.Maps;
71 import com.google.common.collect.Sets;
72
73 public final class YangParserImpl implements YangModelParser {
74     private static final Logger LOG = LoggerFactory.getLogger(YangParserImpl.class);
75
76     @Override
77     public Set<Module> parseYangModels(final List<File> yangFiles) {
78         return Sets.newLinkedHashSet(parseYangModelsMapped(yangFiles).values());
79     }
80
81     @Override
82     public Set<Module> parseYangModels(final List<File> yangFiles, final SchemaContext context) {
83         if (yangFiles != null) {
84             final Map<InputStream, File> inputStreams = Maps.newHashMap();
85
86             for (final File yangFile : yangFiles) {
87                 try {
88                     inputStreams.put(new FileInputStream(yangFile), yangFile);
89                 } catch (FileNotFoundException e) {
90                     LOG.warn("Exception while reading yang file: " + yangFile.getName(), e);
91                 }
92             }
93
94             Map<ModuleBuilder, InputStream> builderToStreamMap = Maps.newHashMap();
95
96             final Map<String, TreeMap<Date, ModuleBuilder>> modules = resolveModuleBuilders(
97                     Lists.newArrayList(inputStreams.keySet()), builderToStreamMap);
98
99             for (InputStream is : inputStreams.keySet()) {
100                 try {
101                     is.close();
102                 } catch (IOException e) {
103                     LOG.debug("Failed to close stream.");
104                 }
105             }
106
107             return new LinkedHashSet<Module>(buildWithContext(modules, context).values());
108         }
109         return Collections.emptySet();
110     }
111
112     @Override
113     public Set<Module> parseYangModelsFromStreams(final List<InputStream> yangModelStreams) {
114         return Sets.newHashSet(parseYangModelsFromStreamsMapped(yangModelStreams).values());
115     }
116
117     @Override
118     public Set<Module> parseYangModelsFromStreams(final List<InputStream> yangModelStreams, SchemaContext context) {
119         if (yangModelStreams != null) {
120             Map<ModuleBuilder, InputStream> builderToStreamMap = Maps.newHashMap();
121             final Map<String, TreeMap<Date, ModuleBuilder>> modules = resolveModuleBuildersWithContext(
122                     yangModelStreams, builderToStreamMap, context);
123             return new LinkedHashSet<Module>(buildWithContext(modules, context).values());
124         }
125         return Collections.emptySet();
126     }
127
128     @Override
129     public Map<File, Module> parseYangModelsMapped(List<File> yangFiles) {
130         if (yangFiles != null) {
131             final Map<InputStream, File> inputStreams = Maps.newHashMap();
132
133             for (final File yangFile : yangFiles) {
134                 try {
135                     inputStreams.put(new FileInputStream(yangFile), yangFile);
136                 } catch (FileNotFoundException e) {
137                     LOG.warn("Exception while reading yang file: " + yangFile.getName(), e);
138                 }
139             }
140
141             Map<ModuleBuilder, InputStream> builderToStreamMap = Maps.newHashMap();
142             final Map<String, TreeMap<Date, ModuleBuilder>> modules = resolveModuleBuilders(
143                     Lists.newArrayList(inputStreams.keySet()), builderToStreamMap);
144
145             for (InputStream is : inputStreams.keySet()) {
146                 try {
147                     is.close();
148                 } catch (IOException e) {
149                     LOG.debug("Failed to close stream.");
150                 }
151             }
152
153             Map<File, Module> retVal = Maps.newLinkedHashMap();
154             Map<ModuleBuilder, Module> builderToModuleMap = build(modules);
155
156             for (Entry<ModuleBuilder, Module> builderToModule : builderToModuleMap.entrySet()) {
157                 retVal.put(inputStreams.get(builderToStreamMap.get(builderToModule.getKey())),
158                         builderToModule.getValue());
159             }
160
161             return retVal;
162         }
163         return Collections.emptyMap();
164     }
165
166     @Override
167     public Map<InputStream, Module> parseYangModelsFromStreamsMapped(final List<InputStream> yangModelStreams) {
168         Map<ModuleBuilder, InputStream> builderToStreamMap = Maps.newHashMap();
169
170         final Map<String, TreeMap<Date, ModuleBuilder>> modules = resolveModuleBuilders(yangModelStreams,
171                 builderToStreamMap);
172         Map<InputStream, Module> retVal = Maps.newLinkedHashMap();
173         Map<ModuleBuilder, Module> builderToModuleMap = build(modules);
174
175         for (Entry<ModuleBuilder, Module> builderToModule : builderToModuleMap.entrySet()) {
176             retVal.put(builderToStreamMap.get(builderToModule.getKey()), builderToModule.getValue());
177         }
178         return retVal;
179     }
180
181     @Override
182     public SchemaContext resolveSchemaContext(final Set<Module> modules) {
183         return new SchemaContextImpl(modules);
184     }
185
186     private ModuleBuilder[] parseModuleBuilders(List<InputStream> inputStreams,
187             Map<ModuleBuilder, InputStream> streamToBuilderMap) {
188
189         final ParseTreeWalker walker = new ParseTreeWalker();
190         final List<ParseTree> trees = parseStreams(inputStreams);
191         final ModuleBuilder[] builders = new ModuleBuilder[trees.size()];
192
193         // validate yang
194         new YangModelBasicValidator(walker).validate(trees);
195
196         YangParserListenerImpl yangModelParser = null;
197         for (int i = 0; i < trees.size(); i++) {
198             yangModelParser = new YangParserListenerImpl();
199             walker.walk(yangModelParser, trees.get(i));
200             ModuleBuilder moduleBuilder = yangModelParser.getModuleBuilder();
201
202             // We expect the order of trees and streams has to be the same
203             streamToBuilderMap.put(moduleBuilder, inputStreams.get(i));
204             builders[i] = moduleBuilder;
205         }
206         return builders;
207     }
208
209     private Map<String, TreeMap<Date, ModuleBuilder>> resolveModuleBuilders(final List<InputStream> yangFileStreams,
210             Map<ModuleBuilder, InputStream> streamToBuilderMap) {
211         return resolveModuleBuildersWithContext(yangFileStreams, streamToBuilderMap, null);
212     }
213
214     private Map<String, TreeMap<Date, ModuleBuilder>> resolveModuleBuildersWithContext(
215             final List<InputStream> yangFileStreams, final Map<ModuleBuilder, InputStream> streamToBuilderMap,
216             final SchemaContext context) {
217         final ModuleBuilder[] builders = parseModuleBuilders(yangFileStreams, streamToBuilderMap);
218
219         // LinkedHashMap must be used to preserve order
220         final LinkedHashMap<String, TreeMap<Date, ModuleBuilder>> modules = new LinkedHashMap<String, TreeMap<Date, ModuleBuilder>>();
221
222         // module dependency graph sorted
223         List<ModuleBuilder> sorted = null;
224         if (context == null) {
225             sorted = ModuleDependencySort.sort(builders);
226         } else {
227             sorted = ModuleDependencySort.sortWithContext(context, builders);
228         }
229
230         for (final ModuleBuilder builder : sorted) {
231             if (builder == null) {
232                 continue;
233             }
234             final String builderName = builder.getName();
235             Date builderRevision = builder.getRevision();
236             if (builderRevision == null) {
237                 builderRevision = new Date(0L);
238             }
239             TreeMap<Date, ModuleBuilder> builderByRevision = modules.get(builderName);
240             if (builderByRevision == null) {
241                 builderByRevision = new TreeMap<Date, ModuleBuilder>();
242             }
243             builderByRevision.put(builderRevision, builder);
244             modules.put(builderName, builderByRevision);
245         }
246         return modules;
247     }
248
249     private List<ParseTree> parseStreams(final List<InputStream> yangStreams) {
250         final List<ParseTree> trees = new ArrayList<ParseTree>();
251         for (InputStream yangStream : yangStreams) {
252             trees.add(parseStream(yangStream));
253         }
254         return trees;
255     }
256
257     private ParseTree parseStream(final InputStream yangStream) {
258         ParseTree result = null;
259         try {
260             final ANTLRInputStream input = new ANTLRInputStream(yangStream);
261             final YangLexer lexer = new YangLexer(input);
262             final CommonTokenStream tokens = new CommonTokenStream(lexer);
263             final YangParser parser = new YangParser(tokens);
264             parser.removeErrorListeners();
265             parser.addErrorListener(new YangErrorListener());
266
267             result = parser.yang();
268         } catch (IOException e) {
269             LOG.warn("Exception while reading yang file: " + yangStream, e);
270         }
271         return result;
272     }
273
274     private Map<ModuleBuilder, Module> build(final Map<String, TreeMap<Date, ModuleBuilder>> modules) {
275         findUsesTargets(modules, null);
276
277         // fix unresolved nodes
278         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
279             for (Map.Entry<Date, ModuleBuilder> childEntry : entry.getValue().entrySet()) {
280                 final ModuleBuilder moduleBuilder = childEntry.getValue();
281                 fixUnresolvedNodes(modules, moduleBuilder);
282             }
283         }
284         resolveAugments(modules);
285         resolveUses(modules);
286         resolveDeviations(modules);
287
288         // build
289         final Map<ModuleBuilder, Module> result = new LinkedHashMap<ModuleBuilder, Module>();
290         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
291             final Map<Date, Module> modulesByRevision = new HashMap<Date, Module>();
292             for (Map.Entry<Date, ModuleBuilder> childEntry : entry.getValue().entrySet()) {
293                 final ModuleBuilder moduleBuilder = childEntry.getValue();
294                 final Module module = moduleBuilder.build();
295                 modulesByRevision.put(childEntry.getKey(), module);
296                 result.put(moduleBuilder, module);
297             }
298         }
299         return result;
300     }
301
302     private Map<ModuleBuilder, Module> buildWithContext(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
303             SchemaContext context) {
304         findUsesTargets(modules, context);
305
306         // fix unresolved nodes
307         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
308             for (Map.Entry<Date, ModuleBuilder> childEntry : entry.getValue().entrySet()) {
309                 final ModuleBuilder moduleBuilder = childEntry.getValue();
310                 fixUnresolvedNodesWithContext(modules, moduleBuilder, context);
311             }
312         }
313         resolveAugmentsWithContext(modules, context);
314         resolveUsesWithContext(modules, context);
315         resolveDeviationsWithContext(modules, context);
316
317         // build
318         final Map<ModuleBuilder, Module> result = new LinkedHashMap<ModuleBuilder, Module>();
319         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
320             final Map<Date, Module> modulesByRevision = new HashMap<Date, Module>();
321             for (Map.Entry<Date, ModuleBuilder> childEntry : entry.getValue().entrySet()) {
322                 final ModuleBuilder moduleBuilder = childEntry.getValue();
323                 final Module module = moduleBuilder.build();
324                 modulesByRevision.put(childEntry.getKey(), module);
325                 result.put(moduleBuilder, module);
326             }
327         }
328         return result;
329     }
330
331     private void fixUnresolvedNodes(final Map<String, TreeMap<Date, ModuleBuilder>> modules, final ModuleBuilder builder) {
332         resolveDirtyNodes(modules, builder);
333         resolveIdentities(modules, builder);
334         resolveUnknownNodes(modules, builder);
335     }
336
337     private void fixUnresolvedNodesWithContext(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
338             final ModuleBuilder builder, final SchemaContext context) {
339         resolveDirtyNodesWithContext(modules, builder, context);
340         resolveIdentitiesWithContext(modules, builder, context);
341         resolveUnknownNodesWithContext(modules, builder, context);
342     }
343
344     /**
345      * Search for dirty nodes (node which contains UnknownType) and resolve
346      * unknown types.
347      *
348      * @param modules
349      *            all available modules
350      * @param module
351      *            current module
352      */
353     private void resolveDirtyNodes(final Map<String, TreeMap<Date, ModuleBuilder>> modules, final ModuleBuilder module) {
354         final Set<TypeAwareBuilder> dirtyNodes = module.getDirtyNodes();
355         if (!dirtyNodes.isEmpty()) {
356             for (TypeAwareBuilder nodeToResolve : dirtyNodes) {
357                 if (nodeToResolve instanceof UnionTypeBuilder) {
358                     // special handling for union types
359                     resolveTypeUnion((UnionTypeBuilder) nodeToResolve, modules, module);
360                 } else if (nodeToResolve.getTypedef() instanceof IdentityrefTypeBuilder) {
361                     // special handling for identityref types
362                     IdentityrefTypeBuilder idref = (IdentityrefTypeBuilder) nodeToResolve.getTypedef();
363                     nodeToResolve.setType(new IdentityrefType(findFullQName(modules, module, idref), idref.getPath()));
364                 } else {
365                     resolveType(nodeToResolve, modules, module);
366                 }
367             }
368         }
369     }
370
371     private void resolveDirtyNodesWithContext(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
372             final ModuleBuilder module, SchemaContext context) {
373         final Set<TypeAwareBuilder> dirtyNodes = module.getDirtyNodes();
374         if (!dirtyNodes.isEmpty()) {
375             for (TypeAwareBuilder nodeToResolve : dirtyNodes) {
376                 if (nodeToResolve instanceof UnionTypeBuilder) {
377                     // special handling for union types
378                     resolveTypeUnionWithContext((UnionTypeBuilder) nodeToResolve, modules, module, context);
379                 } else if (nodeToResolve.getTypedef() instanceof IdentityrefTypeBuilder) {
380                     // special handling for identityref types
381                     IdentityrefTypeBuilder idref = (IdentityrefTypeBuilder) nodeToResolve.getTypedef();
382                     nodeToResolve.setType(new IdentityrefType(findFullQName(modules, module, idref), idref.getPath()));
383                 } else {
384                     resolveTypeWithContext(nodeToResolve, modules, module, context);
385                 }
386             }
387         }
388     }
389
390     /**
391      * Go through all augment definitions and perform augmentation. It is
392      * expected that modules are already sorted by their dependencies.
393      *
394      * @param modules
395      *            all loaded modules
396      */
397     private void resolveAugments(final Map<String, TreeMap<Date, ModuleBuilder>> modules) {
398         // collect augments from all loaded modules
399         final List<AugmentationSchemaBuilder> allAugments = new ArrayList<>();
400         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
401             for (Map.Entry<Date, ModuleBuilder> inner : entry.getValue().entrySet()) {
402                 allAugments.addAll(inner.getValue().getAllAugments());
403             }
404         }
405
406         for (int i = 0; i < allAugments.size(); i++) {
407             // pick one augment
408             final AugmentationSchemaBuilder augment = allAugments.get(i);
409             // create collection of others
410             List<AugmentationSchemaBuilder> others = new ArrayList<>(allAugments);
411             others.remove(augment);
412
413             // try to resolve it
414             boolean resolved = resolveAugment(modules, augment);
415             // while not resolved
416             int j = 0;
417             while (!(resolved) && j < others.size()) {
418                 // try to resolve next augment
419                 resolveAugment(modules, others.get(j));
420                 // then try to resolve first again
421                 resolved = resolveAugment(modules, augment);
422                 j++;
423
424             }
425
426             if (!resolved) {
427                 throw new YangParseException(augment.getModuleName(), augment.getLine(),
428                         "Error in augment parsing: failed to find augment target");
429             }
430         }
431     }
432
433     /**
434      * Search for augment target and perform augmentation.
435      *
436      * @param modules
437      *            all loaded modules
438      * @param augmentBuilder
439      *            augment to resolve
440      * @return true if target node found, false otherwise
441      */
442     private boolean resolveAugment(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
443             final AugmentationSchemaBuilder augmentBuilder) {
444         if (augmentBuilder.isResolved()) {
445             return true;
446         }
447
448         int line = augmentBuilder.getLine();
449         ModuleBuilder module = getParentModule(augmentBuilder);
450         List<QName> path = augmentBuilder.getTargetPath().getPath();
451         Builder augmentParent = augmentBuilder.getParent();
452
453         Builder firstNodeParent = null;
454         if (augmentParent instanceof ModuleBuilder) {
455             // if augment is defined under module, parent of first node is
456             // target module
457             final QName firstNameInPath = path.get(0);
458             String prefix = firstNameInPath.getPrefix();
459             if (prefix == null) {
460                 prefix = module.getPrefix();
461             }
462             firstNodeParent = findDependentModuleBuilder(modules, module, prefix, line);
463         } else if (augmentParent instanceof UsesNodeBuilder) {
464             firstNodeParent = augmentParent.getParent();
465         } else {
466             // augment can be defined only under module or uses
467             throw new YangParseException(augmentBuilder.getModuleName(), line,
468                     "Failed to parse augment: Unresolved parent of augment: " + augmentParent);
469         }
470
471         return processAugmentation(augmentBuilder, firstNodeParent, path);
472     }
473
474     /**
475      * Go through all augment definitions and resolve them. This method works in
476      * same way as {@link #resolveAugments(Map)} except that if target node is
477      * not found in loaded modules, it search for target node in given context.
478      *
479      * @param modules
480      *            all loaded modules
481      * @param context
482      *            SchemaContext containing already resolved modules
483      */
484     private void resolveAugmentsWithContext(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
485             final SchemaContext context) {
486         // collect augments from all loaded modules
487         final List<AugmentationSchemaBuilder> allAugments = new ArrayList<>();
488         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
489             for (Map.Entry<Date, ModuleBuilder> inner : entry.getValue().entrySet()) {
490                 allAugments.addAll(inner.getValue().getAllAugments());
491             }
492         }
493
494         for (int i = 0; i < allAugments.size(); i++) {
495             // pick augment from list
496             final AugmentationSchemaBuilder augment = allAugments.get(i);
497             // try to resolve it
498             boolean resolved = resolveAugmentWithContext(modules, augment, context);
499             // while not resolved
500             int j = i + 1;
501             while (!(resolved) && j < allAugments.size()) {
502                 // try to resolve next augment
503                 resolveAugmentWithContext(modules, allAugments.get(j), context);
504                 // then try to resolve first again
505                 resolved = resolveAugmentWithContext(modules, augment, context);
506                 j++;
507             }
508
509             if (!resolved) {
510                 throw new YangParseException(augment.getModuleName(), augment.getLine(),
511                         "Error in augment parsing: failed to find augment target");
512             }
513         }
514     }
515
516     /**
517      * Search for augment target and perform augmentation.
518      *
519      * @param modules
520      *            all loaded modules
521      * @param augment
522      *            augment to resolve
523      * @param context
524      *            SchemaContext containing already resolved modules
525      * @return true if target node found, false otherwise
526      */
527     private boolean resolveAugmentWithContext(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
528             final AugmentationSchemaBuilder augment, final SchemaContext context) {
529         if (augment.isResolved()) {
530             return true;
531         }
532         int line = augment.getLine();
533         ModuleBuilder module = getParentModule(augment);
534         List<QName> path = augment.getTargetPath().getPath();
535         final QName firstNameInPath = path.get(0);
536         String prefix = firstNameInPath.getPrefix();
537         if (prefix == null) {
538             prefix = module.getPrefix();
539         }
540         Builder augmentParent = augment.getParent();
541         Builder currentParent = null;
542
543         if (augmentParent instanceof ModuleBuilder) {
544             // if augment is defined under module, first parent is target module
545             currentParent = findDependentModuleBuilder(modules, module, prefix, line);
546         } else if (augmentParent instanceof UsesNodeBuilder) {
547             currentParent = augmentParent.getParent();
548         } else {
549             // augment can be defined only under module or uses
550             throw new YangParseException(augment.getModuleName(), augment.getLine(),
551                     "Error in augment parsing: Unresolved parent of augment: " + augmentParent);
552         }
553
554         if (currentParent == null) {
555             return processAugmentationOnContext(augment, path, module, prefix, context);
556         } else {
557             return processAugmentation(augment, currentParent, path);
558         }
559     }
560
561     /**
562      * Go through identity statements defined in current module and resolve
563      * their 'base' statement if present.
564      *
565      * @param modules
566      *            all modules
567      * @param module
568      *            module being resolved
569      */
570     private void resolveIdentities(final Map<String, TreeMap<Date, ModuleBuilder>> modules, final ModuleBuilder module) {
571         final Set<IdentitySchemaNodeBuilder> identities = module.getIdentities();
572         for (IdentitySchemaNodeBuilder identity : identities) {
573             final String baseIdentityName = identity.getBaseIdentityName();
574             if (baseIdentityName != null) {
575                 String baseIdentityPrefix = null;
576                 String baseIdentityLocalName = null;
577                 if (baseIdentityName.contains(":")) {
578                     final String[] splitted = baseIdentityName.split(":");
579                     baseIdentityPrefix = splitted[0];
580                     baseIdentityLocalName = splitted[1];
581                 } else {
582                     baseIdentityPrefix = module.getPrefix();
583                     baseIdentityLocalName = baseIdentityName;
584                 }
585                 final ModuleBuilder dependentModule = findDependentModuleBuilder(modules, module, baseIdentityPrefix,
586                         identity.getLine());
587
588                 final Set<IdentitySchemaNodeBuilder> dependentModuleIdentities = dependentModule.getIdentities();
589                 for (IdentitySchemaNodeBuilder idBuilder : dependentModuleIdentities) {
590                     if (idBuilder.getQName().getLocalName().equals(baseIdentityLocalName)) {
591                         identity.setBaseIdentity(idBuilder);
592                     }
593                 }
594             }
595         }
596     }
597
598     /**
599      * Go through identity statements defined in current module and resolve
600      * their 'base' statement. Method tries to find base identity in given
601      * modules. If base identity is not found, method will search it in context.
602      *
603      * @param modules
604      *            all loaded modules
605      * @param module
606      *            current module
607      * @param context
608      *            SchemaContext containing already resolved modules
609      */
610     private void resolveIdentitiesWithContext(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
611             final ModuleBuilder module, final SchemaContext context) {
612         final Set<IdentitySchemaNodeBuilder> identities = module.getIdentities();
613         for (IdentitySchemaNodeBuilder identity : identities) {
614             final String baseIdentityName = identity.getBaseIdentityName();
615             if (baseIdentityName != null) {
616                 String baseIdentityPrefix = null;
617                 String baseIdentityLocalName = null;
618                 if (baseIdentityName.contains(":")) {
619                     final String[] splitted = baseIdentityName.split(":");
620                     baseIdentityPrefix = splitted[0];
621                     baseIdentityLocalName = splitted[1];
622                 } else {
623                     baseIdentityPrefix = module.getPrefix();
624                     baseIdentityLocalName = baseIdentityName;
625                 }
626                 final ModuleBuilder dependentModuleBuilder = findDependentModuleBuilder(modules, module,
627                         baseIdentityPrefix, identity.getLine());
628
629                 if (dependentModuleBuilder == null) {
630                     final Module dependentModule = findModuleFromContext(context, module, baseIdentityPrefix,
631                             identity.getLine());
632                     final Set<IdentitySchemaNode> dependentModuleIdentities = dependentModule.getIdentities();
633                     for (IdentitySchemaNode idNode : dependentModuleIdentities) {
634                         if (idNode.getQName().getLocalName().equals(baseIdentityLocalName)) {
635                             identity.setBaseIdentity(idNode);
636                         }
637                     }
638                 } else {
639                     final Set<IdentitySchemaNodeBuilder> dependentModuleIdentities = dependentModuleBuilder
640                             .getIdentities();
641                     for (IdentitySchemaNodeBuilder idBuilder : dependentModuleIdentities) {
642                         if (idBuilder.getQName().getLocalName().equals(baseIdentityLocalName)) {
643                             identity.setBaseIdentity(idBuilder);
644                         }
645                     }
646                 }
647             }
648         }
649     }
650
651     /**
652      * Find target grouping for all uses nodes.
653      *
654      * @param modules
655      *            all loaded modules
656      * @param context
657      *            SchemaContext containing already resolved modules or null if
658      *            context is not available
659      */
660     private void findUsesTargets(final Map<String, TreeMap<Date, ModuleBuilder>> modules, final SchemaContext context) {
661         final List<UsesNodeBuilder> allUses = new ArrayList<>();
662         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
663             for (Map.Entry<Date, ModuleBuilder> inner : entry.getValue().entrySet()) {
664                 allUses.addAll(inner.getValue().getAllUsesNodes());
665             }
666         }
667         for (UsesNodeBuilder usesNode : allUses) {
668             ModuleBuilder module = ParserUtils.getParentModule(usesNode);
669             final GroupingBuilder targetGroupingBuilder = GroupingUtils.getTargetGroupingFromModules(usesNode, modules,
670                     module);
671             if (targetGroupingBuilder == null) {
672                 if (context == null) {
673                     throw new YangParseException(module.getName(), usesNode.getLine(), "Referenced grouping '"
674                             + usesNode.getGroupingName() + "' not found.");
675                 } else {
676                     GroupingDefinition targetGroupingDefinition = GroupingUtils.getTargetGroupingFromContext(usesNode,
677                             module, context);
678                     usesNode.setGroupingDefinition(targetGroupingDefinition);
679                     usesNode.setGroupingPath(targetGroupingDefinition.getPath());
680                 }
681             } else {
682                 usesNode.setGrouping(targetGroupingBuilder);
683                 usesNode.setGroupingPath(targetGroupingBuilder.getPath());
684             }
685         }
686     }
687
688     /**
689      * Copy data from uses target, update uses parent and perform refinement.
690      * Augmentations have to be resolved already.
691      *
692      * @param modules
693      *            all loaded modules
694      */
695     private void resolveUses(final Map<String, TreeMap<Date, ModuleBuilder>> modules) {
696         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
697             for (Map.Entry<Date, ModuleBuilder> inner : entry.getValue().entrySet()) {
698                 ModuleBuilder module = inner.getValue();
699                 List<UsesNodeBuilder> usesNodes = null;
700                 boolean dataCollected = module.isAllUsesDataCollected();
701
702                 while (!dataCollected) {
703                     usesNodes = new ArrayList<>(module.getAllUsesNodes());
704                     for (UsesNodeBuilder usesNode : usesNodes) {
705                         if (!usesNode.isDataCollected()) {
706                             GroupingUtils.collectUsesData(usesNode);
707                         }
708                     }
709                     dataCollected = module.isAllUsesDataCollected();
710                 }
711             }
712         }
713
714         // new cycle is must because in collecting data process new uses could
715         // be created
716         final List<UsesNodeBuilder> allModulesUses = new ArrayList<>();
717         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
718             for (Map.Entry<Date, ModuleBuilder> inner : entry.getValue().entrySet()) {
719                 allModulesUses.addAll(inner.getValue().getAllUsesNodes());
720             }
721         }
722
723         for (UsesNodeBuilder usesNode : allModulesUses) {
724             GroupingUtils.updateUsesParent(usesNode);
725             GroupingUtils.performRefine(usesNode);
726         }
727         for (UsesNodeBuilder usesNode : allModulesUses) {
728             GroupingUtils.fixUsesNodesPath(usesNode);
729         }
730
731         for (UsesNodeBuilder usesNode : allModulesUses) {
732             if (usesNode.isCopy()) {
733                 usesNode.getParent().getUsesNodes().remove(usesNode);
734             }
735         }
736     }
737
738     /**
739      * Copy data from uses target, update uses parent and perform refinement.
740      * Augmentations have to be resolved already.
741      *
742      * @param modules
743      *            all loaded modules
744      * @param context
745      *            SchemaContext containing already resolved modules
746      */
747     private void resolveUsesWithContext(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
748             final SchemaContext context) {
749         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
750             for (Map.Entry<Date, ModuleBuilder> inner : entry.getValue().entrySet()) {
751                 ModuleBuilder module = inner.getValue();
752                 List<UsesNodeBuilder> usesNodes = null;
753                 boolean dataCollected = module.isAllUsesDataCollected();
754
755                 while (!dataCollected) {
756                     usesNodes = new ArrayList<>(module.getAllUsesNodes());
757                     for (UsesNodeBuilder usesNode : usesNodes) {
758                         if (!usesNode.isDataCollected()) {
759                             if (usesNode.getGroupingBuilder() == null) {
760                                 GroupingUtils.collectUsesDataFromContext(usesNode);
761                             } else {
762                                 GroupingUtils.collectUsesData(usesNode);
763                             }
764                         }
765                     }
766                     dataCollected = module.isAllUsesDataCollected();
767                 }
768             }
769         }
770
771         // new cycle is must because in collecting data process new uses could
772         // be created
773         final List<UsesNodeBuilder> allModulesUses = new ArrayList<>();
774         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
775             for (Map.Entry<Date, ModuleBuilder> inner : entry.getValue().entrySet()) {
776                 allModulesUses.addAll(inner.getValue().getAllUsesNodes());
777             }
778         }
779
780         for (UsesNodeBuilder usesNode : allModulesUses) {
781             GroupingUtils.updateUsesParent(usesNode);
782             GroupingUtils.performRefine(usesNode);
783         }
784         for (UsesNodeBuilder usesNode : allModulesUses) {
785             GroupingUtils.fixUsesNodesPath(usesNode);
786         }
787     }
788
789     private void resolveUnknownNodes(final Map<String, TreeMap<Date, ModuleBuilder>> modules, final ModuleBuilder module) {
790         for (UnknownSchemaNodeBuilder usnb : module.getAllUnknownNodes()) {
791             QName nodeType = usnb.getNodeType();
792             try {
793                 ModuleBuilder dependentModule = findDependentModuleBuilder(modules, module, nodeType.getPrefix(),
794                         usnb.getLine());
795                 for (ExtensionBuilder extension : dependentModule.getExtensions()) {
796                     if (extension.getQName().getLocalName().equals(nodeType.getLocalName())) {
797                         usnb.setNodeType(extension.getQName());
798                         usnb.setExtensionBuilder(extension);
799                         break;
800                     }
801                 }
802             } catch (YangParseException e) {
803                 throw new YangParseException(module.getName(), usnb.getLine(), "Failed to resolve node " + usnb
804                         + ": no such extension definition found.");
805             }
806         }
807     }
808
809     private void resolveUnknownNodesWithContext(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
810             final ModuleBuilder module, final SchemaContext context) {
811         for (UnknownSchemaNodeBuilder usnb : module.getAllUnknownNodes()) {
812             QName nodeType = usnb.getNodeType();
813             try {
814                 ModuleBuilder dependentModuleBuilder = findDependentModuleBuilder(modules, module,
815                         nodeType.getPrefix(), usnb.getLine());
816
817                 if (dependentModuleBuilder == null) {
818                     Module dependentModule = findModuleFromContext(context, module, nodeType.getPrefix(),
819                             usnb.getLine());
820                     for (ExtensionDefinition e : dependentModule.getExtensionSchemaNodes()) {
821                         if (e.getQName().getLocalName().equals(nodeType.getLocalName())) {
822                             usnb.setNodeType(new QName(e.getQName().getNamespace(),e.getQName().getRevision() , nodeType.getPrefix(), e.getQName().getLocalName()));
823                             usnb.setExtensionDefinition(e);
824                             break;
825                         }
826                     }
827                 } else {
828                     for (ExtensionBuilder extension : dependentModuleBuilder.getExtensions()) {
829                         if (extension.getQName().getLocalName().equals(nodeType.getLocalName())) {
830                             usnb.setExtensionBuilder(extension);
831                             break;
832                         }
833                     }
834                 }
835
836             } catch (YangParseException e) {
837                 throw new YangParseException(module.getName(), usnb.getLine(), "Failed to resolve node " + usnb
838                         + ": no such extension definition found.");
839             }
840
841         }
842     }
843
844     /**
845      * Traverse through modules and resolve their deviation statements.
846      *
847      * @param modules
848      *            all loaded modules
849      */
850     private void resolveDeviations(final Map<String, TreeMap<Date, ModuleBuilder>> modules) {
851         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
852             for (Map.Entry<Date, ModuleBuilder> inner : entry.getValue().entrySet()) {
853                 ModuleBuilder b = inner.getValue();
854                 resolveDeviation(modules, b);
855             }
856         }
857     }
858
859     /**
860      * Traverse through module and resolve its deviation statements.
861      *
862      * @param modules
863      *            all loaded modules
864      * @param module
865      *            module in which resolve deviations
866      */
867     private void resolveDeviation(final Map<String, TreeMap<Date, ModuleBuilder>> modules, final ModuleBuilder module) {
868         for (DeviationBuilder dev : module.getDeviations()) {
869             int line = dev.getLine();
870             SchemaPath targetPath = dev.getTargetPath();
871             List<QName> path = targetPath.getPath();
872             QName q0 = path.get(0);
873             String prefix = q0.getPrefix();
874             if (prefix == null) {
875                 prefix = module.getPrefix();
876             }
877
878             ModuleBuilder dependentModuleBuilder = findDependentModuleBuilder(modules, module, prefix, line);
879             processDeviation(dev, dependentModuleBuilder, path, module);
880         }
881     }
882
883     /**
884      * Traverse through modules and resolve their deviation statements with
885      * given context.
886      *
887      * @param modules
888      *            all loaded modules
889      * @param context
890      *            already resolved context
891      */
892     private void resolveDeviationsWithContext(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
893             final SchemaContext context) {
894         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
895             for (Map.Entry<Date, ModuleBuilder> inner : entry.getValue().entrySet()) {
896                 ModuleBuilder b = inner.getValue();
897                 resolveDeviationWithContext(modules, b, context);
898             }
899         }
900     }
901
902     /**
903      * Traverse through module and resolve its deviation statements with given
904      * context.
905      *
906      * @param modules
907      *            all loaded modules
908      * @param module
909      *            module in which resolve deviations
910      * @param context
911      *            already resolved context
912      */
913     private void resolveDeviationWithContext(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
914             final ModuleBuilder module, final SchemaContext context) {
915         for (DeviationBuilder dev : module.getDeviations()) {
916             int line = dev.getLine();
917             SchemaPath targetPath = dev.getTargetPath();
918             List<QName> path = targetPath.getPath();
919             QName q0 = path.get(0);
920             String prefix = q0.getPrefix();
921             if (prefix == null) {
922                 prefix = module.getPrefix();
923             }
924             String name = null;
925
926             ModuleBuilder dependentModuleBuilder = findDependentModuleBuilder(modules, module, prefix, line);
927             if (dependentModuleBuilder == null) {
928                 Module dependentModule = findModuleFromContext(context, module, prefix, line);
929                 Object currentParent = dependentModule;
930
931                 for (int i = 0; i < path.size(); i++) {
932                     if (currentParent == null) {
933                         throw new YangParseException(module.getName(), line, "Failed to find deviation target.");
934                     }
935                     QName q = path.get(i);
936                     name = q.getLocalName();
937                     if (currentParent instanceof DataNodeContainer) {
938                         currentParent = ((DataNodeContainer) currentParent).getDataChildByName(name);
939                     }
940                 }
941
942                 if (currentParent == null) {
943                     throw new YangParseException(module.getName(), line, "Failed to find deviation target.");
944                 }
945                 if (currentParent instanceof SchemaNode) {
946                     dev.setTargetPath(((SchemaNode) currentParent).getPath());
947                 }
948
949             } else {
950                 processDeviation(dev, dependentModuleBuilder, path, module);
951             }
952         }
953     }
954
955     /**
956      * Correct deviation target path in deviation builder.
957      *
958      * @param dev
959      *            deviation
960      * @param dependentModuleBuilder
961      *            module containing deviation target
962      * @param path
963      *            current deviation target path
964      * @param module
965      *            current module
966      */
967     private void processDeviation(final DeviationBuilder dev, final ModuleBuilder dependentModuleBuilder,
968             final List<QName> path, final ModuleBuilder module) {
969         final int line = dev.getLine();
970         Builder currentParent = dependentModuleBuilder;
971
972         for (int i = 0; i < path.size(); i++) {
973             if (currentParent == null) {
974                 throw new YangParseException(module.getName(), line, "Failed to find deviation target.");
975             }
976             QName q = path.get(i);
977             String name = q.getLocalName();
978             if (currentParent instanceof DataNodeContainerBuilder) {
979                 currentParent = ((DataNodeContainerBuilder) currentParent).getDataChildByName(name);
980             }
981         }
982
983         if (currentParent == null || !(currentParent instanceof SchemaNodeBuilder)) {
984             throw new YangParseException(module.getName(), line, "Failed to find deviation target.");
985         }
986         dev.setTargetPath(((SchemaNodeBuilder) currentParent).getPath());
987     }
988
989 }