public class ActionFormWrapperClass implements DynaClass {

    /**
     * アクションマッピングです。
     */
    protected S2ActionMapping actionMapping;

    /**
     * 動的プロパティ用のマップです。
     */
    protected Map<String, DynaProperty> propertyMap = new HashMap<String, DynaProperty>();

    /**
     * インスタンスを構築します。
     * 
     * @param actionMapping
     *            アクションマッピング
     */
    public ActionFormWrapperClass(S2ActionMapping actionMapping) {
        this.actionMapping = actionMapping;
    }

    public DynaProperty[] getDynaProperties() {
        return propertyMap.values().toArray(
                new DynaProperty[propertyMap.size()]);
    }

    public DynaProperty getDynaProperty(String name) {
        if (name == null) {
            throw new NullPointerException("name");
        }
        return propertyMap.get(name);
    }

    /**
     * 動的プロパティを追加します。
     * 
     * @param property
     *            動的プロパティ
     */
    public void addDynaProperty(DynaProperty property) {
        propertyMap.put(property.getName(), property);
    }

    public String getName() {
        return actionMapping.getName();
    }

    public DynaBean newInstance() throws IllegalAccessException,
            InstantiationException {
        return new ActionFormWrapper(this);
    }

    /**
     * アクションマッピングを返します。
     * 
     * @return アクションマッピング
     */
    public S2ActionMapping getActionMapping() {
        return actionMapping;
    }
} public class S2DynaProperty extends DynaProperty {

    private static final long serialVersionUID = 1L;

    /**
     * プロパティ記述です。
     */
    protected PropertyDesc propertyDesc;

    /**
     * インスタンスを構築します。
     * 
     * @param propertyDesc
     *            プロパティ記述
     */
    public S2DynaProperty(PropertyDesc propertyDesc) {
        super(propertyDesc.getPropertyName(), propertyDesc.getPropertyType());
        this.propertyDesc = propertyDesc;
    }

    @Override
    public boolean isIndexed() {
        return false;
    }

    @Override
    public boolean isMapped() {
        return false;
    }

    /**
     * プロパティ記述を返します。
     * 
     * @return プロパティ記述
     */
    public PropertyDesc getPropertyDesc() {
        return propertyDesc;
    }

    /**
     * 値を返します。
     * 
     * @param bean
     *            Bean
     * @return 値
     */
    public Object getValue(Object bean) {
        return WrapperUtil.convert(propertyDesc.getValue(bean));
    }
} public class S2ActionMapping extends ActionMapping {

    private static final long serialVersionUID = 1L;

    private static final String REDIRECT = "redirect=true";

    /**
     * コンポーネント定義です。
     */
    protected ComponentDef componentDef;

    /**
     * アクションフォームのコンポーネント定義です。
     */
    protected ComponentDef actionFormComponentDef;

    /**
     * アクションのBean記述です。
     */
    protected BeanDesc actionBeanDesc;

    /**
     * アクションフォームのBean記述です。
     */
    protected BeanDesc actionFormBeanDesc;

    /**
     * 実行設定のマップです
     */
    protected ArrayMap executeConfigs = new ArrayMap();

    /**
     * アクションフォーム用のフィールドです。
     */
    protected Field actionFormField;

    /**
     * インスタンスを構築します。
     */
    public S2ActionMapping() {
        scope = "request";
        validate = false;
    }

    /**
     * アクションフォワードを作成します。
     * 
     * @param path
     *            パス
     * @return アクションフォワード
     */
    public ActionForward createForward(String path) {
        return createForward(path, false);
    }

    /**
     * アクションフォワードを作成します。
     * 
     * @param path
     *            パス
     * @param redirect
     *            リダイレクトするかどうか
     * @return アクションフォワード
     */
    public ActionForward createForward(String path, boolean redirect) {
        if (path == null) {
            return null;
        }
        if (path.endsWith(REDIRECT)) {
            redirect = true;
            path = path.substring(0, path.length() - REDIRECT.length() - 1);
        }
        if (path.indexOf(":") < 0) {
            if (!path.startsWith("/")) {
                path = getActionPath(componentDef.getComponentName()) + path;
            }
            if (!redirect) {
                if (path.indexOf('.') < 0) {
                    path = createRoutingPath(path);
                } else {
                    String viewPrefix = ServletContextUtil.getViewPrefix();
                    if (viewPrefix != null) {
                        path = viewPrefix + path;
                    }
                }
            }
        }
        return new ActionForward(path, redirect);
    }

    /**
     * ルーティング用のパスを作成します。
     * 
     * @param path
     *            パス
     * @return ルーティング用のパス
     */
    protected String createRoutingPath(String path) {
        String originalPath = path;
        String queryString = "";
        int index = path.indexOf('?');
        if (index >= 0) {
            queryString = path.substring(index);
            path = path.substring(0, index);
        }
        String[] names = StringUtil.split(path, "/");
        S2Container container = SingletonS2ContainerFactory.getContainer();
        StringBuilder sb = new StringBuilder(50);
        for (int i = 0; i < names.length; i++) {
            if (container.hasComponentDef(sb + names[i] + "Action")) {
                String actionPath = RoutingUtil.getActionPath(names, i);
                String paramPath = RoutingUtil.getParamPath(names, i + 1);
                if (StringUtil.isEmpty(paramPath)) {
                    return actionPath
                            + ".do"
                            + getQueryString(queryString, actionPath,
                                    paramPath, null);
                }
                S2ExecuteConfig executeConfig = S2ExecuteConfigUtil
                        .findExecuteConfig(actionPath, paramPath);
                if (executeConfig != null) {
                    return actionPath
                            + ".do"
                            + getQueryString(queryString, actionPath,
                                    paramPath, executeConfig);
                }
            }
            if (container.hasComponentDef(sb + "indexAction")) {
                String actionPath = RoutingUtil.getActionPath(names, i - 1)
                        + "/index";
                String paramPath = RoutingUtil.getParamPath(names, i);
                if (StringUtil.isEmpty(paramPath)) {
                    return actionPath
                            + ".do"
                            + getQueryString(queryString, actionPath,
                                    paramPath, null);
                }
                S2ExecuteConfig executeConfig = S2ExecuteConfigUtil
                        .findExecuteConfig(actionPath, paramPath);
                if (executeConfig != null) {
                    return actionPath
                            + ".do"
                            + getQueryString(queryString, actionPath,
                                    paramPath, executeConfig);
                }
            }
            sb.append(names[i] + "_");
        }
        if (container.hasComponentDef(sb + "indexAction")) {
            String actionPath = RoutingUtil.getActionPath(names,
                    names.length - 1)
                    + "/index";
            return actionPath + ".do"
                    + getQueryString(queryString, actionPath, "", null);
        }
        return originalPath;
    }

    /**
     * Viewのディレクトリを返します。
     * 
     * @param componentName
     *            アクションのコンポーネント名
     * @return Viewのディレクトリ
     */
    protected String getActionPath(String componentName) {
        if (!componentName.endsWith("Action")) {
            throw new IllegalArgumentException(componentName);
        }
        if (componentName.equals("indexAction")) {
            return "/";
        }
        if (componentName.endsWith("indexAction")) {
            componentName = componentName.substring(0,
                    componentName.length() - 12);
        } else {
            componentName = componentName.substring(0,
                    componentName.length() - 6);
        }
        return "/" + componentName.replace('_', '/') + "/";

    }

    /**
     * クエリストリングを返します。
     * 
     * @param queryString
     *            元のクエリストリング
     * @param actionPath
     *            アクションパス
     * @param paramPath
     *            パラメータ用のパス
     * @param executeConfig
     *            実行設定
     * @return クエリストリング
     */
    protected String getQueryString(String queryString, String actionPath,
            String paramPath, S2ExecuteConfig executeConfig) {
        String queryString2 = "";
        if (executeConfig != null) {
            queryString2 = executeConfig.getQueryString(paramPath);
        } else {
            executeConfig = getExecuteConfig("index");
            if (executeConfig != null) {
                queryString2 = executeConfig.getQueryString(paramPath);
            }
        }
        if (StringUtil.isEmpty(queryString)) {
            return queryString2;
        }
        if (StringUtil.isEmpty(queryString2)) {
            return queryString;
        }
        return queryString + "&" + queryString2.substring(1);
    }

    /**
     * コンポーネント定義を返します。
     * 
     * @return コンポーネント定義
     */
    public ComponentDef getComponentDef() {
        return componentDef;
    }

    /**
     * コンポーネント定義を設定します。
     * 
     * @param componentDef
     */
    public void setComponentDef(ComponentDef componentDef) {
        this.componentDef = componentDef;
        actionBeanDesc = BeanDescFactory.getBeanDesc(componentDef
                .getComponentClass());
    }

    /**
     * アクションフォームのコンポーネント定義を返します。
     * 
     * @return アクションフォームのコンポーネント定義
     */
    public ComponentDef getActionFormComponentDef() {
        if (actionFormField == null) {
            return componentDef;
        }
        if (actionFormComponentDef == null) {
            actionFormComponentDef = SingletonS2ContainerFactory.getContainer()
                    .getComponentDef(actionFormField.getType());
        }
        return actionFormComponentDef;
    }

    /**
     * アクションのBean記述を返します。
     * 
     * @return アクションのBean記述
     */
    public BeanDesc getActionBeanDesc() {
        return actionBeanDesc;
    }

    /**
     * アクションフォームのBean記述を返します。
     * 
     * @return アクションフォームのBean記述
     */
    public BeanDesc getActionFormBeanDesc() {
        if (actionFormField == null) {
            return actionBeanDesc;
        }
        return actionFormBeanDesc;
    }

    /**
     * POJOアクションを返します。
     * 
     * @return POJOアクション
     */
    public Object getAction() {
        return componentDef.getComponent();
    }

    /**
     * POJOアクションフォームを返します。
     * 
     * @return POJOアクションフォーム
     */
    public Object getActionForm() {
        return getActionFormComponentDef().getComponent();
    }

    /**
     * プロパティの値を返します。
     * 
     * @param name
     *            プロパティ名
     * @return プロパティの値
     */
    public String getPropertyAsString(String name) {
        Object target = getActionForm();
        BeanDesc beanDesc = getActionFormBeanDesc();
        Object value = null;
        if (beanDesc.hasPropertyDesc(name)) {
            value = beanDesc.getPropertyDesc(name).getValue(target);
        }
        if (value != null) {
            return value.toString();
        }
        target = getAction();
        beanDesc = getActionBeanDesc();
        if (beanDesc.hasPropertyDesc(name)) {
            value = beanDesc.getPropertyDesc(name).getValue(target);
        }
        if (value != null) {
            return value.toString();
        }
        return "null";
    }

    @Override
    public String getType() {
        return componentDef.getComponentClass().getName();
    }

    /**
     * 実行メソッド名の配列を返します。
     * 
     * @return 実行メソッド名の配列
     */
    @SuppressWarnings("unchecked")
    public String[] getExecuteMethodNames() {
        return (String[]) executeConfigs.keySet().toArray(
                new String[executeConfigs.size()]);
    }

    /**
     * 実行メソッドを探します。
     * 
     * @param paramPath
     *            パラメータのパス
     * @return 実行メソッド
     */
    public S2ExecuteConfig findExecuteConfig(String paramPath) {
        for (int i = 0; i < executeConfigs.size(); i++) {
            S2ExecuteConfig executeConfig = (S2ExecuteConfig) executeConfigs
                    .get(i);
            if (executeConfig.isTarget(paramPath)) {
                return executeConfig;
            }
        }
        return null;
    }

    /**
     * 実行メソッドを探します。
     * 
     * @param request
     *            リクエスト
     * @return 実行メソッド
     */
    public S2ExecuteConfig findExecuteConfig(HttpServletRequest request) {
        if (executeConfigs.size() == 1) {
            return (S2ExecuteConfig) executeConfigs.get(0);
        }
        for (int i = 0; i < executeConfigs.size(); i++) {
            S2ExecuteConfig executeConfig = (S2ExecuteConfig) executeConfigs
                    .get(i);
            if (executeConfig.isTarget(request)) {
                return executeConfig;
            }
        }
        return getExecuteConfig("index");
    }

    /**
     * 実行設定を返します。
     * 
     * @param name
     *            名前
     * @return 実行設定
     */
    public S2ExecuteConfig getExecuteConfig(String name) {
        return (S2ExecuteConfig) executeConfigs.get(name);
    }

    /**
     * 実行設定の数を返します。
     * 
     * @return 実行設定の数
     */
    public int getExecuteConfigSize() {
        return executeConfigs.size();
    }

    /**
     * 実行設定を追加します。
     * 
     * @param executeConfig
     *            実行設定
     */
    public void addExecuteConfig(S2ExecuteConfig executeConfig) {
        executeConfigs.put(executeConfig.method.getName(), executeConfig);
    }

    /**
     * アクションフォーム用のフィールドを返します。
     * 
     * @return アクションフォーム用のフィールド
     */
    public Field getActionFormField() {
        return actionFormField;
    }

    /**
     * アクションフォーム用ののフィールドを設定します。
     * 
     * @param actionFormField
     *            アクションフォーム用のフィールド
     */
    public void setActionFormField(Field actionFormField) {
        this.actionFormField = actionFormField;
        actionFormBeanDesc = BeanDescFactory.getBeanDesc(actionFormField
                .getType());
    }
} public class S2ExecuteConfig implements Serializable {

    private static final long serialVersionUID = 1L;

    private static final String METHOD_NAME = "SAStruts.method";

    /**
     * メソッドです。
     */
    protected Method method;

    /**
     * 検証メソッドです。
     */
    protected List<S2ValidationConfig> validationConfigs;

    /**
     * エラーメッセージの保存場所です。
     */
    protected SaveType saveErrors = SaveType.REQUEST;

    /**
     * 検証エラー時の遷移先です。
     */
    protected String input;

    /**
     * 検証エラー時遷移先のパラメータ名のリストです。
     */
    protected List<String> inputParamNames = new ArrayList<String>();

    /**
     * URLのパターンです。
     */
    protected String urlPattern;

    /**
     * URLのパターンのコンパイル結果です。
     */
    protected Pattern urlPatternRegexp;

    /**
     * URLパターンが{id}のように全選択かどうかです。
     */
    protected boolean urlPatternAllSelected = false;

    /**
     * URLのパラメータ名のリストです。
     */
    protected List<String> urlParamNames = new ArrayList<String>();

    /**
     * ロールの配列です。
     */
    protected String[] roles;

    /**
     * trueの場合、バリデータや検証メソッドで検証エラーがあるとそこで検証がとまります。
     * falseの場合、検証エラーがあっても後続の検証を続行します。 どちらの場合も検証エラーがあると実行メソッドは呼び出されません。
     */
    protected boolean stopOnValidationError = true;

    /**
     * 実行メソッドの正常終了時にセッションからアクションフォームを削除するかどうかです。 trueの場合、アクションフォームを削除します。
     */
    protected boolean removeActionForm = false;

    /**
     * リセットメソッドです。
     */
    protected Method resetMethod;

    /**
     * 正常終了時に遷移先にリダイレクトするかどうかです。
     */
    protected boolean redirect = false;

    /**
     * インスタンスを構築します。
     * 
     * @param method
     *            メソッド
     * @param validator
     *            バリデータを呼び出すかどうか
     * @param validateMethod
     *            検証メソッド
     * @param saveErrors
     *            エラーメッセージの保存場所
     * @param input
     *            検証エラー時の遷移先
     * @param urlPattern
     *            URLのパターン
     */
    public S2ExecuteConfig() {
    }

    /**
     * メソッドを返します。
     * 
     * @return メソッド
     */
    public Method getMethod() {
        return method;
    }

    /**
     * メソッドを設定します。
     * 
     * @param method
     *            メソッド
     */
    public void setMethod(Method method) {
        this.method = method;
        setUrlPattern(method.getName());
    }

    /**
     * バリデータを呼び出すかどうかを返します。
     * 
     * @return バリデータを呼び出すかどうか
     */
    public boolean isValidator() {
        if (validationConfigs != null) {
            for (S2ValidationConfig cfg : validationConfigs) {
                if (cfg.isValidator()) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 検証設定のリストを返します。
     * 
     * @return 検証設定のリスト
     */
    public List<S2ValidationConfig> getValidationConfigs() {
        return validationConfigs;
    }

    /**
     * 検証設定のリストを設定します。
     * 
     * @param validationConfigs
     *            検証設定のリスト
     */
    public void setValidationConfigs(List<S2ValidationConfig> validationConfigs) {
        this.validationConfigs = validationConfigs;
    }

    /**
     * エラーメッセージの保存場所を返します。
     * 
     * @return エラーメッセージの保存場所
     */
    public SaveType getSaveErrors() {
        return saveErrors;
    }

    /**
     * エラーメッセージの保存場所を設定します。
     * 
     * @param saveErrors
     *            エラーメッセージの保存場所
     */
    public void setSaveErrors(SaveType saveErrors) {
        this.saveErrors = saveErrors;
    }

    /**
     * 検証エラー時の遷移先を返します。
     * 
     * @return 検証エラー時の遷移先
     */
    public String getInput() {
        return input;
    }

    /**
     * 検証エラー時の遷移先を設定します。
     * 
     * @param input
     *            検証エラー時の遷移先
     */
    public void setInput(String input) {
        this.input = input;
        if (StringUtil.isEmpty(input)) {
            return;
        }
        char[] chars = input.toCharArray();
        int length = chars.length;
        int index = -1;
        for (int i = 0; i < length; i++) {
            if (chars[i] == '{') {
                index = i;
            } else if (chars[i] == '}') {
                if (index >= 0) {
                    inputParamNames.add(input.substring(index + 1, i));
                    index = -1;
                } else {
                    throw new IllegalInputPatternRuntimeException(input);
                }
            }
        }
        if (index >= 0) {
            throw new IllegalInputPatternRuntimeException(input);
        }
    }

    /**
     * パラメータを解決した検証エラー時の遷移先を返します。
     * 
     * @param actionMapping
     *            アクションマッピング
     * 
     * @return 検証エラー時の遷移先
     */
    public String resolveInput(S2ActionMapping actionMapping) {
        String s = input;
        for (String name : inputParamNames) {
            s = s.replace("{" + name + "}", actionMapping
                    .getPropertyAsString(name));
        }
        return s;
    }

    /**
     * URLのパターンを返します。
     * 
     * @return URLのパターン
     */
    public String getUrlPattern() {
        return urlPattern;
    }

    /**
     * URLのパターンを設定します。
     * 
     * @param urlPattern
     *            URLのパターン
     */
    public void setUrlPattern(String urlPattern) {
        if (StringUtil.isEmpty(urlPattern)) {
            return;
        }
        this.urlPattern = urlPattern;
        StringBuilder sb = new StringBuilder(50);
        char[] chars = urlPattern.toCharArray();
        int length = chars.length;
        int index = -1;
        for (int i = 0; i < length; i++) {
            if (chars[i] == '{') {
                index = i;
            } else if (chars[i] == '}') {
                if (index >= 0) {
                    sb.append("([^/]+)");
                    urlParamNames.add(urlPattern.substring(index + 1, i));
                    index = -1;
                } else {
                    throw new IllegalUrlPatternRuntimeException(urlPattern);
                }
            } else if (index < 0) {
                sb.append(chars[i]);
            }
        }
        if (index >= 0) {
            throw new IllegalUrlPatternRuntimeException(urlPattern);
        }
        String pattern = sb.toString();
        urlPatternAllSelected = pattern.equals("([^/]+)");
        urlPatternRegexp = Pattern.compile("^" + pattern + "$");
    }

    /**
     * URLパターンが{id}のように全選択かどうかを返します。
     * 
     * @return URLパターンが{id}のように全選択かどうか
     */
    public boolean isUrlPatternAllSelected() {
        return urlPatternAllSelected;
    }

    /**
     * ロールの配列を返します。
     * 
     * @return ロールの配列
     */
    public String[] getRoles() {
        return roles;
    }

    /**
     * ロールの配列を設定します。
     * 
     * @param roles
     *            ロールの配列
     */
    public void setRoles(String[] roles) {
        this.roles = roles;
    }

    /**
     * 検証エラーがあった場合にそこで検証をやめるかどうかを返します。
     * 
     * @return 検証エラーがあった場合にそこで検証をやめるかどうか
     */
    public boolean isStopOnValidationError() {
        return stopOnValidationError;
    }

    /**
     * 検証エラーがあった場合にそこで検証をやめるかどうかを設定します。
     * 
     * @param stopOnValidationError
     *            検証エラーがあった場合にそこで検証をやめるかどうか
     */
    public void setStopOnValidationError(boolean stopOnValidationError) {
        this.stopOnValidationError = stopOnValidationError;
    }

    /**
     * 実行メソッドの正常終了時にセッションからアクションフォームを削除するかどうか返します。
     * 
     * @return 実行メソッドの正常終了時にセッションからアクションフォームを削除するかどうか
     */
    public boolean isRemoveActionForm() {
        return removeActionForm;
    }

    /**
     * 実行メソッドの正常終了時にセッションからアクションフォームを削除するかどうかを設定します。
     * 
     * @param removeActionForm
     *            実行メソッドの正常終了時にセッションからアクションフォームを削除するかどうか
     */
    public void setRemoveActionForm(boolean removeActionForm) {
        this.removeActionForm = removeActionForm;
    }

    /**
     * リセットメソッドを返します。
     * 
     * @return リセットメソッド
     */
    public Method getResetMethod() {
        return resetMethod;
    }

    /**
     * リセットメソッドを設定します。
     * 
     * @param resetMethod
     *            リセットメソッド
     */
    public void setResetMethod(Method resetMethod) {
        this.resetMethod = resetMethod;
    }

    /**
     * 正常終了時に遷移先にリダイレクトするかどうかを返します。
     * 
     * @return 正常終了時に遷移先にリダイレクトするかどうか
     */
    public boolean isRedirect() {
        return redirect;
    }

    /**
     * 正常終了時に遷移先にリダイレクトするかどうかを設定します。
     * 
     * @param redirect
     *            正常終了時に遷移先にリダイレクトするかどうか
     */
    public void setRedirect(boolean redirect) {
        this.redirect = redirect;
    }

    /**
     * 実行メソッドの対象かどうかを返します。
     * 
     * @param paramPath
     *            パラメータ用のパス
     * @return 実行メソッドの対象かどうか
     */
    public boolean isTarget(String paramPath) {
        if (!StringUtil.isEmpty(paramPath)) {
            return urlPatternRegexp.matcher(paramPath).find();
        }
        return "index".equals(urlPattern);
    }

    /**
     * 実行メソッドの対象かどうかを返します。
     * 
     * @param request
     *            リクエスト
     * @return リクエストが実行メソッドの対象かどうか
     */
    public boolean isTarget(HttpServletRequest request) {
        String[] methodNames = request.getParameterValues(METHOD_NAME);
        if (methodNames != null && methodNames.length > 0) {
            return methodNames[0].equals(method.getName());
        }
        return !StringUtil.isEmpty(request.getParameter(method.getName()))
                || !StringUtil.isEmpty(request.getParameter(method.getName()
                        + ".x"))
                || !StringUtil.isEmpty(request.getParameter(method.getName()
                        + ".y"));
    }

    /**
     * クエリストリングを返します。
     * 
     * @param paramPath
     *            パラメータ用のパス
     * @return クエリストリング
     */
    public String getQueryString(String paramPath) {
        String urlEncodedMethodName = URLEncoderUtil.encode(method.getName());
        if (StringUtil.isEmpty(paramPath)) {
            return "?" + METHOD_NAME + "=" + urlEncodedMethodName;
        }
        Matcher matcher = urlPatternRegexp.matcher(paramPath);
        if (!matcher.find()) {
            return "?" + METHOD_NAME + "=" + urlEncodedMethodName;
        }
        if (urlParamNames.size() == 0) {
            return "?" + METHOD_NAME + "=" + urlEncodedMethodName;
        }
        StringBuilder sb = new StringBuilder(50);
        sb.append("?");
        int index = 1;
        for (String name : urlParamNames) {
            if (index != 1) {
                sb.append("&");
            }
            sb.append(URLEncoderUtil.encode(name)).append("=").append(
                    URLEncoderUtil.encode(matcher.group(index++)));
        }
        sb.append("&").append(METHOD_NAME).append("=").append(
                urlEncodedMethodName);
        return sb.toString();
    }
} public class S2FormBeanConfig extends FormBeanConfig {

    private static final long serialVersionUID = 1L;

    private static final String TYPE = "org.seasar.struts.action.ActionFormWrapper";

    /**
     * 動的クラスです。
     */
    protected DynaClass dynaClass;

    @Override
    public ActionForm createActionForm(ActionServlet servlet)
            throws IllegalAccessException, InstantiationException {

        ActionForm actionForm = (ActionForm) dynaClass.newInstance();
        actionForm.setServlet(servlet);
        return actionForm;
    }

    /**
     * 動的クラスを返します。
     * 
     * @return 動的クラス
     */
    public DynaClass getDynaClass() {
        return dynaClass;
    }

    /**
     * 動的クラスを設定します。
     * 
     * @param dynaClass
     *            動的クラス
     */
    public void setDynaClass(DynaClass dynaClass) {
        this.dynaClass = dynaClass;
    }

    @Override
    public String getType() {
        return TYPE;
    }

}
 public class S2ModuleConfig extends ModuleConfigImpl implements Disposable {

    private static final long serialVersionUID = 1L;

    /**
     * 初期化されたかどうかです。
     */
    protected volatile boolean initialized;

    /**
     * インスタンスを構築します。
     * 
     * @param prefix
     *            プレフィックス
     */
    public S2ModuleConfig(String prefix) {
        super(prefix);
        initialize();
    }

    /**
     * 初期化を行ないます。
     */
    public void initialize() {
        DisposableUtil.add(this);
        initialized = true;
    }

    public void dispose() {
        actionConfigs.clear();
        actionConfigList.clear();
        formBeans.clear();
        BeanUtilsBean.setInstance(new BeanUtilsBean());
        initialized = false;
    }

    @Override
    public ActionConfig findActionConfig(String path) {
        if (!initialized) {
            initialize();
        }
        ActionConfig ac = (ActionConfig) actionConfigs.get(path);
        if (ac == null && HotdeployUtil.isHotdeploy()) {
            SingletonS2ContainerFactory.getContainer().getComponent(
                    ActionUtil.fromPathToActionName(path));
        }
        return (ActionConfig) actionConfigs.get(path);
    }

    @Override
    public void freeze() {
    }
} public class S2ValidationConfig {

    /**
     * 検証メソッドです。
     */
    protected Method validateMethod;

    /**
     * コンストラクタです。
     */
    public S2ValidationConfig() {
    }

    /**
     * コンストラクタです。
     * 
     * @param validateMethod
     *            検証メソッド
     */
    public S2ValidationConfig(Method validateMethod) {
        this.validateMethod = validateMethod;
    }

    /**
     * 検証メソッドを返します。
     * 
     * @return 検証メソッド
     */
    public Method getValidateMethod() {
        return validateMethod;
    }

    /**
     * バリデータを実行するかどうかを返します。
     * 
     * @return バリデータを実行するかどうか
     */
    public boolean isValidator() {
        return validateMethod == null;
    }
}
 public class DuplicateExecuteMethodAndPropertyRuntimeException extends
        SRuntimeException {

    private static final long serialVersionUID = 1L;

    private Class<?> actionClass;

    private String executeMethodName;

    /**
     * インスタンスを構築します。
     * 
     * @param actionClass
     *            アクションクラス
     * @param executeMethodName
     *            実行メソッド名
     */
    public DuplicateExecuteMethodAndPropertyRuntimeException(
            Class<?> actionClass, String executeMethodName) {
        super("ESAS0012", new Object[] { actionClass.getName(),
                executeMethodName });
        this.actionClass = actionClass;
        this.executeMethodName = executeMethodName;
    }

    /**
     * アクションクラスを返します。
     * 
     * @return アクションクラス
     */
    public Class<?> getActionClass() {
        return actionClass;
    }

    /**
     * 実行メソッド名を返します。
     * 
     * @return 実行メソッド名
     */
    public String getExecuteMethodName() {
        return executeMethodName;
    }
} public class ExecuteMethodNotFoundRuntimeException extends SRuntimeException {

    private static final long serialVersionUID = 1L;

    private Class<?> targetClass;

    /**
     * インスタンスを構築します。
     * 
     * @param targetClass
     *            対象クラス
     */
    public ExecuteMethodNotFoundRuntimeException(Class<?> targetClass) {
        super("ESAS0004", new Object[] { targetClass.getName() });
        this.targetClass = targetClass;
    }

    /**
     * 対象クラスを返します。
     * 
     * @return 対象クラス
     */
    public Class<?> getTargetClass() {
        return targetClass;
    }
} public class IllegalExecuteMethodRuntimeException extends SRuntimeException {

    private static final long serialVersionUID = 1L;

    private Class<?> actionClass;

    private String executeMethodName;

    /**
     * インスタンスを構築します。
     * 
     * @param actionClass
     *            アクションクラス
     * @param executeMethodName
     *            実行メソッド名
     */
    public IllegalExecuteMethodRuntimeException(Class<?> actionClass,
            String executeMethodName) {
        super("ESAS0001", new Object[] { actionClass.getName(),
                executeMethodName });
        this.actionClass = actionClass;
        this.executeMethodName = executeMethodName;
    }

    /**
     * アクションクラスを返します。
     * 
     * @return アクションクラス
     */
    public Class<?> getActionClass() {
        return actionClass;
    }

    /**
     * 実行メソッド名を返します。
     * 
     * @return 実行メソッド名
     */
    public String getExecuteMethodName() {
        return executeMethodName;
    }
} public class IllegalValidateMethodRuntimeException extends SRuntimeException {

    private static final long serialVersionUID = 1L;

    private Class<?> actionClass;

    private String validateMethodName;

    /**
     * インスタンスを構築します。
     * 
     * @param actionClass
     *            アクションクラス
     * @param validateMethodName
     *            検証メソッド名
     */
    public IllegalValidateMethodRuntimeException(Class<?> actionClass,
            String validateMethodName) {
        super("ESAS0006", new Object[] { actionClass.getName(),
                validateMethodName });
        this.actionClass = actionClass;
        this.validateMethodName = validateMethodName;
    }

    /**
     * アクションクラスを返します。
     * 
     * @return アクションクラス
     */
    public Class<?> getActionClass() {
        return actionClass;
    }

    /**
     * 検証メソッド名を返します。
     * 
     * @return 検証メソッド名
     */
    public String getValidateMethodName() {
        return validateMethodName;
    }
} public class IllegalValidatorOfExecuteMethodRuntimeException extends SRuntimeException {

    private static final long serialVersionUID = 1L;

    private Class<?> actionClass;

    private String executeMethodName;

    /**
     * インスタンスを構築します。
     * 
     * @param actionClass
     *            アクションクラス
     * @param executeMethodName
     *            実行メソッド名
     */
    public IllegalValidatorOfExecuteMethodRuntimeException(Class<?> actionClass,
            String executeMethodName) {
        super("ESAS0005", new Object[] { actionClass.getName(),
                executeMethodName });
        this.actionClass = actionClass;
        this.executeMethodName = executeMethodName;
    }

    /**
     * アクションクラスを返します。
     * 
     * @return アクションクラス
     */
    public Class<?> getActionClass() {
        return actionClass;
    }

    /**
     * 実行メソッド名を返します。
     * 
     * @return 実行メソッド名
     */
    public String getExecuteMethodName() {
        return executeMethodName;
    }
} public class MultipleAllSelectedUrlPatternRuntimeException extends
        SRuntimeException {

    private static final long serialVersionUID = 1L;

    private String urlPattern;

    private String urlPattern2;

    /**
     * インスタンスを構築します。
     * 
     * @param urlPattern
     *            URLパターン
     * @param urlPattern2
     *            URLパターン2
     * 
     */
    public MultipleAllSelectedUrlPatternRuntimeException(String urlPattern,
            String urlPattern2) {
        super("ESAS0009", new Object[] { urlPattern, urlPattern2 });
        this.urlPattern = urlPattern;
        this.urlPattern2 = urlPattern2;
    }

    /**
     * URLパターンを返します。
     * 
     * @return URLパターン
     */
    public String getUrlPattern() {
        return urlPattern;
    }

    /**
     * URLパターン2を返します。
     * 
     * @return URLパターン2
     */
    public String getUrlPattern2() {
        return urlPattern2;
    }
} public class UnmatchValidatorAndValidateRuntimeException extends
        SRuntimeException {

    private static final long serialVersionUID = 1L;

    private Class<?> actionClass;

    private String executeMethodName;

    /**
     * インスタンスを構築します。
     * 
     * @param actionClass
     *            アクションクラス
     * @param executeMethodName
     *            実行メソッド名
     */
    public UnmatchValidatorAndValidateRuntimeException(Class<?> actionClass,
            String executeMethodName) {
        super("ESAS0011", new Object[] { actionClass.getName(),
                executeMethodName });
        this.actionClass = actionClass;
        this.executeMethodName = executeMethodName;
    }

    /**
     * アクションクラスを返します。
     * 
     * @return アクションクラス
     */
    public Class<?> getActionClass() {
        return actionClass;
    }

    /**
     * 実行メソッド名を返します。
     * 
     * @return 実行メソッド名
     */
    public String getExecuteMethodName() {
        return executeMethodName;
    }
} public final class MessageResourcesUtil {

    private MessageResourcesUtil() {
    }

    /**
     * メッセージリソースを返します。
     * 
     * @return メッセージリソース
     */
    public static MessageResources getMessageResources() {
        return (MessageResources) ServletContextUtil.getServletContext()
                .getAttribute(Globals.MESSAGES_KEY);
    }

    /**
     * メッセージを返します。
     * 
     * @param locale
     *            ロケール
     * @param key
     *            キー
     * @return メッセージ
     */
    public static String getMessage(Locale locale, String key) {
        return getMessageResources().getMessage(locale, key);
    }

    /**
     * メッセージを返します。
     * 
     * @param locale
     *            ロケール
     * @param key
     *            キー
     * @param args
     *            引数の配列
     * @return メッセージ
     */
    public static String getMessage(Locale locale, String key, Object... args) {
        return getMessageResources().getMessage(locale, key, args);
    }

    /**
     * メッセージを返します。
     * 
     * @param key
     *            キー
     * @return メッセージ
     */
    public static String getMessage(String key) {
        return getMessageResources().getMessage(key);
    }

    /**
     * メッセージを返します。
     * 
     * @param key
     *            キー
     * @param args
     *            引数の配列
     * @return メッセージ
     */
    public static String getMessage(String key, Object... args) {
        return getMessageResources().getMessage(key, args);
    }
} public final class S2ModuleConfigUtil {

    private S2ModuleConfigUtil() {
    }

    /**
     * モジュール設定を返します。
     * 
     * @return モジュール設定
     */
    public static S2ModuleConfig getModuleConfig() {
        return (S2ModuleConfig) ServletContextUtil.getServletContext()
                .getAttribute(Globals.MODULE_KEY);
    }
} public final class ValidatorResourcesUtil {

    private ValidatorResourcesUtil() {
    }

    /**
     * 検証リソースを返します。
     * 
     * @return 検証リソース
     */
    public static S2ValidatorResources getValidatorResources() {
        return (S2ValidatorResources) ServletContextUtil.getServletContext()
                .getAttribute(ValidatorPlugIn.VALIDATOR_KEY);
    }
} public class S2ValidatorResources extends ValidatorResources implements
        Disposable {

    private static final long serialVersionUID = 1L;

    /**
     * DTDがどこに登録されているかです。
     */
    protected static final String REGISTRATIONS[] = {
            "-//Apache Software Foundation//DTD Commons Validator Rules Configuration 1.0//EN",
            "/org/apache/commons/validator/resources/validator_1_0.dtd",
            "-//Apache Software Foundation//DTD Commons Validator Rules Configuration 1.0.1//EN",
            "/org/apache/commons/validator/resources/validator_1_0_1.dtd",
            "-//Apache Software Foundation//DTD Commons Validator Rules Configuration 1.1//EN",
            "/org/apache/commons/validator/resources/validator_1_1.dtd",
            "-//Apache Software Foundation//DTD Commons Validator Rules Configuration 1.1.3//EN",
            "/org/apache/commons/validator/resources/validator_1_1_3.dtd",
            "-//Apache Software Foundation//DTD Commons Validator Rules Configuration 1.2.0//EN",
            "/org/apache/commons/validator/resources/validator_1_2_0.dtd",
            "-//Apache Software Foundation//DTD Commons Validator Rules Configuration 1.3.0//EN",
            "/org/apache/commons/validator/resources/validator_1_3_0.dtd" };

    /**
     * 初期化されたかどうかです。
     */
    protected volatile boolean initialized = false;

    /**
     * フォームのマップです。
     */
    protected Map<String, Form> forms = new HashMap<String, Form>();

    /**
     * インスタンスを構築します。
     */
    public S2ValidatorResources() {
    }

    /**
     * インスタンスを構築します。
     * 
     * @param streams
     *            入力ストリームの配列
     * @throws IOException
     *             IO例外が発生した場合。
     * @throws SAXException
     *             SAX例外が発生した場合。
     */
    public S2ValidatorResources(InputStream[] streams) throws IOException,
            SAXException {
        URL rulesUrl = ValidatorResources.class
                .getResource("digester-rules.xml");
        Digester digester = DigesterLoader.createDigester(rulesUrl);
        digester.setNamespaceAware(true);
        digester.setValidating(true);
        digester.setUseContextClassLoader(true);
        for (int i = 0; i < REGISTRATIONS.length; i += 2) {
            URL url = ValidatorResources.class
                    .getResource(REGISTRATIONS[i + 1]);
            if (url != null) {
                digester.register(REGISTRATIONS[i], url.toString());
            }
        }
        for (int i = 0; i < streams.length; i++) {
            digester.push(this);
            digester.parse(streams[i]);
        }
        initialize();
    }

    /**
     * 初期化します。
     */
    public void initialize() {
        DisposableUtil.add(this);
        initialized = true;
    }

    public void dispose() {
        forms.clear();
        initialized = false;
    }

    @Override
    public Form getForm(Locale locale, String formKey) {
        if (!initialized) {
            initialize();
        }
        Form form = forms.get(formKey);
        if (form == null) {
            if (HotdeployUtil.isHotdeploy() && formKey.endsWith("Form")) {
                SingletonS2ContainerFactory.getContainer().getComponentDef(
                        formKey.substring(0, formKey.length() - 4));
                form = forms.get(formKey);
            }
        }
        return form;
    }

    /**
     * フォームを追加します。
     * 
     * @param form
     *            フォーム
     */
    public void addForm(Form form) {
        forms.put(form.getName(), form);
    }

    /**
     * 定数を返します。
     * 
     * @param name
     * @return 定数
     */
    @SuppressWarnings("deprecation")
    public String getConstant(String name) {
        return (String) hConstants.get(name);
    }
}

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;

/*
 * 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;



/**
 * 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;
