You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by jo...@apache.org on 2022/09/24 17:42:16 UTC

[sling-org-apache-sling-servlets-resolver] 04/05: Reimplement the LocationIterator logic

This is an automated email from the ASF dual-hosted git repository.

joerghoh pushed a commit to branch SLING-11558-remove-iterator
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-servlets-resolver.git

commit 4dc385b1b7d62e65d55b6760db7f238cdda21146
Author: Jörg Hoh <jo...@joerghoh.de>
AuthorDate: Sat Sep 24 19:32:27 2022 +0200

    Reimplement the LocationIterator logic
    
    This new logic in the LocationCollector reimplements the logic of the
    LocationIterator, but instead of a iterator it returns a simple list;
    this makes the whole logic much easier to understand.
    
    Code from the LocationIterator has been re-used 1:1, most notably the
    methods "getResourceSuperType". Only some minor renamings have been made
    to reflect the changed field names.
---
 .../internal/helper/LocationCollector.java         | 192 ++++++++++++++++++++-
 1 file changed, 183 insertions(+), 9 deletions(-)

diff --git a/src/main/java/org/apache/sling/servlets/resolver/internal/helper/LocationCollector.java b/src/main/java/org/apache/sling/servlets/resolver/internal/helper/LocationCollector.java
index 578137e..a67679a 100644
--- a/src/main/java/org/apache/sling/servlets/resolver/internal/helper/LocationCollector.java
+++ b/src/main/java/org/apache/sling/servlets/resolver/internal/helper/LocationCollector.java
@@ -19,26 +19,200 @@
 package org.apache.sling.servlets.resolver.internal.helper;
 
 import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Set;
 
+import org.apache.commons.lang3.StringUtils;
+import org.apache.sling.api.resource.Resource;
 import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.api.resource.ResourceUtil;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.slf4j.LoggerFactory;
