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