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;
11 import java.io.FileNotFoundException;
12 import java.io.IOException;
13 import java.io.InputStream;
15 import java.util.ArrayList;
16 import java.util.Arrays;
17 import java.util.Collection;
18 import java.util.Date;
19 import java.util.HashSet;
20 import java.util.Iterator;
21 import java.util.List;
24 import java.util.TreeMap;
26 import org.apache.commons.io.IOUtils;
27 import org.opendaylight.yangtools.yang.common.QName;
28 import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
29 import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
30 import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
31 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
32 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
33 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
34 import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
35 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
36 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
37 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
38 import org.opendaylight.yangtools.yang.model.api.Module;
39 import org.opendaylight.yangtools.yang.model.api.ModuleImport;
40 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
41 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
42 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
43 import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
44 import org.opendaylight.yangtools.yang.model.util.ExtendedType;
45 import org.opendaylight.yangtools.yang.parser.builder.api.AugmentationSchemaBuilder;
46 import org.opendaylight.yangtools.yang.parser.builder.api.AugmentationTargetBuilder;
47 import org.opendaylight.yangtools.yang.parser.builder.api.Builder;
48 import org.opendaylight.yangtools.yang.parser.builder.api.DataNodeContainerBuilder;
49 import org.opendaylight.yangtools.yang.parser.builder.api.DataSchemaNodeBuilder;
50 import org.opendaylight.yangtools.yang.parser.builder.api.GroupingBuilder;
51 import org.opendaylight.yangtools.yang.parser.builder.api.GroupingMember;
52 import org.opendaylight.yangtools.yang.parser.builder.api.SchemaNodeBuilder;
53 import org.opendaylight.yangtools.yang.parser.builder.api.TypeDefinitionBuilder;
54 import org.opendaylight.yangtools.yang.parser.builder.api.UsesNodeBuilder;
55 import org.opendaylight.yangtools.yang.parser.builder.impl.AnyXmlBuilder;
56 import org.opendaylight.yangtools.yang.parser.builder.impl.ChoiceBuilder;
57 import org.opendaylight.yangtools.yang.parser.builder.impl.ChoiceCaseBuilder;
58 import org.opendaylight.yangtools.yang.parser.builder.impl.ContainerSchemaNodeBuilder;
59 import org.opendaylight.yangtools.yang.parser.builder.impl.GroupingBuilderImpl;
60 import org.opendaylight.yangtools.yang.parser.builder.impl.IdentitySchemaNodeBuilder;
61 import org.opendaylight.yangtools.yang.parser.builder.impl.LeafListSchemaNodeBuilder;
62 import org.opendaylight.yangtools.yang.parser.builder.impl.LeafSchemaNodeBuilder;
63 import org.opendaylight.yangtools.yang.parser.builder.impl.ListSchemaNodeBuilder;
64 import org.opendaylight.yangtools.yang.parser.builder.impl.ModuleBuilder;
65 import org.opendaylight.yangtools.yang.parser.builder.impl.NotificationBuilder;
66 import org.opendaylight.yangtools.yang.parser.builder.impl.RpcDefinitionBuilder;
67 import org.opendaylight.yangtools.yang.parser.builder.impl.TypeDefinitionBuilderImpl;
68 import org.opendaylight.yangtools.yang.parser.builder.impl.UnknownSchemaNodeBuilder;
69 import org.slf4j.Logger;
70 import org.slf4j.LoggerFactory;
72 import com.google.common.base.Function;
73 import com.google.common.base.Preconditions;
74 import com.google.common.base.Splitter;
75 import com.google.common.collect.Collections2;
76 import com.google.common.io.ByteSource;
78 public final class ParserUtils {
80 private static final Logger LOG = LoggerFactory.getLogger(ParserUtils.class);
81 private static final Splitter SLASH_SPLITTER = Splitter.on('/');
82 private static final Splitter COLON_SPLITTER = Splitter.on(':');
84 private ParserUtils() {
87 public static Collection<ByteSource> streamsToByteSources(final Collection<InputStream> streams) {
88 return Collections2.transform(streams, new Function<InputStream, ByteSource>() {
90 public ByteSource apply(final InputStream input) {
91 return new ByteSource() {
93 public InputStream openStream() throws IOException {
94 return NamedByteArrayInputStream.create(input);
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) throws FileNotFoundException {
111 return Collections2.transform(streams, new Function<File, ByteSource>() {
113 public ByteSource apply(final File input) {
114 return new ByteSource() {
116 public InputStream openStream() throws IOException {
117 return new NamedFileInputStream(input, input.getAbsolutePath());
125 * Set string representation of source to ModuleBuilder.
127 * @param sourceToBuilder
128 * source to module mapping
130 public static void setSourceToBuilder(final Map<ByteSource, ModuleBuilder> sourceToBuilder) throws IOException {
131 for (Map.Entry<ByteSource, ModuleBuilder> entry : sourceToBuilder.entrySet()) {
132 ModuleBuilder builder = entry.getValue();
133 ByteSource source = entry.getKey();
135 String content = null;
136 InputStream stream = null;
138 stream = source.openStream();
139 content = IOUtils.toString(stream);
141 if (stream != null) {
144 } catch (IOException e) {
145 LOG.warn("Failed to close stream {}", stream);
149 builder.setSource(content);
154 * Create new SchemaPath from given path and qname.
159 * one or more qnames added to base path
160 * @return new SchemaPath from given path and qname
162 * @deprecated Use {@link SchemaPath#createChild(QName...)} instead.
165 public static SchemaPath createSchemaPath(final SchemaPath schemaPath, final QName... qname) {
166 List<QName> path = new ArrayList<>(schemaPath.getPath());
167 path.addAll(Arrays.asList(qname));
168 return SchemaPath.create(path, schemaPath.isAbsolute());
172 * Get module import referenced by given prefix.
177 * prefix associated with import
178 * @return ModuleImport based on given prefix
180 public static ModuleImport getModuleImport(final ModuleBuilder builder, final String prefix) {
181 for (ModuleImport mi : builder.getModuleImports()) {
182 if (mi.getPrefix().equals(prefix)) {
191 * Find dependent module based on given prefix
194 * all available modules
198 * target module prefix
200 * current line in yang model
201 * @return module builder if found, null otherwise
203 public static ModuleBuilder findModuleFromBuilders(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
204 final ModuleBuilder module, final String prefix, final int line) {
205 ModuleBuilder dependentModule = null;
206 Date dependentModuleRevision = null;
208 if (prefix == null) {
209 dependentModule = module;
210 } else if (prefix.equals(module.getPrefix())) {
211 dependentModule = module;
213 ModuleImport dependentModuleImport = getModuleImport(module, prefix);
214 if (dependentModuleImport == null) {
215 throw new YangParseException(module.getName(), line, "No import found with prefix '" + prefix + "'.");
217 String dependentModuleName = dependentModuleImport.getModuleName();
218 dependentModuleRevision = dependentModuleImport.getRevision();
220 TreeMap<Date, ModuleBuilder> moduleBuildersByRevision = modules.get(dependentModuleName);
221 if (moduleBuildersByRevision == null) {
224 if (dependentModuleRevision == null) {
225 dependentModule = moduleBuildersByRevision.lastEntry().getValue();
227 dependentModule = moduleBuildersByRevision.get(dependentModuleRevision);
230 return dependentModule;
234 * Find module from context based on prefix.
238 * @param currentModule
241 * current prefix used to reference dependent module
243 * current line in yang model
244 * @return module based on given prefix if found in context, null otherwise
246 public static Module findModuleFromContext(final SchemaContext context, final ModuleBuilder currentModule, final String prefix,
248 if (context == null) {
249 throw new YangParseException(currentModule.getName(), line, "Cannot find module with prefix '" + prefix
252 TreeMap<Date, Module> modulesByRevision = new TreeMap<>();
254 ModuleImport dependentModuleImport = ParserUtils.getModuleImport(currentModule, prefix);
255 if (dependentModuleImport == null) {
256 throw new YangParseException(currentModule.getName(), line, "No import found with prefix '" + prefix + "'.");
258 String dependentModuleName = dependentModuleImport.getModuleName();
259 Date dependentModuleRevision = dependentModuleImport.getRevision();
261 for (Module contextModule : context.getModules()) {
262 if (contextModule.getName().equals(dependentModuleName)) {
263 Date revision = contextModule.getRevision();
264 if (revision == null) {
265 revision = new Date(0L);
267 modulesByRevision.put(revision, contextModule);
271 Module result = null;
272 if (dependentModuleRevision == null) {
273 result = modulesByRevision.get(modulesByRevision.firstKey());
275 result = modulesByRevision.get(dependentModuleRevision);
281 * Parse XPath string.
285 * @return SchemaPath from given String
287 public static SchemaPath parseXPathString(final String xpathString) {
288 final boolean absolute = xpathString.indexOf('/') == 0;
290 final List<QName> path = new ArrayList<QName>();
291 for (String pathElement : SLASH_SPLITTER.split(xpathString)) {
292 if (pathElement.length() > 0) {
293 final Iterator<String> it = COLON_SPLITTER.split(pathElement).iterator();
294 final String s = it.next();
298 name = new QName(null, null, s, it.next());
300 name = new QName(null, null, null, s);
305 return SchemaPath.create(path, absolute);
309 * Add all augment's child nodes to given target.
312 * builder of augment statement
314 * augmentation target node
316 public static void fillAugmentTarget(final AugmentationSchemaBuilder augment, final Builder target) {
317 if (target instanceof DataNodeContainerBuilder) {
318 fillAugmentTarget(augment, (DataNodeContainerBuilder) target);
319 } else if (target instanceof ChoiceBuilder) {
320 fillAugmentTarget(augment, (ChoiceBuilder) target);
322 throw new YangParseException(
323 augment.getModuleName(),
325 "Error in augment parsing: The target node MUST be either a container, list, choice, case, input, output, or notification node.");
330 * Add all augment's child nodes to given target.
333 * builder of augment statement
335 * augmentation target node
337 private static void fillAugmentTarget(final AugmentationSchemaBuilder augment, final DataNodeContainerBuilder target) {
338 for (DataSchemaNodeBuilder child : augment.getChildNodeBuilders()) {
339 DataSchemaNodeBuilder childCopy = CopyUtils.copy(child, target, false);
340 if (augment.getParent() instanceof UsesNodeBuilder) {
341 setNodeAddedByUses(childCopy);
343 setNodeAugmenting(childCopy);
345 target.addChildNode(childCopy);
346 } catch (YangParseException e) {
348 // more descriptive message
349 throw new YangParseException(augment.getModuleName(), augment.getLine(),
350 "Failed to perform augmentation: " + e.getMessage());
356 * Add all augment's child nodes to given target.
359 * builder of augment statement
361 * augmentation target choice node
363 private static void fillAugmentTarget(final AugmentationSchemaBuilder augment, final ChoiceBuilder target) {
364 for (DataSchemaNodeBuilder builder : augment.getChildNodeBuilders()) {
365 DataSchemaNodeBuilder childCopy = CopyUtils.copy(builder, target, false);
366 if (augment.getParent() instanceof UsesNodeBuilder) {
367 setNodeAddedByUses(childCopy);
369 setNodeAugmenting(childCopy);
370 target.addCase(childCopy);
372 for (UsesNodeBuilder usesNode : augment.getUsesNodeBuilders()) {
373 if (usesNode != null) {
374 throw new YangParseException(augment.getModuleName(), augment.getLine(),
375 "Error in augment parsing: cannot augment choice with nodes from grouping");
381 * Set augmenting flag to true for node and all its child nodes.
385 private static void setNodeAugmenting(final DataSchemaNodeBuilder node) {
386 node.setAugmenting(true);
387 if (node instanceof DataNodeContainerBuilder) {
388 DataNodeContainerBuilder dataNodeChild = (DataNodeContainerBuilder) node;
389 for (DataSchemaNodeBuilder inner : dataNodeChild.getChildNodeBuilders()) {
390 setNodeAugmenting(inner);
392 } else if (node instanceof ChoiceBuilder) {
393 ChoiceBuilder choiceChild = (ChoiceBuilder) node;
394 for (ChoiceCaseBuilder inner : choiceChild.getCases()) {
395 setNodeAugmenting(inner);
401 * Set addedByUses flag to true for node and all its child nodes.
405 public static void setNodeAddedByUses(final GroupingMember node) {
406 node.setAddedByUses(true);
407 if (node instanceof DataNodeContainerBuilder) {
408 DataNodeContainerBuilder dataNodeChild = (DataNodeContainerBuilder) node;
409 for (DataSchemaNodeBuilder inner : dataNodeChild.getChildNodeBuilders()) {
410 setNodeAddedByUses(inner);
412 } else if (node instanceof ChoiceBuilder) {
413 ChoiceBuilder choiceChild = (ChoiceBuilder) node;
414 for (ChoiceCaseBuilder inner : choiceChild.getCases()) {
415 setNodeAddedByUses(inner);
421 * Set config flag to new value.
428 public static void setNodeConfig(final DataSchemaNodeBuilder node, final Boolean config) {
429 if (node instanceof ContainerSchemaNodeBuilder || node instanceof LeafSchemaNodeBuilder
430 || node instanceof LeafListSchemaNodeBuilder || node instanceof ListSchemaNodeBuilder
431 || node instanceof ChoiceBuilder || node instanceof AnyXmlBuilder) {
432 node.setConfiguration(config);
434 if (node instanceof DataNodeContainerBuilder) {
435 DataNodeContainerBuilder dataNodeChild = (DataNodeContainerBuilder) node;
436 for (DataSchemaNodeBuilder inner : dataNodeChild.getChildNodeBuilders()) {
437 setNodeConfig(inner, config);
439 } else if (node instanceof ChoiceBuilder) {
440 ChoiceBuilder choiceChild = (ChoiceBuilder) node;
441 for (ChoiceCaseBuilder inner : choiceChild.getCases()) {
442 setNodeConfig(inner, config);
447 public static DataSchemaNodeBuilder findSchemaNode(final List<QName> path, final SchemaNodeBuilder parentNode) {
448 DataSchemaNodeBuilder node = null;
449 SchemaNodeBuilder parent = parentNode;
451 while (i < path.size()) {
452 String name = path.get(i).getLocalName();
453 if (parent instanceof DataNodeContainerBuilder) {
454 node = ((DataNodeContainerBuilder) parent).getDataChildByName(name);
455 } else if (parent instanceof ChoiceBuilder) {
456 node = ((ChoiceBuilder) parent).getCaseNodeByName(name);
457 } else if (parent instanceof RpcDefinitionBuilder) {
458 if ("input".equals(name)) {
459 node = ((RpcDefinitionBuilder) parent).getInput();
460 } else if ("output".equals(name)) {
461 node = ((RpcDefinitionBuilder) parent).getOutput();
469 if (i < path.size() - 1) {
478 public static SchemaNodeBuilder findSchemaNodeInModule(final List<QName> pathToNode, final ModuleBuilder module) {
479 List<QName> path = new ArrayList<>(pathToNode);
480 QName first = path.remove(0);
482 SchemaNodeBuilder node = module.getDataChildByName(first.getLocalName());
484 Set<NotificationBuilder> notifications = module.getAddedNotifications();
485 for (NotificationBuilder notification : notifications) {
486 if (notification.getQName().getLocalName().equals(first.getLocalName())) {
492 Set<RpcDefinitionBuilder> rpcs = module.getAddedRpcs();
493 for (RpcDefinitionBuilder rpc : rpcs) {
494 if (rpc.getQName().getLocalName().equals(first.getLocalName())) {
503 if (!path.isEmpty()) {
504 node = findSchemaNode(path, node);
511 * Find augment target node and perform augmentation.
514 * @param firstNodeParent
515 * parent of first node in path
517 * path to augment target
518 * @return true if augmentation process succeed, false otherwise
520 public static boolean processAugmentation(final AugmentationSchemaBuilder augment, final ModuleBuilder firstNodeParent) {
521 List<QName> path = augment.getTargetPath().getPath();
522 Builder targetNode = findSchemaNodeInModule(path, firstNodeParent);
523 if (targetNode == null) {
527 fillAugmentTarget(augment, targetNode);
528 ((AugmentationTargetBuilder) targetNode).addAugmentation(augment);
529 augment.setResolved(true);
533 public static IdentitySchemaNodeBuilder findBaseIdentity(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
534 final ModuleBuilder module, final String baseString, final int line) {
536 // FIXME: optimize indexOf() away?
537 if (baseString.indexOf(':') != -1) {
538 final Iterator<String> it = COLON_SPLITTER.split(baseString).iterator();
539 final String prefix = it.next();
540 final String name = it.next();
543 throw new YangParseException(module.getName(), line, "Failed to parse identityref base: " + baseString);
546 ModuleBuilder dependentModule = findModuleFromBuilders(modules, module, prefix, line);
547 if (dependentModule == null) {
551 return findIdentity(dependentModule.getAddedIdentities(), name);
553 return findIdentity(module.getAddedIdentities(), baseString);
557 public static IdentitySchemaNodeBuilder findIdentity(final Set<IdentitySchemaNodeBuilder> identities, final String name) {
558 for (IdentitySchemaNodeBuilder identity : identities) {
559 if (identity.getQName().getLocalName().equals(name)) {
567 * Get module in which this node is defined.
570 * @return builder of module where this node is defined
572 public static ModuleBuilder getParentModule(final Builder node) {
573 if (node instanceof ModuleBuilder) {
574 return (ModuleBuilder) node;
576 Builder parent = node.getParent();
577 while (!(parent instanceof ModuleBuilder)) {
578 parent = parent.getParent();
580 Preconditions.checkState(parent instanceof ModuleBuilder);
581 ModuleBuilder parentModule = (ModuleBuilder) parent;
582 if (parentModule.isSubmodule()) {
583 parentModule = parentModule.getParent();
588 public static Set<DataSchemaNodeBuilder> wrapChildNodes(final String moduleName, final int line, final Set<DataSchemaNode> nodes,
589 final SchemaPath parentPath, final URI ns, final Date rev, final String pref) {
590 Set<DataSchemaNodeBuilder> result = new HashSet<>();
592 for (DataSchemaNode node : nodes) {
593 QName qname = new QName(ns, rev, pref, node.getQName().getLocalName());
594 DataSchemaNodeBuilder wrapped = wrapChildNode(moduleName, line, node, parentPath, qname);
600 public static DataSchemaNodeBuilder wrapChildNode(final String moduleName, final int line, final DataSchemaNode node,
601 final SchemaPath parentPath, final QName qname) {
603 final SchemaPath schemaPath = parentPath.createChild(qname);
605 if (node instanceof AnyXmlSchemaNode) {
606 return new AnyXmlBuilder(moduleName, line, qname, schemaPath, ((AnyXmlSchemaNode) node));
607 } else if (node instanceof ChoiceNode) {
608 return new ChoiceBuilder(moduleName, line, qname, schemaPath, ((ChoiceNode) node));
609 } else if (node instanceof ContainerSchemaNode) {
610 return new ContainerSchemaNodeBuilder(moduleName, line, qname, schemaPath, ((ContainerSchemaNode) node));
611 } else if (node instanceof LeafSchemaNode) {
612 return new LeafSchemaNodeBuilder(moduleName, line, qname, schemaPath, ((LeafSchemaNode) node));
613 } else if (node instanceof LeafListSchemaNode) {
614 return new LeafListSchemaNodeBuilder(moduleName, line, qname, schemaPath, ((LeafListSchemaNode) node));
615 } else if (node instanceof ListSchemaNode) {
616 return new ListSchemaNodeBuilder(moduleName, line, qname, schemaPath, ((ListSchemaNode) node));
617 } else if (node instanceof ChoiceCaseNode) {
618 return new ChoiceCaseBuilder(moduleName, line, qname, schemaPath, ((ChoiceCaseNode) node));
620 throw new YangParseException(moduleName, line, "Failed to copy node: Unknown type of DataSchemaNode: "
625 public static Set<GroupingBuilder> wrapGroupings(final String moduleName, final int line, final Set<GroupingDefinition> nodes,
626 final SchemaPath parentPath, final URI ns, final Date rev, final String pref) {
627 Set<GroupingBuilder> result = new HashSet<>();
628 for (GroupingDefinition node : nodes) {
629 QName qname = new QName(ns, rev, pref, node.getQName().getLocalName());
630 SchemaPath schemaPath = parentPath.createChild(qname);
631 result.add(new GroupingBuilderImpl(moduleName, line, qname, schemaPath, node));
636 public static Set<TypeDefinitionBuilder> wrapTypedefs(final String moduleName, final int line, final DataNodeContainer dataNode,
637 final SchemaPath parentPath, final URI ns, final Date rev, final String pref) {
638 Set<TypeDefinition<?>> nodes = dataNode.getTypeDefinitions();
639 Set<TypeDefinitionBuilder> result = new HashSet<>();
640 for (TypeDefinition<?> node : nodes) {
641 QName qname = new QName(ns, rev, pref, node.getQName().getLocalName());
642 List<QName> path = new ArrayList<>(parentPath.getPath());
644 SchemaPath schemaPath = SchemaPath.create(path, parentPath.isAbsolute());
645 result.add(new TypeDefinitionBuilderImpl(moduleName, line, qname, schemaPath, ((ExtendedType) node)));
650 public static List<UnknownSchemaNodeBuilder> wrapUnknownNodes(final String moduleName, final int line,
651 final List<UnknownSchemaNode> nodes, final SchemaPath parentPath, final URI ns, final Date rev, final String pref) {
652 List<UnknownSchemaNodeBuilder> result = new ArrayList<>();
653 for (UnknownSchemaNode node : nodes) {
654 QName qname = new QName(ns, rev, pref, node.getQName().getLocalName());
655 List<QName> path = new ArrayList<>(parentPath.getPath());
657 SchemaPath schemaPath = SchemaPath.create(path, parentPath.isAbsolute());
658 result.add(new UnknownSchemaNodeBuilder(moduleName, line, qname, schemaPath, node));