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.LinkedHashMap;
25 import java.util.LinkedHashSet;
26 import java.util.List;
28 import java.util.Map.Entry;
30 import java.util.TreeMap;
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;
74 import com.google.common.collect.Lists;
75 import com.google.common.collect.Maps;
76 import com.google.common.collect.Sets;
78 public final class YangParserImpl implements YangModelParser {
79 private static final Logger LOG = LoggerFactory.getLogger(YangParserImpl.class);
81 private static final String FAIL_DEVIATION_TARGET = "Failed to find deviation target.";
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");
89 final String yangFileName = yangFile.getName();
90 final String[] fileList = directory.list();
91 Preconditions.checkNotNull(fileList, directory + " not found");
93 FileInputStream yangFileStream = null;
94 LinkedHashMap<InputStream, File> streamToFileMap = new LinkedHashMap<>();
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);
103 for (String fileName : fileList) {
104 if (fileName.equals(yangFileName)) {
107 File dependency = new File(directory, fileName);
109 if (dependency.isFile()) {
110 streamToFileMap.put(new FileInputStream(dependency), dependency);
112 } catch(FileNotFoundException e) {
113 LOG.warn("Exception while reading yang file: " + fileName, e);
117 Map<InputStream, ModuleBuilder> parsedBuilders = parseModuleBuilders(
118 new ArrayList<>(streamToFileMap.keySet()), new HashMap<ModuleBuilder, InputStream>());
119 ModuleBuilder main = parsedBuilders.get(yangFileStream);
121 List<ModuleBuilder> moduleBuilders = new ArrayList<>();
122 moduleBuilders.add(main);
123 filterImports(main, new ArrayList<>(parsedBuilders.values()), moduleBuilders);
125 ModuleBuilder[] builders = new ModuleBuilder[moduleBuilders.size()];
126 moduleBuilders.toArray(builders);
128 // module dependency graph sorted
129 List<ModuleBuilder> sorted = ModuleDependencySort.sort(builders);
131 final LinkedHashMap<String, TreeMap<Date, ModuleBuilder>> modules = orderModules(sorted);
132 return new LinkedHashSet<>(build(modules).values());
136 public Set<Module> parseYangModels(final List<File> yangFiles) {
137 return Sets.newLinkedHashSet(parseYangModelsMapped(yangFiles).values());
141 public Set<Module> parseYangModels(final List<File> yangFiles, final SchemaContext context) {
142 if (yangFiles != null) {
143 final Map<InputStream, File> inputStreams = Maps.newHashMap();
145 for (final File yangFile : yangFiles) {
147 inputStreams.put(new FileInputStream(yangFile), yangFile);
148 } catch (FileNotFoundException e) {
149 LOG.warn("Exception while reading yang file: " + yangFile.getName(), e);
153 Map<ModuleBuilder, InputStream> builderToStreamMap = Maps.newHashMap();
154 final Map<String, TreeMap<Date, ModuleBuilder>> modules = resolveModuleBuilders(
155 Lists.newArrayList(inputStreams.keySet()), builderToStreamMap);
157 for (InputStream is : inputStreams.keySet()) {
160 } catch (IOException e) {
161 LOG.debug("Failed to close stream.");
165 return new LinkedHashSet<Module>(buildWithContext(modules, context).values());
167 return Collections.emptySet();
171 public Set<Module> parseYangModelsFromStreams(final List<InputStream> yangModelStreams) {
172 return Sets.newHashSet(parseYangModelsFromStreamsMapped(yangModelStreams).values());
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());
183 return Collections.emptySet();
187 public Map<File, Module> parseYangModelsMapped(List<File> yangFiles) {
188 if (yangFiles != null) {
189 final Map<InputStream, File> inputStreams = Maps.newHashMap();
191 for (final File yangFile : yangFiles) {
193 inputStreams.put(new FileInputStream(yangFile), yangFile);
194 } catch (FileNotFoundException e) {
195 LOG.warn("Exception while reading yang file: " + yangFile.getName(), e);
199 Map<ModuleBuilder, InputStream> builderToStreamMap = Maps.newHashMap();
200 final Map<String, TreeMap<Date, ModuleBuilder>> modules = resolveModuleBuilders(
201 Lists.newArrayList(inputStreams.keySet()), builderToStreamMap);
203 for (InputStream is : inputStreams.keySet()) {
206 } catch (IOException e) {
207 LOG.debug("Failed to close stream.");
211 Map<File, Module> retVal = Maps.newLinkedHashMap();
212 Map<ModuleBuilder, Module> builderToModuleMap = build(modules);
214 for (Entry<ModuleBuilder, Module> builderToModule : builderToModuleMap.entrySet()) {
215 retVal.put(inputStreams.get(builderToStreamMap.get(builderToModule.getKey())),
216 builderToModule.getValue());
221 return Collections.emptyMap();
225 public Map<InputStream, Module> parseYangModelsFromStreamsMapped(final List<InputStream> yangModelStreams) {
226 Map<ModuleBuilder, InputStream> builderToStreamMap = Maps.newHashMap();
228 final Map<String, TreeMap<Date, ModuleBuilder>> modules = resolveModuleBuilders(yangModelStreams,
230 Map<InputStream, Module> retVal = Maps.newLinkedHashMap();
231 Map<ModuleBuilder, Module> builderToModuleMap = build(modules);
233 for (Entry<ModuleBuilder, Module> builderToModule : builderToModuleMap.entrySet()) {
234 retVal.put(builderToStreamMap.get(builderToModule.getKey()), builderToModule.getValue());
240 public SchemaContext resolveSchemaContext(final Set<Module> modules) {
241 return new SchemaContextImpl(modules);
244 private Map<InputStream, ModuleBuilder> parseModuleBuilders(List<InputStream> inputStreams,
245 Map<ModuleBuilder, InputStream> streamToBuilderMap) {
247 final ParseTreeWalker walker = new ParseTreeWalker();
248 final Map<InputStream, ParseTree> trees = parseStreams(inputStreams);
249 final Map<InputStream, ModuleBuilder> builders = new LinkedHashMap<>();
252 new YangModelBasicValidator(walker).validate(new ArrayList<ParseTree>(trees.values()));
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();
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);
268 private Map<String, TreeMap<Date, ModuleBuilder>> resolveModuleBuilders(final List<InputStream> yangFileStreams,
269 Map<ModuleBuilder, InputStream> streamToBuilderMap) {
270 return resolveModuleBuildersWithContext(yangFileStreams, streamToBuilderMap, null);
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);
280 // module dependency graph sorted
281 List<ModuleBuilder> sorted;
282 if (context == null) {
283 sorted = ModuleDependencySort.sort(builders);
285 sorted = ModuleDependencySort.sortWithContext(context, builders);
288 return orderModules(sorted);
292 * Order modules by name and revision.
296 * @return modules ordered by name and revision
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) {
305 final String builderName = builder.getName();
306 Date builderRevision = builder.getRevision();
307 if (builderRevision == null) {
308 builderRevision = new Date(0L);
310 TreeMap<Date, ModuleBuilder> builderByRevision = result.get(builderName);
311 if (builderByRevision == null) {
312 builderByRevision = new TreeMap<Date, ModuleBuilder>();
314 builderByRevision.put(builderRevision, builder);
315 result.put(builderName, builderByRevision);
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);
330 if (mi.getRevision().equals(builder.getRevision())) {
331 if (!filtered.contains(builder)) {
332 filtered.add(builder);
333 filterImports(builder, other, filtered);
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));
350 private ParseTree parseStream(final InputStream yangStream) {
351 ParseTree result = null;
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());
360 result = parser.yang();
361 } catch (IOException e) {
362 LOG.warn("Exception while reading yang file: " + yangStream, e);
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);
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);
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);
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);
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);
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);
436 * Search for dirty nodes (node which contains UnknownType) and resolve
440 * all available modules
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(),
456 if (identity == null) {
457 throw new YangParseException(module.getName(), idref.getLine(), "Failed to find base identity");
459 idref.setBaseIdentity(identity);
460 nodeToResolve.setType(idref.build());
462 resolveType(nodeToResolve, modules, module);
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(),
481 idref.setBaseIdentity(identity);
482 nodeToResolve.setType(idref.build());
484 resolveTypeWithContext(nodeToResolve, modules, module, context);
491 * Correct augment target path.
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());
506 for (AugmentationSchemaBuilder augment : allAugments) {
507 setCorrectAugmentTargetPath(modules, augment, context);
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,
525 if (currentModule == null) {
526 Module m = ParserUtils.findModuleFromContext(context, module, localPrefix, augment.getLine());
528 throw new YangParseException(module.getName(), augment.getLine(), "Module with prefix "
529 + localPrefix + " not found.");
531 ns = m.getNamespace();
532 rev = m.getRevision();
533 pref = m.getPrefix();
535 ns = currentModule.getNamespace();
536 rev = currentModule.getRevision();
537 pref = currentModule.getPrefix();
540 newPath.add(new QName(ns, rev, pref, qn.getLocalName()));
542 augment.setTargetNodeSchemaPath(new SchemaPath(newPath, augment.getTargetPath().isAbsolute()));
546 * Go through all augment definitions and perform augmentation. It is
547 * expected that modules are already sorted by their dependencies.
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());
561 checkAugmentMandatoryNodes(allAugments);
563 for (int i = 0; i < allAugments.size(); i++) {
565 final AugmentationSchemaBuilder augment = allAugments.get(i);
566 // create collection of others
567 List<AugmentationSchemaBuilder> others = new ArrayList<>(allAugments);
568 others.remove(augment);
571 boolean resolved = resolveAugment(modules, augment);
572 // while not resolved
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);
584 throw new YangParseException(augment.getModuleName(), augment.getLine(),
585 "Error in augment parsing: failed to find augment target");
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.
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();
604 if (augmentPrefix == null || augmentPrefix.isEmpty() || augmentPrefix.equals(modulePrefix)) {
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");
618 * Search for augment target and perform augmentation.
624 * @return true if target node found, false otherwise
626 private boolean resolveAugment(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
627 final AugmentationSchemaBuilder augment) {
628 if (augment.isResolved()) {
632 int line = augment.getLine();
633 ModuleBuilder module = getParentModule(augment);
634 List<QName> path = augment.getTargetPath().getPath();
635 Builder augmentParent = augment.getParent();
637 Builder firstNodeParent;
638 if (augmentParent instanceof ModuleBuilder) {
639 // if augment is defined under module, parent of first node is
641 final QName firstNameInPath = path.get(0);
642 String prefix = firstNameInPath.getPrefix();
643 if (prefix == null) {
644 prefix = module.getPrefix();
646 firstNodeParent = findModuleFromBuilders(modules, module, prefix, line);
647 } else if (augmentParent instanceof UsesNodeBuilder) {
648 firstNodeParent = augmentParent.getParent();
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);
655 return processAugmentation(augment, firstNodeParent, path);
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.
666 * SchemaContext containing already resolved modules
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());
678 for (int i = 0; i < allAugments.size(); i++) {
679 // pick augment from list
680 final AugmentationSchemaBuilder augment = allAugments.get(i);
682 boolean resolved = resolveAugmentWithContext(modules, augment, context);
683 // while not resolved
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);
694 throw new YangParseException(augment.getModuleName(), augment.getLine(),
695 "Error in augment parsing: failed to find augment target");
701 * Search for augment target and perform augmentation.
708 * SchemaContext containing already resolved modules
709 * @return true if target node found, false otherwise
711 private boolean resolveAugmentWithContext(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
712 final AugmentationSchemaBuilder augment, final SchemaContext context) {
713 if (augment.isResolved()) {
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();
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();
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);
737 if (currentParent == null) {
738 return processAugmentationOnContext(augment, path, module, prefix, context);
740 return processAugmentation(augment, currentParent, path);
745 * Go through identity statements defined in current module and resolve
746 * their 'base' statement if present.
751 * module being resolved
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");
763 identity.setBaseIdentity(baseIdentity);
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.
779 * SchemaContext containing already resolved modules
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,
792 identity.setBaseIdentity(baseId);
794 identity.setBaseIdentity(baseIdentity);
801 * Find and add reference of uses target grouping.
806 * SchemaContext containing already resolved modules or null if
807 * context is not available
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());
817 for (UsesNodeBuilder usesNode : allUses) {
818 ModuleBuilder module = ParserUtils.getParentModule(usesNode);
819 final GroupingBuilder targetGroupingBuilder = GroupingUtils.getTargetGroupingFromModules(usesNode, modules,
821 if (targetGroupingBuilder == null) {
822 if (context == null) {
823 throw new YangParseException(module.getName(), usesNode.getLine(), "Referenced grouping '"
824 + usesNode.getGroupingPathAsString() + "' not found.");
826 GroupingDefinition targetGroupingDefinition = GroupingUtils.getTargetGroupingFromContext(usesNode,
828 usesNode.setGroupingDefinition(targetGroupingDefinition);
831 usesNode.setGrouping(targetGroupingBuilder);
837 * Copy data from uses target. Augmentations have to be resolved already.
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.
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();
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);
861 GroupingUtils.collectUsesData(usesNode);
865 dataCollected = module.isAllUsesDataCollected();
872 * Update uses parent and perform refinement.
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.
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
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());
893 for (UsesNodeBuilder usesNode : allModulesUses) {
894 GroupingUtils.updateUsesParent(usesNode);
895 GroupingUtils.performRefine(usesNode);
898 if (!resolveWithContext) {
899 for (UsesNodeBuilder usesNode : allModulesUses) {
900 if (usesNode.isCopy()) {
901 usesNode.getParent().getUsesNodes().remove(usesNode);
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();
911 ModuleBuilder dependentModule = findModuleFromBuilders(modules, module, nodeType.getPrefix(),
913 for (ExtensionBuilder extension : dependentModule.getExtensions()) {
914 if (extension.getQName().getLocalName().equals(nodeType.getLocalName())) {
915 usnb.setNodeType(extension.getQName());
916 usnb.setExtensionBuilder(extension);
920 } catch (YangParseException e) {
921 throw new YangParseException(module.getName(), usnb.getLine(), "Failed to resolve node " + usnb
922 + ": no such extension definition found.", e);
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();
932 ModuleBuilder dependentModuleBuilder = findModuleFromBuilders(modules, module,
933 nodeType.getPrefix(), usnb.getLine());
935 if (dependentModuleBuilder == null) {
936 Module dependentModule = findModuleFromContext(context, module, nodeType.getPrefix(),
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);
947 for (ExtensionBuilder extension : dependentModuleBuilder.getExtensions()) {
948 if (extension.getQName().getLocalName().equals(nodeType.getLocalName())) {
949 usnb.setExtensionBuilder(extension);
955 } catch (YangParseException e) {
956 throw new YangParseException(module.getName(), usnb.getLine(), "Failed to resolve node " + usnb
957 + ": no such extension definition found.", e);
964 * Traverse through modules and resolve their deviation statements.
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);
979 * Traverse through module and resolve its deviation statements.
984 * module in which resolve deviations
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();
997 ModuleBuilder dependentModuleBuilder = findModuleFromBuilders(modules, module, prefix, line);
998 processDeviation(dev, dependentModuleBuilder, path, module);
1003 * Traverse through modules and resolve their deviation statements with
1007 * all loaded modules
1009 * already resolved context
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);
1022 * Traverse through module and resolve its deviation statements with given
1026 * all loaded modules
1028 * module in which resolve deviations
1030 * already resolved context
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();
1045 ModuleBuilder dependentModuleBuilder = findModuleFromBuilders(modules, module, prefix, line);
1046 if (dependentModuleBuilder == null) {
1047 Module dependentModule = findModuleFromContext(context, module, prefix, line);
1048 Object currentParent = dependentModule;
1050 for (QName q : path) {
1051 if (currentParent == null) {
1052 throw new YangParseException(module.getName(), line, FAIL_DEVIATION_TARGET);
1054 name = q.getLocalName();
1055 if (currentParent instanceof DataNodeContainer) {
1056 currentParent = ((DataNodeContainer) currentParent).getDataChildByName(name);
1060 if (currentParent == null) {
1061 throw new YangParseException(module.getName(), line, FAIL_DEVIATION_TARGET);
1063 if (currentParent instanceof SchemaNode) {
1064 dev.setTargetPath(((SchemaNode) currentParent).getPath());
1068 processDeviation(dev, dependentModuleBuilder, path, module);
1074 * Correct deviation target path in deviation builder.
1078 * @param dependentModuleBuilder
1079 * module containing deviation target
1081 * current deviation target path
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;
1090 for (QName q : path) {
1091 if (currentParent == null) {
1092 throw new YangParseException(module.getName(), line, FAIL_DEVIATION_TARGET);
1094 String name = q.getLocalName();
1095 if (currentParent instanceof DataNodeContainerBuilder) {
1096 currentParent = ((DataNodeContainerBuilder) currentParent).getDataChildByName(name);
1100 if (!(currentParent instanceof SchemaNodeBuilder)) {
1101 throw new YangParseException(module.getName(), line, FAIL_DEVIATION_TARGET);
1103 dev.setTargetPath(((SchemaNodeBuilder) currentParent).getPath());