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.builder.impl;
10 import com.google.common.base.Function;
11 import com.google.common.base.Optional;
12 import com.google.common.base.Preconditions;
13 import com.google.common.base.Splitter;
14 import com.google.common.collect.Collections2;
15 import com.google.common.collect.Iterables;
16 import com.google.common.io.ByteSource;
17 import com.google.common.io.ByteStreams;
19 import java.io.FileNotFoundException;
20 import java.io.IOException;
21 import java.io.InputStream;
23 import java.util.ArrayList;
24 import java.util.Collection;
25 import java.util.Date;
26 import java.util.HashMap;
27 import java.util.HashSet;
28 import java.util.Iterator;
29 import java.util.LinkedHashSet;
30 import java.util.List;
32 import java.util.Map.Entry;
33 import java.util.NavigableMap;
35 import java.util.TreeMap;
36 import org.antlr.v4.runtime.tree.ParseTree;
37 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Belongs_to_stmtContext;
38 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Module_header_stmtsContext;
39 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Module_stmtContext;
40 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Namespace_stmtContext;
41 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Revision_stmtsContext;
42 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Submodule_header_stmtsContext;
43 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Submodule_stmtContext;
44 import org.opendaylight.yangtools.yang.common.QName;
45 import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
46 import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
47 import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
48 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
49 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
50 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
51 import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
52 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
53 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
54 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
55 import org.opendaylight.yangtools.yang.model.api.Module;
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.api.TypeDefinition;
60 import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
61 import org.opendaylight.yangtools.yang.model.util.ExtendedType;
62 import org.opendaylight.yangtools.yang.parser.builder.api.AugmentationSchemaBuilder;
63 import org.opendaylight.yangtools.yang.parser.builder.api.AugmentationTargetBuilder;
64 import org.opendaylight.yangtools.yang.parser.builder.api.Builder;
65 import org.opendaylight.yangtools.yang.parser.builder.api.DataNodeContainerBuilder;
66 import org.opendaylight.yangtools.yang.parser.builder.api.DataSchemaNodeBuilder;
67 import org.opendaylight.yangtools.yang.parser.builder.api.GroupingBuilder;
68 import org.opendaylight.yangtools.yang.parser.builder.api.GroupingMember;
69 import org.opendaylight.yangtools.yang.parser.builder.api.SchemaNodeBuilder;
70 import org.opendaylight.yangtools.yang.parser.builder.api.TypeDefinitionBuilder;
71 import org.opendaylight.yangtools.yang.parser.builder.api.UnknownSchemaNodeBuilder;
72 import org.opendaylight.yangtools.yang.parser.builder.api.UsesNodeBuilder;
73 import org.opendaylight.yangtools.yang.parser.impl.ParserListenerUtils;
74 import org.opendaylight.yangtools.yang.parser.impl.util.YangModelDependencyInfo;
75 import org.opendaylight.yangtools.yang.parser.util.NamedByteArrayInputStream;
76 import org.opendaylight.yangtools.yang.parser.util.NamedFileInputStream;
77 import org.opendaylight.yangtools.yang.parser.util.YangParseException;
78 import org.slf4j.Logger;
79 import org.slf4j.LoggerFactory;
81 public final class BuilderUtils {
83 private static final Logger LOG = LoggerFactory.getLogger(BuilderUtils.class);
84 private static final Splitter COLON_SPLITTER = Splitter.on(':');
85 private static final Date NULL_DATE = new Date(0L);
86 private static final String INPUT = "input";
87 private static final String OUTPUT = "output";
88 private static final String CHILD_NOT_FOUND_IN_NODE_STR = "Child {} not found in node {}";
90 private BuilderUtils() {
93 public static Collection<ByteSource> streamsToByteSources(final Collection<InputStream> streams) throws IOException {
94 Collection<ByteSource> result = new HashSet<>();
95 for (InputStream stream : streams) {
96 result.add(new ByteSourceImpl(stream));
101 public static ByteSource fileToByteSource(final File file) {
102 return new ByteSource() {
104 public InputStream openStream() throws IOException {
105 return new NamedFileInputStream(file, file.getAbsolutePath());
110 public static Collection<ByteSource> filesToByteSources(final Collection<File> streams)
111 throws FileNotFoundException {
112 return Collections2.transform(streams, new Function<File, ByteSource>() {
114 public ByteSource apply(final File input) {
115 return new ByteSource() {
117 public InputStream openStream() throws IOException {
118 return new NamedFileInputStream(input, input.getAbsolutePath());
126 * Find dependent module based on given prefix
129 * all available modules
133 * target module prefix
135 * current line in yang model
136 * @return module builder if found, null otherwise
138 public static ModuleBuilder findModuleFromBuilders(final Map<String, NavigableMap<Date, ModuleBuilder>> modules,
139 final ModuleBuilder module, final String prefix, final int line) {
140 ModuleBuilder dependentModule;
141 Date dependentModuleRevision;
143 if (prefix == null) {
144 dependentModule = module;
145 } else if (prefix.equals(module.getPrefix())) {
146 dependentModule = module;
148 ModuleImport dependentModuleImport = module.getImport(prefix);
149 if (dependentModuleImport == null) {
150 throw new YangParseException(module.getName(), line, "No import found with prefix '" + prefix + "'.");
152 String dependentModuleName = dependentModuleImport.getModuleName();
153 dependentModuleRevision = dependentModuleImport.getRevision();
155 NavigableMap<Date, ModuleBuilder> moduleBuildersByRevision = modules.get(dependentModuleName);
156 if (moduleBuildersByRevision == null) {
159 if (dependentModuleRevision == null) {
160 dependentModule = moduleBuildersByRevision.lastEntry().getValue();
162 dependentModule = moduleBuildersByRevision.get(dependentModuleRevision);
165 return dependentModule;
168 public static ModuleBuilder findModuleFromBuilders(final ModuleImport imp, final Iterable<ModuleBuilder> modules) {
169 String name = imp.getModuleName();
170 Date revision = imp.getRevision();
171 NavigableMap<Date, ModuleBuilder> map = new TreeMap<>();
172 for (ModuleBuilder module : modules) {
173 if (module != null && module.getName().equals(name)) {
174 map.put(module.getRevision(), module);
180 if (revision == null) {
181 return map.lastEntry().getValue();
183 return map.get(revision);
187 * Find module from context based on prefix.
191 * @param currentModule
194 * prefix used to reference dependent module
196 * current line in yang model
197 * @return module based on import with given prefix if found in context,
199 * @throws YangParseException
200 * if no import found with given prefix
202 public static Module findModuleFromContext(final SchemaContext context, final ModuleBuilder currentModule,
203 final String prefix, final int line) {
204 NavigableMap<Date, Module> modulesByRevision = new TreeMap<>();
206 ModuleImport dependentModuleImport = currentModule.getImport(prefix);
207 if (dependentModuleImport == null) {
208 throw new YangParseException(currentModule.getName(), line, "No import found with prefix '" + prefix + "'.");
210 String dependentModuleName = dependentModuleImport.getModuleName();
211 Date dependentModuleRevision = dependentModuleImport.getRevision();
213 for (Module contextModule : context.getModules()) {
214 if (contextModule.getName().equals(dependentModuleName)) {
215 Date revision = contextModule.getRevision();
216 if (revision == null) {
217 revision = NULL_DATE;
219 modulesByRevision.put(revision, contextModule);
224 if (dependentModuleRevision == null) {
225 result = modulesByRevision.get(modulesByRevision.firstKey());
227 result = modulesByRevision.get(dependentModuleRevision);
229 if (result == null) {
230 throw new YangParseException(currentModule.getName(), line, "Module not found for prefix " + prefix);
237 * Add all augment's child nodes to given target.
240 * builder of augment statement
242 * augmentation target node
244 public static void fillAugmentTarget(final AugmentationSchemaBuilder augment, final Builder target) {
245 if (target instanceof DataNodeContainerBuilder) {
246 fillAugmentTarget(augment, (DataNodeContainerBuilder) target);
247 } else if (target instanceof ChoiceBuilder) {
248 fillAugmentTarget(augment, (ChoiceBuilder) target);
250 throw new YangParseException(
251 augment.getModuleName(),
253 "Error in augment parsing: The target node MUST be either a container, list, choice, case, input, output, or notification node.");
258 * Add all augment's child nodes to given target.
261 * builder of augment statement
263 * augmentation target node
265 private static void fillAugmentTarget(final AugmentationSchemaBuilder augment, final DataNodeContainerBuilder target) {
266 for (DataSchemaNodeBuilder child : augment.getChildNodeBuilders()) {
267 DataSchemaNodeBuilder childCopy = CopyUtils.copy(child, target, false);
268 if (augment.getParent() instanceof UsesNodeBuilder) {
269 setNodeAddedByUses(childCopy);
271 setNodeAugmenting(childCopy);
273 target.addChildNode(childCopy);
274 } catch (YangParseException e) {
276 // more descriptive message
277 throw new YangParseException(augment.getModuleName(), augment.getLine(),
278 "Failed to perform augmentation: " + e.getMessage());
284 * Add all augment's child nodes to given target.
287 * builder of augment statement
289 * augmentation target choice node
291 private static void fillAugmentTarget(final AugmentationSchemaBuilder augment, final ChoiceBuilder target) {
292 for (DataSchemaNodeBuilder builder : augment.getChildNodeBuilders()) {
293 DataSchemaNodeBuilder childCopy = CopyUtils.copy(builder, target, false);
294 if (augment.getParent() instanceof UsesNodeBuilder) {
295 setNodeAddedByUses(childCopy);
297 setNodeAugmenting(childCopy);
298 target.addCase(childCopy);
300 for (UsesNodeBuilder usesNode : augment.getUsesNodeBuilders()) {
301 if (usesNode != null) {
302 throw new YangParseException(augment.getModuleName(), augment.getLine(),
303 "Error in augment parsing: cannot augment choice with nodes from grouping");
309 * Set augmenting flag to true for node and all its child nodes.
313 private static void setNodeAugmenting(final DataSchemaNodeBuilder node) {
314 node.setAugmenting(true);
315 if (node instanceof DataNodeContainerBuilder) {
316 DataNodeContainerBuilder dataNodeChild = (DataNodeContainerBuilder) node;
317 for (DataSchemaNodeBuilder inner : dataNodeChild.getChildNodeBuilders()) {
318 setNodeAugmenting(inner);
320 } else if (node instanceof ChoiceBuilder) {
321 ChoiceBuilder choiceChild = (ChoiceBuilder) node;
322 for (ChoiceCaseBuilder inner : choiceChild.getCases()) {
323 setNodeAugmenting(inner);
329 * Set addedByUses flag to true for node and all its child nodes.
331 * @param node grouping member node
333 public static void setNodeAddedByUses(final GroupingMember node) {
334 node.setAddedByUses(true);
335 if (node instanceof DataNodeContainerBuilder) {
336 DataNodeContainerBuilder dataNodeChild = (DataNodeContainerBuilder) node;
337 for (DataSchemaNodeBuilder inner : dataNodeChild.getChildNodeBuilders()) {
338 setNodeAddedByUses(inner);
340 } else if (node instanceof ChoiceBuilder) {
341 ChoiceBuilder choiceChild = (ChoiceBuilder) node;
342 for (ChoiceCaseBuilder inner : choiceChild.getCases()) {
343 setNodeAddedByUses(inner);
349 * Find builder of schema node under parent builder (including under
350 * AugmentationSchemaBuilder).
353 * - path of target schema node builder
355 * - base data node container builder under which the target
356 * schema node builder should be found
357 * @return builder of schema node
359 public static SchemaNodeBuilder findTargetNode(final Iterable<QName> path,
360 final DataNodeContainerBuilder parent) {
362 Preconditions.checkNotNull(parent);
363 Preconditions.checkNotNull(path);
365 SchemaNodeBuilder foundNode = null;
367 final Iterator<QName> pathIterator = path.iterator();
368 if (pathIterator.hasNext()) {
369 String name = pathIterator.next().getLocalName();
370 foundNode = parent.getDataChildByName(name);
371 if (foundNode == null) {
372 foundNode = findUnknownNode(name, parent);
376 if (pathIterator.hasNext() && foundNode != null) {
377 return findSchemaNode(Iterables.skip(path, 1), foundNode);
383 public static SchemaNodeBuilder findSchemaNode(final Iterable<QName> path, final SchemaNodeBuilder parentNode) {
384 SchemaNodeBuilder node = null;
385 SchemaNodeBuilder parent = parentNode;
386 int size = Iterables.size(path);
388 for (QName qname : path) {
389 String name = qname.getLocalName();
390 if (parent instanceof DataNodeContainerBuilder) {
391 node = ((DataNodeContainerBuilder) parent).getDataChildByName(name);
393 node = findUnknownNode(name, parent);
395 } else if (parent instanceof ChoiceBuilder) {
396 node = ((ChoiceBuilder) parent).getCaseNodeByName(name);
398 node = findUnknownNode(name, parent);
400 } else if (parent instanceof RpcDefinitionBuilder) {
401 if (INPUT.equals(name)) {
402 node = ((RpcDefinitionBuilder) parent).getInput();
403 } else if (OUTPUT.equals(name)) {
404 node = ((RpcDefinitionBuilder) parent).getOutput();
407 node = findUnknownNode(name, parent);
411 node = findUnknownNode(name, parent);
423 private static UnknownSchemaNodeBuilder findUnknownNode(final String name, final Builder parent) {
424 for (UnknownSchemaNodeBuilder un : parent.getUnknownNodes()) {
425 if (un.getQName().getLocalName().equals(name)) {
434 * Find a builder for node in data namespace of YANG module.
436 * Search is performed on full QName equals, this means builders and schema
437 * path MUST be resolved against imports and their namespaces.
439 * Search is done in data namespace, this means notification, rpc
440 * definitions and top level data definitions are considered as top-level
441 * items, from which it is possible to traverse.
445 * Schema Path to node
447 * ModuleBuilder to start lookup in
448 * @return Node Builder if found, {@link Optional#absent()} otherwise.
450 public static Optional<SchemaNodeBuilder> findSchemaNodeInModule(final SchemaPath schemaPath,
451 final ModuleBuilder module) {
452 Iterator<QName> path = schemaPath.getPathFromRoot().iterator();
453 Preconditions.checkArgument(path.hasNext(), "Schema Path must contain at least one element.");
454 QName first = path.next();
455 Optional<SchemaNodeBuilder> currentNode = getDataNamespaceChild(module, first);
457 while (currentNode.isPresent() && path.hasNext()) {
458 SchemaNodeBuilder currentParent = currentNode.get();
459 QName currentPath = path.next();
460 currentNode = findDataChild(currentParent, currentPath);
461 if (!currentNode.isPresent()) {
462 for (SchemaNodeBuilder un : currentParent.getUnknownNodes()) {
463 if (un.getQName().equals(currentPath)) {
464 currentNode = Optional.of(un);
472 private static Optional<SchemaNodeBuilder> findDataChild(final SchemaNodeBuilder parent, final QName child) {
473 if (parent instanceof DataNodeContainerBuilder) {
474 return castOptional(SchemaNodeBuilder.class,
475 findDataChildInDataNodeContainer((DataNodeContainerBuilder) parent, child));
476 } else if (parent instanceof ChoiceBuilder) {
477 return castOptional(SchemaNodeBuilder.class, findCaseInChoice((ChoiceBuilder) parent, child));
478 } else if (parent instanceof RpcDefinitionBuilder) {
479 return castOptional(SchemaNodeBuilder.class, findContainerInRpc((RpcDefinitionBuilder) parent, child));
481 LOG.trace(CHILD_NOT_FOUND_IN_NODE_STR, child, parent);
482 return Optional.absent();
487 * Casts optional from one argument to other.
490 * Class to be checked
493 * @return Optional object with type argument casted as cls
495 private static <T> Optional<T> castOptional(final Class<T> cls, final Optional<?> optional) {
496 if (optional.isPresent()) {
497 Object value = optional.get();
498 if (cls.isInstance(value)) {
499 @SuppressWarnings("unchecked")
500 // Actually checked by outer if
501 T casted = (T) value;
502 return Optional.of(casted);
505 return Optional.absent();
508 // FIXME: if rpc does not define input or output, this method creates it
511 * Gets input / output container from {@link RpcDefinitionBuilder} if QName
516 * RPC Definition builder
519 * @return Optional of input/output if defined and QName is input/output.
520 * Otherwise {@link Optional#absent()}.
522 private static Optional<ContainerSchemaNodeBuilder> findContainerInRpc(final RpcDefinitionBuilder parent,
524 if (INPUT.equals(child.getLocalName())) {
525 if (parent.getInput() == null) {
526 QName qname = QName.create(parent.getQName().getModule(), INPUT);
527 final ContainerSchemaNodeBuilder inputBuilder = new ContainerSchemaNodeBuilder(parent.getModuleName(),
528 parent.getLine(), qname, parent.getPath().createChild(qname));
529 inputBuilder.setParent(parent);
530 parent.setInput(inputBuilder);
531 return Optional.of(inputBuilder);
533 return Optional.of(parent.getInput());
534 } else if (OUTPUT.equals(child.getLocalName())) {
535 if (parent.getOutput() == null) {
536 QName qname = QName.create(parent.getQName().getModule(), OUTPUT);
537 final ContainerSchemaNodeBuilder outputBuilder = new ContainerSchemaNodeBuilder(parent.getModuleName(),
538 parent.getLine(), qname, parent.getPath().createChild(qname));
539 outputBuilder.setParent(parent);
540 parent.setOutput(outputBuilder);
541 return Optional.of(outputBuilder);
543 return Optional.of(parent.getOutput());
545 LOG.trace(CHILD_NOT_FOUND_IN_NODE_STR, child, parent);
546 return Optional.absent();
550 * Finds case by QName in {@link ChoiceBuilder}
554 * DataNodeContainer in which lookup should be performed
557 * @return Optional of child if found.
560 private static Optional<ChoiceCaseBuilder> findCaseInChoice(final ChoiceBuilder parent, final QName child) {
561 for (ChoiceCaseBuilder caze : parent.getCases()) {
562 if (caze.getQName().equals(child)) {
563 return Optional.of(caze);
566 LOG.trace(CHILD_NOT_FOUND_IN_NODE_STR, child, parent);
567 return Optional.absent();
571 * Finds direct child by QName in {@link DataNodeContainerBuilder}
575 * DataNodeContainer in which lookup should be performed
578 * @return Optional of child if found.
580 private static Optional<DataSchemaNodeBuilder> findDataChildInDataNodeContainer(final DataNodeContainerBuilder parent,
582 for (DataSchemaNodeBuilder childNode : parent.getChildNodeBuilders()) {
583 if (childNode.getQName().equals(child)) {
584 return Optional.of(childNode);
587 LOG.trace(CHILD_NOT_FOUND_IN_NODE_STR, child, parent);
588 return Optional.absent();
593 * Find a child builder for node in data namespace of YANG module.
595 * Search is performed on full QName equals, this means builders and schema
596 * path MUST be resolved against imports and their namespaces.
598 * Search is done in data namespace, this means notification, rpc
599 * definitions and top level data definitions are considered as top-level
600 * items, from which it is possible to traverse.
606 * ModuleBuilder to start lookup in
607 * @return Node Builder if found, {@link Optional#absent()} otherwise.
609 private static Optional<SchemaNodeBuilder> getDataNamespaceChild(final ModuleBuilder module, final QName child) {
611 * First we do lookup in data tree, if node is found we return it.
613 final Optional<SchemaNodeBuilder> dataTreeNode = getDataChildByQName(module, child);
614 if (dataTreeNode.isPresent()) {
619 * We lookup in notifications
621 Set<NotificationBuilder> notifications = module.getAddedNotifications();
622 for (NotificationBuilder notification : notifications) {
623 if (notification.getQName().equals(child)) {
624 return Optional.<SchemaNodeBuilder> of(notification);
631 Set<RpcDefinitionBuilder> rpcs = module.getAddedRpcs();
632 for (RpcDefinitionBuilder rpc : rpcs) {
633 if (rpc.getQName().equals(child)) {
634 return Optional.<SchemaNodeBuilder> of(rpc);
637 LOG.trace("Child {} not found in data namespace of module {}", child, module);
638 return Optional.absent();
641 private static Optional<SchemaNodeBuilder> getDataChildByQName(final DataNodeContainerBuilder builder, final QName child) {
642 for (DataSchemaNodeBuilder childNode : builder.getChildNodeBuilders()) {
643 if (childNode.getQName().equals(child)) {
644 return Optional.<SchemaNodeBuilder> of(childNode);
647 LOG.trace(CHILD_NOT_FOUND_IN_NODE_STR, child, builder);
648 return Optional.absent();
652 * Find augment target node and perform augmentation.
655 * augment builder to process
656 * @param firstNodeParent
657 * parent of first node in path
658 * @return true if augmentation process succeed, false otherwise
660 public static boolean processAugmentation(final AugmentationSchemaBuilder augment,
661 final ModuleBuilder firstNodeParent) {
662 Optional<SchemaNodeBuilder> potentialTargetNode = findSchemaNodeInModule(augment.getTargetPath(),
664 if (!potentialTargetNode.isPresent()) {
666 } else if (potentialTargetNode.get() instanceof UnknownSchemaNodeBuilder) {
667 LOG.warn("Error in augment parsing: unsupported augment target: {}", potentialTargetNode.get());
670 SchemaNodeBuilder targetNode = potentialTargetNode.get();
671 fillAugmentTarget(augment, targetNode);
672 Preconditions.checkState(targetNode instanceof AugmentationTargetBuilder,
673 "Node refered by augmentation must be valid augmentation target");
674 ((AugmentationTargetBuilder) targetNode).addAugmentation(augment);
675 augment.setResolved(true);
679 public static IdentitySchemaNodeBuilder findBaseIdentity(final ModuleBuilder module, final String baseString,
682 // FIXME: optimize indexOf() away?
683 if (baseString.indexOf(':') != -1) {
684 final Iterator<String> it = COLON_SPLITTER.split(baseString).iterator();
685 final String prefix = it.next();
686 final String name = it.next();
689 throw new YangParseException(module.getName(), line, "Failed to parse identityref base: " + baseString);
692 ModuleBuilder dependentModule = getModuleByPrefix(module, prefix);
693 if (dependentModule == null) {
697 return findIdentity(dependentModule.getAddedIdentities(), name);
699 return findIdentity(module.getAddedIdentities(), baseString);
703 public static IdentitySchemaNodeBuilder findIdentity(final Set<IdentitySchemaNodeBuilder> identities,
705 for (IdentitySchemaNodeBuilder identity : identities) {
706 if (identity.getQName().getLocalName().equals(name)) {
714 * Get module in which this node is defined.
717 * @return builder of module where this node is defined
719 public static ModuleBuilder getParentModule(final Builder node) {
720 if (node instanceof ModuleBuilder) {
721 return (ModuleBuilder) node;
723 Builder parent = node.getParent();
724 while (!(parent instanceof ModuleBuilder)) {
725 parent = parent.getParent();
727 ModuleBuilder parentModule = (ModuleBuilder) parent;
728 if (parentModule.isSubmodule()) {
729 parentModule = parentModule.getParent();
734 public static Set<DataSchemaNodeBuilder> wrapChildNodes(final String moduleName, final int line,
735 final Collection<DataSchemaNode> nodes, final SchemaPath parentPath, final QName parentQName) {
736 Set<DataSchemaNodeBuilder> result = new LinkedHashSet<>(nodes.size());
738 for (DataSchemaNode node : nodes) {
739 QName qname = QName.create(parentQName, node.getQName().getLocalName());
740 DataSchemaNodeBuilder wrapped = wrapChildNode(moduleName, line, node, parentPath, qname);
746 public static DataSchemaNodeBuilder wrapChildNode(final String moduleName, final int line,
747 final DataSchemaNode node, final SchemaPath parentPath, final QName qname) {
749 final SchemaPath schemaPath = parentPath.createChild(qname);
751 if (node instanceof AnyXmlSchemaNode) {
752 return new AnyXmlBuilder(moduleName, line, qname, schemaPath, (AnyXmlSchemaNode) node);
753 } else if (node instanceof ChoiceSchemaNode) {
754 return new ChoiceBuilder(moduleName, line, qname, schemaPath, (ChoiceSchemaNode) node);
755 } else if (node instanceof ContainerSchemaNode) {
756 return new ContainerSchemaNodeBuilder(moduleName, line, qname, schemaPath, (ContainerSchemaNode) node);
757 } else if (node instanceof LeafSchemaNode) {
758 return new LeafSchemaNodeBuilder(moduleName, line, qname, schemaPath, (LeafSchemaNode) node);
759 } else if (node instanceof LeafListSchemaNode) {
760 return new LeafListSchemaNodeBuilder(moduleName, line, qname, schemaPath, (LeafListSchemaNode) node);
761 } else if (node instanceof ListSchemaNode) {
762 return new ListSchemaNodeBuilder(moduleName, line, qname, schemaPath, (ListSchemaNode) node);
763 } else if (node instanceof ChoiceCaseNode) {
764 return new ChoiceCaseBuilder(moduleName, line, qname, schemaPath, (ChoiceCaseNode) node);
766 throw new YangParseException(moduleName, line, "Failed to copy node: Unknown type of DataSchemaNode: "
771 public static Set<GroupingBuilder> wrapGroupings(final String moduleName, final int line,
772 final Set<GroupingDefinition> nodes, final SchemaPath parentPath, final QName parentQName) {
773 Set<GroupingBuilder> result = new HashSet<>();
774 for (GroupingDefinition node : nodes) {
775 QName qname = QName.create(parentQName, node.getQName().getLocalName());
776 SchemaPath schemaPath = parentPath.createChild(qname);
777 result.add(new GroupingBuilderImpl(moduleName, line, qname, schemaPath, node));
782 public static Set<TypeDefinitionBuilder> wrapTypedefs(final String moduleName, final int line,
783 final DataNodeContainer dataNode, final SchemaPath parentPath, final QName parentQName) {
784 Set<TypeDefinition<?>> nodes = dataNode.getTypeDefinitions();
785 Set<TypeDefinitionBuilder> result = new HashSet<>();
786 for (TypeDefinition<?> node : nodes) {
787 QName qname = QName.create(parentQName, node.getQName().getLocalName());
788 SchemaPath schemaPath = parentPath.createChild(qname);
789 result.add(new TypeDefinitionBuilderImpl(moduleName, line, qname, schemaPath, (ExtendedType) node));
794 public static List<UnknownSchemaNodeBuilderImpl> wrapUnknownNodes(final String moduleName, final int line,
795 final List<UnknownSchemaNode> nodes, final SchemaPath parentPath, final QName parentQName) {
796 List<UnknownSchemaNodeBuilderImpl> result = new ArrayList<>();
797 for (UnknownSchemaNode node : nodes) {
798 QName qname = QName.create(parentQName, node.getQName().getLocalName());
799 SchemaPath schemaPath = parentPath.createChild(qname);
800 result.add(new UnknownSchemaNodeBuilderImpl(moduleName, line, qname, schemaPath, node));
805 private static final class ByteSourceImpl extends ByteSource {
806 private final String toString;
807 private final byte[] data;
809 private ByteSourceImpl(final InputStream input) throws IOException {
810 toString = input.toString();
811 data = ByteStreams.toByteArray(input);
815 public InputStream openStream() throws IOException {
816 return new NamedByteArrayInputStream(data, toString);
820 public static ModuleBuilder getModuleByPrefix(final ModuleBuilder module, final String prefix) {
821 if (prefix == null || prefix.isEmpty() || prefix.equals(module.getPrefix())) {
824 return module.getImportedModule(prefix);
828 public static ModuleBuilder findModule(final QName qname, final Map<URI, NavigableMap<Date, ModuleBuilder>> modules) {
829 NavigableMap<Date, ModuleBuilder> map = modules.get(qname.getNamespace());
833 if (qname.getRevision() == null) {
834 return map.lastEntry().getValue();
837 final Entry<Date, ModuleBuilder> lastEntry = map.lastEntry();
838 if (qname.getRevision().compareTo(lastEntry.getKey()) > 0) {
840 * We are trying to find more recent revision of module than is in
841 * the map. Most probably the yang models are not referenced
842 * correctly and the revision of a base module or submodule has not
843 * been updated along with revision of a referenced module or
844 * submodule. However, we should return the most recent entry in the
845 * map, otherwise the null pointer exception occurs (see Bug3799).
848 .format("Attempt to find more recent revision of module than is available. "
849 + "The requested revision is [%s], but the most recent available revision of module is [%s]."
850 + " Most probably some of Yang models do not have updated revision or they are not "
851 + "referenced correctly.",
852 qname.getRevision(), lastEntry.getKey()));
853 return lastEntry.getValue();
856 return map.get(qname.getRevision());
859 public static Map<String, NavigableMap<Date, URI>> createYangNamespaceContext(
860 final Collection<? extends ParseTree> modules, final Optional<SchemaContext> context) {
861 Map<String, NavigableMap<Date, URI>> namespaceContext = new HashMap<>();
862 Set<Submodule_stmtContext> submodules = new HashSet<>();
863 // first read ParseTree collection and separate modules and submodules
864 for (ParseTree module : modules) {
865 for (int i = 0; i < module.getChildCount(); i++) {
866 ParseTree moduleTree = module.getChild(i);
867 if (moduleTree instanceof Submodule_stmtContext) {
868 // put submodule context to separate collection
869 submodules.add((Submodule_stmtContext) moduleTree);
870 } else if (moduleTree instanceof Module_stmtContext) {
871 // get name, revision and namespace from module
872 Module_stmtContext moduleCtx = (Module_stmtContext) moduleTree;
873 final String moduleName = ParserListenerUtils.stringFromNode(moduleCtx);
875 URI namespace = null;
876 for (int j = 0; j < moduleCtx.getChildCount(); j++) {
877 ParseTree moduleCtxChildTree = moduleCtx.getChild(j);
878 if (moduleCtxChildTree instanceof Revision_stmtsContext) {
879 String revisionDateStr = YangModelDependencyInfo
880 .getLatestRevision((Revision_stmtsContext) moduleCtxChildTree);
881 if (revisionDateStr == null) {
884 rev = QName.parseRevision(revisionDateStr);
887 if (moduleCtxChildTree instanceof Module_header_stmtsContext) {
888 Module_header_stmtsContext headerCtx = (Module_header_stmtsContext) moduleCtxChildTree;
889 for (int k = 0; k < headerCtx.getChildCount(); k++) {
890 ParseTree ctx = headerCtx.getChild(k);
891 if (ctx instanceof Namespace_stmtContext) {
892 final String namespaceStr = ParserListenerUtils.stringFromNode(ctx);
893 namespace = URI.create(namespaceStr);
899 // update namespaceContext
900 NavigableMap<Date, URI> revToNs = namespaceContext.get(moduleName);
901 if (revToNs == null) {
902 revToNs = new TreeMap<>();
903 revToNs.put(rev, namespace);
904 namespaceContext.put(moduleName, revToNs);
906 revToNs.put(rev, namespace);
910 // after all ParseTree-s are parsed update namespaceContext with modules
911 // from SchemaContext
912 if (context.isPresent()) {
913 for (Module module : context.get().getModules()) {
914 NavigableMap<Date, URI> revToNs = namespaceContext.get(module.getName());
915 if (revToNs == null) {
916 revToNs = new TreeMap<>();
917 revToNs.put(module.getRevision(), module.getNamespace());
918 namespaceContext.put(module.getName(), revToNs);
920 revToNs.put(module.getRevision(), module.getNamespace());
923 // when all modules are processed, traverse submodules and update
924 // namespaceContext with mapping for submodules
925 for (Submodule_stmtContext submodule : submodules) {
926 final String moduleName = ParserListenerUtils.stringFromNode(submodule);
927 for (int i = 0; i < submodule.getChildCount(); i++) {
928 ParseTree subHeaderCtx = submodule.getChild(i);
929 if (subHeaderCtx instanceof Submodule_header_stmtsContext) {
930 for (int j = 0; j < subHeaderCtx.getChildCount(); j++) {
931 ParseTree belongsCtx = subHeaderCtx.getChild(j);
932 if (belongsCtx instanceof Belongs_to_stmtContext) {
933 final String belongsTo = ParserListenerUtils.stringFromNode(belongsCtx);
934 NavigableMap<Date, URI> ns = namespaceContext.get(belongsTo);
936 throw new YangParseException(moduleName, submodule.getStart().getLine(), String.format(
937 "Unresolved belongs-to statement: %s", belongsTo));
939 // submodule get namespace and revision from module
940 NavigableMap<Date, URI> subNs = new TreeMap<>();
941 subNs.put(ns.firstKey(), ns.firstEntry().getValue());
942 namespaceContext.put(moduleName, subNs);
948 return namespaceContext;