This is an automated email from the ASF dual-hosted git repository. sseifert pushed a commit to branch feature/SLING-11812-collection-default-values in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-models-impl.git
commit 4d49bc9ea137dbdbf67e884fc94cc5c219b1bbb6 Author: Stefan Seifert <[email protected]> AuthorDate: Tue Mar 28 12:33:18 2023 +0200 SLING-11812 support default value injection for Collection and List types --- .../impl/model/AbstractInjectableElement.java | 43 +++++++++++++++++++- .../models/impl/ResourceModelClassesTest.java | 45 ++++++++++++++++++++- .../classes/CollectionDefaultsModel.java | 46 ++++++++++++++++++++++ .../testmodels/classes/ListDefaultsModel.java | 46 ++++++++++++++++++++++ 4 files changed, 178 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/apache/sling/models/impl/model/AbstractInjectableElement.java b/src/main/java/org/apache/sling/models/impl/model/AbstractInjectableElement.java index 72ccb18..cdb79f8 100644 --- a/src/main/java/org/apache/sling/models/impl/model/AbstractInjectableElement.java +++ b/src/main/java/org/apache/sling/models/impl/model/AbstractInjectableElement.java @@ -19,7 +19,12 @@ package org.apache.sling.models.impl.model; import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.Array; +import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; import javax.inject.Named; @@ -37,6 +42,7 @@ import org.apache.sling.models.impl.ReflectionUtil; import org.apache.sling.models.spi.injectorspecific.InjectAnnotationProcessor; import org.apache.sling.models.spi.injectorspecific.InjectAnnotationProcessor2; import org.apache.sling.models.spi.injectorspecific.StaticInjectAnnotationProcessorFactory; +import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -148,7 +154,20 @@ abstract class AbstractInjectableElement implements InjectableElement { Object value = null; - if (type instanceof Class) { + if (type instanceof ParameterizedType) { + ParameterizedType parameterizedType = (ParameterizedType)type; + Class<?> rawType = (Class<?>)parameterizedType.getRawType(); + if (Collection.class.isAssignableFrom(rawType)) { + value = getFirstNonEmptyArrayAsList( + defaultAnnotation.values(), + defaultAnnotation.intValues(), + defaultAnnotation.longValues(), + defaultAnnotation.floatValues(), + defaultAnnotation.doubleValues(), + defaultAnnotation.booleanValues()); + } + } + else if (type instanceof Class) { Class<?> injectedClass = (Class<?>) type; if (injectedClass.isArray()) { Class<?> componentType = injectedClass.getComponentType(); @@ -206,6 +225,28 @@ abstract class AbstractInjectableElement implements InjectableElement { return value; } + /** + * Gets the first non-empty array from the given list of arrays, and returns its values as list. + * @param arrays Arrays + * @return List with values of first non-empty array, or null. + */ + private static @Nullable List<Object> getFirstNonEmptyArrayAsList(Object... arrays) { + for (Object array : arrays) { + if (array != null && array.getClass().isArray()) { + int arrayLength = Array.getLength(array); + if (arrayLength > 0) { + List<Object> result = new ArrayList<>(); + for (int i=0; i<arrayLength; i++) { + result.add(Array.get(array, i)); + } + return result; + } + + } + } + return null; + } + private static boolean getOptional(AnnotatedElement element, InjectAnnotationProcessor annotationProcessor) { if (element.isAnnotationPresent(Optional.class)) { return true; 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 8bbdbf8..c6836a1 100644 --- a/src/test/java/org/apache/sling/models/impl/ResourceModelClassesTest.java +++ b/src/test/java/org/apache/sling/models/impl/ResourceModelClassesTest.java @@ -46,6 +46,8 @@ import org.apache.sling.models.testmodels.classes.ArrayWrappersModel; import org.apache.sling.models.testmodels.classes.ChildModel; import org.apache.sling.models.testmodels.classes.ChildResourceModel; import org.apache.sling.models.testmodels.classes.ChildValueMapModel; +import org.apache.sling.models.testmodels.classes.CollectionDefaultsModel; +import org.apache.sling.models.testmodels.classes.ListDefaultsModel; import org.apache.sling.models.testmodels.classes.ListModel; import org.apache.sling.models.testmodels.classes.ParentModel; import org.apache.sling.models.testmodels.classes.ResourceModelWithRequiredField; @@ -72,7 +74,18 @@ public class ResourceModelClassesTest { factory.bindInjector(new ChildResourceInjector(), new ServicePropertiesMap(1, 1)); factory.injectAnnotationProcessorFactories = factory.injectAnnotationProcessorFactories = Collections.<InjectAnnotationProcessorFactory>singletonList(new ValueMapInjector()); - factory.adapterImplementations.addClassesAsAdapterAndImplementation(SimplePropertyModel.class, ArrayWrappersModel.class, ResourceModelWithRequiredField.class, ChildValueMapModel.class, ArrayPrimitivesModel.class, ChildResourceModel.class, ResourceModelWithRequiredFieldOptionalStrategy.class, ParentModel.class, ChildModel.class, ListModel.class); + factory.adapterImplementations.addClassesAsAdapterAndImplementation(SimplePropertyModel.class, + ArrayWrappersModel.class, + ResourceModelWithRequiredField.class, + ChildValueMapModel.class, + ArrayPrimitivesModel.class, + ChildResourceModel.class, + ResourceModelWithRequiredFieldOptionalStrategy.class, + ParentModel.class, + ChildModel.class, + ListModel.class, + ListDefaultsModel.class, + CollectionDefaultsModel.class); } @Test @@ -167,6 +180,36 @@ public class ResourceModelClassesTest { assertEquals("hello", model.getStringList().get(0)); } + @Test + public void testListDefaultsModel() { + Map<String, Object> map = new HashMap<>(); + + ValueMap vm = new ValueMapDecorator(map); + Resource res = mock(Resource.class); + when(res.adaptTo(ValueMap.class)).thenReturn(vm); + + ListDefaultsModel model = factory.getAdapter(res, ListDefaultsModel.class); + assertNotNull(model); + + assertEquals(Arrays.asList(1,2,3), model.getIntList()); + assertEquals(Arrays.asList("v1", "v2"), model.getStringList()); + } + + @Test + public void testCollectionDefaultsModel() { + Map<String, Object> map = new HashMap<>(); + + ValueMap vm = new ValueMapDecorator(map); + Resource res = mock(Resource.class); + when(res.adaptTo(ValueMap.class)).thenReturn(vm); + + CollectionDefaultsModel model = factory.getAdapter(res, CollectionDefaultsModel.class); + assertNotNull(model); + + assertEquals(Arrays.asList(1,2,3), model.getIntCollection()); + assertEquals(Arrays.asList("v1", "v2"), model.getStringCollection()); + } + @SuppressWarnings({ "unused", "null" }) @Test public void testRequiredPropertyModel() { diff --git a/src/test/java/org/apache/sling/models/testmodels/classes/CollectionDefaultsModel.java b/src/test/java/org/apache/sling/models/testmodels/classes/CollectionDefaultsModel.java new file mode 100644 index 0000000..666f1c1 --- /dev/null +++ b/src/test/java/org/apache/sling/models/testmodels/classes/CollectionDefaultsModel.java @@ -0,0 +1,46 @@ +/* + * 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.models.testmodels.classes; + +import java.util.Collection; + +import javax.inject.Inject; + +import org.apache.sling.api.resource.Resource; +import org.apache.sling.models.annotations.Default; +import org.apache.sling.models.annotations.Model; + +@Model(adaptables = Resource.class) +public class CollectionDefaultsModel { + + @Inject + @Default(intValues = {1,2,3}) + private Collection<Integer> intList; + + @Inject + @Default(values = {"v1","v2"}) + private Collection<String> stringList; + + public Collection<Integer> getIntCollection() { + return intList; + } + + public Collection<String> getStringCollection() { + return stringList; + } + +} diff --git a/src/test/java/org/apache/sling/models/testmodels/classes/ListDefaultsModel.java b/src/test/java/org/apache/sling/models/testmodels/classes/ListDefaultsModel.java new file mode 100644 index 0000000..4bef12c --- /dev/null +++ b/src/test/java/org/apache/sling/models/testmodels/classes/ListDefaultsModel.java @@ -0,0 +1,46 @@ +/* + * 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.models.testmodels.classes; + +import java.util.List; + +import javax.inject.Inject; + +import org.apache.sling.api.resource.Resource; +import org.apache.sling.models.annotations.Default; +import org.apache.sling.models.annotations.Model; + +@Model(adaptables = Resource.class) +public class ListDefaultsModel { + + @Inject + @Default(intValues = {1,2,3}) + private List<Integer> intList; + + @Inject + @Default(values = {"v1","v2"}) + private List<String> stringList; + + public List<Integer> getIntList() { + return intList; + } + + public List<String> getStringList() { + return stringList; + } + +}
