All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.craftforge.essential.controller.resolvers.DefaultInputResolver Maven / Gradle / Ivy

The newest version!
/*
 * This file is part of essential.
 *
 *     essential is free software: you can redistribute it and/or modify
 *     it under the terms of the GNU General Public License as published by
 *     the Free Software Foundation, either version 3 of the License, or
 *     (at your option) any later version.
 *
 *     essential is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU General Public License for more details.
 *
 *     You should have received a copy of the GNU General Public License
 *     along with essential.  If not, see .
 */

package net.craftforge.essential.controller.resolvers;

import net.craftforge.essential.controller.ControllerConfiguration;
import net.craftforge.essential.controller.annotations.*;
import net.craftforge.essential.controller.utils.ControllerReflUtils;
import net.craftforge.essential.core.constants.Charsets;
import net.craftforge.essential.core.constants.HttpHeaders;
import net.craftforge.essential.core.exceptions.InjectionException;
import net.craftforge.essential.core.exceptions.MissingHeaderException;
import net.craftforge.essential.core.resolvers.*;
import net.craftforge.essential.supply.Consumer;
import net.craftforge.essential.supply.exceptions.BadInputException;
import net.craftforge.essential.supply.exceptions.UnsupportedMediaTypeException;
import net.craftforge.reflection.managers.ClassManager;
import net.craftforge.reflection.utils.ReflUtils;

import javax.inject.Inject;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

/**
 * {@inheritDoc}
 *
 * @author Christian Bick
 * @since 21.08.11
 */
public class DefaultInputResolver implements InputResolver {

    private BodyResolver bodyResolver;
    private HeaderResolver headerResolver;
    private QueryParamResolver queryParamResolver;
    private PathParamResolver pathParamResolver;
    private ControllerConfiguration configuration;

    @Inject
    public DefaultInputResolver(BodyResolver bodyResolver, HeaderResolver headerResolver,
                                QueryParamResolver queryParamResolver, PathParamResolver pathParamResolver,
                                ControllerConfiguration configuration) {
        this.bodyResolver = bodyResolver;
        this.headerResolver = headerResolver;
        this.queryParamResolver = queryParamResolver;
        this.pathParamResolver = pathParamResolver;
        this.configuration = configuration;
    }

    /**
     * {@inheritDoc}
     */
    public Object getInputForField(Field field, Consumer consumer)
            throws BadInputException, UnsupportedMediaTypeException, UnsupportedEncodingException {

        Class type = field.getType();
        // Try to obtain a body annotation
        if (ControllerReflUtils.isBodyOnProperty(field)) {
            return getBodyInput(consumer, type);
        }
        String[] defaultValues = ControllerReflUtils.getDefaultValuesFromProperty(field);
        // Try to obtain a header annotation
        String headerName = ControllerReflUtils.getHeaderFromProperty(field);
        if (headerName != null) {
            return getHeaderInput(type, headerName, defaultValues);
        }
        // Try to obtain a parameter annotation
        String paramName = ControllerReflUtils.getParamFromProperty(field);
        if (paramName != null) {
            paramName = paramName.isEmpty() ? field.getName() : paramName;
            Type genericType = field.getGenericType();
            return getParameterInput(type, genericType, paramName, defaultValues);
        }
        // Try to obtain a property annotation
        String propertyName = ControllerReflUtils.getPropertyFromProperty(field);
        if (propertyName != null) {
            return getPropertyInput(type, propertyName, defaultValues);
        }
        return null;
    }

    /**
     * {@inheritDoc}
     */
    public List getInputForMethodParameters(Method method, Consumer consumer)
            throws BadInputException, UnsupportedMediaTypeException, UnsupportedEncodingException {

        Class[] paramTypes = method.getParameterTypes();
        Type[] paramGenericTypes = method.getGenericParameterTypes();

        List parameterInputs = new ArrayList(method.getParameterTypes().length);

        Annotation[][] usedAnnotations = ClassManager.getInstance(method).getMethodParameterAnnotations(method, Body.class);
        if (usedAnnotations == null || usedAnnotations.length < 1) {
            usedAnnotations = ClassManager.getInstance(method).getMethodParameterAnnotations(method, Header.class);
        }
        if (usedAnnotations == null || usedAnnotations.length < 1) {
            usedAnnotations = ClassManager.getInstance(method).getMethodParameterAnnotations(method, Param.class);
        }
        if (usedAnnotations == null || usedAnnotations.length < 1) {
            usedAnnotations = ClassManager.getInstance(method).getMethodParameterAnnotations(method, Property.class);
        }
        if (usedAnnotations == null || usedAnnotations.length < 1) {
            return Collections.emptyList();
        }

        int paramIterator = 0;
        for (Annotation[] annotations : usedAnnotations) {
            Class type = paramTypes[paramIterator];
            Type genericType = paramGenericTypes[paramIterator];

            String[] defaultValues = null;
            for (Annotation ann : annotations) {
                if (ann.annotationType().equals(DefaultValue.class)) {
                    defaultValues = ((DefaultValue)ann).value();
                    break;
                }
            }

            for (Annotation ann : annotations) {
                if (ann.annotationType().equals(Body.class)) {
                    Object input = getBodyInput(consumer, type);
                    parameterInputs.add(input);
                    break;
                } else if (ann.annotationType().equals(Header.class)) {
                    String headerName = ((Header)ann).value();
                    Object input = getHeaderInput(type, headerName, defaultValues);
                    parameterInputs.add(input);
                    break;
                } else if (ann.annotationType().equals(Param.class)) {
                    String paramName = ((Param)ann).value();
                    Object input = getParameterInput(type, genericType, paramName, defaultValues);
                    parameterInputs.add(input);
                    break;
                } else if (ann.annotationType().equals(Property.class)) {
                    String propertyName = ((Property)ann).value();
                    Object input = getPropertyInput(type, propertyName, defaultValues);
                    parameterInputs.add(input);
                    break;
                }
            }
            paramIterator++;
        }
        return parameterInputs;
    }

