blob: 1ed8b53cfa0c7590886e57565b353db9bce96f56 [file] [log] [blame]
/*
* 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.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;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.sling.models.annotations.Default;
import org.apache.sling.models.annotations.DefaultInjectionStrategy;
import org.apache.sling.models.annotations.Optional;
import org.apache.sling.models.annotations.Required;
import org.apache.sling.models.annotations.Source;
import org.apache.sling.models.annotations.Via;
import org.apache.sling.models.annotations.ViaProviderType;
import org.apache.sling.models.annotations.via.BeanProperty;
import org.apache.sling.models.impl.ModelAdapterFactory;
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;
@SuppressWarnings("deprecation")
abstract class AbstractInjectableElement implements InjectableElement {
private final AnnotatedElement element;
private final Type type;
private final String name;
private final String source;
private final ViaSpec via;
private final boolean hasDefaultValue;
private final Object defaultValue;
private final boolean isOptional;
private final boolean isRequired;
private final DefaultInjectionStrategy injectionStrategy;
private final DefaultInjectionStrategy defaultInjectionStrategy;
private static final Logger log = LoggerFactory.getLogger(ModelAdapterFactory.class);
public AbstractInjectableElement(AnnotatedElement element, Type type, String defaultName,
StaticInjectAnnotationProcessorFactory[] processorFactories, DefaultInjectionStrategy defaultInjectionStrategy) {
this.element = element;
this.type = type;
InjectAnnotationProcessor2 annotationProcessor = getAnnotationProcessor(element, processorFactories);
this.name = getName(element, defaultName, annotationProcessor);
this.source = getSource(element);
this.via = getVia(element, annotationProcessor);
this.hasDefaultValue = getHasDefaultValue(element, annotationProcessor);
this.defaultValue = getDefaultValue(element, type, annotationProcessor);
this.isOptional = getOptional(element, annotationProcessor);
this.isRequired = getRequired(element, annotationProcessor);
this.injectionStrategy = getInjectionStrategy(element, annotationProcessor, defaultInjectionStrategy);
this.defaultInjectionStrategy = defaultInjectionStrategy;
}
private static InjectAnnotationProcessor2 getAnnotationProcessor(AnnotatedElement element, StaticInjectAnnotationProcessorFactory[] processorFactories) {
for (StaticInjectAnnotationProcessorFactory processorFactory : processorFactories) {
InjectAnnotationProcessor2 annotationProcessor = processorFactory.createAnnotationProcessor(element);
if (annotationProcessor != null) {
return annotationProcessor;
}
}
return null;
}
@SuppressWarnings("unused")
private static String getName(AnnotatedElement element, String defaultName, InjectAnnotationProcessor2 annotationProcessor) {
String name = null;
if (annotationProcessor != null) {
name = annotationProcessor.getName();
}
if (name == null) {
Named namedAnnotation = element.getAnnotation(Named.class);
if (namedAnnotation != null) {
name = namedAnnotation.value();
}
else {
name = defaultName;
}
}
return name;
}
@SuppressWarnings("unused")
private static String getSource(AnnotatedElement element) {
Source source = ReflectionUtil.getAnnotation(element, Source.class);
if (source != null) {
return source.value();
}
return null;
}
private static ViaSpec getVia(AnnotatedElement element, InjectAnnotationProcessor2 annotationProcessor) {
ViaSpec spec = new ViaSpec();
if (annotationProcessor != null) {
spec.via = annotationProcessor.getVia();
}
if (spec.via == null) {
Via viaAnnotation = element.getAnnotation(Via.class);
if (viaAnnotation != null) {
spec.via = viaAnnotation.value();
spec.type = viaAnnotation.type();
}
} else {
// use default type
spec.type = BeanProperty.class;
}
return spec;
}
private static boolean getHasDefaultValue(AnnotatedElement element, InjectAnnotationProcessor2 annotationProcessor) {
if (annotationProcessor != null) {
return annotationProcessor.hasDefault();
}
return element.isAnnotationPresent(Default.class);
}
@SuppressWarnings("unused")
private static Object getDefaultValue(AnnotatedElement element, Type type, InjectAnnotationProcessor2 annotationProcessor) {
if (annotationProcessor != null && annotationProcessor.hasDefault()) {
return annotationProcessor.getDefault();
}
Default defaultAnnotation = element.getAnnotation(Default.class);
if (defaultAnnotation == null) {
return null;
}
Object value = null;
if (type instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType)type;
Type rawType = parameterizedType.getRawType();
if ((rawType == Collection.class || rawType == List.class)
&& parameterizedType.getActualTypeArguments().length > 0) {
Type itemType = parameterizedType.getActualTypeArguments()[0];
if (itemType == String.class) {
value = arrayToTypedList(defaultAnnotation.values());
} else if (itemType == Integer.class) {
value = arrayToTypedList(defaultAnnotation.intValues());
} else if (itemType == Long.class) {
value = arrayToTypedList(defaultAnnotation.longValues());
} else if (itemType == Boolean.class) {
value = arrayToTypedList(defaultAnnotation.booleanValues());
} else if (itemType == Short.class) {
value = arrayToTypedList(defaultAnnotation.shortValues());
} else if (itemType == Float.class) {
value = arrayToTypedList(defaultAnnotation.floatValues());
} else if (itemType == Double.class) {
value = arrayToTypedList(defaultAnnotation.doubleValues());
} else {
log.warn("Default values for {} List/Collection are not supported (used on {})", itemType, element);
}
}
else {
log.warn("Cannot provide default for {} (used on {})", type, element);
}
}
else if (type instanceof Class) {
Class<?> injectedClass = (Class<?>) type;
if (injectedClass.isArray()) {
Class<?> componentType = injectedClass.getComponentType();
if (componentType == String.class) {
value = defaultAnnotation.values();
} else if (componentType == Integer.TYPE) {
value = defaultAnnotation.intValues();
} else if (componentType == Integer.class) {
value = ArrayUtils.toObject(defaultAnnotation.intValues());
} else if (componentType == Long.TYPE) {
value = defaultAnnotation.longValues();
} else if (componentType == Long.class) {
value = ArrayUtils.toObject(defaultAnnotation.longValues());
} else if (componentType == Boolean.TYPE) {
value = defaultAnnotation.booleanValues();
} else if (componentType == Boolean.class) {
value = ArrayUtils.toObject(defaultAnnotation.booleanValues());
} else if (componentType == Short.TYPE) {
value = defaultAnnotation.shortValues();
} else if (componentType == Short.class) {
value = ArrayUtils.toObject(defaultAnnotation.shortValues());
} else if (componentType == Float.TYPE) {
value = defaultAnnotation.floatValues();
} else if (componentType == Float.class) {
value = ArrayUtils.toObject(defaultAnnotation.floatValues());
} else if (componentType == Double.TYPE) {
value = defaultAnnotation.doubleValues();
} else if (componentType == Double.class) {
value = ArrayUtils.toObject(defaultAnnotation.doubleValues());
} else {
log.warn("Default values for {} are not supported (used on {})", componentType, element);
}
} else {
if (injectedClass == String.class) {
value = defaultAnnotation.values().length == 0 ? "" : defaultAnnotation.values()[0];
} else if (injectedClass == Integer.class) {
value = defaultAnnotation.intValues().length == 0 ? 0 : defaultAnnotation.intValues()[0];
} else if (injectedClass == Long.class) {
value = defaultAnnotation.longValues().length == 0 ? 0l : defaultAnnotation.longValues()[0];
} else if (injectedClass == Boolean.class) {
value = defaultAnnotation.booleanValues().length == 0 ? false : defaultAnnotation.booleanValues()[0];
} else if (injectedClass == Short.class) {
value = defaultAnnotation.shortValues().length == 0 ? ((short) 0) : defaultAnnotation.shortValues()[0];
} else if (injectedClass == Float.class) {
value = defaultAnnotation.floatValues().length == 0 ? 0f : defaultAnnotation.floatValues()[0];
} else if (injectedClass == Double.class) {
value = defaultAnnotation.doubleValues().length == 0 ? 0d : defaultAnnotation.doubleValues()[0];
} else {
log.warn("Default values for {} are not supported (used on {})", injectedClass, element);
}
}
} else {
log.warn("Cannot provide default for {} (used on {})", type, element);
}
return value;
}
/**
* Converts array to typed list of values.
* @param <T> Array/List type
* @param array Array
* @return Typed list or null if array is empty
*/
@SuppressWarnings("unchecked")
private static <T> @Nullable List<T> arrayToTypedList(Object array) {
if (array != null && array.getClass().isArray()) {
int arrayLength = Array.getLength(array);
if (arrayLength > 0) {
List<T> result = new ArrayList<>();
for (int i=0; i<arrayLength; i++) {
result.add((T)Array.get(array, i));
}
return result;
}
}
return null;
}
private static boolean getOptional(AnnotatedElement element, InjectAnnotationProcessor annotationProcessor) {
if (element.isAnnotationPresent(Optional.class)) {
return true;
}
if (annotationProcessor != null) {
Boolean optional = annotationProcessor.isOptional();
if (optional != null) {
return optional.booleanValue();
}
}
return false;
}
private static boolean getRequired(AnnotatedElement element, InjectAnnotationProcessor annotationProcessor) {
// do not evaluate the injector-specific annotation (those are only considered for optional)
// even setting optional=false will not make an attribute mandatory
return element.isAnnotationPresent(Required.class);
}
private static DefaultInjectionStrategy getInjectionStrategy(AnnotatedElement element, InjectAnnotationProcessor annotationProcessor, DefaultInjectionStrategy defaultInjectionStrategy) {
if (annotationProcessor != null) {
if (annotationProcessor instanceof InjectAnnotationProcessor2) {
switch (((InjectAnnotationProcessor2)annotationProcessor).getInjectionStrategy()) {
case OPTIONAL:
return DefaultInjectionStrategy.OPTIONAL;
case REQUIRED:
return DefaultInjectionStrategy.REQUIRED;
case DEFAULT:
break;
}
}
}
return defaultInjectionStrategy;
}
@Override
public final AnnotatedElement getAnnotatedElement() {
return this.element;
}
@Override
public final Type getType() {
return type;
}
@Override
public final String getName() {
return this.name;
}
@Override
public String getSource() {
return this.source;
}
@Override
public String getVia() {
return this.via.via;
}
@Override
public Class<? extends ViaProviderType> getViaProviderType() { return this.via.type; }
@Override
public boolean hasDefaultValue() {
return this.hasDefaultValue;
}
@Override
public Object getDefaultValue() {
return this.defaultValue;
}
@Override
public boolean isOptional(InjectAnnotationProcessor annotationProcessor) {
DefaultInjectionStrategy injectionStrategy = this.injectionStrategy;
boolean isOptional = this.isOptional;
boolean isRequired = this.isRequired;
// evaluate annotationProcessor (which depends on the adapter)
if (annotationProcessor != null) {
isOptional = getOptional(getAnnotatedElement(), annotationProcessor);
isRequired = getRequired(getAnnotatedElement(), annotationProcessor);
injectionStrategy = getInjectionStrategy(element, annotationProcessor, defaultInjectionStrategy);
}
if (injectionStrategy == DefaultInjectionStrategy.REQUIRED) {
return isOptional;
} else {
return !isRequired;
}
}
private static class ViaSpec {
String via;
Class<? extends ViaProviderType> type;
}
}