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
62 import com.google.common.base.Preconditions
64 public final class ParserUtils {
70 * Create new SchemaPath from given path and qname.
74 * @return new SchemaPath from given path and qname
76 public static def SchemaPath createSchemaPath(SchemaPath schemaPath, QName... qname) {
77 val path = new ArrayList<QName>(schemaPath.getPath());
78 path.addAll(Arrays.asList(qname));
79 return new SchemaPath(path, schemaPath.isAbsolute());
83 * Get module import referenced by given prefix.
88 * prefix associated with import
89 * @return ModuleImport based on given prefix
91 public static def ModuleImport getModuleImport(ModuleBuilder builder, String prefix) {
92 for (ModuleImport mi : builder.getModuleImports()) {
93 if (mi.getPrefix().equals(prefix)) {
102 * Find dependent module based on given prefix
105 * all available modules
109 * target module prefix
111 * current line in yang model
112 * @return module builder if found, null otherwise
114 public static def ModuleBuilder findModuleFromBuilders(Map<String, TreeMap<Date, ModuleBuilder>> modules,
115 ModuleBuilder module, String prefix, int line) {
116 var ModuleBuilder dependentModule = null;
117 var Date dependentModuleRevision = null;
120 dependentModule = module;
121 } else if (prefix.equals(module.getPrefix())) {
122 dependentModule = module;
124 val ModuleImport dependentModuleImport = getModuleImport(module, prefix);
125 if (dependentModuleImport === null) {
126 throw new YangParseException(module.getName(), line, "No import found with prefix '" + prefix + "'.");
128 val String dependentModuleName = dependentModuleImport.getModuleName();
129 dependentModuleRevision = dependentModuleImport.getRevision();
131 val TreeMap<Date, ModuleBuilder> moduleBuildersByRevision = modules.get(dependentModuleName);
132 if (moduleBuildersByRevision === null) {
135 if (dependentModuleRevision === null) {
136 dependentModule = moduleBuildersByRevision.lastEntry().getValue();
138 dependentModule = moduleBuildersByRevision.get(dependentModuleRevision);
141 return dependentModule;
145 * Find module from context based on prefix.
149 * @param currentModule
152 * current prefix used to reference dependent module
154 * current line in yang model
155 * @return module based on given prefix if found in context, null otherwise
157 public static def Module findModuleFromContext(SchemaContext context, ModuleBuilder currentModule,
158 String prefix, int line) {
159 if (context === null) {
160 throw new YangParseException(currentModule.getName(), line,
161 "Cannot find module with prefix '" + prefix + "'.");
163 val modulesByRevision = new TreeMap<Date, Module>();
165 val dependentModuleImport = ParserUtils.getModuleImport(currentModule, prefix);
166 if (dependentModuleImport === null) {
167 throw new YangParseException(currentModule.getName(), line, "No import found with prefix '" + prefix + "'.");
169 val dependentModuleName = dependentModuleImport.getModuleName();
170 val dependentModuleRevision = dependentModuleImport.getRevision();
172 for (Module contextModule : context.getModules()) {
173 if (contextModule.getName().equals(dependentModuleName)) {
174 var revision = contextModule.getRevision();
175 if (revision === null) {
176 revision = new Date(0L);
178 modulesByRevision.put(revision, contextModule);
182 var Module result = null;
183 if (dependentModuleRevision === null) {
184 result = modulesByRevision.get(modulesByRevision.firstKey());
186 result = modulesByRevision.get(dependentModuleRevision);
192 * Parse XPath string.
196 * @return SchemaPath from given String
198 public static def SchemaPath parseXPathString(String xpathString) {
199 val absolute = xpathString.startsWith("/");
200 val String[] splittedPath = xpathString.split("/");
201 val path = new ArrayList<QName>();
203 for (String pathElement : splittedPath) {
204 if (pathElement.length() > 0) {
205 val String[] splittedElement = pathElement.split(":");
206 if (splittedElement.length == 1) {
207 name = new QName(null, null, null, splittedElement.get(0));
209 name = new QName(null, null, splittedElement.get(0), splittedElement.get(1));
214 return new SchemaPath(path, absolute);
217 public static def dispatch fillAugmentTarget(AugmentationSchemaBuilder augment, Builder target) {
221 * Add all augment's child nodes to given target.
224 * builder of augment statement
226 * augmentation target node
228 public static def dispatch fillAugmentTarget(AugmentationSchemaBuilder augment, DataNodeContainerBuilder target) {
229 for (DataSchemaNodeBuilder child : augment.getChildNodeBuilders()) {
230 val childCopy = CopyUtils.copy(child, target, false);
231 if (augment.parent instanceof UsesNodeBuilder) {
232 setNodeAddedByUses(childCopy);
234 setNodeAugmenting(childCopy);
236 target.addChildNode(childCopy);
237 } catch (YangParseException e) {
239 // more descriptive message
240 throw new YangParseException(augment.getModuleName(), augment.getLine(),
241 "Failed to perform augmentation: " + e.getMessage());
247 * Add all augment's child nodes to given target.
250 * builder of augment statement
252 * augmentation target choice node
254 public static def dispatch fillAugmentTarget(AugmentationSchemaBuilder augment, ChoiceBuilder target) {
255 for (DataSchemaNodeBuilder builder : augment.getChildNodeBuilders()) {
256 val childCopy = CopyUtils.copy(builder, target, false);
257 if (augment.parent instanceof UsesNodeBuilder) {
258 setNodeAddedByUses(childCopy);
260 setNodeAugmenting(childCopy)
261 target.addCase(childCopy);
263 for (UsesNodeBuilder usesNode : augment.getUsesNodeBuilders()) {
264 if (usesNode !== null) {
265 throw new YangParseException(augment.getModuleName(), augment.getLine(),
266 "Error in augment parsing: cannot augment choice with nodes from grouping");
272 * Set augmenting flag to true for node and all its child nodes.
274 private static def void setNodeAugmenting(DataSchemaNodeBuilder child) {
275 child.setAugmenting(true);
276 if (child instanceof DataNodeContainerBuilder) {
277 val DataNodeContainerBuilder dataNodeChild = child as DataNodeContainerBuilder;
278 for (inner : dataNodeChild.getChildNodeBuilders()) {
279 setNodeAugmenting(inner);
281 } else if (child instanceof ChoiceBuilder) {
282 val ChoiceBuilder choiceChild = child as ChoiceBuilder;
283 for (inner : choiceChild.cases) {
284 setNodeAugmenting(inner);
290 * Set addedByUses flag to true for node and all its child nodes.
292 public static def void setNodeAddedByUses(GroupingMember child) {
293 child.setAddedByUses(true);
294 if (child instanceof DataNodeContainerBuilder) {
295 val DataNodeContainerBuilder dataNodeChild = child as DataNodeContainerBuilder;
296 for (inner : dataNodeChild.getChildNodeBuilders()) {
297 setNodeAddedByUses(inner);
299 } else if (child instanceof ChoiceBuilder) {
300 val ChoiceBuilder choiceChild = child as ChoiceBuilder;
301 for (inner : choiceChild.cases) {
302 setNodeAddedByUses(inner);
307 public static def DataSchemaNodeBuilder findSchemaNode(List<QName> path, SchemaNodeBuilder parentNode) {
308 var DataSchemaNodeBuilder node
309 var SchemaNodeBuilder parent = parentNode
311 while (i < path.size) {
312 val String name = path.get(i).localName
313 if (parent instanceof DataNodeContainerBuilder) {
314 node = (parent as DataNodeContainerBuilder).getDataChildByName(name)
315 } else if (parent instanceof ChoiceBuilder) {
316 node = (parent as ChoiceBuilder).getCaseNodeByName(name)
317 } else if (parent instanceof RpcDefinitionBuilder) {
318 if ("input".equals(name)) {
319 node = (parent as RpcDefinitionBuilder).input
320 } else if ("output".equals(name)) {
321 node = (parent as RpcDefinitionBuilder).output
329 if (i < path.size - 1) {
338 public static def SchemaNodeBuilder findSchemaNodeInModule(List<QName> pathToNode, ModuleBuilder module) {
339 val List<QName> path = new ArrayList(pathToNode)
340 val QName first = path.remove(0)
342 var SchemaNodeBuilder node = module.getDataChildByName(first.localName)
344 val notifications = module.getAddedNotifications
345 for (notification : notifications) {
346 if (notification.QName.localName.equals(first.localName)) {
352 val rpcs = module.getAddedRpcs
354 if (rpc.QName.localName.equals(first.localName)) {
364 node = findSchemaNode(path, node)
371 * Find augment target node and perform augmentation.
374 * @param firstNodeParent
375 * parent of first node in path
377 * path to augment target
378 * @return true if augmentation process succeed, false otherwise
380 public static def boolean processAugmentation(AugmentationSchemaBuilder augment, ModuleBuilder firstNodeParent) {
381 val path = augment.targetPath.path
382 var Builder targetNode = findSchemaNodeInModule(path, firstNodeParent as ModuleBuilder)
383 if(targetNode === null) return false;
385 if ((targetNode instanceof DataNodeContainerBuilder)) {
386 val targetDataNodeContainer = targetNode as DataNodeContainerBuilder;
387 augment.setTargetNodeSchemaPath(targetDataNodeContainer.getPath());
388 } else if (targetNode instanceof ChoiceBuilder) {
389 val targetChoiceBuilder = targetNode as ChoiceBuilder;
390 augment.setTargetNodeSchemaPath(targetChoiceBuilder.getPath());
392 throw new YangParseException(augment.getModuleName(), augment.getLine(),
393 "Error in augment parsing: The target node MUST be either a container, list, choice, case, input, output, or notification node.");
395 fillAugmentTarget(augment, targetNode);
396 (targetNode as AugmentationTargetBuilder).addAugmentation(augment);
397 augment.setResolved(true);
401 public static def IdentitySchemaNodeBuilder findBaseIdentity(Map<String, TreeMap<Date, ModuleBuilder>> modules,
402 ModuleBuilder module, String baseString, int line) {
403 var IdentitySchemaNodeBuilder result = null;
404 if (baseString.contains(":")) {
405 val String[] splittedBase = baseString.split(":");
406 if (splittedBase.length > 2) {
407 throw new YangParseException(module.getName(), line,
408 "Failed to parse identityref base: " + baseString);
410 val prefix = splittedBase.get(0);
411 val name = splittedBase.get(1);
412 val dependentModule = findModuleFromBuilders(modules, module, prefix, line);
413 if (dependentModule !== null) {
414 result = findIdentity(dependentModule.getAddedIdentities, name);
417 result = findIdentity(module.getAddedIdentities, baseString);
422 public static def IdentitySchemaNode findBaseIdentityFromContext(Map<String, TreeMap<Date, ModuleBuilder>> modules,
423 ModuleBuilder module, String baseString, int line, SchemaContext context) {
424 var IdentitySchemaNode result = null;
426 val String[] splittedBase = baseString.split(":");
427 if (splittedBase.length > 2) {
428 throw new YangParseException(module.getName(), line, "Failed to parse identityref base: " + baseString);
430 val prefix = splittedBase.get(0);
431 val name = splittedBase.get(1);
432 val dependentModule = findModuleFromContext(context, module, prefix, line);
433 result = findIdentityNode(dependentModule.identities, name);
435 if (result == null) {
436 throw new YangParseException(module.name, line, "Failed to find base identity");
441 private static def IdentitySchemaNodeBuilder findIdentity(Set<IdentitySchemaNodeBuilder> identities, String name) {
442 for (identity : identities) {
443 if (identity.QName.localName.equals(name)) {
450 private static def IdentitySchemaNode findIdentityNode(Set<IdentitySchemaNode> identities, String name) {
451 for (identity : identities) {
452 if (identity.QName.localName.equals(name)) {
460 * Get module in which this node is defined.
463 * @return builder of module where this node is defined
465 public static def ModuleBuilder getParentModule(Builder node) {
466 if (node instanceof ModuleBuilder) {
467 return node as ModuleBuilder;
469 var parent = node.getParent();
470 while (!(parent instanceof ModuleBuilder)) {
471 parent = parent.getParent();
473 Preconditions.checkState(parent instanceof ModuleBuilder)
474 var parentModule = parent as ModuleBuilder
475 if(parentModule.submodule) {
476 parentModule = parentModule.parent;
481 public static def Set<DataSchemaNodeBuilder> wrapChildNodes(String moduleName, int line, Set<DataSchemaNode> nodes,
482 SchemaPath parentPath, URI ns, Date rev, String pref) {
483 val Set<DataSchemaNodeBuilder> result = new HashSet()
485 for (DataSchemaNode node : nodes) {
486 val qname = new QName(ns, rev, pref, node.QName.localName)
487 val DataSchemaNodeBuilder wrapped = wrapChildNode(moduleName, line, node, parentPath, qname)
493 public static def DataSchemaNodeBuilder wrapChildNode(String moduleName, int line, DataSchemaNode node,
494 SchemaPath parentPath, QName qname) {
495 val List<QName> path = new ArrayList(parentPath.getPath())
497 val SchemaPath schemaPath = new SchemaPath(path, parentPath.isAbsolute())
499 if (node instanceof AnyXmlSchemaNode) {
500 return new AnyXmlBuilder(moduleName, line, qname, schemaPath, (node as AnyXmlSchemaNode));
501 } else if (node instanceof ChoiceNode) {
502 return new ChoiceBuilder(moduleName, line, qname, schemaPath, (node as ChoiceNode));
503 } else if (node instanceof ContainerSchemaNode) {
504 return new ContainerSchemaNodeBuilder(moduleName, line, qname, schemaPath, (node as ContainerSchemaNode));
505 } else if (node instanceof LeafSchemaNode) {
506 return new LeafSchemaNodeBuilder(moduleName, line, qname, schemaPath, (node as LeafSchemaNode));
507 } else if (node instanceof LeafListSchemaNode) {
508 return new LeafListSchemaNodeBuilder(moduleName, line, qname, schemaPath, (node as LeafListSchemaNode));
509 } else if (node instanceof ListSchemaNode) {
510 return new ListSchemaNodeBuilder(moduleName, line, qname, schemaPath, (node as ListSchemaNode));
511 } else if (node instanceof ChoiceCaseNode) {
512 return new ChoiceCaseBuilder(moduleName, line, qname, schemaPath, (node as ChoiceCaseNode));
514 throw new YangParseException(moduleName, line,
515 "Failed to copy node: Unknown type of DataSchemaNode: " + node)
519 public static def Set<GroupingBuilder> wrapGroupings(String moduleName, int line, Set<GroupingDefinition> nodes,
520 SchemaPath parentPath, URI ns, Date rev, String pref) {
521 val Set<GroupingBuilder> result = new HashSet()
522 for (GroupingDefinition node : nodes) {
523 val qname = new QName(ns, rev, pref, node.QName.localName)
524 val List<QName> path = new ArrayList(parentPath.getPath())
526 val SchemaPath schemaPath = new SchemaPath(path, parentPath.isAbsolute())
527 result.add(new GroupingBuilderImpl(moduleName, line, qname, schemaPath, node))
532 public static def Set<TypeDefinitionBuilder> wrapTypedefs(String moduleName, int line, DataNodeContainer dataNode,
533 SchemaPath parentPath, URI ns, Date rev, String pref) {
534 val Set<TypeDefinition<?>> nodes = dataNode.typeDefinitions
535 val Set<TypeDefinitionBuilder> result = new HashSet()
536 for (TypeDefinition<?> node : nodes) {
537 val qname = new QName(ns, rev, pref, node.QName.localName)
538 val List<QName> path = new ArrayList(parentPath.getPath())
540 val SchemaPath schemaPath = new SchemaPath(path, parentPath.isAbsolute())
541 result.add(new TypeDefinitionBuilderImpl(moduleName, line, qname, schemaPath, (node as ExtendedType)))
546 public static def List<UnknownSchemaNodeBuilder> wrapUnknownNodes(String moduleName, int line,
547 List<UnknownSchemaNode> nodes, SchemaPath parentPath, URI ns, Date rev, String pref) {
548 val List<UnknownSchemaNodeBuilder> result = new ArrayList()
549 for (UnknownSchemaNode node : nodes) {
550 val qname = new QName(ns, rev, pref, node.QName.localName)
551 val List<QName> path = new ArrayList(parentPath.getPath())
553 val SchemaPath schemaPath = new SchemaPath(path, parentPath.isAbsolute())
554 result.add(new UnknownSchemaNodeBuilder(moduleName, line, qname, schemaPath, (node as UnknownSchemaNode)))