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; } }