X-Git-Url: https://gerrit.onap.org/r/gitweb?a=blobdiff_plain;f=cps-ri%2Fsrc%2Fmain%2Fjava%2Forg%2Fonap%2Fcps%2Fspi%2Fimpl%2FCpsDataPersistenceServiceImpl.java;h=ded234bb489598e902f665590fb4bf3c3bc4f493;hb=0b80343610a215f26a7d764cc849f8e9ca44fea0;hp=f924c70453c48037fd91b6437d1da5709ce696c2;hpb=f3798ee9857edb45243d540606ceac21df1215ab;p=cps.git diff --git a/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java b/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java index f924c7045..ded234bb4 100644 --- a/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java +++ b/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java @@ -1,8 +1,8 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2021 Nordix Foundation + * Copyright (C) 2021-2022 Nordix Foundation * Modifications Copyright (C) 2021 Pantheon.tech - * Modifications Copyright (C) 2020-2021 Bell Canada. + * Modifications Copyright (C) 2020-2022 Bell Canada. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,12 +24,8 @@ 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; @@ -41,9 +37,12 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; import javax.transaction.Transactional; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.hibernate.StaleStateException; import org.onap.cps.cpspath.parser.CpsPathQuery; +import org.onap.cps.cpspath.parser.CpsPathUtil; +import org.onap.cps.cpspath.parser.PathParsingException; import org.onap.cps.spi.CpsDataPersistenceService; import org.onap.cps.spi.FetchDescendantsOption; import org.onap.cps.spi.entities.AnchorEntity; @@ -51,78 +50,66 @@ import org.onap.cps.spi.entities.DataspaceEntity; 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.CpsAdminException; 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; import org.onap.cps.spi.repository.DataspaceRepository; import org.onap.cps.spi.repository.FragmentRepository; +import org.onap.cps.spi.utils.SessionManager; +import org.onap.cps.utils.JsonObjectMapper; import org.springframework.dao.DataIntegrityViolationException; import org.springframework.stereotype.Service; @Service @Slf4j +@RequiredArgsConstructor public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService { - private DataspaceRepository dataspaceRepository; + private final DataspaceRepository dataspaceRepository; - private AnchorRepository anchorRepository; + private final AnchorRepository anchorRepository; - private FragmentRepository fragmentRepository; + private final FragmentRepository fragmentRepository; - private final ObjectMapper objectMapper; + private final JsonObjectMapper jsonObjectMapper; - /** - * Constructor. - * - * @param dataspaceRepository dataspaceRepository - * @param anchorRepository anchorRepository - * @param fragmentRepository fragmentRepository - */ - public CpsDataPersistenceServiceImpl(final DataspaceRepository dataspaceRepository, - final AnchorRepository anchorRepository, final FragmentRepository fragmentRepository) { - this.dataspaceRepository = dataspaceRepository; - this.anchorRepository = anchorRepository; - this.fragmentRepository = fragmentRepository; - objectMapper = new ObjectMapper(); - } + private final SessionManager sessionManager; - 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 = "\\[(\\@([^/]*?)){0,99}( and)*\\]$"; + private static final Pattern REG_EX_PATTERN_FOR_LIST_ELEMENT_KEY_PREDICATE = + Pattern.compile("\\[(\\@([^\\/]{0,9999}))\\]$"); @Override - public void addChildDataNode(final String dataspaceName, final String anchorName, final String parentXpath, - final DataNode dataNode) { - final FragmentEntity parentFragment = getFragmentByXpath(dataspaceName, anchorName, parentXpath); - final FragmentEntity fragmentEntity = - toFragmentEntity(parentFragment.getDataspace(), parentFragment.getAnchor(), dataNode); - parentFragment.getChildFragments().add(fragmentEntity); - try { - fragmentRepository.save(parentFragment); - } catch (final DataIntegrityViolationException exception) { - throw AlreadyDefinedException.forDataNode(dataNode.getXpath(), anchorName, exception); - } + @Transactional + public void addChildDataNode(final String dataspaceName, final String anchorName, final String parentNodeXpath, + final DataNode newChildDataNode) { + addChildDataNodes(dataspaceName, anchorName, parentNodeXpath, Collections.singleton(newChildDataNode)); } @Override - public void addListDataNodes(final String dataspaceName, final String anchorName, final String parentNodeXpath, - final Collection dataNodes) { - final FragmentEntity parentFragment = getFragmentByXpath(dataspaceName, anchorName, parentNodeXpath); - final List newFragmentEntities = - dataNodes.stream().map( - dataNode -> toFragmentEntity(parentFragment.getDataspace(), parentFragment.getAnchor(), dataNode) - ).collect(Collectors.toUnmodifiableList()); - parentFragment.getChildFragments().addAll(newFragmentEntities); + @Transactional + public void addListElements(final String dataspaceName, final String anchorName, final String parentNodeXpath, + final Collection newListElements) { + addChildDataNodes(dataspaceName, anchorName, parentNodeXpath, newListElements); + } + + private void addChildDataNodes(final String dataspaceName, final String anchorName, final String parentNodeXpath, + final Collection newChildren) { + final FragmentEntity parentFragmentEntity = getFragmentByXpath(dataspaceName, anchorName, parentNodeXpath); try { - fragmentRepository.save(parentFragment); - dataNodes.forEach( - dataNode -> getChildFragments(dataspaceName, anchorName, dataNode) - ); + for (final DataNode newChildAsDataNode : newChildren) { + final FragmentEntity newChildAsFragmentEntity = convertToFragmentWithAllDescendants( + parentFragmentEntity.getDataspace(), + parentFragmentEntity.getAnchor(), + newChildAsDataNode); + newChildAsFragmentEntity.setParentId(parentFragmentEntity.getId()); + fragmentRepository.save(newChildAsFragmentEntity); + } } catch (final DataIntegrityViolationException exception) { - final List conflictXpaths = dataNodes.stream() + final List conflictXpaths = newChildren.stream() .map(DataNode::getXpath) .collect(Collectors.toList()); throw AlreadyDefinedException.forDataNodes(conflictXpaths, anchorName, exception); @@ -151,7 +138,7 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService * @param dataNodeToBeConverted dataNode * @return a Fragment built from current DataNode */ - private static FragmentEntity convertToFragmentWithAllDescendants(final DataspaceEntity dataspaceEntity, + private FragmentEntity convertToFragmentWithAllDescendants(final DataspaceEntity dataspaceEntity, final AnchorEntity anchorEntity, final DataNode dataNodeToBeConverted) { final FragmentEntity parentFragment = toFragmentEntity(dataspaceEntity, anchorEntity, dataNodeToBeConverted); final Builder childFragmentsImmutableSetBuilder = ImmutableSet.builder(); @@ -165,24 +152,13 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService return parentFragment; } - private void getChildFragments(final String dataspaceName, final String anchorName, final DataNode dataNode) { - for (final DataNode childDataNode: dataNode.getChildDataNodes()) { - final FragmentEntity getChildsParentFragmentByXPath = - getFragmentByXpath(dataspaceName, anchorName, dataNode.getXpath()); - final FragmentEntity childFragmentEntity = toFragmentEntity(getChildsParentFragmentByXPath.getDataspace(), - getChildsParentFragmentByXPath.getAnchor(), childDataNode); - getChildsParentFragmentByXPath.getChildFragments().add(childFragmentEntity); - fragmentRepository.save(getChildsParentFragmentByXPath); - } - } - - private static FragmentEntity toFragmentEntity(final DataspaceEntity dataspaceEntity, + private FragmentEntity toFragmentEntity(final DataspaceEntity dataspaceEntity, final AnchorEntity anchorEntity, final DataNode dataNode) { return FragmentEntity.builder() .dataspace(dataspaceEntity) .anchor(anchorEntity) .xpath(dataNode.getXpath()) - .attributes(GSON.toJson(dataNode.getLeaves())) + .attributes(jsonObjectMapper.asJsonString(dataNode.getLeaves())) .build(); } @@ -200,8 +176,14 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService if (isRootXpath(xpath)) { return fragmentRepository.findFirstRootByDataspaceAndAnchor(dataspaceEntity, anchorEntity); } else { + final String normalizedXpath; + try { + normalizedXpath = CpsPathUtil.getNormalizedXpath(xpath); + } catch (final PathParsingException e) { + throw new CpsPathException(e.getMessage()); + } return fragmentRepository.getByDataspaceAndAnchorAndXpath(dataspaceEntity, anchorEntity, - xpath); + normalizedXpath); } } @@ -212,8 +194,8 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService final AnchorEntity anchorEntity = anchorRepository.getByDataspaceAndName(dataspaceEntity, anchorName); final CpsPathQuery cpsPathQuery; try { - cpsPathQuery = CpsPathQuery.createFrom(cpsPath); - } catch (final IllegalStateException e) { + cpsPathQuery = CpsPathUtil.getCpsPathQuery(cpsPath); + } catch (final PathParsingException e) { throw new CpsPathException(e.getMessage()); } List fragmentEntities = @@ -228,6 +210,22 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService .collect(Collectors.toUnmodifiableList()); } + @Override + public String startSession() { + return sessionManager.startSession(); + } + + @Override + public void closeSession(final String sessionId) { + sessionManager.closeSession(sessionId, SessionManager.WITH_COMMIT); + } + + @Override + public void lockAnchor(final String sessionId, final String dataspaceName, + final String anchorName, final Long timeoutInMilliseconds) { + sessionManager.lockAnchor(sessionId, dataspaceName, anchorName, timeoutInMilliseconds); + } + private static Set processAncestorXpath(final List fragmentEntities, final CpsPathQuery cpsPathQuery) { final Set ancestorXpath = new HashSet<>(); @@ -248,14 +246,7 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService final List childDataNodes = getChildDataNodes(fragmentEntity, fetchDescendantsOption); Map 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); - } + leaves = jsonObjectMapper.convertJsonString(fragmentEntity.getAttributes(), Map.class); } return new DataNodeBuilder() .withXpath(fragmentEntity.getXpath()) @@ -277,7 +268,7 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService public void updateDataLeaves(final String dataspaceName, final String anchorName, final String xpath, final Map leaves) { final FragmentEntity fragmentEntity = getFragmentByXpath(dataspaceName, anchorName, xpath); - fragmentEntity.setAttributes(GSON.toJson(leaves)); + fragmentEntity.setAttributes(jsonObjectMapper.asJsonString(leaves)); fragmentRepository.save(fragmentEntity); } @@ -295,24 +286,24 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService } } - private static void replaceDataNodeTree(final FragmentEntity existingFragmentEntity, - final DataNode submittedDataNode) { + private void replaceDataNodeTree(final FragmentEntity existingFragmentEntity, + final DataNode newDataNode) { - existingFragmentEntity.setAttributes(GSON.toJson(submittedDataNode.getLeaves())); + existingFragmentEntity.setAttributes(jsonObjectMapper.asJsonString(newDataNode.getLeaves())); final Map existingChildrenByXpath = existingFragmentEntity.getChildFragments() .stream().collect(Collectors.toMap(FragmentEntity::getXpath, childFragmentEntity -> childFragmentEntity)); - final Collection updatedChildFragments = new HashSet(); + final Collection updatedChildFragments = new HashSet<>(); - for (final DataNode submittedChildDataNode : submittedDataNode.getChildDataNodes()) { + for (final DataNode newDataNodeChild : newDataNode.getChildDataNodes()) { final FragmentEntity childFragment; - if (isNewDataNode(submittedChildDataNode, existingChildrenByXpath)) { + if (isNewDataNode(newDataNodeChild, existingChildrenByXpath)) { childFragment = convertToFragmentWithAllDescendants( - existingFragmentEntity.getDataspace(), existingFragmentEntity.getAnchor(), submittedChildDataNode); + existingFragmentEntity.getDataspace(), existingFragmentEntity.getAnchor(), newDataNodeChild); } else { - childFragment = existingChildrenByXpath.get(submittedChildDataNode.getXpath()); - replaceDataNodeTree(childFragment, submittedChildDataNode); + childFragment = existingChildrenByXpath.get(newDataNodeChild.getXpath()); + replaceDataNodeTree(childFragment, newDataNodeChild); } updatedChildFragments.add(childFragment); } @@ -322,19 +313,19 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService @Override @Transactional - public void replaceListDataNodes(final String dataspaceName, final String anchorName, final String parentNodeXpath, - final Collection replacementDataNodes) { + public void replaceListContent(final String dataspaceName, final String anchorName, final String parentNodeXpath, + final Collection newListElements) { final FragmentEntity parentEntity = getFragmentByXpath(dataspaceName, anchorName, parentNodeXpath); - final String listNodeXpathPrefix = getListNodeXpathPrefix(replacementDataNodes); + final String listElementXpathPrefix = getListElementXpathPrefix(newListElements); final Map existingListElementFragmentEntitiesByXPath = - extractListElementFragmentEntitiesByXPath(parentEntity.getChildFragments(), listNodeXpathPrefix); - removeExistingListElements(parentEntity.getChildFragments(), existingListElementFragmentEntitiesByXPath); + extractListElementFragmentEntitiesByXPath(parentEntity.getChildFragments(), listElementXpathPrefix); + deleteListElements(parentEntity.getChildFragments(), existingListElementFragmentEntitiesByXPath); final Set updatedChildFragmentEntities = new HashSet<>(); - for (final DataNode replacementDataNode : replacementDataNodes) { - final FragmentEntity existingListNodeElementEntity = - existingListElementFragmentEntitiesByXPath.get(replacementDataNode.getXpath()); - final FragmentEntity entityToBeAdded = getFragmentForReplacement(parentEntity, replacementDataNode, - existingListNodeElementEntity); + for (final DataNode newListElement : newListElements) { + final FragmentEntity existingListElementEntity = + existingListElementFragmentEntitiesByXPath.get(newListElement.getXpath()); + final FragmentEntity entityToBeAdded = getFragmentForReplacement(parentEntity, newListElement, + existingListElementEntity); updatedChildFragmentEntities.add(entityToBeAdded); } @@ -342,85 +333,144 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService fragmentRepository.save(parentEntity); } - private static void removeExistingListElements( + @Override + @Transactional + public void deleteDataNodes(final String dataspaceName, final String anchorName) { + final DataspaceEntity dataspaceEntity = dataspaceRepository.getByName(dataspaceName); + anchorRepository.findByDataspaceAndName(dataspaceEntity, anchorName) + .ifPresent( + anchorEntity -> fragmentRepository.deleteByAnchorIn(Set.of(anchorEntity))); + } + + @Override + @Transactional + public void deleteListDataNode(final String dataspaceName, final String anchorName, + final String targetXpath) { + deleteDataNode(dataspaceName, anchorName, targetXpath, true); + } + + @Override + @Transactional + public void deleteDataNode(final String dataspaceName, final String anchorName, final String targetXpath) { + deleteDataNode(dataspaceName, anchorName, targetXpath, false); + } + + private void deleteDataNode(final String dataspaceName, final String anchorName, final String targetXpath, + final boolean onlySupportListNodeDeletion) { + final String parentNodeXpath; + FragmentEntity parentFragmentEntity = null; + boolean targetDeleted = false; + if (isRootXpath(targetXpath)) { + deleteDataNodes(dataspaceName, anchorName); + targetDeleted = true; + } else { + if (isRootContainerNodeXpath(targetXpath)) { + parentNodeXpath = targetXpath; + } else { + parentNodeXpath = targetXpath.substring(0, targetXpath.lastIndexOf('/')); + } + parentFragmentEntity = getFragmentByXpath(dataspaceName, anchorName, parentNodeXpath); + final String lastXpathElement = targetXpath.substring(targetXpath.lastIndexOf('/')); + final boolean isListElement = REG_EX_PATTERN_FOR_LIST_ELEMENT_KEY_PREDICATE + .matcher(lastXpathElement).find(); + if (isListElement) { + targetDeleted = deleteDataNode(parentFragmentEntity, targetXpath); + } else { + targetDeleted = deleteAllListElements(parentFragmentEntity, targetXpath); + final boolean tryToDeleteDataNode = !targetDeleted && !onlySupportListNodeDeletion; + if (tryToDeleteDataNode) { + targetDeleted = deleteDataNode(parentFragmentEntity, targetXpath); + } + } + } + if (!targetDeleted) { + final String additionalInformation = onlySupportListNodeDeletion + ? "The target is probably not a List." : ""; + throw new DataNodeNotFoundException(parentFragmentEntity.getDataspace().getName(), + parentFragmentEntity.getAnchor().getName(), targetXpath, additionalInformation); + } + } + + private boolean deleteDataNode(final FragmentEntity parentFragmentEntity, final String targetXpath) { + final String normalizedTargetXpath = CpsPathUtil.getNormalizedXpath(targetXpath); + if (parentFragmentEntity.getXpath().equals(normalizedTargetXpath)) { + fragmentRepository.delete(parentFragmentEntity); + return true; + } + if (parentFragmentEntity.getChildFragments() + .removeIf(fragment -> fragment.getXpath().equals(normalizedTargetXpath))) { + fragmentRepository.save(parentFragmentEntity); + return true; + } + return false; + } + + private boolean deleteAllListElements(final FragmentEntity parentFragmentEntity, final String listXpath) { + final String normalizedListXpath = CpsPathUtil.getNormalizedXpath(listXpath); + final String deleteTargetXpathPrefix = normalizedListXpath + "["; + if (parentFragmentEntity.getChildFragments() + .removeIf(fragment -> fragment.getXpath().startsWith(deleteTargetXpathPrefix))) { + fragmentRepository.save(parentFragmentEntity); + return true; + } + return false; + } + + private static void deleteListElements( final Collection fragmentEntities, final Map existingListElementFragmentEntitiesByXPath) { fragmentEntities.removeAll(existingListElementFragmentEntitiesByXPath.values()); } - private static String getListNodeXpathPrefix(final Collection replacementDataNodes) { - final String firstChildNodeXpath = replacementDataNodes.iterator().next().getXpath(); - return firstChildNodeXpath.substring(0, firstChildNodeXpath.lastIndexOf("[") + 1); + private static String getListElementXpathPrefix(final Collection newListElements) { + if (newListElements.isEmpty()) { + throw new CpsAdminException("Invalid list replacement", + "Cannot replace list elements with empty collection"); + } + final String firstChildNodeXpath = newListElements.iterator().next().getXpath(); + return firstChildNodeXpath.substring(0, firstChildNodeXpath.lastIndexOf('[') + 1); } - private static FragmentEntity getFragmentForReplacement(final FragmentEntity parentEntity, - final DataNode replacementDataNode, - final FragmentEntity existingListNodeElementEntity) { - if (existingListNodeElementEntity == null) { + private FragmentEntity getFragmentForReplacement(final FragmentEntity parentEntity, + final DataNode newListElement, + final FragmentEntity existingListElementEntity) { + if (existingListElementEntity == null) { return convertToFragmentWithAllDescendants( - parentEntity.getDataspace(), parentEntity.getAnchor(), replacementDataNode); + parentEntity.getDataspace(), parentEntity.getAnchor(), newListElement); } - if (replacementDataNode.getChildDataNodes().isEmpty()) { - copyAttributesFromReplacementDataNode(existingListNodeElementEntity, replacementDataNode); - existingListNodeElementEntity.getChildFragments().clear(); + if (newListElement.getChildDataNodes().isEmpty()) { + copyAttributesFromNewListElement(existingListElementEntity, newListElement); + existingListElementEntity.getChildFragments().clear(); } else { - replaceDataNodeTree(existingListNodeElementEntity, replacementDataNode); + replaceDataNodeTree(existingListElementEntity, newListElement); } - return existingListNodeElementEntity; + return existingListElementEntity; } private static boolean isNewDataNode(final DataNode replacementDataNode, - final Map existingListNodeElementsByXpath) { - return !existingListNodeElementsByXpath.containsKey(replacementDataNode.getXpath()); + final Map existingListElementsByXpath) { + return !existingListElementsByXpath.containsKey(replacementDataNode.getXpath()); + } + + private static boolean isRootContainerNodeXpath(final String xpath) { + return 0 == xpath.lastIndexOf('/'); } - private static void copyAttributesFromReplacementDataNode(final FragmentEntity existingListNodeElementEntity, - final DataNode replacementDataNode) { + private void copyAttributesFromNewListElement(final FragmentEntity existingListElementEntity, + final DataNode newListElement) { final FragmentEntity replacementFragmentEntity = - FragmentEntity.builder().attributes(GSON.toJson(replacementDataNode.getLeaves())).build(); - existingListNodeElementEntity.setAttributes(replacementFragmentEntity.getAttributes()); + FragmentEntity.builder().attributes(jsonObjectMapper.asJsonString( + newListElement.getLeaves())).build(); + existingListElementEntity.setAttributes(replacementFragmentEntity.getAttributes()); } private static Map extractListElementFragmentEntitiesByXPath( - final Set childEntities, final String listNodeXpathPrefix) { + final Set childEntities, final String listElementXpathPrefix) { return childEntities.stream() - .filter(fragmentEntity -> fragmentEntity.getXpath().startsWith(listNodeXpathPrefix)) + .filter(fragmentEntity -> fragmentEntity.getXpath().startsWith(listElementXpathPrefix)) .collect(Collectors.toMap(FragmentEntity::getXpath, fragmentEntity -> fragmentEntity)); } - @Override - @Transactional - public void deleteListDataNodes(final String dataspaceName, final String anchorName, final String listNodeXpath) { - final String parentNodeXpath = listNodeXpath.substring(0, listNodeXpath.lastIndexOf('/')); - final FragmentEntity parentEntity = getFragmentByXpath(dataspaceName, anchorName, parentNodeXpath); - final String 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 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); - } - } - private static boolean isRootXpath(final String xpath) { return "/".equals(xpath) || "".equals(xpath); }