com.laxser.blitz.web.paramresolver.ResolverFactoryImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.laxser.blitz.web.paramresolver.ResolverFactoryImpl.java

Source

/*
 * $Id$
 * Copyright 2007-2009 the original author or authors.
 *
 * Licensed 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 com.laxser.blitz.web.paramresolver;

import java.beans.PropertyEditorSupport;
import java.lang.annotation.Annotation;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.servlet.ServletContext;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.SimpleTypeConverter;
import org.springframework.beans.TypeConverter;
import org.springframework.context.ApplicationContext;
import org.springframework.context.MessageSource;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.validation.BindingResult;
import org.springframework.validation.Errors;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.multipart.MultipartRequest;
import org.springframework.web.multipart.MultipartResolver;
import org.springframework.web.util.WebUtils;

import com.laxser.blitz.util.BlitzBeanUtils;
import com.laxser.blitz.web.Invocation;
import com.laxser.blitz.web.annotation.Create;
import com.laxser.blitz.web.annotation.DefValue;
import com.laxser.blitz.web.annotation.FlashParam;
import com.laxser.blitz.web.annotation.Param;
import com.laxser.blitz.web.annotation.Pattern;
import com.laxser.blitz.web.impl.module.Module;
import com.laxser.blitz.web.impl.thread.InvocationBean;
import com.laxser.blitz.web.impl.thread.Blitz;
import com.laxser.blitz.web.var.Flash;
import com.laxser.blitz.web.var.Model;

/**
 * @author laxser  Date 2012-3-23 ?4:47:53
@contact [duqifan@gmail.com]
@ResolverFactoryImpl.java
 */
public class ResolverFactoryImpl implements ResolverFactory {

    private static Log logger = LogFactory.getLog(MethodParameterResolver.class);

    public static final String MAP_SEPARATOR = ":";

    private static final Map<Class<?>, Class<?>> primitiveWrapperTypeMap = new HashMap<Class<?>, Class<?>>(8);
    static {
        primitiveWrapperTypeMap.put(boolean.class, Boolean.class);
        primitiveWrapperTypeMap.put(byte.class, Byte.class);
        primitiveWrapperTypeMap.put(char.class, Character.class);
        primitiveWrapperTypeMap.put(double.class, Double.class);
        primitiveWrapperTypeMap.put(float.class, Float.class);
        primitiveWrapperTypeMap.put(int.class, Integer.class);
        primitiveWrapperTypeMap.put(long.class, Long.class);
        primitiveWrapperTypeMap.put(short.class, Short.class);
    }

    private static final Map<Class<?>, Class<?>> arrayTypeMap = new HashMap<Class<?>, Class<?>>();
    static {
        arrayTypeMap.put(boolean.class, Boolean.class);
        arrayTypeMap.put(byte.class, byte[].class);
        arrayTypeMap.put(char.class, char[].class);
        arrayTypeMap.put(double.class, double[].class);
        arrayTypeMap.put(float.class, float[].class);
        arrayTypeMap.put(int.class, int[].class);
        arrayTypeMap.put(long.class, long[].class);
        arrayTypeMap.put(short.class, short[].class);

        arrayTypeMap.put(Boolean.class, Boolean[].class);
        arrayTypeMap.put(Byte.class, Byte[].class);
        arrayTypeMap.put(Character.class, Character[].class);
        arrayTypeMap.put(Double.class, Double[].class);
        arrayTypeMap.put(Float.class, Float[].class);
        arrayTypeMap.put(Integer.class, Integer[].class);
        arrayTypeMap.put(Long.class, Long[].class);
        arrayTypeMap.put(Short.class, Short[].class);
    }

