package com.austinv11.collectiveframework.utils;
import com.austinv11.collectiveframework.utils.math.MathUtils;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
import java.lang.annotation.Annotation;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
/**
* Class that contains a few useful utilities to simplify reflection
*/
public class ReflectionUtils {
/**
* Gets a list of declared methods with the given annotation
* @param annotation The annotation to search for
* @param clazz The class to search in
* @return The methods with the annotation
*/
public static List<Method> getDeclaredMethodsWithAnnotation(Class<? extends Annotation> annotation, Class clazz) {
Method[] methods = clazz.getDeclaredMethods();
List<Method> annotated = new ArrayList<Method>();
for (Method m : methods)
if (m.isAnnotationPresent(annotation))
annotated.add(m);
return annotated;
}
/**
* Gets a list of declared fields with the given annotation
* @param annotation The annotation to search for
* @param clazz The class to search in
* @return The fields with the annotation
*/
public static List<Field> getDeclaredFieldsWithAnnotation(Class<? extends Annotation> annotation, Class clazz) {
Field[] fields = clazz.getDeclaredFields();
List<Field> annotated = new ArrayList<Field>();
for (Field f : fields)
if (f.isAnnotationPresent(annotation))
annotated.add(f);
return annotated;
}
/**
* Gets a list of methods with the given annotation
* @param annotation The annotation to search for
* @param clazz The class to search in
* @return The methods with the annotation
*/
public static List<Method> getMethodsWithAnnotation(Class<? extends Annotation> annotation, Class clazz) {
Method[] methods = clazz.getMethods();
List<Method> annotated = new ArrayList<Method>();
for (Method m : methods)
if (m.isAnnotationPresent(annotation))
annotated.add(m);
return annotated;
}
/**
* Gets a list of fields with the given annotation
* @param annotation The annotation to search for
* @param clazz The class to search in
* @return The fields with the annotation
*/
public static List<Field> getFieldsWithAnnotation(Class<? extends Annotation> annotation, Class clazz) {
Field[] fields = clazz.getFields();
List<Field> annotated = new ArrayList<Field>();
for (Field f : fields)
if (f.isAnnotationPresent(annotation))
annotated.add(f);
return annotated;
}
/**
* Checks if the given method has the required parameters
* @param method The method
* @param params The parameters (ORDER MATTERS!)
* @return True of the parameters match, else false
*/
public static boolean paramsMatch(Method method, Class<?>... params) {
Class<?>[] parameters = method.getParameterTypes();
if (parameters.length == params.length) {
for (int i = 0; i < parameters.length; i++)
if (!parameters[i].getName().equals(params[i].getName()))
return false;
return true;
}
return false;
}
/**
* Attempts to find either a declared or normal field (in that order)
* @param fieldName The field to get
* @param clazz The class to search
* @return The field, or null if it wasn't found
*/
public static Field getDeclaredOrNormalField(String fieldName, Class clazz) {
for (Field f : clazz.getDeclaredFields())
if (f.getName().equals(fieldName))
return f;
for (Field f1 : clazz.getFields())
if (f1.getName().equals(fieldName))
return f1;
return null;
}
/**
* Attempts to find either a declared or normal method (in that order)
* @param methodName The method to get
* @param clazz The class to search
* @return The field, or null if it wasn't found
*/
public static Method getDeclaredOrNormalMethod(String methodName, Class clazz) {
for (Method m : clazz.getDeclaredMethods())
if (m.getName().equals(methodName))
return m;
for (Method m1 : clazz.getMethods())
if (m1.getName().equals(methodName))
return m1;
return null;
}
/**
* Gets the jvm signature for an object
* @param o The object
* @return The signature
*/
public static String getSignatureFromObject(Object o) {
if (o.getClass().isArray())
return o.getClass().getName().replace(".", "/");
return getSignatureFromObject(o, false);
}
private static String getSignatureFromObject(Object o, boolean isRecursive) {
if (o instanceof Integer)
return "I";
if (o instanceof Long)
return "J";
if (o instanceof Double)
return "D";
if (o instanceof Float)
return "F";
if (o instanceof Boolean)
return "Z";
if (!(o instanceof String) && o instanceof Character)
return "C";
if (o instanceof Byte)
return "B";
if (o instanceof Void)
return "V";
if (o instanceof Short)
return "S";
return "L"+o.getClass().getName().replace(".", "/")+";";
}
/**
* Gets the class for a signature (only handles one signature at a time)
* @param signature The signature
* @return The class representing the signature inputted
* @throws ClassNotFoundException
*/
public static Class getTypeFromSignature(String signature) throws ClassNotFoundException {
if (signature.charAt(0) == '[') {
signature = signature.replace("[", "");
if (signature.equals("I"))
return int[].class;
if (signature.equals("J"))
return long[].class;
if (signature.equals("D"))
return double[].class;
if (signature.equals("F"))
return float[].class;
if (signature.equals("Z"))
return boolean[].class;
if (signature.equals("C"))
return char[].class;
if (signature.equals("B"))
return byte[].class;
if (signature.equals("S"))
return short[].class;
if (signature.contains("L"))
return Array.newInstance(Class.forName(signature.substring(1, signature.length()).replace("/", ".")), 1).getClass();
} else {
if (signature.equals("I"))
return int.class;
if (signature.equals("J"))
return long.class;
if (signature.equals("D"))
return double.class;
if (signature.equals("F"))
return float.class;
if (signature.equals("Z"))
return boolean.class;
if (signature.equals("C"))
return char.class;
if (signature.equals("B"))
return byte.class;
if (signature.equals("V"))
return void.class;
if (signature.equals("S"))
return short.class;
if (signature.contains("L"))
return Class.forName(signature.substring(1, signature.length()-1).replace("/", "."));
}
return null;
}
/**
* Gets classes for a given name, including primitives
* @param name The name
* @return The class
* @throws ClassNotFoundException
*/
public static Class getPrimitiveSafeClassForName(String name) throws ClassNotFoundException {
if (name.equalsIgnoreCase("int"))
return Integer.TYPE;
if (name.equalsIgnoreCase("long"))
return Long.TYPE;
if (name.equalsIgnoreCase("double"))
return Double.TYPE;
if (name.equalsIgnoreCase("float"))
return Float.TYPE;
if (name.equalsIgnoreCase("boolean"))
return Boolean.TYPE;
if (name.equalsIgnoreCase("char"))
return Character.TYPE;
if (name.equalsIgnoreCase("byte"))
return Byte.TYPE;
if (name.equalsIgnoreCase("void"))
return Void.TYPE;
if (name.equalsIgnoreCase("short"))
return Short.TYPE;
return Class.forName(name);
}
/**
* This method checks if the specified class name is of a primitive
* @param name The class name
* @return True if the name is of a primitive data type
*/
public static boolean isNamePrimitive(String name) {
return name.equalsIgnoreCase("int") || name.equalsIgnoreCase("long") || name.equalsIgnoreCase("double") ||
name.equalsIgnoreCase("float") || name.equalsIgnoreCase("boolean") || name.equalsIgnoreCase("char") ||
name.equalsIgnoreCase("byte") || name.equalsIgnoreCase("void") || name.equalsIgnoreCase("short") ||
name.equalsIgnoreCase("integer") || name.equalsIgnoreCase("character");
}
/**
* This method checks if the specified class name is of a primitive, includes primitive object names
* @param name The class name
* @return True if the name is of a primitive data type
*/
public static boolean isNamePrimitiveOrPrimitiveObject(String name) {
return name.equalsIgnoreCase("int") || name.equalsIgnoreCase("long") || name.equalsIgnoreCase("double") ||
name.equalsIgnoreCase("float") || name.equalsIgnoreCase("boolean") || name.equalsIgnoreCase("char") ||
name.equalsIgnoreCase("byte") || name.equalsIgnoreCase("void") || name.equalsIgnoreCase("short") ||
name.equalsIgnoreCase("integer") || name.equalsIgnoreCase("character");
}
/**
* Checks if the given object is a primitive wrapper object
* @param o The object
* @return True of the object represents a primitive wrapper object
*/
public static boolean isPrimitiveObject(Object o) {
return o instanceof Integer || o instanceof Long || o instanceof Double || o instanceof Float ||
o instanceof Boolean || o instanceof Character || o instanceof Byte || o instanceof Void ||
o instanceof Short;
}
/**
* This attempts to coerce a string into a standard java object
* @param containingString The string to coerce
* @return The object, either a {@link Double}, {@link Integer}, {@link Character},
* {@link Boolean}, null, or {@link String}
*/
public static Object coerceStringToJavaObject(String containingString) {
if (MathUtils.isStringNumber(containingString)) {
if (containingString.contains("."))
return Double.parseDouble(containingString);
else
return Integer.parseInt(containingString);
}
if (containingString.length() == 1)
return containingString.charAt(0);
if (containingString.equals("false") || containingString.equals("true"))
return Boolean.parseBoolean(containingString);
if (containingString.equals("void"))
return null;
return containingString;
}
/**
* Creates an array from a string
* @param containingString The string
* @return The array
*/
public static Object[] coerceStringToArray(String containingString) {
containingString = containingString.replace("{", "").replace("}", "").trim();
String[] objects = containingString.split(",");
Object[] array = new Object[objects.length];
for (int i = 0; i < objects.length; i++)
array[i] = coerceStringToJavaObject(objects[i].trim());
return array;
}
/**
* Creates an array from a string
* @param containingString The string
* @param toObject The type for the array
* @return The array
* @throws ClassNotFoundException
* @throws InstantiationException
* @throws IllegalAccessException
* @throws IOException
*/
public static Object[] coerceStringToArray(String containingString, Object toObject) throws ClassNotFoundException, InstantiationException, IllegalAccessException, IOException {
containingString = containingString.replace("{", "").replace("}", "").trim();
String[] objects = containingString.split(",");
Object[] array = new Object[objects.length];
for (int i = 0; i < objects.length; i++)
array[i] = objectFromString(objects[i].trim(), toObject.getClass().newInstance()); //FIXME: Don't use newInstance()
return array;
}
/**
* Tries to create an object from a string
* @param objectString The string representing the object
* @param object The object to deserialize the string into
* @return The object
* @throws IOException
*/
public static Object objectFromString(String objectString, Object object) throws IOException, IllegalAccessException, ClassNotFoundException, InstantiationException {
if (object == null)
if (objectString.contains("[") && objectString.endsWith("]")) {
return coerceStringToArray(objectString);
} else {
return coerceStringToJavaObject(objectString);
}
if (object instanceof ISerializable)
return ((ISerializable) object).deserialize(objectString);
if (object.getClass().isArray())
return coerceStringToArray(objectString, object);
BufferedReader reader = new BufferedReader(new StringReader(objectString));
String line, block = "", blockField = null, blockType = null;
boolean isReadingBlock = false;
while ((line = reader.readLine()) != null) {
line = line.replace("\t", "");
if (!isReadingBlock && line.contains("{")) {
blockType = line.trim().substring(0, line.indexOf(":"));
blockField = line.trim().substring(line.indexOf(":")+1, line.indexOf("="));
isReadingBlock = true;
continue;
}
if (isReadingBlock && line.trim().equals("}")) {
isReadingBlock = false;
Field field = getDeclaredOrNormalField(blockField, object.getClass());
field.setAccessible(true);
field.set(object, objectFromString(objectString, getTypeFromSignature(blockType).newInstance())); //FIXME: Don't use newInstance()
continue;
}
if (isReadingBlock) {
block = block+line;
continue;
}
if (line.length() > 4) {
Field field = getDeclaredOrNormalField(line.trim().substring(line.indexOf(":")+1, line.contains("=") ? line.indexOf("=") : line.length()), object.getClass());
if (field != null) {
field.setAccessible(true);
field.set(object, coerceStringToJavaObject(line.substring(line.indexOf("=")+1).trim()));
}
}
}
return object;
}
/**
* Translates an object into a string readable by {@link #objectFromString}
* @param o The object
* @return The String
*/
public static String objectToString(Object o) {
return objectToString(o, 0);
}
/**
* Translates an object into a string readable by {@link #objectFromString}
* @param o The object
* @param tabs The number of tab indents
* @return The string
*/
public static String objectToString(Object o, int tabs) {
if (o.getClass().isArray()) {
String array = "[";
for (int i = 0; i < Array.getLength(o); i++) {
array = array+","+objectToString(Array.get(o, i), tabs);
}
array = array.replaceFirst(",", "");
return array+"]";
}
if (o instanceof ISerializable)
return ((ISerializable) o).serialize();
if (isPrimitiveObject(o) || o instanceof String)
return o.toString();
StringBuilder sb = new StringBuilder();
sb.append("{\n");
Field[] declared = o.getClass().getDeclaredFields();
for (Field f : declared) {
f.setAccessible(true);
try {
sb.append(StringUtils.repeatString("\t", tabs+1));
sb.append(getSignatureFromObject(o));
sb.append(":");
sb.append(f.getName());
sb.append("=");
sb.append(objectToString(f.get(o), tabs+1));
sb.append("\n");
} catch (IllegalAccessException e) {
e.printStackTrace();//This should never be reached
}
}
Field[] normal = o.getClass().getFields();
for (Field f : normal) {
f.setAccessible(true);
try {
sb.append(StringUtils.repeatString("\t", tabs+1));
sb.append(getSignatureFromObject(o));
sb.append(":");
sb.append(f.getName());
sb.append("=");
sb.append(objectToString(f.get(o), tabs+1));
sb.append("\n");
} catch (IllegalAccessException e) {
e.printStackTrace();//This should never be reached
}
}
sb.append(StringUtils.repeatString("\t", tabs));
sb.append("}");
return sb.toString();
}
/**
* Checks if a class implements another class
* @param classToExamine The class to check
* @param interfaceClass The interface to look for
* @return True if the class implements the provided interface
*/
public static boolean classImplementsInterface(Class classToExamine, Class interfaceClass) {
return interfaceClass.isInterface() && interfaceClass.isAssignableFrom(classToExamine);
}
}