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.util;
10 import com.google.common.base.Function;
11 import com.google.common.base.Preconditions;
12 import com.google.common.collect.Collections2;
13 import com.google.common.io.ByteSource;
15 import java.io.FileNotFoundException;
16 import java.io.IOException;
17 import java.io.InputStream;
19 import java.util.ArrayList;
20 import java.util.Arrays;
21 import java.util.Collection;
22 import java.util.Date;
23 import java.util.HashSet;
24 import java.util.List;
27 import java.util.TreeMap;
28 import org.apache.commons.io.IOUtils;
29 import org.opendaylight.yangtools.yang.common.QName;
30 import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
31 import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
32 import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
33 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
34 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
35 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
36 import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
37 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
38 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
39 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
40 import org.opendaylight.yangtools.yang.model.api.Module;
41 import org.opendaylight.yangtools.yang.model.api.ModuleImport;
42 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
43 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
44 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
45 import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
46 import org.opendaylight.yangtools.yang.model.util.ExtendedType;
47 import org.opendaylight.yangtools.yang.parser.builder.api.AugmentationSchemaBuilder;
48 import org.opendaylight.yangtools.yang.parser.builder.api.AugmentationTargetBuilder;
49 import org.opendaylight.yangtools.yang.parser.builder.api.Builder;
50 import org.opendaylight.yangtools.yang.parser.builder.api.DataNodeContainerBuilder;
51 import org.opendaylight.yangtools.yang.parser.builder.api.DataSchemaNodeBuilder;
52 import org.opendaylight.yangtools.yang.parser.builder.api.GroupingBuilder;
53 import org.opendaylight.yangtools.yang.parser.builder.api.GroupingMember;
54 import org.opendaylight.yangtools.yang.parser.builder.api.SchemaNodeBuilder;
55 import org.opendaylight.yangtools.yang.parser.builder.api.TypeDefinitionBuilder;
56 import org.opendaylight.yangtools.yang.parser.builder.api.UsesNodeBuilder;
57 import org.opendaylight.yangtools.yang.parser.builder.impl.AnyXmlBuilder;
58 import org.opendaylight.yangtools.yang.parser.builder.impl.ChoiceBuilder;
59 import org.opendaylight.yangtools.yang.parser.builder.impl.ChoiceCaseBuilder;
60 import org.opendaylight.yangtools.yang.parser.builder.impl.ContainerSchemaNodeBuilder;
61 import org.opendaylight.yangtools.yang.parser.builder.impl.GroupingBuilderImpl;
62 import org.opendaylight.yangtools.yang.parser.builder.impl.IdentitySchemaNodeBuilder;
63 import org.opendaylight.yangtools.yang.parser.builder.impl.LeafListSchemaNodeBuilder;
64 import org.opendaylight.yangtools.yang.parser.builder.impl.LeafSchemaNodeBuilder;
65 import org.opendaylight.yangtools.yang.parser.builder.impl.ListSchemaNodeBuilder;
66 import org.opendaylight.yangtools.yang.parser.builder.impl.ModuleBuilder;
67 import org.opendaylight.yangtools.yang.parser.builder.impl.NotificationBuilder;
68 import org.opendaylight.yangtools.yang.parser.builder.impl.RpcDefinitionBuilder;
69 import org.opendaylight.yangtools.yang.parser.builder.impl.TypeDefinitionBuilderImpl;
70 import org.opendaylight.yangtools.yang.parser.builder.impl.UnknownSchemaNodeBuilder;
71 import org.slf4j.Logger;
72 import org.slf4j.LoggerFactory;
74 public final class ParserUtils {
76 private static final Logger LOG = LoggerFactory.getLogger(ParserUtils.class);
78 private ParserUtils() {
81 public static Collection<ByteSource> streamsToByteSources(Collection<InputStream> streams) {
82 return Collections2.transform(streams, new Function<InputStream, ByteSource>() {
84 public ByteSource apply(final InputStream input) {
85 return new ByteSource() {
87 public InputStream openStream() throws IOException {
88 return NamedByteArrayInputStream.create(input);
95 public static ByteSource fileToByteSource(final File file) {
96 return new ByteSource() {
98 public InputStream openStream() throws IOException {
99 return new NamedFileInputStream(file, file.getAbsolutePath());
104 public static Collection<ByteSource> filesToByteSources(Collection<File> streams) throws FileNotFoundException {
105 return Collections2.transform(streams, new Function<File, ByteSource>() {
107 public ByteSource apply(final File input) {
108 return new ByteSource() {
110 public InputStream openStream() throws IOException {
111 return new NamedFileInputStream(input, input.getAbsolutePath());
119 * Set string representation of source to ModuleBuilder.
121 * @param sourceToBuilder
122 * source to module mapping
124 public static void setSourceToBuilder(Map<ByteSource, ModuleBuilder> sourceToBuilder) throws IOException {
125 for (Map.Entry<ByteSource, ModuleBuilder> entry : sourceToBuilder.entrySet()) {
126 ModuleBuilder builder = entry.getValue();
127 ByteSource source = entry.getKey();
129 String content = null;
130 InputStream stream = null;
132 stream = source.openStream();
133 content = IOUtils.toString(stream);
135 if (stream != null) {
138 } catch (IOException e) {
139 LOG.warn("Failed to close stream {}", stream);
143 builder.setSource(content);
148 * Create new SchemaPath from given path and qname.
153 * one or more qnames added to base path
154 * @return new SchemaPath from given path and qname
156 public static SchemaPath createSchemaPath(SchemaPath schemaPath, QName... qname) {
157 List<QName> path = new ArrayList<>(schemaPath.getPath());
158 path.addAll(Arrays.asList(qname));
159 return new SchemaPath(path, schemaPath.isAbsolute());
163 * Get module import referenced by given prefix.
168 * prefix associated with import
169 * @return ModuleImport based on given prefix
171 public static ModuleImport getModuleImport(ModuleBuilder builder, String prefix) {
172 for (ModuleImport mi : builder.getModuleImports()) {
173 if (mi.getPrefix().equals(prefix)) {
182 * Find dependent module based on given prefix
185 * all available modules
189 * target module prefix
191 * current line in yang model
192 * @return module builder if found, null otherwise
194 public static ModuleBuilder findModuleFromBuilders(Map<String, TreeMap<Date, ModuleBuilder>> modules,
195 ModuleBuilder module, String prefix, int line) {
196 ModuleBuilder dependentModule = null;
197 Date dependentModuleRevision = null;
199 if (prefix == null) {
200 dependentModule = module;
201 } else if (prefix.equals(module.getPrefix())) {
202 dependentModule = module;
204 ModuleImport dependentModuleImport = getModuleImport(module, prefix);
205 if (dependentModuleImport == null) {
206 throw new YangParseException(module.getName(), line, "No import found with prefix '" + prefix + "'.");
208 String dependentModuleName = dependentModuleImport.getModuleName();
209 dependentModuleRevision = dependentModuleImport.getRevision();
211 TreeMap<Date, ModuleBuilder> moduleBuildersByRevision = modules.get(dependentModuleName);
212 if (moduleBuildersByRevision == null) {
215 if (dependentModuleRevision == null) {
216 dependentModule = moduleBuildersByRevision.lastEntry().getValue();
218 dependentModule = moduleBuildersByRevision.get(dependentModuleRevision);
221 return dependentModule;
225 * Find module from context based on prefix.
229 * @param currentModule
232 * current prefix used to reference dependent module
234 * current line in yang model
235 * @return module based on given prefix if found in context, null otherwise
237 public static Module findModuleFromContext(SchemaContext context, ModuleBuilder currentModule, String prefix,
239 if (context == null) {
240 throw new YangParseException(currentModule.getName(), line, "Cannot find module with prefix '" + prefix
243 TreeMap<Date, Module> modulesByRevision = new TreeMap<>();
245 ModuleImport dependentModuleImport = ParserUtils.getModuleImport(currentModule, prefix);
246 if (dependentModuleImport == null) {
247 throw new YangParseException(currentModule.getName(), line, "No import found with prefix '" + prefix + "'.");
249 String dependentModuleName = dependentModuleImport.getModuleName();
250 Date dependentModuleRevision = dependentModuleImport.getRevision();
252 for (Module contextModule : context.getModules()) {
253 if (contextModule.getName().equals(dependentModuleName)) {
254 Date revision = contextModule.getRevision();
255 if (revision == null) {
256 revision = new Date(0L);
258 modulesByRevision.put(revision, contextModule);
262 Module result = null;
263 if (dependentModuleRevision == null) {
264 result = modulesByRevision.get(modulesByRevision.firstKey());
266 result = modulesByRevision.get(dependentModuleRevision);
272 * Parse XPath string.
276 * @return SchemaPath from given String
278 public static SchemaPath parseXPathString(String xpathString) {
279 boolean absolute = xpathString.startsWith("/");
280 String[] splittedPath = xpathString.split("/");
281 List<QName> path = new ArrayList<QName>();
283 for (String pathElement : splittedPath) {
284 if (pathElement.length() > 0) {
285 String[] splittedElement = pathElement.split(":");
286 if (splittedElement.length == 1) {
287 name = new QName(null, null, null, splittedElement[0]);
289 name = new QName(null, null, splittedElement[0], splittedElement[1]);
294 return new SchemaPath(path, absolute);
298 * Add all augment's child nodes to given target.
301 * builder of augment statement
303 * augmentation target node
305 public static void fillAugmentTarget(AugmentationSchemaBuilder augment, Builder target) {
306 if (target instanceof DataNodeContainerBuilder) {
307 fillAugmentTarget(augment, (DataNodeContainerBuilder) target);
308 } else if (target instanceof ChoiceBuilder) {
309 fillAugmentTarget(augment, (ChoiceBuilder) target);
311 throw new YangParseException(
312 augment.getModuleName(),
314 "Error in augment parsing: The target node MUST be either a container, list, choice, case, input, output, or notification node.");
319 * Add all augment's child nodes to given target.
322 * builder of augment statement
324 * augmentation target node
326 private static void fillAugmentTarget(AugmentationSchemaBuilder augment, DataNodeContainerBuilder target) {
327 for (DataSchemaNodeBuilder child : augment.getChildNodeBuilders()) {
328 DataSchemaNodeBuilder childCopy = CopyUtils.copy(child, target, false);
329 if (augment.getParent() instanceof UsesNodeBuilder) {
330 setNodeAddedByUses(childCopy);
332 setNodeAugmenting(childCopy);
334 target.addChildNode(childCopy);
335 } catch (YangParseException e) {
337 // more descriptive message
338 throw new YangParseException(augment.getModuleName(), augment.getLine(),
339 "Failed to perform augmentation: " + e.getMessage());
345 * Add all augment's child nodes to given target.
348 * builder of augment statement
350 * augmentation target choice node
352 private static void fillAugmentTarget(AugmentationSchemaBuilder augment, ChoiceBuilder target) {
353 for (DataSchemaNodeBuilder builder : augment.getChildNodeBuilders()) {
354 DataSchemaNodeBuilder childCopy = CopyUtils.copy(builder, target, false);
355 if (augment.getParent() instanceof UsesNodeBuilder) {
356 setNodeAddedByUses(childCopy);
358 setNodeAugmenting(childCopy);
359 target.addCase(childCopy);
361 for (UsesNodeBuilder usesNode : augment.getUsesNodeBuilders()) {
362 if (usesNode != null) {
363 throw new YangParseException(augment.getModuleName(), augment.getLine(),
364 "Error in augment parsing: cannot augment choice with nodes from grouping");
370 * Set augmenting flag to true for node and all its child nodes.
374 private static void setNodeAugmenting(DataSchemaNodeBuilder node) {
375 node.setAugmenting(true);
376 if (node instanceof DataNodeContainerBuilder) {
377 DataNodeContainerBuilder dataNodeChild = (DataNodeContainerBuilder) node;
378 for (DataSchemaNodeBuilder inner : dataNodeChild.getChildNodeBuilders()) {
379 setNodeAugmenting(inner);
381 } else if (node instanceof ChoiceBuilder) {
382 ChoiceBuilder choiceChild = (ChoiceBuilder) node;
383 for (ChoiceCaseBuilder inner : choiceChild.getCases()) {
384 setNodeAugmenting(inner);
390 * Set addedByUses flag to true for node and all its child nodes.
394 public static void setNodeAddedByUses(GroupingMember node) {
395 node.setAddedByUses(true);
396 if (node instanceof DataNodeContainerBuilder) {
397 DataNodeContainerBuilder dataNodeChild = (DataNodeContainerBuilder) node;
398 for (DataSchemaNodeBuilder inner : dataNodeChild.getChildNodeBuilders()) {
399 setNodeAddedByUses(inner);
401 } else if (node instanceof ChoiceBuilder) {
402 ChoiceBuilder choiceChild = (ChoiceBuilder) node;
403 for (ChoiceCaseBuilder inner : choiceChild.getCases()) {
404 setNodeAddedByUses(inner);
410 * Set config flag to new value.
417 public static void setNodeConfig(DataSchemaNodeBuilder node, Boolean config) {
418 if (node instanceof ContainerSchemaNodeBuilder || node instanceof LeafSchemaNodeBuilder
419 || node instanceof LeafListSchemaNodeBuilder || node instanceof ListSchemaNodeBuilder
420 || node instanceof ChoiceBuilder || node instanceof AnyXmlBuilder) {
421 node.setConfiguration(config);
423 if (node instanceof DataNodeContainerBuilder) {
424 DataNodeContainerBuilder dataNodeChild = (DataNodeContainerBuilder) node;
425 for (DataSchemaNodeBuilder inner : dataNodeChild.getChildNodeBuilders()) {
426 setNodeConfig(inner, config);
428 } else if (node instanceof ChoiceBuilder) {
429 ChoiceBuilder choiceChild = (ChoiceBuilder) node;
430 for (ChoiceCaseBuilder inner : choiceChild.getCases()) {
431 setNodeConfig(inner, config);
436 public static DataSchemaNodeBuilder findSchemaNode(List<QName> path, SchemaNodeBuilder parentNode) {
437 DataSchemaNodeBuilder node = null;
438 SchemaNodeBuilder parent = parentNode;
440 while (i < path.size()) {
441 String name = path.get(i).getLocalName();
442 if (parent instanceof DataNodeContainerBuilder) {
443 node = ((DataNodeContainerBuilder) parent).getDataChildByName(name);
444 } else if (parent instanceof ChoiceBuilder) {
445 node = ((ChoiceBuilder) parent).getCaseNodeByName(name);
446 } else if (parent instanceof RpcDefinitionBuilder) {
447 if ("input".equals(name)) {
448 node = ((RpcDefinitionBuilder) parent).getInput();
449 } else if ("output".equals(name)) {
450 node = ((RpcDefinitionBuilder) parent).getOutput();
458 if (i < path.size() - 1) {
467 public static SchemaNodeBuilder findSchemaNodeInModule(List<QName> pathToNode, ModuleBuilder module) {
468 List<QName> path = new ArrayList<>(pathToNode);
469 QName first = path.remove(0);
471 SchemaNodeBuilder node = module.getDataChildByName(first.getLocalName());
473 Set<NotificationBuilder> notifications = module.getAddedNotifications();
474 for (NotificationBuilder notification : notifications) {
475 if (notification.getQName().getLocalName().equals(first.getLocalName())) {
481 Set<RpcDefinitionBuilder> rpcs = module.getAddedRpcs();
482 for (RpcDefinitionBuilder rpc : rpcs) {
483 if (rpc.getQName().getLocalName().equals(first.getLocalName())) {
492 if (!path.isEmpty()) {
493 node = findSchemaNode(path, node);
500 * Find augment target node and perform augmentation.
503 * @param firstNodeParent
504 * parent of first node in path
506 * path to augment target
507 * @return true if augmentation process succeed, false otherwise
509 public static boolean processAugmentation(AugmentationSchemaBuilder augment, ModuleBuilder firstNodeParent) {
510 List<QName> path = augment.getTargetPath().getPath();
511 Builder targetNode = findSchemaNodeInModule(path, firstNodeParent);
512 if (targetNode == null) {
516 fillAugmentTarget(augment, targetNode);
517 ((AugmentationTargetBuilder) targetNode).addAugmentation(augment);
518 augment.setResolved(true);
522 public static IdentitySchemaNodeBuilder findBaseIdentity(Map<String, TreeMap<Date, ModuleBuilder>> modules,
523 ModuleBuilder module, String baseString, int line) {
524 IdentitySchemaNodeBuilder result = null;
525 if (baseString.contains(":")) {
526 String[] splittedBase = baseString.split(":");
527 if (splittedBase.length > 2) {
528 throw new YangParseException(module.getName(), line, "Failed to parse identityref base: " + baseString);
530 String prefix = splittedBase[0];
531 String name = splittedBase[1];
532 ModuleBuilder dependentModule = findModuleFromBuilders(modules, module, prefix, line);
533 if (dependentModule != null) {
534 result = findIdentity(dependentModule.getAddedIdentities(), name);
537 result = findIdentity(module.getAddedIdentities(), baseString);
542 public static IdentitySchemaNodeBuilder findIdentity(Set<IdentitySchemaNodeBuilder> identities, String name) {
543 for (IdentitySchemaNodeBuilder identity : identities) {
544 if (identity.getQName().getLocalName().equals(name)) {
552 * Get module in which this node is defined.
555 * @return builder of module where this node is defined
557 public static ModuleBuilder getParentModule(Builder node) {
558 if (node instanceof ModuleBuilder) {
559 return (ModuleBuilder) node;
561 Builder parent = node.getParent();
562 while (!(parent instanceof ModuleBuilder)) {
563 parent = parent.getParent();
565 Preconditions.checkState(parent instanceof ModuleBuilder);
566 ModuleBuilder parentModule = (ModuleBuilder) parent;
567 if (parentModule.isSubmodule()) {
568 parentModule = parentModule.getParent();
573 public static Set<DataSchemaNodeBuilder> wrapChildNodes(String moduleName, int line, Set<DataSchemaNode> nodes,
574 SchemaPath parentPath, URI ns, Date rev, String pref) {
575 Set<DataSchemaNodeBuilder> result = new HashSet<>();
577 for (DataSchemaNode node : nodes) {
578 QName qname = new QName(ns, rev, pref, node.getQName().getLocalName());
579 DataSchemaNodeBuilder wrapped = wrapChildNode(moduleName, line, node, parentPath, qname);
585 public static DataSchemaNodeBuilder wrapChildNode(String moduleName, int line, DataSchemaNode node,
586 SchemaPath parentPath, QName qname) {
587 List<QName> path = new ArrayList<>(parentPath.getPath());
589 SchemaPath schemaPath = new SchemaPath(path, parentPath.isAbsolute());
591 if (node instanceof AnyXmlSchemaNode) {
592 return new AnyXmlBuilder(moduleName, line, qname, schemaPath, ((AnyXmlSchemaNode) node));
593 } else if (node instanceof ChoiceNode) {
594 return new ChoiceBuilder(moduleName, line, qname, schemaPath, ((ChoiceNode) node));
595 } else if (node instanceof ContainerSchemaNode) {
596 return new ContainerSchemaNodeBuilder(moduleName, line, qname, schemaPath, ((ContainerSchemaNode) node));
597 } else if (node instanceof LeafSchemaNode) {
598 return new LeafSchemaNodeBuilder(moduleName, line, qname, schemaPath, ((LeafSchemaNode) node));
599 } else if (node instanceof LeafListSchemaNode) {
600 return new LeafListSchemaNodeBuilder(moduleName, line, qname, schemaPath, ((LeafListSchemaNode) node));
601 } else if (node instanceof ListSchemaNode) {
602 return new ListSchemaNodeBuilder(moduleName, line, qname, schemaPath, ((ListSchemaNode) node));
603 } else if (node instanceof ChoiceCaseNode) {
604 return new ChoiceCaseBuilder(moduleName, line, qname, schemaPath, ((ChoiceCaseNode) node));
606 throw new YangParseException(moduleName, line, "Failed to copy node: Unknown type of DataSchemaNode: "
611 public static Set<GroupingBuilder> wrapGroupings(String moduleName, int line, Set<GroupingDefinition> nodes,
612 SchemaPath parentPath, URI ns, Date rev, String pref) {
613 Set<GroupingBuilder> result = new HashSet<>();
614 for (GroupingDefinition node : nodes) {
615 QName qname = new QName(ns, rev, pref, node.getQName().getLocalName());
616 List<QName> path = new ArrayList<>(parentPath.getPath());
618 SchemaPath schemaPath = new SchemaPath(path, parentPath.isAbsolute());
619 result.add(new GroupingBuilderImpl(moduleName, line, qname, schemaPath, node));
624 public static Set<TypeDefinitionBuilder> wrapTypedefs(String moduleName, int line, DataNodeContainer dataNode,
625 SchemaPath parentPath, URI ns, Date rev, String pref) {
626 Set<TypeDefinition<?>> nodes = dataNode.getTypeDefinitions();
627 Set<TypeDefinitionBuilder> result = new HashSet<>();
628 for (TypeDefinition<?> node : nodes) {
629 QName qname = new QName(ns, rev, pref, node.getQName().getLocalName());
630 List<QName> path = new ArrayList<>(parentPath.getPath());
632 SchemaPath schemaPath = new SchemaPath(path, parentPath.isAbsolute());
633 result.add(new TypeDefinitionBuilderImpl(moduleName, line, qname, schemaPath, ((ExtendedType) node)));
638 public static List<UnknownSchemaNodeBuilder> wrapUnknownNodes(String moduleName, int line,
639 List<UnknownSchemaNode> nodes, SchemaPath parentPath, URI ns, Date rev, String pref) {
640 List<UnknownSchemaNodeBuilder> result = new ArrayList<>();
641 for (UnknownSchemaNode node : nodes) {
642 QName qname = new QName(ns, rev, pref, node.getQName().getLocalName());
643 List<QName> path = new ArrayList<>(parentPath.getPath());
645 SchemaPath schemaPath = new SchemaPath(path, parentPath.isAbsolute());
646 result.add(new UnknownSchemaNodeBuilder(moduleName, line, qname, schemaPath, node));