Monday, October 19, 2009

Parsing command-line options - part 3

So we have defined two interfaces - 1) the interface we want to use to access command-line values, - a real-world example of usage, and 2) the annotation we want to apply to the interface #1 to furnish it with option-specific information. It's time to start writing some code to bind them together. Firstly we'll need some helper methods to aid with JavaBean-related operations. Lets create BeanUtils class where we'll keep those:
public final class BeanUtils
{
   
private static BeanUtils _instance = new BeanUtils();
   
   
private BeanUtils() {
       
PropertyEditorManager.registerEditor(new String[0].getClass(), StringArrayPropertyEditor.class);
        PropertyEditorManager.registerEditor
(new int[0].getClass(), IntegerArrayPropertyEditor.class);
        PropertyEditorManager.registerEditor
(new double[0].getClass(), DoubleArrayPropertyEditor.class);
        PropertyEditorManager.registerEditor
(new boolean[0].getClass(), BooleanArrayPropertyEditor.class);
        PropertyEditorManager.registerEditor
(Character.TYPE, CharacterPropertyEditor.class);
   
}

   
/**
     * Evaluates to
<code>true</code> for any specified property.
     */
   
public static final IPredicate<PropertyDescriptor> AnyProperty = new IPredicate<PropertyDescriptor>() {
       
public final boolean evaluate(PropertyDescriptor property) {
           
return true;
       
}
    }
;

   
/**
     * Evaluates to
<code>true</code> for a simple non-indexed getter.
     */
   
public static final IPredicate<Method> IsSimpleGetter = new IPredicate<Method>() {
       
public final boolean evaluate(Method method) {
           
return method != null && method.getName().startsWith("get") && method.getParameterTypes().length == 0 &&
                method.getReturnType
() != Void.TYPE;
       
}
    }
;

   
/**
     * Evaluates to
<code>true</code> for a simple non-indexed setter.
     */
   
public static final IPredicate<Method> IsSimpleSetter = new IPredicate<Method>() {
       
public final boolean evaluate(Method method) {
           
return method != null && method.getName().startsWith("set") && method.getParameterTypes().length == 1;
       
}
    }
;

   
/**
     * Invokes the specified action for all properties matching the predicate
     * within the given class.
     *
@param type
     */
   
public static void forProperties(Class<?> type, IPredicate<PropertyDescriptor> filter,
        IAction<PropertyDescriptor> action
) throws Exception {
       
Guard.notNull(type, "type");
       
final BeanInfo beanInfo = Introspector.getBeanInfo(type);
       
final PropertyDescriptor[] properties = beanInfo.getPropertyDescriptors();
       
for (PropertyDescriptor property : properties) {
           
if (filter.evaluate(property)) {
               
action.invoke(property);
           
}
        }
    }

   
/**
     * Tries to map the specified type into Java primitive type. Returns the
     * same type back if mapping does not exist.
     *
@param wrapper
     *
@return Non-null type.
     */
   
public static Class<?> tryMapToPrimitiveType(Class<?> wrapper) {
       
if (wrapper == Boolean.class)
           
return Boolean.TYPE;
       
if (wrapper == Byte.class)
           
return Byte.TYPE;
       
if (wrapper == Character.class)
           
return Character.TYPE;
       
if (wrapper == Short.class)
           
return Short.TYPE;
       
if (wrapper == Integer.class)
           
return Integer.TYPE;
       
if (wrapper == Long.class)
           
return Long.TYPE;
       
if (wrapper == Float.class)
           
return Float.TYPE;
       
if (wrapper == Double.class)
           
return Double.TYPE;
       
if (wrapper == Void.class)
           
return Void.TYPE;
       
return wrapper;
   
}

   
/**
     * Converts a string value into an instance of the specified class.
     *
@param <T>
    
* @param valueStr
     *
@param type
     *
@return A new instance of the specified type initialized from the string
     *
@throws IllegalArgumentException if conversion is not possible
     */
   
@SuppressWarnings("unchecked")
   
public static <T> T convert(String valueStr, Class<T> type) {
       
Guard.notNull(valueStr, "valueStr");
        Guard.notNull
(type, "type");
       
final Class<?> primitiveType = BeanUtils.tryMapToPrimitiveType(type);
       
final PropertyEditor editor = PropertyEditorManager.findEditor(primitiveType);
        Guard.notNull
(editor, "conversion not supported to type " + type.getSimpleName() + " from '" + valueStr + "'");
        editor.setAsText
(valueStr);
       
final Object value = editor.getValue();
       
return (T) value;
   
}
}
From the code above it can be seen that for conversion of a string value to a specific Java type we rely on the existing infrastructure of JavaBean property editors. Support for other types can be easily added by registering custom implementations with the PropertyEditorManager.registerEditor() call.
You can also see a few methods to facilitate enumeration over bean properties where we occasionally need to distinguish between setters and getters.

No comments:

Post a Comment