SLING-8848 : Wrong calculation for copy/move whether single provider can be used. Patch by Christian Keller
diff --git a/src/main/java/org/apache/sling/resourceresolver/impl/helper/ResourceResolverControl.java b/src/main/java/org/apache/sling/resourceresolver/impl/helper/ResourceResolverControl.java
index a5d591b..2eefd39 100644
--- a/src/main/java/org/apache/sling/resourceresolver/impl/helper/ResourceResolverControl.java
+++ b/src/main/java/org/apache/sling/resourceresolver/impl/helper/ResourceResolverControl.java
@@ -45,10 +45,12 @@
 import org.apache.sling.api.resource.ValueMap;
 import org.apache.sling.api.resource.path.PathBuilder;
 import org.apache.sling.resourceresolver.impl.providers.ResourceProviderHandler;
+import org.apache.sling.resourceresolver.impl.providers.ResourceProviderInfo;
 import org.apache.sling.resourceresolver.impl.providers.ResourceProviderStorage;
 import org.apache.sling.resourceresolver.impl.providers.ResourceProviderStorageProvider;
 import org.apache.sling.resourceresolver.impl.providers.stateful.AuthenticatedResourceProvider;
 import org.apache.sling.resourceresolver.impl.providers.tree.Node;
+import org.apache.sling.resourceresolver.impl.providers.tree.PathTree;
 import org.apache.sling.spi.resource.provider.ResourceProvider;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
@@ -499,71 +501,66 @@
         return null;
     }
 
