16224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala/* 26224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * Copyright 2012 AndroidPlot.com 36224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * 46224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * Licensed under the Apache License, Version 2.0 (the "License"); 56224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * you may not use this file except in compliance with the License. 66224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * You may obtain a copy of the License at 76224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * 86224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * http://www.apache.org/licenses/LICENSE-2.0 96224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * 106224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * Unless required by applicable law or agreed to in writing, software 116224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * distributed under the License is distributed on an "AS IS" BASIS, 126224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 136224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * See the License for the specific language governing permissions and 146224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * limitations under the License. 156224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala */ 166224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala 176224eda509d436a575f801942337da92a6c18767Eino-Ville Talvalapackage com.androidplot.util; 186224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala 196224eda509d436a575f801942337da92a6c18767Eino-Ville Talvalaimport android.content.Context; 206224eda509d436a575f801942337da92a6c18767Eino-Ville Talvalaimport android.content.res.XmlResourceParser; 216224eda509d436a575f801942337da92a6c18767Eino-Ville Talvalaimport android.graphics.Color; 226224eda509d436a575f801942337da92a6c18767Eino-Ville Talvalaimport android.util.Log; 236224eda509d436a575f801942337da92a6c18767Eino-Ville Talvalaimport android.util.TypedValue; 246224eda509d436a575f801942337da92a6c18767Eino-Ville Talvalaimport org.xmlpull.v1.XmlPullParserException; 256224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala 266224eda509d436a575f801942337da92a6c18767Eino-Ville Talvalaimport java.io.IOException; 276224eda509d436a575f801942337da92a6c18767Eino-Ville Talvalaimport java.lang.reflect.InvocationTargetException; 286224eda509d436a575f801942337da92a6c18767Eino-Ville Talvalaimport java.lang.reflect.Method; 296224eda509d436a575f801942337da92a6c18767Eino-Ville Talvalaimport java.lang.reflect.Type; 306224eda509d436a575f801942337da92a6c18767Eino-Ville Talvalaimport java.util.HashMap; 316224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala 326224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala/** 336224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * Utility class for "configuring" objects via XML config files. Supports the following field types: 346224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * String 356224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * Enum 366224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * int 376224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * float 386224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * boolean 396224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * <p/> 406224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * Config files should be stored in /res/xml. Given the XML configuration /res/xml/myConfig.xml, one can apply the 416224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * configuration to an Object instance as follows: 426224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * <p/> 436224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * MyObject obj = new MyObject(); 446224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * Configurator.configure(obj, R.xml.myConfig); 456224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * <p/> 466224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * WHAT IT DOES: 476224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * Given a series of parameters stored in an XML file, Configurator iterates through each parameter, using the name 486224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * as a map to the field within a given object. For example: 496224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * <p/> 506224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * <pre> 516224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * {@code 526224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * <config car.engine.sparkPlug.condition="poor"/> 536224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * } 546224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * </pre> 556224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * <p/> 566224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * Given a Car instance car and assuming the method setCondition(String) exists within the SparkPlug class, 576224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * Configurator does the following: 586224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * <p/> 596224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * <pre> 606224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * {@code 616224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * car.getEngine().getSparkPlug().setCondition("poor"); 626224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * } 636224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * </pre> 646224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * <p/> 656224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * Now let's pretend that setCondition takes an instance of the Condition enum as it's argument. 666224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * Configurator then does the following: 676224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * <p/> 686224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * car.getEngine().getSparkPlug().setCondition(Condition.valueOf("poor"); 696224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * <p/> 706224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * Now let's look at how ints are handled. Given the following xml: 716224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * <p/> 726224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * <config car.engine.miles="100000"/> 736224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * <p/> 746224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * would result in: 756224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * car.getEngine.setMiles(Integer.ParseInt("100000"); 766224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * <p/> 776224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * That's pretty straight forward. But colors are expressed as ints too in Android 786224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * but can be defined using hex values or even names of colors. When Configurator 796224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * attempts to parse a parameter for a method that it knows takes an int as it's argument, 806224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * Configurator will first attempt to parse the parameter as a color. Only after this 816224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * attempt fails will Configurator resort to Integer.ParseInt. So: 826224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * <p/> 836224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * <config car.hood.paint.color="Red"/> 846224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * <p/> 856224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * would result in: 866224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * car.getHood().getPaint().setColor(Color.parseColor("Red"); 876224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * <p/> 886224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * Next lets talk about float. Floats can appear in XML a few different ways in Android, 896224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * especially when it comes to defining dimensions: 906224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * <p/> 916224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * <config height="10dp" depth="2mm" width="5em"/> 926224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * <p/> 936224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * Configurator will correctly parse each of these into their corresponding real pixel value expressed as a float. 946224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * <p/> 956224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * One last thing to keep in mind when using Configurator: 966224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * Values for Strings and ints can be assigned to localized values, allowing 976224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * a cleaner solution for those developing apps to run on multiple form factors 986224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * or in multiple languages: 996224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * <p/> 1006224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * <config thingy.description="@string/thingyDescription" 1016224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * thingy.titlePaint.textSize=""/> 1026224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala */ 1036224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala@SuppressWarnings("WeakerAccess") 1046224eda509d436a575f801942337da92a6c18767Eino-Ville Talvalapublic abstract class Configurator { 1056224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala 1066224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala private static final String TAG = Configurator.class.getName(); 1076224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala protected static final String CFG_ELEMENT_NAME = "config"; 1086224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala 1096224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala protected static int parseResId(Context ctx, String prefix, String value) { 1106224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala String[] split = value.split("/"); 1116224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala // is this a localized resource? 1126224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala if (split.length > 1 && split[0].equalsIgnoreCase(prefix)) { 1136224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala String pack = split[0].replace("@", ""); 1146224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala String name = split[1]; 1156224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala return ctx.getResources().getIdentifier(name, pack, ctx.getPackageName()); 1166224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala } else { 1176224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala throw new IllegalArgumentException(); 1186224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala } 1196224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala } 1206224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala 1216224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala protected static int parseIntAttr(Context ctx, String value) { 1226224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala try { 1236224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala return ctx.getResources().getColor(parseResId(ctx, "@color", value)); 1246224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala } catch (IllegalArgumentException e1) { 1256224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala try { 1266224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala return Color.parseColor(value); 1276224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala } catch (IllegalArgumentException e2) { 1286224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala // wasn't a color so try parsing as a plain old int: 1296224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala return Integer.parseInt(value); 1306224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala } 1316224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala } 1326224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala } 1336224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala 1346224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala /** 1356224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * Treats value as a float parameter. First value is tested to see whether 1366224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * it contains a resource identifier. Failing that, it is tested to see whether 1376224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * a dimension suffix (dp, em, mm etc.) exists. Failing that, it is evaluated as 1386224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * a plain old float. 1396224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * @param ctx 1406224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * @param value 1416224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * @return 1426224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala */ 1436224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala protected static float parseFloatAttr(Context ctx, String value) { 1446224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala try { 1456224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala return ctx.getResources().getDimension(parseResId(ctx, "@dimen", value)); 1466224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala } catch (IllegalArgumentException e1) { 1476224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala try { 1486224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala return PixelUtils.stringToDimension(value); 1496224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala } catch (Exception e2) { 1506224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala return Float.parseFloat(value); 1516224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala } 1526224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala } 1536224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala } 1546224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala 1556224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala protected static String parseStringAttr(Context ctx, String value) { 1566224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala try { 1576224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala return ctx.getResources().getString(parseResId(ctx, "@string", value)); 1586224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala } catch (IllegalArgumentException e1) { 1596224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala return value; 1606224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala } 1616224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala } 1626224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala 1636224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala 1646224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala protected static Method getSetter(Class clazz, final String fieldId) throws NoSuchMethodException { 1656224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala Method[] methods = clazz.getMethods(); 1666224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala 1676224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala String methodName = "set" + fieldId; 1686224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala for (Method method : methods) { 1696224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala if (method.getName().equalsIgnoreCase(methodName)) { 1706224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala return method; 1716224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala } 1726224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala } 1736224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala throw new NoSuchMethodException("No such public method (case insensitive): " + 1746224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala methodName + " in " + clazz); 1756224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala } 1766224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala 1776224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala @SuppressWarnings("unchecked") 1786224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala protected static Method getGetter(Class clazz, final String fieldId) throws NoSuchMethodException { 1796224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala Log.d(TAG, "Attempting to find getter for " + fieldId + " in class " + clazz.getName()); 1806224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala String firstLetter = fieldId.substring(0, 1); 1816224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala String methodName = "get" + firstLetter.toUpperCase() + fieldId.substring(1, fieldId.length()); 1826224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala return clazz.getMethod(methodName); 1836224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala } 1846224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala 1856224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala /** 1866224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * Returns the object containing the field specified by path. 1876224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * @param obj 1886224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * @param path Path through member hierarchy to the destination field. 1896224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * @return null if the object at path cannot be found. 1906224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * @throws java.lang.reflect.InvocationTargetException 1916224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * 1926224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * @throws IllegalAccessException 1936224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala */ 1946224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala protected static Object getObjectContaining(Object obj, String path) throws 1956224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala InvocationTargetException, IllegalAccessException, NoSuchMethodException { 1966224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala if(obj == null) { 1976224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala throw new NullPointerException("Attempt to call getObjectContaining(Object obj, String path) " + 1986224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala "on a null Object instance. Path was: " + path); 1996224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala } 2006224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala Log.d(TAG, "Looking up object containing: " + path); 2016224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala int separatorIndex = path.indexOf("."); 2026224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala 2036224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala // not there yet, descend deeper: 2046224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala if (separatorIndex > 0) { 2056224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala String lhs = path.substring(0, separatorIndex); 2066224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala String rhs = path.substring(separatorIndex + 1, path.length()); 2076224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala 2086224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala // use getter to retrieve the instance 2096224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala Method m = getGetter(obj.getClass(), lhs); 2106224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala if(m == null) { 2116224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala throw new NullPointerException("No getter found for field: " + lhs + " within " + obj.getClass()); 2126224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala } 2136224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala Log.d(TAG, "Invoking " + m.getName() + " on instance of " + obj.getClass().getName()); 2146224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala Object o = m.invoke(obj); 2156224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala // delve into o 2166224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala return getObjectContaining(o, rhs); 2176224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala //} catch (NoSuchMethodException e) { 2186224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala // TODO: log a warning 2196224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala // return null; 2206224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala //} 2216224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala } else { 2226224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala // found it! 2236224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala return obj; 2246224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala } 2256224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala } 2266224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala 2276224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala @SuppressWarnings("unchecked") 2286224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala private static Object[] inflateParams(Context ctx, Class[] params, String[] vals) throws NoSuchMethodException, 2296224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala InvocationTargetException, IllegalAccessException { 2306224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala Object[] out = new Object[params.length]; 2316224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala int i = 0; 2326224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala for (Class param : params) { 2336224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala if (Enum.class.isAssignableFrom(param)) { 2346224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala out[i] = param.getMethod("valueOf", String.class).invoke(null, vals[i].toUpperCase()); 2356224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala } else if (param.equals(Float.TYPE)) { 2366224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala out[i] = parseFloatAttr(ctx, vals[i]); 2376224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala } else if (param.equals(Integer.TYPE)) { 2386224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala out[i] = parseIntAttr(ctx, vals[i]); 2396224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala } else if (param.equals(Boolean.TYPE)) { 2406224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala out[i] = Boolean.valueOf(vals[i]); 2416224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala } else if (param.equals(String.class)) { 2426224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala out[i] = parseStringAttr(ctx, vals[i]); 2436224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala } else { 2446224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala throw new IllegalArgumentException( 2456224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala "Error inflating XML: Setter requires param of unsupported type: " + param); 2466224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala } 2476224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala i++; 2486224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala } 2496224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala return out; 2506224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala } 2516224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala 2526224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala /** 2536224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * 2546224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * @param ctx 2556224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * @param obj 2566224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * @param xmlFileId ID of the XML config file within /res/xml 2576224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala */ 2586224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala public static void configure(Context ctx, Object obj, int xmlFileId) { 2596224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala XmlResourceParser xrp = ctx.getResources().getXml(xmlFileId); 2606224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala try { 2616224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala HashMap<String, String> params = new HashMap<String, String>(); 2626224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala while (xrp.getEventType() != XmlResourceParser.END_DOCUMENT) { 2636224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala xrp.next(); 2646224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala String name = xrp.getName(); 2656224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala if (xrp.getEventType() == XmlResourceParser.START_TAG) { 2666224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala if (name.equalsIgnoreCase(CFG_ELEMENT_NAME)) 2676224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala for (int i = 0; i < xrp.getAttributeCount(); i++) { 2686224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala params.put(xrp.getAttributeName(i), xrp.getAttributeValue(i)); 2696224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala } 2706224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala break; 2716224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala } 2726224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala } 2736224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala configure(ctx, obj, params); 2746224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala } catch (XmlPullParserException e) { 2756224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala e.printStackTrace(); 2766224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala } catch (IOException e) { 2776224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala e.printStackTrace(); 2786224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala } finally { 2796224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala xrp.close(); 2806224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala } 2816224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala } 2826224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala 2836224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala public static void configure(Context ctx, Object obj, HashMap<String, String> params) { 2846224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala for (String key : params.keySet()) { 2856224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala try { 2866224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala configure(ctx, obj, key, params.get(key)); 2876224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala } catch (InvocationTargetException e) { 2886224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala e.printStackTrace(); 2896224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala } catch (IllegalAccessException e) { 2906224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala e.printStackTrace(); 2916224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala } catch (NoSuchMethodException e) { 2926224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala Log.w(TAG, "Error inflating XML: Setter for field \"" + key + "\" does not exist. "); 2936224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala e.printStackTrace(); 2946224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala } 2956224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala } 2966224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala } 2976224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala 2986224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala /** 2996224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * Recursively descend into an object using key as the pathway and invoking the corresponding setter 3006224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * if one exists. 3016224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * 3026224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * @param key 3036224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * @param value 3046224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala */ 3056224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala protected static void configure(Context ctx, Object obj, String key, String value) 3066224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala throws InvocationTargetException, IllegalAccessException, NoSuchMethodException { 3076224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala Object o = getObjectContaining(obj, key); 3086224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala if (o != null) { 3096224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala int idx = key.lastIndexOf("."); 3106224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala String fieldId = idx > 0 ? key.substring(idx + 1, key.length()) : key; 3116224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala 3126224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala Method m = getSetter(o.getClass(), fieldId); 3136224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala Class[] paramTypes = m.getParameterTypes(); 3146224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala // TODO: add support for generic type params 3156224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala if (paramTypes.length >= 1) { 3166224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala 3176224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala // split on "|" 3186224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala // TODO: add support for String args containing a | 3196224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala String[] paramStrs = value.split("\\|"); 3206224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala if (paramStrs.length == paramTypes.length) { 3216224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala 3226224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala Object[] oa = inflateParams(ctx, paramTypes, paramStrs); 3236224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala Log.d(TAG, "Invoking " + m.getName() + " with arg(s) " + argArrToString(oa)); 3246224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala m.invoke(o, oa); 3256224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala } else { 3266224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala throw new IllegalArgumentException("Error inflating XML: Unexpected number of argments passed to \"" 3276224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala + m.getName() + "\". Expected: " + paramTypes.length + " Got: " + paramStrs.length); 3286224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala } 3296224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala } else { 3306224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala // Obvious this is not a setter 3316224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala throw new IllegalArgumentException("Error inflating XML: no setter method found for param \"" + 3326224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala fieldId + "\"."); 3336224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala } 3346224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala } 3356224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala } 3366224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala 3376224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala protected static String argArrToString(Object[] args) { 3386224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala String out = ""; 3396224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala for(Object obj : args) { 3406224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala out += (obj == null ? (out += "[null] ") : 3416224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala ("[" + obj.getClass() + ": " + obj + "] ")); 3426224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala } 3436224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala return out; 3446224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala } 3456224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala} 3466224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala 347