Merge pull request #17 from etugarev/improvement/SLING-8706

SLING-8706 - Injections for java.util.Optional<> should be automatically optional
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 b3b90ff..1ecd7f7 100644
--- a/src/main/java/org/apache/sling/models/impl/ModelAdapterFactory.java
+++ b/src/main/java/org/apache/sling/models/impl/ModelAdapterFactory.java
@@ -19,23 +19,8 @@
 import java.lang.ref.PhantomReference;
 import java.lang.ref.ReferenceQueue;
 import java.lang.ref.SoftReference;
-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.HashMap;
-import java.util.HashSet;
-import java.util.Hashtable;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.WeakHashMap;
+import java.lang.reflect.*;
+import java.util.*;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 
@@ -74,6 +59,7 @@
 import org.apache.sling.models.impl.model.InjectableMethod;
 import org.apache.sling.models.impl.model.ModelClass;
 import org.apache.sling.models.impl.model.ModelClassConstructor;
+import org.apache.sling.models.impl.model.OptionalTypedInjectableElement;
 import org.apache.sling.models.spi.AcceptsNullName;
 import org.apache.sling.models.spi.DisposalCallback;
 import org.apache.sling.models.spi.DisposalCallbackRegistry;
@@ -504,12 +490,54 @@
         }
     }
 
