2 * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
5 * and is available at http://www.eclipse.org/legal/epl-v10.html
7 package org.opendaylight.yangtools.yang.parser.impl;
9 import static com.google.common.base.Preconditions.checkNotNull;
10 import static org.opendaylight.yangtools.yang.parser.builder.impl.BuilderUtils.fillAugmentTarget;
11 import static org.opendaylight.yangtools.yang.parser.builder.impl.BuilderUtils.findBaseIdentity;
12 import static org.opendaylight.yangtools.yang.parser.builder.impl.BuilderUtils.findModuleFromBuilders;
13 import static org.opendaylight.yangtools.yang.parser.builder.impl.BuilderUtils.findModuleFromContext;
14 import static org.opendaylight.yangtools.yang.parser.builder.impl.BuilderUtils.findSchemaNode;
15 import static org.opendaylight.yangtools.yang.parser.builder.impl.BuilderUtils.findSchemaNodeInModule;
16 import static org.opendaylight.yangtools.yang.parser.builder.impl.BuilderUtils.processAugmentation;
17 import static org.opendaylight.yangtools.yang.parser.builder.impl.TypeUtils.resolveType;
18 import static org.opendaylight.yangtools.yang.parser.builder.impl.TypeUtils.resolveTypeUnion;
20 import com.google.common.base.Optional;
21 import com.google.common.base.Preconditions;
22 import com.google.common.base.Splitter;
23 import com.google.common.collect.HashBiMap;
24 import com.google.common.io.ByteSource;
27 import java.io.IOException;
28 import java.io.InputStream;
29 import java.util.ArrayList;
30 import java.util.Collection;
31 import java.util.Collections;
32 import java.util.Date;
33 import java.util.HashMap;
34 import java.util.HashSet;
35 import java.util.LinkedHashMap;
36 import java.util.LinkedHashSet;
37 import java.util.List;
40 import java.util.TreeMap;
42 import javax.annotation.concurrent.Immutable;
44 import org.antlr.v4.runtime.ANTLRInputStream;
45 import org.antlr.v4.runtime.CommonTokenStream;
46 import org.antlr.v4.runtime.tree.ParseTree;
47 import org.antlr.v4.runtime.tree.ParseTreeWalker;
48 import org.opendaylight.yangtools.antlrv4.code.gen.YangLexer;
49 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser;
50 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.YangContext;
51 import org.opendaylight.yangtools.yang.common.QName;
52 import org.opendaylight.yangtools.yang.common.QNameModule;
53 import org.opendaylight.yangtools.yang.model.api.ExtensionDefinition;
54 import org.opendaylight.yangtools.yang.model.api.Module;
55 import org.opendaylight.yangtools.yang.model.api.ModuleIdentifier;
56 import org.opendaylight.yangtools.yang.model.api.ModuleImport;
57 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
58 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
59 import org.opendaylight.yangtools.yang.model.parser.api.YangContextParser;
60 import org.opendaylight.yangtools.yang.model.parser.api.YangSyntaxErrorException;
61 import org.opendaylight.yangtools.yang.parser.builder.api.AugmentationSchemaBuilder;
62 import org.opendaylight.yangtools.yang.parser.builder.api.AugmentationTargetBuilder;
63 import org.opendaylight.yangtools.yang.parser.builder.api.Builder;
64 import org.opendaylight.yangtools.yang.parser.builder.api.DataNodeContainerBuilder;
65 import org.opendaylight.yangtools.yang.parser.builder.api.DataSchemaNodeBuilder;
66 import org.opendaylight.yangtools.yang.parser.builder.api.ExtensionBuilder;
67 import org.opendaylight.yangtools.yang.parser.builder.api.GroupingBuilder;
68 import org.opendaylight.yangtools.yang.parser.builder.api.SchemaNodeBuilder;
69 import org.opendaylight.yangtools.yang.parser.builder.api.TypeAwareBuilder;
70 import org.opendaylight.yangtools.yang.parser.builder.api.UnknownSchemaNodeBuilder;
71 import org.opendaylight.yangtools.yang.parser.builder.api.UsesNodeBuilder;
72 import org.opendaylight.yangtools.yang.parser.builder.impl.BuilderUtils;
73 import org.opendaylight.yangtools.yang.parser.builder.impl.ChoiceBuilder;
74 import org.opendaylight.yangtools.yang.parser.builder.impl.ChoiceCaseBuilder;
75 import org.opendaylight.yangtools.yang.parser.builder.impl.DeviationBuilder;
76 import org.opendaylight.yangtools.yang.parser.builder.impl.GroupingUtils;
77 import org.opendaylight.yangtools.yang.parser.builder.impl.IdentitySchemaNodeBuilder;
78 import org.opendaylight.yangtools.yang.parser.builder.impl.IdentityrefTypeBuilder;
79 import org.opendaylight.yangtools.yang.parser.builder.impl.ModuleBuilder;
80 import org.opendaylight.yangtools.yang.parser.builder.impl.ModuleImpl;
81 import org.opendaylight.yangtools.yang.parser.builder.impl.UnionTypeBuilder;
82 import org.opendaylight.yangtools.yang.parser.builder.util.Comparators;
83 import org.opendaylight.yangtools.yang.parser.util.ModuleDependencySort;
84 import org.opendaylight.yangtools.yang.parser.util.NamedByteArrayInputStream;
85 import org.opendaylight.yangtools.yang.parser.util.NamedFileInputStream;
86 import org.opendaylight.yangtools.yang.parser.util.NamedInputStream;
87 import org.opendaylight.yangtools.yang.parser.util.YangParseException;
88 import org.slf4j.Logger;
89 import org.slf4j.LoggerFactory;
92 public final class YangParserImpl implements YangContextParser {
93 private static final Logger LOG = LoggerFactory.getLogger(YangParserImpl.class);
94 private static final String FAIL_DEVIATION_TARGET = "Failed to find deviation target.";
95 private static final Splitter COLON_SPLITTER = Splitter.on(':');
96 private static final YangParserImpl INSTANCE = new YangParserImpl();
98 public static YangParserImpl getInstance() {
104 public Set<Module> parseYangModels(final File yangFile, final File directory) {
106 return parseFile(yangFile, directory).getModules();
107 } catch (IOException | YangSyntaxErrorException e) {
108 throw new YangParseException("Failed to parse yang data", e);
113 public SchemaContext parseFile(final File yangFile, final File directory) throws IOException,
114 YangSyntaxErrorException {
115 Preconditions.checkState(yangFile.exists(), yangFile + " does not exists");
116 Preconditions.checkState(directory.exists(), directory + " does not exists");
117 Preconditions.checkState(directory.isDirectory(), directory + " is not a directory");
119 final String yangFileName = yangFile.getName();
120 final String[] fileList = checkNotNull(directory.list(), directory + " not found or is not a directory");
122 Map<ByteSource, File> sourceToFile = new LinkedHashMap<>();
123 ByteSource mainFileSource = BuilderUtils.fileToByteSource(yangFile);
124 sourceToFile.put(mainFileSource, yangFile);
126 for (String fileName : fileList) {
127 if (fileName.equals(yangFileName)) {
130 File dependency = new File(directory, fileName);
131 if (dependency.isFile()) {
132 sourceToFile.put(BuilderUtils.fileToByteSource(dependency), dependency);
136 Map<ByteSource, ModuleBuilder> sourceToBuilder = parseSourcesToBuilders(sourceToFile.keySet());
137 ModuleBuilder main = sourceToBuilder.get(mainFileSource);
139 List<ModuleBuilder> moduleBuilders = new ArrayList<>();
140 moduleBuilders.add(main);
141 filterImports(main, new ArrayList<>(sourceToBuilder.values()), moduleBuilders);
142 Collection<ModuleBuilder> resolved = resolveSubmodules(moduleBuilders);
144 // module builders sorted by dependencies
145 List<ModuleBuilder> sortedBuilders = ModuleDependencySort.sort(resolved);
146 LinkedHashMap<String, TreeMap<Date, ModuleBuilder>> modules = resolveModulesWithImports(sortedBuilders, null);
147 Collection<Module> unsorted = build(modules).values();
148 Set<Module> result = new LinkedHashSet<>(
149 ModuleDependencySort.sort(unsorted.toArray(new Module[unsorted.size()])));
150 return resolveSchemaContext(result);
155 public Set<Module> parseYangModels(final List<File> yangFiles) {
156 return parseFiles(yangFiles).getModules();
160 public SchemaContext parseFiles(final Collection<File> yangFiles) {
161 Collection<Module> unsorted = parseYangModelsMapped(yangFiles).values();
162 Set<Module> sorted = new LinkedHashSet<>(
163 ModuleDependencySort.sort(unsorted.toArray(new Module[unsorted.size()])));
164 return resolveSchemaContext(sorted);
169 public Set<Module> parseYangModels(final List<File> yangFiles, final SchemaContext context) {
171 return parseFiles(yangFiles, context).getModules();
172 } catch (IOException | YangSyntaxErrorException e) {
173 throw new YangParseException("Failed to parse yang data", e);
178 public SchemaContext parseFiles(final Collection<File> yangFiles, final SchemaContext context) throws IOException,
179 YangSyntaxErrorException {
180 if (yangFiles == null) {
181 return resolveSchemaContext(Collections.<Module> emptySet());
184 Collection<ByteSource> sources = BuilderUtils.filesToByteSources(yangFiles);
185 return parseSources(sources, context);
190 public Set<Module> parseYangModelsFromStreams(final List<InputStream> yangModelStreams) {
192 Collection<ByteSource> sources = BuilderUtils.streamsToByteSources(yangModelStreams);
193 return parseSources(sources).getModules();
194 } catch (IOException | YangSyntaxErrorException e) {
195 throw new YangParseException("Failed to parse yang data", e);
200 public SchemaContext parseSources(final Collection<ByteSource> sources) throws IOException,
201 YangSyntaxErrorException {
202 Collection<Module> unsorted = parseYangModelSources(sources).values();
203 Set<Module> sorted = new LinkedHashSet<>(
204 ModuleDependencySort.sort(unsorted.toArray(new Module[unsorted.size()])));
205 return resolveSchemaContext(sorted);
210 public Set<Module> parseYangModelsFromStreams(final List<InputStream> yangModelStreams, final SchemaContext context) {
212 Collection<ByteSource> sources = BuilderUtils.streamsToByteSources(yangModelStreams);
213 return parseSources(sources, context).getModules();
214 } catch (IOException | YangSyntaxErrorException e) {
215 throw new YangParseException("Failed to parse yang data", e);
220 public SchemaContext parseSources(final Collection<ByteSource> sources, final SchemaContext context)
221 throws IOException, YangSyntaxErrorException {
222 if (sources == null) {
223 return resolveSchemaContext(Collections.<Module> emptySet());
226 final List<ModuleBuilder> sorted = resolveModuleBuilders(sources, context);
227 final Map<String, TreeMap<Date, ModuleBuilder>> modules = resolveModulesWithImports(sorted, context);
229 final Set<Module> unsorted = new LinkedHashSet<>(build(modules).values());
230 if (context != null) {
231 for (Module m : context.getModules()) {
232 if (!unsorted.contains(m)) {
237 Set<Module> result = new LinkedHashSet<>(
238 ModuleDependencySort.sort(unsorted.toArray(new Module[unsorted.size()])));
239 return resolveSchemaContext(result);
242 private LinkedHashMap<String, TreeMap<Date, ModuleBuilder>> resolveModulesWithImports(final List<ModuleBuilder> sorted,
243 final SchemaContext context) {
244 final LinkedHashMap<String, TreeMap<Date, ModuleBuilder>> modules = orderModules(sorted);
245 for (ModuleBuilder module : sorted) {
246 if (module != null) {
247 for (ModuleImport imp : module.getImports().values()) {
248 String prefix = imp.getPrefix();
249 ModuleBuilder targetModule = findModuleFromBuilders(modules, module, prefix, 0);
250 if (targetModule == null) {
251 Module result = findModuleFromContext(context, module, prefix, 0);
252 targetModule = new ModuleBuilder(result);
253 TreeMap<Date, ModuleBuilder> map = modules.get(prefix);
255 map = new TreeMap<>();
256 map.put(targetModule.getRevision(), targetModule);
257 modules.put(targetModule.getName(), map);
259 map.put(targetModule.getRevision(), targetModule);
262 module.addImportedModule(prefix, targetModule);
270 public Map<File, Module> parseYangModelsMapped(final Collection<File> yangFiles) {
271 if (yangFiles == null || yangFiles.isEmpty()) {
272 return Collections.emptyMap();
275 Map<ByteSource, File> byteSourceToFile = new HashMap<>();
276 for (final File file : yangFiles) {
277 ByteSource source = new ByteSource() {
279 public InputStream openStream() throws IOException {
280 return new NamedFileInputStream(file, file.getPath());
283 byteSourceToFile.put(source, file);
286 Map<ByteSource, Module> byteSourceToModule;
288 byteSourceToModule = parseYangModelSources(byteSourceToFile.keySet());
289 } catch (IOException | YangSyntaxErrorException e) {
290 throw new YangParseException("Failed to parse yang data", e);
292 Map<File, Module> result = new LinkedHashMap<>();
293 for (Map.Entry<ByteSource, Module> entry : byteSourceToModule.entrySet()) {
294 result.put(byteSourceToFile.get(entry.getKey()), entry.getValue());
300 public Map<InputStream, Module> parseYangModelsFromStreamsMapped(final Collection<InputStream> yangModelStreams) {
301 if (yangModelStreams == null || yangModelStreams.isEmpty()) {
302 return Collections.emptyMap();
305 Map<ByteSource, InputStream> sourceToStream = new HashMap<>();
306 for (final InputStream stream : yangModelStreams) {
307 ByteSource source = new ByteSource() {
309 public InputStream openStream() throws IOException {
310 return NamedByteArrayInputStream.create(stream);
313 sourceToStream.put(source, stream);
316 Map<ByteSource, Module> sourceToModule;
318 sourceToModule = parseYangModelSources(sourceToStream.keySet());
319 } catch (IOException | YangSyntaxErrorException e) {
320 throw new YangParseException("Failed to parse yang data", e);
322 Map<InputStream, Module> result = new LinkedHashMap<>();
323 for (Map.Entry<ByteSource, Module> entry : sourceToModule.entrySet()) {
324 result.put(sourceToStream.get(entry.getKey()), entry.getValue());
330 public SchemaContext resolveSchemaContext(final Set<Module> modules) {
331 // after merging parse method with this one, add support for getting
332 // submodule sources.
333 Map<ModuleIdentifier, String> identifiersToSources = new HashMap<>();
334 for (Module module : modules) {
335 ModuleImpl moduleImpl = (ModuleImpl) module;
336 identifiersToSources.put(module, moduleImpl.getSource());
338 return new SchemaContextImpl(modules, identifiersToSources);
341 private Map<ByteSource, Module> parseYangModelSources(final Collection<ByteSource> sources) throws IOException,
342 YangSyntaxErrorException {
343 if (sources == null || sources.isEmpty()) {
344 return Collections.emptyMap();
347 Map<ByteSource, ModuleBuilder> sourceToBuilder = resolveSources(sources);
348 // sort and check for duplicates
349 List<ModuleBuilder> sorted = ModuleDependencySort.sort(sourceToBuilder.values());
350 Map<String, TreeMap<Date, ModuleBuilder>> modules = resolveModulesWithImports(sorted, null);
351 Map<ModuleBuilder, Module> builderToModule = build(modules);
352 Map<ModuleBuilder, ByteSource> builderToSource = HashBiMap.create(sourceToBuilder).inverse();
353 sorted = ModuleDependencySort.sort(builderToModule.keySet());
355 Map<ByteSource, Module> result = new LinkedHashMap<>();
356 for (ModuleBuilder moduleBuilder : sorted) {
357 Module value = checkNotNull(builderToModule.get(moduleBuilder), "Cannot get module for %s", moduleBuilder);
358 result.put(builderToSource.get(moduleBuilder), value);
365 * Parse streams and resolve submodules.
368 * collection of streams to parse
369 * @return map, where key is source stream and value is module builder
371 * @throws YangSyntaxErrorException
373 // TODO: remove ByteSource result after removing YangModelParser
374 private Map<ByteSource, ModuleBuilder> resolveSources(final Collection<ByteSource> streams) throws IOException,
375 YangSyntaxErrorException {
376 Map<ByteSource, ModuleBuilder> builders = parseSourcesToBuilders(streams);
377 return resolveSubmodules(builders);
380 private Map<ByteSource, ModuleBuilder> parseSourcesToBuilders(final Collection<ByteSource> sources)
381 throws IOException, YangSyntaxErrorException {
382 final ParseTreeWalker walker = new ParseTreeWalker();
383 final Map<ByteSource, ParseTree> sourceToTree = parseYangSources(sources);
384 final Map<ByteSource, ModuleBuilder> sourceToBuilder = new LinkedHashMap<>();
387 new YangModelBasicValidator(walker).validate(sourceToTree.values());
389 YangParserListenerImpl yangModelParser;
390 for (Map.Entry<ByteSource, ParseTree> entry : sourceToTree.entrySet()) {
391 ByteSource source = entry.getKey();
392 String path = null; // TODO refactor to Optional
393 // TODO refactor so that path can be retrieved without opening
394 // stream: NamedInputStream -> NamedByteSource ?
395 try (InputStream stream = source.openStream()) {
396 if (stream instanceof NamedInputStream) {
397 path = stream.toString();
400 yangModelParser = new YangParserListenerImpl(path);
401 walker.walk(yangModelParser, entry.getValue());
402 ModuleBuilder moduleBuilder = yangModelParser.getModuleBuilder();
403 moduleBuilder.setSource(source);
404 sourceToBuilder.put(source, moduleBuilder);
406 return sourceToBuilder;
409 private Map<ByteSource, ModuleBuilder> resolveSubmodules(final Map<ByteSource, ModuleBuilder> builders) {
410 Map<ByteSource, ModuleBuilder> modules = new HashMap<>();
411 Set<ModuleBuilder> submodules = new HashSet<>();
412 for (Map.Entry<ByteSource, ModuleBuilder> entry : builders.entrySet()) {
413 ModuleBuilder moduleBuilder = entry.getValue();
414 if (moduleBuilder.isSubmodule()) {
415 submodules.add(moduleBuilder);
417 modules.put(entry.getKey(), moduleBuilder);
421 Collection<ModuleBuilder> values = modules.values();
422 for (ModuleBuilder submodule : submodules) {
423 for (ModuleBuilder module : values) {
424 if (module.getName().equals(submodule.getBelongsTo())) {
425 addSubmoduleToModule(submodule, module);
433 * Traverse collection of builders, find builders representing submodule and
434 * add this submodule to its parent module.
437 * collection of builders containing modules and submodules
438 * @return collection of module builders
440 private Collection<ModuleBuilder> resolveSubmodules(final Collection<ModuleBuilder> builders) {
441 Collection<ModuleBuilder> modules = new HashSet<>();
442 Set<ModuleBuilder> submodules = new HashSet<>();
443 for (ModuleBuilder moduleBuilder : builders) {
444 if (moduleBuilder.isSubmodule()) {
445 submodules.add(moduleBuilder);
447 modules.add(moduleBuilder);
451 for (ModuleBuilder submodule : submodules) {
452 for (ModuleBuilder module : modules) {
453 if (module.getName().equals(submodule.getBelongsTo())) {
454 addSubmoduleToModule(submodule, module);
461 private void addSubmoduleToModule(final ModuleBuilder submodule, final ModuleBuilder module) {
462 submodule.setParent(module);
463 module.getDirtyNodes().addAll(submodule.getDirtyNodes());
464 module.getImports().putAll(submodule.getImports());
465 module.getAugments().addAll(submodule.getAugments());
466 module.getAugmentBuilders().addAll(submodule.getAugmentBuilders());
467 module.getAllAugments().addAll(submodule.getAllAugments());
468 module.getChildNodeBuilders().addAll(submodule.getChildNodeBuilders());
469 module.getChildNodes().putAll(submodule.getChildNodes());
470 module.getGroupings().addAll(submodule.getGroupings());
471 module.getGroupingBuilders().addAll(submodule.getGroupingBuilders());
472 module.getTypeDefinitions().addAll(submodule.getTypeDefinitions());
473 module.getTypeDefinitionBuilders().addAll(submodule.getTypeDefinitionBuilders());
474 module.getUsesNodes().addAll(submodule.getUsesNodes());
475 module.getUsesNodeBuilders().addAll(submodule.getUsesNodeBuilders());
476 module.getAllGroupings().addAll(submodule.getAllGroupings());
477 module.getAllUsesNodes().addAll(submodule.getAllUsesNodes());
478 module.getRpcs().addAll(submodule.getRpcs());
479 module.getAddedRpcs().addAll(submodule.getAddedRpcs());
480 module.getNotifications().addAll(submodule.getNotifications());
481 module.getAddedNotifications().addAll(submodule.getAddedNotifications());
482 module.getIdentities().addAll(submodule.getIdentities());
483 module.getAddedIdentities().addAll(submodule.getAddedIdentities());
484 module.getFeatures().addAll(submodule.getFeatures());
485 module.getAddedFeatures().addAll(submodule.getAddedFeatures());
486 module.getDeviations().addAll(submodule.getDeviations());
487 module.getDeviationBuilders().addAll(submodule.getDeviationBuilders());
488 module.getExtensions().addAll(submodule.getExtensions());
489 module.getAddedExtensions().addAll(submodule.getAddedExtensions());
490 module.getUnknownNodes().addAll(submodule.getUnknownNodes());
491 module.getAllUnknownNodes().addAll(submodule.getAllUnknownNodes());
494 private List<ModuleBuilder> resolveModuleBuilders(final Collection<ByteSource> yangFileStreams,
495 final SchemaContext context) throws IOException, YangSyntaxErrorException {
496 Map<ByteSource, ModuleBuilder> parsedBuilders = resolveSources(yangFileStreams);
497 ModuleBuilder[] builders = new ModuleBuilder[parsedBuilders.size()];
498 parsedBuilders.values().toArray(builders);
500 // module dependency graph sorted
501 List<ModuleBuilder> sorted;
502 if (context == null) {
503 sorted = ModuleDependencySort.sort(builders);
505 sorted = ModuleDependencySort.sortWithContext(context, builders);
511 * Order modules by name and revision.
514 * topologically sorted modules
515 * @return modules ordered by name and revision
517 private LinkedHashMap<String, TreeMap<Date, ModuleBuilder>> orderModules(final List<ModuleBuilder> modules) {
518 final LinkedHashMap<String, TreeMap<Date, ModuleBuilder>> result = new LinkedHashMap<>();
519 for (final ModuleBuilder builder : modules) {
520 if (builder == null) {
523 final String builderName = builder.getName();
524 Date builderRevision = builder.getRevision();
525 if (builderRevision == null) {
526 builderRevision = new Date(0L);
528 TreeMap<Date, ModuleBuilder> builderByRevision = result.get(builderName);
529 if (builderByRevision == null) {
530 builderByRevision = new TreeMap<>();
531 builderByRevision.put(builderRevision, builder);
532 result.put(builderName, builderByRevision);
534 builderByRevision.put(builderRevision, builder);
541 * Find {@code main} dependencies from {@code other} and add them to
549 * collection to fill up
551 private void filterImports(final ModuleBuilder main, final Collection<ModuleBuilder> other,
552 final Collection<ModuleBuilder> filtered) {
553 Map<String, ModuleImport> imports = main.getImports();
555 // if this is submodule, add parent to filtered and pick its imports
556 if (main.isSubmodule()) {
557 TreeMap<Date, ModuleBuilder> dependencies = new TreeMap<>();
558 for (ModuleBuilder mb : other) {
559 if (mb.getName().equals(main.getBelongsTo())) {
560 dependencies.put(mb.getRevision(), mb);
563 ModuleBuilder parent = dependencies.get(dependencies.firstKey());
564 filtered.add(parent);
565 imports.putAll(parent.getImports());
568 for (ModuleImport mi : imports.values()) {
569 for (ModuleBuilder builder : other) {
570 if (mi.getModuleName().equals(builder.getModuleName())) {
571 if (mi.getRevision() == null) {
572 if (!filtered.contains(builder)) {
573 filtered.add(builder);
574 filterImports(builder, other, filtered);
577 if (mi.getRevision().equals(builder.getRevision())) {
578 if (!filtered.contains(builder)) {
579 filtered.add(builder);
580 filterImports(builder, other, filtered);
589 private Map<ByteSource, ParseTree> parseYangSources(final Collection<ByteSource> sources) throws IOException,
590 YangSyntaxErrorException {
591 final Map<ByteSource, ParseTree> trees = new HashMap<>();
592 for (ByteSource source : sources) {
593 trees.put(source, parseYangSource(source));
598 private YangContext parseYangSource(final ByteSource source) throws IOException, YangSyntaxErrorException {
599 try (InputStream stream = source.openStream()) {
600 final ANTLRInputStream input = new ANTLRInputStream(stream);
601 final YangLexer lexer = new YangLexer(input);
602 final CommonTokenStream tokens = new CommonTokenStream(lexer);
603 final YangParser parser = new YangParser(tokens);
604 parser.removeErrorListeners();
606 final YangErrorListener errorListener = new YangErrorListener();
607 parser.addErrorListener(errorListener);
609 final YangContext result = parser.yang();
610 errorListener.validate();
617 * Mini parser: This parsing context does not validate full YANG module,
618 * only parses header up to the revisions and imports.
620 * @see org.opendaylight.yangtools.yang.parser.impl.util.YangModelDependencyInfo
622 public static YangContext parseStreamWithoutErrorListeners(final InputStream yangStream) {
623 YangContext result = null;
625 final ANTLRInputStream input = new ANTLRInputStream(yangStream);
626 final YangLexer lexer = new YangLexer(input);
627 final CommonTokenStream tokens = new CommonTokenStream(lexer);
628 final YangParser parser = new YangParser(tokens);
629 parser.removeErrorListeners();
630 result = parser.yang();
631 } catch (IOException e) {
632 LOG.warn("Exception while reading yang file: " + yangStream, e);
638 * Creates builder-to-module map based on given modules. Method first
639 * resolve unresolved type references, instantiate groupings through uses
640 * statements and perform augmentation.
642 * Node resolving must be performed in following order:
645 * unresolved type references</li>
647 * uses in groupings</li>
649 * uses in other nodes</li>
656 * @return modules mapped on their builders
658 private Map<ModuleBuilder, Module> build(final Map<String, TreeMap<Date, ModuleBuilder>> modules) {
659 resolveDirtyNodes(modules);
660 resolveAugmentsTargetPath(modules);
661 resolveUsesTargetGrouping(modules);
662 resolveUsesForGroupings(modules);
663 resolveUsesForNodes(modules);
664 resolveAugments(modules);
665 resolveIdentities(modules);
666 resolveDeviations(modules);
669 final Map<ModuleBuilder, Module> result = new LinkedHashMap<>();
670 for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
671 for (Map.Entry<Date, ModuleBuilder> childEntry : entry.getValue().entrySet()) {
672 final ModuleBuilder moduleBuilder = childEntry.getValue();
673 final Module module = moduleBuilder.build();
674 result.put(moduleBuilder, module);
681 * Resolve all unresolved type references.
686 private void resolveDirtyNodes(final Map<String, TreeMap<Date, ModuleBuilder>> modules) {
687 for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
688 for (Map.Entry<Date, ModuleBuilder> childEntry : entry.getValue().entrySet()) {
689 final ModuleBuilder module = childEntry.getValue();
690 resolveUnknownNodes(modules, module);
691 resolveDirtyNodes(modules, module);
697 * Search for dirty nodes (node which contains UnknownType) and resolve
701 * all available modules
705 private void resolveDirtyNodes(final Map<String, TreeMap<Date, ModuleBuilder>> modules, final ModuleBuilder module) {
706 final Set<TypeAwareBuilder> dirtyNodes = module.getDirtyNodes();
707 if (!dirtyNodes.isEmpty()) {
708 for (TypeAwareBuilder nodeToResolve : dirtyNodes) {
709 if (nodeToResolve instanceof UnionTypeBuilder) {
710 // special handling for union types
711 resolveTypeUnion((UnionTypeBuilder) nodeToResolve, modules, module);
712 } else if (nodeToResolve.getTypedef() instanceof IdentityrefTypeBuilder) {
713 // special handling for identityref types
714 IdentityrefTypeBuilder idref = (IdentityrefTypeBuilder) nodeToResolve.getTypedef();
715 IdentitySchemaNodeBuilder identity = findBaseIdentity(modules, module, idref.getBaseString(),
717 if (identity == null) {
718 throw new YangParseException(module.getName(), idref.getLine(), "Failed to find base identity");
720 idref.setBaseIdentity(identity);
721 nodeToResolve.setType(idref.build());
723 resolveType(nodeToResolve, modules, module);
730 * Traverse through augmentations of modules and fix their child nodes
736 private void resolveAugmentsTargetPath(final Map<String, TreeMap<Date, ModuleBuilder>> modules) {
737 // collect augments from all loaded modules
738 final List<AugmentationSchemaBuilder> allAugments = new ArrayList<>();
739 for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
740 for (Map.Entry<Date, ModuleBuilder> inner : entry.getValue().entrySet()) {
741 allAugments.addAll(inner.getValue().getAllAugments());
745 for (AugmentationSchemaBuilder augment : allAugments) {
746 setCorrectAugmentTargetPath(modules, augment);
751 * Find augment target and set correct schema path for all its child nodes.
758 private void setCorrectAugmentTargetPath(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
759 final AugmentationSchemaBuilder augment) {
760 ModuleBuilder module = BuilderUtils.getParentModule(augment);
761 final SchemaPath newSchemaPath;
763 Builder parent = augment.getParent();
764 if (parent instanceof UsesNodeBuilder) {
765 DataNodeContainerBuilder usesParent = ((UsesNodeBuilder) parent).getParent();
767 QName baseQName = usesParent.getQName();
768 final QNameModule qnm;
770 if (baseQName == null) {
771 ModuleBuilder m = BuilderUtils.getParentModule(usesParent);
772 qnm = m.getQNameModule();
773 prefix = m.getPrefix();
775 qnm = baseQName.getModule();
776 prefix = baseQName.getPrefix();
779 SchemaPath s = usesParent.getPath();
780 for (QName qn : augment.getTargetPath().getPathFromRoot()) {
781 s = s.createChild(QName.create(qnm, prefix, qn.getLocalName()));
786 final List<QName> newPath = new ArrayList<>();
788 for (QName qn : augment.getTargetPath().getPathFromRoot()) {
789 QNameModule qnm = module.getQNameModule();
790 String localPrefix = qn.getPrefix();
791 if (localPrefix != null && !localPrefix.isEmpty()) {
792 ModuleBuilder currentModule = BuilderUtils.getModuleByPrefix(module, localPrefix);
793 if (currentModule == null) {
794 throw new YangParseException(module.getName(), augment.getLine(), "Module with prefix "
795 + localPrefix + " not found.");
797 qnm = currentModule.getQNameModule();
799 newPath.add(QName.create(qnm, localPrefix, qn.getLocalName()));
803 * FIXME: this method of SchemaPath construction is highly ineffective.
804 * It would be great if we could actually dive into the context,
805 * find the actual target node and reuse its SchemaPath. Can we
808 newSchemaPath = SchemaPath.create(newPath, true);
810 augment.setTargetNodeSchemaPath(newSchemaPath);
812 for (DataSchemaNodeBuilder childNode : augment.getChildNodeBuilders()) {
813 correctPathForAugmentNodes(childNode, augment.getTargetNodeSchemaPath());
818 * Set new schema path to node and all its child nodes based on given parent
819 * path. This method do not change the namespace.
822 * node which schema path should be updated
824 * schema path of parent node
826 private void correctPathForAugmentNodes(final DataSchemaNodeBuilder node, final SchemaPath parentPath) {
827 SchemaPath newPath = parentPath.createChild(node.getQName());
828 node.setPath(newPath);
829 if (node instanceof DataNodeContainerBuilder) {
830 for (DataSchemaNodeBuilder child : ((DataNodeContainerBuilder) node).getChildNodeBuilders()) {
831 correctPathForAugmentNodes(child, node.getPath());
834 if (node instanceof ChoiceBuilder) {
835 for (ChoiceCaseBuilder child : ((ChoiceBuilder) node).getCases()) {
836 correctPathForAugmentNodes(child, node.getPath());
842 * Check augments for mandatory nodes. If the target node is in another
843 * module, then nodes added by the augmentation MUST NOT be mandatory nodes.
844 * If mandatory node is found, throw an exception.
849 private void checkAugmentMandatoryNodes(final Collection<AugmentationSchemaBuilder> augments) {
850 for (AugmentationSchemaBuilder augment : augments) {
851 String augmentPrefix = augment.getTargetPath().getPathFromRoot().iterator().next().getPrefix();
852 ModuleBuilder module = BuilderUtils.getParentModule(augment);
853 String modulePrefix = module.getPrefix();
855 if (augmentPrefix == null || augmentPrefix.isEmpty() || augmentPrefix.equals(modulePrefix)) {
859 for (DataSchemaNodeBuilder childNode : augment.getChildNodeBuilders()) {
860 if (childNode.getConstraints().isMandatory()) {
861 throw new YangParseException(augment.getModuleName(), augment.getLine(),
862 "Error in augment parsing: cannot augment mandatory node "
863 + childNode.getQName().getLocalName());
870 * Go through all augment definitions and resolve them.
873 * all loaded modules topologically sorted (based on dependencies
874 * between each other)
876 private void resolveAugments(final Map<String, TreeMap<Date, ModuleBuilder>> modules) {
877 List<ModuleBuilder> all = new ArrayList<>();
878 for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
879 for (Map.Entry<Date, ModuleBuilder> inner : entry.getValue().entrySet()) {
880 all.add(inner.getValue());
884 for (ModuleBuilder mb : all) {
886 List<AugmentationSchemaBuilder> augments = mb.getAllAugments();
887 checkAugmentMandatoryNodes(augments);
888 Collections.sort(augments, Comparators.AUGMENT_BUILDER_COMP);
889 for (AugmentationSchemaBuilder augment : augments) {
890 if (!(augment.isResolved())) {
891 boolean resolved = resolveAugment(augment, mb, modules);
893 throw new YangParseException(augment.getModuleName(), augment.getLine(),
894 "Error in augment parsing: failed to find augment target: " + augment);
903 * Perform augmentation defined under uses statement.
911 * @return true if augment process succeed
913 private boolean resolveUsesAugment(final AugmentationSchemaBuilder augment, final ModuleBuilder module,
914 final Map<String, TreeMap<Date, ModuleBuilder>> modules) {
915 if (augment.isResolved()) {
919 UsesNodeBuilder usesNode = (UsesNodeBuilder) augment.getParent();
920 DataNodeContainerBuilder parentNode = usesNode.getParent();
921 Optional<SchemaNodeBuilder> potentialTargetNode;
922 SchemaPath resolvedTargetPath = augment.getTargetNodeSchemaPath();
923 if (parentNode instanceof ModuleBuilder && resolvedTargetPath.isAbsolute()) {
924 // Uses is directly used in module body, we lookup
925 // We lookup in data namespace to find correct augmentation target
926 potentialTargetNode = findSchemaNodeInModule(resolvedTargetPath, (ModuleBuilder) parentNode);
928 // Uses is used in local context (be it data namespace or grouping
930 // since all nodes via uses are imported to localName, it is safe to
931 // to proceed only with local names.
933 // Conflicting elements in other namespaces are still not present
934 // since resolveUsesAugment occurs before augmenting from external
936 potentialTargetNode = Optional.<SchemaNodeBuilder> fromNullable(findSchemaNode(augment.getTargetPath()
937 .getPath(), (SchemaNodeBuilder) parentNode));
940 if (potentialTargetNode.isPresent()) {
941 SchemaNodeBuilder targetNode = potentialTargetNode.get();
942 if (targetNode instanceof AugmentationTargetBuilder) {
943 fillAugmentTarget(augment, targetNode);
944 ((AugmentationTargetBuilder) targetNode).addAugmentation(augment);
945 augment.setResolved(true);
948 throw new YangParseException(module.getName(), augment.getLine(), String.format(
949 "Failed to resolve augment in uses. Invalid augment target: %s", potentialTargetNode));
952 throw new YangParseException(module.getName(), augment.getLine(), String.format(
953 "Failed to resolve augment in uses. Invalid augment target path: %s", augment.getTargetPath()));
959 * Find augment target module and perform augmentation.
967 * @return true if augment process succeed
969 private boolean resolveAugment(final AugmentationSchemaBuilder augment, final ModuleBuilder module,
970 final Map<String, TreeMap<Date, ModuleBuilder>> modules) {
971 if (augment.isResolved()) {
975 QName targetModuleName = augment.getTargetPath().getPathFromRoot().iterator().next();
976 ModuleBuilder targetModule = BuilderUtils.getModuleByPrefix(module, targetModuleName.getPrefix());
977 if (targetModule == null) {
978 throw new YangParseException(module.getModuleName(), augment.getLine(), "Failed to resolve augment "
982 return processAugmentation(augment, targetModule);
986 * Go through identity statements defined in current module and resolve
987 * their 'base' statement.
992 private void resolveIdentities(final Map<String, TreeMap<Date, ModuleBuilder>> modules) {
993 for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
994 for (Map.Entry<Date, ModuleBuilder> inner : entry.getValue().entrySet()) {
995 ModuleBuilder module = inner.getValue();
996 final Set<IdentitySchemaNodeBuilder> identities = module.getAddedIdentities();
997 for (IdentitySchemaNodeBuilder identity : identities) {
998 resolveIdentity(modules, module, identity);
1004 private void resolveIdentity(final Map<String, TreeMap<Date, ModuleBuilder>> modules, final ModuleBuilder module,
1005 final IdentitySchemaNodeBuilder identity) {
1006 final String baseIdentityName = identity.getBaseIdentityName();
1007 if (baseIdentityName != null) {
1008 IdentitySchemaNodeBuilder result = null;
1009 if (baseIdentityName.contains(":")) {
1010 final int line = identity.getLine();
1011 String[] splittedBase = baseIdentityName.split(":");
1012 if (splittedBase.length > 2) {
1013 throw new YangParseException(module.getName(), line, "Failed to parse identityref base: "
1014 + baseIdentityName);
1016 String prefix = splittedBase[0];
1017 String name = splittedBase[1];
1018 ModuleBuilder dependentModule = BuilderUtils.getModuleByPrefix(module, prefix);
1019 result = BuilderUtils.findIdentity(dependentModule.getAddedIdentities(), name);
1021 result = BuilderUtils.findIdentity(module.getAddedIdentities(), baseIdentityName);
1023 identity.setBaseIdentity(result);
1028 * Find and add reference of uses target grouping.
1031 * all loaded modules
1033 private void resolveUsesTargetGrouping(final Map<String, TreeMap<Date, ModuleBuilder>> modules) {
1034 final List<UsesNodeBuilder> allUses = new ArrayList<>();
1035 for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
1036 for (Map.Entry<Date, ModuleBuilder> inner : entry.getValue().entrySet()) {
1037 allUses.addAll(inner.getValue().getAllUsesNodes());
1040 for (UsesNodeBuilder usesNode : allUses) {
1041 ModuleBuilder module = BuilderUtils.getParentModule(usesNode);
1042 final GroupingBuilder targetGroupingBuilder = GroupingUtils.getTargetGroupingFromModules(usesNode, modules,
1044 if (targetGroupingBuilder == null) {
1045 throw new YangParseException(module.getName(), usesNode.getLine(), "Referenced grouping '"
1046 + usesNode.getGroupingPathAsString() + "' not found.");
1048 usesNode.setGrouping(targetGroupingBuilder);
1053 * Resolve uses statements defined in groupings.
1056 * all loaded modules
1058 private void resolveUsesForGroupings(final Map<String, TreeMap<Date, ModuleBuilder>> modules) {
1059 final Set<GroupingBuilder> allGroupings = new HashSet<>();
1060 for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
1061 for (Map.Entry<Date, ModuleBuilder> inner : entry.getValue().entrySet()) {
1062 ModuleBuilder module = inner.getValue();
1063 allGroupings.addAll(module.getAllGroupings());
1066 final List<GroupingBuilder> sorted = GroupingSort.sort(allGroupings);
1067 for (GroupingBuilder gb : sorted) {
1068 List<UsesNodeBuilder> usesNodes = new ArrayList<>(GroupingSort.getAllUsesNodes(gb));
1069 Collections.sort(usesNodes, new GroupingUtils.UsesComparator());
1070 for (UsesNodeBuilder usesNode : usesNodes) {
1071 resolveUses(usesNode, modules);
1077 * Resolve uses statements.
1080 * all loaded modules
1082 private void resolveUsesForNodes(final Map<String, TreeMap<Date, ModuleBuilder>> modules) {
1083 for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
1084 for (Map.Entry<Date, ModuleBuilder> inner : entry.getValue().entrySet()) {
1085 ModuleBuilder module = inner.getValue();
1086 List<UsesNodeBuilder> usesNodes = module.getAllUsesNodes();
1087 Collections.sort(usesNodes, new GroupingUtils.UsesComparator());
1088 for (UsesNodeBuilder usesNode : usesNodes) {
1089 resolveUses(usesNode, modules);
1096 * Find target grouping and copy its child nodes to current location with
1100 * uses node to resolve
1102 * all loaded modules
1104 private void resolveUses(final UsesNodeBuilder usesNode, final Map<String, TreeMap<Date, ModuleBuilder>> modules) {
1105 if (!usesNode.isResolved()) {
1106 DataNodeContainerBuilder parent = usesNode.getParent();
1107 ModuleBuilder module = BuilderUtils.getParentModule(parent);
1108 GroupingBuilder target = GroupingUtils.getTargetGroupingFromModules(usesNode, modules, module);
1110 int index = nodeAfterUsesIndex(usesNode);
1111 List<DataSchemaNodeBuilder> targetNodes = target.instantiateChildNodes(parent);
1112 for (DataSchemaNodeBuilder targetNode : targetNodes) {
1113 parent.addChildNode(index++, targetNode);
1115 parent.getTypeDefinitionBuilders().addAll(target.instantiateTypedefs(parent));
1116 parent.getGroupingBuilders().addAll(target.instantiateGroupings(parent));
1117 parent.getUnknownNodes().addAll(target.instantiateUnknownNodes(parent));
1118 usesNode.setResolved(true);
1119 for (AugmentationSchemaBuilder augment : usesNode.getAugmentations()) {
1120 resolveUsesAugment(augment, module, modules);
1123 GroupingUtils.performRefine(usesNode);
1127 private int nodeAfterUsesIndex(final UsesNodeBuilder usesNode) {
1128 DataNodeContainerBuilder parent = usesNode.getParent();
1129 int usesLine = usesNode.getLine();
1131 List<DataSchemaNodeBuilder> childNodes = parent.getChildNodeBuilders();
1132 if (childNodes.isEmpty()) {
1136 DataSchemaNodeBuilder nextNodeAfterUses = null;
1137 for (DataSchemaNodeBuilder childNode : childNodes) {
1138 if (!(childNode.isAddedByUses()) && !(childNode.isAugmenting())) {
1139 if (childNode.getLine() > usesLine) {
1140 nextNodeAfterUses = childNode;
1146 // uses is declared after child nodes
1147 if (nextNodeAfterUses == null) {
1148 return childNodes.size();
1151 return parent.getChildNodeBuilders().indexOf(nextNodeAfterUses);
1155 * Try to find extension describing this unknown node and assign it to
1156 * unknown node builder.
1159 * all loaded modules
1163 private void resolveUnknownNodes(final Map<String, TreeMap<Date, ModuleBuilder>> modules, final ModuleBuilder module) {
1164 for (UnknownSchemaNodeBuilder usnb : module.getAllUnknownNodes()) {
1165 QName nodeType = usnb.getNodeType();
1166 ModuleBuilder dependentModuleBuilder = BuilderUtils.getModuleByPrefix(module, nodeType.getPrefix());
1167 ExtensionBuilder extBuilder = findExtBuilder(nodeType.getLocalName(),
1168 dependentModuleBuilder.getAddedExtensions());
1169 if (extBuilder == null) {
1170 ExtensionDefinition extDef = findExtDef(nodeType.getLocalName(), dependentModuleBuilder.getExtensions());
1171 if (extDef == null) {
1173 "Error in module {} at line {}: Failed to resolve node {}: no such extension definition found.",
1174 module.getName(), usnb.getLine(), usnb);
1176 usnb.setNodeType(new QName(extDef.getQName().getNamespace(), extDef.getQName().getRevision(),
1177 nodeType.getPrefix(), extDef.getQName().getLocalName()));
1178 usnb.setExtensionDefinition(extDef);
1181 usnb.setNodeType(QName.create(extBuilder.getQName().getModule(),
1182 nodeType.getPrefix(), extBuilder.getQName().getLocalName()));
1183 usnb.setExtensionBuilder(extBuilder);
1188 private ExtensionBuilder findExtBuilder(final String name, final Collection<ExtensionBuilder> extensions) {
1189 for (ExtensionBuilder extension : extensions) {
1190 if (extension.getQName().getLocalName().equals(name)) {
1197 private ExtensionDefinition findExtDef(final String name, final Collection<ExtensionDefinition> extensions) {
1198 for (ExtensionDefinition extension : extensions) {
1199 if (extension.getQName().getLocalName().equals(name)) {
1207 * Traverse through modules and resolve their deviation statements.
1210 * all loaded modules
1212 private void resolveDeviations(final Map<String, TreeMap<Date, ModuleBuilder>> modules) {
1213 for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
1214 for (Map.Entry<Date, ModuleBuilder> inner : entry.getValue().entrySet()) {
1215 ModuleBuilder b = inner.getValue();
1216 resolveDeviation(modules, b);
1222 * Traverse through module and resolve its deviation statements.
1225 * all loaded modules
1227 * module in which resolve deviations
1229 private void resolveDeviation(final Map<String, TreeMap<Date, ModuleBuilder>> modules, final ModuleBuilder module) {
1230 for (DeviationBuilder dev : module.getDeviationBuilders()) {
1231 SchemaPath targetPath = dev.getTargetPath();
1232 Iterable<QName> path = targetPath.getPathFromRoot();
1233 QName q0 = path.iterator().next();
1234 String prefix = q0.getPrefix();
1235 if (prefix == null) {
1236 prefix = module.getPrefix();
1239 ModuleBuilder dependentModuleBuilder = BuilderUtils.getModuleByPrefix(module, prefix);
1240 processDeviation(dev, dependentModuleBuilder, path, module);
1245 * Correct deviation target path in deviation builder.
1249 * @param dependentModuleBuilder
1250 * module containing deviation target
1252 * current deviation target path
1256 private void processDeviation(final DeviationBuilder dev, final ModuleBuilder dependentModuleBuilder,
1257 final Iterable<QName> path, final ModuleBuilder module) {
1258 final int line = dev.getLine();
1259 Builder currentParent = dependentModuleBuilder;
1261 for (QName q : path) {
1262 if (currentParent == null) {
1263 throw new YangParseException(module.getName(), line, FAIL_DEVIATION_TARGET);
1265 String name = q.getLocalName();
1266 if (currentParent instanceof DataNodeContainerBuilder) {
1267 currentParent = ((DataNodeContainerBuilder) currentParent).getDataChildByName(name);
1271 if (!(currentParent instanceof SchemaNodeBuilder)) {
1272 throw new YangParseException(module.getName(), line, FAIL_DEVIATION_TARGET);
1274 dev.setTargetPath(((SchemaNodeBuilder) currentParent).getPath());