    private static final ParamResolver[] buildinResolvers = new ParamResolver[] { //
            new InvocationResolver(), //
            new BlitzResolver(), //
            new ApplicationContextResolver(), //
            new MessageSourceResolver(), //
            new ModelResolver(), //
            new FlashResolver(), //
            new ModuleResolver(), //
            new IndexAliasResolver(new StringResolver()), //
            new RequestResolver(), //
            new ResponseResolver(), //
            new HttpSessionResolver(), //
            new MultipartFileResolver(), //
            new MultipartRequestResolver(), //
            new MultipartHttpServletRequestResolver(), //
            new ServletContextResolver(), //
            new IndexAliasResolver(new ArrayResolver()), //
            new IndexAliasResolver(new ListResolver()), //
            new IndexAliasResolver(new SetResolver()), //
            new IndexAliasResolver(new MapResolver()), //
            new BindingResultResolver(), //
            new IndexAliasResolver(new DateResolver()), //
            new IndexAliasResolver(new EditorResolver()), //
            new IndexAliasResolver(new EnumResolver()), //
            new BeanResolver(), //
            new IndexAliasResolver(new MultipartResolverResolver()), //
    };

    private static class IndexAliasResolver implements ParamResolver {

        ParamResolver inner;

        public IndexAliasResolver(ParamResolver inner) {
            this.inner = inner;
        }

        @Override
        public boolean supports(ParamMetaData metaData) {
            if (inner.supports(metaData)) {
                addIndexAliasParamNameIfNeccessary(metaData);
                return true;
            }
            return false;
        }

        @Override
        public Object resolve(Invocation inv, ParamMetaData metaData) throws Exception {
            return inner.resolve(inv, metaData);
        }

        @Override
        public String toString() {
            return inner.toString();
        }

    }

    public ResolverFactoryImpl() {
    }

    private final List<ParamResolver> customerResolvers = new ArrayList<ParamResolver>();

    public void addCustomerResolver(ParamResolver resolver) {
        customerResolvers.add(new IndexAliasResolver(resolver));
    }

    @Override
    public ParamResolver supports(ParamMetaData metaData) {
        for (ParamResolver resolver : customerResolvers) {
            if (resolver.supports(metaData)) {
                return resolver;
            }
        }
        for (ParamResolver resolver : buildinResolvers) {
            if (resolver.supports(metaData)) {
                return resolver;
            }
        }
        return null;
    }

    // ---------------------------------------------------------

    public static final class InvocationResolver implements ParamResolver {

        @Override
        public boolean supports(ParamMetaData metaData) {
            return Invocation.class == metaData.getParamType();
        }

        @Override
        public Invocation resolve(Invocation inv, ParamMetaData metaData) {
            return inv;
        }
    }

    public static final class ApplicationContextResolver implements ParamResolver {

        @Override
        public boolean supports(ParamMetaData metaData) {
            return ApplicationContext.class == metaData.getParamType()
                    || WebApplicationContext.class == metaData.getParamType();
        }

        @Override
        public ApplicationContext resolve(Invocation inv, ParamMetaData metaData) {
            return inv.getApplicationContext();
        }
    }

    public static final class MessageSourceResolver implements ParamResolver {

        @Override
        public boolean supports(ParamMetaData metaData) {
            return MessageSource.class == metaData.getParamType();
        }

        @Override
        public MessageSource resolve(Invocation inv, ParamMetaData metaData) {
            return inv.getApplicationContext();
        }
    }

    public static final class RequestResolver implements ParamResolver {

        @Override
        public boolean supports(ParamMetaData metaData) {
            return HttpServletRequest.class == metaData.getParamType()
                    || ServletRequest.class == metaData.getParamType();
        }

        @Override
        public HttpServletRequest resolve(Invocation inv, ParamMetaData metaData) {
            return inv.getRequest();
        }
    }

    public static final class ResponseResolver implements ParamResolver {

        @Override
        public boolean supports(ParamMetaData metaData) {
            return HttpServletResponse.class == metaData.getParamType()
                    || ServletResponse.class == metaData.getParamType();
        }

        @Override
        public ServletResponse resolve(Invocation inv, ParamMetaData metaData) {
            return inv.getResponse();
        }
    }

    public static final class ServletContextResolver implements ParamResolver {

        @Override
        public boolean supports(ParamMetaData metaData) {
            return ServletContext.class == metaData.getParamType();
        }

        @Override
        public ServletContext resolve(Invocation inv, ParamMetaData metaData) {
            return inv.getServletContext();
        }
    }

    public static final class HttpSessionResolver implements ParamResolver {

        @Override
        public boolean supports(ParamMetaData metaData) {
            return HttpSession.class == metaData.getParamType();
        }

        @Override
        public HttpSession resolve(Invocation inv, ParamMetaData metaData) {
            boolean create = true;
            Create createAnnotation = metaData.getAnnotation(Create.class);
            if (createAnnotation != null) {
                create = createAnnotation.value();
            }
            return inv.getRequest().getSession(create);
        }
    }

    public static final class ModelResolver implements ParamResolver {

        @Override
        public boolean supports(ParamMetaData metaData) {
            return Model.class == metaData.getParamType();
        }

        @Override
        public Object resolve(Invocation inv, ParamMetaData metaData) {
            return inv.getModel();
        }
    }

    public static final class FlashResolver implements ParamResolver {

        @Override
        public boolean supports(ParamMetaData metaData) {
            return Flash.class == metaData.getParamType();
        }

        @Override
        public Flash resolve(Invocation inv, ParamMetaData metaData) {
            return inv.getFlash();
        }
    }

    public static final class ModuleResolver implements ParamResolver {

        @Override
        public boolean supports(ParamMetaData metaData) {
            return Module.class == metaData.getParamType();
        }

        @Override
        public Module resolve(Invocation inv, ParamMetaData metaData) {
            return ((InvocationBean) inv).getModule();
        }
    }

    public static final class StringResolver implements ParamResolver {

        @Override
        public boolean supports(ParamMetaData metaData) {
            return String.class == metaData.getParamType();
        }

        @Override
        public String resolve(Invocation inv, ParamMetaData metaData) {
            for (String paramName : metaData.getParamNames()) {
                if (paramName != null) {
                    String value = inv.getParameter(paramName);
                    if (value != null) {
                        return value;
                    }
                }
            }
            return null;
        }
    }

    public static final class MultipartRequestResolver implements ParamResolver {

        @Override
        public boolean supports(ParamMetaData metaData) {
            return MultipartRequest.class == metaData.getParamType()
                    || MultipartHttpServletRequest.class == metaData.getParamType();
        }

        @Override
        public MultipartRequest resolve(Invocation inv, ParamMetaData metaData) {
            if (inv.getRequest() instanceof MultipartRequest) {
                return (MultipartRequest) inv.getRequest();
            } else {
                if (logger.isDebugEnabled()) {
                    logger.debug("cann't set MultipartRequest param to method "
                            + ", the request is not a MultipartRequest");
                }
                return null;
            }
        }
    }

    public static final class MultipartHttpServletRequestResolver implements ParamResolver {

        @Override
        public boolean supports(ParamMetaData metaData) {
            return MultipartHttpServletRequest.class == metaData.getParamType();
        }

        @Override
        public MultipartRequest resolve(Invocation inv, ParamMetaData metaData) {
            if (inv.getRequest() instanceof MultipartHttpServletRequest) {
                return (MultipartHttpServletRequest) inv.getRequest();
            } else {
                if (logger.isDebugEnabled()) {
                    logger.debug("cann't set MultipartRequest param to method "
                            + ", the request is not a MultipartRequest");
                }
                return null;
            }
        }
    }

    public static final class MultipartResolverResolver implements ParamResolver {

        @Override
        public boolean supports(ParamMetaData metaData) {
            return MultipartResolver.class == metaData.getParamType();
        }

        @Override
        public Object resolve(Invocation inv, ParamMetaData metaData) throws Exception {
            return ((InvocationBean) inv).getModuleEngine().getMultipartResolver();
        }
    }

    public static final class MultipartFileResolver implements ParamResolver {

        @Override
        public boolean supports(ParamMetaData metaData) {
            return MultipartFile.class == metaData.getParamType();
        }

