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.model.util;
11 import java.util.LinkedList;
12 import java.util.List;
13 import java.util.Queue;
16 import org.opendaylight.yangtools.yang.common.QName;
17 import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
18 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
19 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
20 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
21 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
22 import org.opendaylight.yangtools.yang.model.api.Module;
23 import org.opendaylight.yangtools.yang.model.api.ModuleImport;
24 import org.opendaylight.yangtools.yang.model.api.RevisionAwareXPath;
25 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
26 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
27 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
28 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
31 * The Schema Context Util contains support methods for searching through Schema Context modules for specified schema
32 * nodes via Schema Path or Revision Aware XPath. The Schema Context Util is designed as mixin,
33 * so it is not instantiable.
35 * @author Lukas Sedlak <lsedlak@cisco.com>
37 public final class SchemaContextUtil {
39 private SchemaContextUtil() {
43 * Method attempts to find DataSchemaNode in Schema Context via specified Schema Path. The returned
44 * DataSchemaNode from method will be the node at the end of the SchemaPath. If the DataSchemaNode is not present
45 * in the Schema Context the method will return <code>null</code>.
47 * In case that Schema Context or Schema Path are not specified correctly (i.e. contains <code>null</code>
48 * values) the method will return IllegalArgumentException.
50 * @throws IllegalArgumentException
55 * Schema Path to search for
56 * @return DataSchemaNode from the end of the Schema Path or
57 * <code>null</code> if the Node is not present.
59 public static DataSchemaNode findDataSchemaNode(final SchemaContext context, final SchemaPath schemaPath) {
60 if (context == null) {
61 throw new IllegalArgumentException("Schema Context reference cannot be NULL!");
63 if (schemaPath == null) {
64 throw new IllegalArgumentException("Schema Path reference cannot be NULL");
67 final Module module = resolveModuleFromSchemaPath(context, schemaPath);
68 final Queue<QName> prefixedPath = new LinkedList<>(schemaPath.getPath());
70 if ((module != null) && (prefixedPath != null)) {
71 return findSchemaNodeForGivenPath(context, module, prefixedPath);
77 * Method attempts to find DataSchemaNode inside of provided Schema Context and Yang Module accordingly to
78 * Non-conditional Revision Aware XPath. The specified Module MUST be present in Schema Context otherwise the
79 * operation would fail and return <code>null</code>.
81 * The Revision Aware XPath MUST be specified WITHOUT the conditional statement (i.e. without [cond]) in path,
82 * because in this state the Schema Context is completely unaware of data state and will be not able to properly
83 * resolve XPath. If the XPath contains condition the method will return IllegalArgumentException.
85 * In case that Schema Context or Module or Revision Aware XPath contains <code>null</code> references the method
86 * will throw IllegalArgumentException
88 * If the Revision Aware XPath is correct and desired Data Schema Node is present in Yang module or in depending
89 * module in Schema Context the method will return specified Data Schema Node, otherwise the operation will fail
90 * and method will return <code>null</code>.
92 * @throws IllegalArgumentException
94 * @param context Schema Context
95 * @param module Yang Module
96 * @param nonCondXPath Non Conditional Revision Aware XPath
97 * @return Returns Data Schema Node for specified Schema Context for given Non-conditional Revision Aware XPath,
98 * or <code>null</code> if the DataSchemaNode is not present in Schema Context.
100 public static DataSchemaNode findDataSchemaNode(final SchemaContext context, final Module module,
101 final RevisionAwareXPath nonCondXPath) {
102 if (context == null) {
103 throw new IllegalArgumentException("Schema Context reference cannot be NULL!");
105 if (module == null) {
106 throw new IllegalArgumentException("Module reference cannot be NULL!");
108 if (nonCondXPath == null) {
109 throw new IllegalArgumentException("Non Conditional Revision Aware XPath cannot be NULL!");
112 final String strXPath = nonCondXPath.toString();
113 if (strXPath != null) {
114 if (strXPath.contains("[")) {
115 throw new IllegalArgumentException("Revision Aware XPath cannot contains condition!");
117 if (nonCondXPath.isAbsolute()) {
118 final Queue<QName> qnamedPath = xpathToQNamePath(context, module, strXPath);
119 if (qnamedPath != null) {
120 final DataSchemaNode dataNode = findSchemaNodeForGivenPath(context, module, qnamedPath);
129 * Method attempts to find DataSchemaNode inside of provided Schema Context and Yang Module accordingly to
130 * Non-conditional relative Revision Aware XPath. The specified Module MUST be present in Schema Context otherwise
131 * the operation would fail and return <code>null</code>.
133 * The relative Revision Aware XPath MUST be specified WITHOUT the conditional statement (i.e. without [cond]) in
134 * path, because in this state the Schema Context is completely unaware of data state and will be not able to
135 * properly resolve XPath. If the XPath contains condition the method will return IllegalArgumentException.
137 * The Actual Schema Node MUST be specified correctly because from this Schema Node will search starts. If the
138 * Actual Schema Node is not correct the operation will simply fail, because it will be unable to find desired
141 * In case that Schema Context or Module or Actual Schema Node or relative Revision Aware XPath contains
142 * <code>null</code> references the method will throw IllegalArgumentException
144 * If the Revision Aware XPath doesn't have flag <code>isAbsolute == false</code> the method will
145 * throw IllegalArgumentException.
147 * If the relative Revision Aware XPath is correct and desired Data Schema Node is present in Yang module or in
148 * depending module in Schema Context the method will return specified Data Schema Node,
149 * otherwise the operation will fail
150 * and method will return <code>null</code>.
152 * @throws IllegalArgumentException
154 * @param context Schema Context
155 * @param module Yang Module
156 * @param actualSchemaNode Actual Schema Node
157 * @param relativeXPath Relative Non Conditional Revision Aware XPath
158 * @return DataSchemaNode if is present in specified Schema Context for given relative Revision Aware XPath,
159 * otherwise will return <code>null</code>.
161 public static DataSchemaNode findDataSchemaNodeForRelativeXPath(final SchemaContext context, final Module module,
162 final SchemaNode actualSchemaNode, final RevisionAwareXPath relativeXPath) {
163 if (context == null) {
164 throw new IllegalArgumentException("Schema Context reference cannot be NULL!");
166 if (module == null) {
167 throw new IllegalArgumentException("Module reference cannot be NULL!");
169 if (actualSchemaNode == null) {
170 throw new IllegalArgumentException("Actual Schema Node reference cannot be NULL!");
172 if (relativeXPath == null) {
173 throw new IllegalArgumentException("Non Conditional Revision Aware XPath cannot be NULL!");
175 if (relativeXPath.isAbsolute()) {
176 throw new IllegalArgumentException("Revision Aware XPath MUST be relative i.e. MUST contains ../, "
177 + "for non relative Revision Aware XPath use findDataSchemaNode method!");
180 final SchemaPath actualNodePath = actualSchemaNode.getPath();
181 if (actualNodePath != null) {
182 final Queue<QName> qnamePath = resolveRelativeXPath(context, module, relativeXPath, actualNodePath);
184 if (qnamePath != null) {
185 final DataSchemaNode dataNode = findSchemaNodeForGivenPath(context, module, qnamePath);
193 * Retrieve information from Schema Path and returns the module reference to which Schema Node belongs. The
194 * search for correct Module is based on namespace within the last item in Schema Path. If schema context
195 * contains module with namespace specified in last item of Schema Path, then operation will returns Module
196 * reference, otherwise returns <code>null</code>
198 * If Schema Context or Schema Node contains <code>null</code> references the method will throw IllegalArgumentException
200 * @throws IllegalArgumentException
202 * @param context Schema Context
203 * @param schemaPath Schema Path
204 * @return Module reference for given Schema Path if module is present in Schema Context,
205 * otherwise returns <code>null</code>
207 private static Module resolveModuleFromSchemaPath(final SchemaContext context, final SchemaPath schemaPath) {
208 if (context == null) {
209 throw new IllegalArgumentException("Schema Context reference cannot be NULL!");
211 if (schemaPath == null) {
212 throw new IllegalArgumentException("Schema Path reference cannot be NULL");
215 final List<QName> path = schemaPath.getPath();
216 if (!path.isEmpty()) {
217 final QName qname = path.get(path.size() - 1);
219 if ((qname != null) && (qname.getNamespace() != null)) {
220 return context.findModuleByNamespace(qname.getNamespace());
228 * Returns the Yang Module from specified Schema Context in which the TypeDefinition is declared. If the
229 * TypeDefinition si not present in Schema Context then the method will return <code>null</code>
231 * If Schema Context or TypeDefinition contains <code>null</code> references the method will throw IllegalArgumentException
233 * @throws IllegalArgumentException
235 * @param context Schema Context
236 * @param type Type Definition
237 * @return Yang Module in which the TypeDefinition is declared, if is not present, returns <code>null</code>.
239 public static Module findParentModuleForTypeDefinition(final SchemaContext context, final TypeDefinition<?> type) {
240 final SchemaPath schemaPath = type.getPath();
241 if (schemaPath == null) {
242 throw new IllegalArgumentException("Schema Path reference cannot be NULL");
244 final List<QName> qnamedPath = schemaPath.getPath();
245 if (qnamedPath == null || qnamedPath.isEmpty()) {
246 throw new IllegalStateException("Schema Path contains invalid state of path parts."
247 + "The Schema Path MUST contain at least ONE QName which defines namespace and Local name"
251 if (type instanceof ExtendedType) {
252 final QName qname = qnamedPath.get(qnamedPath.size() - 1);
253 if ((qname != null) && (qname.getNamespace() != null)) {
254 return context.findModuleByNamespace(qname.getNamespace());
257 final QName qname = qnamedPath.get(qnamedPath.size() - 2);
258 if ((qname != null) && (qname.getNamespace() != null)) {
259 return context.findModuleByNamespace(qname.getNamespace());
266 * Returns parent Yang Module for specified Schema Context in which Schema Node is declared. If the Schema Node
267 * is not present in Schema Context the operation will return <code>null</code>.
269 * If Schema Context or Schema Node contains <code>null</code> references the method will throw IllegalArgumentException
271 * @throws IllegalArgumentException
273 * @param context Schema Context
274 * @param schemaNode Schema Node
275 * @return Yang Module for specified Schema Context and Schema Node, if Schema Node is NOT present,
276 * the method will returns <code>null</code>
278 public static Module findParentModule(final SchemaContext context, final SchemaNode schemaNode) {
279 if (context == null) {
280 throw new IllegalArgumentException("Schema Context reference cannot be NULL!");
282 if (schemaNode == null) {
283 throw new IllegalArgumentException("Schema Node cannot be NULL!");
286 final SchemaPath schemaPath = schemaNode.getPath();
287 if (schemaPath == null) {
288 throw new IllegalStateException("Schema Path for Schema Node is not "
289 + "set properly (Schema Path is NULL)");
291 final List<QName> qnamedPath = schemaPath.getPath();
292 if (qnamedPath == null || qnamedPath.isEmpty()) {
293 throw new IllegalStateException("Schema Path contains invalid state of path parts."
294 + "The Schema Path MUST contain at least ONE QName which defines namespace and Local name"
297 final QName qname = qnamedPath.get(qnamedPath.size() - 1);
298 return context.findModuleByNamespace(qname.getNamespace());
302 * Method will attempt to find DataSchemaNode from specified Module and Queue of QNames through the Schema
303 * Context. The QNamed path could be defined across multiple modules in Schema Context so the method is called
304 * recursively. If the QNamed path contains QNames that are not part of any Module or Schema Context Path the
305 * operation will fail and returns <code>null</code>
307 * If Schema Context, Module or Queue of QNames refers to <code>null</code> values,
308 * the method will throws IllegalArgumentException
310 * @throws IllegalArgumentException
312 * @param context Schema Context
313 * @param module Yang Module
314 * @param qnamedPath Queue of QNames
315 * @return DataSchemaNode if is present in Module(s) for specified Schema Context and given QNamed Path,
316 * otherwise will return <code>null</code>.
318 private static DataSchemaNode findSchemaNodeForGivenPath(final SchemaContext context, final Module module,
319 final Queue<QName> qnamedPath) {
320 if (context == null) {
321 throw new IllegalArgumentException("Schema Context reference cannot be NULL!");
323 if (module == null) {
324 throw new IllegalArgumentException("Module reference cannot be NULL!");
326 if (module.getNamespace() == null) {
327 throw new IllegalArgumentException("Namespace for Module cannot contains NULL reference!");
329 if (qnamedPath == null || qnamedPath.isEmpty()) {
330 throw new IllegalStateException("Schema Path contains invalid state of path parts."
331 + "The Schema Path MUST contain at least ONE QName which defines namespace and Local name"
335 DataNodeContainer nextNode = module;
336 final URI moduleNamespace = module.getNamespace();
338 QName childNodeQName;
339 DataSchemaNode schemaNode = null;
340 while ((nextNode != null) && !qnamedPath.isEmpty()) {
341 childNodeQName = qnamedPath.peek();
342 if (childNodeQName != null) {
343 final URI childNodeNamespace = childNodeQName.getNamespace();
345 schemaNode = nextNode.getDataChildByName(childNodeQName);
346 if (schemaNode != null) {
347 if (schemaNode instanceof ContainerSchemaNode) {
348 nextNode = (ContainerSchemaNode) schemaNode;
349 } else if (schemaNode instanceof ListSchemaNode) {
350 nextNode = (ListSchemaNode) schemaNode;
351 } else if (schemaNode instanceof ChoiceNode) {
352 final ChoiceNode choice = (ChoiceNode) schemaNode;
354 if (!qnamedPath.isEmpty()) {
355 childNodeQName = qnamedPath.peek();
356 nextNode = choice.getCaseNodeByName(childNodeQName);
357 schemaNode = (DataSchemaNode) nextNode;
362 } else if (!childNodeNamespace.equals(moduleNamespace)) {
363 final Module nextModule = context.findModuleByNamespace(childNodeNamespace);
364 schemaNode = findSchemaNodeForGivenPath(context, nextModule, qnamedPath);
374 * Transforms string representation of XPath to Queue of QNames. The XPath is split by "/" and for each part of
375 * XPath is assigned correct module in Schema Path.
377 * If Schema Context, Parent Module or XPath string contains <code>null</code> values,
378 * the method will throws IllegalArgumentException
380 * @throws IllegalArgumentException
382 * @param context Schema Context
383 * @param parentModule Parent Module
384 * @param xpath XPath String
387 private static Queue<QName> xpathToQNamePath(final SchemaContext context, final Module parentModule,
388 final String xpath) {
389 if (context == null) {
390 throw new IllegalArgumentException("Schema Context reference cannot be NULL!");
392 if (parentModule == null) {
393 throw new IllegalArgumentException("Parent Module reference cannot be NULL!");
396 throw new IllegalArgumentException("XPath string reference cannot be NULL!");
399 final Queue<QName> path = new LinkedList<>();
400 final String[] prefixedPath = xpath.split("/");
401 for (int i = 0; i < prefixedPath.length; ++i) {
402 if (!prefixedPath[i].isEmpty()) {
403 path.add(stringPathPartToQName(context, parentModule, prefixedPath[i]));
410 * Transforms part of Prefixed Path as java String to QName.
412 * If the string contains module prefix separated by ":" (i.e. mod:container) this module is provided from from
413 * Parent Module list of imports. If the Prefixed module is present in Schema Context the QName can be
416 * If the Prefixed Path Part does not contains prefix the Parent's Module namespace is taken for construction of
419 * If Schema Context, Parent Module or Prefixed Path Part refers to <code>null</code> the method will throw
420 * IllegalArgumentException
422 * @throws IllegalArgumentException
424 * @param context Schema Context
425 * @param parentModule Parent Module
426 * @param prefixedPathPart Prefixed Path Part string
427 * @return QName from prefixed Path Part String.
429 private static QName stringPathPartToQName(final SchemaContext context, final Module parentModule,
430 final String prefixedPathPart) {
431 if (context == null) {
432 throw new IllegalArgumentException("Schema Context reference cannot be NULL!");
434 if (parentModule == null) {
435 throw new IllegalArgumentException("Parent Module reference cannot be NULL!");
437 if (prefixedPathPart == null) {
438 throw new IllegalArgumentException("Prefixed Path Part cannot be NULL!");
441 if (prefixedPathPart.contains(":")) {
442 final String[] prefixedName = prefixedPathPart.split(":");
443 final Module module = resolveModuleForPrefix(context, parentModule, prefixedName[0]);
444 if (module != null) {
445 return new QName(module.getNamespace(), module.getRevision(), prefixedName[1]);
448 return new QName(parentModule.getNamespace(), parentModule.getRevision(), prefixedPathPart);
454 * Method will attempt to resolve and provide Module reference for specified module prefix. Each Yang module
455 * could contains multiple imports which MUST be associated with corresponding module prefix. The method simply
456 * looks into module imports and returns the module that is bounded with specified prefix. If the prefix is not
457 * present in module or the prefixed module is not present in specified Schema Context,
458 * the method will return <code>null</code>.
460 * If String prefix is the same as prefix of the specified Module the reference to this module is returned.
462 * If Schema Context, Module or Prefix are referring to <code>null</code> the method will return
463 * IllegalArgumentException
465 * @throws IllegalArgumentException
467 * @param context Schema Context
468 * @param module Yang Module
469 * @param prefix Module Prefix
470 * @return Module for given prefix in specified Schema Context if is present, otherwise returns <code>null</code>
472 private static Module resolveModuleForPrefix(final SchemaContext context, final Module module, final String prefix) {
473 if (context == null) {
474 throw new IllegalArgumentException("Schema Context reference cannot be NULL!");
476 if (module == null) {
477 throw new IllegalArgumentException("Module reference cannot be NULL!");
479 if (prefix == null) {
480 throw new IllegalArgumentException("Prefix string cannot be NULL!");
483 if (prefix.equals(module.getPrefix())) {
487 final Set<ModuleImport> imports = module.getImports();
488 for (final ModuleImport mi : imports) {
489 if (prefix.equals(mi.getPrefix())) {
490 return context.findModuleByName(mi.getModuleName(), mi.getRevision());
497 * @throws IllegalArgumentException
499 * @param context Schema Context
500 * @param module Yang Module
501 * @param relativeXPath Non conditional Revision Aware Relative XPath
502 * @param leafrefSchemaPath Schema Path for Leafref
505 private static Queue<QName> resolveRelativeXPath(final SchemaContext context, final Module module,
506 final RevisionAwareXPath relativeXPath, final SchemaPath leafrefSchemaPath) {
507 final Queue<QName> absolutePath = new LinkedList<>();
508 if (context == null) {
509 throw new IllegalArgumentException("Schema Context reference cannot be NULL!");
511 if (module == null) {
512 throw new IllegalArgumentException("Module reference cannot be NULL!");
514 if (relativeXPath == null) {
515 throw new IllegalArgumentException("Non Conditional Revision Aware XPath cannot be NULL!");
517 if (relativeXPath.isAbsolute()) {
518 throw new IllegalArgumentException("Revision Aware XPath MUST be relative i.e. MUST contains ../, "
519 + "for non relative Revision Aware XPath use findDataSchemaNode method!");
521 if (leafrefSchemaPath == null) {
522 throw new IllegalArgumentException("Schema Path reference for Leafref cannot be NULL!");
525 final String strXPath = relativeXPath.toString();
526 if (strXPath != null) {
527 final String[] xpaths = strXPath.split("/");
528 if (xpaths != null) {
530 while (xpaths[colCount].contains("..")) {
533 final List<QName> path = leafrefSchemaPath.getPath();
535 int lenght = path.size() - colCount - 1;
536 for (int i = 0; i < lenght; ++i) {
537 absolutePath.add(path.get(i));
539 for (int i = colCount; i < xpaths.length; ++i) {
540 absolutePath.add(stringPathPartToQName(context, module, xpaths[i]));