package org.kohsuke.args4j; import java.lang.reflect.AccessibleObject; import java.net.URL; import org.kohsuke.args4j.spi.ArgumentImpl; import org.kohsuke.args4j.spi.ConfigElement; import org.kohsuke.args4j.spi.OptionImpl; import org.kohsuke.args4j.spi.Setters; import org.xml.sax.InputSource; /** * Parses an XML-file specifying the 'annotations'. * The XML must have the structure: * <pre> * <args> * <option field="" method="" name="" usage="" metavar="" handler=""/> * <argument field="" method="" usage="" metavar="" handler=""/> * </args> * </pre> * Exactly one of the attributes 'field' or 'method' must be set. * The 'handler' value specifies a full qualified class name. * * <h3>Example</h3> * <pre> * <args> * <option field="recursive" name="-r" usage="recursively run something"/> * <option field="out" name="-o" usage="output to this file" metavar="OUTPUT"/> * <option method="setStr(String)" name="-str"/> * <option field="data" name="-custom" handler="org.kohsuke.args4j.spi.BooleanOptionHandler" usage="boolean value for checking the custom handler"/> * <argument field="arguments"/> * <args> * </pre> * * @author Jan Materne */ public class XmlParser { public void parse(URL xml, CmdLineParser parser, Object bean) { parse(new InputSource(xml.toExternalForm()),parser,bean); } public void parse(InputSource xml, CmdLineParser parser, Object bean) { try { Config config = Config.parse(xml); for(ConfigElement ce : config.options) { Option option = new OptionImpl(ce); parser.addOption(Setters.create(parser, findMethodOrField(bean, ce.field, ce.method),bean), option); } for (ConfigElement ce : config.arguments) { Argument argument = new ArgumentImpl(ce); parser.addArgument(Setters.create(parser, findMethodOrField(bean, ce.field, ce.method),bean), argument); } } catch (Exception e) { throw new RuntimeException(Messages.METADATA_ERROR.format(), e); } } /** * Finds a {@link java.lang.reflect.Method} or {@link java.lang.reflect.Method} in the bean * instance with the requested name. * @param bean bean instance * @param field name of the field (field XOR method must be specified) * @param method name of the method (field XOR method must be specified) * @return the reflection reference * @throws SecurityException * @throws NoSuchFieldException * @throws NoSuchMethodException * @throws ClassNotFoundException */ private AccessibleObject findMethodOrField(Object bean, String field, String method) throws SecurityException, NoSuchFieldException, NoSuchMethodException, ClassNotFoundException { AccessibleObject rv; if (field != null) { rv = bean.getClass().getDeclaredField(field); } else { String methodName = method.substring(0, method.indexOf("(")); String[] params = method.substring(method.indexOf("(")+1, method.indexOf(")")).split(","); Class[] paramTypes = new Class[params.length]; for(int i=0; i<params.length; i++) { String className = params[i]; if (className.indexOf('.') < 0) { className = "java.lang." + className; } paramTypes[i] = Class.forName(className); } rv = bean.getClass().getMethod(methodName, paramTypes); } return rv; } }