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

kwin pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-api.git


The following commit(s) were added to refs/heads/master by this push:
     new 0fc8622  SLING-12781 Expose resource type via getValueMap() (#59)
0fc8622 is described below

commit 0fc8622cf1fdb0236039586e11da5fc6a044563b
Author: Konrad Windszus <[email protected]>
AuthorDate: Tue Jun 17 14:06:15 2025 +0200

    SLING-12781 Expose resource type via getValueMap() (#59)
---
 .../sling/api/resource/ModifiableValueMap.java     |  7 +-
 .../org/apache/sling/api/resource/Resource.java    |  1 +
 .../sling/api/resource/ResourceResolver.java       | 21 ++++--
 .../sling/api/resource/SyntheticResource.java      | 25 +++++-
 .../sling/api/resource/SyntheticResourceTest.java  | 88 ++++++++++++++++++++++
 5 files changed, 132 insertions(+), 10 deletions(-)

diff --git 
a/src/main/java/org/apache/sling/api/resource/ModifiableValueMap.java 
b/src/main/java/org/apache/sling/api/resource/ModifiableValueMap.java
index a1a8209..572d880 100644
--- a/src/main/java/org/apache/sling/api/resource/ModifiableValueMap.java
+++ b/src/main/java/org/apache/sling/api/resource/ModifiableValueMap.java
@@ -59,11 +59,12 @@ import org.osgi.annotation.versioning.ConsumerType;
  * </ul>
  * <p>
  *
- * A modifiable value map should value {@link 
ResourceResolver#PROPERTY_RESOURCE_TYPE}
- * to set the resource type of a resource.
+ * A modifiable value map should consider property {@value 
ResourceResolver#PROPERTY_RESOURCE_TYPE}
+ * to set the resource type of a resource and {@value 
ResourceResolver#PROPERTY_RESOURCE_SUPER_TYPE}
+ * to set the optional super type.
  * <p>
  * A modifiable value map must not support deep writes. A call of a 
modification method
- * with a path should result in an IllegalArgumentException.
+ * with a path (i.e. a key containing a slash) should result in an {@link 
IllegalArgumentException}.
  *
  * @since 2.2  (Sling API Bundle 2.2.0)
  */
diff --git a/src/main/java/org/apache/sling/api/resource/Resource.java 
b/src/main/java/org/apache/sling/api/resource/Resource.java
index d2b6308..c15fcbf 100644
--- a/src/main/java/org/apache/sling/api/resource/Resource.java
+++ b/src/main/java/org/apache/sling/api/resource/Resource.java
@@ -222,6 +222,7 @@ public interface Resource extends Adaptable {
     /**
      * Returns a value map for this resource.
      * The value map allows to read the properties of the resource.
+     * Each map is containing at least the mandatory property {@value 
ResourceResolver#PROPERTY_RESOURCE_TYPE}.
      * @return A value map
      * @since 2.5 (Sling API Bundle 2.7.0)
      */
diff --git a/src/main/java/org/apache/sling/api/resource/ResourceResolver.java 
b/src/main/java/org/apache/sling/api/resource/ResourceResolver.java
index 51339d2..5f26517 100644
--- a/src/main/java/org/apache/sling/api/resource/ResourceResolver.java
+++ b/src/main/java/org/apache/sling/api/resource/ResourceResolver.java
@@ -168,16 +168,23 @@ public interface ResourceResolver extends Adaptable, 
Closeable {
     String USER_IMPERSONATOR = "user.impersonator";
 
     /**
-     * This is the suggested property to be used for setting the resource type
+     * This is the property to be used for setting the resource type
      * of a resource during either creation ({@link #create(Resource, String, 
Map)})
-     * or modifying ({@link ModifiableValueMap}).
-     * However the exact way to set the resource type of a resource is defined
-     * by the underlying resource provider. It should value this property but
-     * is not required to do so.
+     * or modifying ({@link ModifiableValueMap}). This property is usually 
also exposed
+     * via {@link Resource#getValueMap()}.
      * @since 2.3 (Sling API Bundle 2.4.0)
      */
     String PROPERTY_RESOURCE_TYPE = "sling:resourceType";
 
+    /**
+     * This is property to be used for setting the resource super type
+     * of a resource during either creation ({@link #create(Resource, String, 
Map)})
+     * or modifying ({@link ModifiableValueMap}). This property is usually 
also exposed
+     * via {@link Resource#getValueMap()}.
+     * @since 2.15.0 (Sling API Bundle 3.0.0)
+     */
+    String PROPERTY_RESOURCE_SUPER_TYPE = "sling:resourceSuperType";
+
     /**
      * Resolves the resource from the given <code>absPath</code> optionally
      * taking <code>HttpServletRequest</code> into account, such as the value 
of
@@ -769,10 +776,12 @@ public interface ResourceResolver extends Adaptable, 
Closeable {
     /**
      * Add a child resource to the given parent resource.
      * The changes are transient and require a call to {@link #commit()} for 
persisting.
+     * The mandatory resource type is either determined by the property 
{@value ResourceResolver#PROPERTY_RESOURCE_TYPE} or set by the underlying 
resource provider to some value.
+     * The optional resource super type is determined by the property {@value 
ResourceResolver#PROPERTY_RESOURCE_SUPER_TYPE}.
      *
      * @param parent The parent resource
      * @param name   The name of the child resource - this is a plain name, 
not a path!
-     * @param properties Optional properties for the resource
+     * @param properties Optional properties for the resource (may be 
<code>null</code>).
      * @return The new resource
      *
      * @throws NullPointerException if the resource parameter or name 
parameter is null
diff --git a/src/main/java/org/apache/sling/api/resource/SyntheticResource.java 
b/src/main/java/org/apache/sling/api/resource/SyntheticResource.java
index d869aa0..4fd047b 100644
--- a/src/main/java/org/apache/sling/api/resource/SyntheticResource.java
+++ b/src/main/java/org/apache/sling/api/resource/SyntheticResource.java
@@ -18,12 +18,17 @@
  */
 package org.apache.sling.api.resource;
 
+import java.util.Map;
+
+import org.apache.sling.api.wrappers.ValueMapDecorator;
+import org.apache.sling.api.wrappers.ValueMapUtil;
 import org.jetbrains.annotations.NotNull;
 
 /**
  * The <code>SyntheticResource</code> class is a simple implementation of the
  * <code>Resource</code> interface which may be used to provide a resource
- * object which has no actual resource data.
+ * object which has no actual resource data (except for the mandatory property
+ * {@value ResourceResolver#PROPERTY_RESOURCE_TYPE}).
  */
 public class SyntheticResource extends AbstractResource {
 
@@ -112,6 +117,24 @@ public class SyntheticResource extends AbstractResource {
         return resourceResolver;
     }
 
+    /**
+     * Merges the original value map with one containing the single property 
{@value ResourceResolver#PROPERTY_RESOURCE_TYPE}.
+     */
+    @Override
+    public <AdapterType> AdapterType adaptTo(Class<AdapterType> type) {
+        if (type == ValueMap.class) {
+            ValueMap newValueMap = new 
ValueMapDecorator(Map.of(ResourceResolver.PROPERTY_RESOURCE_TYPE, 
resourceType));
+            ValueMap originalValueMap = super.adaptTo(ValueMap.class);
+            if (originalValueMap != null) {
+                // the one with the resource type takes precedence, i.e. a 
property with the same name in the original
+                // value map is hidden
+                return (AdapterType) ValueMapUtil.merge(newValueMap, 
originalValueMap);
+            }
+            return (AdapterType) newValueMap;
+        }
+        return super.adaptTo(type);
+    }
+
     @Override
     public String toString() {
         return getClass().getSimpleName() + ", type=" + getResourceType() + ", 
path=" + getPath();
diff --git 
a/src/test/java/org/apache/sling/api/resource/SyntheticResourceTest.java 
b/src/test/java/org/apache/sling/api/resource/SyntheticResourceTest.java
new file mode 100644
index 0000000..a7cdd4f
--- /dev/null
+++ b/src/test/java/org/apache/sling/api/resource/SyntheticResourceTest.java
@@ -0,0 +1,88 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.api.resource;
+
+import java.util.Map;
+
+import org.apache.sling.api.adapter.AdapterManager;
+import org.apache.sling.api.adapter.SlingAdaptable;
+import org.apache.sling.api.wrappers.ValueMapDecorator;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.Mockito.mock;
+
+public class SyntheticResourceTest {
+
+    @Test
+    public void testSyntheticResourceCreation() {
+        ResourceResolver resourceResolver = mock(ResourceResolver.class);
+        String path = "/content/synthetic";
+        String resourceType = "/apps/my/resourcetype";
+
+        SyntheticResource syntheticResource = new 
SyntheticResource(resourceResolver, path, resourceType);
+
+        assertEquals(path, syntheticResource.getPath());
+        assertEquals(resourceType, syntheticResource.getResourceType());
+        assertNotNull(syntheticResource.getResourceMetadata());
+        assertEquals(path, 
syntheticResource.getResourceMetadata().getResolutionPath());
+        ValueMap vm = syntheticResource.getValueMap();
+        assertNotNull(vm);
+        assertEquals(1, vm.size());
+        assertEquals(resourceType, 
vm.get(ResourceResolver.PROPERTY_RESOURCE_TYPE, String.class));
+    }
+
+    @Test
+    public void testDerivedSyntheticWithProperties() {
+        ResourceResolver resourceResolver = mock(ResourceResolver.class);
+        String path = "/content/synthetic";
+        String resourceType = "/apps/my/resourcetype";
+
+        SyntheticResource syntheticResource = new 
SyntheticResource(resourceResolver, path, resourceType);
+        AdapterManager adapterMgr = new AdapterManager() {
+            @SuppressWarnings("unchecked")
+            @Override
+            public <AdapterType> @Nullable AdapterType getAdapter(
+                    @NotNull Object adaptable, @NotNull Class<AdapterType> 
type) {
+                if (adaptable instanceof SyntheticResource && type == 
ValueMap.class) {
+                    return (AdapterType)
+                            new ValueMapDecorator(Map.of("foo", "bar", 
ResourceResolver.PROPERTY_RESOURCE_TYPE, "baz"));
+                }
+                return null;
+            }
+        };
+        SlingAdaptable.setAdapterManager(adapterMgr);
+        try {
+            assertEquals(path, syntheticResource.getPath());
+            assertEquals(resourceType, syntheticResource.getResourceType());
+            assertNotNull(syntheticResource.getResourceMetadata());
+            assertEquals(path, 
syntheticResource.getResourceMetadata().getResolutionPath());
+            ValueMap vm = syntheticResource.getValueMap();
+            assertNotNull(vm);
+            assertEquals(2, vm.size());
+            assertEquals(resourceType, 
vm.get(ResourceResolver.PROPERTY_RESOURCE_TYPE, String.class));
+            assertEquals("bar", vm.get("foo", String.class));
+        } finally {
+            SlingAdaptable.unsetAdapterManager(adapterMgr);
+        }
+    }
+}

Reply via email to