package org.corfudb.util;
import com.google.common.collect.ImmutableMap;
import lombok.extern.slf4j.Slf4j;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
/**
* Created by mwei on 3/29/16.
*/
@Slf4j
public class ReflectionUtils {
static Map<String, Class> primitiveTypeMap = ImmutableMap.<String, Class>builder()
.put("int", Integer.TYPE)
.put("long", Long.TYPE)
.put("double", Double.TYPE)
.put("float", Float.TYPE)
.put("bool", Boolean.TYPE)
.put("char", Character.TYPE)
.put("byte", Byte.TYPE)
.put("void", Void.TYPE)
.put("short", Short.TYPE)
.put("int[]", int[].class)
.put("long[]", long[].class)
.put("double[]", double[].class)
.put("float[]", float[].class)
.put("bool[]", boolean[].class)
.put("char[]", char[].class)
.put("byte[]", byte[].class)
.put("short[]", short[].class)
.build();
private static Pattern methodExtractor = Pattern.compile("([^.\\s]*)\\((.*)\\)$");
private static Pattern classExtractor = Pattern.compile("(\\S*)\\.(.*)\\(.*$");
public static String getShortMethodName(String longName) {
int packageIndex = longName.substring(0, longName.indexOf("(")).lastIndexOf(".");
return longName.substring(packageIndex + 1);
}
public static String getMethodNameOnlyFromString(String s) {
return s.substring(0, s.indexOf("("));
}
public static Class<?> getPrimitiveType(String s) {
return primitiveTypeMap.get(s);
}
public static Class[] getArgumentTypesFromString(String s) {
String argList = s.contains("(") ? s.substring(s.indexOf("(") + 1, s.length() - 1) : s;
return Arrays.stream(argList.split(","))
.filter(x -> !x.equals(""))
.map(x -> {
try {
return Class.forName(x);
} catch (ClassNotFoundException cnfe) {
Class retVal = getPrimitiveType(x);
if (retVal == null) {
log.warn("Class {} not found", x);
}
return retVal;
}
})
.toArray(Class[]::new);
}
public static Class[] getArgumentTypesFromArgumentList(Object[] args) {
return Arrays.stream(args)
.map(Object::getClass)
.toArray(Class[]::new);
}
public static <T> T newInstanceFromUnknownArgumentTypes(Class<T> cls, Object[] args) {
try {
return cls.getDeclaredConstructor(getArgumentTypesFromArgumentList(args)).newInstance(args);
} catch (InstantiationException | InvocationTargetException | IllegalAccessException ie) {
throw new RuntimeException(ie);
} catch (NoSuchMethodException nsme) {
// Now we need to find a matching primitive type.
// We can maybe cheat by running through all the constructors until we get what we want
// due to autoboxing
Constructor[] ctors = Arrays.stream(cls.getDeclaredConstructors())
.filter(c -> c.getParameterTypes().length == args.length)
.toArray(Constructor[]::new);
for (Constructor<?> ctor : ctors) {
try {
return (T) ctor.newInstance(args);
} catch (Exception e) {
// silently drop exceptions since we are brute forcing here...
}
}
String argTypes = Arrays.stream(args)
.map(Object::getClass)
.map(Object::toString)
.collect(Collectors.joining(", "));
String availableCtors = Arrays.stream(ctors)
.map(Constructor::toString)
.collect(Collectors.joining(", "));
throw new RuntimeException("Couldn't find a matching ctor for " +
argTypes + "; available ctors are " + availableCtors);
}
}
public static Method getMethodFromToString(String methodString) {
Class<?> cls = getClassFromMethodToString(methodString);
Matcher m = methodExtractor.matcher(methodString);
m.find();
try {
return cls.getDeclaredMethod(m.group(1), getArgumentTypesFromString(m.group(2)));
} catch (NoSuchMethodException nsme) {
throw new RuntimeException(nsme);
}
}
public static Class getClassFromMethodToString(String methodString) {
Matcher m = classExtractor.matcher(methodString);
m.find();
String className = m.group(1);
try {
return Class.forName(className);
} catch (ClassNotFoundException cnfe) {
throw new RuntimeException(cnfe);
}
}
}