2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2018 Huawei Technologies Co., Ltd. All rights reserved.
6 * ================================================================================
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 * ============LICENSE_END=========================================================
21 package org.onap.ccsdk.sli.plugins.yangserializers.pnserializer;
23 import static com.google.common.base.Preconditions.checkArgument;
24 import static java.lang.String.format;
25 import static java.util.regex.Pattern.quote;
26 import static org.opendaylight.restconf.nb.rfc8040.utils.parser.ParserIdentifier.toInstanceIdentifier;
27 import java.util.Collection;
28 import java.util.Deque;
29 import java.util.Iterator;
30 import java.util.Optional;
31 import org.onap.ccsdk.sli.core.sli.SvcLogicException;
32 import org.opendaylight.restconf.common.context.InstanceIdentifierContext;
33 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
34 import org.opendaylight.yangtools.yang.common.QName;
35 import org.opendaylight.yangtools.yang.common.Revision;
36 import org.opendaylight.yangtools.yang.data.impl.schema.SchemaUtils;
37 import org.opendaylight.yangtools.yang.data.util.ParserStreamUtils;
38 import org.opendaylight.yangtools.yang.data.util.codec.IdentityCodecUtil;
39 import org.opendaylight.yangtools.yang.model.api.AnyxmlSchemaNode;
40 import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
41 import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
42 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
43 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
44 import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
45 import org.opendaylight.yangtools.yang.model.api.Module;
46 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
47 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
48 import org.slf4j.Logger;
49 import org.slf4j.LoggerFactory;
52 * Represents utilities for properties node tree.
54 public final class MdsalPropertiesNodeUtils {
56 static final String COLON = ":";
58 static final String UNDERSCORE = "_";
60 static final String SLASH = "/";
62 static final String DOT_REGEX = "\\.";
64 private static final String INFO_MSG = "The %s formed is currently not" +
67 private static final String EXC_MSG = "Unable to form a formatted path";
70 * Logger for the Mdsal properties util class.
72 private static final Logger log = LoggerFactory.getLogger(
73 MdsalPropertiesNodeUtils.class);
75 private MdsalPropertiesNodeUtils() {
79 * Returns the index from multi instance property name.
81 * @param name name of the property
82 * @return index from multi instance property name
84 public static String getIndex(String name) {
85 return name.substring(name.indexOf("[") + 1,
90 * Returns the multi instance property name.
92 * @param name name of the property
93 * @return the multi instance property name
95 public static String getListName(String name) {
96 String[] s = name.split("\\[");
101 * Returns true if property is multi instance.
103 * @param name name of the property
104 * @return true if property is multi instance
106 public static boolean isListEntry(String name) {
107 String s[] = name.split("\\[");
112 * Returns name of the property after pruning namespace and
113 * index if the property is multi instance.
115 * @param name name of the property
116 * @return name of the property
118 static String resolveName(String name) {
119 String localName = getListName(name);
120 final int lastIndexOfColon = localName.lastIndexOf(":");
121 if (lastIndexOfColon != -1) {
122 localName = localName.substring(lastIndexOfColon + 1);
128 * Returns name of the property after pruning namespace and index if the
129 * property is multi instance by knowing the module name from namespace.
131 * @param ns namespace
132 * @param name name of the node
133 * @return resolved name
135 static String resolveName(Namespace ns, String name) {
136 String localName = getListName(name);
137 String modName = ns.moduleName();
138 if ((localName.contains(COLON) || localName.contains(UNDERSCORE))
139 && localName.startsWith(modName)) {
140 localName = localName.substring(modName.length()+1);
146 * Adds current node to parent's augmentation map.
148 * @param augSchema augment schema
149 * @param parent parent property node
150 * @param curNode current property node
152 public static void addToAugmentations(AugmentationSchemaNode augSchema,
153 PropertiesNode parent,
154 PropertiesNode curNode) {
155 Collection<PropertiesNode> childsFromAugmentation = parent
156 .augmentations().get(augSchema);
157 if (!childsFromAugmentation.isEmpty()) {
158 for (PropertiesNode pNode : childsFromAugmentation) {
159 if (pNode.name().equals(curNode.name())) {
164 parent.augmentations().put(augSchema, curNode);
169 * Returns augmented properties node if it is already
170 * added in properties tree.
172 * @param augSchema augmented schema node
173 * @param parent parent properties node
174 * @param name name of the properties
175 * @return augmented properties node if it is already added
177 public static PropertiesNode getAugmentationNode(
178 AugmentationSchemaNode augSchema,
179 PropertiesNode parent, String name) {
180 if (augSchema == null) {
184 Collection<PropertiesNode> childsFromAugmentation = parent
185 .augmentations().get(augSchema);
186 if (!childsFromAugmentation.isEmpty()) {
187 for (PropertiesNode pNode : childsFromAugmentation) {
188 if (pNode.name().equals(name)) {
198 * Creates uri with specified name and namespace.
200 * @param parent parent properties node
201 * @param name name of the node
202 * @param ns namespace of the node
203 * @return uri with specified name and namespace
205 public static String getUri(PropertiesNode parent, String name,
208 if (!(parent.namespace().moduleNs().equals(ns.moduleNs()))) {
209 uri = ns.moduleName() + ":" + name;
211 return parent.uri() + "." + uri;
215 * Creates new properties with specified parameters.
217 * @param name name of the properties node
218 * @param namespace namespace of the properties node
219 * @param uri uri of the properties node
220 * @param parent parent node
221 * @param appInfo application info
222 * @param type node type
223 * @return new properties node
224 * @throws SvcLogicException exception while creating properties node
226 public static PropertiesNode createNode(String name, Namespace namespace,
227 String uri, PropertiesNode parent,
228 Object appInfo, NodeType type)
229 throws SvcLogicException {
231 case SINGLE_INSTANCE_NODE:
232 return new SingleInstanceNode(name, namespace, uri, parent, appInfo, type);
233 case MULTI_INSTANCE_HOLDER_NODE:
234 return new ListHolderNode(name, namespace, uri, parent, appInfo, type);
235 case MULTI_INSTANCE_LEAF_HOLDER_NODE:
236 return new LeafListHolderNode(name, namespace, uri, parent, appInfo, type);
238 throw new SvcLogicException("Invalid node type " + type);
243 * Returns true if namespace is same as parent's namespace.
245 * @param parent parent property node
246 * @param curNode current property node
247 * @return true if namespace is same as parent namespace
249 public static boolean isNamespaceAsParent(PropertiesNode parent,
250 PropertiesNode curNode) {
251 return parent.namespace().moduleNs().equals(curNode.namespace().moduleNs());
255 * Returns the schema path holder with a formatted url and the instance
256 * identifier context from a given uri or the parameters from svc logic
259 * @param uri unformatted uri or parameter
260 * @param context schema context
261 * @return schema path holder
263 public static SchemaPathHolder getProcessedPath(String uri,
264 EffectiveModelContext context) {
266 String uri1 = uri.replaceAll(UNDERSCORE, COLON);
268 InstanceIdentifierContext<?> id = toInstanceIdentifier(
269 uri1, context, Optional.ofNullable(null));
270 return new SchemaPathHolder(id, uri1);
271 } catch (IllegalArgumentException | RestconfDocumentedException
272 | NullPointerException e) {
273 log.info("Exception while converting uri to instance identifier" +
274 " context. Process each node in uri to get instance identifier" +
276 return processNodesAndAppendPath(uri, context);
281 * Processes the nodes in the given uri and finds instance identifier
282 * context till it reaches the last node in uri. If its not able to find
283 * schema for the path, it appends the suffix part and puts it back in
286 * @param uri uri with underscore
287 * @param context schema context
288 * @return schema and path holder
290 private static SchemaPathHolder processNodesAndAppendPath(String uri,
291 EffectiveModelContext context) {
294 SchemaPathHolder id = new SchemaPathHolder(null, "");
295 String[] uriParts = uri.split(SLASH);
297 if (uri.contains(UNDERSCORE)) {
298 sec = uri.substring(uriParts[0].length()+1);
300 for (int i = 0; i<uriParts.length; i++) {
303 id = processIdentifier(uriParts[i], context, actPath);
304 } catch (IllegalArgumentException e) {
305 log.info(format(EXC_MSG, e));
306 id.setUri(actPath+ uriParts[i] + sec);
310 actPath = actPath + id.getUri() + SLASH;
311 if (sec.startsWith(SLASH)) {
312 sec = sec.replaceFirst(SLASH, "");
314 if (i+1 < uriParts.length) {
315 sec = sec.replaceFirst(quote(uriParts[i + 1]), "");
318 id.setUri(actPath.substring(0,actPath.length() - 1));
323 * Processes the schema and path holder for a given node in the path. It
324 * figures if the path is valid by replacing underscore in the node
325 * consecutively, till it finds the proper schema for the node.
327 * @param node node in the path
328 * @param context schema context
329 * @param prefix prefix for the node in the path
330 * @return schema and path holder
332 private static SchemaPathHolder processIdentifier(String node, EffectiveModelContext context, String prefix) {
334 String[] values = node.split(UNDERSCORE);
335 String val = values[0];
336 StringBuilder firstHalf = new StringBuilder();
337 String secondHalf = "";
338 if (node.contains(UNDERSCORE)) {
339 secondHalf = node.substring(values[0].length()+1);
341 InstanceIdentifierContext<?> id;
342 for (int i = 0; i< values.length-1; i++) {
344 val = firstHalf + val + COLON + secondHalf;
346 id = toInstanceIdentifier(prefix + val, context, null);
347 return new SchemaPathHolder(id, val);
348 } catch (IllegalArgumentException | RestconfDocumentedException |
349 NullPointerException e) {
350 log.info(format(INFO_MSG, val, e));
352 firstHalf.append(values[i]).append(UNDERSCORE);
353 secondHalf = secondHalf.replaceFirst(
354 values[i + 1] + UNDERSCORE,"");
356 val = val.replace(COLON,UNDERSCORE);
358 id = toInstanceIdentifier(prefix + val, context, null);
359 return new SchemaPathHolder(id, val);
360 } catch (IllegalArgumentException | RestconfDocumentedException |
361 NullPointerException e1) {
362 throw new IllegalArgumentException(EXC_MSG, e1);
367 * Returns the namespace of the given node name. If the node name is
368 * separated by colon, the it splits with colon and forms the namespace.
369 * If the node name is formed with underscore, then it splits the node
370 * name consecutively to figure out the proper module name.
372 * @param childName node name
373 * @param ctx schema context
374 * @param parent parent properties node
375 * @param curSchema current schema
376 * @return namespace of the given node
378 static Namespace getNamespace(String childName, EffectiveModelContext ctx,
379 PropertiesNode parent, SchemaNode curSchema) {
381 Namespace parentNs = parent.namespace();
382 Namespace ns = new Namespace(parentNs.moduleName(),
383 parentNs.moduleNs(), parentNs.revision());
384 int lastIndexOfColon = childName.lastIndexOf(COLON);
385 if (lastIndexOfColon != -1) {
386 String moduleName = childName.substring(0, lastIndexOfColon);
387 childName = childName.substring(lastIndexOfColon+1);
388 Namespace ns1 = getNs(moduleName, ctx);
394 SchemaNode child = getChildSchemaNode(curSchema, childName, ns);
396 if (child == null && childName.contains(UNDERSCORE)) {
397 String[] children = childName.split(UNDERSCORE);
398 String second = childName.substring(children[0].length() + 1);
399 StringBuilder first = new StringBuilder();
401 for (int i =0; i< children.length; i++) {
402 String moduleName = first + children[i];
403 Namespace newNs = getNs(moduleName, ctx);
407 first.append(children[i]).append(UNDERSCORE);
408 if (i + 1 < children.length) {
409 second = second.replaceFirst(
410 children[i + 1] + UNDERSCORE, "");
419 * Returns the namespace by finding the given module in the schema context.
421 * @param modName module name
422 * @param ctx schema context
423 * @return namespace of the given node name
425 private static Namespace getNs(String modName, EffectiveModelContext ctx) {
426 Iterator<? extends Module> it = ctx.findModules(modName).iterator();
428 Module m = it.next();
429 return new Namespace(modName, m.getQNameModule().getNamespace(),
430 getRevision(m.getRevision()));
436 * Returns child schema node.
438 * @param curSchema current schema node
439 * @param name name of the property
440 * @param namespace namespace of the property
441 * @return child schema node
443 public static SchemaNode getChildSchemaNode(SchemaNode curSchema,
445 Namespace namespace) {
446 if (namespace == null) {
450 QName qname = QName.create(namespace.moduleNs(),
451 Revision.of(namespace.revision()), name);
453 // YANG RPC will not be instance of DataSchemaNode
454 if (curSchema instanceof DataSchemaNode) {
455 Deque<DataSchemaNode> schemaNodeDeque = ParserStreamUtils.
456 findSchemaNodeByNameAndNamespace(((DataSchemaNode)
457 curSchema), name, namespace.moduleNs());
458 if (schemaNodeDeque.isEmpty()) {
459 // could not find schema node
463 DataSchemaNode schemaNode = schemaNodeDeque.pop();
464 if (schemaNodeDeque.isEmpty()){
469 // node is child of Choice/case
470 return SchemaUtils.findSchemaForChild(((ChoiceSchemaNode) schemaNode),
473 return SchemaUtils.findDataChildSchemaByQName(curSchema, qname);
478 * Returns the property node type.
480 * @param index current index
481 * @param length length of the properties
482 * @param name name of the property
483 * @return the property node type
485 public static NodeType getNodeType(int index, int length, String name,
487 if (index == length-1) {
488 if (schema instanceof AnyxmlSchemaNode) {
489 return NodeType.ANY_XML_NODE;
491 return (isListEntry(name) ? NodeType.MULTI_INSTANCE_LEAF_NODE :
492 NodeType.SINGLE_INSTANCE_LEAF_NODE);
494 return (isListEntry(name) ? NodeType.MULTI_INSTANCE_NODE :
495 NodeType.SINGLE_INSTANCE_NODE);
500 * Returns revision in string.
502 * @param r YANG revision
503 * @return revision in string
505 public static String getRevision(Optional<Revision> r) {
506 return (r.isPresent()) ? r.get().toString() : null;
510 * Returns value namespace for leaf value.
512 * @param value value of the leaf
513 * @param ctx schema context
514 * @return value namespace
515 * @throws SvcLogicException if identity/module could not be found
517 static Namespace getValueNamespace(String value, EffectiveModelContext ctx) throws SvcLogicException {
518 String prefix = getPrefixFromValue(value);
519 if (prefix == null) {
523 IdentitySchemaNode id = IdentityCodecUtil.parseIdentity(value,
526 final Iterator<? extends Module> modules = ctx.findModules(prefix).iterator();
527 checkArgument(modules.hasNext(), "Could not find " +
528 "module %s", prefix);
529 return modules.next().getQNameModule();
533 throw new SvcLogicException("Could not find identity");
536 return getModuleNamespace(id.getQName(), ctx);
539 private static String getPrefixFromValue(String value) {
540 int lastIndexOfColon = value.lastIndexOf(":");
541 if (lastIndexOfColon != -1) {
542 return value.substring(0, lastIndexOfColon);
548 * Returns module namespace from a given qName.
550 * @param qName qName of a node
551 * @param ctx schema context
552 * @return module namespace of the node
553 * @throws SvcLogicException when the module is not available
555 public static Namespace getModuleNamespace(QName qName, SchemaContext ctx)
556 throws SvcLogicException {
557 Optional<Module> module = ctx.findModule(qName.getModule());
558 if (!module.isPresent()) {
559 throw new SvcLogicException("Could not find module node");
561 Module m = module.get();
562 return new Namespace(m.getName(), m.getQNameModule().getNamespace(),
563 getRevision(m.getRevision()));
566 static String getParsedValue(Namespace valNs, String value) {
567 if (valNs != null && value.contains(":")) {
568 String[] valArr = value.split(":");