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 java.util.Collection;
 
  24 import java.util.Deque;
 
  25 import java.util.Iterator;
 
  26 import java.util.Optional;
 
  28 import org.onap.ccsdk.sli.core.sli.SvcLogicException;
 
  29 import org.opendaylight.restconf.common.context.InstanceIdentifierContext;
 
  30 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
 
  31 import org.opendaylight.yangtools.yang.common.QName;
 
  32 import org.opendaylight.yangtools.yang.common.Revision;
 
  33 import org.opendaylight.yangtools.yang.data.impl.schema.SchemaUtils;
 
  34 import org.opendaylight.yangtools.yang.data.util.ParserStreamUtils;
 
  35 import org.opendaylight.yangtools.yang.data.util.codec.IdentityCodecUtil;
 
  36 import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
 
  37 import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
 
  38 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
 
  39 import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
 
  40 import org.opendaylight.yangtools.yang.model.api.Module;
 
  41 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 
  42 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
 
  43 import org.slf4j.Logger;
 
  44 import org.slf4j.LoggerFactory;
 
  46 import static com.google.common.base.Preconditions.checkArgument;
 
  47 import static java.lang.String.format;
 
  48 import static java.util.regex.Pattern.quote;
 
  49 import static org.opendaylight.restconf.nb.rfc8040.utils.parser.ParserIdentifier.toInstanceIdentifier;
 
  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) {
 
 181             Collection<PropertiesNode> childsFromAugmentation = parent
 
 182                     .augmentations().get(augSchema);
 
 183             if (!childsFromAugmentation.isEmpty()) {
 
 184                 for (PropertiesNode pNode : childsFromAugmentation) {
 
 185                     if (pNode.name().equals(name)) {
 
 195      * Creates uri with specified name and namespace.
 
 197      * @param parent parent properties node
 
 198      * @param name name of the node
 
 199      * @param ns namespace of the node
 
 200      * @return uri with specified name and namespace
 
 202     public static String getUri(PropertiesNode parent, String name,
 
 205         if (!(parent.namespace().moduleNs().equals(ns.moduleNs()))) {
 
 206             uri = ns.moduleName() + ":" + name;
 
 208         return parent.uri() + "." + uri;
 
 212      * Creates new properties with specified parameters.
 
 214      * @param name name of the properties node
 
 215      * @param namespace namespace of the properties node
 
 216      * @param uri uri of the properties node
 
 217      * @param parent parent node
 
 218      * @param appInfo application info
 
 219      * @param type node type
 
 220      * @return new properties node
 
 222     public static PropertiesNode createNode(String name, Namespace namespace,
 
 223                                             String uri, PropertiesNode parent,
 
 224                                             Object appInfo, NodeType type) {
 
 226             case SINGLE_INSTANCE_NODE:
 
 227                 return new SingleInstanceNode(name, namespace, uri, parent, appInfo, type);
 
 228             case MULTI_INSTANCE_HOLDER_NODE:
 
 229                 return new ListHolderNode(name, namespace, uri, parent, appInfo, type);
 
 230             case MULTI_INSTANCE_LEAF_HOLDER_NODE:
 
 231                 return new LeafListHolderNode(name, namespace, uri, parent, appInfo, type);
 
 233                 throw new RuntimeException("Invalid node type");
 
 238      * Returns true if namespace is same as parent's namespace.
 
 240      * @param parent parent property node
 
 241      * @param curNode current property node
 
 242      * @return true if namespace is same as parent namespace
 
 244     public static boolean isNamespaceAsParent(PropertiesNode parent,
 
 245                                               PropertiesNode curNode) {
 
 246         return parent.namespace().moduleNs().equals(curNode.namespace().moduleNs());
 
 250      * Returns the schema path holder with a formatted url and the instance
 
 251      * identifier context from a given uri or the parameters from svc logic
 
 254      * @param uri     unformatted uri or parameter
 
 255      * @param context schema context
 
 256      * @return schema path holder
 
 258     public static SchemaPathHolder getProcessedPath(String uri,
 
 259                                                     SchemaContext context) {
 
 261         String uri1 = uri.replaceAll(UNDERSCORE, COLON);
 
 263             InstanceIdentifierContext<?> id = toInstanceIdentifier(
 
 264                     uri1, context, null);
 
 265             return new SchemaPathHolder(id, uri1);
 
 266         } catch (IllegalArgumentException | RestconfDocumentedException
 
 267                 | NullPointerException e) {
 
 268             return processNodesAndAppendPath(uri, context);
 
 273      * Processes the nodes in the given uri and finds instance identifier
 
 274      * context till it reaches the last node in uri. If its not able to find
 
 275      * schema for the path, it appends the suffix part and puts it back in
 
 278      * @param uri     uri with underscore
 
 279      * @param context schema context
 
 280      * @return schema and path holder
 
 282     private static SchemaPathHolder processNodesAndAppendPath(String uri,
 
 283                                                               SchemaContext context) {
 
 286         SchemaPathHolder id = new SchemaPathHolder(null, "");
 
 287         String[] uriParts = uri.split(SLASH);
 
 289         if (uri.contains(UNDERSCORE)) {
 
 290             sec = uri.substring(uriParts[0].length()+1);
 
 292         for (int i = 0; i<uriParts.length; i++) {
 
 295                 id = processIdentifier(uriParts[i], context, actPath);
 
 296             } catch (IllegalArgumentException e) {
 
 297                 id.setUri(actPath+ uriParts[i] + sec);
 
 301             actPath = actPath + id.getUri() + SLASH;
 
 302             if (sec.startsWith(SLASH)) {
 
 303                 sec = sec.replaceFirst(SLASH, "");
 
 305             if (i+1 < uriParts.length) {
 
 306                 sec = sec.replaceFirst(quote(uriParts[i + 1]), "");
 
 309         id.setUri(actPath.substring(0,actPath.length() - 1));
 
 314      * Processes the schema and path holder for a given node in the path. It
 
 315      * figures if the path is valid by replacing underscore in the node
 
 316      * consecutively, till it finds the proper schema for the node.
 
 318      * @param node    node in the path
 
 319      * @param context schema context
 
 320      * @param prefix  prefix for the node in the path
 
 321      * @return schema and path holder
 
 323     private static SchemaPathHolder processIdentifier(String node,
 
 324                                                       SchemaContext context,
 
 327         String[] values = node.split(UNDERSCORE);
 
 328         String val = values[0];
 
 329         StringBuilder firstHalf = new StringBuilder();
 
 330         String secondHalf = "";
 
 331         if (node.contains(UNDERSCORE)) {
 
 332             secondHalf = node.substring(values[0].length()+1);
 
 334         InstanceIdentifierContext<?> id;
 
 335         for (int i = 0; i< values.length-1; i++) {
 
 337             val = firstHalf + val + COLON + secondHalf;
 
 339                 id = toInstanceIdentifier(prefix + val, context, null);
 
 340                 return new SchemaPathHolder(id, val);
 
 341             } catch (IllegalArgumentException | RestconfDocumentedException |
 
 342                     NullPointerException e) {
 
 343                 log.info(format(INFO_MSG, val));
 
 345             firstHalf.append(values[i]).append(UNDERSCORE);
 
 346             secondHalf = secondHalf.replaceFirst(
 
 347                     values[i + 1] + UNDERSCORE,"");
 
 349         val = val.replace(COLON,UNDERSCORE);
 
 351             id = toInstanceIdentifier(prefix + val, context, null);
 
 352             return new SchemaPathHolder(id, val);
 
 353         } catch (IllegalArgumentException | RestconfDocumentedException |
 
 354                 NullPointerException e1) {
 
 355             throw new IllegalArgumentException(EXC_MSG, e1);
 
 360      * Returns the namespace of the given node name. If the node name is
 
 361      * separated by colon, the it splits with colon and forms the namespace.
 
 362      * If the node name is formed with underscore, then it splits the node
 
 363      * name consecutively to figure out the proper module name.
 
 365      * @param childName node name
 
 366      * @param ctx       schema context
 
 367      * @param parent    parent properties node
 
 368      * @param curSchema current schema
 
 369      * @return namespace of the given node
 
 371     static Namespace getNamespace(String childName, SchemaContext ctx,
 
 372                                   PropertiesNode parent, SchemaNode curSchema) {
 
 374         Namespace parentNs = parent.namespace();
 
 375         Namespace ns = new Namespace(parentNs.moduleName(),
 
 376                                      parentNs.moduleNs(), parentNs.revision());
 
 377         int lastIndexOfColon = childName.lastIndexOf(COLON);
 
 378         if (lastIndexOfColon != -1) {
 
 379             String moduleName = childName.substring(0, lastIndexOfColon);
 
 380             childName = childName.substring(lastIndexOfColon+1);
 
 381             Namespace ns1 = getNs(moduleName, ctx);
 
 387         SchemaNode child = getChildSchemaNode(curSchema, childName, ns);
 
 389         if (child == null && childName.contains(UNDERSCORE)) {
 
 390             String[] children = childName.split(UNDERSCORE);
 
 391             String second = childName.substring(children[0].length() + 1);
 
 392             StringBuilder first = new StringBuilder();
 
 394             for (int i =0; i< children.length; i++) {
 
 395                 String moduleName = first + children[i];
 
 396                 Namespace newNs = getNs(moduleName, ctx);
 
 400                 first.append(children[i]).append(UNDERSCORE);
 
 401                 if (i + 1 < children.length) {
 
 402                     second = second.replaceFirst(
 
 403                             children[i + 1] + UNDERSCORE, "");
 
 412      * Returns the namespace by finding the given module in the schema context.
 
 414      * @param modName module name
 
 415      * @param ctx     schema context
 
 416      * @return namespace of the given node name
 
 418     private static Namespace getNs(String modName, SchemaContext ctx) {
 
 419         Iterator<Module> it = ctx.findModules(modName).iterator();
 
 421             Module m = it.next();
 
 422             return new Namespace(modName, m.getQNameModule().getNamespace(),
 
 423                                  getRevision(m.getRevision()));
 
 429      * Returns child schema node.
 
 431      * @param curSchema current schema node
 
 432      * @param name name of the property
 
 433      * @param namespace namespace of the property
 
 434      * @return child schema node
 
 436     public static SchemaNode getChildSchemaNode(SchemaNode curSchema,
 
 438                                                 Namespace namespace) {
 
 439         if (namespace == null) {
 
 443         QName qname =  QName.create(namespace.moduleNs(),
 
 444                                     Revision.of(namespace.revision()), name);
 
 446         // YANG RPC will not be instance of DataSchemaNode
 
 447         if (curSchema instanceof DataSchemaNode) {
 
 448             Deque<DataSchemaNode> schemaNodeDeque = ParserStreamUtils.
 
 449                     findSchemaNodeByNameAndNamespace(((DataSchemaNode)
 
 450                             curSchema), name, namespace.moduleNs());
 
 451             if (schemaNodeDeque.isEmpty()) {
 
 452                 // could not find schema node
 
 456             DataSchemaNode schemaNode = schemaNodeDeque.pop();
 
 457             if (schemaNodeDeque.isEmpty()){
 
 462             // node is child of Choice/case
 
 463             return SchemaUtils.findSchemaForChild(((ChoiceSchemaNode) schemaNode),
 
 466             return SchemaUtils.findDataChildSchemaByQName(curSchema, qname);
 
 471      * Returns the property node type.
 
 473      * @param index current index
 
 474      * @param length length of the properties
 
 475      * @param name name of the property
 
 476      * @return the property node type
 
 478     public static NodeType getNodeType(int index, int length, String name) {
 
 479         if (index == length-1) {
 
 480             return (isListEntry(name) ? NodeType.MULTI_INSTANCE_LEAF_NODE :
 
 481                     NodeType.SINGLE_INSTANCE_LEAF_NODE);
 
 483             return (isListEntry(name) ? NodeType.MULTI_INSTANCE_NODE :
 
 484                     NodeType.SINGLE_INSTANCE_NODE);
 
 489      * Returns revision in string.
 
 491      * @param r YANG revision
 
 492      * @return revision in string
 
 494     public static String getRevision(Optional<Revision> r) {
 
 495         return (r.isPresent()) ? r.get().toString() : null;
 
 499      * Returns value namespace for leaf value.
 
 501      * @param value value of the leaf
 
 502      * @param ctx schema context
 
 503      * @return value namespace
 
 504      * @throws SvcLogicException if identity/module could not be found
 
 506     static Namespace getValueNamespace(String value,
 
 508             throws SvcLogicException {
 
 509         String prefix = getPrefixFromValue(value);
 
 510         if (prefix == null) {
 
 514         IdentitySchemaNode id = IdentityCodecUtil.parseIdentity(value,
 
 517             final Iterator<Module> modules = ctx.findModules(prefix).iterator();
 
 518             checkArgument(modules.hasNext(), "Could not find " +
 
 519                                   "module %s", prefix);
 
 520             return modules.next().getQNameModule();
 
 524             throw new SvcLogicException("Could not find identity");
 
 527         return getModuleNamespace(id.getQName(), ctx);
 
 530     private static String getPrefixFromValue(String value) {
 
 531         int lastIndexOfColon = value.lastIndexOf(":");
 
 532         if (lastIndexOfColon != -1) {
 
 533             return value.substring(0, lastIndexOfColon);
 
 539      * Returns module namespace from a given qName.
 
 541      * @param qName qName of a node
 
 542      * @param ctx   schema context
 
 543      * @return module namespace of the node
 
 544      * @throws SvcLogicException when the module is not available
 
 546     public static Namespace getModuleNamespace(QName qName, SchemaContext ctx)
 
 547             throws SvcLogicException {
 
 548         Optional<Module> module = ctx.findModule(qName.getModule());
 
 549         if (!module.isPresent()) {
 
 550             throw new SvcLogicException("Could not find module node");
 
 552         Module m = module.get();
 
 553         return new Namespace(m.getName(), m.getQNameModule().getNamespace(),
 
 554                              getRevision(m.getRevision()));
 
 557     static String getParsedValue(Namespace valNs, String value) {
 
 558         if (valNs != null && value.contains(":")) {
 
 559             String[] valArr = value.split(":");