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