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.controller.yang.model.util;
11 import java.util.LinkedList;
12 import java.util.List;
13 import java.util.Queue;
16 import org.opendaylight.controller.yang.common.QName;
17 import org.opendaylight.controller.yang.model.api.*;
20 * The Schema Context Util contains support methods for searching through Schema Context modules for specified schema
21 * nodes via Schema Path or Revision Aware XPath. The Schema Context Util is designed as mixin,
22 * so it is not instantiable.
24 * @author Lukas Sedlak <lsedlak@cisco.com>
26 public final class SchemaContextUtil {
28 private SchemaContextUtil() {
32 * Method attempts to find DataSchemaNode in Schema Context via specified Schema Path. The returned
33 * DataSchemaNode from method will be the node at the end of the SchemaPath. If the DataSchemaNode is not present
34 * in the Schema Context the method will return <code>null</code>.
36 * In case that Schema Context or Schema Path are not specified correctly (i.e. contains <code>null</code>
37 * values) the method will return IllegalArgumentException.
39 * @throws IllegalArgumentException
44 * Schema Path to search for
45 * @return DataSchemaNode from the end of the Schema Path or
46 * <code>null</code> if the Node is not present.
48 public static DataSchemaNode findDataSchemaNode(final SchemaContext context, final SchemaPath schemaPath) {
49 if (context == null) {
50 throw new IllegalArgumentException("Schema Context reference cannot be NULL!");
52 if (schemaPath == null) {
53 throw new IllegalArgumentException("Schema Path reference cannot be NULL");
56 final Module module = resolveModuleFromSchemaPath(context, schemaPath);
57 final Queue<QName> prefixedPath = new LinkedList<>(schemaPath.getPath());
59 if ((module != null) && (prefixedPath != null)) {
60 return findSchemaNodeForGivenPath(context, module, prefixedPath);
66 * Method attempts to find DataSchemaNode inside of provided Schema Context and Yang Module accordingly to
67 * Non-conditional Revision Aware XPath. The specified Module MUST be present in Schema Context otherwise the
68 * operation would fail and return <code>null</code>.
70 * The Revision Aware XPath MUST be specified WITHOUT the conditional statement (i.e. without [cond]) in path,
71 * because in this state the Schema Context is completely unaware of data state and will be not able to properly
72 * resolve XPath. If the XPath contains condition the method will return IllegalArgumentException.
74 * In case that Schema Context or Module or Revision Aware XPath contains <code>null</code> references the method
75 * will throw IllegalArgumentException
77 * If the Revision Aware XPath is correct and desired Data Schema Node is present in Yang module or in depending
78 * module in Schema Context the method will return specified Data Schema Node, otherwise the operation will fail
79 * and method will return <code>null</code>.
81 * @throws IllegalArgumentException
83 * @param context Schema Context
84 * @param module Yang Module
85 * @param nonCondXPath Non Conditional Revision Aware XPath
86 * @return Returns Data Schema Node for specified Schema Context for given Non-conditional Revision Aware XPath,
87 * or <code>null</code> if the DataSchemaNode is not present in Schema Context.
89 public static DataSchemaNode findDataSchemaNode(final SchemaContext context, final Module module,
90 final RevisionAwareXPath nonCondXPath) {
91 if (context == null) {
92 throw new IllegalArgumentException("Schema Context reference cannot be NULL!");
95 throw new IllegalArgumentException("Module reference cannot be NULL!");
97 if (nonCondXPath == null) {
98 throw new IllegalArgumentException("Non Conditional Revision Aware XPath cannot be NULL!");
101 final String strXPath = nonCondXPath.toString();
102 if (strXPath != null) {
103 if (strXPath.contains("[")) {
104 throw new IllegalArgumentException("Revision Aware XPath cannot contains condition!");
106 if (nonCondXPath.isAbsolute()) {
107 final Queue<QName> qnamedPath = xpathToQNamePath(context, module, strXPath);
108 if (qnamedPath != null) {
109 final DataSchemaNode dataNode = findSchemaNodeForGivenPath(context, module, qnamedPath);
118 * Method attempts to find DataSchemaNode inside of provided Schema Context and Yang Module accordingly to
119 * Non-conditional relative Revision Aware XPath. The specified Module MUST be present in Schema Context otherwise
120 * the operation would fail and return <code>null</code>.
122 * The relative Revision Aware XPath MUST be specified WITHOUT the conditional statement (i.e. without [cond]) in
123 * path, because in this state the Schema Context is completely unaware of data state and will be not able to
124 * properly resolve XPath. If the XPath contains condition the method will return IllegalArgumentException.
126 * The Actual Schema Node MUST be specified correctly because from this Schema Node will search starts. If the
127 * Actual Schema Node is not correct the operation will simply fail, because it will be unable to find desired
130 * In case that Schema Context or Module or Actual Schema Node or relative Revision Aware XPath contains
131 * <code>null</code> references the method will throw IllegalArgumentException
133 * If the Revision Aware XPath doesn't have flag <code>isAbsolute == false</code> the method will
134 * throw IllegalArgumentException.
136 * If the relative Revision Aware XPath is correct and desired Data Schema Node is present in Yang module or in
137 * depending module in Schema Context the method will return specified Data Schema Node,
138 * otherwise the operation will fail
139 * and method will return <code>null</code>.
141 * @throws IllegalArgumentException
143 * @param context Schema Context
144 * @param module Yang Module
145 * @param actualSchemaNode Actual Schema Node
146 * @param relativeXPath Relative Non Conditional Revision Aware XPath
147 * @return DataSchemaNode if is present in specified Schema Context for given relative Revision Aware XPath,
148 * otherwise will return <code>null</code>.
150 public static DataSchemaNode findDataSchemaNodeForRelativeXPath(final SchemaContext context, final Module module,
151 final SchemaNode actualSchemaNode, final RevisionAwareXPath relativeXPath) {
152 if (context == null) {
153 throw new IllegalArgumentException("Schema Context reference cannot be NULL!");
155 if (module == null) {
156 throw new IllegalArgumentException("Module reference cannot be NULL!");
158 if (actualSchemaNode == null) {
159 throw new IllegalArgumentException("Actual Schema Node reference cannot be NULL!");
161 if (relativeXPath == null) {
162 throw new IllegalArgumentException("Non Conditional Revision Aware XPath cannot be NULL!");
164 if (relativeXPath.isAbsolute()) {
165 throw new IllegalArgumentException("Revision Aware XPath MUST be relative i.e. MUST contains ../, "
166 + "for non relative Revision Aware XPath use findDataSchemaNode method!");
169 final SchemaPath actualNodePath = actualSchemaNode.getPath();
170 if (actualNodePath != null) {
171 final Queue<QName> qnamePath = resolveRelativeXPath(context, module, relativeXPath, actualNodePath);
173 if (qnamePath != null) {
174 final DataSchemaNode dataNode = findSchemaNodeForGivenPath(context, module, qnamePath);
182 * Retrieve information from Schema Path and returns the module reference to which Schema Node belongs. The
183 * search for correct Module is based on namespace within the last item in Schema Path. If schema context
184 * contains module with namespace specified in last item of Schema Path, then operation will returns Module
185 * reference, otherwise returns <code>null</code>
187 * If Schema Context or Schema Node contains <code>null</code> references the method will throw IllegalArgumentException
189 * @throws IllegalArgumentException
191 * @param context Schema Context
192 * @param schemaPath Schema Path
193 * @return Module reference for given Schema Path if module is present in Schema Context,
194 * otherwise returns <code>null</code>
196 private static Module resolveModuleFromSchemaPath(final SchemaContext context, final SchemaPath schemaPath) {
197 if (context == null) {
198 throw new IllegalArgumentException("Schema Context reference cannot be NULL!");
200 if (schemaPath == null) {
201 throw new IllegalArgumentException("Schema Path reference cannot be NULL");
204 final List<QName> path = schemaPath.getPath();
205 if (!path.isEmpty()) {
206 final QName qname = path.get(path.size() - 1);
208 if ((qname != null) && (qname.getNamespace() != null)) {
209 return context.findModuleByNamespace(qname.getNamespace());
217 * Returns the Yang Module from specified Schema Context in which the TypeDefinition is declared. If the
218 * TypeDefinition si not present in Schema Context then the method will return <code>null</code>
220 * If Schema Context or TypeDefinition contains <code>null</code> references the method will throw IllegalArgumentException
222 * @throws IllegalArgumentException
224 * @param context Schema Context
225 * @param type Type Definition
226 * @return Yang Module in which the TypeDefinition is declared, if is not present, returns <code>null</code>.
228 public static Module findParentModuleForTypeDefinition(final SchemaContext context, final TypeDefinition<?> type) {
229 final SchemaPath schemaPath = type.getPath();
230 if (schemaPath == null) {
231 throw new IllegalArgumentException("Schema Path reference cannot be NULL");
233 final List<QName> qnamedPath = schemaPath.getPath();
234 if (qnamedPath == null || qnamedPath.isEmpty()) {
235 throw new IllegalStateException("Schema Path contains invalid state of path parts."
236 + "The Schema Path MUST contain at least ONE QName which defines namespace and Local name"
240 if (type instanceof ExtendedType) {
241 final QName qname = qnamedPath.get(qnamedPath.size() - 1);
242 if ((qname != null) && (qname.getNamespace() != null)) {
243 return context.findModuleByNamespace(qname.getNamespace());
246 final QName qname = qnamedPath.get(qnamedPath.size() - 2);
247 if ((qname != null) && (qname.getNamespace() != null)) {
248 return context.findModuleByNamespace(qname.getNamespace());
255 * Returns parent Yang Module for specified Schema Context in which Schema Node is declared. If the Schema Node
256 * is not present in Schema Context the operation will return <code>null</code>.
258 * If Schema Context or Schema Node contains <code>null</code> references the method will throw IllegalArgumentException
260 * @throws IllegalArgumentException
262 * @param context Schema Context
263 * @param schemaNode Schema Node
264 * @return Yang Module for specified Schema Context and Schema Node, if Schema Node is NOT present,
265 * the method will returns <code>null</code>
267 public static Module findParentModule(final SchemaContext context, final SchemaNode schemaNode) {
268 if (context == null) {
269 throw new IllegalArgumentException("Schema Context reference cannot be NULL!");
271 if (schemaNode == null) {
272 throw new IllegalArgumentException("Schema Node cannot be NULL!");
275 final SchemaPath schemaPath = schemaNode.getPath();
276 if (schemaPath == null) {
277 throw new IllegalStateException("Schema Path for Schema Node is not "
278 + "set properly (Schema Path is NULL)");
280 final List<QName> qnamedPath = schemaPath.getPath();
281 if (qnamedPath == null || qnamedPath.isEmpty()) {
282 throw new IllegalStateException("Schema Path contains invalid state of path parts."
283 + "The Schema Path MUST contain at least ONE QName which defines namespace and Local name"
286 final QName qname = qnamedPath.get(qnamedPath.size() - 1);
287 return context.findModuleByNamespace(qname.getNamespace());
291 * Method will attempt to find DataSchemaNode from specified Module and Queue of QNames through the Schema
292 * Context. The QNamed path could be defined across multiple modules in Schema Context so the method is called
293 * recursively. If the QNamed path contains QNames that are not part of any Module or Schema Context Path the
294 * operation will fail and returns <code>null</code>
296 * If Schema Context, Module or Queue of QNames refers to <code>null</code> values,
297 * the method will throws IllegalArgumentException
299 * @throws IllegalArgumentException
301 * @param context Schema Context
302 * @param module Yang Module
303 * @param qnamedPath Queue of QNames
304 * @return DataSchemaNode if is present in Module(s) for specified Schema Context and given QNamed Path,
305 * otherwise will return <code>null</code>.
307 private static DataSchemaNode findSchemaNodeForGivenPath(final SchemaContext context, final Module module,
308 final Queue<QName> qnamedPath) {
309 if (context == null) {
310 throw new IllegalArgumentException("Schema Context reference cannot be NULL!");
312 if (module == null) {
313 throw new IllegalArgumentException("Module reference cannot be NULL!");
315 if (module.getNamespace() == null) {
316 throw new IllegalArgumentException("Namespace for Module cannot contains NULL reference!");
318 if (qnamedPath == null || qnamedPath.isEmpty()) {
319 throw new IllegalStateException("Schema Path contains invalid state of path parts."
320 + "The Schema Path MUST contain at least ONE QName which defines namespace and Local name"
324 DataNodeContainer nextNode = module;
325 final URI moduleNamespace = module.getNamespace();
327 QName childNodeQName;
328 DataSchemaNode schemaNode = null;
329 while ((nextNode != null) && !qnamedPath.isEmpty()) {
330 childNodeQName = qnamedPath.peek();
331 if (childNodeQName != null) {
332 final URI childNodeNamespace = childNodeQName.getNamespace();
334 schemaNode = nextNode.getDataChildByName(childNodeQName);
335 if (schemaNode != null) {
336 if (schemaNode instanceof ContainerSchemaNode) {
337 nextNode = (ContainerSchemaNode) schemaNode;
338 } else if (schemaNode instanceof ListSchemaNode) {
339 nextNode = (ListSchemaNode) schemaNode;
340 } else if (schemaNode instanceof ChoiceNode) {
341 final ChoiceNode choice = (ChoiceNode) schemaNode;
343 if (!qnamedPath.isEmpty()) {
344 childNodeQName = qnamedPath.peek();
345 nextNode = choice.getCaseNodeByName(childNodeQName);
346 schemaNode = (DataSchemaNode) nextNode;
351 } else if (!childNodeNamespace.equals(moduleNamespace)) {
352 final Module nextModule = context.findModuleByNamespace(childNodeNamespace);
353 schemaNode = findSchemaNodeForGivenPath(context, nextModule, qnamedPath);
363 * Transforms string representation of XPath to Queue of QNames. The XPath is split by "/" and for each part of
364 * XPath is assigned correct module in Schema Path.
366 * If Schema Context, Parent Module or XPath string contains <code>null</code> values,
367 * the method will throws IllegalArgumentException
369 * @throws IllegalArgumentException
371 * @param context Schema Context
372 * @param parentModule Parent Module
373 * @param xpath XPath String
376 private static Queue<QName> xpathToQNamePath(final SchemaContext context, final Module parentModule,
377 final String xpath) {
378 if (context == null) {
379 throw new IllegalArgumentException("Schema Context reference cannot be NULL!");
381 if (parentModule == null) {
382 throw new IllegalArgumentException("Parent Module reference cannot be NULL!");
385 throw new IllegalArgumentException("XPath string reference cannot be NULL!");
388 final Queue<QName> path = new LinkedList<>();
389 final String[] prefixedPath = xpath.split("/");
390 for (int i = 0; i < prefixedPath.length; ++i) {
391 if (!prefixedPath[i].isEmpty()) {
392 path.add(stringPathPartToQName(context, parentModule, prefixedPath[i]));
399 * Transforms part of Prefixed Path as java String to QName.
401 * If the string contains module prefix separated by ":" (i.e. mod:container) this module is provided from from
402 * Parent Module list of imports. If the Prefixed module is present in Schema Context the QName can be
405 * If the Prefixed Path Part does not contains prefix the Parent's Module namespace is taken for construction of
408 * If Schema Context, Parent Module or Prefixed Path Part refers to <code>null</code> the method will throw
409 * IllegalArgumentException
411 * @throws IllegalArgumentException
413 * @param context Schema Context
414 * @param parentModule Parent Module
415 * @param prefixedPathPart Prefixed Path Part string
416 * @return QName from prefixed Path Part String.
418 private static QName stringPathPartToQName(final SchemaContext context, final Module parentModule,
419 final String prefixedPathPart) {
420 if (context == null) {
421 throw new IllegalArgumentException("Schema Context reference cannot be NULL!");
423 if (parentModule == null) {
424 throw new IllegalArgumentException("Parent Module reference cannot be NULL!");
426 if (prefixedPathPart == null) {
427 throw new IllegalArgumentException("Prefixed Path Part cannot be NULL!");
430 if (prefixedPathPart.contains(":")) {
431 final String[] prefixedName = prefixedPathPart.split(":");
432 final Module module = resolveModuleForPrefix(context, parentModule, prefixedName[0]);
433 if (module != null) {
434 return new QName(module.getNamespace(), module.getRevision(), prefixedName[1]);
437 return new QName(parentModule.getNamespace(), parentModule.getRevision(), prefixedPathPart);
443 * Method will attempt to resolve and provide Module reference for specified module prefix. Each Yang module
444 * could contains multiple imports which MUST be associated with corresponding module prefix. The method simply
445 * looks into module imports and returns the module that is bounded with specified prefix. If the prefix is not
446 * present in module or the prefixed module is not present in specified Schema Context,
447 * the method will return <code>null</code>.
449 * If String prefix is the same as prefix of the specified Module the reference to this module is returned.
451 * If Schema Context, Module or Prefix are referring to <code>null</code> the method will return
452 * IllegalArgumentException
454 * @throws IllegalArgumentException
456 * @param context Schema Context
457 * @param module Yang Module
458 * @param prefix Module Prefix
459 * @return Module for given prefix in specified Schema Context if is present, otherwise returns <code>null</code>
461 private static Module resolveModuleForPrefix(final SchemaContext context, final Module module, final String prefix) {
462 if (context == null) {
463 throw new IllegalArgumentException("Schema Context reference cannot be NULL!");
465 if (module == null) {
466 throw new IllegalArgumentException("Module reference cannot be NULL!");
468 if (prefix == null) {
469 throw new IllegalArgumentException("Prefix string cannot be NULL!");
472 if (prefix.equals(module.getPrefix())) {
476 final Set<ModuleImport> imports = module.getImports();
477 for (final ModuleImport mi : imports) {
478 if (prefix.equals(mi.getPrefix())) {
479 return context.findModuleByName(mi.getModuleName(), mi.getRevision());
486 * @throws IllegalArgumentException
488 * @param context Schema Context
489 * @param module Yang Module
490 * @param relativeXPath Non conditional Revision Aware Relative XPath
491 * @param leafrefSchemaPath Schema Path for Leafref
494 private static Queue<QName> resolveRelativeXPath(final SchemaContext context, final Module module,
495 final RevisionAwareXPath relativeXPath, final SchemaPath leafrefSchemaPath) {
496 final Queue<QName> absolutePath = new LinkedList<>();
497 if (context == null) {
498 throw new IllegalArgumentException("Schema Context reference cannot be NULL!");
500 if (module == null) {
501 throw new IllegalArgumentException("Module reference cannot be NULL!");
503 if (relativeXPath == null) {
504 throw new IllegalArgumentException("Non Conditional Revision Aware XPath cannot be NULL!");
506 if (relativeXPath.isAbsolute()) {
507 throw new IllegalArgumentException("Revision Aware XPath MUST be relative i.e. MUST contains ../, "
508 + "for non relative Revision Aware XPath use findDataSchemaNode method!");
510 if (leafrefSchemaPath == null) {
511 throw new IllegalArgumentException("Schema Path reference for Leafref cannot be NULL!");
514 final String strXPath = relativeXPath.toString();
515 if (strXPath != null) {
516 final String[] xpaths = strXPath.split("/");
517 if (xpaths != null) {
519 while (xpaths[colCount].contains("..")) {
522 final List<QName> path = leafrefSchemaPath.getPath();
524 int lenght = path.size() - colCount - 1;
525 for (int i = 0; i < lenght; ++i) {
526 absolutePath.add(path.get(i));
528 for (int i = colCount; i < xpaths.length; ++i) {
529 absolutePath.add(stringPathPartToQName(context, module, xpaths[i]));