2 * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.yangtools.yang.parser.impl;
10 import static org.opendaylight.yangtools.yang.parser.util.ParserUtils.*;
11 import static org.opendaylight.yangtools.yang.parser.util.TypeUtils.*;
14 import java.io.FileInputStream;
15 import java.io.FileNotFoundException;
16 import java.io.IOException;
17 import java.io.InputStream;
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.HashSet;
25 import java.util.LinkedHashMap;
26 import java.util.LinkedHashSet;
27 import java.util.List;
29 import java.util.Map.Entry;
31 import java.util.TreeMap;
33 import org.antlr.v4.runtime.ANTLRInputStream;
34 import org.antlr.v4.runtime.CommonTokenStream;
35 import org.antlr.v4.runtime.tree.ParseTree;
36 import org.antlr.v4.runtime.tree.ParseTreeWalker;
37 import org.opendaylight.yangtools.antlrv4.code.gen.YangLexer;
38 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser;
39 import org.opendaylight.yangtools.yang.common.QName;
40 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
41 import org.opendaylight.yangtools.yang.model.api.ExtensionDefinition;
42 import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
43 import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
44 import org.opendaylight.yangtools.yang.model.api.Module;
45 import org.opendaylight.yangtools.yang.model.api.ModuleImport;
46 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
47 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
48 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
49 import org.opendaylight.yangtools.yang.model.parser.api.YangModelParser;
50 import org.opendaylight.yangtools.yang.parser.builder.api.AugmentationSchemaBuilder;
51 import org.opendaylight.yangtools.yang.parser.builder.api.Builder;
52 import org.opendaylight.yangtools.yang.parser.builder.api.DataNodeContainerBuilder;
53 import org.opendaylight.yangtools.yang.parser.builder.api.DataSchemaNodeBuilder;
54 import org.opendaylight.yangtools.yang.parser.builder.api.GroupingBuilder;
55 import org.opendaylight.yangtools.yang.parser.builder.api.SchemaNodeBuilder;
56 import org.opendaylight.yangtools.yang.parser.builder.api.TypeAwareBuilder;
57 import org.opendaylight.yangtools.yang.parser.builder.api.TypeDefinitionBuilder;
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.GroupingSort;
67 import org.opendaylight.yangtools.yang.parser.util.GroupingUtils;
68 import org.opendaylight.yangtools.yang.parser.util.ModuleDependencySort;
69 import org.opendaylight.yangtools.yang.parser.util.ParserUtils;
70 import org.opendaylight.yangtools.yang.parser.util.YangParseException;
71 import org.opendaylight.yangtools.yang.validator.YangModelBasicValidator;
72 import org.slf4j.Logger;
73 import org.slf4j.LoggerFactory;
75 import com.google.common.base.Preconditions;
76 import com.google.common.collect.Lists;
77 import com.google.common.collect.Maps;
78 import com.google.common.collect.Sets;
80 public final class YangParserImpl implements YangModelParser {
81 private static final Logger LOG = LoggerFactory.getLogger(YangParserImpl.class);
83 private static final String FAIL_DEVIATION_TARGET = "Failed to find deviation target.";
86 public Set<Module> parseYangModels(final File yangFile, final File directory) {
87 Preconditions.checkState(yangFile.exists(), yangFile + " does not exists");
88 Preconditions.checkState(directory.exists(), directory + " does not exists");
89 Preconditions.checkState(directory.isDirectory(), directory + " is not a directory");
91 final String yangFileName = yangFile.getName();
92 final String[] fileList = directory.list();
93 Preconditions.checkNotNull(fileList, directory + " not found");
95 FileInputStream yangFileStream = null;
96 LinkedHashMap<InputStream, File> streamToFileMap = new LinkedHashMap<>();
99 yangFileStream = new FileInputStream(yangFile);
100 streamToFileMap.put(yangFileStream, yangFile);
101 } catch (FileNotFoundException e) {
102 LOG.warn("Exception while reading yang file: " + yangFile.getName(), e);
105 for (String fileName : fileList) {
106 if (fileName.equals(yangFileName)) {
109 File dependency = new File(directory, fileName);
111 if (dependency.isFile()) {
112 streamToFileMap.put(new FileInputStream(dependency), dependency);
114 } catch (FileNotFoundException e) {
115 LOG.warn("Exception while reading yang file: " + fileName, e);
119 Map<InputStream, ModuleBuilder> parsedBuilders = parseModuleBuilders(new ArrayList<>(streamToFileMap.keySet()),
120 new HashMap<ModuleBuilder, InputStream>());
121 ModuleBuilder main = parsedBuilders.get(yangFileStream);
123 List<ModuleBuilder> moduleBuilders = new ArrayList<>();
124 moduleBuilders.add(main);
125 filterImports(main, new ArrayList<>(parsedBuilders.values()), moduleBuilders);
127 ModuleBuilder[] builders = new ModuleBuilder[moduleBuilders.size()];
128 moduleBuilders.toArray(builders);
130 // module dependency graph sorted
131 List<ModuleBuilder> sorted = ModuleDependencySort.sort(builders);
133 final LinkedHashMap<String, TreeMap<Date, ModuleBuilder>> modules = orderModules(sorted);
134 return new LinkedHashSet<>(build(modules).values());
138 public Set<Module> parseYangModels(final List<File> yangFiles) {
139 return Sets.newLinkedHashSet(parseYangModelsMapped(yangFiles).values());
143 public Set<Module> parseYangModels(final List<File> yangFiles, final SchemaContext context) {
144 if (yangFiles != null) {
145 final Map<InputStream, File> inputStreams = Maps.newHashMap();
147 for (final File yangFile : yangFiles) {
149 inputStreams.put(new FileInputStream(yangFile), yangFile);
150 } catch (FileNotFoundException e) {
151 LOG.warn("Exception while reading yang file: " + yangFile.getName(), e);
155 Map<ModuleBuilder, InputStream> builderToStreamMap = Maps.newHashMap();
156 final Map<String, TreeMap<Date, ModuleBuilder>> modules = resolveModuleBuilders(
157 Lists.newArrayList(inputStreams.keySet()), builderToStreamMap);
159 for (InputStream is : inputStreams.keySet()) {
162 } catch (IOException e) {
163 LOG.debug("Failed to close stream.");
167 return new LinkedHashSet<>(buildWithContext(modules, context).values());
169 return Collections.emptySet();
173 public Set<Module> parseYangModelsFromStreams(final List<InputStream> yangModelStreams) {
174 return Sets.newHashSet(parseYangModelsFromStreamsMapped(yangModelStreams).values());
178 public Set<Module> parseYangModelsFromStreams(final List<InputStream> yangModelStreams, SchemaContext context) {
179 if (yangModelStreams != null) {
180 Map<ModuleBuilder, InputStream> builderToStreamMap = Maps.newHashMap();
181 final Map<String, TreeMap<Date, ModuleBuilder>> modules = resolveModuleBuildersWithContext(
182 yangModelStreams, builderToStreamMap, context);
183 return new LinkedHashSet<>(buildWithContext(modules, context).values());
185 return Collections.emptySet();
189 public Map<File, Module> parseYangModelsMapped(List<File> yangFiles) {
190 if (yangFiles != null) {
191 final Map<InputStream, File> inputStreams = Maps.newHashMap();
193 for (final File yangFile : yangFiles) {
195 inputStreams.put(new FileInputStream(yangFile), yangFile);
196 } catch (FileNotFoundException e) {
197 LOG.warn("Exception while reading yang file: " + yangFile.getName(), e);
201 Map<ModuleBuilder, InputStream> builderToStreamMap = Maps.newHashMap();
202 final Map<String, TreeMap<Date, ModuleBuilder>> modules = resolveModuleBuilders(
203 Lists.newArrayList(inputStreams.keySet()), builderToStreamMap);
205 for (InputStream is : inputStreams.keySet()) {
208 } catch (IOException e) {
209 LOG.debug("Failed to close stream.");
213 Map<File, Module> retVal = Maps.newLinkedHashMap();
214 Map<ModuleBuilder, Module> builderToModuleMap = build(modules);
216 for (Entry<ModuleBuilder, Module> builderToModule : builderToModuleMap.entrySet()) {
217 retVal.put(inputStreams.get(builderToStreamMap.get(builderToModule.getKey())),
218 builderToModule.getValue());
223 return Collections.emptyMap();
227 public Map<InputStream, Module> parseYangModelsFromStreamsMapped(final List<InputStream> yangModelStreams) {
228 Map<ModuleBuilder, InputStream> builderToStreamMap = Maps.newHashMap();
230 final Map<String, TreeMap<Date, ModuleBuilder>> modules = resolveModuleBuilders(yangModelStreams,
232 Map<InputStream, Module> retVal = Maps.newLinkedHashMap();
233 Map<ModuleBuilder, Module> builderToModuleMap = build(modules);
235 for (Entry<ModuleBuilder, Module> builderToModule : builderToModuleMap.entrySet()) {
236 retVal.put(builderToStreamMap.get(builderToModule.getKey()), builderToModule.getValue());
242 public SchemaContext resolveSchemaContext(final Set<Module> modules) {
243 return new SchemaContextImpl(modules);
246 private Map<InputStream, ModuleBuilder> parseModuleBuilders(List<InputStream> inputStreams,
247 Map<ModuleBuilder, InputStream> streamToBuilderMap) {
249 final ParseTreeWalker walker = new ParseTreeWalker();
250 final Map<InputStream, ParseTree> trees = parseStreams(inputStreams);
251 final Map<InputStream, ModuleBuilder> builders = new LinkedHashMap<>();
254 new YangModelBasicValidator(walker).validate(new ArrayList<>(trees.values()));
256 YangParserListenerImpl yangModelParser;
257 for (Map.Entry<InputStream, ParseTree> entry : trees.entrySet()) {
258 yangModelParser = new YangParserListenerImpl();
259 walker.walk(yangModelParser, entry.getValue());
260 ModuleBuilder moduleBuilder = yangModelParser.getModuleBuilder();
262 // We expect the order of trees and streams has to be the same
263 streamToBuilderMap.put(moduleBuilder, entry.getKey());
264 builders.put(entry.getKey(), moduleBuilder);
270 private Map<String, TreeMap<Date, ModuleBuilder>> resolveModuleBuilders(final List<InputStream> yangFileStreams,
271 Map<ModuleBuilder, InputStream> streamToBuilderMap) {
272 return resolveModuleBuildersWithContext(yangFileStreams, streamToBuilderMap, null);
275 private Map<String, TreeMap<Date, ModuleBuilder>> resolveModuleBuildersWithContext(
276 final List<InputStream> yangFileStreams, final Map<ModuleBuilder, InputStream> streamToBuilderMap,
277 final SchemaContext context) {
278 Map<InputStream, ModuleBuilder> parsedBuilders = parseModuleBuilders(yangFileStreams, streamToBuilderMap);
279 ModuleBuilder[] builders = new ModuleBuilder[parsedBuilders.size()];
280 parsedBuilders.values().toArray(builders);
282 // module dependency graph sorted
283 List<ModuleBuilder> sorted;
284 if (context == null) {
285 sorted = ModuleDependencySort.sort(builders);
287 sorted = ModuleDependencySort.sortWithContext(context, builders);
290 return orderModules(sorted);
294 * Order modules by name and revision.
298 * @return modules ordered by name and revision
300 private LinkedHashMap<String, TreeMap<Date, ModuleBuilder>> orderModules(List<ModuleBuilder> modules) {
301 // LinkedHashMap must be used to preserve order
302 LinkedHashMap<String, TreeMap<Date, ModuleBuilder>> result = new LinkedHashMap<>();
303 for (final ModuleBuilder builder : modules) {
304 if (builder == null) {
307 final String builderName = builder.getName();
308 Date builderRevision = builder.getRevision();
309 if (builderRevision == null) {
310 builderRevision = new Date(0L);
312 TreeMap<Date, ModuleBuilder> builderByRevision = result.get(builderName);
313 if (builderByRevision == null) {
314 builderByRevision = new TreeMap<>();
316 builderByRevision.put(builderRevision, builder);
317 result.put(builderName, builderByRevision);
322 private void filterImports(ModuleBuilder main, List<ModuleBuilder> other, List<ModuleBuilder> filtered) {
323 for (ModuleImport mi : main.getModuleImports()) {
324 for (ModuleBuilder builder : other) {
325 if (mi.getModuleName().equals(builder.getModuleName())) {
326 if (mi.getRevision() == null) {
327 if (!filtered.contains(builder)) {
328 filtered.add(builder);
329 filterImports(builder, other, filtered);
332 if (mi.getRevision().equals(builder.getRevision())) {
333 if (!filtered.contains(builder)) {
334 filtered.add(builder);
335 filterImports(builder, other, filtered);
344 private Map<InputStream, ParseTree> parseStreams(final List<InputStream> yangStreams) {
345 final Map<InputStream, ParseTree> trees = new HashMap<>();
346 for (InputStream yangStream : yangStreams) {
347 trees.put(yangStream, parseStream(yangStream));
352 private ParseTree parseStream(final InputStream yangStream) {
353 ParseTree result = null;
355 final ANTLRInputStream input = new ANTLRInputStream(yangStream);
356 final YangLexer lexer = new YangLexer(input);
357 final CommonTokenStream tokens = new CommonTokenStream(lexer);
358 final YangParser parser = new YangParser(tokens);
359 parser.removeErrorListeners();
360 YangErrorListener errorListener = new YangErrorListener();
361 parser.addErrorListener(errorListener);
362 result = parser.yang();
363 errorListener.validate();
364 } catch (IOException e) {
365 LOG.warn("Exception while reading yang file: " + yangStream, e);
370 private Map<ModuleBuilder, Module> build(final Map<String, TreeMap<Date, ModuleBuilder>> modules) {
371 // fix unresolved nodes
372 resolveDirtyNodes(modules);
373 resolveAugmentsTargetPath(modules, null);
374 resolveUsesTargetGrouping(modules, null);
375 resolveUsesForGroupings(modules, null);
376 resolveUsesForNodes(modules, null);
377 resolveAugments(modules);
378 resolveDeviations(modules);
381 final Map<ModuleBuilder, Module> result = new LinkedHashMap<>();
382 for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
383 for (Map.Entry<Date, ModuleBuilder> childEntry : entry.getValue().entrySet()) {
384 final ModuleBuilder moduleBuilder = childEntry.getValue();
385 final Module module = moduleBuilder.build();
386 result.put(moduleBuilder, module);
392 private Map<ModuleBuilder, Module> buildWithContext(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
393 final SchemaContext context) {
394 // fix unresolved nodes
395 resolvedDirtyNodesWithContext(modules, context);
396 resolveAugmentsTargetPath(modules, context);
397 resolveUsesTargetGrouping(modules, context);
398 resolveUsesForGroupings(modules, context);
399 resolveUsesForNodes(modules, context);
400 resolveAugmentsWithContext(modules, context);
401 resolveDeviationsWithContext(modules, context);
404 final Map<ModuleBuilder, Module> result = new LinkedHashMap<>();
405 for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
406 for (Map.Entry<Date, ModuleBuilder> childEntry : entry.getValue().entrySet()) {
407 final ModuleBuilder moduleBuilder = childEntry.getValue();
408 final Module module = moduleBuilder.build();
409 result.put(moduleBuilder, module);
415 private void resolveDirtyNodes(final Map<String, TreeMap<Date, ModuleBuilder>> modules) {
416 for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
417 for (Map.Entry<Date, ModuleBuilder> childEntry : entry.getValue().entrySet()) {
418 final ModuleBuilder module = childEntry.getValue();
419 resolveUnknownNodes(modules, module);
420 resolveIdentities(modules, module);
421 resolveDirtyNodes(modules, module);
426 private void resolvedDirtyNodesWithContext(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
427 final SchemaContext context) {
428 for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
429 for (Map.Entry<Date, ModuleBuilder> childEntry : entry.getValue().entrySet()) {
430 final ModuleBuilder module = childEntry.getValue();
431 resolveUnknownNodesWithContext(modules, module, context);
432 resolveIdentitiesWithContext(modules, module, context);
433 resolveDirtyNodesWithContext(modules, module, context);
439 * Search for dirty nodes (node which contains UnknownType) and resolve
443 * all available modules
447 private void resolveDirtyNodes(final Map<String, TreeMap<Date, ModuleBuilder>> modules, final ModuleBuilder module) {
448 final Set<TypeAwareBuilder> dirtyNodes = module.getDirtyNodes();
449 if (!dirtyNodes.isEmpty()) {
450 for (TypeAwareBuilder nodeToResolve : dirtyNodes) {
451 if (nodeToResolve instanceof UnionTypeBuilder) {
452 // special handling for union types
453 resolveTypeUnion((UnionTypeBuilder) nodeToResolve, modules, module);
454 } else if (nodeToResolve.getTypedef() instanceof IdentityrefTypeBuilder) {
455 // special handling for identityref types
456 IdentityrefTypeBuilder idref = (IdentityrefTypeBuilder) nodeToResolve.getTypedef();
457 IdentitySchemaNodeBuilder identity = findBaseIdentity(modules, module, idref.getBaseString(),
459 if (identity == null) {
460 throw new YangParseException(module.getName(), idref.getLine(), "Failed to find base identity");
462 idref.setBaseIdentity(identity);
463 nodeToResolve.setType(idref.build(null));
465 resolveType(nodeToResolve, modules, module);
471 private void resolveDirtyNodesWithContext(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
472 final ModuleBuilder module, SchemaContext context) {
473 final Set<TypeAwareBuilder> dirtyNodes = module.getDirtyNodes();
474 if (!dirtyNodes.isEmpty()) {
475 for (TypeAwareBuilder nodeToResolve : dirtyNodes) {
476 if (nodeToResolve instanceof UnionTypeBuilder) {
477 // special handling for union types
478 resolveTypeUnionWithContext((UnionTypeBuilder) nodeToResolve, modules, module, context);
479 } else if (nodeToResolve.getTypedef() instanceof IdentityrefTypeBuilder) {
480 // special handling for identityref types
481 IdentityrefTypeBuilder idref = (IdentityrefTypeBuilder) nodeToResolve.getTypedef();
482 IdentitySchemaNodeBuilder identity = findBaseIdentity(modules, module, idref.getBaseString(),
484 idref.setBaseIdentity(identity);
485 nodeToResolve.setType(idref.build(null));
487 resolveTypeWithContext(nodeToResolve, modules, module, context);
494 * Correct augment target path.
499 private void resolveAugmentsTargetPath(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
500 SchemaContext context) {
501 // collect augments from all loaded modules
502 final List<AugmentationSchemaBuilder> allAugments = new ArrayList<>();
503 for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
504 for (Map.Entry<Date, ModuleBuilder> inner : entry.getValue().entrySet()) {
505 allAugments.addAll(inner.getValue().getAllAugments());
509 for (AugmentationSchemaBuilder augment : allAugments) {
510 setCorrectAugmentTargetPath(modules, augment, context);
514 private void setCorrectAugmentTargetPath(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
515 final AugmentationSchemaBuilder augment, final SchemaContext context) {
516 ModuleBuilder module = ParserUtils.getParentModule(augment);
517 SchemaPath oldSchemaPath = augment.getTargetPath();
518 List<QName> oldPath = oldSchemaPath.getPath();
519 List<QName> newPath = new ArrayList<>();
521 Builder parent = augment.getParent();
522 if (parent instanceof UsesNodeBuilder) {
523 DataNodeContainerBuilder usesParent = ((UsesNodeBuilder) parent).getParent();
524 newPath.addAll(usesParent.getPath().getPath());
529 QName baseQName = usesParent.getQName();
530 if (baseQName == null) {
531 ModuleBuilder m = ParserUtils.getParentModule(usesParent);
532 ns = m.getNamespace();
533 revision = m.getRevision();
534 prefix = m.getPrefix();
536 ns = baseQName.getNamespace();
537 revision = baseQName.getRevision();
538 prefix = baseQName.getPrefix();
541 for (QName qn : oldSchemaPath.getPath()) {
542 newPath.add(new QName(ns, revision, prefix, qn.getLocalName()));
546 for (QName qn : oldPath) {
547 URI ns = module.getNamespace();
548 Date rev = module.getRevision();
549 String pref = module.getPrefix();
550 String localPrefix = qn.getPrefix();
551 if (localPrefix != null && !("".equals(localPrefix))) {
552 ModuleBuilder currentModule = ParserUtils.findModuleFromBuilders(modules, module, localPrefix,
554 if (currentModule == null) {
555 Module m = ParserUtils.findModuleFromContext(context, module, localPrefix, augment.getLine());
557 throw new YangParseException(module.getName(), augment.getLine(), "Module with prefix "
558 + localPrefix + " not found.");
560 ns = m.getNamespace();
561 rev = m.getRevision();
562 pref = m.getPrefix();
564 ns = currentModule.getNamespace();
565 rev = currentModule.getRevision();
566 pref = currentModule.getPrefix();
569 newPath.add(new QName(ns, rev, pref, qn.getLocalName()));
572 augment.setTargetNodeSchemaPath(new SchemaPath(newPath, augment.getTargetPath().isAbsolute()));
574 for (DataSchemaNodeBuilder childNode : augment.getChildNodeBuilders()) {
575 correctPathForAugmentNodes(childNode, augment.getTargetNodeSchemaPath());
579 private void correctPathForAugmentNodes(DataSchemaNodeBuilder node, SchemaPath parentPath) {
580 node.setPath(ParserUtils.createSchemaPath(parentPath, node.getQName()));
581 if (node instanceof DataNodeContainerBuilder) {
582 for (DataSchemaNodeBuilder child : ((DataNodeContainerBuilder) node).getChildNodeBuilders()) {
583 correctPathForAugmentNodes(child, node.getPath());
589 * Go through all augment definitions and perform augmentation. It is
590 * expected that modules are already sorted by their dependencies.
595 private void resolveAugments(final Map<String, TreeMap<Date, ModuleBuilder>> modules) {
596 // collect augments from all loaded modules
597 final List<AugmentationSchemaBuilder> allAugments = new ArrayList<>();
598 for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
599 for (Map.Entry<Date, ModuleBuilder> inner : entry.getValue().entrySet()) {
600 allAugments.addAll(inner.getValue().getAllAugments());
604 checkAugmentMandatoryNodes(allAugments);
606 for (int i = 0; i < allAugments.size(); i++) {
608 final AugmentationSchemaBuilder augment = allAugments.get(i);
609 // create collection of others
610 List<AugmentationSchemaBuilder> others = new ArrayList<>(allAugments);
611 others.remove(augment);
614 boolean resolved = resolveAugment(modules, augment);
615 // while not resolved
617 while (!(resolved) && j < others.size()) {
618 // try to resolve next augment
619 resolveAugment(modules, others.get(j));
620 // then try to resolve first again
621 resolved = resolveAugment(modules, augment);
627 throw new YangParseException(augment.getModuleName(), augment.getLine(),
628 "Error in augment parsing: failed to find augment target");
634 * Check augments for mandatory nodes. If the target node is in another
635 * module, then nodes added by the augmentation MUST NOT be mandatory nodes.
636 * If mandatory node is found, throw an exception.
641 private void checkAugmentMandatoryNodes(Collection<AugmentationSchemaBuilder> augments) {
642 for (AugmentationSchemaBuilder augment : augments) {
643 String augmentPrefix = augment.getTargetPath().getPath().get(0).getPrefix();
644 ModuleBuilder module = ParserUtils.getParentModule(augment);
645 String modulePrefix = module.getPrefix();
647 if (augmentPrefix == null || augmentPrefix.isEmpty() || augmentPrefix.equals(modulePrefix)) {
651 for (DataSchemaNodeBuilder childNode : augment.getChildNodeBuilders()) {
652 if (childNode.getConstraints().isMandatory()) {
653 throw new YangParseException(augment.getModuleName(), augment.getLine(),
654 "Error in augment parsing: cannot augment mandatory node "
655 + childNode.getQName().getLocalName());
662 * Search for augment target and perform augmentation.
668 * @return true if target node found, false otherwise
670 private boolean resolveAugment(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
671 final AugmentationSchemaBuilder augment) {
672 if (augment.isResolved()) {
676 int line = augment.getLine();
677 ModuleBuilder module = getParentModule(augment);
678 List<QName> path = augment.getTargetPath().getPath();
679 Builder augmentParent = augment.getParent();
681 Builder firstNodeParent;
682 if (augmentParent instanceof ModuleBuilder) {
683 // if augment is defined under module, parent of first node is
685 final QName firstNameInPath = path.get(0);
686 String prefix = firstNameInPath.getPrefix();
687 if (prefix == null) {
688 prefix = module.getPrefix();
690 firstNodeParent = findModuleFromBuilders(modules, module, prefix, line);
691 } else if (augmentParent instanceof UsesNodeBuilder) {
692 firstNodeParent = augmentParent.getParent();
694 // augment can be defined only under module or uses
695 throw new YangParseException(augment.getModuleName(), line,
696 "Failed to parse augment: Unresolved parent of augment: " + augmentParent);
699 return processAugmentation(augment, firstNodeParent, path);
703 * Go through all augment definitions and resolve them. This method works in
704 * same way as {@link #resolveAugments(Map)} except that if target node is
705 * not found in loaded modules, it search for target node in given context.
710 * SchemaContext containing already resolved modules
712 private void resolveAugmentsWithContext(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
713 final SchemaContext context) {
714 // collect augments from all loaded modules
715 final List<AugmentationSchemaBuilder> allAugments = new ArrayList<>();
716 for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
717 for (Map.Entry<Date, ModuleBuilder> inner : entry.getValue().entrySet()) {
718 allAugments.addAll(inner.getValue().getAllAugments());
722 for (int i = 0; i < allAugments.size(); i++) {
723 // pick augment from list
724 final AugmentationSchemaBuilder augment = allAugments.get(i);
726 boolean resolved = resolveAugmentWithContext(modules, augment, context);
727 // while not resolved
729 while (!(resolved) && j < allAugments.size()) {
730 // try to resolve next augment
731 resolveAugmentWithContext(modules, allAugments.get(j), context);
732 // then try to resolve first again
733 resolved = resolveAugmentWithContext(modules, augment, context);
738 throw new YangParseException(augment.getModuleName(), augment.getLine(),
739 "Error in augment parsing: failed to find augment target");
745 * Search for augment target and perform augmentation.
752 * SchemaContext containing already resolved modules
753 * @return true if target node found, false otherwise
755 private boolean resolveAugmentWithContext(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
756 final AugmentationSchemaBuilder augment, final SchemaContext context) {
757 if (augment.isResolved()) {
760 int line = augment.getLine();
761 ModuleBuilder module = getParentModule(augment);
762 List<QName> path = augment.getTargetNodeSchemaPath().getPath();
763 final QName firstNameInPath = path.get(0);
764 String prefix = firstNameInPath.getPrefix();
765 if (prefix == null) {
766 prefix = module.getPrefix();
768 Builder augmentParent = augment.getParent();
769 Builder currentParent;
770 if (augmentParent instanceof ModuleBuilder) {
771 // if augment is defined under module, first parent is target module
772 currentParent = findModuleFromBuilders(modules, module, prefix, line);
773 } else if (augmentParent instanceof UsesNodeBuilder) {
774 currentParent = augmentParent.getParent();
776 // augment can be defined only under module or uses
777 throw new YangParseException(augment.getModuleName(), augment.getLine(),
778 "Error in augment parsing: Unresolved parent of augment: " + augmentParent);
781 if (currentParent == null) {
782 return processAugmentationOnContext(augment, path, module, prefix, context);
784 return processAugmentation(augment, currentParent, path);
789 * Go through identity statements defined in current module and resolve
790 * their 'base' statement if present.
795 * module being resolved
797 private void resolveIdentities(final Map<String, TreeMap<Date, ModuleBuilder>> modules, final ModuleBuilder module) {
798 final Set<IdentitySchemaNodeBuilder> identities = module.getIdentities();
799 for (IdentitySchemaNodeBuilder identity : identities) {
800 final String baseIdentityName = identity.getBaseIdentityName();
801 final int line = identity.getLine();
802 if (baseIdentityName != null) {
803 IdentitySchemaNodeBuilder baseIdentity = findBaseIdentity(modules, module, baseIdentityName, line);
804 if (baseIdentity == null) {
805 throw new YangParseException(module.getName(), identity.getLine(), "Failed to find base identity");
807 identity.setBaseIdentity(baseIdentity);
814 * Go through identity statements defined in current module and resolve
815 * their 'base' statement. Method tries to find base identity in given
816 * modules. If base identity is not found, method will search it in context.
823 * SchemaContext containing already resolved modules
825 private void resolveIdentitiesWithContext(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
826 final ModuleBuilder module, final SchemaContext context) {
827 final Set<IdentitySchemaNodeBuilder> identities = module.getIdentities();
828 for (IdentitySchemaNodeBuilder identity : identities) {
829 final String baseIdentityName = identity.getBaseIdentityName();
830 final int line = identity.getLine();
831 if (baseIdentityName != null) {
832 IdentitySchemaNodeBuilder baseIdentity = findBaseIdentity(modules, module, baseIdentityName, line);
833 if (baseIdentity == null) {
834 IdentitySchemaNode baseId = findBaseIdentityFromContext(modules, module, baseIdentityName, line,
836 identity.setBaseIdentity(baseId);
838 identity.setBaseIdentity(baseIdentity);
845 * Find and add reference of uses target grouping.
850 * SchemaContext containing already resolved modules or null if
851 * context is not available
853 private void resolveUsesTargetGrouping(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
854 final SchemaContext context) {
855 final List<UsesNodeBuilder> allUses = new ArrayList<>();
856 for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
857 for (Map.Entry<Date, ModuleBuilder> inner : entry.getValue().entrySet()) {
858 allUses.addAll(inner.getValue().getAllUsesNodes());
861 for (UsesNodeBuilder usesNode : allUses) {
862 ModuleBuilder module = ParserUtils.getParentModule(usesNode);
863 final GroupingBuilder targetGroupingBuilder = GroupingUtils.getTargetGroupingFromModules(usesNode, modules,
865 if (targetGroupingBuilder == null) {
866 if (context == null) {
867 throw new YangParseException(module.getName(), usesNode.getLine(), "Referenced grouping '"
868 + usesNode.getGroupingPathAsString() + "' not found.");
870 GroupingDefinition targetGroupingDefinition = GroupingUtils.getTargetGroupingFromContext(usesNode,
872 usesNode.setGroupingDefinition(targetGroupingDefinition);
875 usesNode.setGrouping(targetGroupingBuilder);
880 private void resolveUsesForGroupings(final Map<String, TreeMap<Date, ModuleBuilder>> modules, final SchemaContext context) {
881 final Set<GroupingBuilder> allGroupings = new HashSet<>();
882 for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
883 for (Map.Entry<Date, ModuleBuilder> inner : entry.getValue().entrySet()) {
884 ModuleBuilder module = inner.getValue();
885 allGroupings.addAll(module.getAllGroupings());
888 final List<GroupingBuilder> sorted = GroupingSort.sort(allGroupings);
889 for (GroupingBuilder gb : sorted) {
890 List<UsesNodeBuilder> usesNodes = new ArrayList<>(GroupingSort.getAllUsesNodes(gb));
891 Collections.sort(usesNodes, new GroupingUtils.UsesComparator());
892 for (UsesNodeBuilder usesNode : usesNodes) {
893 resolveUses(usesNode, modules, context);
898 private void resolveUsesForNodes(final Map<String, TreeMap<Date, ModuleBuilder>> modules, final SchemaContext context) {
899 for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
900 for (Map.Entry<Date, ModuleBuilder> inner : entry.getValue().entrySet()) {
901 ModuleBuilder module = inner.getValue();
902 List<UsesNodeBuilder> usesNodes = module.getAllUsesNodes();
903 Collections.sort(usesNodes, new GroupingUtils.UsesComparator());
904 for (UsesNodeBuilder usesNode : usesNodes) {
905 resolveUses(usesNode, modules, context);
911 private void resolveUses(UsesNodeBuilder usesNode,
912 final Map<String, TreeMap<Date, ModuleBuilder>> modules, final SchemaContext context) {
913 if (!usesNode.isResolved()) {
914 final int line = usesNode.getLine();
915 DataNodeContainerBuilder parent = usesNode.getParent();
916 ModuleBuilder module = ParserUtils.getParentModule(parent);
917 GroupingBuilder target = GroupingUtils.getTargetGroupingFromModules(usesNode, modules, module);
918 if (target == null) {
921 String prefix = null;
922 if (parent instanceof AugmentationSchemaBuilder || parent instanceof ModuleBuilder) {
923 ns = module.getNamespace();
924 rev = module.getRevision();
925 prefix = module.getPrefix();
927 ns = ((DataSchemaNodeBuilder) parent).getQName().getNamespace();
928 rev = ((DataSchemaNodeBuilder) parent).getQName().getRevision();
929 prefix = ((DataSchemaNodeBuilder) parent).getQName().getPrefix();
932 Set<DataSchemaNodeBuilder> childNodes = GroupingUtils.getTargetGroupingDefinitionNodesWithNewNamespace(
933 usesNode, ns, rev, prefix, module.getName(), line);
934 parent.getChildNodeBuilders().addAll(childNodes);
935 Set<TypeDefinitionBuilder> typedefs = GroupingUtils
936 .getTargetGroupingDefinitionTypedefsWithNewNamespace(usesNode, ns, rev, prefix,
937 module.getName(), line);
938 parent.getTypeDefinitionBuilders().addAll(typedefs);
939 Set<GroupingBuilder> groupings = GroupingUtils.getTargetGroupingDefinitionGroupingsWithNewNamespace(
940 usesNode, ns, rev, prefix, module.getName(), line);
941 parent.getGroupingBuilders().addAll(groupings);
942 List<UnknownSchemaNodeBuilder> unknownNodes = GroupingUtils
943 .getTargetGroupingDefinitionUnknownNodesWithNewNamespace(usesNode, ns, rev, prefix,
944 module.getName(), line);
945 parent.getUnknownNodeBuilders().addAll(unknownNodes);
946 usesNode.setResolved(true);
948 for (AugmentationSchemaBuilder augment : usesNode.getAugmentations()) {
949 processAugmentationOnContext(augment, augment.getTargetPath().getPath(), module, prefix, context);
951 GroupingUtils.performRefine(usesNode);
953 parent.getChildNodeBuilders().addAll(target.instantiateChildNodes(parent));
954 parent.getTypeDefinitionBuilders().addAll(target.instantiateTypedefs(parent));
955 parent.getGroupingBuilders().addAll(target.instantiateGroupings(parent));
956 parent.getUnknownNodeBuilders().addAll(target.instantiateUnknownNodes(parent));
957 usesNode.setResolved(true);
959 for (AugmentationSchemaBuilder augment : usesNode.getAugmentations()) {
960 processAugmentation(augment, parent, augment.getTargetPath().getPath());
962 GroupingUtils.performRefine(usesNode);
967 private void resolveUnknownNodes(final Map<String, TreeMap<Date, ModuleBuilder>> modules, final ModuleBuilder module) {
968 for (UnknownSchemaNodeBuilder usnb : module.getAllUnknownNodes()) {
969 QName nodeType = usnb.getNodeType();
971 ModuleBuilder dependentModule = findModuleFromBuilders(modules, module, nodeType.getPrefix(),
973 for (ExtensionBuilder extension : dependentModule.getExtensions()) {
974 if (extension.getQName().getLocalName().equals(nodeType.getLocalName())) {
975 usnb.setNodeType(extension.getQName());
976 usnb.setExtensionBuilder(extension);
980 } catch (YangParseException e) {
981 throw new YangParseException(module.getName(), usnb.getLine(), "Failed to resolve node " + usnb
982 + ": no such extension definition found.", e);
987 private void resolveUnknownNodesWithContext(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
988 final ModuleBuilder module, final SchemaContext context) {
989 for (UnknownSchemaNodeBuilder usnb : module.getAllUnknownNodes()) {
990 QName nodeType = usnb.getNodeType();
992 ModuleBuilder dependentModuleBuilder = findModuleFromBuilders(modules, module, nodeType.getPrefix(),
995 if (dependentModuleBuilder == null) {
996 Module dependentModule = findModuleFromContext(context, module, nodeType.getPrefix(),
998 for (ExtensionDefinition e : dependentModule.getExtensionSchemaNodes()) {
999 if (e.getQName().getLocalName().equals(nodeType.getLocalName())) {
1000 usnb.setNodeType(new QName(e.getQName().getNamespace(), e.getQName().getRevision(),
1001 nodeType.getPrefix(), e.getQName().getLocalName()));
1002 usnb.setExtensionDefinition(e);
1007 for (ExtensionBuilder extension : dependentModuleBuilder.getExtensions()) {
1008 if (extension.getQName().getLocalName().equals(nodeType.getLocalName())) {
1009 usnb.setExtensionBuilder(extension);
1015 } catch (YangParseException e) {
1016 throw new YangParseException(module.getName(), usnb.getLine(), "Failed to resolve node " + usnb
1017 + ": no such extension definition found.", e);
1024 * Traverse through modules and resolve their deviation statements.
1027 * all loaded modules
1029 private void resolveDeviations(final Map<String, TreeMap<Date, ModuleBuilder>> modules) {
1030 for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
1031 for (Map.Entry<Date, ModuleBuilder> inner : entry.getValue().entrySet()) {
1032 ModuleBuilder b = inner.getValue();
1033 resolveDeviation(modules, b);
1039 * Traverse through module and resolve its deviation statements.
1042 * all loaded modules
1044 * module in which resolve deviations
1046 private void resolveDeviation(final Map<String, TreeMap<Date, ModuleBuilder>> modules, final ModuleBuilder module) {
1047 for (DeviationBuilder dev : module.getDeviations()) {
1048 int line = dev.getLine();
1049 SchemaPath targetPath = dev.getTargetPath();
1050 List<QName> path = targetPath.getPath();
1051 QName q0 = path.get(0);
1052 String prefix = q0.getPrefix();
1053 if (prefix == null) {
1054 prefix = module.getPrefix();
1057 ModuleBuilder dependentModuleBuilder = findModuleFromBuilders(modules, module, prefix, line);
1058 processDeviation(dev, dependentModuleBuilder, path, module);
1063 * Traverse through modules and resolve their deviation statements with
1067 * all loaded modules
1069 * already resolved context
1071 private void resolveDeviationsWithContext(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
1072 final SchemaContext context) {
1073 for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
1074 for (Map.Entry<Date, ModuleBuilder> inner : entry.getValue().entrySet()) {
1075 ModuleBuilder b = inner.getValue();
1076 resolveDeviationWithContext(modules, b, context);
1082 * Traverse through module and resolve its deviation statements with given
1086 * all loaded modules
1088 * module in which resolve deviations
1090 * already resolved context
1092 private void resolveDeviationWithContext(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
1093 final ModuleBuilder module, final SchemaContext context) {
1094 for (DeviationBuilder dev : module.getDeviations()) {
1095 int line = dev.getLine();
1096 SchemaPath targetPath = dev.getTargetPath();
1097 List<QName> path = targetPath.getPath();
1098 QName q0 = path.get(0);
1099 String prefix = q0.getPrefix();
1100 if (prefix == null) {
1101 prefix = module.getPrefix();
1104 ModuleBuilder dependentModuleBuilder = findModuleFromBuilders(modules, module, prefix, line);
1105 if (dependentModuleBuilder == null) {
1106 Object currentParent = findModuleFromContext(context, module, prefix, line);
1108 for (QName q : path) {
1109 if (currentParent == null) {
1110 throw new YangParseException(module.getName(), line, FAIL_DEVIATION_TARGET);
1112 String name = q.getLocalName();
1113 if (currentParent instanceof DataNodeContainer) {
1114 currentParent = ((DataNodeContainer) currentParent).getDataChildByName(name);
1118 if (currentParent == null) {
1119 throw new YangParseException(module.getName(), line, FAIL_DEVIATION_TARGET);
1121 if (currentParent instanceof SchemaNode) {
1122 dev.setTargetPath(((SchemaNode) currentParent).getPath());
1126 processDeviation(dev, dependentModuleBuilder, path, module);
1132 * Correct deviation target path in deviation builder.
1136 * @param dependentModuleBuilder
1137 * module containing deviation target
1139 * current deviation target path
1143 private void processDeviation(final DeviationBuilder dev, final ModuleBuilder dependentModuleBuilder,
1144 final List<QName> path, final ModuleBuilder module) {
1145 final int line = dev.getLine();
1146 Builder currentParent = dependentModuleBuilder;
1148 for (QName q : path) {
1149 if (currentParent == null) {
1150 throw new YangParseException(module.getName(), line, FAIL_DEVIATION_TARGET);
1152 String name = q.getLocalName();
1153 if (currentParent instanceof DataNodeContainerBuilder) {
1154 currentParent = ((DataNodeContainerBuilder) currentParent).getDataChildByName(name);
1158 if (!(currentParent instanceof SchemaNodeBuilder)) {
1159 throw new YangParseException(module.getName(), line, FAIL_DEVIATION_TARGET);
1161 dev.setTargetPath(((SchemaNodeBuilder) currentParent).getPath());