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.DataNodeContainer;
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.NotificationDefinition;
24 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
25 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
26 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
27 import org.opendaylight.yangtools.yang.parser.builder.api.AugmentationSchemaBuilder;
28 import org.opendaylight.yangtools.yang.parser.builder.api.AugmentationTargetBuilder;
29 import org.opendaylight.yangtools.yang.parser.builder.api.Builder;
30 import org.opendaylight.yangtools.yang.parser.builder.api.DataNodeContainerBuilder;
31 import org.opendaylight.yangtools.yang.parser.builder.api.DataSchemaNodeBuilder;
32 import org.opendaylight.yangtools.yang.parser.builder.api.GroupingBuilder;
33 import org.opendaylight.yangtools.yang.parser.builder.api.SchemaNodeBuilder;
34 import org.opendaylight.yangtools.yang.parser.builder.api.UsesNodeBuilder;
35 import org.opendaylight.yangtools.yang.parser.builder.impl.ChoiceBuilder;
36 import org.opendaylight.yangtools.yang.parser.builder.impl.ChoiceBuilder.ChoiceNodeImpl;
37 import org.opendaylight.yangtools.yang.parser.builder.impl.ChoiceCaseBuilder;
38 import org.opendaylight.yangtools.yang.parser.builder.impl.ChoiceCaseBuilder.ChoiceCaseNodeImpl;
39 import org.opendaylight.yangtools.yang.parser.builder.impl.ContainerSchemaNodeBuilder;
40 import org.opendaylight.yangtools.yang.parser.builder.impl.ContainerSchemaNodeBuilder.ContainerSchemaNodeImpl;
41 import org.opendaylight.yangtools.yang.parser.builder.impl.IdentityrefTypeBuilder;
42 import org.opendaylight.yangtools.yang.parser.builder.impl.ListSchemaNodeBuilder;
43 import org.opendaylight.yangtools.yang.parser.builder.impl.ListSchemaNodeBuilder.ListSchemaNodeImpl;
44 import org.opendaylight.yangtools.yang.parser.builder.impl.ModuleBuilder;
45 import org.opendaylight.yangtools.yang.parser.builder.impl.NotificationBuilder;
46 import org.opendaylight.yangtools.yang.parser.builder.impl.NotificationBuilder.NotificationDefinitionImpl;
48 public final class ParserUtils {
50 private ParserUtils() {
54 * Create new SchemaPath from given path and qname.
60 public static SchemaPath createSchemaPath(SchemaPath schemaPath, QName... qname) {
61 List<QName> path = new ArrayList<>(schemaPath.getPath());
62 path.addAll(Arrays.asList(qname));
63 return new SchemaPath(path, schemaPath.isAbsolute());
67 * Get module import referenced by given prefix.
72 * prefix associated with import
73 * @return ModuleImport based on given prefix
75 public static ModuleImport getModuleImport(final ModuleBuilder builder, final String prefix) {
76 ModuleImport moduleImport = null;
77 for (ModuleImport mi : builder.getModuleImports()) {
78 if (mi.getPrefix().equals(prefix)) {
87 * Find dependent module based on given prefix
90 * all available modules
94 * target module prefix
96 * current line in yang model
97 * @return module builder if found, null otherwise
99 public static ModuleBuilder findDependentModuleBuilder(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
100 final ModuleBuilder module, final String prefix, final int line) {
101 ModuleBuilder dependentModule = null;
102 Date dependentModuleRevision = null;
104 if (prefix.equals(module.getPrefix())) {
105 dependentModule = module;
107 final ModuleImport dependentModuleImport = getModuleImport(module, prefix);
108 if (dependentModuleImport == null) {
109 throw new YangParseException(module.getName(), line, "No import found with prefix '" + prefix + "'.");
111 final String dependentModuleName = dependentModuleImport.getModuleName();
112 dependentModuleRevision = dependentModuleImport.getRevision();
114 final TreeMap<Date, ModuleBuilder> moduleBuildersByRevision = modules.get(dependentModuleName);
115 if (moduleBuildersByRevision == null) {
118 if (dependentModuleRevision == null) {
119 dependentModule = moduleBuildersByRevision.lastEntry().getValue();
121 dependentModule = moduleBuildersByRevision.get(dependentModuleRevision);
124 return dependentModule;
128 * Find module from context based on prefix.
132 * @param currentModule
135 * current prefix used to reference dependent module
137 * current line in yang model
138 * @return module based on given prefix if found in context, null otherwise
140 public static Module findModuleFromContext(final SchemaContext context, final ModuleBuilder currentModule,
141 final String prefix, final int line) {
142 TreeMap<Date, Module> modulesByRevision = new TreeMap<Date, Module>();
144 final ModuleImport dependentModuleImport = ParserUtils.getModuleImport(currentModule, prefix);
145 if (dependentModuleImport == null) {
146 throw new YangParseException(currentModule.getName(), line, "No import found with prefix '" + prefix + "'.");
148 final String dependentModuleName = dependentModuleImport.getModuleName();
149 final Date dependentModuleRevision = dependentModuleImport.getRevision();
151 for (Module contextModule : context.getModules()) {
152 if (contextModule.getName().equals(dependentModuleName)) {
153 Date revision = contextModule.getRevision();
154 if (revision == null) {
155 revision = new Date(0L);
157 modulesByRevision.put(revision, contextModule);
162 Module result = null;
163 if (dependentModuleRevision == null) {
164 result = modulesByRevision.get(modulesByRevision.firstKey());
166 result = modulesByRevision.get(dependentModuleRevision);
173 * Parse XPath string.
177 * @return SchemaPath from given String
179 public static SchemaPath parseXPathString(final String xpathString) {
180 final boolean absolute = xpathString.startsWith("/");
181 final String[] splittedPath = xpathString.split("/");
182 final List<QName> path = new ArrayList<QName>();
184 for (String pathElement : splittedPath) {
185 if (pathElement.length() > 0) {
186 final String[] splittedElement = pathElement.split(":");
187 if (splittedElement.length == 1) {
188 name = new QName(null, null, null, splittedElement[0]);
190 name = new QName(null, null, splittedElement[0], splittedElement[1]);
195 return new SchemaPath(path, absolute);
199 * Add all augment's child nodes to given target.
202 * builder of augment statement
204 * augmentation target node
206 public static void fillAugmentTarget(final AugmentationSchemaBuilder augment, final DataNodeContainerBuilder target) {
207 for (DataSchemaNodeBuilder child : augment.getChildNodeBuilders()) {
208 DataSchemaNodeBuilder childCopy = CopyUtils.copy(child, target, false);
209 childCopy.setAugmenting(true);
210 correctNodePath(child, target.getPath());
211 correctNodePath(childCopy, target.getPath());
213 target.addChildNode(childCopy);
214 } catch (YangParseException e) {
215 // more descriptive message
216 throw new YangParseException(augment.getModuleName(), augment.getLine(),
217 "Failed to perform augmentation: " + e.getMessage());
221 for (UsesNodeBuilder usesNode : augment.getUsesNodes()) {
222 UsesNodeBuilder copy = CopyUtils.copyUses(usesNode, target);
223 target.addUsesNode(copy);
228 * Add all augment's child nodes to given target.
231 * builder of augment statement
233 * augmentation target choice node
235 public static void fillAugmentTarget(final AugmentationSchemaBuilder augment, final ChoiceBuilder target) {
236 for (DataSchemaNodeBuilder builder : augment.getChildNodeBuilders()) {
237 DataSchemaNodeBuilder childCopy = CopyUtils.copy(builder, target, false);
238 childCopy.setAugmenting(true);
239 correctNodePath(builder, target.getPath());
240 correctNodePath(childCopy, target.getPath());
241 target.addCase(childCopy);
243 for (UsesNodeBuilder usesNode : augment.getUsesNodes()) {
244 if (usesNode != null) {
245 throw new YangParseException(augment.getModuleName(), augment.getLine(),
246 "Error in augment parsing: cannot augment uses to choice");
251 static void correctNodePath(final SchemaNodeBuilder node, final SchemaPath parentSchemaPath) {
253 List<QName> targetNodePath = new ArrayList<QName>(parentSchemaPath.getPath());
254 targetNodePath.add(node.getQName());
255 node.setPath(new SchemaPath(targetNodePath, true));
257 // set correct path for all child nodes
258 if (node instanceof DataNodeContainerBuilder) {
259 DataNodeContainerBuilder dataNodeContainer = (DataNodeContainerBuilder) node;
260 for (DataSchemaNodeBuilder child : dataNodeContainer.getChildNodeBuilders()) {
261 correctNodePath(child, node.getPath());
265 // set correct path for all cases
266 if (node instanceof ChoiceBuilder) {
267 ChoiceBuilder choiceBuilder = (ChoiceBuilder) node;
268 for (ChoiceCaseBuilder choiceCaseBuilder : choiceBuilder.getCases()) {
269 correctNodePath(choiceCaseBuilder, node.getPath());
275 * Find augment target node and perform augmentation.
278 * @param firstNodeParent
279 * parent of first node in path
281 * path to augment target
282 * @return true if augment process succeed, false otherwise
284 public static boolean processAugmentation(final AugmentationSchemaBuilder augment, final Builder firstNodeParent,
285 final List<QName> path) {
286 // traverse augment target path and try to reach target node
287 String currentName = null;
288 Builder currentParent = firstNodeParent;
290 for (int i = 0; i < path.size(); i++) {
291 QName qname = path.get(i);
293 currentName = qname.getLocalName();
294 if (currentParent instanceof DataNodeContainerBuilder) {
295 DataSchemaNodeBuilder nodeFound = ((DataNodeContainerBuilder) currentParent)
296 .getDataChildByName(currentName);
297 // if not found as regular child, search in uses
298 if (nodeFound == null) {
299 boolean found = false;
300 for (UsesNodeBuilder unb : ((DataNodeContainerBuilder) currentParent).getUsesNodes()) {
301 DataSchemaNodeBuilder result = findNodeInUses(currentName, unb);
302 if (result != null) {
303 currentParent = result;
308 // if not found even in uses nodes, return false
313 currentParent = nodeFound;
315 } else if (currentParent instanceof ChoiceBuilder) {
316 currentParent = ((ChoiceBuilder) currentParent).getCaseNodeByName(currentName);
318 throw new YangParseException(augment.getModuleName(), augment.getLine(),
319 "Error in augment parsing: failed to find node " + currentName);
322 // if node in path not found, return false
323 if (currentParent == null) {
327 if (!(currentParent instanceof DataSchemaNodeBuilder)) {
328 throw new YangParseException(
329 augment.getModuleName(),
331 "Error in augment parsing: The target node MUST be either a container, list, choice, case, input, output, or notification node.");
334 if (currentParent instanceof ChoiceBuilder) {
335 fillAugmentTarget(augment, (ChoiceBuilder) currentParent);
337 fillAugmentTarget(augment, (DataNodeContainerBuilder) currentParent);
339 ((AugmentationTargetBuilder) currentParent).addAugmentation(augment);
340 SchemaPath oldPath = ((DataSchemaNodeBuilder) currentParent).getPath();
341 augment.setTargetPath(new SchemaPath(oldPath.getPath(), oldPath.isAbsolute()));
342 augment.setResolved(true);
347 private static DataSchemaNodeBuilder findNodeInUses(String localName, UsesNodeBuilder uses) {
348 GroupingBuilder target = uses.getGroupingBuilder();
349 for (DataSchemaNodeBuilder child : target.getChildNodeBuilders()) {
350 if (child.getQName().getLocalName().equals(localName)) {
354 for (UsesNodeBuilder usesNode : target.getUsesNodes()) {
355 DataSchemaNodeBuilder result = findNodeInUses(localName, usesNode);
356 if (result != null) {
364 * Find augment target node in given context and perform augmentation.
368 * path to augment target
372 * current prefix of target module
374 * SchemaContext containing already resolved modules
375 * @return true if augment process succeed, false otherwise
377 public static boolean processAugmentationOnContext(final AugmentationSchemaBuilder augment, final List<QName> path,
378 final ModuleBuilder module, final String prefix, final SchemaContext context) {
379 final int line = augment.getLine();
380 final Module dependentModule = findModuleFromContext(context, module, prefix, line);
381 if (dependentModule == null) {
382 throw new YangParseException(module.getName(), line,
383 "Error in augment parsing: failed to find module with prefix " + prefix + ".");
386 String currentName = path.get(0).getLocalName();
387 SchemaNode currentParent = dependentModule.getDataChildByName(currentName);
388 if (currentParent == null) {
389 Set<NotificationDefinition> notifications = dependentModule.getNotifications();
390 for (NotificationDefinition ntf : notifications) {
391 if (ntf.getQName().getLocalName().equals(currentName)) {
397 if (currentParent == null) {
398 throw new YangParseException(module.getName(), line, "Error in augment parsing: failed to find node "
399 + currentName + ".");
402 for (int i = 1; i < path.size(); i++) {
403 currentName = path.get(i).getLocalName();
404 if (currentParent instanceof DataNodeContainer) {
405 currentParent = ((DataNodeContainer) currentParent).getDataChildByName(currentName);
406 } else if (currentParent instanceof ChoiceNode) {
407 currentParent = ((ChoiceNode) currentParent).getCaseNodeByName(currentName);
409 throw new YangParseException(augment.getModuleName(), line,
410 "Error in augment parsing: failed to find node " + currentName);
412 // if node in path not found, return false
413 if (currentParent == null) {
414 throw new YangParseException(module.getName(), line, "Error in augment parsing: failed to find node "
415 + currentName + ".");
419 if (currentParent instanceof ContainerSchemaNodeImpl) {
420 // includes container, input and output statement
421 ContainerSchemaNodeImpl c = (ContainerSchemaNodeImpl) currentParent;
422 ContainerSchemaNodeBuilder cb = c.toBuilder();
423 fillAugmentTarget(augment, cb);
424 ((AugmentationTargetBuilder) cb).addAugmentation(augment);
425 SchemaPath oldPath = cb.getPath();
427 augment.setTargetPath(new SchemaPath(oldPath.getPath(), oldPath.isAbsolute()));
428 augment.setResolved(true);
429 } else if (currentParent instanceof ListSchemaNodeImpl) {
430 ListSchemaNodeImpl l = (ListSchemaNodeImpl) currentParent;
431 ListSchemaNodeBuilder lb = l.toBuilder();
432 fillAugmentTarget(augment, lb);
433 ((AugmentationTargetBuilder) lb).addAugmentation(augment);
434 SchemaPath oldPath = lb.getPath();
436 augment.setTargetPath(new SchemaPath(oldPath.getPath(), oldPath.isAbsolute()));
437 augment.setResolved(true);
438 } else if (currentParent instanceof ChoiceNodeImpl) {
439 ChoiceNodeImpl ch = (ChoiceNodeImpl) currentParent;
440 ChoiceBuilder chb = ch.toBuilder();
441 fillAugmentTarget(augment, chb);
442 ((AugmentationTargetBuilder) chb).addAugmentation(augment);
443 SchemaPath oldPath = chb.getPath();
445 augment.setTargetPath(new SchemaPath(oldPath.getPath(), oldPath.isAbsolute()));
446 augment.setResolved(true);
447 } else if (currentParent instanceof ChoiceCaseNodeImpl) {
448 ChoiceCaseNodeImpl chc = (ChoiceCaseNodeImpl) currentParent;
449 ChoiceCaseBuilder chcb = chc.toBuilder();
450 fillAugmentTarget(augment, chcb);
451 ((AugmentationTargetBuilder) chcb).addAugmentation(augment);
452 SchemaPath oldPath = chcb.getPath();
454 augment.setTargetPath(new SchemaPath(oldPath.getPath(), oldPath.isAbsolute()));
455 augment.setResolved(true);
456 } else if (currentParent instanceof NotificationDefinitionImpl) {
457 NotificationDefinitionImpl nd = (NotificationDefinitionImpl) currentParent;
458 NotificationBuilder nb = nd.toBuilder();
459 fillAugmentTarget(augment, nb);
460 ((AugmentationTargetBuilder) nb).addAugmentation(augment);
461 SchemaPath oldPath = nb.getPath();
463 augment.setTargetPath(new SchemaPath(oldPath.getPath(), oldPath.isAbsolute()));
464 augment.setResolved(true);
466 throw new YangParseException(module.getName(), line, "Target of type " + currentParent.getClass()
467 + " cannot be augmented.");
473 public static QName findFullQName(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
474 final ModuleBuilder module, final IdentityrefTypeBuilder idref) {
476 String baseString = idref.getBaseString();
477 if (baseString.contains(":")) {
478 String[] splittedBase = baseString.split(":");
479 if (splittedBase.length > 2) {
480 throw new YangParseException(module.getName(), idref.getLine(), "Failed to parse identityref base: "
483 String prefix = splittedBase[0];
484 String name = splittedBase[1];
485 ModuleBuilder dependentModule = findDependentModuleBuilder(modules, module, prefix, idref.getLine());
486 result = new QName(dependentModule.getNamespace(), dependentModule.getRevision(), prefix, name);
488 result = new QName(module.getNamespace(), module.getRevision(), module.getPrefix(), baseString);
494 * Get module in which this node is defined.
497 * @return builder of module where this node is defined
499 public static ModuleBuilder getParentModule(Builder node) {
500 if(node instanceof ModuleBuilder) {
501 return (ModuleBuilder)node;
504 Builder parent = node.getParent();
505 while (!(parent instanceof ModuleBuilder)) {
506 parent = parent.getParent();
508 return (ModuleBuilder) parent;