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 java.util.ArrayList;
11 import java.util.Arrays;
12 import java.util.Date;
13 import java.util.List;
16 import java.util.TreeMap;
18 import org.opendaylight.yangtools.yang.common.QName;
19 import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
20 import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
21 import org.opendaylight.yangtools.yang.model.api.Module;
22 import org.opendaylight.yangtools.yang.model.api.ModuleImport;
23 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
24 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
25 import org.opendaylight.yangtools.yang.parser.builder.api.AugmentationSchemaBuilder;
26 import org.opendaylight.yangtools.yang.parser.builder.api.AugmentationTargetBuilder;
27 import org.opendaylight.yangtools.yang.parser.builder.api.Builder;
28 import org.opendaylight.yangtools.yang.parser.builder.api.DataNodeContainerBuilder;
29 import org.opendaylight.yangtools.yang.parser.builder.api.DataSchemaNodeBuilder;
30 import org.opendaylight.yangtools.yang.parser.builder.api.SchemaNodeBuilder;
31 import org.opendaylight.yangtools.yang.parser.builder.api.UsesNodeBuilder;
32 import org.opendaylight.yangtools.yang.parser.builder.impl.ChoiceBuilder;
33 import org.opendaylight.yangtools.yang.parser.builder.impl.ChoiceCaseBuilder;
34 import org.opendaylight.yangtools.yang.parser.builder.impl.IdentitySchemaNodeBuilder;
35 import org.opendaylight.yangtools.yang.parser.builder.impl.ModuleBuilder;
36 import org.opendaylight.yangtools.yang.parser.builder.impl.RpcDefinitionBuilder
37 import org.opendaylight.yangtools.yang.parser.builder.api.GroupingMember
38 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode
39 import java.util.HashSet
41 import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode
42 import org.opendaylight.yangtools.yang.parser.builder.impl.AnyXmlBuilder
43 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode
44 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode
45 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode
46 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode
47 import org.opendaylight.yangtools.yang.parser.builder.impl.ContainerSchemaNodeBuilder
48 import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode
49 import org.opendaylight.yangtools.yang.parser.builder.impl.ListSchemaNodeBuilder
50 import org.opendaylight.yangtools.yang.parser.builder.impl.LeafListSchemaNodeBuilder
51 import org.opendaylight.yangtools.yang.parser.builder.impl.LeafSchemaNodeBuilder
52 import org.opendaylight.yangtools.yang.model.api.GroupingDefinition
53 import org.opendaylight.yangtools.yang.parser.builder.api.GroupingBuilder
54 import org.opendaylight.yangtools.yang.parser.builder.impl.GroupingBuilderImpl
55 import org.opendaylight.yangtools.yang.model.api.TypeDefinition
56 import org.opendaylight.yangtools.yang.parser.builder.impl.TypeDefinitionBuilderImpl
57 import org.opendaylight.yangtools.yang.model.util.ExtendedType
58 import org.opendaylight.yangtools.yang.parser.builder.api.TypeDefinitionBuilder
59 import org.opendaylight.yangtools.yang.parser.builder.impl.UnknownSchemaNodeBuilder
60 import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode
61 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer
63 public final class ParserUtils {
69 * Create new SchemaPath from given path and qname.
73 * @return new SchemaPath from given path and qname
75 public static def SchemaPath createSchemaPath(SchemaPath schemaPath, QName... qname) {
76 val path = new ArrayList<QName>(schemaPath.getPath());
77 path.addAll(Arrays.asList(qname));
78 return new SchemaPath(path, schemaPath.isAbsolute());
82 * Get module import referenced by given prefix.
87 * prefix associated with import
88 * @return ModuleImport based on given prefix
90 public static def ModuleImport getModuleImport(ModuleBuilder builder, String prefix) {
91 for (ModuleImport mi : builder.getModuleImports()) {
92 if (mi.getPrefix().equals(prefix)) {
101 * Find dependent module based on given prefix
104 * all available modules
108 * target module prefix
110 * current line in yang model
111 * @return module builder if found, null otherwise
113 public static def ModuleBuilder findModuleFromBuilders(Map<String, TreeMap<Date, ModuleBuilder>> modules,
114 ModuleBuilder module, String prefix, int line) {
115 var ModuleBuilder dependentModule = null;
116 var Date dependentModuleRevision = null;
118 if (prefix.equals(module.getPrefix())) {
119 dependentModule = module;
121 val ModuleImport dependentModuleImport = getModuleImport(module, prefix);
122 if (dependentModuleImport === null) {
123 throw new YangParseException(module.getName(), line, "No import found with prefix '" + prefix + "'.");
125 val String dependentModuleName = dependentModuleImport.getModuleName();
126 dependentModuleRevision = dependentModuleImport.getRevision();
128 val TreeMap<Date, ModuleBuilder> moduleBuildersByRevision = modules.get(dependentModuleName);
129 if (moduleBuildersByRevision === null) {
132 if (dependentModuleRevision === null) {
133 dependentModule = moduleBuildersByRevision.lastEntry().getValue();
135 dependentModule = moduleBuildersByRevision.get(dependentModuleRevision);
138 return dependentModule;
142 * Find module from context based on prefix.
146 * @param currentModule
149 * current prefix used to reference dependent module
151 * current line in yang model
152 * @return module based on given prefix if found in context, null otherwise
154 public static def Module findModuleFromContext(SchemaContext context, ModuleBuilder currentModule,
155 String prefix, int line) {
156 if (context === null) {
157 throw new YangParseException(currentModule.getName(), line,
158 "Cannot find module with prefix '" + prefix + "'.");
160 val modulesByRevision = new TreeMap<Date, Module>();
162 val dependentModuleImport = ParserUtils.getModuleImport(currentModule, prefix);
163 if (dependentModuleImport === null) {
164 throw new YangParseException(currentModule.getName(), line, "No import found with prefix '" + prefix + "'.");
166 val dependentModuleName = dependentModuleImport.getModuleName();
167 val dependentModuleRevision = dependentModuleImport.getRevision();
169 for (Module contextModule : context.getModules()) {
170 if (contextModule.getName().equals(dependentModuleName)) {
171 var revision = contextModule.getRevision();
172 if (revision === null) {
173 revision = new Date(0L);
175 modulesByRevision.put(revision, contextModule);
179 var Module result = null;
180 if (dependentModuleRevision === null) {
181 result = modulesByRevision.get(modulesByRevision.firstKey());
183 result = modulesByRevision.get(dependentModuleRevision);
189 * Parse XPath string.
193 * @return SchemaPath from given String
195 public static def SchemaPath parseXPathString(String xpathString) {
196 val absolute = xpathString.startsWith("/");
197 val String[] splittedPath = xpathString.split("/");
198 val path = new ArrayList<QName>();
200 for (String pathElement : splittedPath) {
201 if (pathElement.length() > 0) {
202 val String[] splittedElement = pathElement.split(":");
203 if (splittedElement.length == 1) {
204 name = new QName(null, null, null, splittedElement.get(0));
206 name = new QName(null, null, splittedElement.get(0), splittedElement.get(1));
211 return new SchemaPath(path, absolute);
214 public static def dispatch fillAugmentTarget(AugmentationSchemaBuilder augment, Builder target) {
218 * Add all augment's child nodes to given target.
221 * builder of augment statement
223 * augmentation target node
225 public static def dispatch fillAugmentTarget(AugmentationSchemaBuilder augment, DataNodeContainerBuilder target) {
226 for (DataSchemaNodeBuilder child : augment.getChildNodes()) {
227 val childCopy = CopyUtils.copy(child, target, false);
228 if (augment.parent instanceof UsesNodeBuilder) {
229 setNodeAddedByUses(childCopy);
231 setNodeAugmenting(childCopy);
233 target.addChildNode(childCopy);
234 } catch (YangParseException e) {
236 // more descriptive message
237 throw new YangParseException(augment.getModuleName(), augment.getLine(),
238 "Failed to perform augmentation: " + e.getMessage());
244 * Add all augment's child nodes to given target.
247 * builder of augment statement
249 * augmentation target choice node
251 public static def dispatch fillAugmentTarget(AugmentationSchemaBuilder augment, ChoiceBuilder target) {
252 for (DataSchemaNodeBuilder builder : augment.getChildNodes()) {
253 val childCopy = CopyUtils.copy(builder, target, false);
254 if (augment.parent instanceof UsesNodeBuilder) {
255 setNodeAddedByUses(childCopy);
257 setNodeAugmenting(childCopy)
258 target.addCase(childCopy);
260 for (UsesNodeBuilder usesNode : augment.getUsesNodes()) {
261 if (usesNode !== null) {
262 throw new YangParseException(augment.getModuleName(), augment.getLine(),
263 "Error in augment parsing: cannot augment choice with nodes from grouping");
269 * Set augmenting flag to true for node and all its child nodes.
271 private static def void setNodeAugmenting(DataSchemaNodeBuilder child) {
272 child.setAugmenting(true);
273 if (child instanceof DataNodeContainerBuilder) {
274 val DataNodeContainerBuilder dataNodeChild = child as DataNodeContainerBuilder;
275 for (inner : dataNodeChild.getChildNodes()) {
276 setNodeAugmenting(inner);
278 } else if (child instanceof ChoiceBuilder) {
279 val ChoiceBuilder choiceChild = child as ChoiceBuilder;
280 for (inner : choiceChild.cases) {
281 setNodeAugmenting(inner);
287 * Set addedByUses flag to true for node and all its child nodes.
289 public static def void setNodeAddedByUses(GroupingMember child) {
290 child.setAddedByUses(true);
291 if (child instanceof DataNodeContainerBuilder) {
292 val DataNodeContainerBuilder dataNodeChild = child as DataNodeContainerBuilder;
293 for (inner : dataNodeChild.getChildNodes()) {
294 setNodeAddedByUses(inner);
296 } else if (child instanceof ChoiceBuilder) {
297 val ChoiceBuilder choiceChild = child as ChoiceBuilder;
298 for (inner : choiceChild.cases) {
299 setNodeAddedByUses(inner);
304 public static def DataSchemaNodeBuilder findSchemaNode(List<QName> path, SchemaNodeBuilder parentNode) {
305 var DataSchemaNodeBuilder node
306 var SchemaNodeBuilder parent = parentNode
308 while (i < path.size) {
309 val String name = path.get(i).localName
310 if (parent instanceof DataNodeContainerBuilder) {
311 node = (parent as DataNodeContainerBuilder).getDataChildByName(name)
312 } else if (parent instanceof ChoiceBuilder) {
313 node = (parent as ChoiceBuilder).getCaseNodeByName(name)
314 } else if (parent instanceof RpcDefinitionBuilder) {
315 if ("input".equals(name)) {
316 node = (parent as RpcDefinitionBuilder).input
317 } else if ("output".equals(name)) {
318 node = (parent as RpcDefinitionBuilder).output
326 if (i < path.size - 1) {
335 public static def SchemaNodeBuilder findSchemaNodeInModule(List<QName> pathToNode, ModuleBuilder module) {
336 val List<QName> path = new ArrayList(pathToNode)
337 val QName first = path.remove(0)
339 var SchemaNodeBuilder node = module.getDataChildByName(first.localName)
341 val notifications = module.notifications
342 for (notification : notifications) {
343 if (notification.QName.localName.equals(first.localName)) {
349 val rpcs = module.rpcs
351 if (rpc.QName.localName.equals(first.localName)) {
361 node = findSchemaNode(path, node)
368 * Find augment target node and perform augmentation.
371 * @param firstNodeParent
372 * parent of first node in path
374 * path to augment target
375 * @return true if augmentation process succeed, false otherwise
377 public static def boolean processAugmentation(AugmentationSchemaBuilder augment, ModuleBuilder firstNodeParent) {
378 val path = augment.targetPath.path
379 var Builder targetNode = findSchemaNodeInModule(path, firstNodeParent as ModuleBuilder)
380 if(targetNode === null) return false;
382 if ((targetNode instanceof DataNodeContainerBuilder)) {
383 val targetDataNodeContainer = targetNode as DataNodeContainerBuilder;
384 augment.setTargetNodeSchemaPath(targetDataNodeContainer.getPath());
385 } else if (targetNode instanceof ChoiceBuilder) {
386 val targetChoiceBuilder = targetNode as ChoiceBuilder;
387 augment.setTargetNodeSchemaPath(targetChoiceBuilder.getPath());
389 throw new YangParseException(augment.getModuleName(), augment.getLine(),
390 "Error in augment parsing: The target node MUST be either a container, list, choice, case, input, output, or notification node.");
392 fillAugmentTarget(augment, targetNode);
393 (targetNode as AugmentationTargetBuilder).addAugmentation(augment);
394 augment.setResolved(true);
398 public static def IdentitySchemaNodeBuilder findBaseIdentity(Map<String, TreeMap<Date, ModuleBuilder>> modules,
399 ModuleBuilder module, String baseString, int line) {
400 var IdentitySchemaNodeBuilder result = null;
401 if (baseString.contains(":")) {
402 val String[] splittedBase = baseString.split(":");
403 if (splittedBase.length > 2) {
404 throw new YangParseException(module.getName(), line,
405 "Failed to parse identityref base: " + baseString);
407 val prefix = splittedBase.get(0);
408 val name = splittedBase.get(1);
409 val dependentModule = findModuleFromBuilders(modules, module, prefix, line);
410 if (dependentModule !== null) {
411 result = findIdentity(dependentModule.identities, name);
414 result = findIdentity(module.identities, baseString);
419 public static def IdentitySchemaNode findBaseIdentityFromContext(Map<String, TreeMap<Date, ModuleBuilder>> modules,
420 ModuleBuilder module, String baseString, int line, SchemaContext context) {
421 var IdentitySchemaNode result = null;
423 val String[] splittedBase = baseString.split(":");
424 if (splittedBase.length > 2) {
425 throw new YangParseException(module.getName(), line, "Failed to parse identityref base: " + baseString);
427 val prefix = splittedBase.get(0);
428 val name = splittedBase.get(1);
429 val dependentModule = findModuleFromContext(context, module, prefix, line);
430 result = findIdentityNode(dependentModule.identities, name);
432 if (result == null) {
433 throw new YangParseException(module.name, line, "Failed to find base identity");
438 private static def IdentitySchemaNodeBuilder findIdentity(Set<IdentitySchemaNodeBuilder> identities, String name) {
439 for (identity : identities) {
440 if (identity.QName.localName.equals(name)) {
447 private static def IdentitySchemaNode findIdentityNode(Set<IdentitySchemaNode> identities, String name) {
448 for (identity : identities) {
449 if (identity.QName.localName.equals(name)) {
457 * Get module in which this node is defined.
460 * @return builder of module where this node is defined
462 public static def ModuleBuilder getParentModule(Builder node) {
463 if (node instanceof ModuleBuilder) {
464 return node as ModuleBuilder;
466 var parent = node.getParent();
467 while (!(parent instanceof ModuleBuilder)) {
468 parent = parent.getParent();
470 return parent as ModuleBuilder;
473 public static def Set<DataSchemaNodeBuilder> wrapChildNodes(String moduleName, int line, Set<DataSchemaNode> nodes,
474 SchemaPath parentPath, URI ns, Date rev, String pref) {
475 val Set<DataSchemaNodeBuilder> result = new HashSet()
477 for (DataSchemaNode node : nodes) {
478 val qname = new QName(ns, rev, pref, node.QName.localName)
479 val DataSchemaNodeBuilder wrapped = wrapChildNode(moduleName, line, node, parentPath, qname)
485 public static def DataSchemaNodeBuilder wrapChildNode(String moduleName, int line, DataSchemaNode node,
486 SchemaPath parentPath, QName qname) {
487 val List<QName> path = new ArrayList(parentPath.getPath())
489 val SchemaPath schemaPath = new SchemaPath(path, parentPath.isAbsolute())
491 if (node instanceof AnyXmlSchemaNode) {
492 return new AnyXmlBuilder(moduleName, line, qname, schemaPath, (node as AnyXmlSchemaNode));
493 } else if (node instanceof ChoiceNode) {
494 return new ChoiceBuilder(moduleName, line, qname, schemaPath, (node as ChoiceNode));
495 } else if (node instanceof ContainerSchemaNode) {
496 return new ContainerSchemaNodeBuilder(moduleName, line, qname, schemaPath, (node as ContainerSchemaNode));
497 } else if (node instanceof LeafSchemaNode) {
498 return new LeafSchemaNodeBuilder(moduleName, line, qname, schemaPath, (node as LeafSchemaNode));
499 } else if (node instanceof LeafListSchemaNode) {
500 return new LeafListSchemaNodeBuilder(moduleName, line, qname, schemaPath, (node as LeafListSchemaNode));
501 } else if (node instanceof ListSchemaNode) {
502 return new ListSchemaNodeBuilder(moduleName, line, qname, schemaPath, (node as ListSchemaNode));
503 } else if (node instanceof ChoiceCaseNode) {
504 return new ChoiceCaseBuilder(moduleName, line, qname, schemaPath, (node as ChoiceCaseNode));
506 throw new YangParseException(moduleName, line,
507 "Failed to copy node: Unknown type of DataSchemaNode: " + node)
511 public static def Set<GroupingBuilder> wrapGroupings(String moduleName, int line, Set<GroupingDefinition> nodes,
512 SchemaPath parentPath, URI ns, Date rev, String pref) {
513 val Set<GroupingBuilder> result = new HashSet()
514 for (GroupingDefinition node : nodes) {
515 val qname = new QName(ns, rev, pref, node.QName.localName)
516 val List<QName> path = new ArrayList(parentPath.getPath())
518 val SchemaPath schemaPath = new SchemaPath(path, parentPath.isAbsolute())
519 result.add(new GroupingBuilderImpl(moduleName, line, qname, schemaPath, node))
524 public static def Set<TypeDefinitionBuilder> wrapTypedefs(String moduleName, int line, DataNodeContainer dataNode,
525 SchemaPath parentPath, URI ns, Date rev, String pref) {
526 val Set<TypeDefinition<?>> nodes = dataNode.typeDefinitions
527 val Set<TypeDefinitionBuilder> result = new HashSet()
528 for (TypeDefinition<?> node : nodes) {
529 val qname = new QName(ns, rev, pref, node.QName.localName)
530 val List<QName> path = new ArrayList(parentPath.getPath())
532 val SchemaPath schemaPath = new SchemaPath(path, parentPath.isAbsolute())
533 result.add(new TypeDefinitionBuilderImpl(moduleName, line, qname, schemaPath, (node as ExtendedType)))
538 public static def List<UnknownSchemaNodeBuilder> wrapUnknownNodes(String moduleName, int line,
539 List<UnknownSchemaNode> nodes, SchemaPath parentPath, URI ns, Date rev, String pref) {
540 val List<UnknownSchemaNodeBuilder> result = new ArrayList()
541 for (UnknownSchemaNode node : nodes) {
542 val qname = new QName(ns, rev, pref, node.QName.localName)
543 val List<QName> path = new ArrayList(parentPath.getPath())
545 val SchemaPath schemaPath = new SchemaPath(path, parentPath.isAbsolute())
546 result.add(new UnknownSchemaNodeBuilder(moduleName, line, qname, schemaPath, (node as UnknownSchemaNode)))