    /**
     * Gets the object of the given type from the request body content. Uses the given consumer
     * for deserialization.
     *
     * @param consumer The consumer
     * @param type The desired object's type
     * @return The body content as object
     * @throws BadInputException if the request body content for the input is not valid
     * @throws UnsupportedEncodingException if the request body's charset encoding is not supported
     * @throws UnsupportedMediaTypeException if the request body's media type is not supported
     */
    protected Object getBodyInput(Consumer consumer, Class type)
            throws BadInputException, UnsupportedEncodingException, UnsupportedMediaTypeException {

        String contentCharset = headerResolver.getHeaderValueAttribute(HttpHeaders.CONTENT_TYPE, "charset", Charsets.UTF8);
        if (type.isAssignableFrom(InputStream.class)) {
            return bodyResolver.getBodyInputStream();
        } else if (type.isAssignableFrom(String.class)) {
            return bodyResolver.getBody(contentCharset);
        } else {
            return bodyResolver.getBodyAsObject(consumer, type, contentCharset);
        }
    }

    /**
     * Gets the header value for the specified header name for a single or multi header depending on the given type.
     * Uses the given default values if the header is not present.
     *
     * @param type String for single header or String[] for multi header
     * @param headerName The header name
     * @param defaultValues The default values
     * @return The parameter values(s)
     * @throws MissingHeaderException if the header is not present an not default values a are given.
     */
    protected Object getHeaderInput(Class type, String headerName, String[] defaultValues)
            throws MissingHeaderException {

        if (type.isAssignableFrom(String[].class)) {
            if (defaultValues == null) {
                return headerResolver.getHeaderValues(headerName);
            } else {
                return headerResolver.getHeaderValues(headerName, defaultValues);
            }
        } else if (type.isAssignableFrom(String.class)) {
            if (defaultValues == null || defaultValues.length < 1) {
                return headerResolver.getHeaderValue(headerName);
            } else {
                return headerResolver.getHeaderValue(headerName, defaultValues[0]);
            }
        }
        throw getHeaderInjectionException(headerName);
    }

    /**
     * 

Gets the parameter value for the given parameter name and converts it an object of the given class.

*

Beside this, the following three behaviors are supported:

*
    *
  1. if the class is of type String then it will return the parameter value directly
  2. *
  3. if the parameter value is String[] then all values of parameters with the given name will be * returned directly
  4. *
  5. if the class implements Collection and the generic type is given then a collection of the given type * is returned containing the values of all parameters with the given name.
  6. *
* * * @param type The object's type * @param genericType The generic type in case of collections * @param parameterName The parameter name * @param defaultValues The default values * @return The parameter values(s) as object * @throws BadInputException if the parameter value for the input is not valid */ protected Object getParameterInput(Class type, Type genericType, String parameterName, String[] defaultValues) throws BadInputException { // Choose the right parameter resolver ParamResolver resolver; if (parameterName.startsWith("{") && parameterName.endsWith("}")) { resolver = pathParamResolver; } else { resolver = queryParamResolver; } if (type.isAssignableFrom(String[].class)) { return resolver.getParameterValues(parameterName, defaultValues); } else if (type.isAssignableFrom(String.class)) { if (defaultValues == null || defaultValues.length < 1) { return resolver.getParameterValue(parameterName); } return resolver.getParameterValue(parameterName, defaultValues[0]); } else if (ReflUtils.isVirtualPrimitive(type)) { if (defaultValues == null || defaultValues.length < 1) { return resolver.getParameterValueAsObject(type, parameterName); } return resolver.getParameterValueAsObject(type, parameterName, defaultValues[0]); } else if (Collection.class.isAssignableFrom(type) && genericType instanceof ParameterizedType) { Class typeArgument = (Class)((ParameterizedType)genericType).getActualTypeArguments()[0]; if (! ReflUtils.isVirtualPrimitive(typeArgument)) { throw getParameterInjectionException(parameterName); } if (defaultValues == null || defaultValues.length < 1) { return resolver.getParameterValuesAsObjects(type, typeArgument, parameterName); } return resolver.getParameterValuesAsObjects(type, typeArgument, parameterName, defaultValues); } throw getParameterInjectionException(parameterName); } /** * Gets the property value or multiple values depending on the given type. * Uses the first of the given default values if the property is not present. * * @param type The object's type * @param propertyName The property name * @param defaultValues The default values * @return The property value(s) */ protected Object getPropertyInput(Class type, String propertyName, String[] defaultValues) { if (! type.isAssignableFrom(String.class)) { throw getPropertyInjectionException(propertyName); } if (defaultValues == null || defaultValues.length < 1) { return configuration.getProperty(propertyName); } String defaultValue = defaultValues[0]; return configuration.getProperty(propertyName, defaultValue); } protected InjectionException getHeaderInjectionException(String headerName) { return new InjectionException("Failed to inject header " + headerName + " because the " + "target type neither applicable to String nor to String[]"); } protected InjectionException getPropertyInjectionException(String propertyName) { return new InjectionException("Failed to inject property " + propertyName + " because the " + "target type is neither applicable to String nor to Map"); } protected InjectionException getParameterInjectionException(String parameterName) { return new InjectionException("Failed inject parameter " + parameterName + " because the " + "target type is not applicable to a virtual primitive (e.g. int, Double, String, Date)"); } }