You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ro...@apache.org on 2017/11/07 09:55:21 UTC
[sling-org-apache-sling-models-impl] 08/16: SLING-3516 - adding
ability to inject a list of grandchild resources. Thanks to Igor Sechyn for
the initial patch!
This is an automated email from the ASF dual-hosted git repository.
rombert pushed a commit to annotated tag org.apache.sling.models.impl-1.0.6
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-models-impl.git
commit 0df8a43a76ae0892ccacaa9f33d8bb7dc9704e13
Author: Justin Edelson <ju...@apache.org>
AuthorDate: Tue Jun 17 19:53:52 2014 +0000
SLING-3516 - adding ability to inject a list of grandchild resources. Thanks to Igor Sechyn for the initial patch!
git-svn-id: https://svn.apache.org/repos/asf/sling/trunk/bundles/extensions/models/impl@1603277 13f79535-47bb-0310-9956-ffa450edef68
---
.../sling/models/impl/ModelAdapterFactory.java | 52 +++++++++++++++---
.../impl/injectors/ChildResourceInjector.java | 50 ++++++++++++++++-
.../models/impl/ResourceModelClassesTest.java | 64 +++++++++++++++++-----
.../testmodels/classes/ChildResourceModel.java | 19 ++++++-
.../models/testmodels/classes/ParentModel.java | 17 ++++++
5 files changed, 180 insertions(+), 22 deletions(-)
diff --git a/src/main/java/org/apache/sling/models/impl/ModelAdapterFactory.java b/src/main/java/org/apache/sling/models/impl/ModelAdapterFactory.java
index effe935..abdd732 100644
--- a/src/main/java/org/apache/sling/models/impl/ModelAdapterFactory.java
+++ b/src/main/java/org/apache/sling/models/impl/ModelAdapterFactory.java
@@ -25,15 +25,18 @@ import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
+import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -622,10 +625,31 @@ public class ModelAdapterFactory implements AdapterFactory, Runnable {
private static boolean setField(Field field, Object createdObject, Object value) {
if (value != null) {
- if (!isAcceptableType(field.getType(), value) && value instanceof Adaptable) {
- value = ((Adaptable) value).adaptTo(field.getType());
- if (value == null) {
- return false;
+ if (!isAcceptableType(field.getType(), field.getGenericType(), value)) {
+ Class<?> declaredType = field.getType();
+ Type genericType = field.getGenericType();
+ if (value instanceof Adaptable) {
+ value = ((Adaptable) value).adaptTo(field.getType());
+ if (value == null) {
+ return false;
+ }
+ } else if (genericType instanceof ParameterizedType) {
+ ParameterizedType type = (ParameterizedType) genericType;
+ Class<?> collectionType = (Class<?>) declaredType;
+ if (value instanceof Collection &&
+ (collectionType.equals(Collection.class) || collectionType.equals(List.class)) &&
+ type.getActualTypeArguments().length == 1) {
+ List<Object> result = new ArrayList<Object>();
+ for (Object valueObject : (Collection<?>) value) {
+ if (valueObject instanceof Adaptable) {
+ Object adapted = ((Adaptable) valueObject).adaptTo((Class<?>) type.getActualTypeArguments()[0]);
+ if (adapted != null) {
+ result.add(adapted);
+ }
+ }
+ }
+ value = result;
+ }
}
}
boolean accessible = field.isAccessible();
@@ -650,7 +674,7 @@ public class ModelAdapterFactory implements AdapterFactory, Runnable {
private static boolean setMethod(Method method, Map<Method, Object> methods, Object value) {
if (value != null) {
- if (!isAcceptableType(method.getReturnType(), value) && value instanceof Adaptable) {
+ if (!isAcceptableType(method.getReturnType(), method.getGenericReturnType(), value) && value instanceof Adaptable) {
value = ((Adaptable) value).adaptTo(method.getReturnType());
if (value == null) {
return false;
@@ -663,9 +687,23 @@ public class ModelAdapterFactory implements AdapterFactory, Runnable {
}
}
- private static boolean isAcceptableType(Class<?> type, Object value) {
+ private static boolean isAcceptableType(Class<?> type, Type genericType, Object value) {
if (type.isInstance(value)) {
- return true;
+ if ((type == Collection.class || type == List.class) && genericType instanceof ParameterizedType &&
+ value instanceof Collection) {
+ Iterator<?> it = ((Collection<?>) value).iterator();
+ if (!it.hasNext()) {
+ // empty collection, so it doesn't really matter
+ return true;
+ } else {
+ // this is not an ideal way to get the actual component type, but erasure...
+ Class<?> actualComponentType = it.next().getClass();
+ Class<?> desiredComponentType = (Class<?>) ((ParameterizedType) genericType).getActualTypeArguments()[0];
+ return desiredComponentType.isAssignableFrom(actualComponentType);
+ }
+ } else {
+ return true;
+ }
}
if (type == Integer.TYPE) {
diff --git a/src/main/java/org/apache/sling/models/impl/injectors/ChildResourceInjector.java b/src/main/java/org/apache/sling/models/impl/injectors/ChildResourceInjector.java
index 500473b..53c5ba8 100644
--- a/src/main/java/org/apache/sling/models/impl/injectors/ChildResourceInjector.java
+++ b/src/main/java/org/apache/sling/models/impl/injectors/ChildResourceInjector.java
@@ -17,7 +17,12 @@
package org.apache.sling.models.impl.injectors;
import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
import org.apache.commons.lang.StringUtils;
import org.apache.felix.scr.annotations.Component;
@@ -47,12 +52,55 @@ public class ChildResourceInjector implements Injector, InjectAnnotationProcesso
public Object getValue(Object adaptable, String name, Type declaredType, AnnotatedElement element,
DisposalCallbackRegistry callbackRegistry) {
if (adaptable instanceof Resource) {
- return ((Resource) adaptable).getChild(name);
+ Resource child = ((Resource) adaptable).getChild(name);
+ if (child != null) {
+ return getValue(child, declaredType);
+ }
+ }
+ return null;
+ }
+
+ private Object getValue(Resource adaptable, Type declaredType) {
+ if (declaredType instanceof Class) {
+ return adaptable;
+ } else if (isDeclaredTypeCollection(declaredType)) {
+ return getResultList(adaptable, declaredType);
} else {
return null;
}
}
+ private Object getResultList(Resource resource, Type declaredType) {
+ List<Resource> result = new ArrayList<Resource>();
+ Class<?> type = getActualType((ParameterizedType) declaredType);
+ if (type != null && resource != null) {
+ Iterator<Resource> children = resource.listChildren();
+ while (children.hasNext()) {
+ result.add(children.next());
+ }
+ }
+ return result;
+ }
+
+ private Class<?> getActualType(ParameterizedType declaredType) {
+ Type[] types = declaredType.getActualTypeArguments();
+ if (types != null && types.length > 0) {
+ return (Class<?>) types[0];
+ }
+ return null;
+ }
+
+ private boolean isDeclaredTypeCollection(Type declaredType) {
+ boolean isCollection = false;
+ if (declaredType instanceof ParameterizedType) {
+ ParameterizedType type = (ParameterizedType) declaredType;
+ Class<?> collectionType = (Class<?>) type.getRawType();
+ isCollection = collectionType.equals(Collection.class)
+ || collectionType.equals(List.class);
+ }
+ return isCollection;
+ }
+
@Override
public InjectAnnotationProcessor createAnnotationProcessor(Object adaptable, AnnotatedElement element) {
// check if the element has the expected annotation
diff --git a/src/test/java/org/apache/sling/models/impl/ResourceModelClassesTest.java b/src/test/java/org/apache/sling/models/impl/ResourceModelClassesTest.java
index 5818f44..8f7485f 100644
--- a/src/test/java/org/apache/sling/models/impl/ResourceModelClassesTest.java
+++ b/src/test/java/org/apache/sling/models/impl/ResourceModelClassesTest.java
@@ -19,6 +19,7 @@ package org.apache.sling.models.impl;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
+import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@@ -157,13 +158,26 @@ public class ResourceModelClassesTest {
@Test
public void testChildResource() {
Resource child = mock(Resource.class);
+ Resource secondChild = mock(Resource.class);
+ Resource emptyChild = mock(Resource.class);
+
+ Resource firstGrandChild = mock(Resource.class);
+ Resource secondGrandChild = mock(Resource.class);
+ when(secondChild.listChildren()).thenReturn(Arrays.asList(firstGrandChild, secondGrandChild).iterator());
+ when(emptyChild.listChildren()).thenReturn(Collections.<Resource>emptySet().iterator());
Resource res = mock(Resource.class);
when(res.getChild("firstChild")).thenReturn(child);
+ when(res.getChild("secondChild")).thenReturn(secondChild);
+ when(res.getChild("emptyChild")).thenReturn(emptyChild);
ChildResourceModel model = factory.getAdapter(res, ChildResourceModel.class);
assertNotNull(model);
assertEquals(child, model.getFirstChild());
+ assertEquals(2, model.getGrandChildren().size());
+ assertEquals(firstGrandChild, model.getGrandChildren().get(0));
+ assertEquals(secondGrandChild, model.getGrandChildren().get(1));
+ assertEquals(0, model.getEmptyGrandChildren().size());
}
@Test
@@ -183,30 +197,54 @@ public class ResourceModelClassesTest {
@Test
public void testChildModel() {
- Object value = RandomStringUtils.randomAlphabetic(10);
- Map<String, Object> props = Collections.singletonMap("property", value);
- ValueMap map = new ValueMapDecorator(props);
+ Object firstValue = RandomStringUtils.randomAlphabetic(10);
+ ValueMap firstMap = new ValueMapDecorator(Collections.singletonMap("property", firstValue));
- final Resource child = mock(Resource.class);
- when(child.adaptTo(ValueMap.class)).thenReturn(map);
- when(child.adaptTo(ChildModel.class)).thenAnswer(new Answer<ChildModel>() {
+ final Resource firstChild = mock(Resource.class);
+ when(firstChild.adaptTo(ValueMap.class)).thenReturn(firstMap);
+ when(firstChild.adaptTo(ChildModel.class)).thenAnswer(new AdaptToChildMap());
+
+ Object firstGrandChildValue = RandomStringUtils.randomAlphabetic(10);
+ ValueMap firstGrandChildMap = new ValueMapDecorator(Collections.singletonMap("property", firstGrandChildValue));
+ Object secondGrandChildValue = RandomStringUtils.randomAlphabetic(10);
+ ValueMap secondGrandChildMap = new ValueMapDecorator(Collections.singletonMap("property", secondGrandChildValue));
+
+ final Resource firstGrandChild = mock(Resource.class);
+ when(firstGrandChild.adaptTo(ValueMap.class)).thenReturn(firstGrandChildMap);
+ when(firstGrandChild.adaptTo(ChildModel.class)).thenAnswer(new AdaptToChildMap());
+
+ final Resource secondGrandChild = mock(Resource.class);
+ when(secondGrandChild.adaptTo(ValueMap.class)).thenReturn(secondGrandChildMap);
+ when(secondGrandChild.adaptTo(ChildModel.class)).thenAnswer(new AdaptToChildMap());
- @Override
- public ChildModel answer(InvocationOnMock invocation) throws Throwable {
- return factory.getAdapter(child, ChildModel.class);
- }
+ Resource secondChild = mock(Resource.class);
+ when(secondChild.listChildren()).thenReturn(Arrays.asList(firstGrandChild, secondGrandChild).iterator());
- });
+ Resource emptyChild = mock(Resource.class);
+ when(emptyChild.listChildren()).thenReturn(Collections.<Resource>emptySet().iterator());
Resource res = mock(Resource.class);
- when(res.getChild("firstChild")).thenReturn(child);
+ when(res.getChild("firstChild")).thenReturn(firstChild);
+ when(res.getChild("secondChild")).thenReturn(secondChild);
+ when(res.getChild("emptyChild")).thenReturn(emptyChild);
ParentModel model = factory.getAdapter(res, ParentModel.class);
assertNotNull(model);
ChildModel childModel = model.getFirstChild();
assertNotNull(childModel);
- assertEquals(value, childModel.getProperty());
+ assertEquals(firstValue, childModel.getProperty());
+ assertEquals(2, model.getGrandChildren().size());
+ assertEquals(firstGrandChildValue, model.getGrandChildren().get(0).getProperty());
+ assertEquals(secondGrandChildValue, model.getGrandChildren().get(1).getProperty());
+ }
+
+ private class AdaptToChildMap implements Answer<ChildModel> {
+
+ @Override
+ public ChildModel answer(InvocationOnMock invocation) throws Throwable {
+ return factory.getAdapter(invocation.getMock(), ChildModel.class);
+ }
}
}
diff --git a/src/test/java/org/apache/sling/models/testmodels/classes/ChildResourceModel.java b/src/test/java/org/apache/sling/models/testmodels/classes/ChildResourceModel.java
index 0bca5ec..c69242f 100644
--- a/src/test/java/org/apache/sling/models/testmodels/classes/ChildResourceModel.java
+++ b/src/test/java/org/apache/sling/models/testmodels/classes/ChildResourceModel.java
@@ -16,7 +16,10 @@
*/
package org.apache.sling.models.testmodels.classes;
+import java.util.List;
+
import javax.inject.Inject;
+import javax.inject.Named;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.models.annotations.Model;
@@ -26,9 +29,23 @@ public class ChildResourceModel {
@Inject
private Resource firstChild;
-
+
+ @Inject @Named("secondChild")
+ private List<Resource> grandChildren;
+
+ @Inject @Named("emptyChild")
+ private List<Resource> emptyGrandChildren;
+
public Resource getFirstChild() {
return firstChild;
}
+ public List<Resource> getGrandChildren() {
+ return grandChildren;
+ }
+
+ public List<Resource> getEmptyGrandChildren() {
+ return emptyGrandChildren;
+ }
+
}
diff --git a/src/test/java/org/apache/sling/models/testmodels/classes/ParentModel.java b/src/test/java/org/apache/sling/models/testmodels/classes/ParentModel.java
index c0da400..c714e32 100644
--- a/src/test/java/org/apache/sling/models/testmodels/classes/ParentModel.java
+++ b/src/test/java/org/apache/sling/models/testmodels/classes/ParentModel.java
@@ -16,7 +16,10 @@
*/
package org.apache.sling.models.testmodels.classes;
+import java.util.List;
+
import javax.inject.Inject;
+import javax.inject.Named;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.models.annotations.Model;
@@ -27,7 +30,21 @@ public class ParentModel {
@Inject
private ChildModel firstChild;
+ @Inject @Named("secondChild")
+ private List<ChildModel> grandChildren;
+
+ @Inject @Named("emptyChild")
+ private List<ChildModel> emptyGrandChildren;
+
public ChildModel getFirstChild() {
return firstChild;
}
+
+ public List<ChildModel> getGrandChildren() {
+ return grandChildren;
+ }
+
+ public List<ChildModel> getEmptyGrandChildren() {
+ return emptyGrandChildren;
+ }
}
--
To stop receiving notification emails like this one, please contact
"commits@sling.apache.org" <co...@sling.apache.org>.