package org.jboss.resteasy.core;
import org.jboss.resteasy.annotations.Form;
import org.jboss.resteasy.annotations.Query;
import org.jboss.resteasy.spi.ConstructorInjector;
import org.jboss.resteasy.spi.HttpRequest;
import org.jboss.resteasy.spi.HttpResponse;
import org.jboss.resteasy.spi.InjectorFactory;
import org.jboss.resteasy.spi.MethodInjector;
import org.jboss.resteasy.spi.PropertyInjector;
import org.jboss.resteasy.spi.ResteasyProviderFactory;
import org.jboss.resteasy.spi.ValueInjector;
import org.jboss.resteasy.spi.metadata.Parameter;
import org.jboss.resteasy.spi.metadata.ResourceClass;
import org.jboss.resteasy.spi.metadata.ResourceConstructor;
import org.jboss.resteasy.spi.metadata.ResourceLocator;
import org.jboss.resteasy.spi.util.Types;
import javax.ws.rs.BeanParam;
import javax.ws.rs.CookieParam;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.Encoded;
import javax.ws.rs.FormParam;
import javax.ws.rs.HeaderParam;
import javax.ws.rs.MatrixParam;
import javax.ws.rs.PathParam;
import javax.ws.rs.QueryParam;
import javax.ws.rs.container.Suspended;
import javax.ws.rs.core.Context;
import java.lang.annotation.Annotation;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Constructor;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.OptionalDouble;
import java.util.OptionalInt;
import java.util.OptionalLong;
import java.util.concurrent.CompletionStage;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import static org.jboss.resteasy.spi.util.FindAnnotation.findAnnotation;
@SuppressWarnings({"unchecked", "rawtypes"})
public class InjectorFactoryImpl implements InjectorFactory
{
public static final InjectorFactoryImpl INSTANCE = new InjectorFactoryImpl();
@Override
public ConstructorInjector createConstructor(Constructor constructor, ResteasyProviderFactory providerFactory)
{
return new ConstructorInjectorImpl(constructor, providerFactory);
}
@Override
public ConstructorInjector createConstructor(ResourceConstructor constructor, ResteasyProviderFactory providerFactory)
{
return new ConstructorInjectorImpl(constructor, providerFactory);
}
@Override
public PropertyInjector createPropertyInjector(Class resourceClass, ResteasyProviderFactory providerFactory)
{
return new PropertyInjectorImpl(resourceClass, providerFactory);
}
@Override
public PropertyInjector createPropertyInjector(ResourceClass resourceClass, ResteasyProviderFactory providerFactory)
{
return new ResourcePropertyInjector(resourceClass, providerFactory);
}
@Override
public MethodInjector createMethodInjector(ResourceLocator method, ResteasyProviderFactory factory)
{
return new MethodInjectorImpl(method, factory);
}
@Override
public ValueInjector (Parameter parameter, ResteasyProviderFactory providerFactory)
{
return OptionalInjections.wrap(parameter.getGenericType(), innerType -> createParameterExtractor0(parameter, providerFactory, innerType));
}
private ValueInjector (Parameter parameter, ResteasyProviderFactory providerFactory, Type parameterType)
{
Class<?> rawType = Types.getRawType(parameterType);
switch (parameter.getParamType())
{
case QUERY_PARAM:
return new QueryParamInjector(rawType, parameterType, parameter.getAccessibleObject(), parameter.getParamName(), parameter.getDefaultValue(), parameter.isEncoded(), parameter.getAnnotations(), providerFactory);
case QUERY:
return new QueryInjector(rawType, providerFactory);
case HEADER_PARAM:
return new HeaderParamInjector(rawType, parameterType, parameter.getAccessibleObject(), parameter.getParamName(), parameter.getDefaultValue(), parameter.getAnnotations(), providerFactory);
case FORM_PARAM:
return new FormParamInjector(rawType, parameterType, parameter.getAccessibleObject(), parameter.getParamName(), parameter.getDefaultValue(), parameter.isEncoded(), parameter.getAnnotations(), providerFactory);
case COOKIE_PARAM:
return new CookieParamInjector(rawType, parameterType, parameter.getAccessibleObject(), parameter.getParamName(), parameter.getDefaultValue(), parameter.getAnnotations(), providerFactory);
case PATH_PARAM:
return new PathParamInjector(parameter.getType(), parameter.getGenericType(), parameter.getAccessibleObject(), parameter.getParamName(), parameter.getDefaultValue(), parameter.isEncoded(), parameter.getAnnotations(), providerFactory);
case FORM:
{
String prefix = parameter.getParamName();
if (prefix.length() > 0)
{
if (parameterType instanceof ParameterizedType)
{
ParameterizedType pType = (ParameterizedType) parameterType;
if (Types.isA(List.class, pType))
{
return new ListFormInjector(rawType, Types.getArgumentType(pType, 0), prefix, providerFactory);
}
if (Types.isA(Map.class, pType))
{
return new MapFormInjector(rawType, Types.getArgumentType(pType, 0), Types.getArgumentType(pType, 1), prefix, providerFactory);
}
}
return new PrefixedFormInjector(rawType, prefix, providerFactory);
}
return new FormInjector(rawType, providerFactory);
}
case BEAN_PARAM:
return new FormInjector(rawType, providerFactory);
case MATRIX_PARAM:
return new MatrixParamInjector(rawType, parameterType, parameter.getAccessibleObject(), parameter.getParamName(), parameter.getDefaultValue(), parameter.isEncoded(), parameter.getAnnotations(), providerFactory);
case CONTEXT:
return new ContextParameterInjector(null, rawType, parameterType, parameter.getAnnotations(), providerFactory);
case SUSPENDED:
return new AsynchronousResponseInjector();
case MESSAGE_BODY:
return new MessageBodyParameterInjector(parameter.getResourceClass().getClazz(), parameter.getAccessibleObject(), rawType, parameterType, parameter.getAnnotations(), providerFactory);
default:
return null;
}
}
@Override
public ValueInjector (Class injectTargetClass, AccessibleObject injectTarget, String defaultName, Class type,
Type genericType, Annotation[] annotations, ResteasyProviderFactory providerFactory)
{
return createParameterExtractor(injectTargetClass, injectTarget, defaultName, type, genericType, annotations, true, providerFactory);
}
@Override
public ValueInjector (Class injectTargetClass, AccessibleObject injectTarget, String defaultName, Class type, Type genericType, Annotation[] annotations, boolean useDefault, ResteasyProviderFactory providerFactory)
{
return OptionalInjections.wrap(genericType, innerType -> createParameterExtractor0(injectTargetClass, injectTarget, defaultName, genericType.equals(innerType) ? type : Types.getRawType(innerType), innerType, annotations, useDefault, providerFactory));
}
private ValueInjector (Class injectTargetClass, AccessibleObject injectTarget, String defaultName, Class type, Type genericType, Annotation[] annotations, boolean useDefault, ResteasyProviderFactory providerFactory)
{
DefaultValue defaultValue = findAnnotation(annotations, DefaultValue.class);
boolean encode = findAnnotation(annotations, Encoded.class) != null || injectTarget.isAnnotationPresent(Encoded.class) || type.isAnnotationPresent(Encoded.class);
String defaultVal = null;
if (defaultValue != null) defaultVal = defaultValue.value();
QueryParam queryParam;
HeaderParam header;
MatrixParam matrix;
PathParam uriParam;
CookieParam cookie;
FormParam formParam;
Form form;
if ((queryParam = findAnnotation(annotations, QueryParam.class)) != null)
{
return new QueryParamInjector(type, genericType, injectTarget, queryParam.value(), defaultVal, encode, annotations, providerFactory);
}
else if (findAnnotation(annotations, org.jboss.resteasy.annotations.jaxrs.QueryParam.class) != null)
{
return new QueryParamInjector(type, genericType, injectTarget, defaultName, defaultVal, encode, annotations, providerFactory);
}
else if (findAnnotation(annotations, Query.class) != null) {
return new QueryInjector(type, providerFactory);
}
else if ((header = findAnnotation(annotations, HeaderParam.class)) != null)
{
return new HeaderParamInjector(type, genericType, injectTarget, header.value(), defaultVal, annotations, providerFactory);
}
else if (findAnnotation(annotations, org.jboss.resteasy.annotations.jaxrs.HeaderParam.class) != null)
{
return new HeaderParamInjector(type, genericType, injectTarget, defaultName, defaultVal, annotations, providerFactory);
}
else if ((formParam = findAnnotation(annotations, FormParam.class)) != null)
{
return new FormParamInjector(type, genericType, injectTarget, formParam.value(), defaultVal, encode, annotations, providerFactory);
}
else if (findAnnotation(annotations, org.jboss.resteasy.annotations.jaxrs.FormParam.class) != null)
{
return new FormParamInjector(type, genericType, injectTarget, defaultName, defaultVal, encode, annotations, providerFactory);
}
else if ((cookie = findAnnotation(annotations, CookieParam.class)) != null)
{
return new CookieParamInjector(type, genericType, injectTarget, cookie.value(), defaultVal, annotations, providerFactory);
}
else if (findAnnotation(annotations, org.jboss.resteasy.annotations.jaxrs.CookieParam.class) != null)
{
return new CookieParamInjector(type, genericType, injectTarget, defaultName, defaultVal, annotations, providerFactory);
}
else if ((uriParam = findAnnotation(annotations, PathParam.class)) != null)
{
return new PathParamInjector(type, genericType, injectTarget, uriParam.value(), defaultVal, encode, annotations, providerFactory);
}
else if (findAnnotation(annotations, org.jboss.resteasy.annotations.jaxrs.PathParam.class) != null)
{
return new PathParamInjector(type, genericType, injectTarget, defaultName, defaultVal, encode, annotations, providerFactory);
}
else if ((form = findAnnotation(annotations, Form.class)) != null)
{
String prefix = form.prefix();
if (prefix.length() > 0)
{
if (genericType instanceof ParameterizedType)
{
ParameterizedType pType = (ParameterizedType) genericType;
if (Types.isA(List.class, pType))
{
return new ListFormInjector(type, Types.getArgumentType(pType, 0), prefix, providerFactory);
}
if (Types.isA(Map.class, pType))
{
return new MapFormInjector(type, Types.getArgumentType(pType, 0), Types.getArgumentType(pType, 1), prefix, providerFactory);
}
}
return new PrefixedFormInjector(type, prefix, providerFactory);
}
return new FormInjector(type, providerFactory);
}
else if (findAnnotation(annotations, BeanParam.class) != null)
{
return new FormInjector(type, providerFactory);
}
else if ((matrix = findAnnotation(annotations, MatrixParam.class)) != null)
{
return new MatrixParamInjector(type, genericType, injectTarget, matrix.value(), defaultVal, encode, annotations, providerFactory);
}
else if (findAnnotation(annotations, org.jboss.resteasy.annotations.jaxrs.MatrixParam.class) != null)
{
return new MatrixParamInjector(type, genericType, injectTarget, defaultName, defaultVal, encode, annotations, providerFactory);
}
else if (findAnnotation(annotations, Context.class) != null)
{
return new ContextParameterInjector(null, type, genericType, annotations, providerFactory);
}
else if (findAnnotation(annotations, Suspended.class) != null)
{
return new AsynchronousResponseInjector();
}
else if (javax.ws.rs.container.AsyncResponse.class.isAssignableFrom(type))
{
return new AsynchronousResponseInjector();
}
else if (useDefault)
{
return new MessageBodyParameterInjector(injectTargetClass, injectTarget, type, genericType, annotations, providerFactory);
}
else
{
return null;
}
}
enum OptionalInjections {
OPT (Optional.class, OptionalInjections::getTypeArgument, Optional::empty, Optional::of),
OINT (OptionalInt.class, x -> Integer.class, OptionalInt::empty, v -> OptionalInt.of((Integer) v)),
OLONG (OptionalLong.class, x -> Long.class, OptionalLong::empty, v -> OptionalLong.of((Long) v)),
ODOUBLE (OptionalDouble.class, x -> Double.class, OptionalDouble::empty, v -> OptionalDouble.of((Double) v)),
;
static Map<Class<?>, OptionalInjections> optionalTypes;
static {
optionalTypes = Arrays.stream(OptionalInjections.values())
.collect(Collectors.toMap(o -> o.optional, Function.identity()));
}
final Class<?> optional;
final Function<Type, Type> valueType;
final Supplier<Object> empty;
final Function<Object, Object> present;
OptionalInjections(final Class<?> optional, final Function<Type, Type> valueType,
final Supplier<Object> empty, final Function<Object, Object> present)
{
this.optional = optional;
this.valueType = valueType;
this.empty = empty;
this.present = present;
}
static ValueInjector wrap(Type paramType, Function<Type, ValueInjector> injectorFactory) {
return Optional.ofNullable(optionalTypes.get(Types.getRawType(paramType)))
.<ValueInjector>map(oi -> {
ValueInjector valueInjector = injectorFactory.apply(oi.valueType.apply(paramType));
return valueInjector == null ? null : new DelegatingInjector(oi, valueInjector);
})
.orElseGet(() -> injectorFactory.apply(paramType));
}
static Type getTypeArgument(Type type) {
if (!(type instanceof ParameterizedType))
throw new UnsupportedOperationException("non-parameterized Optional type: " + type);
return ((ParameterizedType) type).getActualTypeArguments()[0];
}
static class DelegatingInjector implements ValueInjector {
private final OptionalInjections oi;
private final ValueInjector delegate;
DelegatingInjector(final OptionalInjections oi, final ValueInjector delegate) {
this.oi = oi;
this.delegate = delegate;
}
@Override
public Object inject(boolean unwrapAsync) {
Object injectedValue = delegate.inject(unwrapAsync);
if (injectedValue != null && injectedValue instanceof CompletionStage) {
return ((CompletionStage<Object>)injectedValue).thenApply(this::wrap);
}
return wrap(CompletionStageHolder.resolve(injectedValue));
}
@Override
public Object inject(HttpRequest request, HttpResponse response, boolean unwrapAsync) {
Object injectedValue = delegate.inject(request, response, unwrapAsync);
injectedValue = injectedValue;
if (injectedValue != null && injectedValue instanceof CompletionStage) {
return ((CompletionStage<Object>)injectedValue).thenApply(this::wrap);
}
return wrap(CompletionStageHolder.resolve(injectedValue));
}
public Object wrap(Object value) {
if (value == null) {
return oi.empty.get();
}
return oi.present.apply(value);
}
}
}
}