        @Override
        public MultipartFile resolve(Invocation inv, ParamMetaData metaData) {
            MultipartFile multipartFile = null;
            if (inv.getRequest() instanceof MultipartRequest) {
                MultipartRequest multipartRequest = (MultipartRequest) inv.getRequest();
                String fileName = metaData.getParamName();
                if (StringUtils.isBlank(fileName)) {
                    @SuppressWarnings("unchecked")
                    Iterator<String> allFileNames = multipartRequest.getFileNames();
                    if (allFileNames.hasNext()) {
                        fileName = allFileNames.next();
                    }
                }
                if (StringUtils.isNotBlank(fileName)) {
                    multipartFile = multipartRequest.getFile(fileName);
                }
                if (multipartFile == null) {
                    if (StringUtils.isNotBlank(fileName)) {
                        if (logger.isDebugEnabled()) {
                            logger.debug("not found multipartFile named " + fileName + " in this request: "
                                    + inv.getRequestPath().getUri());
                        }
                    } else {
                        if (logger.isDebugEnabled()) {
                            logger.debug("not found MultipartFile named:" + metaData.getParamName()
                                    + " in this request: " + inv.getRequestPath().getUri());
                        }
                    }
                }
            } else {
                if (logger.isDebugEnabled()) {
                    logger.debug("cann't set MultipartFile param to method "
                            + ", the request is not a MultipartRequest");
                }
            }
            return multipartFile;
        }
    }

    public static final class BlitzResolver implements ParamResolver {

        @Override
        public boolean supports(ParamMetaData metaData) {
            boolean result = metaData.getParamType() == Blitz.class;
            if (result) {
                if (!metaData.getControllerClass().getName().startsWith("com.laxser.blitz")) {
                    throw new IllegalStateException("Blitz is not allowed as a method parameter:" + metaData);
                }
            }
            return result;
        }

        @Override
        public Object resolve(Invocation inv, ParamMetaData metaData) throws Exception {
            return ((InvocationBean) inv).getBlitz();
        }
    }

    public static final class EnumResolver implements ParamResolver {

        @Override
        public boolean supports(ParamMetaData metaData) {
            return metaData.getParamType().isEnum();

        }

        @SuppressWarnings({ "unchecked", "rawtypes" })
        @Override
        public Object resolve(Invocation inv, ParamMetaData metaData) {
            for (String paramName : metaData.getParamNames()) {
                if (paramName != null) {
                    String value = inv.getParameter(paramName);
                    if (value != null) {
                        return Enum.valueOf((Class<? extends Enum>) metaData.getParamType(), value);
                    }
                }
            }
            return null;
        }
    }

    public static final class BeanResolver implements ParamResolver {

        @Override
        public boolean supports(ParamMetaData metaData) {
            return !Modifier.isAbstract(metaData.getParamType().getModifiers());

        }

        @Override
        public Object resolve(Invocation inv, ParamMetaData metaData) {
            Object bean = BlitzBeanUtils.instantiateClass(metaData.getParamType());
            ServletRequestDataBinder binder;
            if (!metaData.isAnnotationPresent(Param.class)) {
                binder = new ServletRequestDataBinder(bean);
            } else {
                binder = new ServletRequestDataBinder(bean, metaData.getParamName());
            }
            binder.bind(inv.getRequest());
            String bindingResultName = BindingResult.MODEL_KEY_PREFIX + metaData.getParamName() + "BindingResult";
            inv.addModel(bindingResultName, binder.getBindingResult());
            return bean;
        }
    }

    public static final class BindingResultResolver implements ParamResolver {

        @Override
        public boolean supports(ParamMetaData metaData) {
            return BindingResult.class == metaData.getParamType() || Errors.class == metaData.getParamType();
        }

        @Override
        public BindingResult resolve(Invocation inv, ParamMetaData metaData) {
            if (metaData.getParamName() != null) {
                return inv.getBindingResult(metaData.getParamName());
            } else {
                return inv.getParameterBindingResult();
            }
        }
    }

