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