/**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com).
*
* 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.jfinal.core;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.jfinal.aop.Interceptor;
import com.jfinal.aop.InterceptorManager;
import com.jfinal.config.Interceptors;
import com.jfinal.config.Routes;
import com.jfinal.config.Routes.Route;
/**
* ActionMapping
*/
final class ActionMapping {
private static final String SLASH = "/";
private Routes routes;
// private Interceptors interceptors;
private final Map<String, Action> mapping = new HashMap<String, Action>();
ActionMapping(Routes routes, Interceptors interceptors) {
this.routes = routes;
// this.interceptors = interceptors;
}
private Set<String> buildExcludedMethodName() {
Set<String> excludedMethodName = new HashSet<String>();
Method[] methods = Controller.class.getMethods();
for (Method m : methods) {
if (m.getParameterTypes().length == 0)
excludedMethodName.add(m.getName());
}
return excludedMethodName;
}
private List<Routes> getRoutesList() {
List<Routes> routesList = Routes.getRoutesList();
List<Routes> ret = new ArrayList<Routes>(routesList.size() + 1);
ret.add(routes);
ret.addAll(routesList);
return ret;
}
void buildActionMapping() {
mapping.clear();
Set<String> excludedMethodName = buildExcludedMethodName();
InterceptorManager interMan = InterceptorManager.me();
for (Routes routes : getRoutesList()) {
for (Route route : routes.getRouteItemList()) {
Class<? extends Controller> controllerClass = route.getControllerClass();
Interceptor[] controllerInters = interMan.createControllerInterceptor(controllerClass);
boolean sonOfController = (controllerClass.getSuperclass() == Controller.class);
Method[] methods = (sonOfController ? controllerClass.getDeclaredMethods() : controllerClass.getMethods());
for (Method method : methods) {
String methodName = method.getName();
if (excludedMethodName.contains(methodName) || method.getParameterTypes().length != 0)
continue ;
if (sonOfController && !Modifier.isPublic(method.getModifiers()))
continue ;
Interceptor[] actionInters = interMan.buildControllerActionInterceptor(routes.getInterceptors(), controllerInters, controllerClass, method);
String controllerKey = route.getControllerKey();
ActionKey ak = method.getAnnotation(ActionKey.class);
String actionKey;
if (ak != null) {
actionKey = ak.value().trim();
if ("".equals(actionKey))
throw new IllegalArgumentException(controllerClass.getName() + "." + methodName + "(): The argument of ActionKey can not be blank.");
if (!actionKey.startsWith(SLASH))
actionKey = SLASH + actionKey;
}
else if (methodName.equals("index")) {
actionKey = controllerKey;
}
else {
actionKey = controllerKey.equals(SLASH) ? SLASH + methodName : controllerKey + SLASH + methodName;
}
Action action = new Action(controllerKey, actionKey, controllerClass, method, methodName, actionInters, route.getFinalViewPath(routes.getBaseViewPath()));
if (mapping.put(actionKey, action) != null) {
throw new RuntimeException(buildMsg(actionKey, controllerClass, method));
}
}
}
}
routes.clear();
// support url = controllerKey + urlParas with "/" of controllerKey
Action action = mapping.get("/");
if (action != null) {
mapping.put("", action);
}
}
private static final String buildMsg(String actionKey, Class<? extends Controller> controllerClass, Method method) {
StringBuilder sb = new StringBuilder("The action \"")
.append(controllerClass.getName()).append(".")
.append(method.getName()).append("()\" can not be mapped, ")
.append("actionKey \"").append(actionKey).append("\" is already in use.");
String msg = sb.toString();
System.err.println("\nException: " + msg);
return msg;
}
/**
* Support four types of url
* 1: http://abc.com/controllerKey ---> 00
* 2: http://abc.com/controllerKey/para ---> 01
* 3: http://abc.com/controllerKey/method ---> 10
* 4: http://abc.com/controllerKey/method/para ---> 11
* The controllerKey can also contains "/"
* Example: http://abc.com/uvw/xyz/method/para
*/
Action getAction(String url, String[] urlPara) {
Action action = mapping.get(url);
if (action != null) {
return action;
}
// --------
int i = url.lastIndexOf(SLASH);
if (i != -1) {
action = mapping.get(url.substring(0, i));
urlPara[0] = url.substring(i + 1);
}
return action;
}
List<String> getAllActionKeys() {
List<String> allActionKeys = new ArrayList<String>(mapping.keySet());
Collections.sort(allActionKeys);
return allActionKeys;
}
}