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 java.io.ByteArrayOutputStream;
19 import java.io.FileNotFoundException;
20 import java.io.IOException;
21 import java.io.InputStream;
23 import java.text.DateFormat;
24 import java.text.SimpleDateFormat;
25 import java.util.ArrayList;
26 import java.util.Collection;
27 import java.util.Date;
28 import java.util.HashMap;
29 import java.util.HashSet;
30 import java.util.Iterator;
31 import java.util.LinkedHashSet;
32 import java.util.List;
35 import java.util.TreeMap;
36 import org.antlr.v4.runtime.tree.ParseTree;
37 import org.apache.commons.io.IOUtils;
38 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Belongs_to_stmtContext;
39 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Module_header_stmtsContext;
40 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Module_stmtContext;
41 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Namespace_stmtContext;
42 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Revision_stmtsContext;
43 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Submodule_header_stmtsContext;
44 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Submodule_stmtContext;
45 import org.opendaylight.yangtools.yang.common.QName;
46 import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
47 import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
48 import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
49 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
50 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
51 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
52 import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
53 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
54 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
55 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
56 import org.opendaylight.yangtools.yang.model.api.Module;
57 import org.opendaylight.yangtools.yang.model.api.ModuleImport;
58 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
59 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
60 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
61 import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
62 import org.opendaylight.yangtools.yang.model.util.ExtendedType;
63 import org.opendaylight.yangtools.yang.parser.builder.api.AugmentationSchemaBuilder;
64 import org.opendaylight.yangtools.yang.parser.builder.api.AugmentationTargetBuilder;
65 import org.opendaylight.yangtools.yang.parser.builder.api.Builder;
66 import org.opendaylight.yangtools.yang.parser.builder.api.DataNodeContainerBuilder;
67 import org.opendaylight.yangtools.yang.parser.builder.api.DataSchemaNodeBuilder;
68 import org.opendaylight.yangtools.yang.parser.builder.api.GroupingBuilder;
69 import org.opendaylight.yangtools.yang.parser.builder.api.GroupingMember;
70 import org.opendaylight.yangtools.yang.parser.builder.api.SchemaNodeBuilder;
71 import org.opendaylight.yangtools.yang.parser.builder.api.TypeDefinitionBuilder;
72 import org.opendaylight.yangtools.yang.parser.builder.api.UnknownSchemaNodeBuilder;
73 import org.opendaylight.yangtools.yang.parser.builder.api.UsesNodeBuilder;
74 import org.opendaylight.yangtools.yang.parser.impl.ParserListenerUtils;
75 import org.opendaylight.yangtools.yang.parser.impl.util.YangModelDependencyInfo;
76 import org.opendaylight.yangtools.yang.parser.util.NamedByteArrayInputStream;
77 import org.opendaylight.yangtools.yang.parser.util.NamedFileInputStream;
78 import org.opendaylight.yangtools.yang.parser.util.YangParseException;
79 import org.slf4j.Logger;
80 import org.slf4j.LoggerFactory;
82 public final class BuilderUtils {
84 private static final DateFormat SIMPLE_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd");
85 private static final Logger LOG = LoggerFactory.getLogger(BuilderUtils.class);
86 private static final Splitter SLASH_SPLITTER = Splitter.on('/').omitEmptyStrings();
87 private static final Splitter COLON_SPLITTER = Splitter.on(':');
88 private static final Date NULL_DATE = new Date(0L);
89 private static final String INPUT = "input";
90 private static final String OUTPUT = "output";
92 private BuilderUtils() {
95 public static Collection<ByteSource> streamsToByteSources(final Collection<InputStream> streams) throws IOException {
96 Collection<ByteSource> result = new HashSet<>();
97 for (InputStream stream : streams) {
98 result.add(new ByteSourceImpl(stream));
103 public static ByteSource fileToByteSource(final File file) {
104 return new ByteSource() {
106 public InputStream openStream() throws IOException {
107 return new NamedFileInputStream(file, file.getAbsolutePath());
112 public static Collection<ByteSource> filesToByteSources(final Collection<File> streams)
113 throws FileNotFoundException {
114 return Collections2.transform(streams, new Function<File, ByteSource>() {
116 public ByteSource apply(final File input) {
117 return new ByteSource() {
119 public InputStream openStream() throws IOException {
120 return new NamedFileInputStream(input, input.getAbsolutePath());
128 * Create new SchemaPath from given path and qname.
133 * one or more qnames added to base path
134 * @return new SchemaPath from given path and qname
136 * @deprecated Use {@link SchemaPath#createChild(QName...)} instead.
139 public static SchemaPath createSchemaPath(final SchemaPath schemaPath, final QName... qname) {
140 return schemaPath.createChild(qname);
144 * Find dependent module based on given prefix
147 * all available modules
151 * target module prefix
153 * current line in yang model
154 * @return module builder if found, null otherwise
156 public static ModuleBuilder findModuleFromBuilders(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
157 final ModuleBuilder module, final String prefix, final int line) {
158 ModuleBuilder dependentModule;
159 Date dependentModuleRevision;
161 if (prefix == null) {
162 dependentModule = module;
163 } else if (prefix.equals(module.getPrefix())) {
164 dependentModule = module;
166 ModuleImport dependentModuleImport = module.getImport(prefix);
167 if (dependentModuleImport == null) {
168 throw new YangParseException(module.getName(), line, "No import found with prefix '" + prefix + "'.");
170 String dependentModuleName = dependentModuleImport.getModuleName();
171 dependentModuleRevision = dependentModuleImport.getRevision();
173 TreeMap<Date, ModuleBuilder> moduleBuildersByRevision = modules.get(dependentModuleName);
174 if (moduleBuildersByRevision == null) {
177 if (dependentModuleRevision == null) {
178 dependentModule = moduleBuildersByRevision.lastEntry().getValue();
180 dependentModule = moduleBuildersByRevision.get(dependentModuleRevision);
183 return dependentModule;
186 public static ModuleBuilder findModuleFromBuilders(ModuleImport imp, Iterable<ModuleBuilder> modules) {
187 String name = imp.getModuleName();
188 Date revision = imp.getRevision();
189 TreeMap<Date, ModuleBuilder> map = new TreeMap<>();
190 for (ModuleBuilder module : modules) {
191 if (module != null) {
192 if (module.getName().equals(name)) {
193 map.put(module.getRevision(), module);
200 if (revision == null) {
201 return map.lastEntry().getValue();
203 return map.get(revision);
207 * Find module from context based on prefix.
211 * @param currentModule
214 * prefix used to reference dependent module
216 * current line in yang model
217 * @return module based on import with given prefix if found in context,
219 * @throws YangParseException
220 * if no import found with given prefix
222 public static Module findModuleFromContext(final SchemaContext context, final ModuleBuilder currentModule,
223 final String prefix, final int line) {
224 TreeMap<Date, Module> modulesByRevision = new TreeMap<>();
226 ModuleImport dependentModuleImport = currentModule.getImport(prefix);
227 if (dependentModuleImport == null) {
228 throw new YangParseException(currentModule.getName(), line, "No import found with prefix '" + prefix + "'.");
230 String dependentModuleName = dependentModuleImport.getModuleName();
231 Date dependentModuleRevision = dependentModuleImport.getRevision();
233 for (Module contextModule : context.getModules()) {
234 if (contextModule.getName().equals(dependentModuleName)) {
235 Date revision = contextModule.getRevision();
236 if (revision == null) {
237 revision = NULL_DATE;
239 modulesByRevision.put(revision, contextModule);
244 if (dependentModuleRevision == null) {
245 result = modulesByRevision.get(modulesByRevision.firstKey());
247 result = modulesByRevision.get(dependentModuleRevision);
249 if (result == null) {
250 throw new YangParseException(currentModule.getName(), line, "Module not found for prefix " + prefix);
257 * Add all augment's child nodes to given target.
260 * builder of augment statement
262 * augmentation target node
264 public static void fillAugmentTarget(final AugmentationSchemaBuilder augment, final Builder target) {
265 if (target instanceof DataNodeContainerBuilder) {
266 fillAugmentTarget(augment, (DataNodeContainerBuilder) target);
267 } else if (target instanceof ChoiceBuilder) {
268 fillAugmentTarget(augment, (ChoiceBuilder) target);
270 throw new YangParseException(
271 augment.getModuleName(),
273 "Error in augment parsing: The target node MUST be either a container, list, choice, case, input, output, or notification node.");
278 * Add all augment's child nodes to given target.
281 * builder of augment statement
283 * augmentation target node
285 private static void fillAugmentTarget(final AugmentationSchemaBuilder augment, final DataNodeContainerBuilder target) {
286 for (DataSchemaNodeBuilder child : augment.getChildNodeBuilders()) {
287 DataSchemaNodeBuilder childCopy = CopyUtils.copy(child, target, false);
288 if (augment.getParent() instanceof UsesNodeBuilder) {
289 setNodeAddedByUses(childCopy);
291 setNodeAugmenting(childCopy);
293 target.addChildNode(childCopy);
294 } catch (YangParseException e) {
296 // more descriptive message
297 throw new YangParseException(augment.getModuleName(), augment.getLine(),
298 "Failed to perform augmentation: " + e.getMessage());
304 * Add all augment's child nodes to given target.
307 * builder of augment statement
309 * augmentation target choice node
311 private static void fillAugmentTarget(final AugmentationSchemaBuilder augment, final ChoiceBuilder target) {
312 for (DataSchemaNodeBuilder builder : augment.getChildNodeBuilders()) {
313 DataSchemaNodeBuilder childCopy = CopyUtils.copy(builder, target, false);
314 if (augment.getParent() instanceof UsesNodeBuilder) {
315 setNodeAddedByUses(childCopy);
317 setNodeAugmenting(childCopy);
318 target.addCase(childCopy);
320 for (UsesNodeBuilder usesNode : augment.getUsesNodeBuilders()) {
321 if (usesNode != null) {
322 throw new YangParseException(augment.getModuleName(), augment.getLine(),
323 "Error in augment parsing: cannot augment choice with nodes from grouping");
329 * Set augmenting flag to true for node and all its child nodes.
333 private static void setNodeAugmenting(final DataSchemaNodeBuilder node) {
334 node.setAugmenting(true);
335 if (node instanceof DataNodeContainerBuilder) {
336 DataNodeContainerBuilder dataNodeChild = (DataNodeContainerBuilder) node;
337 for (DataSchemaNodeBuilder inner : dataNodeChild.getChildNodeBuilders()) {
338 setNodeAugmenting(inner);
340 } else if (node instanceof ChoiceBuilder) {
341 ChoiceBuilder choiceChild = (ChoiceBuilder) node;
342 for (ChoiceCaseBuilder inner : choiceChild.getCases()) {
343 setNodeAugmenting(inner);
349 * Set addedByUses flag to true for node and all its child nodes.
353 public static void setNodeAddedByUses(final GroupingMember node) {
354 node.setAddedByUses(true);
355 if (node instanceof DataNodeContainerBuilder) {
356 DataNodeContainerBuilder dataNodeChild = (DataNodeContainerBuilder) node;
357 for (DataSchemaNodeBuilder inner : dataNodeChild.getChildNodeBuilders()) {
358 setNodeAddedByUses(inner);
360 } else if (node instanceof ChoiceBuilder) {
361 ChoiceBuilder choiceChild = (ChoiceBuilder) node;
362 for (ChoiceCaseBuilder inner : choiceChild.getCases()) {
363 setNodeAddedByUses(inner);
368 public static SchemaNodeBuilder findSchemaNode(final Iterable<QName> path, final SchemaNodeBuilder parentNode) {
369 SchemaNodeBuilder node = null;
370 SchemaNodeBuilder parent = parentNode;
371 int size = Iterables.size(path);
373 for (QName qname : path) {
374 String name = qname.getLocalName();
375 if (parent instanceof DataNodeContainerBuilder) {
376 node = ((DataNodeContainerBuilder) parent).getDataChildByName(name);
378 node = findUnknownNode(name, parent);
380 } else if (parent instanceof ChoiceBuilder) {
381 node = ((ChoiceBuilder) parent).getCaseNodeByName(name);
383 node = findUnknownNode(name, parent);
385 } else if (parent instanceof RpcDefinitionBuilder) {
386 if ("input".equals(name)) {
387 node = ((RpcDefinitionBuilder) parent).getInput();
388 } else if ("output".equals(name)) {
389 node = ((RpcDefinitionBuilder) parent).getOutput();
392 node = findUnknownNode(name, parent);
396 node = findUnknownNode(name, parent);
408 private static UnknownSchemaNodeBuilder findUnknownNode(final String name, final Builder parent) {
409 for (UnknownSchemaNodeBuilder un : parent.getUnknownNodes()) {
410 if (un.getQName().getLocalName().equals(name)) {
419 * Find a builder for node in data namespace of YANG module.
421 * Search is performed on full QName equals, this means builders and schema
422 * path MUST be resolved against imports and their namespaces.
424 * Search is done in data namespace, this means notification, rpc
425 * definitions and top level data definitions are considered as top-level
426 * items, from which it is possible to traverse.
430 * Schema Path to node
432 * ModuleBuilder to start lookup in
433 * @return Node Builder if found, {@link Optional#absent()} otherwise.
435 public static Optional<SchemaNodeBuilder> findSchemaNodeInModule(final SchemaPath schemaPath,
436 final ModuleBuilder module) {
437 Iterator<QName> path = schemaPath.getPathFromRoot().iterator();
438 Preconditions.checkArgument(path.hasNext(), "Schema Path must contain at least one element.");
439 QName first = path.next();
440 Optional<SchemaNodeBuilder> currentNode = getDataNamespaceChild(module, first);
442 while (currentNode.isPresent() && path.hasNext()) {
443 SchemaNodeBuilder currentParent = currentNode.get();
444 QName currentPath = path.next();
445 currentNode = findDataChild(currentParent, currentPath);
446 if (!currentNode.isPresent()) {
447 for (SchemaNodeBuilder un : currentParent.getUnknownNodes()) {
448 if (un.getQName().equals(currentPath)) {
449 currentNode = Optional.of(un);
457 private static Optional<SchemaNodeBuilder> findDataChild(final SchemaNodeBuilder parent, final QName child) {
458 if (parent instanceof DataNodeContainerBuilder) {
459 return castOptional(SchemaNodeBuilder.class,
460 findDataChildInDataNodeContainer((DataNodeContainerBuilder) parent, child));
461 } else if (parent instanceof ChoiceBuilder) {
462 return castOptional(SchemaNodeBuilder.class, findCaseInChoice((ChoiceBuilder) parent, child));
463 } else if (parent instanceof RpcDefinitionBuilder) {
464 return castOptional(SchemaNodeBuilder.class, findContainerInRpc((RpcDefinitionBuilder) parent, child));
467 LOG.trace("Child {} not found in node {}", child, parent);
468 return Optional.absent();
473 * Casts optional from one argument to other.
476 * Class to be checked
481 private static <T> Optional<T> castOptional(final Class<T> cls, final Optional<?> optional) {
482 if (optional.isPresent()) {
483 Object value = optional.get();
484 if (cls.isInstance(value)) {
485 @SuppressWarnings("unchecked")
486 // Actually checked by outer if
487 T casted = (T) value;
488 return Optional.of(casted);
491 return Optional.absent();
494 // FIXME: if rpc does not define input or output, this method creates it
497 * Gets input / output container from {@link RpcDefinitionBuilder} if QName
502 * RPC Definition builder
505 * @return Optional of input/output if defined and QName is input/output.
506 * Otherwise {@link Optional#absent()}.
508 private static Optional<ContainerSchemaNodeBuilder> findContainerInRpc(final RpcDefinitionBuilder parent,
510 if (INPUT.equals(child.getLocalName())) {
511 if (parent.getInput() == null) {
512 QName qname = QName.create(parent.getQName().getModule(), "input");
513 final ContainerSchemaNodeBuilder inputBuilder = new ContainerSchemaNodeBuilder(parent.getModuleName(),
514 parent.getLine(), qname, parent.getPath().createChild(qname));
515 inputBuilder.setParent(parent);
516 parent.setInput(inputBuilder);
517 return Optional.of(inputBuilder);
519 return Optional.of(parent.getInput());
520 } else if (OUTPUT.equals(child.getLocalName())) {
521 if (parent.getOutput() == null) {
522 QName qname = QName.create(parent.getQName().getModule(), "output");
523 final ContainerSchemaNodeBuilder outputBuilder = new ContainerSchemaNodeBuilder(parent.getModuleName(),
524 parent.getLine(), qname, parent.getPath().createChild(qname));
525 outputBuilder.setParent(parent);
526 parent.setOutput(outputBuilder);
527 return Optional.of(outputBuilder);
529 return Optional.of(parent.getOutput());
531 LOG.trace("Child {} not found in node {}", child, parent);
532 return Optional.absent();
536 * Finds case by QName in {@link ChoiceBuilder}
540 * DataNodeContainer in which lookup should be performed
543 * @return Optional of child if found.
546 private static Optional<ChoiceCaseBuilder> findCaseInChoice(final ChoiceBuilder parent, final QName child) {
547 for (ChoiceCaseBuilder caze : parent.getCases()) {
548 if (caze.getQName().equals(child)) {
549 return Optional.of(caze);
552 LOG.trace("Child {} not found in node {}", child, parent);
553 return Optional.absent();
557 * Finds direct child by QName in {@link DataNodeContainerBuilder}
561 * DataNodeContainer in which lookup should be performed
564 * @return Optional of child if found.
566 private static Optional<DataSchemaNodeBuilder> findDataChildInDataNodeContainer(final DataNodeContainerBuilder parent,
568 for (DataSchemaNodeBuilder childNode : parent.getChildNodeBuilders()) {
569 if (childNode.getQName().equals(child)) {
570 return Optional.of(childNode);
573 LOG.trace("Child {} not found in node {}", child, parent);
574 return Optional.absent();
579 * Find a child builder for node in data namespace of YANG module.
581 * Search is performed on full QName equals, this means builders and schema
582 * path MUST be resolved against imports and their namespaces.
584 * Search is done in data namespace, this means notification, rpc
585 * definitions and top level data definitions are considered as top-level
586 * items, from which it is possible to traverse.
592 * ModuleBuilder to start lookup in
593 * @return Node Builder if found, {@link Optional#absent()} otherwise.
595 private static Optional<SchemaNodeBuilder> getDataNamespaceChild(final ModuleBuilder module, final QName child) {
597 * First we do lookup in data tree, if node is found we return it.
599 final Optional<SchemaNodeBuilder> dataTreeNode = getDataChildByQName(module, child);
600 if (dataTreeNode.isPresent()) {
605 * We lookup in notifications
607 Set<NotificationBuilder> notifications = module.getAddedNotifications();
608 for (NotificationBuilder notification : notifications) {
609 if (notification.getQName().equals(child)) {
610 return Optional.<SchemaNodeBuilder> of(notification);
617 Set<RpcDefinitionBuilder> rpcs = module.getAddedRpcs();
618 for (RpcDefinitionBuilder rpc : rpcs) {
619 if (rpc.getQName().equals(child)) {
620 return Optional.<SchemaNodeBuilder> of(rpc);
623 LOG.trace("Child {} not found in data namespace of module {}", child, module);
624 return Optional.absent();
627 private static Optional<SchemaNodeBuilder> getDataChildByQName(final DataNodeContainerBuilder builder, final QName child) {
628 for (DataSchemaNodeBuilder childNode : builder.getChildNodeBuilders()) {
629 if (childNode.getQName().equals(child)) {
630 return Optional.<SchemaNodeBuilder> of(childNode);
633 LOG.trace("Child {} not found in node {}", child, builder);
634 return Optional.absent();
638 * Find augment target node and perform augmentation.
641 * @param firstNodeParent
642 * parent of first node in path
644 * path to augment target
645 * @return true if augmentation process succeed, false otherwise
647 public static boolean processAugmentation(final AugmentationSchemaBuilder augment,
648 final ModuleBuilder firstNodeParent) {
649 Optional<SchemaNodeBuilder> potentialTargetNode = findSchemaNodeInModule(augment.getTargetPath(),
651 if (!potentialTargetNode.isPresent()) {
653 } else if (potentialTargetNode.get() instanceof UnknownSchemaNodeBuilder) {
654 LOG.warn("Error in augment parsing: unsupported augment target: {}", potentialTargetNode.get());
657 SchemaNodeBuilder targetNode = potentialTargetNode.get();
658 fillAugmentTarget(augment, targetNode);
659 Preconditions.checkState(targetNode instanceof AugmentationTargetBuilder,
660 "Node refered by augmentation must be valid augmentation target");
661 ((AugmentationTargetBuilder) targetNode).addAugmentation(augment);
662 augment.setResolved(true);
666 public static IdentitySchemaNodeBuilder findBaseIdentity(final ModuleBuilder module, final String baseString,
669 // FIXME: optimize indexOf() away?
670 if (baseString.indexOf(':') != -1) {
671 final Iterator<String> it = COLON_SPLITTER.split(baseString).iterator();
672 final String prefix = it.next();
673 final String name = it.next();
676 throw new YangParseException(module.getName(), line, "Failed to parse identityref base: " + baseString);
679 ModuleBuilder dependentModule = getModuleByPrefix(module, prefix);
680 if (dependentModule == null) {
684 return findIdentity(dependentModule.getAddedIdentities(), name);
686 return findIdentity(module.getAddedIdentities(), baseString);
690 public static IdentitySchemaNodeBuilder findIdentity(final Set<IdentitySchemaNodeBuilder> identities,
692 for (IdentitySchemaNodeBuilder identity : identities) {
693 if (identity.getQName().getLocalName().equals(name)) {
701 * Get module in which this node is defined.
704 * @return builder of module where this node is defined
706 public static ModuleBuilder getParentModule(final Builder node) {
707 if (node instanceof ModuleBuilder) {
708 return (ModuleBuilder) node;
710 Builder parent = node.getParent();
711 while (!(parent instanceof ModuleBuilder)) {
712 parent = parent.getParent();
714 ModuleBuilder parentModule = (ModuleBuilder) parent;
715 if (parentModule.isSubmodule()) {
716 parentModule = parentModule.getParent();
721 public static Set<DataSchemaNodeBuilder> wrapChildNodes(final String moduleName, final int line,
722 final Collection<DataSchemaNode> nodes, final SchemaPath parentPath, final QName parentQName) {
723 Set<DataSchemaNodeBuilder> result = new LinkedHashSet<>(nodes.size());
725 for (DataSchemaNode node : nodes) {
726 QName qname = QName.create(parentQName, node.getQName().getLocalName());
727 DataSchemaNodeBuilder wrapped = wrapChildNode(moduleName, line, node, parentPath, qname);
733 public static DataSchemaNodeBuilder wrapChildNode(final String moduleName, final int line,
734 final DataSchemaNode node, final SchemaPath parentPath, final QName qname) {
736 final SchemaPath schemaPath = parentPath.createChild(qname);
738 if (node instanceof AnyXmlSchemaNode) {
739 return new AnyXmlBuilder(moduleName, line, qname, schemaPath, ((AnyXmlSchemaNode) node));
740 } else if (node instanceof ChoiceNode) {
741 return new ChoiceBuilder(moduleName, line, qname, schemaPath, ((ChoiceNode) node));
742 } else if (node instanceof ContainerSchemaNode) {
743 return new ContainerSchemaNodeBuilder(moduleName, line, qname, schemaPath, ((ContainerSchemaNode) node));
744 } else if (node instanceof LeafSchemaNode) {
745 return new LeafSchemaNodeBuilder(moduleName, line, qname, schemaPath, ((LeafSchemaNode) node));
746 } else if (node instanceof LeafListSchemaNode) {
747 return new LeafListSchemaNodeBuilder(moduleName, line, qname, schemaPath, ((LeafListSchemaNode) node));
748 } else if (node instanceof ListSchemaNode) {
749 return new ListSchemaNodeBuilder(moduleName, line, qname, schemaPath, ((ListSchemaNode) node));
750 } else if (node instanceof ChoiceCaseNode) {
751 return new ChoiceCaseBuilder(moduleName, line, qname, schemaPath, ((ChoiceCaseNode) node));
753 throw new YangParseException(moduleName, line, "Failed to copy node: Unknown type of DataSchemaNode: "
758 public static Set<GroupingBuilder> wrapGroupings(final String moduleName, final int line,
759 final Set<GroupingDefinition> nodes, final SchemaPath parentPath, final QName parentQName) {
760 Set<GroupingBuilder> result = new HashSet<>();
761 for (GroupingDefinition node : nodes) {
762 QName qname = QName.create(parentQName, node.getQName().getLocalName());
763 SchemaPath schemaPath = parentPath.createChild(qname);
764 result.add(new GroupingBuilderImpl(moduleName, line, qname, schemaPath, node));
769 public static Set<TypeDefinitionBuilder> wrapTypedefs(final String moduleName, final int line,
770 final DataNodeContainer dataNode, final SchemaPath parentPath, final QName parentQName) {
771 Set<TypeDefinition<?>> nodes = dataNode.getTypeDefinitions();
772 Set<TypeDefinitionBuilder> result = new HashSet<>();
773 for (TypeDefinition<?> node : nodes) {
774 QName qname = QName.create(parentQName, node.getQName().getLocalName());
775 SchemaPath schemaPath = parentPath.createChild(qname);
776 result.add(new TypeDefinitionBuilderImpl(moduleName, line, qname, schemaPath, ((ExtendedType) node)));
781 public static List<UnknownSchemaNodeBuilderImpl> wrapUnknownNodes(final String moduleName, final int line,
782 final List<UnknownSchemaNode> nodes, final SchemaPath parentPath, final QName parentQName) {
783 List<UnknownSchemaNodeBuilderImpl> result = new ArrayList<>();
784 for (UnknownSchemaNode node : nodes) {
785 QName qname = QName.create(parentQName, node.getQName().getLocalName());
786 SchemaPath schemaPath = parentPath.createChild(qname);
787 result.add(new UnknownSchemaNodeBuilderImpl(moduleName, line, qname, schemaPath, node));
792 private static final class ByteSourceImpl extends ByteSource {
793 private final String toString;
794 private final ByteArrayOutputStream output = new ByteArrayOutputStream();
796 private ByteSourceImpl(final InputStream input) throws IOException {
797 toString = input.toString();
798 IOUtils.copy(input, output);
802 public InputStream openStream() throws IOException {
803 return new NamedByteArrayInputStream(output.toByteArray(), toString);
807 public static ModuleBuilder getModuleByPrefix(final ModuleBuilder module, final String prefix) {
808 if (prefix == null || prefix.isEmpty() || prefix.equals(module.getPrefix())) {
811 return module.getImportedModule(prefix);
815 public static ModuleBuilder findModule(final QName qname, final Map<URI, TreeMap<Date, ModuleBuilder>> modules) {
816 TreeMap<Date, ModuleBuilder> map = modules.get(qname.getNamespace());
820 if (qname.getRevision() == null) {
821 return map.lastEntry().getValue();
823 return map.get(qname.getRevision());
826 public static Map<String, TreeMap<Date, URI>> createYangNamespaceContext(
827 final Collection<? extends ParseTree> modules, final Optional<SchemaContext> context) {
828 Map<String, TreeMap<Date, URI>> namespaceContext = new HashMap<>();
829 Set<Submodule_stmtContext> submodules = new HashSet<>();
830 // first read ParseTree collection and separate modules and submodules
831 for (ParseTree module : modules) {
832 for (int i = 0; i < module.getChildCount(); i++) {
833 ParseTree moduleTree = module.getChild(i);
834 if (moduleTree instanceof Submodule_stmtContext) {
835 // put submodule context to separate collection
836 submodules.add((Submodule_stmtContext) moduleTree);
837 } else if (moduleTree instanceof Module_stmtContext) {
838 // get name, revision and namespace from module
839 Module_stmtContext moduleCtx = (Module_stmtContext) moduleTree;
840 final String moduleName = ParserListenerUtils.stringFromNode(moduleCtx);
842 URI namespace = null;
843 for (int j = 0; j < moduleCtx.getChildCount(); j++) {
844 ParseTree moduleCtxChildTree = moduleCtx.getChild(j);
845 if (moduleCtxChildTree instanceof Revision_stmtsContext) {
846 String revisionDateStr = YangModelDependencyInfo
847 .getLatestRevision((Revision_stmtsContext) moduleCtxChildTree);
848 if (revisionDateStr == null) {
851 rev = QName.parseRevision(revisionDateStr);
854 if (moduleCtxChildTree instanceof Module_header_stmtsContext) {
855 Module_header_stmtsContext headerCtx = (Module_header_stmtsContext) moduleCtxChildTree;
856 for (int k = 0; k < headerCtx.getChildCount(); k++) {
857 ParseTree ctx = headerCtx.getChild(k);
858 if (ctx instanceof Namespace_stmtContext) {
859 final String namespaceStr = ParserListenerUtils.stringFromNode(ctx);
860 namespace = URI.create(namespaceStr);
866 // update namespaceContext
867 TreeMap<Date, URI> revToNs = namespaceContext.get(moduleName);
868 if (revToNs == null) {
869 revToNs = new TreeMap<>();
870 revToNs.put(rev, namespace);
871 namespaceContext.put(moduleName, revToNs);
873 revToNs.put(rev, namespace);
877 // after all ParseTree-s are parsed update namespaceContext with modules
878 // from SchemaContext
879 if (context.isPresent()) {
880 for (Module module : context.get().getModules()) {
881 TreeMap<Date, URI> revToNs = namespaceContext.get(module.getName());
882 if (revToNs == null) {
883 revToNs = new TreeMap<>();
884 revToNs.put(module.getRevision(), module.getNamespace());
885 namespaceContext.put(module.getName(), revToNs);
887 revToNs.put(module.getRevision(), module.getNamespace());
890 // when all modules are processed, traverse submodules and update
891 // namespaceContext with mapping for submodules
892 for (Submodule_stmtContext submodule : submodules) {
893 final String moduleName = ParserListenerUtils.stringFromNode(submodule);
894 for (int i = 0; i < submodule.getChildCount(); i++) {
895 ParseTree subHeaderCtx = submodule.getChild(i);
896 if (subHeaderCtx instanceof Submodule_header_stmtsContext) {
897 for (int j = 0; j < subHeaderCtx.getChildCount(); j++) {
898 ParseTree belongsCtx = subHeaderCtx.getChild(j);
899 if (belongsCtx instanceof Belongs_to_stmtContext) {
900 final String belongsTo = ParserListenerUtils.stringFromNode(belongsCtx);
901 TreeMap<Date, URI> ns = namespaceContext.get(belongsTo);
903 throw new YangParseException(moduleName, submodule.getStart().getLine(), String.format(
904 "Unresolved belongs-to statement: %s", belongsTo));
906 // submodule get namespace and revision from module
907 TreeMap<Date, URI> subNs = new TreeMap<>();
908 subNs.put(ns.firstKey(), ns.firstEntry().getValue());
909 namespaceContext.put(moduleName, subNs);
915 return namespaceContext;