    @SuppressWarnings("unchecked")
    private static Object resolveArray(Invocation inv, ParamMetaData metaData, Class<?> compnentType) {
        if (compnentType == MultipartFile.class) {
            String filterName = metaData.getParamName();
            if (filterName == null) {
                filterName = "";
            }
            if (inv.getRequest() instanceof MultipartRequest) {
                List<MultipartFile> files = new LinkedList<MultipartFile>();
                MultipartRequest multipartRequest = (MultipartRequest) inv.getRequest();
                Iterator<String> names = multipartRequest.getFileNames();
                while (names.hasNext()) {
                    String name = names.next();
                    if (name.startsWith(filterName)) {
                        files.add(multipartRequest.getFile(name));
                    }
                }
                return files.toArray(new MultipartFile[0]);
            } else {
                if (logger.isDebugEnabled()) {
                    logger.debug("cann't " + "set MultipartFile param to method "
                            + ", the request is not a MultipartRequest");
                }
            }
        } else {
            Object toConvert = null;
            for (String paramName : metaData.getParamNames()) {
                if (paramName != null) {
                    toConvert = inv.getRequest().getParameterValues(paramName);
                    if (toConvert != null) {
                        break;
                    }
                }
            }
            if (toConvert != null) {
                if (((String[]) toConvert).length == 1) {
                    toConvert = ((String[]) toConvert)[0].split(",");
                }
                Class<?> arrayType;
                if (metaData.getParamType().isArray()) {
                    arrayType = metaData.getParamType();
                } else {
                    arrayType = arrayTypeMap.get(compnentType);
                    if (arrayType == null) {
                        arrayType = Array.newInstance((Class<?>) compnentType, 0).getClass();
                    }
                }
                TypeConverter typeConverter = SafedTypeConverterFactory.getCurrentConverter();
                Object array = typeConverter.convertIfNecessary(toConvert, arrayType);
                return array;
            }
        }
        return Array.newInstance((Class<?>) compnentType, 0);
    }

    static final class ArrayResolver implements ParamResolver {

        @Override
        public boolean supports(ParamMetaData metaData) {
            return metaData.getParamType().isArray();
        }

        @Override
        public Object resolve(Invocation inv, ParamMetaData metaData) {
            return resolveArray(inv, metaData, metaData.getParamType().getComponentType());
        }

    }

    static abstract class CollectionResolver<T extends Collection<?>> implements ParamResolver {

        @Override
        public final boolean supports(ParamMetaData metaData) {
            if (innerSupports(metaData)) {
                Class<?>[] generics = compileGenericParameterTypesDetail(metaData.getMethod(), metaData.getIndex());
                if (generics == null || generics.length == 0) {
                    throw new IllegalArgumentException("please use generic for " + metaData.getParamType().getName()
                            + " [" + metaData.getControllerClass().getName() + "." + metaData.getMethod().getName()
                            + "]");
                }
                metaData.setUserObject(this, generics[0]);
                return true;
            }
            return false;
        }

        public abstract boolean innerSupports(ParamMetaData metaData);

        @Override
        @SuppressWarnings("unchecked")
        public Object resolve(Invocation inv, ParamMetaData metaData) throws Exception {
            Object array = resolveArray(inv, metaData, (Class<?>) metaData.getUserObject(this));
            int len = ArrayUtils.getLength(array);
            @SuppressWarnings("rawtypes")
            Collection collection = create(metaData, len);
            for (int i = 0; i < len; i++) {
                collection.add(Array.get(array, i));
            }
            return collection;
        }

        protected abstract Collection<?> create(ParamMetaData metaData, int len) throws Exception;
    }

    static final class ListResolver extends CollectionResolver<List<?>> {

        @Override
        public boolean innerSupports(ParamMetaData metaData) {
            return List.class == metaData.getParamType() || Collection.class == metaData.getParamType()
                    || (!Modifier.isAbstract(metaData.getParamType().getModifiers())
                            && List.class.isAssignableFrom(metaData.getParamType()));
        }

        @Override
        @SuppressWarnings({ "unchecked", "rawtypes" })
        protected Collection create(ParamMetaData metaData, int len) throws Exception {
            if (metaData.getParamType().isInterface()) {
                return new ArrayList<Object>(len);
            } else {
                return (Collection<?>) metaData.getParamType().getConstructor().newInstance();
            }
        }
    }

    static final class SetResolver extends CollectionResolver<Set<?>> {

        @Override
        public boolean innerSupports(ParamMetaData metaData) {
            return Set.class == metaData.getParamType()
                    || (!Modifier.isAbstract(metaData.getParamType().getModifiers())
                            && Set.class.isAssignableFrom(metaData.getParamType()));
        }