-    private AuthenticatedResourceProvider checkSourceAndDest(final ResourceResolverContext context,
+    private AuthenticatedResourceProvider checkExistence(final ResourceResolverContext context, final String path,
+            final String type) throws PersistenceException {
+        final Node<ResourceProviderHandler> node = getResourceProviderStorage().getTree().getBestMatchingNode(path);
+        if (node == null) {
+            throw new PersistenceException(type.concat(" resource does not exist."), null, path, null);
+        }
+        AuthenticatedResourceProvider provider = null;
+        try {
+            provider = context.getProviderManager().getOrCreateProvider(node.getValue(), this);
+        } catch (LoginException e) {
+            // ignore
+        }
+        if (provider == null) {
+            throw new PersistenceException(type.concat(" resource does not exist."), null, path, null);
+        }
+        final Resource rsrc = provider.getResource(path, null, null);
+        if (rsrc == null) {
+            throw new PersistenceException(type.concat(" resource does not exist."), null, path, null);
+        }
+        return provider;
+    }
+
+    public AuthenticatedResourceProvider checkSourceAndDest(final ResourceResolverContext context,
             final String srcAbsPath, final String destAbsPath) throws PersistenceException {
         // check source
-        final Node<ResourceProviderHandler> srcNode = getResourceProviderStorage().getTree().getBestMatchingNode(srcAbsPath);
-        if ( srcNode == null ) {
-            throw new PersistenceException("Source resource does not exist.", null, srcAbsPath, null);
-        }
-        AuthenticatedResourceProvider srcProvider = null;
-        try {
-            srcProvider = context.getProviderManager().getOrCreateProvider(srcNode.getValue(), this);
-        } catch (LoginException e) {
-            // ignore
-        }
-        if ( srcProvider == null ) {
-            throw new PersistenceException("Source resource does not exist.", null, srcAbsPath, null);
-        }
-        final Resource srcResource = srcProvider.getResource(srcAbsPath, null, null);
-        if ( srcResource == null ) {
-            throw new PersistenceException("Source resource does not exist.", null, srcAbsPath, null);
-        }
-
+        final AuthenticatedResourceProvider srcProvider = checkExistence(context, srcAbsPath, "Source");
         // check destination
-        final Node<ResourceProviderHandler> destNode = getResourceProviderStorage().getTree().getBestMatchingNode(destAbsPath);
-        if ( destNode == null ) {
-            throw new PersistenceException("Destination resource does not exist.", null, destAbsPath, null);
-        }
-        AuthenticatedResourceProvider destProvider = null;
-        try {
-            destProvider = context.getProviderManager().getOrCreateProvider(destNode.getValue(), this);
-        } catch (LoginException e) {
-            // ignore
-        }
-        if ( destProvider == null ) {
-            throw new PersistenceException("Destination resource does not exist.", null, destAbsPath, null);
-        }
-        final Resource destResource = destProvider.getResource(destAbsPath, null, null);
-        if ( destResource == null ) {
-            throw new PersistenceException("Destination resource does not exist.", null, destAbsPath, null);
-        }
+        final AuthenticatedResourceProvider destProvider = checkExistence(context, destAbsPath, "Destination");
 
         // check for sub providers of src and dest
-        if ( srcProvider == destProvider && !collectProviders(context, srcNode) && !collectProviders(context, destNode) ) {
-            return srcProvider;
+        if (srcProvider == destProvider) {
+            final PathTree<ResourceProviderHandler> providerTree = getResourceProviderStorage().getTree();
+            final boolean sourceHasProvider = hasSubProvider(context, providerTree.getNode(srcAbsPath));
+            final boolean destHasProvider = hasSubProvider(context, providerTree.getNode(destAbsPath));
+            if (!(sourceHasProvider || destHasProvider)) {
+                return srcProvider;
+            }
         }
         return null;
     }
 
-    private boolean collectProviders(final ResourceResolverContext context,
-            final Node<ResourceProviderHandler> parent) {
-        boolean hasMoreProviders = false;
-        for (final Entry<String, Node<ResourceProviderHandler>> entry : parent.getChildren().entrySet()) {
-            if ( entry.getValue().getValue() != null ) {
-                try {
-                    context.getProviderManager().getOrCreateProvider(entry.getValue().getValue(), this);
-                    hasMoreProviders = true;
-                } catch ( final LoginException ignore) {
-                    // ignore
+    private boolean hasSubProvider(ResourceResolverContext context, Node<ResourceProviderHandler> node) {
+        if (node != null) {
+            for (final Node<ResourceProviderHandler> childNode : node.getChildren().values()) {
+                final ResourceProviderHandler handler = childNode.getValue();
+                if (handler != null) {
+                    try {
+                        context.getProviderManager().getOrCreateProvider(handler, this);
+                        return true;
+                    } catch (final LoginException ignore) {
+                        // ignore
+                    }
                 }
-            }
-            if ( collectProviders(context, entry.getValue())) {
-                hasMoreProviders = true;
+                if (hasSubProvider(context, childNode)) {
+                    return true;
+                }
             }
         }
 
-        return hasMoreProviders;
+        return false;
     }
 
     private void copy(final ResourceResolverContext context, final Resource src, final String dstPath, final List<Resource> newNodes) throws PersistenceException {
diff --git a/src/test/java/org/apache/sling/resourceresolver/impl/helper/ResourceResolverControlTest.java b/src/test/java/org/apache/sling/resourceresolver/impl/helper/ResourceResolverControlTest.java
index 45c3940..93957a2 100644
--- a/src/test/java/org/apache/sling/resourceresolver/impl/helper/ResourceResolverControlTest.java
+++ b/src/test/java/org/apache/sling/resourceresolver/impl/helper/ResourceResolverControlTest.java
@@ -340,6 +340,23 @@
     }
 
     /**
+     * Checks that its correctly calculated whether a copy/move can be done with the
+     * same provider
+     *
+     * @throws PersistenceException
+     */
+    @Test
+    public void checkProvidersForCopyMove() throws PersistenceException {
+        // first check same provider at root
+        configureResourceAt(rootProvider, "/a");
+        configureResourceAt(rootProvider, "/b");
+        assertNotNull(crp.checkSourceAndDest(context, "/a", "/b"));
+
+        // second check different providers
+        assertNull(crp.checkSourceAndDest(context, "/some/path/object", "/"));
+    }
+
+    /**
      * Verifies copying resources between the same ResourceProvider
      *
      * @throws PersistenceException persistence exception