Ensure Leaf value retains Integer type
[cps.git] / cps-ri / src / main / java / org / onap / cps / spi / impl / CpsDataPersistenceServiceImpl.java
index c638b91..7b3b726 100644 (file)
@@ -24,16 +24,20 @@ package org.onap.cps.spi.impl;
 
 import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS;
 
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.ImmutableSet.Builder;
 import com.google.gson.Gson;
 import com.google.gson.GsonBuilder;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 import java.util.stream.Collectors;
 import javax.transaction.Transactional;
@@ -48,6 +52,8 @@ import org.onap.cps.spi.entities.FragmentEntity;
 import org.onap.cps.spi.exceptions.AlreadyDefinedException;
 import org.onap.cps.spi.exceptions.ConcurrencyException;
 import org.onap.cps.spi.exceptions.CpsPathException;
+import org.onap.cps.spi.exceptions.DataNodeNotFoundException;
+import org.onap.cps.spi.exceptions.DataValidationException;
 import org.onap.cps.spi.model.DataNode;
 import org.onap.cps.spi.model.DataNodeBuilder;
 import org.onap.cps.spi.repository.AnchorRepository;
@@ -66,6 +72,8 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService
 
     private FragmentRepository fragmentRepository;
 
+    private final ObjectMapper objectMapper;
+
     /**
      * Constructor.
      *
@@ -78,10 +86,12 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService
         this.dataspaceRepository = dataspaceRepository;
         this.anchorRepository = anchorRepository;
         this.fragmentRepository = fragmentRepository;
+        this.objectMapper = new ObjectMapper();
     }
 
     private static final Gson GSON = new GsonBuilder().create();
     private static final String REG_EX_FOR_OPTIONAL_LIST_INDEX = "(\\[@[\\s\\S]+?]){0,1})";
+    private static final String REG_EX_FOR_LIST_NODE_KEY = "\\[(\\@([^/]*?))+( and)*\\]$";
 
     @Override
     public void addChildDataNode(final String dataspaceName, final String anchorName, final String parentXpath,
@@ -233,17 +243,27 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService
         return ancestorXpath;
     }
 
-    private static DataNode toDataNode(final FragmentEntity fragmentEntity,
+    private DataNode toDataNode(final FragmentEntity fragmentEntity,
         final FetchDescendantsOption fetchDescendantsOption) {
-        final Map<String, Object> leaves = GSON.fromJson(fragmentEntity.getAttributes(), Map.class);
         final List<DataNode> childDataNodes = getChildDataNodes(fragmentEntity, fetchDescendantsOption);
+        Map<String, Object> leaves = new HashMap<>();
+        if (fragmentEntity.getAttributes() != null) {
+            try {
+                leaves = objectMapper.readValue(fragmentEntity.getAttributes(), Map.class);
+            } catch (final JsonProcessingException jsonProcessingException) {
+                final String message = "Parsing error occurred while processing fragmentEntity attributes.";
+                log.error(message);
+                throw new DataValidationException(message,
+                    jsonProcessingException.getMessage(), jsonProcessingException);
+            }
+        }
         return new DataNodeBuilder()
             .withXpath(fragmentEntity.getXpath())
             .withLeaves(leaves)
             .withChildDataNodes(childDataNodes).build();
     }
 
-    private static List<DataNode> getChildDataNodes(final FragmentEntity fragmentEntity,
+    private List<DataNode> getChildDataNodes(final FragmentEntity fragmentEntity,
         final FetchDescendantsOption fetchDescendantsOption) {
         if (fetchDescendantsOption == INCLUDE_ALL_DESCENDANTS) {
             return fragmentEntity.getChildFragments().stream()
@@ -315,8 +335,33 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService
         fragmentRepository.save(parentEntity);
     }
 
+    @Override
+    @Transactional
+    public void deleteListDataNodes(final String dataspaceName, final String anchorName, final String listNodeXpath) {
+        final var parentNodeXpath = listNodeXpath.substring(0, listNodeXpath.lastIndexOf('/'));
+        final var parentEntity = getFragmentByXpath(dataspaceName, anchorName, parentNodeXpath);
+        final var descendantNode = listNodeXpath.substring(listNodeXpath.lastIndexOf('/'));
+        final Matcher descendantNodeHasListNodeKey = Pattern.compile(REG_EX_FOR_LIST_NODE_KEY).matcher(descendantNode);
+
+        final boolean xpathPointsToAValidChildNodeWithKey = parentEntity.getChildFragments().stream().anyMatch(
+            (fragment) -> fragment.getXpath().equals(listNodeXpath));
+
+        final boolean xpathPointsToAValidChildNodeWithoutKey = parentEntity.getChildFragments().stream().anyMatch(
+            (fragment) -> fragment.getXpath().replaceAll(REG_EX_FOR_LIST_NODE_KEY, "").equals(listNodeXpath));
+
+        if ((descendantNodeHasListNodeKey.find() && xpathPointsToAValidChildNodeWithKey)
+            ||
+            (!descendantNodeHasListNodeKey.find() && xpathPointsToAValidChildNodeWithoutKey)) {
+            removeListNodeDescendants(parentEntity, listNodeXpath);
+        } else {
+            throw new DataNodeNotFoundException(parentEntity.getDataspace().getName(),
+                parentEntity.getAnchor().getName(), listNodeXpath);
+        }
+    }
+
     private void removeListNodeDescendants(final FragmentEntity parentFragmentEntity, final String listNodeXpath) {
-        final String listNodeXpathPrefix = listNodeXpath + "[";
+        final Matcher descendantNodeHasListNodeKey = Pattern.compile(REG_EX_FOR_LIST_NODE_KEY).matcher(listNodeXpath);
+        final String listNodeXpathPrefix = listNodeXpath + (descendantNodeHasListNodeKey.find() ? "" : "[");
         if (parentFragmentEntity.getChildFragments()
             .removeIf(fragment -> fragment.getXpath().startsWith(listNodeXpathPrefix))) {
             fragmentRepository.save(parentFragmentEntity);