org.seasar.struts.customizer.ActionCustomizer.java Source code

Java tutorial

Introduction

Here is the source code for org.seasar.struts.customizer.ActionCustomizer.java

Source

/*
 * Copyright 2004-2009 the Seasar Foundation and the Others.
 *
 * 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 org.seasar.struts.customizer;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.commons.validator.Form;
import org.apache.commons.validator.Var;
import org.apache.struts.action.ActionMessages;
import org.seasar.framework.beans.BeanDesc;
import org.seasar.framework.beans.PropertyDesc;
import org.seasar.framework.container.ComponentCustomizer;
import org.seasar.framework.container.ComponentDef;
import org.seasar.framework.util.ClassUtil;
import org.seasar.framework.util.MethodUtil;
import org.seasar.framework.util.ModifierUtil;
import org.seasar.framework.util.StringUtil;
import org.seasar.framework.util.tiger.AnnotationUtil;
import org.seasar.struts.action.ActionFormWrapperClass;
import org.seasar.struts.action.S2DynaProperty;
import org.seasar.struts.annotation.ActionForm;
import org.seasar.struts.annotation.Arg;
import org.seasar.struts.annotation.Execute;
import org.seasar.struts.annotation.Msg;
import org.seasar.struts.annotation.Validator;
import org.seasar.struts.config.S2ActionMapping;
import org.seasar.struts.config.S2ExecuteConfig;
import org.seasar.struts.config.S2FormBeanConfig;
import org.seasar.struts.config.S2ModuleConfig;
import org.seasar.struts.config.S2ValidationConfig;
import org.seasar.struts.exception.DuplicateExecuteMethodAndPropertyRuntimeException;
import org.seasar.struts.exception.ExecuteMethodNotFoundRuntimeException;
import org.seasar.struts.exception.IllegalExecuteMethodRuntimeException;
import org.seasar.struts.exception.IllegalValidateMethodRuntimeException;
import org.seasar.struts.exception.IllegalValidatorOfExecuteMethodRuntimeException;
import org.seasar.struts.exception.MultipleAllSelectedUrlPatternRuntimeException;
import org.seasar.struts.exception.UnmatchValidatorAndValidateRuntimeException;
import org.seasar.struts.util.ActionUtil;
import org.seasar.struts.util.MessageResourcesUtil;
import org.seasar.struts.util.S2ModuleConfigUtil;
import org.seasar.struts.util.ValidatorResourcesUtil;
import org.seasar.struts.validator.S2ValidatorResources;

/**
 * Action???
 * 
 * @author higa
 * 
 */
public class ActionCustomizer implements ComponentCustomizer {

    /**
     * ??????
     */
    protected static final String VALIDATOR = "@";

    public void customize(ComponentDef componentDef) {
        S2ActionMapping actionMapping = createActionMapping(componentDef);
        S2FormBeanConfig formConfig = createFormBeanConfig(actionMapping);
        S2ModuleConfig moduleConfig = S2ModuleConfigUtil.getModuleConfig();
        moduleConfig.addActionConfig(actionMapping);
        moduleConfig.addFormBeanConfig(formConfig);
        S2ValidatorResources validatorResources = ValidatorResourcesUtil.getValidatorResources();
        setupValidator(actionMapping, validatorResources);
    }

    /**
     * ????
     * 
     * @param componentDef
     *            ??
     * @return 
     */
    protected S2ActionMapping createActionMapping(ComponentDef componentDef) {
        S2ActionMapping actionMapping = createActionMapping();
        actionMapping.setPath(ActionUtil.fromActionNameToPath(componentDef.getComponentName()));
        actionMapping.setComponentDef(componentDef);
        actionMapping.setName(componentDef.getComponentName() + "Form");
        Class<?> actionClass = componentDef.getComponentClass();
        setupActionForm(actionMapping, actionClass);
        setupMethod(actionMapping, actionClass);
        return actionMapping;
    }

    /**
     * ????
     * 
     * @return 
     */
    protected S2ActionMapping createActionMapping() {
        return new S2ActionMapping();
    }

