Minor code refactoring in YangParserImpl.
[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         // fix unresolved nodes
276         findUsesTargets(modules, null);
277         resolveDirtyNodes(modules);
278         resolveAugments(modules);
279         resolveUses(modules);
280         resolveDeviations(modules);
281
282         // build
283         final Map<ModuleBuilder, Module> result = new LinkedHashMap<ModuleBuilder, Module>();
284         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
285             final Map<Date, Module> modulesByRevision = new HashMap<Date, Module>();
286             for (Map.Entry<Date, ModuleBuilder> childEntry : entry.getValue().entrySet()) {
287                 final ModuleBuilder moduleBuilder = childEntry.getValue();
288                 final Module module = moduleBuilder.build();
289                 modulesByRevision.put(childEntry.getKey(), module);
290                 result.put(moduleBuilder, module);
291             }
292         }
293         return result;
294     }
295
296     private Map<ModuleBuilder, Module> buildWithContext(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
297             final SchemaContext context) {
298         // fix unresolved nodes
299         findUsesTargets(modules, context);
300         resolvedDirtyNodesWithContext(modules, context);
301         resolveAugmentsWithContext(modules, context);
302         resolveUsesWithContext(modules, context);
303         resolveDeviationsWithContext(modules, context);
304
305         // build
306         final Map<ModuleBuilder, Module> result = new LinkedHashMap<ModuleBuilder, Module>();
307         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
308             final Map<Date, Module> modulesByRevision = new HashMap<Date, Module>();
309             for (Map.Entry<Date, ModuleBuilder> childEntry : entry.getValue().entrySet()) {
310                 final ModuleBuilder moduleBuilder = childEntry.getValue();
311                 final Module module = moduleBuilder.build();
312                 modulesByRevision.put(childEntry.getKey(), module);
313                 result.put(moduleBuilder, module);
314             }
315         }
316         return result;
317     }
318
319     private void resolveDirtyNodes(final Map<String, TreeMap<Date, ModuleBuilder>> modules) {
320         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
321             for (Map.Entry<Date, ModuleBuilder> childEntry : entry.getValue().entrySet()) {
322                 final ModuleBuilder module = childEntry.getValue();
323                 resolveDirtyNodes(modules, module);
324                 resolveIdentities(modules, module);
325                 resolveUnknownNodes(modules, module);
326             }
327         }
328     }
329
330     private void resolvedDirtyNodesWithContext(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
331             final SchemaContext context) {
332         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
333             for (Map.Entry<Date, ModuleBuilder> childEntry : entry.getValue().entrySet()) {
334                 final ModuleBuilder module = childEntry.getValue();
335                 resolveDirtyNodesWithContext(modules, module, context);
336                 resolveIdentitiesWithContext(modules, module, context);
337                 resolveUnknownNodesWithContext(modules, module, context);
338             }
339         }
340     }
341
342     /**
343      * Search for dirty nodes (node which contains UnknownType) and resolve
344      * unknown types.
345      *
346      * @param modules
347      *            all available modules
348      * @param module
349      *            current module
350      */
351     private void resolveDirtyNodes(final Map<String, TreeMap<Date, ModuleBuilder>> modules, final ModuleBuilder module) {
352         final Set<TypeAwareBuilder> dirtyNodes = module.getDirtyNodes();
353         if (!dirtyNodes.isEmpty()) {
354             for (TypeAwareBuilder nodeToResolve : dirtyNodes) {
355                 if (nodeToResolve instanceof UnionTypeBuilder) {
356                     // special handling for union types
357                     resolveTypeUnion((UnionTypeBuilder) nodeToResolve, modules, module);
358                 } else if (nodeToResolve.getTypedef() instanceof IdentityrefTypeBuilder) {
359                     // special handling for identityref types
360                     IdentityrefTypeBuilder idref = (IdentityrefTypeBuilder) nodeToResolve.getTypedef();
361                     nodeToResolve.setType(new IdentityrefType(findFullQName(modules, module, idref), idref.getPath()));
362                 } else {
363                     resolveType(nodeToResolve, modules, module);
364                 }
365             }
366         }
367     }
368
369     private void resolveDirtyNodesWithContext(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
370             final ModuleBuilder module, SchemaContext context) {
371         final Set<TypeAwareBuilder> dirtyNodes = module.getDirtyNodes();
372         if (!dirtyNodes.isEmpty()) {
373             for (TypeAwareBuilder nodeToResolve : dirtyNodes) {
374                 if (nodeToResolve instanceof UnionTypeBuilder) {
375                     // special handling for union types
376                     resolveTypeUnionWithContext((UnionTypeBuilder) nodeToResolve, modules, module, context);
377                 } else if (nodeToResolve.getTypedef() instanceof IdentityrefTypeBuilder) {
378                     // special handling for identityref types
379                     IdentityrefTypeBuilder idref = (IdentityrefTypeBuilder) nodeToResolve.getTypedef();
380                     nodeToResolve.setType(new IdentityrefType(findFullQName(modules, module, idref), idref.getPath()));
381                 } else {
382                     resolveTypeWithContext(nodeToResolve, modules, module, context);
383                 }
384             }
385         }
386     }
387
388     /**
389      * Go through all augment definitions and perform augmentation. It is
390      * expected that modules are already sorted by their dependencies.
391      *
392      * @param modules
393      *            all loaded modules
394      */
395     private void resolveAugments(final Map<String, TreeMap<Date, ModuleBuilder>> modules) {
396         // collect augments from all loaded modules
397         final List<AugmentationSchemaBuilder> allAugments = new ArrayList<>();
398         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
399             for (Map.Entry<Date, ModuleBuilder> inner : entry.getValue().entrySet()) {
400                 allAugments.addAll(inner.getValue().getAllAugments());
401             }
402         }
403
404         for (int i = 0; i < allAugments.size(); i++) {
405             // pick one augment
406             final AugmentationSchemaBuilder augment = allAugments.get(i);
407             // create collection of others
408             List<AugmentationSchemaBuilder> others = new ArrayList<>(allAugments);
409             others.remove(augment);
410
411             // try to resolve it
412             boolean resolved = resolveAugment(modules, augment);
413             // while not resolved
414             int j = 0;
415             while (!(resolved) && j < others.size()) {
416                 // try to resolve next augment
417                 resolveAugment(modules, others.get(j));
418                 // then try to resolve first again
419                 resolved = resolveAugment(modules, augment);
420                 j++;
421
422             }
423
424             if (!resolved) {
425                 throw new YangParseException(augment.getModuleName(), augment.getLine(),
426                         "Error in augment parsing: failed to find augment target");
427             }
428         }
429     }
430
431     /**
432      * Search for augment target and perform augmentation.
433      *
434      * @param modules
435      *            all loaded modules
436      * @param augmentBuilder
437      *            augment to resolve
438      * @return true if target node found, false otherwise
439      */
440     private boolean resolveAugment(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
441             final AugmentationSchemaBuilder augmentBuilder) {
442         if (augmentBuilder.isResolved()) {
443             return true;
444         }
445
446         int line = augmentBuilder.getLine();
447         ModuleBuilder module = getParentModule(augmentBuilder);
448         List<QName> path = augmentBuilder.getTargetPath().getPath();
449         Builder augmentParent = augmentBuilder.getParent();
450
451         Builder firstNodeParent = null;
452         if (augmentParent instanceof ModuleBuilder) {
453             // if augment is defined under module, parent of first node is
454             // target module
455             final QName firstNameInPath = path.get(0);
456             String prefix = firstNameInPath.getPrefix();
457             if (prefix == null) {
458                 prefix = module.getPrefix();
459             }
460             firstNodeParent = findDependentModuleBuilder(modules, module, prefix, line);
461         } else if (augmentParent instanceof UsesNodeBuilder) {
462             firstNodeParent = augmentParent.getParent();
463         } else {
464             // augment can be defined only under module or uses
465             throw new YangParseException(augmentBuilder.getModuleName(), line,
466                     "Failed to parse augment: Unresolved parent of augment: " + augmentParent);
467         }
468
469         return processAugmentation(augmentBuilder, firstNodeParent, path);
470     }
471
472     /**
473      * Go through all augment definitions and resolve them. This method works in
474      * same way as {@link #resolveAugments(Map)} except that if target node is
475      * not found in loaded modules, it search for target node in given context.
476      *
477      * @param modules
478      *            all loaded modules
479      * @param context
480      *            SchemaContext containing already resolved modules
481      */
482     private void resolveAugmentsWithContext(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
483             final SchemaContext context) {
484         // collect augments from all loaded modules
485         final List<AugmentationSchemaBuilder> allAugments = new ArrayList<>();
486         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
487             for (Map.Entry<Date, ModuleBuilder> inner : entry.getValue().entrySet()) {
488                 allAugments.addAll(inner.getValue().getAllAugments());
489             }
490         }
491
492         for (int i = 0; i < allAugments.size(); i++) {
493             // pick augment from list
494             final AugmentationSchemaBuilder augment = allAugments.get(i);
495             // try to resolve it
496             boolean resolved = resolveAugmentWithContext(modules, augment, context);
497             // while not resolved
498             int j = i + 1;
499             while (!(resolved) && j < allAugments.size()) {
500                 // try to resolve next augment
501                 resolveAugmentWithContext(modules, allAugments.get(j), context);
502                 // then try to resolve first again
503                 resolved = resolveAugmentWithContext(modules, augment, context);
504                 j++;
505             }
506
507             if (!resolved) {
508                 throw new YangParseException(augment.getModuleName(), augment.getLine(),
509                         "Error in augment parsing: failed to find augment target");
510             }
511         }
512     }
513
514     /**
515      * Search for augment target and perform augmentation.
516      *
517      * @param modules
518      *            all loaded modules
519      * @param augment
520      *            augment to resolve
521      * @param context
522      *            SchemaContext containing already resolved modules
523      * @return true if target node found, false otherwise
524      */
525     private boolean resolveAugmentWithContext(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
526             final AugmentationSchemaBuilder augment, final SchemaContext context) {
527         if (augment.isResolved()) {
528             return true;
529         }
530         int line = augment.getLine();
531         ModuleBuilder module = getParentModule(augment);
532         List<QName> path = augment.getTargetPath().getPath();
533         final QName firstNameInPath = path.get(0);
534         String prefix = firstNameInPath.getPrefix();
535         if (prefix == null) {
536             prefix = module.getPrefix();
537         }
538         Builder augmentParent = augment.getParent();
539         Builder currentParent = null;
540
541         if (augmentParent instanceof ModuleBuilder) {
542             // if augment is defined under module, first parent is target module
543             currentParent = findDependentModuleBuilder(modules, module, prefix, line);
544         } else if (augmentParent instanceof UsesNodeBuilder) {
545             currentParent = augmentParent.getParent();
546         } else {
547             // augment can be defined only under module or uses
548             throw new YangParseException(augment.getModuleName(), augment.getLine(),
549                     "Error in augment parsing: Unresolved parent of augment: " + augmentParent);
550         }
551
552         if (currentParent == null) {
553             return processAugmentationOnContext(augment, path, module, prefix, context);
554         } else {
555             return processAugmentation(augment, currentParent, path);
556         }
557     }
558
559     /**
560      * Go through identity statements defined in current module and resolve
561      * their 'base' statement if present.
562      *
563      * @param modules
564      *            all modules
565      * @param module
566      *            module being resolved
567      */
568     private void resolveIdentities(final Map<String, TreeMap<Date, ModuleBuilder>> modules, final ModuleBuilder module) {
569         final Set<IdentitySchemaNodeBuilder> identities = module.getIdentities();
570         for (IdentitySchemaNodeBuilder identity : identities) {
571             final String baseIdentityName = identity.getBaseIdentityName();
572             if (baseIdentityName != null) {
573                 String baseIdentityPrefix = null;
574                 String baseIdentityLocalName = null;
575                 if (baseIdentityName.contains(":")) {
576                     final String[] splitted = baseIdentityName.split(":");
577                     baseIdentityPrefix = splitted[0];
578                     baseIdentityLocalName = splitted[1];
579                 } else {
580                     baseIdentityPrefix = module.getPrefix();
581                     baseIdentityLocalName = baseIdentityName;
582                 }
583                 final ModuleBuilder dependentModule = findDependentModuleBuilder(modules, module, baseIdentityPrefix,
584                         identity.getLine());
585
586                 final Set<IdentitySchemaNodeBuilder> dependentModuleIdentities = dependentModule.getIdentities();
587                 for (IdentitySchemaNodeBuilder idBuilder : dependentModuleIdentities) {
588                     if (idBuilder.getQName().getLocalName().equals(baseIdentityLocalName)) {
589                         identity.setBaseIdentity(idBuilder);
590                     }
591                 }
592             }
593         }
594     }
595
596     /**
597      * Go through identity statements defined in current module and resolve
598      * their 'base' statement. Method tries to find base identity in given
599      * modules. If base identity is not found, method will search it in context.
600      *
601      * @param modules
602      *            all loaded modules
603      * @param module
604      *            current module
605      * @param context
606      *            SchemaContext containing already resolved modules
607      */
608     private void resolveIdentitiesWithContext(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
609             final ModuleBuilder module, final SchemaContext context) {
610         final Set<IdentitySchemaNodeBuilder> identities = module.getIdentities();
611         for (IdentitySchemaNodeBuilder identity : identities) {
612             final String baseIdentityName = identity.getBaseIdentityName();
613             if (baseIdentityName != null) {
614                 String baseIdentityPrefix = null;
615                 String baseIdentityLocalName = null;
616                 if (baseIdentityName.contains(":")) {
617                     final String[] splitted = baseIdentityName.split(":");
618                     baseIdentityPrefix = splitted[0];
619                     baseIdentityLocalName = splitted[1];
620                 } else {
621                     baseIdentityPrefix = module.getPrefix();
622                     baseIdentityLocalName = baseIdentityName;
623                 }
624                 final ModuleBuilder dependentModuleBuilder = findDependentModuleBuilder(modules, module,
625                         baseIdentityPrefix, identity.getLine());
626
627                 if (dependentModuleBuilder == null) {
628                     final Module dependentModule = findModuleFromContext(context, module, baseIdentityPrefix,
629                             identity.getLine());
630                     final Set<IdentitySchemaNode> dependentModuleIdentities = dependentModule.getIdentities();
631                     for (IdentitySchemaNode idNode : dependentModuleIdentities) {
632                         if (idNode.getQName().getLocalName().equals(baseIdentityLocalName)) {
633                             identity.setBaseIdentity(idNode);
634                         }
635                     }
636                 } else {
637                     final Set<IdentitySchemaNodeBuilder> dependentModuleIdentities = dependentModuleBuilder
638                             .getIdentities();
639                     for (IdentitySchemaNodeBuilder idBuilder : dependentModuleIdentities) {
640                         if (idBuilder.getQName().getLocalName().equals(baseIdentityLocalName)) {
641                             identity.setBaseIdentity(idBuilder);
642                         }
643                     }
644                 }
645             }
646         }
647     }
648
649     /**
650      * Find and add reference of uses target grouping.
651      *
652      * @param modules
653      *            all loaded modules
654      * @param context
655      *            SchemaContext containing already resolved modules or null if
656      *            context is not available
657      */
658     private void findUsesTargets(final Map<String, TreeMap<Date, ModuleBuilder>> modules, final SchemaContext context) {
659         final List<UsesNodeBuilder> allUses = new ArrayList<>();
660         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
661             for (Map.Entry<Date, ModuleBuilder> inner : entry.getValue().entrySet()) {
662                 allUses.addAll(inner.getValue().getAllUsesNodes());
663             }
664         }
665         for (UsesNodeBuilder usesNode : allUses) {
666             ModuleBuilder module = ParserUtils.getParentModule(usesNode);
667             final GroupingBuilder targetGroupingBuilder = GroupingUtils.getTargetGroupingFromModules(usesNode, modules,
668                     module);
669             if (targetGroupingBuilder == null) {
670                 if (context == null) {
671                     throw new YangParseException(module.getName(), usesNode.getLine(), "Referenced grouping '"
672                             + usesNode.getGroupingName() + "' not found.");
673                 } else {
674                     GroupingDefinition targetGroupingDefinition = GroupingUtils.getTargetGroupingFromContext(usesNode,
675                             module, context);
676                     usesNode.setGroupingDefinition(targetGroupingDefinition);
677                 }
678             } else {
679                 usesNode.setGrouping(targetGroupingBuilder);
680             }
681         }
682     }
683
684     /**
685      * Copy data from uses target, update uses parent and perform refinement.
686      * Augmentations have to be resolved already.
687      *
688      * @param modules
689      *            all loaded modules
690      */
691     private void resolveUses(final Map<String, TreeMap<Date, ModuleBuilder>> modules) {
692         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
693             for (Map.Entry<Date, ModuleBuilder> inner : entry.getValue().entrySet()) {
694                 ModuleBuilder module = inner.getValue();
695                 List<UsesNodeBuilder> usesNodes = null;
696                 boolean dataCollected = module.isAllUsesDataCollected();
697
698                 while (!dataCollected) {
699                     usesNodes = new ArrayList<>(module.getAllUsesNodes());
700                     for (UsesNodeBuilder usesNode : usesNodes) {
701                         if (!usesNode.isDataCollected()) {
702                             GroupingUtils.collectUsesData(usesNode);
703                         }
704                     }
705                     dataCollected = module.isAllUsesDataCollected();
706                 }
707             }
708         }
709
710         // new cycle is must because in collecting data process new uses could
711         // be created
712         final List<UsesNodeBuilder> allModulesUses = new ArrayList<>();
713         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
714             for (Map.Entry<Date, ModuleBuilder> inner : entry.getValue().entrySet()) {
715                 allModulesUses.addAll(inner.getValue().getAllUsesNodes());
716             }
717         }
718
719         for (UsesNodeBuilder usesNode : allModulesUses) {
720             GroupingUtils.updateUsesParent(usesNode);
721             GroupingUtils.performRefine(usesNode);
722         }
723         for (UsesNodeBuilder usesNode : allModulesUses) {
724             GroupingUtils.fixUsesNodesPath(usesNode);
725         }
726
727         for (UsesNodeBuilder usesNode : allModulesUses) {
728             if (usesNode.isCopy()) {
729                 usesNode.getParent().getUsesNodes().remove(usesNode);
730             }
731         }
732     }
733
734     /**
735      * Copy data from uses target, update uses parent and perform refinement.
736      * Augmentations have to be resolved already.
737      *
738      * @param modules
739      *            all loaded modules
740      * @param context
741      *            SchemaContext containing already resolved modules
742      */
743     private void resolveUsesWithContext(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
744             final SchemaContext context) {
745         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
746             for (Map.Entry<Date, ModuleBuilder> inner : entry.getValue().entrySet()) {
747                 ModuleBuilder module = inner.getValue();
748                 List<UsesNodeBuilder> usesNodes = null;
749                 boolean dataCollected = module.isAllUsesDataCollected();
750
751                 while (!dataCollected) {
752                     usesNodes = new ArrayList<>(module.getAllUsesNodes());
753                     for (UsesNodeBuilder usesNode : usesNodes) {
754                         if (!usesNode.isDataCollected()) {
755                             if (usesNode.getGroupingBuilder() == null) {
756                                 GroupingUtils.collectUsesDataFromContext(usesNode);
757                             } else {
758                                 GroupingUtils.collectUsesData(usesNode);
759                             }
760                         }
761                     }
762                     dataCollected = module.isAllUsesDataCollected();
763                 }
764             }
765         }
766
767         // new cycle is must because in collecting data process new uses could
768         // be created
769         final List<UsesNodeBuilder> allModulesUses = new ArrayList<>();
770         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
771             for (Map.Entry<Date, ModuleBuilder> inner : entry.getValue().entrySet()) {
772                 allModulesUses.addAll(inner.getValue().getAllUsesNodes());
773             }
774         }
775
776         for (UsesNodeBuilder usesNode : allModulesUses) {
777             GroupingUtils.updateUsesParent(usesNode);
778             GroupingUtils.performRefine(usesNode);
779         }
780         for (UsesNodeBuilder usesNode : allModulesUses) {
781             GroupingUtils.fixUsesNodesPath(usesNode);
782         }
783     }
784
785     private void resolveUnknownNodes(final Map<String, TreeMap<Date, ModuleBuilder>> modules, final ModuleBuilder module) {
786         for (UnknownSchemaNodeBuilder usnb : module.getAllUnknownNodes()) {
787             QName nodeType = usnb.getNodeType();
788             try {
789                 ModuleBuilder dependentModule = findDependentModuleBuilder(modules, module, nodeType.getPrefix(),
790                         usnb.getLine());
791                 for (ExtensionBuilder extension : dependentModule.getExtensions()) {
792                     if (extension.getQName().getLocalName().equals(nodeType.getLocalName())) {
793                         usnb.setNodeType(extension.getQName());
794                         usnb.setExtensionBuilder(extension);
795                         break;
796                     }
797                 }
798             } catch (YangParseException e) {
799                 throw new YangParseException(module.getName(), usnb.getLine(), "Failed to resolve node " + usnb
800                         + ": no such extension definition found.");
801             }
802         }
803     }
804
805     private void resolveUnknownNodesWithContext(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
806             final ModuleBuilder module, final SchemaContext context) {
807         for (UnknownSchemaNodeBuilder usnb : module.getAllUnknownNodes()) {
808             QName nodeType = usnb.getNodeType();
809             try {
810                 ModuleBuilder dependentModuleBuilder = findDependentModuleBuilder(modules, module,
811                         nodeType.getPrefix(), usnb.getLine());
812
813                 if (dependentModuleBuilder == null) {
814                     Module dependentModule = findModuleFromContext(context, module, nodeType.getPrefix(),
815                             usnb.getLine());
816                     for (ExtensionDefinition e : dependentModule.getExtensionSchemaNodes()) {
817                         if (e.getQName().getLocalName().equals(nodeType.getLocalName())) {
818                             usnb.setNodeType(new QName(e.getQName().getNamespace(), e.getQName().getRevision(),
819                                     nodeType.getPrefix(), e.getQName().getLocalName()));
820                             usnb.setExtensionDefinition(e);
821                             break;
822                         }
823                     }
824                 } else {
825                     for (ExtensionBuilder extension : dependentModuleBuilder.getExtensions()) {
826                         if (extension.getQName().getLocalName().equals(nodeType.getLocalName())) {
827                             usnb.setExtensionBuilder(extension);
828                             break;
829                         }
830                     }
831                 }
832
833             } catch (YangParseException e) {
834                 throw new YangParseException(module.getName(), usnb.getLine(), "Failed to resolve node " + usnb
835                         + ": no such extension definition found.");
836             }
837
838         }
839     }
840
841     /**
842      * Traverse through modules and resolve their deviation statements.
843      *
844      * @param modules
845      *            all loaded modules
846      */
847     private void resolveDeviations(final Map<String, TreeMap<Date, ModuleBuilder>> modules) {
848         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
849             for (Map.Entry<Date, ModuleBuilder> inner : entry.getValue().entrySet()) {
850                 ModuleBuilder b = inner.getValue();
851                 resolveDeviation(modules, b);
852             }
853         }
854     }
855
856     /**
857      * Traverse through module and resolve its deviation statements.
858      *
859      * @param modules
860      *            all loaded modules
861      * @param module
862      *            module in which resolve deviations
863      */
864     private void resolveDeviation(final Map<String, TreeMap<Date, ModuleBuilder>> modules, final ModuleBuilder module) {
865         for (DeviationBuilder dev : module.getDeviations()) {
866             int line = dev.getLine();
867             SchemaPath targetPath = dev.getTargetPath();
868             List<QName> path = targetPath.getPath();
869             QName q0 = path.get(0);
870             String prefix = q0.getPrefix();
871             if (prefix == null) {
872                 prefix = module.getPrefix();
873             }
874
875             ModuleBuilder dependentModuleBuilder = findDependentModuleBuilder(modules, module, prefix, line);
876             processDeviation(dev, dependentModuleBuilder, path, module);
877         }
878     }
879
880     /**
881      * Traverse through modules and resolve their deviation statements with
882      * given context.
883      *
884      * @param modules
885      *            all loaded modules
886      * @param context
887      *            already resolved context
888      */
889     private void resolveDeviationsWithContext(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
890             final SchemaContext context) {
891         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
892             for (Map.Entry<Date, ModuleBuilder> inner : entry.getValue().entrySet()) {
893                 ModuleBuilder b = inner.getValue();
894                 resolveDeviationWithContext(modules, b, context);
895             }
896         }
897     }
898
899     /**
900      * Traverse through module and resolve its deviation statements with given
901      * context.
902      *
903      * @param modules
904      *            all loaded modules
905      * @param module
906      *            module in which resolve deviations
907      * @param context
908      *            already resolved context
909      */
910     private void resolveDeviationWithContext(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
911             final ModuleBuilder module, final SchemaContext context) {
912         for (DeviationBuilder dev : module.getDeviations()) {
913             int line = dev.getLine();
914             SchemaPath targetPath = dev.getTargetPath();
915             List<QName> path = targetPath.getPath();
916             QName q0 = path.get(0);
917             String prefix = q0.getPrefix();
918             if (prefix == null) {
919                 prefix = module.getPrefix();
920             }
921             String name = null;
922
923             ModuleBuilder dependentModuleBuilder = findDependentModuleBuilder(modules, module, prefix, line);
924             if (dependentModuleBuilder == null) {
925                 Module dependentModule = findModuleFromContext(context, module, prefix, line);
926                 Object currentParent = dependentModule;
927
928                 for (int i = 0; i < path.size(); i++) {
929                     if (currentParent == null) {
930                         throw new YangParseException(module.getName(), line, "Failed to find deviation target.");
931                     }
932                     QName q = path.get(i);
933                     name = q.getLocalName();
934                     if (currentParent instanceof DataNodeContainer) {
935                         currentParent = ((DataNodeContainer) currentParent).getDataChildByName(name);
936                     }
937                 }
938
939                 if (currentParent == null) {
940                     throw new YangParseException(module.getName(), line, "Failed to find deviation target.");
941                 }
942                 if (currentParent instanceof SchemaNode) {
943                     dev.setTargetPath(((SchemaNode) currentParent).getPath());
944                 }
945
946             } else {
947                 processDeviation(dev, dependentModuleBuilder, path, module);
948             }
949         }
950     }
951
952     /**
953      * Correct deviation target path in deviation builder.
954      *
955      * @param dev
956      *            deviation
957      * @param dependentModuleBuilder
958      *            module containing deviation target
959      * @param path
960      *            current deviation target path
961      * @param module
962      *            current module
963      */
964     private void processDeviation(final DeviationBuilder dev, final ModuleBuilder dependentModuleBuilder,
965             final List<QName> path, final ModuleBuilder module) {
966         final int line = dev.getLine();
967         Builder currentParent = dependentModuleBuilder;
968
969         for (int i = 0; i < path.size(); i++) {
970             if (currentParent == null) {
971                 throw new YangParseException(module.getName(), line, "Failed to find deviation target.");
972             }
973             QName q = path.get(i);
974             String name = q.getLocalName();
975             if (currentParent instanceof DataNodeContainerBuilder) {
976                 currentParent = ((DataNodeContainerBuilder) currentParent).getDataChildByName(name);
977             }
978         }
979
980         if (currentParent == null || !(currentParent instanceof SchemaNodeBuilder)) {
981             throw new YangParseException(module.getName(), line, "Failed to find deviation target.");
982         }
983         dev.setTargetPath(((SchemaNodeBuilder) currentParent).getPath());
984     }
985
986 }