From 46a7718bc0f0d2951ed18795c85f6961f839df25 Mon Sep 17 00:00:00 2001 From: ToineSiebelink Date: Tue, 10 Nov 2020 16:32:50 +0000 Subject: Xpath builder on data fragmentation Issue-ID: CPS-72 Change-Id: Iab5be2bf0dd7d5540b8880bbf5ab2c6ed9342a9e Signed-off-by: Ruslan Kashapov --- .../main/java/org/onap/cps/api/impl/Fragment.java | 22 +++++++----- .../main/java/org/onap/cps/utils/YangUtils.java | 40 ++++++++++++++++++++-- .../groovy/org/onap/cps/utils/YangUtilsSpec.groovy | 4 +++ 3 files changed, 54 insertions(+), 12 deletions(-) diff --git a/cps-service/src/main/java/org/onap/cps/api/impl/Fragment.java b/cps-service/src/main/java/org/onap/cps/api/impl/Fragment.java index 252b09e5c..90c92f905 100644 --- a/cps-service/src/main/java/org/onap/cps/api/impl/Fragment.java +++ b/cps-service/src/main/java/org/onap/cps/api/impl/Fragment.java @@ -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 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; } diff --git a/cps-service/src/main/java/org/onap/cps/utils/YangUtils.java b/cps-service/src/main/java/org/onap/cps/utils/YangUtils.java index 0f05d7d92..071ff6ad3 100644 --- a/cps-service/src/main/java/org/onap/cps/utils/YangUtils.java +++ b/cps-service/src/main/java/org/onap/cps/utils/YangUtils.java @@ -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 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 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 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) + "]"; + } + } } diff --git a/cps-service/src/test/groovy/org/onap/cps/utils/YangUtilsSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/utils/YangUtilsSpec.groovy index 6a463ad6f..801e43079 100644 --- a/cps-service/src/test/groovy/org/onap/cps/utils/YangUtilsSpec.groovy +++ b/cps-service/src/test/groovy/org/onap/cps/utils/YangUtilsSpec.groovy @@ -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']"]) } } -- cgit 1.2.3-korg