    /**
     * ????
     * 
     * @param actionMapping
     *            
     * @param actionClass
     *            
     */
    protected void setupMethod(S2ActionMapping actionMapping, Class<?> actionClass) {
        S2ExecuteConfig allSelectedExecuteConfig = null;
        for (Class<?> clazz = actionClass; clazz != Object.class; clazz = clazz.getSuperclass()) {
            for (Method m : clazz.getDeclaredMethods()) {
                if (!ModifierUtil.isPublic(m)) {
                    continue;
                }
                Execute execute = m.getAnnotation(Execute.class);
                if (execute == null) {
                    continue;
                }
                if (actionMapping.getExecuteConfig(m.getName()) != null) {
                    continue;
                }
                if (m.getParameterTypes().length > 0 || m.getReturnType() != String.class) {
                    throw new IllegalExecuteMethodRuntimeException(actionClass, m.getName());
                }
                if (actionMapping.getActionFormBeanDesc().hasPropertyDesc(m.getName())) {
                    throw new DuplicateExecuteMethodAndPropertyRuntimeException(actionClass, m.getName());
                }
                String input = !StringUtil.isEmpty(execute.input()) ? execute.input() : null;
                S2ExecuteConfig executeConfig = createExecuteConfig();
                executeConfig.setMethod(m);
                executeConfig.setSaveErrors(execute.saveErrors());
                executeConfig.setInput(input);
                List<S2ValidationConfig> validationConfigs = new ArrayList<S2ValidationConfig>();
                String validate = execute.validate();
                boolean validator = false;
                if (!StringUtil.isEmpty(validate)) {
                    BeanDesc actionBeanDesc = actionMapping.getActionBeanDesc();
                    BeanDesc actionFormBeanDesc = actionMapping.getActionFormBeanDesc();
                    for (String name : StringUtil.split(validate, ", ")) {
                        if (VALIDATOR.equals(name)) {
                            if (!execute.validator()) {
                                throw new UnmatchValidatorAndValidateRuntimeException(actionClass, m.getName());
                            }
                            validationConfigs.add(new S2ValidationConfig());
                            validator = true;
                        } else if (actionFormBeanDesc.hasMethod(name)) {
                            Method validateMethod = actionFormBeanDesc.getMethod(name);
                            if (validateMethod.getParameterTypes().length > 0
                                    || !ActionMessages.class.isAssignableFrom(validateMethod.getReturnType())) {
                                throw new IllegalValidateMethodRuntimeException(actionClass,
                                        validateMethod.getName());

                            }
                            validationConfigs.add(new S2ValidationConfig(validateMethod));
                        } else {
                            Method validateMethod = actionBeanDesc.getMethod(name);
                            if (validateMethod.getParameterTypes().length > 0
                                    || !ActionMessages.class.isAssignableFrom(validateMethod.getReturnType())) {
                                throw new IllegalValidateMethodRuntimeException(actionClass,
                                        validateMethod.getName());

                            }
                            validationConfigs.add(new S2ValidationConfig(validateMethod));
                        }
                    }
                }
                if (!validator && execute.validator()) {
                    validationConfigs.add(0, new S2ValidationConfig());
                }
                if (!validationConfigs.isEmpty() && input == null) {
                    throw new IllegalValidatorOfExecuteMethodRuntimeException(actionClass, m.getName());
                }
                executeConfig.setValidationConfigs(validationConfigs);
                executeConfig.setUrlPattern(execute.urlPattern());
                String roles = execute.roles().trim();
                if (!StringUtil.isEmpty(roles)) {
                    executeConfig.setRoles(StringUtil.split(roles, ", "));
                }
                executeConfig.setStopOnValidationError(execute.stopOnValidationError());
                executeConfig.setRemoveActionForm(execute.removeActionForm());
                String reset = execute.reset();
                if (!StringUtil.isEmpty(reset)) {
                    Method resetMethod = null;
                    if ("reset".equals(reset)) {
                        resetMethod = actionMapping.getActionFormBeanDesc().getMethodNoException(reset);
                    } else {
                        resetMethod = actionMapping.getActionFormBeanDesc().getMethod(reset);
                    }
                    if (resetMethod != null) {
                        executeConfig.setResetMethod(resetMethod);
                    }
                }
                executeConfig.setRedirect(execute.redirect());
                if (executeConfig.isUrlPatternAllSelected()) {
                    if (allSelectedExecuteConfig != null) {
                        throw new MultipleAllSelectedUrlPatternRuntimeException(
                                allSelectedExecuteConfig.getUrlPattern(), executeConfig.getUrlPattern());
                    }
                    allSelectedExecuteConfig = executeConfig;
                } else {
                    actionMapping.addExecuteConfig(executeConfig);
                }
            }
        }
        if (allSelectedExecuteConfig != null) {
            actionMapping.addExecuteConfig(allSelectedExecuteConfig);
        }
        if (actionMapping.getExecuteConfigSize() == 0) {
            throw new ExecuteMethodNotFoundRuntimeException(actionClass);
        }
    }

    /**
     * ????
     * 
     * @return 
     */
    protected S2ExecuteConfig createExecuteConfig() {
        return new S2ExecuteConfig();
    }

