Author: bpoussin Date: 2008-07-31 08:29:02 +0000 (Thu, 31 Jul 2008) New Revision: 938 Added: trunk/lutinutil/src/main/java/org/codelutin/util/ApplicationConfig.java trunk/lutinutil/src/test/java/org/codelutin/util/ApplicationConfigTest.java Modified: trunk/lutinutil/src/main/java/org/codelutin/util/ObjectUtil.java trunk/lutinutil/src/test/java/org/codelutin/util/ObjectUtilTest.java Log: add new class for commande line parsing and application config Added: trunk/lutinutil/src/main/java/org/codelutin/util/ApplicationConfig.java =================================================================== --- trunk/lutinutil/src/main/java/org/codelutin/util/ApplicationConfig.java (rev 0) +++ trunk/lutinutil/src/main/java/org/codelutin/util/ApplicationConfig.java 2008-07-31 08:29:02 UTC (rev 938) @@ -0,0 +1,417 @@ +/* *##% + * Copyright (C) 2002-2008 Code Lutin, Benjamin Poussin + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + *##%*/ + +package org.codelutin.util; + + +import java.io.File; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; +import java.util.Properties; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Usage: + * <li> create subclass of ApplicationConfig, where in constructor you call + * addAliases, setConfigFileName, setDefaultActionPackage, setDefaultActionClass, setDefaultActionMethod + * to have properly value. + * <li> conf = new MonAppConfig(); + * <li> conf.parse(args); + * <li> here you can used conf.getOption(key); + * <li> conf.doAction(0); + * <li> ... + * <li> conf.doAction(n); + * + * <pre> + * Librairie de parsing de ligne de commande + o Chaque action est en faite une classe java. Les arguments de la methode (type et nom) donne l'information pour la ligne de commande. + o MonAction{ maMethod(File file, Class clazz, String str, int i) {//implantation} } est appelable par MonAction.maMethod clazz=org.codelutin.util.Truc file= tmp/test i=10 str=coucou. Lors de la configuration du parseur on donne un package par defaut, une classe par defaut et une methode par defaut pour la recherche de la methode a appele. + + package.Class.method: aucun element par defaut n'est utilise + + Class.method: le package par defaut est utilise pour retrouver la classe + + method: le package et la classe par defaut sont utilises pour retrouver la methode + + Class: Le package et la methode par defaut sont utilises pour retourver la classe et la methode a appeler + o On peut alors soit avoir une classe par action, une classe pour toute les actions, un mixte des deux. Si la methode n'est pas n'est pas static l'objet la contenant est instancier (IoC ? picocontainer ?) + o Pour les Options de configuration de l'application, de la meme facon on configure une classe par defaut ou les options seront stockee. Cette classe heritera d'une classe generic qui elle meme etendra HashMap, pour implanter les get/set si on le souhaite, sinon les methodes put/get de la hashmap seront utilises. L'utilisateur aura les methodes getString, getInt, getFile, getClass, ... pour recuperer de facon type les options + o l'ordre de recherche d'une option est: depuis les valeurs par defaut, le fichier /etc/prop, le fichier $home/prop, le fichier $CUR/prop, env, line. Pour cela stockage dans N hashmap chainee. + + format du fichier key=value + + format de env key=value + + format de line. option key=value, ou si on a une methode set specifique key value (automatiquement la methode setKey est recherche grace au package par defaut et classe par defaut + o ordre d'execution: Parametrage de la lib, utilisation de la lib avec les arguments du programme, lecture des options de la ligne de commande(ce qui peut modifier le nom du fichier d'option), lecture des options de env(ce qui peut modifier le nom du fichier d'option), lecture des options dans les fichiers. Execution des actions dans l'ordre ou on les trouve sur la ligne de commande. + o Amelioration: Il sera possible de mettre des annotations sur les methodes pour precisser plus de chose pour les options (pattern, min/max, alias, description, ...) + o Les arguments non parser se retrouve dans une liste de chaine + * </pre> + * + * @author poussin + * @version $Revision$ + * + * Last update: $Date$ + * by : $Author$ + */ +public class ApplicationConfig { + + /** to use log facility, just put in your code: log.info(\"...\"); */ + static private Log log = LogFactory.getLog(ApplicationConfig.class); + + /** used to know what is separator between la class et la method sur la + * ligne de commande + */ + static final private String CLASS_METHOD_SEPARATOR = "#"; + + static final public String CONFIG_FILE_NAME = "configFileName"; + + protected boolean useOnlyAliases = false; + protected Map<String, List<String>> aliases = new HashMap<String, List<String>>(); + + protected Properties defaults = new Properties(); + protected Properties classpath = new Properties(defaults); + protected Properties etcfile = new Properties(classpath); + protected Properties homefile = new Properties(etcfile); + protected Properties curfile = new Properties(homefile); + protected Properties env = new Properties(curfile); + protected Properties jvm = new Properties(env); + protected Properties line = new Properties(jvm); + protected Properties options = new Properties(line); + + protected List<String> unparsed = new ArrayList<String>(); + + protected Map<Integer, List<Action>> actions = new HashMap<Integer, List<Action>>(); + + static public class Action { + + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.METHOD) + static public @interface Step { + int value() default 0; + } + + protected int step; + protected Object o; + protected Method m; + protected String[] params; + + public Action(int step, Object o, Method m, String ... params) { + this.step = step; + this.o = o; + this.m = m; + this.params = params; + } + + public void doAction() throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { + ObjectUtil.call(o, m, params); + } + } + + public ApplicationConfig() { + defaults.setProperty(CONFIG_FILE_NAME, this.getClass().getSimpleName()); + } + + /** + * Return list of unparsed command line argument + * @return list of unparsed arguments + */ + public List<String> getUnparsed() { + return unparsed; + } + + /** + * Add action to list of action to do + * @param action action to add, can be null. + */ + public void addAction(Action action) { + if (action != null) { + Integer step = action.step; + List<Action> list = actions.get(step); + if (list == null) { + list = new LinkedList<ApplicationConfig.Action>(); + actions.put(step, list); + } + list.add(action); + } + } + + public void doAction(int step) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { + List<Action> list = actions.get(step); + if (list != null) { + for (Action a : list) { + a.doAction(); + } + } + } + + public void setUseOnlyAliases(boolean useOnlyAliases) { + this.useOnlyAliases = useOnlyAliases; + } + + public boolean isUseOnlyAliases() { + return useOnlyAliases; + } + + /** + * All argument in aliases as key is substitued by target + * @param alias alias string as '-v' + * @param target substitution as '--option verbose true' + */ + public void addAlias(String alias, String ... target) { + aliases.put(alias, Arrays.asList(target)); + } + + /** + * Set name of file where options are read (in /etc, $HOME, $CURDIR) + * @param name file name + */ + public void setConfigFileName(String name) { + // put in defaults, this permit user to overwrite it on commande line + defaults.setProperty(CONFIG_FILE_NAME, name); + } + + public String getConfigFileName() { + String result = options.getProperty(CONFIG_FILE_NAME); + return result; + } + + /** + * Set option value + * @param key + * @param value + */ + public void setOption(String key, String value) { + options.setProperty(key, value); + } + + /** + * get option value as string + * @param key + * @return String representation value + */ + public String getOption(String key) { + String value = options.getProperty(key); + return value; + } + + /** + * Get all set method on this object or super object + * @return map with method name without set and in lower case as key, and + * method as value + */ + protected Map<String, Method> getMethods() { + // looking for all methods set on ApplicationConfig + Method [] allMethods = this.getClass().getMethods(); + Map<String, Method> methods = new HashMap<String, Method>(); + for (Method m : allMethods) { + String methodName = m.getName(); + if (methodName.startsWith("set")) { + methodName = methodName.substring(3).toLowerCase(); + methods.put(methodName, m); + } + } + return methods; + } + + /** + * Take required argument for method in args and call this method on target + * object. Argument used is removed from args + * @param target object where method is + * @param m the method to call + * @param args iterator with many argument (equals or more than necessary + * @throws java.lang.Exception if call failed + */ + protected String[] getParams(Method m, ListIterator<String> args) { + int paramLenght = m.getParameterTypes().length; + String[] result = new String[paramLenght]; + for (int i=0; i < paramLenght; i++) { + String p = args.next(); + args.remove(); // remove this arg because is used now + result[i] = p; + } + return result; + } + + /** + * Create action from string, string must be [package.][class][#][method] + * if package, class or method missing, default is used + * @param name + * @param params + * @return + */ + protected Action createAction(String name, ListIterator<String> args) throws ArgumentsParserException, InstantiationException, IllegalAccessException { + Action result = null; + + Class clazz; + Method method = null; + String className; + String methodName ; + + // looking for method name + int sep = name.lastIndexOf(CLASS_METHOD_SEPARATOR); + if (sep == -1) { + throw new IllegalArgumentException(String.format( + "Can't find action method in %s", name)); + } else { + className = name.substring(0, sep); + methodName = name.substring(sep + 1); + } + + // looking for class name + try { + clazz = Class.forName(className); + } catch (ClassNotFoundException eee) { + throw new IllegalArgumentException(String.format( + "Can't find action class %s", className)); + } + + List<Method> methods = ObjectUtil.getMethod(clazz, methodName, true); + if (methods.size() > 0) { + if (methods.size() > 1) { + log.warn(String.format( + "More than one method found, used the first: %s", methods)); + } + method = methods.get(0); + } + + if (method != null) { + args.remove(); // remove option from command line, because is used now + Object o = null; + if (!Modifier.isStatic(method.getModifiers())) { + o = clazz.newInstance(); + } + int step = 0; + Action.Step annotation = method.getAnnotation(Action.Step.class); + if (annotation != null) { + step = annotation.value(); + } + + String[] params = getParams(method, args); + result = new Action(step, o, method, params); + } + + return result; + } + + /** + * Parse option and call set necessary method, read jvm, env variable, + * Load configuration file and prepare Action. + * @param args argument as main(String[] args) + * @throws org.codelutin.util.ArgumentsParserException + */ + public void parse(String [] args) throws ArgumentsParserException { + try { + Map<String, Method> methods = getMethods(); + + List<String> arguments = new ArrayList<String>(args.length); + for (String arg : args) { + if (aliases.containsKey(arg)) { + arguments.addAll(aliases.get(arg)); + } else { + arguments.add(arg); + } + } + + // first parse option + for (ListIterator<String> i = arguments.listIterator(); i.hasNext();) { + String arg = i.next(); + if (arg.startsWith("--")) { + String optionName = arg.substring(2); + if (methods.containsKey(optionName)) { + i.remove(); // remove this arg because is used now + Method m = methods.get(optionName); + String[] params = getParams(m, i); + log.debug(String.format("Set option '%s' with method '%s %s'", optionName, m, Arrays.toString(params))); + ObjectUtil.call(this, m, params); + } + } + } + + // + // second load options from all sources + // + // JVM + jvm.putAll(System.getProperties()); + // ENV + env.putAll(System.getenv()); + + // classpath + String filename = getConfigFileName(); + URL inClasspath = ClassLoader.getSystemClassLoader().getResource(filename); + if (inClasspath == null) { + inClasspath = ApplicationConfig.class.getResource(filename); + } + if (inClasspath != null) { + log.info("Chargement du fichier de config: " + inClasspath); + classpath.load(inClasspath.openStream()); + } + + // file /etc + File etcConfig = new File(File.separator + "etc" + File.separator + filename); + if (etcConfig.exists()) { + log.info("Chargement du fichier de config: " + etcConfig); + etcfile.load(etcConfig.toURI().toURL().openStream()); + } + + // file /home + String home = System.getProperty("user.home"); + File homeConfig = new File(home + File.separator + filename); + if (homeConfig.exists()) { + log.info("Chargement du fichier de config: " + homeConfig); + homefile.load(homeConfig.toURI().toURL().openStream()); + } + + // file $CURDIR + File config = new File(filename); + if (config.exists()) { + log.info("Chargement du fichier de config: " + config); + curfile.load(config.toURI().toURL().openStream()); + } + + // + // third parse action and do action + // + for (ListIterator<String> i = arguments.listIterator(); i.hasNext();) { + String arg = i.next(); + if (arg.startsWith("--")) { + String optionName = arg.substring(2); + Action action = createAction(optionName, i); + addAction(action); + } + } + + // + // not used args added to unparsed + // + unparsed.addAll(arguments); + + } catch (Exception eee) { + eee.printStackTrace(); + throw new ArgumentsParserException("Can't parse argument", eee); + } + } +} Modified: trunk/lutinutil/src/main/java/org/codelutin/util/ObjectUtil.java =================================================================== --- trunk/lutinutil/src/main/java/org/codelutin/util/ObjectUtil.java 2008-07-28 17:50:33 UTC (rev 937) +++ trunk/lutinutil/src/main/java/org/codelutin/util/ObjectUtil.java 2008-07-31 08:29:02 UTC (rev 938) @@ -1,5 +1,5 @@ /* *##% - * Copyright (C) 2002, 2003, 2004 Code Lutin, C�dric Pineau, + * Copyright (C) 2002, 2003, 2004 Code Lutin, C�dric Pineau, * Benjamin Poussin * * This program is free software; you can redistribute it and/or @@ -33,7 +33,11 @@ import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; import org.apache.commons.beanutils.BeanUtils; +import org.apache.commons.beanutils.ConvertUtils; public class ObjectUtil { // ObjectUtil @@ -90,8 +94,63 @@ } return o; } + + /** + * Call method m with params as String. Each param is converted to required type for + * method with beanutils converter + * @param o object where method must be call + * @param m method to call + * @param params parameters for method call + * @return returned method's value + */ + public static Object call(Object o, Method m, String ... params) + throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { + Class[] types = m.getParameterTypes(); + if (params.length != types.length) { + throw new IllegalArgumentException(String.format( + "Bad number params we have %1$s parameters and waiting %2$s.", + params.length, types.length)); + } + Object[] parameters = new Object[types.length]; + for (int i=0; i<types.length; i++) { + Class clazz = types[i]; + String v = params[i]; + parameters[i] = ConvertUtils.convert(v, clazz); + if (parameters[i] != null && + !String.class.getName().equals(clazz.getName()) && + String.class.getName().equals(parameters[i].getClass().getName()) ) { + throw new IllegalArgumentException(String.format( + "Can convert argument to correct type. %s can't be" + + " converted from String to %s conversion is done to %s", + v, clazz.getName(), parameters[i].getClass().getName())); + } + } + Object result = m.invoke(o, parameters); + return result; + } /** + * Get all methods with name given in argument without check parameters + * @param o object where search is done + * @param methodName method name to search + * @param ignoreCase if true, ignore difference in method name case + * @return + */ + public static List<Method> getMethod(Class clazz, String methodName, boolean ignoreCase) { + List<Method> result = new ArrayList<Method>(); + + Method[] methods = clazz.getMethods(); + for (Method m : methods) { + if(ignoreCase && methodName.equalsIgnoreCase(m.getName()) || + methodName.equals(m.getName())) { + result.add(m); + } + } + + return result; + } + + /** * Method toObject * * @param o Object to transform Added: trunk/lutinutil/src/test/java/org/codelutin/util/ApplicationConfigTest.java =================================================================== --- trunk/lutinutil/src/test/java/org/codelutin/util/ApplicationConfigTest.java (rev 0) +++ trunk/lutinutil/src/test/java/org/codelutin/util/ApplicationConfigTest.java 2008-07-31 08:29:02 UTC (rev 938) @@ -0,0 +1,229 @@ +/* + * Copyright (C) 2002-2008 Code Lutin, Benjamin Poussin + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +package org.codelutin.util; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; +import junit.framework.TestCase; +import org.codelutin.util.ApplicationConfig.Action; + +/** + * + * @author poussin + */ +public class ApplicationConfigTest extends TestCase { + + static int dummyActionCall = 0; + static public class DummyAction { + @Action.Step(1) + public void dummyAction(String s, int step) { + dummyActionCall++; + System.out.println(s + ":" + step); + } + } + + public ApplicationConfigTest(String testName) { + super(testName); + } + + /** + * Test of getUnparsed method, of class ApplicationConfig. + */ + public void testGetUnparsed() throws Exception { + System.out.println("getUnparsed"); + ApplicationConfig instance = new ApplicationConfig(); + List<String> expResult = new ArrayList<String>(); + List<String> result = instance.getUnparsed(); + assertEquals(expResult, result); + + expResult.add("toto"); + expResult.add("titi"); + expResult.add("tata"); + + instance.parse(new String[]{"toto", "titi", "tata"}); + result = instance.getUnparsed(); + assertEquals(expResult, result); + } + + /** + * Test of addAction method, of class ApplicationConfig. + */ + public void testAddAction() throws Exception { + System.out.println("addAction"); + Action action = null; + ApplicationConfig instance = new ApplicationConfig(); + + // test add null Action + instance.addAction(action); + + action = new Action(1, new DummyAction(), DummyAction.class.getMethod("dummyAction", String.class, Integer.TYPE), "coucou", "12"); + instance.addAction(action); + } + + /** + * Test of doAction method, of class ApplicationConfig. + */ + public void testDoAction() throws Exception { + System.out.println("doAction"); + ApplicationConfig instance = new ApplicationConfig(); + + Action action = new Action(1, new DummyAction(), DummyAction.class.getMethod("dummyAction", String.class, Integer.TYPE), "coucou", "12"); + instance.addAction(action); + + dummyActionCall = 0; + assertEquals(0, dummyActionCall); + instance.doAction(0); + assertEquals(0, dummyActionCall); + instance.doAction(1); + assertEquals(1, dummyActionCall); + instance.doAction(2); + assertEquals(1, dummyActionCall); + } + + /** + * Test of setUseOnlyAliases method, of class ApplicationConfig. + */ + public void testSetUseOnlyAliases() { + System.out.println("setUseOnlyAliases"); + ApplicationConfig instance = new ApplicationConfig(); + assertEquals(false, instance.isUseOnlyAliases()); + instance.setUseOnlyAliases(false); + assertEquals(false, instance.isUseOnlyAliases()); + instance.setUseOnlyAliases(true); + assertEquals(true, instance.isUseOnlyAliases()); + } + + /** + * Test of addAlias method, of class ApplicationConfig. + */ + public void testAddAlias() throws Exception { + System.out.println("addAlias"); + ApplicationConfig instance = new ApplicationConfig(); + instance.addAlias("toto", "totochange"); + instance.addAlias("titi", "titichange"); + + List<String> expResult = new ArrayList<String>(); + List<String> result = instance.getUnparsed(); + assertEquals(expResult, result); + + expResult.add("totochange"); + expResult.add("titichange"); + expResult.add("tata"); + + instance.parse(new String[]{"toto", "titi", "tata"}); + result = instance.getUnparsed(); + assertEquals(expResult, result); + } + + /** + * Test of setConfigFileName method, of class ApplicationConfig. + */ + public void testSetConfigFileName() { + System.out.println("setConfigFileName"); + ApplicationConfig instance = new ApplicationConfig(); + instance.setConfigFileName("bidulle"); + assertEquals("bidulle", instance.getConfigFileName()); + } + + /** + * Test of setOption method, of class ApplicationConfig. + */ + public void testSetOption() { + System.out.println("setOption"); + ApplicationConfig instance = new ApplicationConfig(); + assertEquals(null, instance.getOption("truc")); + instance.setOption("truc", "bidulle"); + assertEquals("bidulle", instance.getOption("truc")); + } + + /** + * Test of getMethods method, of class ApplicationConfig. + */ + public void testGetMethods() { + System.out.println("getMethods"); + ApplicationConfig instance = new ApplicationConfig(); + Map<String, Method> result = instance.getMethods(); + assertTrue(result.containsKey("option")); + } + + /** + * Test of getParams method, of class ApplicationConfig. + */ + public void testGetParams() throws Exception { + System.out.println("getParams"); + Method m = DummyAction.class.getMethod("dummyAction", String.class, Integer.TYPE); + List<String> list = new ArrayList<String>(Arrays.asList("toto", "10", "/tmp", "9")); + ListIterator<String> args = list.listIterator(); + + ApplicationConfig instance = new ApplicationConfig(); + String[] expResult = new String[]{"toto", "10"}; + String[] result = instance.getParams(m, args); + assertEquals(Arrays.asList(expResult), Arrays.asList(result)); + assertEquals(2, list.size()); + } + + /** + * Test of createAction method, of class ApplicationConfig. + */ + public void testCreateAction() throws Exception { + System.out.println("createAction"); + List<String> list = new ArrayList<String>(Arrays.asList("dummy", "toto", "10", "/tmp", "9")); + ListIterator<String> args = list.listIterator(); + args.next(); + ApplicationConfig instance = new ApplicationConfig(); + + Action result = instance.createAction( + DummyAction.class.getName() + "#dummyAction", args); + assertEquals(1, result.step); + dummyActionCall = 0; + result.doAction(); + assertEquals(1, dummyActionCall); +} + + /** + * Test of parse method, of class ApplicationConfig. + */ + public void testParse() throws Exception { + System.out.println("parse"); + String[] args = "-f file -v -d -o /tmp/file -m coucou 10 others args".split(" "); + ApplicationConfig instance = new ApplicationConfig(); + instance.addAlias("-f", "--option", "file"); + instance.addAlias("-v", "--option", "verbose", "true"); + instance.addAlias("-d", "--option", "debug", "true"); + instance.addAlias("-o", "--option", "output"); + instance.addAlias("-m", "--" + DummyAction.class.getName() + "#dummyAction"); + instance.parse(args); + + dummyActionCall = 0; + assertEquals("file", instance.getOption("file")); + assertEquals("true", instance.getOption("verbose")); + assertEquals("true", instance.getOption("debug")); + assertEquals("/tmp/file", instance.getOption("output")); + assertEquals(Arrays.asList("others", "args"), instance.getUnparsed()); + + instance.doAction(1); + assertEquals(1, dummyActionCall); + } + +} Modified: trunk/lutinutil/src/test/java/org/codelutin/util/ObjectUtilTest.java =================================================================== --- trunk/lutinutil/src/test/java/org/codelutin/util/ObjectUtilTest.java 2008-07-28 17:50:33 UTC (rev 937) +++ trunk/lutinutil/src/test/java/org/codelutin/util/ObjectUtilTest.java 2008-07-31 08:29:02 UTC (rev 938) @@ -33,6 +33,8 @@ import java.io.File; +import java.lang.reflect.Method; +import java.util.List; import junit.framework.TestCase; @@ -42,6 +44,17 @@ */ public class ObjectUtilTest extends TestCase { + public void testCall() throws Exception { + Dummy dummy = new Dummy(); + + List<Method> methods = ObjectUtil.getMethod(Dummy.class, "setfile", true); + assertEquals(1, methods.size()); + + ObjectUtil.call(dummy, methods.get(0), "/tmp"); + + assertEquals(new File("/tmp"), dummy.getFile()); + } + public void testCreate() throws Exception { Object o = ObjectUtil.create("java.lang.StringBuffer"); assertTrue(o != null);