SLING-8706 - Injections for java.util.Optional<> should be automatic optional
diff --git a/src/main/java/org/apache/sling/models/impl/injectors/ValueMapInjector.java b/src/main/java/org/apache/sling/models/impl/injectors/ValueMapInjector.java
index f24b367..92036f7 100644
--- a/src/main/java/org/apache/sling/models/impl/injectors/ValueMapInjector.java
+++ b/src/main/java/org/apache/sling/models/impl/injectors/ValueMapInjector.java
@@ -23,6 +23,7 @@
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.List;
+import java.util.Optional;
 
 import org.apache.commons.lang3.ClassUtils;
 import org.apache.commons.lang3.ObjectUtils;
@@ -98,6 +99,11 @@
                 return null;
             }
             Class<?> collectionType = (Class<?>) pType.getRawType();
+
+            if (collectionType.equals(Optional.class)) {
+                return Optional.ofNullable(map.get(name));
+            }
+
             if (!(collectionType.equals(Collection.class) || collectionType.equals(List.class))) {
                 return null;
             }
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..13a63b6
--- /dev/null
+++ b/src/test/java/org/apache/sling/models/impl/OptionalObjectsTest.java
@@ -0,0 +1,110 @@
+/*
+ * 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.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ValueMap;
+import org.apache.sling.api.wrappers.ValueMapDecorator;
+import org.apache.sling.models.impl.injectors.ValueMapInjector;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.runners.MockitoJUnitRunner;
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+@RunWith(MockitoJUnitRunner.class)
+public class OptionalObjectsTest {
+
+    private ModelAdapterFactory factory;
+
+    @Before
+    public void setup() {
+        factory = AdapterFactoryTest.createModelAdapterFactory();
+        factory.bindInjector(new ValueMapInjector(), new ServicePropertiesMap(2, 2));
+        factory.adapterImplementations.addClassesAsAdapterAndImplementation(
+                org.apache.sling.models.testmodels.classes.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"));
+        map.put("optionalList", Arrays.asList("foo", "bar", "baz"));
+        map.put("optionalArray", new String[]{"qux", "quux"});
+
+        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,
+                org.apache.sling.models.testmodels.classes.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());
+
+        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());
+    }
+}
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..a130e3d
--- /dev/null
+++ b/src/test/java/org/apache/sling/models/testmodels/classes/OptionalObjectsModel.java
@@ -0,0 +1,182 @@
+/*
+ * 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.Optional;
+import javax.inject.Inject;
+
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.models.annotations.Model;
+
+@Model(adaptables = Resource.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<? extends Collection> optionalList;
+
+    @Inject
+    private Optional<? extends Collection> optionalNullList;
+
+    @Inject
+    private Optional<String[]> optionalArray;
+
+    @Inject
+    private Optional<String[]> optionalNullArray;
+
+    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;
+    }
+}