001package io.jboot.web;
002
003import com.jfinal.aop.Interceptor;
004import com.jfinal.aop.InterceptorManager;
005import com.jfinal.config.Routes;
006import com.jfinal.core.*;
007import io.jboot.utils.ArrayUtil;
008import io.jboot.utils.ClassUtil;
009
010import java.lang.reflect.*;
011
012public class JbootActionMapping extends ActionMapping {
013
014    public JbootActionMapping(Routes routes) {
015        super(routes);
016    }
017
018    @Override
019    protected void buildActionMapping() {
020        mapping.clear();
021        Class<?> dc;
022        InterceptorManager interMan = InterceptorManager.me();
023        for (Routes routes : getRoutesList()) {
024            for (Routes.Route route : routes.getRouteItemList()) {
025                Class<? extends Controller> controllerClass = route.getControllerClass();
026                Interceptor[] controllerInters = interMan.createControllerInterceptor(controllerClass);
027
028                boolean declaredMethods = !routes.getMappingSuperClass() || controllerClass.getSuperclass() == Controller.class;
029
030                Method[] methods = (declaredMethods ? controllerClass.getDeclaredMethods() : controllerClass.getMethods());
031                for (Method method : methods) {
032                    if (declaredMethods) {
033                        if (!Modifier.isPublic(method.getModifiers()))
034                            continue;
035                    } else {
036                        dc = method.getDeclaringClass();
037                        if (dc == Controller.class || dc == Object.class)
038                            continue;
039                    }
040
041                    if (method.getAnnotation(NotAction.class) != null) {
042                        continue;
043                    }
044
045                    Interceptor[] actionInters = interMan.buildControllerActionInterceptor(routes.getInterceptors(), controllerInters, controllerClass, method);
046                    String controllerPath = route.getControllerPath();
047
048                    String methodName = method.getName();
049                    ActionKey ak = method.getAnnotation(ActionKey.class);
050                    String actionKey;
051                    if (ak != null) {
052                        actionKey = ak.value().trim();
053                        if ("".equals(actionKey)) {
054                            throw new IllegalArgumentException(controllerClass.getName() + "." + methodName + "(): The argument of ActionKey can not be blank.");
055                        }
056
057                        if (actionKey.startsWith(SLASH)) {
058                            //actionKey = actionKey
059                        } else if (actionKey.startsWith("./")) {
060                            actionKey = controllerPath + actionKey.substring(1);
061                        } else {
062                            actionKey = SLASH + actionKey;
063                        }
064//                        if (!actionKey.startsWith(SLASH)) {
065//                            actionKey = SLASH + actionKey;
066//                        }
067                    } else if (methodName.equals("index")) {
068                        actionKey = controllerPath;
069                    } else {
070                        actionKey = controllerPath.equals(SLASH) ? SLASH + methodName : controllerPath + SLASH + methodName;
071                    }
072
073//                    Action action = new Action(controllerPath, actionKey, controllerClass, method, methodName, actionInters, route.getFinalViewPath(routes.getBaseViewPath()));
074//                    if (mapping.put(actionKey, action) != null) {
075//                        throw new RuntimeException(buildMsg(actionKey, controllerClass, method));
076//                    }
077
078                    Action newAction = new Action(controllerPath, actionKey, controllerClass, method, methodName, actionInters, route.getFinalViewPath(routes.getBaseViewPath()));
079                    Action existAction = mapping.get(actionKey);
080                    if (existAction == null) {
081                        mapping.put(actionKey, newAction);
082                    } else {
083
084                        Type controllerType = controllerClass.getGenericSuperclass();
085                        Method existActionMethod = existAction.getMethod();
086
087                        // 不是泛型
088                        if (!(controllerType instanceof ParameterizedType)) {
089                            throw new RuntimeException(buildMsg(actionKey, method, existActionMethod));
090                        }
091
092                        if (method.getParameterCount() == 0
093                                || method.getParameterCount() != existActionMethod.getParameterCount()
094                                || method.getDeclaringClass() != existActionMethod.getDeclaringClass()) {
095                            throw new RuntimeException(buildMsg(actionKey, method, existActionMethod));
096                        }
097
098                        Type[] argumentTypes = ((ParameterizedType) controllerType).getActualTypeArguments();
099
100                        Class<?>[] paraTypes = method.getParameterTypes();
101                        Class<?>[] existParaTypes = existActionMethod.getParameterTypes();
102
103                        for (int i = 0; i < paraTypes.length; i++) {
104                            Class<?> newType = paraTypes[i];
105                            Class<?> existType = existParaTypes[i];
106                            if (newType == existType) {
107                                continue;
108                            }
109                            // newType 是父类
110                            else if (newType.isAssignableFrom(existType) && ArrayUtil.contains(argumentTypes, existType)) {
111                                break;
112                            }
113                            // newType 是子类
114                            else if (existType.isAssignableFrom(newType) && ArrayUtil.contains(argumentTypes, newType)) {
115                                mapping.put(actionKey, newAction);
116                                break;
117                            } else {
118                                throw new RuntimeException(buildMsg(actionKey, method, existActionMethod));
119                            }
120                        }
121
122                    }
123                }
124            }
125        }
126        routes.clear();
127
128        // support url = controllerPath + urlParas with "/" of controllerPath
129        Action action = mapping.get("/");
130        if (action != null) {
131            mapping.put("", action);
132        }
133    }
134
135
136    protected String buildMsg(String actionKey, Method method, Method existMethod) {
137        StringBuilder sb = new StringBuilder("The action \"")
138                .append(ClassUtil.buildMethodString(method))
139                .append("\" can not be mapped, actionKey \"")
140                .append(actionKey)
141                .append("\" is already in used by: \"")
142                .append(ClassUtil.buildMethodString(existMethod))
143                .append("\"");
144
145        String msg = sb.toString();
146        System.err.println("\nException: " + msg);
147        return msg;
148    }
149
150
151}