/**
*
*/
package cn.bran.play.routing;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.HEAD;
import javax.ws.rs.OPTIONS;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.QueryParam;
import org.reflections.ReflectionUtils;
import play.libs.F.Tuple;
import com.google.common.base.Predicates;
/**
* @author bran
*
*/
public class RouterUtils {
// old perhaps incorrect impl. was looking for subgroups
// static List<String> findAllIn(Pattern p, String string) {
// List<String> ret = new ArrayList<String>();
// Matcher matcher = p.matcher(string);
// while (matcher.find()) {
// int c = matcher.groupCount();
// for (int i = 1; i <= c; i++) {
// String group = matcher.group(i);
// ret.add(group);
// }
// }
// return ret;
// }
//
// find class level annotation
/**
* find if the uri contains variables
*
* @author Bing Ran (bing.ran@gmail.com)
* @param uri
* @param path
* @return true if it contains, false otherwise
*/
static boolean isResourcePath(String uri, String path) {
String r = path.replaceAll(JaxrsRouter.urlParamCapture, "(.*)");
Pattern p = Pattern.compile(r);
if (!RegMatch.findAllIn(p, uri).isEmpty())
return true;
else
return false;
}
static Class<? extends Annotation> findHttpMethodAnnotation(String httpMethod) {
if (httpMethod.equals("GET"))
return GET.class;
else if (httpMethod.equals("POST"))
return POST.class;
else if (httpMethod.equals("POST"))
return POST.class;
else if (httpMethod.equals("PUT"))
return PUT.class;
else if (httpMethod.equals("DELETE"))
return DELETE.class;
else if (httpMethod.equals("HEAD"))
return HEAD.class;
else if (httpMethod.equals("OPTIONS"))
return OPTIONS.class;
else
return null;
}
static TargetClassWithPath findLongestMatch(Set<Class<?>> classes, play.mvc.Http.RequestHeader reqHeader,
String appPath) {
TargetClassWithPath ret = null;
String rpath = reqHeader.path();
for (Class<?> c : classes) {
String path = appPath + JaxrsRouter.prefixSlash(c.getAnnotation(Path.class).value());
if (isResourcePath(rpath, path)) {
if (ret == null || path.length() > ret._2().length()) {
ret = new TargetClassWithPath(c, path);
}
}
}
return ret;
}
static RouterClass findLongestMatch(List<RouterClass> classes, play.mvc.Http.RequestHeader reqHeader) {
RouterClass ret = null;
String rpath = reqHeader.path();
for (RouterClass c : classes) {
if (!RegMatch.findAllIn(c.absPathPatternForValues, rpath).isEmpty())
if (ret == null || c.absPath.length() > ret.absPath.length()) {
ret = c;
}
}
return ret;
}
static java.util.Set<Method> relevantMethods(Class<?> c, Class<? extends Annotation> a) {
return ReflectionUtils.getAllMethods(c, Predicates.and(ReflectionUtils.withAnnotation(a)));
}
/**
*
* @author Bing Ran (bing.ran@gmail.com)
* @param rootPath
* @param uri
* @param methods
* @param contentType
* @return a tuple of method and its param name-value map
*/
static Tuple<Method, Map<String, String>> findMethodAndGenerateContext(String rootPath, String uri,
java.util.Set<Method> methods, String contentType) {
// include rootPath only for non root slash class level @Path values or
// if the uri is root slash
String[] consumeTypes = new String[] { contentType };
String rootPathPrefix = (rootPath == "/" && uri != "/") ? "" : rootPath;
for (Method m : methods) {
Consumes consumes = m.getAnnotation(Consumes.class);
if (consumes != null) {
consumeTypes = consumes.value();
}
boolean contained = false;
for (String c : consumeTypes) {
if (c.equals(contentType)) {
contained = true;
break;
}
}
if (contained) {
Path p = m.getAnnotation(Path.class);
String mPath = p == null ? "" : JaxrsRouter.prefixSlash(p.value());
String fullMethodPath = (rootPathPrefix + mPath).replace("//", "/");
// List<String> matches =
// RegMatch.findAllIn(Pattern.compile(fullMethodPath), uri );
// if (matches.size() > 0) {
// matches = RegMatch.findAllIn(Pattern.compile(uri),
// fullMethodPath);
// if (matches.size() > 0) {
// return new Tuple<Method, Map<String, String>>(m, new
// HashMap<String, String>());
// }
// }
// bran let's do exact match
if (uri.equals(fullMethodPath)) {
return new Tuple<Method, Map<String, String>>(m, new HashMap<String, String>());
} else // any variables?
if (fullMethodPath.contains("{") && fullMethodPath.contains("}")) {
String combinedReg = fullMethodPath.replaceAll(JaxrsRouter.urlParamCapture, "\\\\{(.*)\\\\}");// .r;
Pattern r = Pattern.compile(combinedReg);
List<RegMatch> rootParamNameMatches = RegMatch.findAllMatchesIn(r, fullMethodPath);
List<String> rootParamNames = new ArrayList<String>();
for (RegMatch rm : rootParamNameMatches) {
rootParamNames.addAll(rm.subgroups);
}
combinedReg = fullMethodPath.replaceAll(JaxrsRouter.urlParamCapture, "(.*)");
r = Pattern.compile(combinedReg);
List<RegMatch> rootParamValueMatches = RegMatch.findAllMatchesIn(r, uri);
List<String> rootParamValues = new ArrayList<String>();
for (RegMatch rm : rootParamValueMatches) {
rootParamValues.addAll(rm.subgroups);
}
Map<String, String> methodMetaData = new java.util.HashMap<String, String>();
int c = 0;
for (String name : rootParamNames) {
try {
methodMetaData.put(name, rootParamValues.get(c++));
} catch (Exception e) {
methodMetaData.put(name, "");
}
}
if (rootParamValues.size() > 0) {
return new Tuple<Method, Map<String, String>>(m, methodMetaData);
}
}
}
}
return null;
}
static play.mvc.Result invokeMethod(Class<?> targetClass, play.GlobalSettings global, Method method,
Map<String, Object> extractedArgs, play.mvc.Http.RequestHeader r) {
try {
Object[] argValues = new Object[0];
List<Object> argVals = new ArrayList<Object>();
Annotation[][] annos = method.getParameterAnnotations();
for (Annotation[] ans : annos) {
PathParam pathParam = null;
QueryParam queryParam = null;
for (Annotation an : ans) {
if (an instanceof PathParam)
pathParam = (PathParam) an;
else if (an instanceof QueryParam)
queryParam = (QueryParam) an;
}
if (pathParam != null) {
Object v = extractedArgs.get(pathParam.value());
if (v != null)
argVals.add(v);
else
throw new IllegalArgumentException("can not find annotation value for argument "
+ pathParam.value() + "in " + targetClass + "#" + method);
} else if (queryParam != null) {
String queryString = r.getQueryString(queryParam.value());
argVals.add(queryString); // string type conversion?
} else
throw new IllegalArgumentException(
"can not find an appropriate JAX-RC annotation for an argument for method:" + targetClass
+ "#" + method);
}
argValues = argVals.toArray(argValues);
return (play.mvc.Result) method.invoke(global.getControllerInstance(targetClass), argValues);
} catch (InvocationTargetException cause) {
System.err.println("Exception occured while trying to invoke: " + targetClass.getName() + "#"
+ method.getName() + " with " + extractedArgs + " for uri:" + r.path());
throw new RuntimeException(cause.getCause());
} catch (Exception e) {
throw new RuntimeException(e.getCause());
}
}
static Set<Class<?>> classes(ClassLoader parentClassloader) {
return JaxrsRouter.getControllersWithPath();
// Set<String> typesAnnotatedWith = JaxrsRouter.ref.getStore().getTypesAnnotatedWith(Path.class.getName());
// Set<Class<?>> clazz = new HashSet<Class<?>>();
// for (String t : typesAnnotatedWith) {
// try {
// clazz.add(Class.forName(t, true, parentClassloader));
// } catch (ClassNotFoundException e) {
//// throw new RuntimeException(e);
// }
// }
// return clazz;
}
}