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

org.dbflute.utflute.seasar.sastruts.ActionUrlPatternChecker Maven / Gradle / Ivy

There is a newer version: 0.9.8
Show newest version
/*
 * Copyright 2014-2017 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 org.dbflute.utflute.seasar.sastruts;

import java.io.File;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import junit.framework.AssertionFailedError;

import org.dbflute.helper.message.ExceptionMessageBuilder;
import org.dbflute.utflute.core.policestory.javaclass.PoliceStoryJavaClassHandler;
import org.seasar.struts.annotation.ActionForm;
import org.seasar.struts.annotation.Execute;
import org.seasar.struts.exception.IllegalUrlPatternRuntimeException;

/**
 * @author jflute
 * @since 0.5.0 (2014/12/03 Wednesday)
 */
public class ActionUrlPatternChecker implements PoliceStoryJavaClassHandler {

    // ===================================================================================
    //                                                                          Definition
    //                                                                          ==========
    protected static final String ELEMENT_PATTERN = "([^/]+)"; // from SAFlute

    // ===================================================================================
    //                                                                           Attribute
    //                                                                           =========
    protected final String _actionSuffix;

    // ===================================================================================
    //                                                                         Constructor
    //                                                                         ===========
    public ActionUrlPatternChecker(String actionSuffix) {
        _actionSuffix = actionSuffix;
    }

    // ===================================================================================
    //                                                                              Handle
    //                                                                              ======
    public void handle(File srcFile, Class actionType) { // field injection only for now
        if (!actionType.getName().endsWith(_actionSuffix)) {
            return;
        }
        final Method[] methods = actionType.getDeclaredMethods();
        for (Method method : methods) {
            int methodModifiers = method.getModifiers();
            if (!isPublicInstanceMember(methodModifiers)) {
                continue;
            }
            final Execute executeAnno = method.getAnnotation(Execute.class);
            if (executeAnno == null) {
                continue;
            }
            final String urlPattern = executeAnno.urlPattern();
            if (urlPattern == null || urlPattern.trim().length() == 0) {
                continue;
            }
            // the @Execute method that has urlPattern here
            final List urlParamNames = new ArrayList();
            analyzeUrlPattern(urlPattern, urlParamNames, new ArrayList()); // not use required list

            final Set urlParamSet = new HashSet(urlParamNames);
            final List targetFieldList = new ArrayList();
            final Class formType = extractActionFormType(actionType);
            for (Field field : extractCandidateFormFields(formType)) {
                if (!isPublicInstanceMember(field.getModifiers())) { // because may contain static
                    continue;
                }
                // public instance field here
                targetFieldList.add(field);
                for (String paramName : urlParamNames) {
                    if (field.getName().equals(paramName)) {
                        urlParamSet.remove(paramName);
                    }
                }
            }
            if (!urlParamSet.isEmpty()) {
                throwUrlPatternPropertyNotFoundException(actionType, method, urlPattern, formType, targetFieldList, urlParamSet);
            }
        }
    }

    protected Field[] extractCandidateFormFields(final Class formType) {
        return formType.getFields(); // contains super class (hopefully only abstract...)
    }

    protected boolean isPublicInstanceMember(int modifiers) {
        return Modifier.isPublic(modifiers) && !Modifier.isStatic(modifiers);
    }

    protected Class extractActionFormType(Class actionType) {
        Class formType = null;
        for (Field field : actionType.getDeclaredFields()) { // contains protected and concrete only
            if (field.getAnnotation(ActionForm.class) != null) {
                formType = field.getType();
            }
        }
        if (formType == null) {
            formType = actionType; // SAStruts's specification: action is action form if no annotation
        }
        return formType;
    }

    protected boolean hasAnnotation(Annotation[] annotations, String annoName) {
        for (Annotation annotation : annotations) {
            if (annoName.equals(annotation.getClass().getName())) {
                return true;
            }
        }
        return false;
    }

    protected void throwUrlPatternPropertyNotFoundException(Class actionType, Method method, String urlPattern, Class formType,
            List targetFieldList, Set notFoundPropertySet) {
        final ExceptionMessageBuilder br = new ExceptionMessageBuilder();
        br.addNotice("Not found the urlPattern property in the action form.");
        br.addItem("Advice");
        br.addElement("The specified property in urlPattern should exist in ActionForm.");
        br.addElement("For example,");
        br.addElement("  (x):");
        br.addElement("    (Action)");
        br.addElement("    urlPattern = \"{memberId}\"");
        br.addElement("    (ActionForm)");
        br.addElement("    public String membirId; // *NG");
        br.addElement("  (o):");
        br.addElement("    (Action)");
        br.addElement("    urlPattern = \"{memberId}\"");
        br.addElement("    (ActionForm)");
        br.addElement("    public String memberId; // OK");
        br.addItem("Action");
        br.addElement(actionType.getName() + "#" + method.getName() + "()");
        br.addItem("urlPattern");
        br.addElement(urlPattern);
        br.addItem("ActionForm");
        br.addElement(formType.getName());
        br.addItem("Public Field");
        final StringBuilder fieldExpSb = new StringBuilder();
        int index = 0;
        for (Field targetField : targetFieldList) {
            if (index % 3 == 0) {
                fieldExpSb.append("\n");
            }
            if (index > 0) {
                fieldExpSb.append(", ");
            }
            fieldExpSb.append(targetField.getName());
            ++index;
        }
        br.addElement(fieldExpSb.toString().trim());
        br.addItem("NotFound Property");
        br.addElement(notFoundPropertySet);
        final String msg = br.buildExceptionMessage();
        throw new AssertionFailedError(msg);
    }

    // ===================================================================================
    //                                                                     Framework Logic
    //                                                                     ===============
    // from SAFlute, keep plain code to re-copy as possible
    protected String analyzeUrlPattern(String urlPattern, List urlParamNames, List urlParamRequiredSet) {
        final StringBuilder sb = new StringBuilder(50);
        final char[] chars = urlPattern.toCharArray();
        final int length = chars.length;
        Character previousChar = null;
        boolean requiredElement = false;
        int index = -1;
        for (int i = 0; i < length; i++) {
            final char currentChar = chars[i];
            if (currentChar == '{') {
                index = i;
            } else if (previousChar != null && previousChar == '{' && currentChar == '*') {
                // e.g. {*id}/{*name} means required parameter, 404 not found if no value
                index = i; // to skip required mark
                requiredElement = true;
            } else if (currentChar == '}') {
                if (index >= 0) {
                    sb.append(ELEMENT_PATTERN);
                    final String elementName = urlPattern.substring(index + 1, i);
                    urlParamNames.add(elementName);
                    if (requiredElement) {
                        urlParamRequiredSet.add(elementName);
                    }
                    requiredElement = false;
                    index = -1;
                } else {
                    throw new IllegalUrlPatternRuntimeException(urlPattern);
                }
            } else if (index < 0) {
                sb.append(currentChar);
                requiredElement = false;
            }
            previousChar = currentChar;
        }
        if (index >= 0) {
            throw new IllegalUrlPatternRuntimeException(urlPattern);
        }
        return sb.toString();
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy