Xpath builder on data fragmentation 75/114875/14
authorToineSiebelink <toine.siebelink@est.tech>
Tue, 10 Nov 2020 16:32:50 +0000 (16:32 +0000)
committerRuslan Kashapov <ruslan.kashapov@pantheon.tech>
Tue, 1 Dec 2020 11:24:01 +0000 (13:24 +0200)
Issue-ID: CPS-72
Change-Id: Iab5be2bf0dd7d5540b8880bbf5ab2c6ed9342a9e
Signed-off-by: Ruslan Kashapov <ruslan.kashapov@pantheon.tech>
cps-service/src/main/java/org/onap/cps/api/impl/Fragment.java
cps-service/src/main/java/org/onap/cps/utils/YangUtils.java
cps-service/src/test/groovy/org/onap/cps/utils/YangUtilsSpec.groovy

index 252b09e..90c92f9 100644 (file)
@@ -39,7 +39,7 @@ import org.opendaylight.yangtools.yang.model.api.Module;
 public class Fragment {
 
     @Getter
-    private String xpath;
+    private final String xpath;
 
     @Getter
     private final Map<String, Object> attributes = new HashMap<>();
@@ -62,9 +62,10 @@ public class Fragment {
      *
      * @param module the Yang module that encompasses this fragment
      * @param qnames the list of qualified names that points the schema node for this fragment
+     * @param xpath  the xpath of root fragment
      */
-    public static Fragment createRootFragment(final Module module, final QName... qnames) {
-        return new Fragment(null, module, qnames);
+    public static Fragment createRootFragment(final Module module, final QName[] qnames, final String xpath) {
+        return new Fragment(null, module, qnames, xpath);
     }
 
     /**
@@ -72,24 +73,27 @@ public class Fragment {
      *
      * @param parentFragment the parent (can be null for 'root' objects)
      * @param module         the Yang module that encompasses this fragment
-     * @param qnames         the list of qualified names that points the schema node for this fragment
+     * @param qnames         array of qualified names that points the schema node for this fragment
+     * @param xpath          the xpath for this fragment
      */
-    private Fragment(final Fragment parentFragment, final Module module, final QName... qnames) {
+    private Fragment(final Fragment parentFragment, final Module module, final QName[] qnames, String xpath) {
         this.parentFragment = parentFragment;
         this.module = module;
         this.qnames = qnames;
+        this.xpath = xpath;
     }
 
     /**
      * Create a Child Fragment where the current Fragment is the parent.
      *
-     * @param qnameChild The Qualified name for the child (relative to the parent)
+     * @param childQname The Qualified name for the child (relative to the parent)
+     * @param childXPath The child xpath (relative to the parrent)
      * @return the child fragment
      */
-    public Fragment createChildFragment(final QName qnameChild) {
+    public Fragment createChildFragment(final QName childQname, final String childXPath) {
         final QName[] qnamesForChild = Arrays.copyOf(qnames, qnames.length + 1);
-        qnamesForChild[qnamesForChild.length - 1] = qnameChild;
-        final Fragment childFragment = new Fragment(this, module, qnamesForChild);
+        qnamesForChild[qnamesForChild.length - 1] = childQname;
+        final Fragment childFragment = new Fragment(this, module, qnamesForChild, getXpath() + childXPath);
         childFragments.add(childFragment);
         return childFragment;
     }
index 0f05d7d..071ff6a 100644 (file)
@@ -24,12 +24,16 @@ import java.io.File;
 import java.io.IOException;
 import java.io.StringReader;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.Iterator;
+import java.util.List;
 import java.util.ServiceLoader;
 import java.util.logging.Logger;
+import java.util.stream.Collectors;
 import org.onap.cps.api.impl.Fragment;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode;
 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
@@ -116,8 +120,9 @@ public class YangUtils {
     public static Fragment fragmentNormalizedNode(
             final NormalizedNode<? extends YangInstanceIdentifier.PathArgument, ?> tree,
             final Module module) {
-        final QName nodeType = tree.getNodeType();
-        final Fragment rootFragment = Fragment.createRootFragment(module, nodeType);
+        final QName[] nodeTypes = {tree.getNodeType()};
+        final String xpath = buildXpathId(tree.getIdentifier());
+        final Fragment rootFragment = Fragment.createRootFragment(module, nodeTypes, xpath);
         fragmentNormalizedNode(rootFragment, tree);
         return rootFragment;
     }
@@ -167,8 +172,37 @@ public class YangUtils {
     private static void createNodeForEachListElement(final Fragment currentFragment, final MapNode mapNode) {
         final Collection<MapEntryNode> mapEntryNodes = mapNode.getValue();
         for (final MapEntryNode mapEntryNode : mapEntryNodes) {
-            final Fragment listElementFragment = currentFragment.createChildFragment(mapNode.getNodeType());
+            final String xpathId = buildXpathId(mapEntryNode.getIdentifier());
+            final Fragment listElementFragment =
+                currentFragment.createChildFragment(mapNode.getNodeType(), xpathId);
             fragmentNormalizedNode(listElementFragment, mapEntryNode);
         }
     }
+
+    private static String buildXpathId(final YangInstanceIdentifier.PathArgument nodeIdentifier) {
+        final StringBuilder xpathIdBuilder = new StringBuilder();
+        xpathIdBuilder.append("/").append(nodeIdentifier.getNodeType().getLocalName());
+
+        if (nodeIdentifier instanceof NodeIdentifierWithPredicates) {
+            xpathIdBuilder.append(getKeyAttributesStatement((NodeIdentifierWithPredicates) nodeIdentifier));
+        }
+        return xpathIdBuilder.toString();
+    }
+
+    private static String getKeyAttributesStatement(final NodeIdentifierWithPredicates nodeIdentifier) {
+        final List<String> keyAttributes = nodeIdentifier.entrySet().stream().map(
+            entry -> {
+                final String name = entry.getKey().getLocalName();
+                final String value = String.valueOf(entry.getValue()).replace("'", "\\'");
+                return String.format("@%s='%s'", name, value);
+            }
+        ).collect(Collectors.toList());
+
+        if (keyAttributes.isEmpty()) {
+            return "";
+        } else {
+            Collections.sort(keyAttributes);
+            return "[" + String.join(" and ", keyAttributes) + "]";
+        }
+    }
 }
index 6a463ad..801e430 100644 (file)
@@ -96,6 +96,10 @@ class YangUtilsSpec extends Specification{
             result.childFragments.size() == 2
         and: 'each child (category) has the root fragment (result) as parent and in turn as 1 child (a list of books)'
             result.childFragments.each { it.parentFragment == result && it.childFragments.size() == 1 }
+        and: 'the fragments have the correct xpaths'
+            assert result.xpath == '/bookstore'
+            assert result.childFragments.collect { it.xpath }
+                .containsAll(["/bookstore/categories[@code='01']", "/bookstore/categories[@code='02']"])
     }
 
 }