        @Override
        @SuppressWarnings({ "unchecked", "rawtypes" })
        protected Collection create(ParamMetaData metaData, int len) throws Exception {
            if (metaData.getParamType().isInterface()) {
                return new HashSet<Object>(len);
            } else {
                return (Collection<?>) metaData.getParamType().getConstructor().newInstance();
            }
        }
    }

    private static Class<?>[] compileGenericParameterTypesDetail(Method method, int index) {
        Type genericParameterType = method.getGenericParameterTypes()[index];
        ArrayList<Class<?>> typeDetailList = new ArrayList<Class<?>>();
        if (genericParameterType instanceof ParameterizedType) {
            ParameterizedType aType = (ParameterizedType) genericParameterType;
            Type[] parameterArgTypes = aType.getActualTypeArguments();
            for (Type parameterArgType : parameterArgTypes) {
                if (parameterArgType instanceof Class) {
                    typeDetailList.add((Class<?>) parameterArgType);
                } else {
                    typeDetailList.add(String.class);
                }
            }
            Class<?>[] types = new Class[typeDetailList.size()];
            typeDetailList.toArray(types);
            return types;
        }
        return null;
    }

    static final class MapResolver implements ParamResolver {

        @Override
        public boolean supports(ParamMetaData metaData) {
            boolean supports = Map.class == metaData.getParamType() || HashMap.class == metaData.getParamType();
            if (supports) {
                Class<?>[] generics = compileGenericParameterTypesDetail(metaData.getMethod(), metaData.getIndex());
                if (generics == null || generics.length == 0) {
                    throw new IllegalArgumentException("please use generic for " + metaData.getParamType().getName()
                            + " [" + metaData.getControllerClass().getName() + "." + metaData.getMethod().getName()
                            + "]");
                }
                metaData.setUserObject(this, generics);
            }
            return supports;
        }

        @Override
        public Map<?, ?> resolve(Invocation inv, ParamMetaData metaData) {
            if (StringUtils.isNotEmpty(metaData.getParamName())) {
                Class<?>[] genericTypes = (Class[]) metaData.getUserObject(this);
                Class<?> keyType = genericTypes[0];
                Class<?> valueType = genericTypes[1];
                Map<?, ?> toConvert = WebUtils.getParametersStartingWith(inv.getRequest(),
                        metaData.getParamName() + MAP_SEPARATOR);
                if (toConvert != null) {
                    if (keyType != String.class || valueType != String.class) {
                        Map<Object, Object> ret = new HashMap<Object, Object>();
                        for (Map.Entry<?, ?> entry : toConvert.entrySet()) {
                            Object key = entry.getKey();
                            Object value = entry.getValue();
                            TypeConverter typeConverter = SafedTypeConverterFactory.getCurrentConverter();
                            if (keyType != String.class) {
                                key = typeConverter.convertIfNecessary(key, keyType);
                            }
                            if (valueType != String.class) {
                                value = typeConverter.convertIfNecessary(value, valueType);
                            }
                            ret.put(key, value);
                        }
                        return ret;
                    }
                    return toConvert;
                }

            }
            return new HashMap<Object, Object>(2);
        }
    }

    static class DateEditor extends PropertyEditorSupport {

        private Class<?> targetType;

        public DateEditor(Class<?> targetType) {
            this.targetType = targetType;
        }

        @Override
        public void setAsText(String text) throws IllegalArgumentException {
            if (text == null || (text = text.trim()).length() == 0) {
                return;
            }
            try {
                setValue(DatePatterns.changeType(DatePatterns.parse(text), targetType));
            } catch (ParseException e) {
                throw new IllegalArgumentException(e);
            }
        }

    }

    static final class DatePatterns {

        private final static String dateTimePattern = "yyyy-MM-dd HH:mm:ss";

        private final static String dateTimePattern2 = "yyyy/MM/dd HH:mm:ss";

        private final static String dateTimePattern3 = "yyyyMMddHHmmss";

        private final static String datePattern = "yyyy-MM-dd";

        private final static String datePattern2 = "yyyy/MM/dd";

        private final static String datePattern3 = "yyyy-MM";

