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);
+ }
+ }
+}