Java tutorial
/* * Copyright 2007-2009 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 net.paoding.rose.web.impl.module; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.Arrays; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import net.paoding.rose.web.annotation.AsSuperController; import net.paoding.rose.web.annotation.Ignored; import net.paoding.rose.web.annotation.ReqMethod; import net.paoding.rose.web.annotation.rest.Delete; import net.paoding.rose.web.annotation.rest.Get; import net.paoding.rose.web.annotation.rest.Head; import net.paoding.rose.web.annotation.rest.Options; import net.paoding.rose.web.annotation.rest.Post; import net.paoding.rose.web.annotation.rest.Put; import net.paoding.rose.web.annotation.rest.Trace; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.util.ClassUtils; /** * * @author [qieqie.wang@gmail.com] * */ public class ControllerRef { private static Log logger = LogFactory.getLog(ControllerRef.class); private String[] mappingPaths; // e.g. UserController, UserInfoControllercontrollerNameuseruserInfo private String controllerName; private Class<?> controllerClass; private Object controllerObject; List<MethodRef> actions; public ControllerRef(String[] mappingPaths, String controllerName, Object controllerObject, Class<?> controllerClass) { setMappingPaths(mappingPaths); setControllerName(controllerName); setControllerObject(controllerObject); setControllerClass(controllerClass); } public MethodRef[] getActions() { if (this.actions == null) { init(); } return actions.toArray(new MethodRef[0]); } private void init() { List<MethodRef> actions = new LinkedList<MethodRef>(); Class<?> clz = controllerClass; // List<Method> pastMethods = new LinkedList<Method>(); while (true) { Method[] declaredMethods = clz.getDeclaredMethods(); for (Method method : declaredMethods) { if (quicklyPass(pastMethods, method, controllerClass)) { continue; } Map<ReqMethod, String[]> shotcutMappings = collectsShotcutMappings(method); if (shotcutMappings.size() == 0) { if (ignoresCommonMethod(method)) { if (logger.isDebugEnabled()) { logger.debug("ignores common methods of controller " + controllerClass.getName() + "#" + method.getName()); } } else { // TODO: ?0.91.0?201007?? if ("get".equals(method.getName()) || "index".equals(method.getName())) { // ??get/index@Get?@Get?? throw new IllegalArgumentException( "please add @Get to " + controllerClass.getName() + "#" + method.getName()); } if ("post".equals(method.getName()) || "delete".equals(method.getName()) || "put".equals(method.getName())) { // ??post/delete/put@Get/@Delete/@Put?@Get?? throw new IllegalArgumentException( "please add @" + StringUtils.capitalize(method.getName()) + " to " + controllerClass.getName() + "#" + method.getName()); } shotcutMappings = new HashMap<ReqMethod, String[]>(); shotcutMappings.put(ReqMethod.GET, new String[] { "/" + method.getName() }); shotcutMappings.put(ReqMethod.POST, new String[] { "/" + method.getName() }); } } if (shotcutMappings.size() > 0) { MethodRef methodRef = new MethodRef(); for (Map.Entry<ReqMethod, String[]> entry : shotcutMappings.entrySet()) { methodRef.addMapping(entry.getKey(), entry.getValue()); } methodRef.setMethod(method); actions.add(methodRef); } } for (int i = 0; i < declaredMethods.length; i++) { pastMethods.add(declaredMethods[i]); } clz = clz.getSuperclass(); if (clz == null || clz.getAnnotation(AsSuperController.class) == null) { break; } } this.actions = actions; } private Map<ReqMethod, String[]> collectsShotcutMappings(Method method) { Map<ReqMethod, String[]> restMethods = new HashMap<ReqMethod, String[]>(); Annotation[] annotations = method.getAnnotations(); for (Annotation annotation : annotations) { if (annotation instanceof Delete) { restMethods.put(ReqMethod.DELETE, ((Delete) annotation).value()); } else if (annotation instanceof Get) { restMethods.put(ReqMethod.GET, ((Get) annotation).value()); } else if (annotation instanceof Head) { restMethods.put(ReqMethod.HEAD, ((Head) annotation).value()); } else if (annotation instanceof Options) { restMethods.put(ReqMethod.OPTIONS, ((Options) annotation).value()); } else if (annotation instanceof Post) { restMethods.put(ReqMethod.POST, ((Post) annotation).value()); } else if (annotation instanceof Put) { restMethods.put(ReqMethod.PUT, ((Put) annotation).value()); } else if (annotation instanceof Trace) { restMethods.put(ReqMethod.TRACE, ((Trace) annotation).value()); } else { } } for (String[] paths : restMethods.values()) { for (int i = 0; i < paths.length; i++) { if (paths[i].equals("/")) { paths[i] = ""; } else if (paths[i].length() > 0 && paths[i].charAt(0) != '/') { paths[i] = "/" + paths[i]; } if (paths[i].length() > 1 && paths[i].endsWith("/")) { if (paths[i].endsWith("//")) { throw new IllegalArgumentException( "invalid path '" + paths[i] + "' for method " + method.getDeclaringClass().getName() + "#" + method.getName() + ": don't end with more than one '/'"); } paths[i] = paths[i].substring(0, paths[i].length() - 1); } } } return restMethods; } private boolean quicklyPass(List<Method> pastMethods, Method method, Class<?> controllerClass) { // public, not static, not abstract, @Ignored if (!Modifier.isPublic(method.getModifiers()) || Modifier.isAbstract(method.getModifiers()) || Modifier.isStatic(method.getModifiers()) || method.isAnnotationPresent(Ignored.class)) { if (logger.isDebugEnabled()) { logger.debug("ignores method of controller " + controllerClass.getName() + "." + method.getName() + " [@ignored?not public?abstract?static?]"); } return true; } // ?(?)????? for (Method past : pastMethods) { if (past.getName().equals(method.getName())) { if (Arrays.equals(past.getParameterTypes(), method.getParameterTypes())) { return true; } } } return false; } private boolean ignoresCommonMethod(Method method) { // ? Object if (ClassUtils.hasMethod(Object.class, method.getName(), method.getParameterTypes())) { return true; } // ?java bean?? String name = method.getName(); if (name.startsWith("get") && name.length() > 3 && Character.isUpperCase(name.charAt("get".length())) && method.getParameterTypes().length == 0 && method.getReturnType() != void.class) { if (null == method.getAnnotation(Get.class)) { return true; } } if (name.startsWith("is") && name.length() > 3 && Character.isUpperCase(name.charAt("is".length())) && method.getParameterTypes().length == 0 && (method.getReturnType() == boolean.class || method.getReturnType() == Boolean.class)) { if (null == method.getAnnotation(Get.class)) { return true; } } if (name.startsWith("set") && name.length() > 3 && Character.isUpperCase(name.charAt("set".length())) && method.getParameterTypes().length == 1 && method.getReturnType() == void.class) { if (null == method.getAnnotation(Post.class)) { return true; } } return false; } public void setMappingPaths(String[] mappingPaths) { this.mappingPaths = mappingPaths; } public String[] getMappingPaths() { return mappingPaths; } public Class<?> getControllerClass() { return controllerClass; } public void setControllerClass(Class<?> controllerClass) { this.controllerClass = controllerClass; } public Object getControllerObject() { return controllerObject; } public void setControllerObject(Object controllerObject) { this.controllerObject = controllerObject; } public String getControllerName() { return controllerName; } public void setControllerName(String controllerName) { this.controllerName = controllerName; } @Override public String toString() { return controllerClass.getName(); } }