        private final static String timePattern = "HH:mm:ss";

        private final static String stimePattern = "HH:mm";

        private static Date parse(String text) throws ParseException {
            if (text.length() == dateTimePattern.length()) {
                if (text.charAt(4) == '-' && text.charAt(7) == '-') {
                    return new SimpleDateFormat(dateTimePattern).parse(text);
                }
                if (text.charAt(4) == '/' && text.charAt(7) == '/') {
                    if (text.charAt(13) == ':' && text.charAt(16) == ':') {
                        return new SimpleDateFormat(dateTimePattern2).parse(text);
                    }
                }
            } else if (text.length() == dateTimePattern3.length()) {
                return new SimpleDateFormat(dateTimePattern3).parse(text);
            } else if (text.length() == datePattern3.length()) {
                return new SimpleDateFormat(datePattern3).parse(text);
            } else if (text.length() == datePattern.length()) {
                if (text.charAt(4) == '-' && text.charAt(7) == '-') {
                    return new SimpleDateFormat(datePattern).parse(text);
                }
                if (text.charAt(4) == '/' && text.charAt(7) == '/') {
                    return new SimpleDateFormat(datePattern2).parse(text);
                }
            } else if (text.length() == timePattern.length()) {
                if (text.charAt(2) == ':' && text.charAt(5) == ':') {
                    return new SimpleDateFormat(timePattern).parse(text);
                }
            } else if (text.length() == stimePattern.length()) {
                if (text.charAt(2) == ':') {
                    return new SimpleDateFormat(stimePattern).parse(text);
                }
            }
            return new Date(Long.parseLong(text));
        }

        private static Date changeType(Date date, Class<?> targetType) {
            if (date == null) {
                return date;
            }
            if (java.sql.Date.class == targetType) {
                date = new java.sql.Date(date.getTime());
            } else if (java.sql.Time.class == targetType) {
                date = new java.sql.Time(date.getTime());
            } else if (java.sql.Timestamp.class == targetType) {
                date = new java.sql.Timestamp(date.getTime());
            }
            return date;
        }

    }

    static final class DateResolver implements ParamResolver {

        @Override
        public boolean supports(ParamMetaData metaData) {
            return Date.class == metaData.getParamType() || java.sql.Date.class == metaData.getParamType()
                    || java.sql.Time.class == metaData.getParamType()
                    || java.sql.Timestamp.class == metaData.getParamType();
        }

        @Override
        public Date resolve(Invocation inv, ParamMetaData metaData) throws Exception {
            String text = null;
            for (String paramName : metaData.getParamNames()) {
                if (paramName != null) {
                    text = inv.getParameter(paramName);
                    if (text != null && (text = text.trim()).length() > 0) {
                        break;
                    }
                }
            }
            Date date = resolveUtilDate(text, metaData);
            return DatePatterns.changeType(date, metaData.getParamType());
        }

        protected Date resolveUtilDate(String text, ParamMetaData metaData) throws ParseException {
            if (StringUtils.isEmpty(text)) {
                DefValue defaultValudeAnnotation = metaData.getAnnotation(DefValue.class);
                if (defaultValudeAnnotation != null
                        && !DefValue.NATIVE_DEFAULT.equals(defaultValudeAnnotation.value())) {
                    if (StringUtils.isEmpty(defaultValudeAnnotation.value())) {
                        return new Date(); // ?!
                    } else {
                        text = defaultValudeAnnotation.value(); // ???!
                    }
                } else {
                    return null; // ?null??
                }
            }
            Annotation[] paramAnnotations = metaData.getMethod().getParameterAnnotations()[metaData.getIndex()];
            for (Annotation annotation : paramAnnotations) {
                if (annotation instanceof Pattern) {
                    String[] patterns = Pattern.class.cast(annotation).value();
                    for (String pattern : patterns) {
                        // long
                        if ("long".equals(pattern)) {
                            boolean digit = true;
                            for (int i = 0; i < text.length(); i++) {
                                if (!Character.isDigit(text.charAt(i))) {
                                    digit = false;
                                    break;
                                }
                            }
                            if (digit) {
                                return new Date(Long.parseLong(text));
                            }
                        }
                        // ??pattern!! ?
                        if (text.length() == pattern.length()) {
                            return new SimpleDateFormat(pattern).parse(text);
                        }
                    }
                    break;
                }
            }
            return DatePatterns.parse(text);
        }
    }