    /**
     * ????
     * 
     * @param actionMapping
     *            
     * @param actionClass
     *            
     */
    protected void setupActionForm(S2ActionMapping actionMapping, Class<?> actionClass) {
        int size = actionMapping.getActionBeanDesc().getFieldSize();
        BeanDesc beanDesc = actionMapping.getActionBeanDesc();
        for (int i = 0; i < size; i++) {
            Field f = beanDesc.getField(i);
            if (f.getAnnotation(ActionForm.class) != null) {
                actionMapping.setActionFormField(f);
                return;
            }

        }
    }

    /**
     * ????
     * 
     * @param actionMapping
     *            
     * @return 
     */
    protected S2FormBeanConfig createFormBeanConfig(S2ActionMapping actionMapping) {

        S2FormBeanConfig formConfig = createFormBeanConfig();
        formConfig.setName(actionMapping.getName());
        ActionFormWrapperClass wrapperClass = createActionFormWrapperClass(actionMapping);
        BeanDesc beanDesc = actionMapping.getActionFormBeanDesc();
        for (int i = 0; i < beanDesc.getPropertyDescSize(); i++) {
            PropertyDesc pd = beanDesc.getPropertyDesc(i);
            if (pd.isReadable()) {
                S2DynaProperty property = createDynaProperty(pd);
                wrapperClass.addDynaProperty(property);
            }
        }
        formConfig.setDynaClass(wrapperClass);
        return formConfig;
    }

    /**
     * Bean????
     * 
     * @return Bean
     * 
     */
    protected S2FormBeanConfig createFormBeanConfig() {
        return new S2FormBeanConfig();
    }

    /**
     * ????
     * 
     * @param actionMapping
     *            
     * @return 
     */
    protected ActionFormWrapperClass createActionFormWrapperClass(S2ActionMapping actionMapping) {
        return new ActionFormWrapperClass(actionMapping);
    }

    /**
     * ????
     * 
     * @param pd
     *            
     * @return 
     */
    protected S2DynaProperty createDynaProperty(PropertyDesc pd) {
        return new S2DynaProperty(pd);
    }

    /**
     * ????
     * 
     * 
     * @param actionMapping
     *            
     * @param validatorResources
     *            
     */
    protected void setupValidator(S2ActionMapping actionMapping, S2ValidatorResources validatorResources) {
        Map<String, Form> forms = new HashMap<String, Form>();
        for (String methodName : actionMapping.getExecuteMethodNames()) {
            if (actionMapping.getExecuteConfig(methodName).isValidator()) {
                Form form = new Form();
                form.setName(actionMapping.getName() + "_" + methodName);
                forms.put(methodName, form);
            }
        }
        for (Class<?> clazz = actionMapping.getActionFormBeanDesc().getBeanClass(); clazz != null
                && clazz != Object.class; clazz = clazz.getSuperclass()) {
            for (Field field : ClassUtil.getDeclaredFields(clazz)) {
                for (Annotation anno : field.getDeclaredAnnotations()) {
                    processAnnotation(field.getName(), anno, validatorResources, forms);
                }
            }
        }
        for (Iterator<Form> i = forms.values().iterator(); i.hasNext();) {
            validatorResources.addForm(i.next());
        }

    }

    /**
     * ????
     * 
     * 
     * @param propertyName
     *            ??
     * @param annotation
     *            
     * @param validatorResources
     *            
     * @param forms
     *            ??????
     */
    protected void processAnnotation(String propertyName, Annotation annotation,
            S2ValidatorResources validatorResources, Map<String, Form> forms) {
        Class<? extends Annotation> annotationType = annotation.annotationType();
        Annotation metaAnnotation = annotationType.getAnnotation(Validator.class);
        if (metaAnnotation == null) {
            return;
        }
        String validatorName = getValidatorName(metaAnnotation);
        Map<String, Object> props = AnnotationUtil.getProperties(annotation);
        registerValidator(propertyName, validatorName, props, validatorResources, forms);
    }

    /**
     * ??????
     * 
     * @param annotation
     *            
     * @return ???
     */
    protected String getValidatorName(Annotation annotation) {
        Class<? extends Annotation> annoType = annotation.annotationType();
        Method m = ClassUtil.getMethod(annoType, "value", null);
        return (String) MethodUtil.invoke(m, annotation, null);
    }