+    private class OptionalWrappingCallback implements InjectCallback {
+
+        private final InjectCallback chainedCallback;
+        private final InjectableElement element;
+
+        private OptionalWrappingCallback(InjectCallback chainedCallback, InjectableElement element) {
+            this.chainedCallback = chainedCallback;
+            this.element = element;
+        }
+
+        @Override
+        public RuntimeException inject(InjectableElement element1, Object value) {
+            return chainedCallback.inject(element, value.equals(Optional.empty())
+                    ? value    // if the value is null it's already represented as Optional.empty(), return as is
+                    : Optional.of(value)); // otherwise wrap in Optional
+        }
+    }
+
     private
     @Nullable
     RuntimeException injectElement(final InjectableElement element, final Object adaptable,
                                    final @NotNull DisposalCallbackRegistry registry, final InjectCallback callback,
                                    final @NotNull Map<ValuePreparer, Object> preparedValues,
                                    final @Nullable BundleContext modelContext) {
+        if (element instanceof InjectableField) {
+            Type genericType = ((InjectableField) element).getFieldGenericType();
+
+            if (genericType instanceof ParameterizedType) {
+                ParameterizedType pType = (ParameterizedType) genericType;
+
+                if (pType.getRawType().equals(Optional.class)) {
+                    InjectableElement el = new OptionalTypedInjectableElement(element, pType.getActualTypeArguments()[0]);
+                    InjectCallback wrappedCallback = new OptionalWrappingCallback(callback, element);
+
+                    return injectElementInternal(el, adaptable, registry, wrappedCallback, preparedValues, modelContext);
+                }
+            }
+        }
+
+        return injectElementInternal(element, adaptable, registry, callback, preparedValues, modelContext);
+    }
+
+    private
+    @Nullable
+    RuntimeException injectElementInternal(final InjectableElement element, final Object adaptable,
+                                   final @NotNull DisposalCallbackRegistry registry, final InjectCallback callback,
+                                   final @NotNull Map<ValuePreparer, Object> preparedValues,
+                                   final @Nullable BundleContext modelContext) {
 
         InjectAnnotationProcessor annotationProcessor = null;
         String source = element.getSource();
diff --git a/src/main/java/org/apache/sling/models/impl/model/OptionalTypedInjectableElement.java b/src/main/java/org/apache/sling/models/impl/model/OptionalTypedInjectableElement.java
new file mode 100644
index 0000000..ba4f160
--- /dev/null
+++ b/src/main/java/org/apache/sling/models/impl/model/OptionalTypedInjectableElement.java
@@ -0,0 +1,86 @@
+/*
+ * 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.impl.model;
+
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.Type;
+import java.util.Optional;
+
+import org.apache.sling.models.annotations.ViaProviderType;
+import org.apache.sling.models.spi.injectorspecific.InjectAnnotationProcessor;
+
+public class OptionalTypedInjectableElement implements InjectableElement {
+
+    private final InjectableElement element;
+    private final Type type;
+
+    public OptionalTypedInjectableElement(InjectableElement element, Type type) {
+        this.element = element;
+        this.type = type;
+    }
+
+    @Override
+    public AnnotatedElement getAnnotatedElement() {
+        return element.getAnnotatedElement();
+    }
+
+    @Override
+    public Type getType() {
+        return type;
+    }
+
+    @Override
+    public boolean isPrimitive() {
+        return element.isPrimitive();
+    }
+
+    @Override
+    public String getName() {
+        return element.getName();
+    }
+
+    @Override
+    public String getSource() {
+        return element.getSource();
+    }
+
+    @Override
+    public String getVia() {
+        return element.getVia();
+    }
+
+    @Override
+    public Class<? extends ViaProviderType> getViaProviderType() {
+        return element.getViaProviderType();
+    }
+
+    @Override
+    public boolean hasDefaultValue() {
+        return element.hasDefaultValue();
+    }
+
+    @Override
+    public Object getDefaultValue() {
+        // Default value injector will be evaluated last, make sure we return a value here so the injection is successful
+        return element.getDefaultValue() == null ? Optional.empty() : element.getDefaultValue();
+    }
+
+    @Override
+    public boolean isOptional(InjectAnnotationProcessor annotationProcessor) {
+        return true;
+    }
+}
diff --git a/src/test/java/org/apache/sling/models/impl/OptionalObjectsTest.java b/src/test/java/org/apache/sling/models/impl/OptionalObjectsTest.java
new file mode 100644
index 0000000..6fd0bfb
--- /dev/null
+++ b/src/test/java/org/apache/sling/models/impl/OptionalObjectsTest.java
@@ -0,0 +1,237 @@
+/*
+ * 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.impl;
+
+import java.util.*;
+
+import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ValueMap;
+import org.apache.sling.api.scripting.SlingBindings;
+import org.apache.sling.api.scripting.SlingScriptHelper;
+import org.apache.sling.api.wrappers.ValueMapDecorator;
+import org.apache.sling.models.impl.injectors.*;
+import org.apache.sling.models.impl.via.BeanPropertyViaProvider;
+import org.apache.sling.models.spi.injectorspecific.InjectAnnotationProcessorFactory;
+import org.apache.sling.models.spi.injectorspecific.InjectAnnotationProcessorFactory2;
+import org.apache.sling.models.testmodels.classes.OptionalObjectsModel;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+import org.slf4j.Logger;
+import static org.junit.Assert.*;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+@RunWith(MockitoJUnitRunner.class)
+public class OptionalObjectsTest {
+
+    private ModelAdapterFactory factory;
+
+    private OSGiServiceInjector osgiInjector;
+
+    @Mock
+    private BundleContext bundleContext;
+
+    @Mock
+    private SlingHttpServletRequest request;
+
+    @Mock
+    private Logger log;
+
+    @Before
+    public void setup() {
+        factory = AdapterFactoryTest.createModelAdapterFactory();
+
+        osgiInjector = new OSGiServiceInjector();
+        osgiInjector.activate(bundleContext);
+
+        BindingsInjector bindingsInjector = new BindingsInjector();
+        ValueMapInjector valueMapInjector = new ValueMapInjector();
+        ChildResourceInjector childResourceInjector = new ChildResourceInjector();
+        RequestAttributeInjector requestAttributeInjector = new RequestAttributeInjector();
+
+        factory.bindInjector(bindingsInjector,
+                Collections.<String, Object>singletonMap(Constants.SERVICE_ID, 1L));
+        factory.bindInjector(valueMapInjector,
+                Collections.<String, Object>singletonMap(Constants.SERVICE_ID, 2L));
+        factory.bindInjector(childResourceInjector,
+                Collections.<String, Object>singletonMap(Constants.SERVICE_ID, 3L));
+        factory.bindInjector(requestAttributeInjector,
+                Collections.<String, Object>singletonMap(Constants.SERVICE_ID, 4L));
+        factory.bindInjector(osgiInjector, Collections.<String, Object>singletonMap(Constants.SERVICE_ID, 5L));
+
+        factory.bindStaticInjectAnnotationProcessorFactory(bindingsInjector,
+                Collections.<String, Object>singletonMap(Constants.SERVICE_ID, 1L));
+        factory.injectAnnotationProcessorFactories = Collections.<InjectAnnotationProcessorFactory>singletonList(valueMapInjector);
+        factory.injectAnnotationProcessorFactories2 = Collections.<InjectAnnotationProcessorFactory2>singletonList(childResourceInjector);
+        factory.bindStaticInjectAnnotationProcessorFactory(requestAttributeInjector,
+                Collections.<String, Object>singletonMap(Constants.SERVICE_ID, 4L));
+        factory.bindStaticInjectAnnotationProcessorFactory(osgiInjector,
+                Collections.<String, Object>singletonMap(Constants.SERVICE_ID, 5L));
+        factory.bindViaProvider(new BeanPropertyViaProvider(), null);
+
+        SlingBindings bindings = new SlingBindings();
+        bindings.setLog(log);
+        Mockito.when(request.getAttribute(SlingBindings.class.getName())).thenReturn(bindings);
+
+        factory.adapterImplementations.addClassesAsAdapterAndImplementation(OptionalObjectsModel.class);
+
+    }
+
+    @Test
+    public void testFieldInjectionClass() {
+        Map<String, Object> map = new HashMap<>();
+        map.put("optionalString", "foo bar baz");
+        map.put("optionalByte", Byte.valueOf("1"));
+        map.put("optionalInteger", Integer.valueOf("1"));
+        map.put("optionalShort", Short.valueOf("1"));
+        map.put("optionalLong", Long.valueOf("1"));
+        map.put("optionalShort", Short.valueOf("1"));
+        map.put("optionalDouble", Double.valueOf("1"));
+        map.put("optionalFloat", Float.valueOf("1"));
+        map.put("optionalChar", '1');
+        map.put("optionalBoolean", Boolean.valueOf("true"));
+
+        Resource res = mock(Resource.class);
+        when(res.adaptTo(ValueMap.class)).thenReturn(new ValueMapDecorator(map));
+
+        org.apache.sling.models.testmodels.classes.OptionalObjectsModel model = factory.getAdapter(res,
+                OptionalObjectsModel.class);
+        assertNotNull(model);
+
+        assertEquals(Optional.of("foo bar baz"), model.getOptionalString());
+        assertEquals(Optional.empty(), model.getOptionalNullString());
+
+        assertEquals(Optional.of(1), model.getOptionalInteger());
+        assertEquals(Optional.empty(), model.getOptionalNullInteger());
+
+        assertEquals(Optional.of(Byte.valueOf("1")), model.getOptionalByte());
+        assertEquals(Optional.empty(), model.getOptionalNullByte());
+
+        assertEquals(Optional.of(Long.valueOf("1")), model.getOptionalLong());
+        assertEquals(Optional.empty(), model.getOptionalNullLong());
+
+        assertEquals(Optional.of(Short.valueOf("1")), model.getOptionalShort());
+        assertEquals(Optional.empty(), model.getOptionalNullShort());
+
+        assertEquals(Optional.of(Double.valueOf("1")), model.getOptionalDouble());
+        assertEquals(Optional.empty(), model.getOptionalNullDouble());
+
+        assertEquals(Optional.of(Float.valueOf("1")), model.getOptionalFloat());
+        assertEquals(Optional.empty(), model.getOptionalNullFloat());
+
+        assertEquals(Optional.of(1L), model.getOptionalLong());
+        assertEquals(Optional.empty(), model.getOptionalNullLong());
+
+        assertEquals(Optional.of('1'), model.getOptionalChar());
+        assertEquals(Optional.empty(), model.getOptionalNullChar());
+
+        assertEquals(Optional.of(Boolean.valueOf("true")), model.getOptionalBoolean());
+        assertEquals(Optional.empty(), model.getOptionalNullBoolean());
+    }
+
+    @Test
+    public void testFieldInjectionListsAndArrays() {
+        Map<String, Object> map = new HashMap<>();
+
+        map.put("intList", new Integer[]{1, 2, 9, 8});
+        map.put("stringList", new String[]{"hello", "world"});
+        map.put("optionalList", Arrays.asList("foo", "bar", "baz"));
+        map.put("optionalArray", new String[]{"qux", "quux"});
+
+        ValueMap vm = new ValueMapDecorator(map);
+        Resource res = mock(Resource.class);
+        when(res.adaptTo(ValueMap.class)).thenReturn(vm);
+
+        OptionalObjectsModel model = factory.getAdapter(res, OptionalObjectsModel.class);
+        assertNotNull(model);
+
+        assertEquals(4, model.getIntList().get().size());
+        assertEquals(new Integer(2), model.getIntList().get().get(1));
+
+        assertEquals(2, model.getStringList().get().size());
+        assertEquals("hello", model.getStringList().get().get(0));
+
+        assertEquals(Optional.of(Arrays.asList("foo", "bar", "baz")), model.getOptionalList());
+        assertEquals(Optional.empty(), model.getOptionalNullList());
+
+        assertTrue(model.getOptionalArray().isPresent());
+        assertTrue("qux".equalsIgnoreCase(model.getOptionalArray().get()[0]));
+        assertTrue("quux".equalsIgnoreCase(model.getOptionalArray().get()[1]));
+        assertEquals(Optional.empty(), model.getOptionalNullArray());
+    }
+
+    @Test
+    public void testFieldInjectionsChildResource() throws Exception {
+        Resource res = mock(Resource.class);
+        Resource child = mock(Resource.class);
+        when(child.getName()).thenReturn("child");
+        when(res.getChild(eq("child"))).thenReturn(child);
+
+        OptionalObjectsModel model = factory.getAdapter(res, OptionalObjectsModel.class);
+        assertNotNull(model);
+
+        assertEquals(child.getName(), model.getOptionalChild().get().getName());
+        assertEquals(Optional.empty(), model.getOptionalNullChild());
+    }
+
+    @Test
+    public void testFieldInjectionsScriptVariable() throws Exception {
+        SlingBindings bindings = new SlingBindings();
+        SlingScriptHelper helper = mock(SlingScriptHelper.class);
+        bindings.setSling(helper);
+        when(request.getAttribute(SlingBindings.class.getName())).thenReturn(bindings);
+
+        OptionalObjectsModel model = factory.getAdapter(request, OptionalObjectsModel.class);
+        assertNotNull(model);
+        assertEquals(helper, model.getOptionalHelper().get());
+        assertEquals(Optional.empty(), model.getOptionalNullHelper());
+    }
+
+    @Test
+    public void testFieldInjectionsOSGiService() throws InvalidSyntaxException {
+        ServiceReference ref = mock(ServiceReference.class);
+        Logger log = mock(Logger.class);
+        when(bundleContext.getServiceReferences(Logger.class.getName(), null)).thenReturn(
+                new ServiceReference[]{ref});
+        when(bundleContext.getService(ref)).thenReturn(log);
+
+        OptionalObjectsModel model = factory.getAdapter(request, OptionalObjectsModel.class);
+        assertNotNull(model);
+        assertEquals(log, model.getLog().get());
+    }
+
+    @Test
+    public void testFieldInjectionsRequestAttribute() throws InvalidSyntaxException {
+        Object attribute = new Object();
+        when(request.getAttribute("attribute")).thenReturn(attribute);
+
+        OptionalObjectsModel model = factory.getAdapter(request, OptionalObjectsModel.class);
+        assertNotNull(model);
+        assertEquals(attribute, model.getOptionalRequestAttribute().get());
+        assertEquals(Optional.empty(), model.getOptionalNullRequestAttribute());
+    }
+}
diff --git a/src/test/java/org/apache/sling/models/testmodels/classes/OptionalObjectsModel.java b/src/test/java/org/apache/sling/models/testmodels/classes/OptionalObjectsModel.java
new file mode 100644
index 0000000..5d20865
--- /dev/null
+++ b/src/test/java/org/apache/sling/models/testmodels/classes/OptionalObjectsModel.java
@@ -0,0 +1,253 @@
+/*
+ * 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 java.util.List;
+import java.util.Optional;
+import javax.inject.Inject;
+
+import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.scripting.SlingScriptHelper;
+import org.apache.sling.models.annotations.Model;
+import org.apache.sling.models.annotations.injectorspecific.ChildResource;
+import org.apache.sling.models.annotations.injectorspecific.OSGiService;
+import org.apache.sling.models.annotations.injectorspecific.RequestAttribute;
+import org.apache.sling.models.annotations.injectorspecific.ScriptVariable;
+import org.slf4j.Logger;
+
+@Model(adaptables = {Resource.class, SlingHttpServletRequest.class})
+public class OptionalObjectsModel {
+
+    @Inject
+    private Optional<String> optionalString;
+
+    @Inject
+    private Optional<String> optionalNullString;
+
+    @Inject
+    private Optional<Byte> optionalByte;
+
+    @Inject
+    private Optional<Byte> optionalNullByte;
+
+    @Inject
+    private Optional<Short> optionalShort;
+
+    @Inject
+    private Optional<Short> optionalNullShort;
+
+    @Inject
+    private Optional<Integer> optionalInteger;
+
+    @Inject
+    private Optional<Integer> optionalNullInteger;
+
+    @Inject
+    private Optional<Long> optionalLong;
+
+    @Inject
+    private Optional<Long> optionalNullLong;
+
+    @Inject
+    private Optional<Float> optionalFloat;
+
+    @Inject
+    private Optional<Float> optionalNullFloat;
+
+    @Inject
+    private Optional<Double> optionalDouble;
+
+    @Inject
+    private Optional<Double> optionalNullDouble;
+
+    @Inject
+    private Optional<Character> optionalChar;
+
+    @Inject
+    private Optional<Character> optionalNullChar;
+
+    @Inject
+    private Optional<Boolean> optionalBoolean;
+
+    @Inject
+    private Optional<Boolean> optionalNullBoolean;
+
+    @Inject
+    private Optional<List> optionalList;
+
+    @Inject
+    private Optional<List> optionalNullList;
+
+    @Inject
+    private Optional<String[]> optionalArray;
+
+    @Inject
+    private Optional<String[]> optionalNullArray;
+
+    @Inject
+    private Optional<List<String>> stringList;
+
+    @Inject
+    private Optional<List<Integer>> intList;
+
+    @ChildResource(name = "child")
+    private Optional<Resource> optionalChild;
+
+    @ChildResource
+    private Optional<Resource> optionalNullChild;
+
+    @ScriptVariable(name = "sling")
+    private Optional<SlingScriptHelper> optionalHelper;
+
+    @ScriptVariable(name = "foo")
+    private Optional<SlingScriptHelper> optionalNullHelper;
+
+    @OSGiService
+    private Optional<Logger> log;
+
+    @RequestAttribute(name = "attribute")
+    private Optional<Object> optionalRequestAttribute;
+
+    @RequestAttribute(name = "foo")
+    private Optional<Object> optionalNullRequestAttribute;
+
+    public Optional<String> getOptionalString() {
+        return optionalString;
+    }
+
+    public Optional<String> getOptionalNullString() {
+        return optionalNullString;
+    }
+
+    public Optional<Byte> getOptionalByte() {
+        return optionalByte;
+    }
+
+    public Optional<Byte> getOptionalNullByte() {
+        return optionalNullByte;
+    }
+
+    public Optional<Short> getOptionalShort() {
+        return optionalShort;
+    }
+
+    public Optional<Short> getOptionalNullShort() {
+        return optionalNullShort;
+    }
+
+    public Optional<Integer> getOptionalInteger() {
+        return optionalInteger;
+    }
+
+    public Optional<Integer> getOptionalNullInteger() {
+        return optionalNullInteger;
+    }
+
+    public Optional<Long> getOptionalLong() {
+        return optionalLong;
+    }
+
+    public Optional<Long> getOptionalNullLong() {
+        return optionalNullLong;
+    }
+
+    public Optional<Float> getOptionalFloat() {
+        return optionalFloat;
+    }
+
+    public Optional<Float> getOptionalNullFloat() {
+        return optionalNullFloat;
+    }
+
+    public Optional<Double> getOptionalDouble() {
+        return optionalDouble;
+    }
+
+    public Optional<Double> getOptionalNullDouble() {
+        return optionalNullDouble;
+    }
+
+    public Optional<Character> getOptionalChar() {
+        return optionalChar;
+    }
+
+    public Optional<Character> getOptionalNullChar() {
+        return optionalNullChar;
+    }
+
+    public Optional<Boolean> getOptionalBoolean() {
+        return optionalBoolean;
+    }
+
+    public Optional<Boolean> getOptionalNullBoolean() {
+        return optionalNullBoolean;
+    }
+
+    public Optional<? extends Collection> getOptionalList() {
+        return optionalList;
+    }
+
+    public Optional<? extends Collection> getOptionalNullList() {
+        return optionalNullList;
+    }
+
+    public Optional<String[]> getOptionalArray() {
+        return optionalArray;
+    }
+
+    public Optional<String[]> getOptionalNullArray() {
+        return optionalNullArray;
+    }
+
+    public Optional<List<String>> getStringList() {
+        return stringList;
+    }
+
+    public Optional<List<Integer>> getIntList() {
+        return intList;
+    }
+
+    public Optional<Resource> getOptionalChild() {
+        return optionalChild;
+    }
+
+    public Optional<Resource> getOptionalNullChild() {
+        return optionalNullChild;
+    }
+
+    public Optional<SlingScriptHelper> getOptionalHelper() {
+        return optionalHelper;
+    }
+
+    public Optional<SlingScriptHelper> getOptionalNullHelper() {
+        return optionalNullHelper;
+    }
+
+    public Optional<Logger> getLog() {
+        return log;
+    }
+
+    public Optional<Object> getOptionalRequestAttribute() {
+        return optionalRequestAttribute;
+    }
+
+    public Optional<Object> getOptionalNullRequestAttribute() {
+        return optionalNullRequestAttribute;
+    }
+}