    static final class EditorResolver implements ParamResolver {

        @Override
        public boolean supports(ParamMetaData metaData) {
            if (ClassUtils.isPrimitiveOrWrapper(metaData.getParamType())) {
                return true;
            }
            SimpleTypeConverter simpleTypeConverter = SafedTypeConverterFactory.getCurrentConverter();
            return simpleTypeConverter.findCustomEditor(metaData.getParamType(), null) != null
                    || simpleTypeConverter.getDefaultEditor(metaData.getParamType()) != null;
        }

        @Override
        public Object resolve(Invocation inv, ParamMetaData metaData) {
            String toConvert = null;
            // 
            FlashParam flashParam = metaData.getAnnotation(FlashParam.class);
            if (flashParam != null) {
                toConvert = inv.getFlash().get(flashParam.value());
            }

            for (String paramName : metaData.getParamNames()) {
                if (paramName != null) {
                    toConvert = inv.getRequest().getParameter(paramName);
                    if (toConvert != null) {
                        break;
                    }
                }
            }
            if (toConvert == null) {
                DefValue defValudeAnnotation = metaData.getAnnotation(DefValue.class);
                if (defValudeAnnotation != null && !DefValue.NATIVE_DEFAULT.equals(defValudeAnnotation.value())) {
                    toConvert = defValudeAnnotation.value();
                }
            }
            if (toConvert != null) {
                SimpleTypeConverter typeConverter = SafedTypeConverterFactory.getCurrentConverter();
                return typeConverter.convertIfNecessary(toConvert, metaData.getParamType());
            }
            if (metaData.getParamType().isPrimitive()) {
                // ?if-else?converter???
                if (metaData.getParamType() == int.class) {
                    return Integer.valueOf(0);
                } else if (metaData.getParamType() == long.class) {
                    return Long.valueOf(0);
                } else if (metaData.getParamType() == boolean.class) {
                    return Boolean.FALSE;
                } else if (metaData.getParamType() == double.class) {
                    return Double.valueOf(0);
                } else if (metaData.getParamType() == float.class) {
                    return Float.valueOf(0);
                } else {
                    SimpleTypeConverter typeConverter = SafedTypeConverterFactory.getCurrentConverter();
                    return typeConverter.convertIfNecessary("0", metaData.getParamType());
                }
            }
            return null;
        }
    }

    private static boolean addIndexAliasParamNameIfNeccessary(ParamMetaData metaData) {
        Class<?>[] paramTypes = metaData.getMethod().getParameterTypes();
        int index = metaData.getIndex(); // index0
        int uriParamIndex = 0; // uriParamIndex**1**
        int breakIndex = 0;// breakIndex0
        for (; breakIndex < paramTypes.length && breakIndex <= index; breakIndex++) {
            Class<?> type = paramTypes[breakIndex];
            if (type.isArray()) {
                type = type.getComponentType();
            } else if (Collection.class.isAssignableFrom(type)) {
                Class<?>[] generics = compileGenericParameterTypesDetail(metaData.getMethod(), breakIndex);
                if (generics == null) {
                    return false;
                }
                Assert.isTrue(generics.length > 0);
                type = generics[0];
            }
            if (ClassUtils.isPrimitiveOrWrapper(type) || type == String.class
                    || Date.class.isAssignableFrom(type)) {
                uriParamIndex++;
            }
        }
        if ((breakIndex - 1) == index && uriParamIndex > 0) {
            String alias = "$" + uriParamIndex;
            metaData.addAliasParamName(alias);
            if (logger.isDebugEnabled()) {
                logger.debug("add index alias paramName: '" + alias + "' for "
                        + metaData.getControllerClass().getName() + "." + metaData.getMethod().getName() + "(..."
                        + metaData.getParamType() + "[index=" + breakIndex + "] ...)");
            }
            return true;
        }
        return false;
    }
}