    /**
     * ????
     * 
     * 
     * @param propertyName
     *            ??
     * @param validatorName
     *            ???
     * @param props
     *            ??
     * @param validatorResources
     *            
     * @param forms
     *            ??????
     */
    protected void registerValidator(String propertyName, String validatorName, Map<String, Object> props,
            S2ValidatorResources validatorResources, Map<String, Form> forms) {
        org.apache.commons.validator.Field field = createField(propertyName, validatorName, props,
                validatorResources);
        for (Iterator<String> i = forms.keySet().iterator(); i.hasNext();) {
            String methodName = i.next();
            if (!isTarget(methodName, (String) props.get("target"))) {
                continue;
            }
            Form form = forms.get(methodName);
            form.addField(field);
        }
    }

    /**
     * ?????
     * 
     * @param propertyName
     *            ??
     * @param validatorName
     *            ???
     * @param props
     *            ??
     * @param validatorResources
     *            
     * @return ?
     */
    protected org.apache.commons.validator.Field createField(String propertyName, String validatorName,
            Map<String, Object> props, S2ValidatorResources validatorResources) {
        org.apache.commons.validator.Field field = new org.apache.commons.validator.Field();
        field.setDepends(validatorName);
        field.setProperty(propertyName);
        Msg msg = (Msg) props.remove("msg");
        if (msg != null) {
            org.apache.commons.validator.Msg m = new org.apache.commons.validator.Msg();
            m.setName(validatorName);
            m.setKey(msg.key());
            String bundle = msg.bundle();
            if (!StringUtil.isEmpty(bundle)) {
                m.setBundle(bundle);
            }
            m.setResource(msg.resource());
            field.addMsg(m);
        }
        Arg[] args = (Arg[]) props.remove("args");
        if (args != null && args.length > 0) {
            for (Arg arg : args) {
                org.apache.commons.validator.Arg a = new org.apache.commons.validator.Arg();
                a.setKey(resolveKey(arg.key(), arg.resource(), props, validatorResources));
                String bundle = arg.bundle();
                if (!StringUtil.isEmpty(bundle)) {
                    a.setBundle(bundle);
                }
                a.setResource(arg.resource());
                a.setPosition(arg.position());
                field.addArg(a);
            }
        }
        for (int i = 0; i < 5; i++) {
            Arg arg = (Arg) props.remove("arg" + i);
            if (arg != null && !StringUtil.isEmpty(arg.key())) {
                org.apache.commons.validator.Arg a = new org.apache.commons.validator.Arg();
                a.setKey(resolveKey(arg.key(), arg.resource(), props, validatorResources));
                String bundle = arg.bundle();
                if (!StringUtil.isEmpty(bundle)) {
                    a.setBundle(bundle);
                }
                a.setResource(arg.resource());
                a.setPosition(i);
                field.addArg(a);
            } else if (i == 0) {
                org.apache.commons.validator.Arg a = new org.apache.commons.validator.Arg();
                String key = "labels." + propertyName;
                String message = MessageResourcesUtil.getMessage(key);
                if (!StringUtil.isEmpty(message)) {
                    a.setKey(key);
                } else {
                    a.setKey(propertyName);
                    a.setResource(false);
                }
                a.setPosition(0);
                field.addArg(a);
            }
        }

        for (Iterator<String> i = props.keySet().iterator(); i.hasNext();) {
            String name = i.next();
            if (name.equals("target")) {
                continue;
            }
            Object value = props.get(name);
            String jsType = Var.JSTYPE_STRING;
            if (value instanceof Number) {
                jsType = Var.JSTYPE_INT;
            } else if (name.equals("mask")) {
                jsType = Var.JSTYPE_REGEXP;
            }
            field.addVar(name, value.toString(), jsType);
        }
        return field;
    }

    /**
     * ??????????????
     * 
     * @param key
     *            
     * @param resource
     *            ?????
     * @param props
     *            ??
     * @param validatorResources
     *            
     * 
     * @return ??
     */
    protected String resolveKey(String key, boolean resource, Map<String, Object> props,
            S2ValidatorResources validatorResources) {
        if (resource) {
            return key;
        }
        if (key.startsWith("${") && key.endsWith("}")) {
            String s = key.substring(2, key.length() - 1);
            if (s.startsWith("var:")) {
                s = s.substring(4);
                return String.valueOf(props.get(s));
            }
            return validatorResources.getConstant(s);
        }
        return key;
    }

    /**
     * ????????
     * 
     * @param methodName
     *            ??
     * @param target
     *            ???????
     * @return ?????
     */
    protected boolean isTarget(String methodName, String target) {
        if (StringUtil.isEmpty(target)) {
            return true;
        }
        String[] names = StringUtil.split(target, ", ");
        for (String name : names) {
            if (methodName.equals(name.trim())) {
                return true;
            }
        }
        return false;
    }
}