+
+import static org.apache.sling.api.servlets.ServletResolverConstants.DEFAULT_RESOURCE_TYPE;
+
+/**
+ * The <code>LocationCollector</code> provides access to an ordered collection
+ * of absolute paths containing potential request handling. The primary order of
+ * the collection is the resource type hierarchy with the base resource type at
+ * the top. The secondary order is the search path retrieved from the resource
+ * resolver.
+ * <p>
+ * Example: For a node type hierarchy "sample" > "super" > "default" and a
+ * search path of [ "/apps", "/libs" ], the iterator would provide access to the
+ * following list of paths:
+ * <ol>
+ * <li><code>/apps/sample</code></li>
+ * <li><code>/libs/sample</code></li>
+ * <li><code>/apps/super</code></li>
+ * <li><code>/libs/super</code></li>
+ * <li><code>/apps/default</code></li>
+ * <li><code>/libs/default</code></li>
+ * </ol>
+ */
 
 public class LocationCollector {
-    
-    
+
     public static List<String> getLocations(String resourceType, String resourceSuperType, String baseResourceType,
             ResourceResolver resolver) {
+        LocationCollector collector = new LocationCollector(resourceType, resourceSuperType, baseResourceType,
+                resolver);
+        return collector.getResolvedLocations();
+    }
+
+    // The search path of the resource resolver
+    private final String[] searchPath;
+
+    private final ResourceResolver resolver;
+    private final String baseResourceType;
+    private final String resourceType;
+    private final String resourceSuperType;
+
+    /** Set of used resource types to detect a circular resource type hierarchy. */
+    private final Set<String> usedResourceTypes = new HashSet<>();
+    
+    private List<String> result = new ArrayList<>();
+
+    private LocationCollector(String resourceType, String resourceSuperType, String baseResourceType,
+            ResourceResolver resolver) {
+
+        this.resourceType = resourceType;
+        this.resourceSuperType = resourceSuperType;
+        this.baseResourceType = baseResourceType;
+        this.resolver = resolver;
+
+        String[] tmpPath = resolver.getSearchPath();
+        if (tmpPath.length == 0) {
+            tmpPath = new String[] { "/" };
+        }
+        searchPath = tmpPath;
+        this.usedResourceTypes.add(this.resourceType);
+        collectPaths();
+    }
+
+    private List<String> getResolvedLocations() {
+        return result;
+    }
+
+    /**
+     * Collect all resource types
+     */
+    private void collectPaths() {
         
-        List<String> result = new ArrayList<>();
-        
-        final Iterator<String> locations = new LocationIterator(resourceType, resourceSuperType,
-                baseResourceType, resolver);
+        String rt = this.resourceType;
+        do {
+            String superType = handleResourceType(rt);
+            rt = superType;
+        } while (rt != null);
         
-        while (locations.hasNext()) {
-            result.add(locations.next());
+        // add default resourceTypes
+        final String defaultResourceTypeSuffix;
+        boolean blankResourceType = StringUtils.isBlank(resourceType);
+        if (blankResourceType) {
+            defaultResourceTypeSuffix = "";
+        } else {
+            defaultResourceTypeSuffix = this.baseResourceType;
         }
-        return result;
+        for (String spath : searchPath) {
+            result.add(spath + defaultResourceTypeSuffix);
+        }
+    }
+    
+    /**
+     * Add all necessary path entries to the result list, and return the resourceSuperType
+     * for the given resourceType
+     * @param resourceType the resourceType
+     * @return the resourceSuperType or null if the given resourceType does not have a resourceSuperType
+     */
+    @Nullable String handleResourceType (String resourceType) {
+        boolean isBlank = StringUtils.isBlank(resourceType);
+        boolean isAbsoluteResourceType = resourceType.startsWith("/");
+        String resourceSuperType = null;
+        if (!isBlank) {
+            if (isAbsoluteResourceType) {
+                result.add(ResourceUtil.resourceTypeToPath(resourceType));
+            } else {
+                for (String spath : searchPath) {
+                    result.add(spath + ResourceUtil.resourceTypeToPath(resourceType));
+                }
+                
+            }
+            resourceSuperType = getResourceSuperType(resourceType);
+        }
+        return resourceSuperType;
+    }
+    
+
+    /**
+     * Returns the resource super type of the given resource type:
+     * <ol>
+     * <li>If the resource type is the base resource type <code>null</code>
+     * is returned.</li>
+     * <li>If the resource type is the resource type of the resource the
+     * resource super type from the resource is returned.</li>
+     * <li>Otherwise the resource super type is tried to be found in the
+     * resource tree. If one is found, it is returned.</li>
+     * <li>Otherwise the base resource type is returned.</li>
+     * </ol>
+     */
+    private String getResourceSuperType(String resourceType) {
+
+        // if the current resource type is the default value, there are no more
+        if (resourceType.equals(baseResourceType)) {
+            return null;
+        }
+
+        // get the super type of the current resource type
+        String superType = null;
+        if (resourceType.equals(this.resourceType)
+                && this.resourceSuperType != null ) {
+            superType = this.resourceSuperType;
+        } else {
+            superType = getResourceSuperType(resolver, resourceType);
+        }
+
+        // detect circular dependency
+        if ( superType != null ) {
+            if ( this.usedResourceTypes.contains(superType) ) {
+                LoggerFactory.getLogger(this.getClass()).error("Circular dependency in resource type hierarchy detected! Check super types of {}", superType);
+                superType = null;
+            } else {
+                this.usedResourceTypes.add(superType);
+            }
+        }
+        return superType;
     }
 
+    // this method is largely duplicated from ResourceUtil
+    private String getResourceSuperType(final ResourceResolver resourceResolver,
+                                        final String resourceType) {
+        // normalize resource type to a path string
+        final String rtPath = ResourceUtil.resourceTypeToPath(resourceType);
+        // get the resource type resource and check its super type
+        String resourceSuperType = null;
+        // if the path is absolute, use it directly
+        if ( rtPath.startsWith("/") ) {
+            final String candidatePath = rtPath;
+
+            final Resource rtResource = resourceResolver.getResource(candidatePath);
+            if ( rtResource != null ) {
+                resourceSuperType = rtResource.getResourceSuperType();
+            }
+
+        } else {
+            // if the path is relative we use the search paths
+            for (final String path : this.searchPath) {
+                final String candidatePath = path + rtPath;
+                final Resource rtResource = resourceResolver.getResource(candidatePath);
+                if ( rtResource != null && rtResource.getResourceSuperType() != null ) {
+                    resourceSuperType = rtResource.getResourceSuperType();
+                    break;
+                }
+            }
+        }
+        return resourceSuperType;
+    }
 }