Class ArgParser
- java.lang.Object
-
- org.glassfish.pfl.basic.tools.argparser.ArgParser
-
public class ArgParser extends java.lang.Object
A general purpose argument parser that uses annotations, reflection, and generics.This class solves a simple problem: How do I write an argument parser that parses arguments of the form
-xxx value
There are many ways of doing this: the ORB has probably at least 6 different ways of attacking this problem (and I wrote half of them). The approach taken here is to start with an interface, annotate it to indicate some information (like default values) for parsing, and then use reflection to produce an implementation of the interface that uses a dynamic proxy.
The argument parsing is entirely specified in terms of an interface with annotated methods. The name of the method is the name used as a -xxx argument when parsing the data. The type of data determines the valid inputs for the argument. All arguments must be in the -xxx yyy form, where yyy is a valid input for the type of method xxx() in the interface.
The interface is very simply: just create an ArgParser of the appropriate type, and then call parse( String[] ) to get an instance of the interface. There is also a getHelpText() method that returns a String with formatted information about all the valid arguments for this parser.
Not all required information can be derived from the method type and name. Some annotations are defined for additional information:
- @DefaultValue. This gives a String that is used to construct a default value for the given method.
- @Separator. This is used to override the default "," separator used for lists of values for fields of type List<Type> or Type[].
- @Help. This is used to give any help text for the field that is displayed by the getHelpText method.
- Support I18N for this (required before it could be used for public parts of the ORB).
- Support Set<Type> Similar to list, but all elements must be unique.
- Support Map<Type,Type>
- Do we want to support really complex types, like Map<String,List<Map<String,Foo>>> How would we specify needed separators?
- Should we extend along the lines of the ORB arg parser, with an embedded language to describe how to parse a String?
- Extend this to support property parsing as well (required for ORB.init parsing). Probably just want to parse Properties as well as String[]. This requires some way to handle the property prefix (as in -ORBInitHost and org.omg.CORBA.ORBInitHost).
- Extend to support abstract classes. Only non-private abstract methods that are read-only beans and are annotated with @DefaultValue can be set by the parser. Other methods are allowed, but not used by the parser.
- Support an @Complete annotation. All such methods must be void(), and are executed in some order after the parsing is complete.
- Add an @Keyword notation to override the default name of the argument (the method name). This allows having different names for the methods and the arguments. Note that this can be used for fully-qualified property names. This supports the ORB dual use of properties and arguments (with perhaps a little fudging for things like ORBInitRef: if there are multiple args, concat them together and use a list separator?)
- Add an @ClassName annotation that takes no arguments. This means that the string passed must be the name of a class that has a no-args constructor. The class must be assignment compatible with the return type of the annotated method. At parse time, we just load the class named by the string and create a new instance of it.
- Consider using apt for this. This solves two problems:
- We can generate a more efficient class than the current version that uses a dynamic proxy and a table lookup. This becomes important when we consider that ORBData may be referenced many times during an invocation.
- We can generate much of the required ORBConstants (say as ORBPropertyNames) directly from the annotated interface.
- Most of the extensions here are aimed at reducing the code size of the ORB data stuff.
- If we don't want to use apt:
- Use codegen to generate a class that implements the argument interface.
- Instead of generating ORBConstants, look things up from it using static
reflection on the name of the constant. This would require some other annotations:
- @IndirectKeyword gives the name of the constant in the @ConstantClass to look for. Do we need multiple constant classes?
- @ConstantClass would give the class to use for looking up public static final constants for @IndirectKeyword, which is like @Keyword.
- @DefaultValue: current method-level takes string
- @Separator: current method-level takes string
- @Help: current method-level takes string
- @ConstantClass: proposed class-level takes class constant
- @Complete: proposed method-level marker
- @Keyword: proposed method-level takes string
- @IndirectKeyword: proposed method-level takes string
- @ClassName: proposed method-level marker
-
-
Nested Class Summary
Nested Classes Modifier and Type Class Description private static class
ArgParser.PrimaryColor
static class
ArgParser.StringPair
Useful utility class for parsing pairs of strings.private static interface
ArgParser.TestInterface1
private static interface
ArgParser.TestInterface2
-
Field Summary
Fields Modifier and Type Field Description private java.util.Map<java.lang.String,java.lang.Object>
defaultValues
private java.util.Map<java.lang.String,java.lang.String>
helpText
private java.util.List<java.lang.Class<?>>
interfaceClasses
private java.util.Map<java.lang.String,ElementParser>
parserData
-
Method Summary
All Methods Static Methods Instance Methods Concrete Methods Modifier and Type Method Description private java.lang.String
checkMethod(java.lang.reflect.Method m)
private java.lang.String
display(java.lang.Object obj)
private void
error(java.lang.String msg)
java.lang.String
getHelpText()
Returns a formatted text string that describes the expected arguments for this parser.private java.lang.String
getKeyword(java.lang.String arg)
private void
init(java.lang.Class<?> cls)
private void
init(java.util.List<java.lang.Class<?>> classes)
private void
internalParse(java.util.Map<java.lang.String,java.lang.String> data, java.util.Map<java.lang.String,java.lang.Object> result)
static void
main(java.lang.String[] args)
private java.util.Map<java.lang.String,java.lang.String>
makeMap(java.lang.String[] args)
private java.lang.Object
makeProxy(java.util.Map<java.lang.String,java.lang.Object> data)
java.lang.Object
parse(java.lang.String[] args)
Parse the argument string into an instance of type T.<T> T
parse(java.lang.String[] args, java.lang.Class<T> cls)
-
-
-
Field Detail
-
interfaceClasses
private final java.util.List<java.lang.Class<?>> interfaceClasses
-
helpText
private final java.util.Map<java.lang.String,java.lang.String> helpText
-
defaultValues
private final java.util.Map<java.lang.String,java.lang.Object> defaultValues
-
parserData
private final java.util.Map<java.lang.String,ElementParser> parserData
-
-
Constructor Detail
-
ArgParser
public ArgParser(java.lang.Class<?> cls)
Construct an ArgParser that parses an argument string into an instance of the Class argument. cls must be an interface. Each method in this interface must take no arguments. Each method must be annotated with a DefaultValue annotation. Each method must return one of the following types:- A primitive type
- A String type
- A type that has a public constructor that takes a single String argument
- An Enum
- A List parameterized by one of the above types
- An array of one of the first four types
-
ArgParser
public ArgParser(java.util.List<java.lang.Class<?>> classes)
-
-
Method Detail
-
init
private void init(java.lang.Class<?> cls)
-
init
private void init(java.util.List<java.lang.Class<?>> classes)
-
display
private java.lang.String display(java.lang.Object obj)
-
getHelpText
public java.lang.String getHelpText()
Returns a formatted text string that describes the expected arguments for this parser.
-
parse
public java.lang.Object parse(java.lang.String[] args)
Parse the argument string into an instance of type T.
-
parse
public <T> T parse(java.lang.String[] args, java.lang.Class<T> cls)
-
error
private void error(java.lang.String msg)
-
checkMethod
private java.lang.String checkMethod(java.lang.reflect.Method m)
-
internalParse
private void internalParse(java.util.Map<java.lang.String,java.lang.String> data, java.util.Map<java.lang.String,java.lang.Object> result)
-
getKeyword
private java.lang.String getKeyword(java.lang.String arg)
-
makeMap
private java.util.Map<java.lang.String,java.lang.String> makeMap(java.lang.String[] args)
-
makeProxy
private java.lang.Object makeProxy(java.util.Map<java.lang.String,java.lang.Object> data)
-
main
public static void main(java.lang.String[] args)
-
-