package com.github.florent37.carpaccio;
import android.support.v4.util.LruCache;
import android.view.View;
import com.github.florent37.carpaccio.model.CarpaccioAction;
import com.github.florent37.carpaccio.model.ObjectAndMethod;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
/**
* Created by florentchampigny on 21/07/15.
*/
public class CarpaccioHelper {
public static String TAG = "CarpaccioHelper";
public static boolean LOG_FAILURES = false;
protected static LruCache<String, Class> classesCache = new LruCache<>(15);
public static Object construct(String name) {
try {
Class objectClass = classesCache.get(name);
if (objectClass == null) {
objectClass = Class.forName(name);
classesCache.put(name, objectClass);
}
return objectClass.newInstance();
} catch (Exception e) {
CarpaccioLogger.e(TAG, "Cannot construct " + name, e);
}
return null;
}
/**
* Return an array of classes from the args[], with headerClass on the first position
* From [object1,object2,object3] with headerClass=View.class return [View.class, object1.class, object2.class, object3.class]
*/
public static Class[] getClassesWithHeaderClass(Object[] args, Class headerClass) {
Class[] classes = new Class[args.length + 1];
classes[0] = headerClass;
for (int i = 0; i < args.length; ++i)
classes[i + 1] = args[i].getClass();
return classes;
}
/**
* Return an array of classes from the args[]
* From [object1,object2,object3] return [object1.class, object2.class, object3.class]
*/
public static Class[] getClasses(Object[] args) {
Class[] classes = new Class[args.length];
for (int i = 0; i < args.length; ++i)
classes[i] = args[i].getClass();
return classes;
}
/**
* From arg=["arg1","arg2"] and viewClass= TextView.class and view instance of TextView (but given as a view)
* return [(TextView)view,"arg1","arg2")];
*/
public static Object[] getArgumentsWithView(View view, Class[] parametersType, Object[] args) {
Object[] out = new Object[args.length + 1];
//add the view on the first parameter
try {
out[0] = parametersType[0].cast(view);
} catch (ClassCastException e) {
if (LOG_FAILURES)
CarpaccioLogger.e(TAG, view.getClass().toString() + " cannot be cast to " + parametersType[0].getClass().toString(), e);
out[0] = view;
}
for (int i = 0; i < args.length; ++i) {
Class paramClass = parametersType[i + 1];
Object param = args[i];
if (param instanceof String && isNumber(paramClass)) {
out[i + 1] = stringToNumber((String) param, paramClass);
} else {
try {
out[i + 1] = paramClass.cast(param);
} catch (ClassCastException e) {
if (LOG_FAILURES)
CarpaccioLogger.e(TAG, param.getClass().toString() + " cannot be cast to " + paramClass.toString(), e);
out[i + 1] = param;
}
}
}
return out;
}
/**
* from "myFunction(arg1,arg2)", return "myFunction"
*/
public static String getFunctionName(String tag) {
return tag.substring(0, tag.indexOf('(')).trim();
}
/**
* from "myFunction(arg1,arg2)", return ["arg1","arg2"]
*/
public static String[] getAttributes(String tag) {
String attributes = tag.substring(tag.indexOf('(') + 1, tag.lastIndexOf(')'));
if (attributes.isEmpty())
return new String[0];
return trim(attributes.split(","));
}
public static Object removeTag(View view, String actionName) {
if (view.getTag() != null && view.getTag() instanceof List && actionName!= null) {
List<CarpaccioAction> actions = (List<CarpaccioAction>) view.getTag();
List<CarpaccioAction> newActions = new ArrayList<>();
for (int i = 0, count = actions.size(); i < count; ++i)
if (!actions.get(i).getCompleteCall().equals(actionName))
newActions.add(actions.get(i));
return newActions;
}
return view.getTag();
}
/**
* Trim an array of String (each element)
*/
public static String[] trim(String[] strings) {
for (int i = 0; i < strings.length; ++i)
strings[i] = strings[i].trim();
return strings;
}
public static ObjectAndMethod findObjectWithThisMethod(List<Object> objects, String function, int numberOfParams) {
if (objects != null && function != null) {
Method method;
Object object;
int numberOfObjects = objects.size();
for (int j = 0; j < numberOfObjects; ++j) {
object = objects.get(j);
int methodCount = object.getClass().getMethods().length;
for (int i = 0; i < methodCount; ++i) {
method = object.getClass().getMethods()[i];
if (function.equals(method.getName()) && method.getParameterTypes().length == numberOfParams) {
return new ObjectAndMethod(object, method);
}
}
}
CarpaccioLogger.v(TAG, "can't find controller with the method " + function + " , controllers=" + objects.toString());
}
return null;
}
/**
*
*/
public static Method callFunction(Object object, String name, View view, Object[] args) {
if (object != null && name != null && view != null && args != null) {
Method method = null;
Class viewClass = View.class;
//if name = font(Roboto.ttf) with a TextView
//try to find the font(TextView,String)
for (Method containedMethods : object.getClass().getMethods()) {
if (name.equals(containedMethods.getName()) && containedMethods.getParameterTypes().length == args.length + 1) { //+1 for the view
method = containedMethods;
break;
}
}
//try {
// method = object.getClass().getMethod(name, getClasses(args));
//}catch (Exception e){
// Log.v(TAG,object.getClass()+" does not contains the method "+name);
//}
return callMethod(object, method, name, view, args);
}
return null;
}
public static Method callMethod(Object object, Method method, String name, View view, Object[] args) {
if (method != null && object != null) {
CarpaccioLogger.d(TAG, view.getClass().getName() + " call method " + name + " on " + object);
try {
method.invoke(object, getArgumentsWithView(view, method.getParameterTypes(), args));
return method;
} catch (Exception e) {
CarpaccioLogger.e(TAG, object.getClass() + " cannot invoke method " + name);
}
}
return null;
}
/**
* Invoke the function object.name() with no arguments
* Then return the result (with cast)
*/
public static <T> T callFunction(Object object, String name) {
Method method = null;
try {
method = object.getClass().getMethod(name);
} catch (Exception e) {
if (LOG_FAILURES)
CarpaccioLogger.v(TAG, object.getClass() + " does not contains the method " + name);
}
if (method != null) {
try {
return (T) method.invoke(object);
} catch (Exception e) {
CarpaccioLogger.e(TAG, object.getClass() + " cannot invoke method " + name);
}
}
return null;
}
/**
* Invoke the function object.name() with no arguments
* Then return the result (with cast)
*/
public static <T> T callFunction(Object object, String name, Object[] args) {
Method method = null;
try {
method = object.getClass().getMethod(name, getClasses(args));
} catch (Exception e) {
if (LOG_FAILURES)
CarpaccioLogger.v(TAG, object.getClass() + " does not contains the method " + name);
}
if (method != null) {
try {
return (T) method.invoke(object, args);
} catch (Exception e) {
CarpaccioLogger.e(TAG, object.getClass() + " cannot invoke method " + name);
}
}
return null;
}
public static Integer stringToInt(String s) {
try {
return Integer.parseInt(s);
} catch (NumberFormatException e) {
CarpaccioLogger.e(TAG, s + " is not an integer", e);
return null;
}
}
public static Double stringToDouble(String s) {
try {
return Double.parseDouble(s);
} catch (NumberFormatException e) {
CarpaccioLogger.e(TAG, s + " is not an double", e);
return null;
}
}
public static Long stringToLong(String s) {
try {
return Long.parseLong(s);
} catch (NumberFormatException e) {
CarpaccioLogger.e(TAG, s + " is not a long", e);
return null;
}
}
public static Float stringToFloat(String s) {
try {
return Float.parseFloat(s);
} catch (NumberFormatException e) {
CarpaccioLogger.e(TAG, s + " is not a long", e);
return null;
}
}
public static boolean isNumber(Class destinationClass) {
return
Integer.class.equals(destinationClass) ||
int.class.equals(destinationClass) ||
Float.class.equals(destinationClass) ||
float.class.equals(destinationClass) ||
Long.class.equals(destinationClass) ||
long.class.equals(destinationClass) ||
Double.class.equals(destinationClass) ||
double.class.equals(destinationClass);
}
public static Object stringToNumber(String s, Class destinationClass) {
if (Integer.class.equals(destinationClass))
return stringToInt(s);
else if (destinationClass.getName().equals("int"))
return stringToInt(s);
else if (Float.class.equals(destinationClass))
return stringToFloat(s);
else if (destinationClass.getName().equals("float"))
return stringToFloat(s);
else if (Long.class.equals(destinationClass))
return stringToLong(s);
else if (destinationClass.getName().equals("long"))
return stringToLong(s);
else if (Double.class.equals(destinationClass))
return stringToDouble(s);
else if (destinationClass.getName().equals("double"))
return stringToDouble(s);
else
return null;
}
public static <T extends View> T findParentOfClass(View view, Class<T> theClass) {
if (theClass.isAssignableFrom(view.getClass()))
return (T) view;
else if (view.getParent() != null && view.getParent() instanceof View)
return findParentOfClass((View) view.getParent(), theClass);
else
return null;
}
public static Carpaccio findParentCarpaccio(View view) {
return findParentOfClass(view, Carpaccio.class);
}
public static Carpaccio registerToParentCarpaccio(View view) {
Carpaccio carpaccio = findParentOfClass(view, Carpaccio.class);
if (carpaccio != null) {
carpaccio.addCarpaccioView(view);
return carpaccio;
}
return null;
}
}