Uploaded image for project: 'Commons CLI'
  1. Commons CLI
  2. CLI-103

ReflectionBuilder

    XMLWordPrintableJSON

Details

    • Improvement
    • Status: Reopened
    • Minor
    • Resolution: Unresolved
    • None
    • 2.1
    • Options definition
    • None
    • 20211

    Description

      I have been doing CLI using reflection for aobut 2 years. I converted my code from getopts to commons-cli, and would like to donate it to Jakarta.

      The beauty of this approach is that it requres ZERO confiuration. All Option objects are created by reflection.
      for example

      public void main (String [] args ) {
         MyClass me = new MyClass();
         ReflectionCLI cli = new ReflectionCLI(me);
         cli.applyCommandLine(args);
      }
      

      Is all it takes.

      If there is a BeanInfo class additional properties are set.
      argName is from the Display Name
      description is from the Short Description.

      I also override the meaning of "preferred" to set the Option to required.
      I have included a simple JUnit test.
      Please let me know what you think, and what else I need to do to get it accepted.

      R,
      Nick

       
      Index: java/com/chalko/tools/batch/Batch.java
      ===================================================================
      RCS file: /cvsroot/chalktools/batch/src/java/com/chalko/tools/batch/Batch.java,v
      retrieving revision 1.19
      diff -u -r1.19 Batch.java
      --- java/com/chalko/tools/batch/Batch.java	18 Jan 2003 08:46:42 -0000	1.19
      +++ java/com/chalko/tools/batch/Batch.java	18 Jan 2003 09:29:22 -0000
      @@ -22,8 +22,9 @@
       import javax.mail.internet.MimeMultipart;
       import javax.mail.internet.MimePart;
      
      +import org.apache.commons.cli.ReflectionCLI;
      +
       import com.chalko.tools.batch.clp.BatchCLI;
      -import com.chalko.tools.batch.clp.ReflectionCLI;
       import com.chalko.tools.rt.BuildVersion;
      
       /** 
      Index: java/com/chalko/tools/batch/clp/BatchCLI.java
      ===================================================================
      RCS file:
      /cvsroot/chalktools/batch/src/java/com/chalko/tools/batch/clp/BatchCLI.java,v
      retrieving revision 1.3
      diff -u -r1.3 BatchCLI.java
      --- java/com/chalko/tools/batch/clp/BatchCLI.java	18 Jan 2003 09:14:37 -0000	1.3
      +++ java/com/chalko/tools/batch/clp/BatchCLI.java	18 Jan 2003 09:29:22 -0000
      @@ -2,6 +2,7 @@
      
       import java.io.IOException;
      
      +import org.apache.commons.cli.*;
       import org.apache.commons.cli.ParseException;
      
       import com.chalko.tools.batch.Batchable;
      Index: java/com/chalko/tools/batch/clp/ReflectionCLI.java
      ===================================================================
      RCS file: java/com/chalko/tools/batch/clp/ReflectionCLI.java
      diff -N java/com/chalko/tools/batch/clp/ReflectionCLI.java
      --- java/com/chalko/tools/batch/clp/ReflectionCLI.java	18 Jan 2003 09:14:37
      -0000	1.4
      +++ /dev/null	1 Jan 1970 00:00:00 -0000
      @@ -1,390 +0,0 @@
      -package com.chalko.tools.batch.clp;
      -/*
      - * $Header:
      /cvsroot/chalktools/batch/src/java/com/chalko/tools/batch/clp/ReflectionCLI.java,v
      1.4 2003/01/18 09:14:37 chalko Exp $
      - */
      -
      -import java.beans.BeanInfo;
      -import java.beans.IntrospectionException;
      -import java.beans.Introspector;
      -import java.beans.PropertyDescriptor;
      -import java.io.IOException;
      -import java.lang.reflect.InvocationTargetException;
      -import java.lang.reflect.Method;
      -import java.util.ArrayList;
      -import java.util.HashMap;
      -import java.util.Iterator;
      -import java.util.List;
      -import java.util.Map;
      -import java.util.Properties;
      -
      -import org.apache.commons.cli.CommandLine;
      -import org.apache.commons.cli.HelpFormatter;
      -import org.apache.commons.cli.Option;
      -import org.apache.commons.cli.OptionBuilder;
      -import org.apache.commons.cli.Options;
      -import org.apache.commons.cli.ParseException;
      -import org.apache.commons.cli.Parser;
      -import org.apache.commons.cli.PosixParser;
      -
      -/**
      - * 
      - * Used to apply command line paramters, setting properties and options.
      - *
      - * @author Nick Chalko (nick@chalko.com)
      - * @author  $Author: chalko $
      - * @version $Revision: 1.4 $
      - */
      -public class ReflectionCLI {
      -    public final static String PROPERTY_FILE_OPTION_NAME = "prop";
      -    private final Properties prop = new Properties();
      -    private Object obj;
      -    private final Parser parser;
      -
      -    private final Map writeableFields = new HashMap();
      -
      -    private final Options options = new Options();
      -
      -    /**
      -     * All propeties that are set as <emp>Preffered</emp> in the client classes
      beaninfo.
      -     *
      -     * @return List of PropertyDescriptor that where isExpert = true
      -     */
      -    protected List getExpertPropertiesList() throws ParseException {
      -
      -        List requiredPropetiesList = new ArrayList(writeableFields.size());
      -
      -        for (Iterator i = writeableFields.values().iterator(); i.hasNext();) {
      -            PropertyDescriptor p = (PropertyDescriptor) i.next();
      -            if (p.isExpert()) {
      -                requiredPropetiesList.add(p);
      -            }
      -        }
      -        return requiredPropetiesList;
      -    }
      -
      -    /**
      -     * All propeties that are set as <emp>expert</emp> 
      -     * in the client classes beaninfo.
      -     *
      -     * @return List of PropertyDescriptor that where isExpert = false.
      -     */
      -    protected List getNonExpertPropertiesList() throws ParseException {
      -
      -        List requiredPropetiesList = new ArrayList(writeableFields.size());
      -
      -        for (Iterator i = writeableFields.values().iterator(); i.hasNext();) {
      -            PropertyDescriptor p = (PropertyDescriptor) i.next();
      -            if (!p.isExpert()) {
      -                requiredPropetiesList.add(p);
      -            }
      -        }
      -        return requiredPropetiesList;
      -    }
      -
      -    /**
      -    * All propeties that are set as <emp>Preffered</emp> in the client classes
      beaninfo.
      -    *
      -    * @return List of PropertyDescriptor that where isPreferred = true.
      -    */
      -    protected List getRequiredPropertiesList() throws ParseException {
      -
      -        List requiredPropetiesList = new ArrayList(writeableFields.size());
      -
      -        for (Iterator i = writeableFields.values().iterator(); i.hasNext();) {
      -            PropertyDescriptor p = (PropertyDescriptor) i.next();
      -            if (p.isPreferred()) {
      -                requiredPropetiesList.add(p);
      -            }
      -        }
      -
      -        return requiredPropetiesList;
      -    }
      -
      -    public boolean isAllRequiredPropertiesSet() throws ParseException {
      -        try {
      -            boolean result = true;
      -            ;
      -            for (Iterator i = getRequiredPropertiesList().iterator();
      -                i.hasNext();
      -                ) {
      -                PropertyDescriptor pd = (PropertyDescriptor) i.next();
      -                Method readMethod = pd.getReadMethod();
      -                if (readMethod != null) {
      -
      -                    Object value = readMethod.invoke(obj, null);
      -                    if (value == null) {
      -                        result = false;
      -                        break;
      -                    }
      -
      -                }
      -            }
      -
      -            return result;
      -        } catch (IllegalAccessException e) {
      -            throw new ParseException(e.getMessage());
      -        } catch (InvocationTargetException e) {
      -            throw new ParseException(e.getMessage());
      -        }
      -
      -    }
      -
      -    /**
      -    * Apply the proprties to the Object.  Uses the java beans introspection to
      find writable properties.
      -    * @param prop java.util.Properties the set of Properties to apply
      -    */
      -    protected void apply(Properties prop) throws ParseException {
      -
      -        Object args[] = new Object[1];
      -        for (Iterator i = writeableFields.values().iterator(); i.hasNext();) {
      -            String newValue = null;
      -            PropertyDescriptor p = (PropertyDescriptor) i.next();
      -            try {
      -                Method writeMethod = p.getWriteMethod();
      -                String propertyName = p.getName();
      -                newValue = prop.getProperty(p.getName());
      -                if (newValue != null) {
      -                    args[0] = newValue;
      -                    // handle primitives.
      -                    Class clazz = p.getPropertyType();
      -                    if (clazz.isPrimitive()) {
      -                        args[0] = primitiveValueOf(clazz, newValue);
      -                    } else {
      -                        args[0] = valueFromStringConstructor(clazz, newValue);
      -                    }
      -                    writeMethod.invoke(obj, args);
      -                }
      -            } catch (Exception e) {
      -                throw new ParseException(
      -                    "Unable to set the value of "
      -                        + p.getName()
      -                        + " to "
      -                        + newValue
      -                        + " because "
      -                        + e);
      -            }
      -        }
      -
      -    }
      -
      -    private void initOptions() throws ParseException, java.io.IOException {
      -
      -        // setup long options from writeable properties
      -
      -        options.addOption(
      -            OptionBuilder
      -                .withArgName("file")
      -                .hasArgs()
      -                .withDescription("Property file")
      -                .withLongOpt(PROPERTY_FILE_OPTION_NAME)
      -                .create());
      -
      -        for (Iterator i = writeableFields.values().iterator(); i.hasNext();) {
      -
      -            PropertyDescriptor pd = (PropertyDescriptor) i.next();
      -            Method writeMethod = pd.getWriteMethod();
      -
      -            if (writeMethod != null) {
      -                options.addOption(
      -                    OptionBuilder
      -                        .hasArg()
      -                        .withDescription(pd.getShortDescription())
      -                        .withArgName(pd.getDisplayName())
      -                        .withLongOpt(pd.getName())
      -                        .create());
      -
      -            }
      -
      -        }
      -
      -        return;
      -    }
      -
      -    /**
      -    */
      -    private void initWriteablePropertiesList() throws ParseException {
      -        writeableFields.clear();
      -
      -        try {
      -
      -            BeanInfo bi = Introspector.getBeanInfo(obj.getClass());
      -
      -            addWritableFields(writeableFields, bi);
      -            BeanInfo[] additionalBI = bi.getAdditionalBeanInfo();
      -            if (additionalBI != null) {
      -                for (int i = 0; i < additionalBI.length; i++) {
      -                    BeanInfo beanInfo = additionalBI[i];
      -                    addWritableFields(writeableFields, beanInfo);
      -
      -                }
      -
      -            }
      -
      -        } catch (IntrospectionException e) {
      -
      -            throw (
      -                new ParseException("Unable to apply Properties becuase " + e));
      -
      -        }
      -
      -    }
      -
      -    private void addWritableFields(Map writeableFields, BeanInfo bi) {
      -        PropertyDescriptor pd[] = bi.getPropertyDescriptors();
      -
      -        Object args[] = new Object[1];
      -
      -        for (int i = 0; i < pd.length; i++) {
      -
      -            String newValue = null;
      -
      -            PropertyDescriptor p = pd[i];
      -
      -            Method writeMethod = p.getWriteMethod();
      -
      -            if (writeMethod != null) {
      -
      -                writeableFields.put(p.getName(), p);
      -            }
      -
      -        }
      -    }
      -
      -    /**
      -    * Loads a set of file into an existing Property set.
      -    * Creation date: (7/25/00 4:52:19 PM)
      -    * @return java.util.Properties
      -    * @param fileNames java.lang.String[]
      -    * @param prop java.util.Properties
      -    */
      -    protected static Properties loadPropertyFile(
      -        String fileName,
      -        Properties prop)
      -        throws java.io.IOException {
      -        if (prop == null) {
      -            prop = new Properties();
      -        }
      -        java.io.File file = new java.io.File(fileName);
      -        java.io.FileInputStream fis = new java.io.FileInputStream(file);
      -        prop.load(fis);
      -        return prop;
      -    }
      -
      -    /**
      -     * Created  CLI using Reflection to get the list of options.
      -     */
      -    public ReflectionCLI(Object obj) throws ParseException, IOException {
      -        super();
      -        parser = new PosixParser();
      -        this.obj = obj;
      -        initWriteablePropertiesList();
      -        initOptions();
      -    }
      -
      -    /**
      -     * Reads the command line passed and applies it to the associated object.
      -     * 
      -     * @param args java.lang.String[]
      -     * @return argumentsLeft String[]
      -     */
      -    public String[] applyCommandLine(String[] args)
      -        throws java.io.IOException, ParseException {
      -
      -        CommandLine cl = parser.parse(options, args);
      -
      -        prop.clear();
      -
      -        int c;
      -        Option[] optArray = cl.getOptions();
      -        for (int i = 0; i < optArray.length; i++) {
      -            Option opt = optArray[i];
      -            String longOpt = opt.getLongOpt();
      -            if (longOpt.equals("prop")) {
      -                String[] files = opt.getValues();
      -                for (int j = 0; j < files.length; j++) {
      -                    loadPropertyFile(files[j], prop);
      -                }
      -
      -            } else {
      -                PropertyDescriptor pd =
      -                    (PropertyDescriptor) writeableFields.get(longOpt);
      -                prop.put(pd.getName(), opt.getValue());
      -
      -            }
      -
      -        }
      -        apply(prop);
      -
      -        return cl.getArgs();
      -
      -    }
      -
      -    public void printUsage() {
      -        HelpFormatter formatter = new HelpFormatter();
      -        formatter.printHelp(obj.getClass().getName(), options);
      -
      -    }
      -
      -    /**
      -     * Insert the method's description here.
      -     * Creation date: (5/29/2001 3:15:03 PM)
      -     * @return java.lang.Object
      -     * @param clazz java.lang.Class
      -     * @param value java.lang.String
      -     */
      -    protected static Object primitiveValueOf(Class clazz, String value)
      -        throws java.text.ParseException {
      -        Object obj = null;
      -        if (clazz == boolean.class || clazz == Boolean.class) {
      -            String tf = value.toLowerCase();
      -            if (tf.equals("true") || tf.equals("yes")) {
      -                obj = Boolean.TRUE;
      -            } else if (tf.equals("false") || tf.equals("no")) {
      -                obj = Boolean.FALSE;
      -            } else {
      -                throw new java.text.ParseException(
      -                    "A boolean must be set to either true or false ",
      -                    0);
      -            }
      -        } else if (clazz == int.class || clazz == Integer.class) {
      -            obj = Integer.valueOf(value);
      -        } else if (clazz == long.class || clazz == Long.class) {
      -            obj = Long.valueOf(value);
      -        } else if (clazz == float.class || clazz == Float.class) {
      -            obj = Float.valueOf(value);
      -        } else if (clazz == double.class || clazz == Double.class) {
      -            obj = Double.valueOf(value);
      -        } else if (clazz == byte.class || clazz == Byte.class) {
      -            obj = Byte.valueOf(value);
      -        } else if (clazz == char.class || clazz == Character.class) {
      -            obj = new Character(value.charAt(0));
      -        }
      -        return obj;
      -    }
      -
      -    /**
      -     * Insert the method's description here.
      -     * Creation date: (5/29/2001 3:15:03 PM)
      -     * @return java.lang.Object
      -     * @param clazz java.lang.Class
      -     * @param value java.lang.String
      -     */
      -    protected static Object valueFromStringConstructor(
      -        Class clazz,
      -        String value) {
      -        Object obj = null;
      -        if (clazz == String.class) {
      -            obj = value;
      -        } else {
      -            try {
      -                obj =
      -                    clazz.getConstructor(
      -                        new Class[] { String.class }).newInstance(
      -                        new Object[] { value });
      -            } catch (Exception e) {
      -                // just return the null
      -            }
      -        }
      -        return obj;
      -    }
      -}
      Index: test/com/chalko/tools/batch/clp/Mirror.java
      ===================================================================
      RCS file: test/com/chalko/tools/batch/clp/Mirror.java
      diff -N test/com/chalko/tools/batch/clp/Mirror.java
      --- test/com/chalko/tools/batch/clp/Mirror.java	18 Jan 2003 09:14:33 -0000	1.2
      +++ /dev/null	1 Jan 1970 00:00:00 -0000
      @@ -1,77 +0,0 @@
      -package com.chalko.tools.batch.clp;
      -
      -import java.io.File;
      -
      -/**
      - * A simple class to test reflection CLI.
      - * @author Nick Chalko (nick@chalko.com)
      - * @author $Author: chalko $
      - * @version $Revision: 1.2 $
      - */
      -public class Mirror {
      -    
      -    private File file;
      -    private String name;
      -    private boolean sleeping;
      -
      -    /**
      -     * Constructor for SimpleClass.
      -     */
      -    public Mirror() {
      -        super();
      -    }
      -
      -    public static void main(String[] args) {}
      -    /**
      -     * Returns the file.
      -     * @return File
      -     */
      -    public File getFile() {
      -        return file;
      -    }
      -
      -
      -
      -    /**
      -     * Returns the name.
      -     * @return String
      -     */
      -    public String getName() {
      -        return name;
      -    }
      -
      -    /**
      -     * Sets the file.
      -     * @param file The file to set
      -     */
      -    public void setFile(File file) {
      -        this.file = file;
      -    }
      -
      -
      -
      -    /**
      -     * Sets the name.
      -     * @param name The name to set
      -     */
      -    public void setName(String name) {
      -        this.name = name;
      -    }
      -
      -    /**
      -     * Returns the sleeping.
      -     * @return boolean
      -     */
      -    public boolean isSleeping() {
      -        return sleeping;
      -    }
      -
      -    /**
      -     * Sets the sleeping.
      -     * @param sleeping The sleeping to set
      -     */
      -    public void setSleeping(boolean sleeping) {
      -        this.sleeping = sleeping;
      -    }
      -
      -}
      Index: test/com/chalko/tools/batch/clp/ReflectionCLITest.java
      ===================================================================
      RCS file: test/com/chalko/tools/batch/clp/ReflectionCLITest.java
      diff -N test/com/chalko/tools/batch/clp/ReflectionCLITest.java
      --- test/com/chalko/tools/batch/clp/ReflectionCLITest.java	18 Jan 2003
      09:14:33 -0000	1.5
      +++ /dev/null	1 Jan 1970 00:00:00 -0000
      @@ -1,81 +0,0 @@
      -package com.chalko.tools.batch.clp;
      -
      -import java.io.IOException;
      -
      -import junit.framework.TestCase;
      -
      -import org.apache.commons.cli.ParseException;
      -
      -/**
      - * 
      - * @author Nick Chalko (nick@chalko.com)
      - * @author $Author: chalko $
      - * @version $Revision: 1.5 $
      - */
      -public class ReflectionCLITest extends TestCase {
      -    private Mirror mirror;
      -    private ReflectionCLI cli;
      -    /**
      -     * Constructor for ReflectionCLITest.
      -     * @param testName
      -     */
      -    public ReflectionCLITest(String testName) {
      -        super(testName);
      -    }
      -
      -    /**
      -     * @see TestCase#setUp()
      -     */
      -    protected void setUp() throws Exception {
      -        super.setUp();
      -        mirror = new Mirror();
      -        cli = new ReflectionCLI(mirror);
      -
      -    }
      -
      -    /**
      -     * @see TestCase#tearDown()
      -     */
      -    protected void tearDown() throws Exception {
      -        super.tearDown();
      -        mirror = null;
      -        cli = null;
      -    }
      -
      -    public void testNoArgs() throws ParseException, IOException {
      -        cli.applyCommandLine(new String[] {});
      -        assertEquals("all required
      properties",true,cli.isAllRequiredPropertiesSet());
      -        cli.printUsage();
      -
      -    }
      -    
      -    public void testFile() throws ParseException, IOException {
      -        cli.applyCommandLine(new String[] {"--file","foo.txt"});
      -        assertEquals("all required
      properties",true,cli.isAllRequiredPropertiesSet());
      -        cli.printUsage();
      -        assertNotNull("File was not set",mirror.getFile());
      -        assertEquals("File name","foo.txt",mirror.getFile().getName());
      -
      -    }
      -    
      -    public void testSleeping() throws ParseException, IOException {
      -        cli.applyCommandLine(new String[] {"--sleeping","true"});
      -        assertEquals("all required
      properties",true,cli.isAllRequiredPropertiesSet());
      -        cli.printUsage();
      -        
      -        assertEquals("sleeping",true,mirror.isSleeping());
      -
      -    }
      -    
      -        public void testProp() throws ParseException, IOException {
      -        cli.applyCommandLine(new String[]
      {"--prop","src/test/com/chalko/tools/batch/clp/mirror.properties"});
      -        assertEquals("all required
      properties",true,cli.isAllRequiredPropertiesSet());
      -        cli.printUsage();
      -        
      -        assertEquals("name","foobar",mirror.getName());
      -
      -    }
      -
      -
      -
      -}
      Index: test/com/chalko/tools/batch/clp/mirror.properties
      ===================================================================
      RCS file: test/com/chalko/tools/batch/clp/mirror.properties
      diff -N test/com/chalko/tools/batch/clp/mirror.properties
      --- test/com/chalko/tools/batch/clp/mirror.properties	18 Jan 2003 08:58:30 -0000	1.1
      +++ /dev/null	1 Jan 1970 00:00:00 -0000
      @@ -1,2 +0,0 @@
      -# $Header: $
      -name=foobar
      \ No newline at end of file
      

      Attachments

        Activity

          People

            Unassigned Unassigned
            jbjk John Keyes
            Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

            Dates

              Created:
              Updated: