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