Index: lutinutil/src/java/org/codelutin/util/OptionParser.java diff -u lutinutil/src/java/org/codelutin/util/OptionParser.java:1.1 lutinutil/src/java/org/codelutin/util/OptionParser.java:1.2 --- lutinutil/src/java/org/codelutin/util/OptionParser.java:1.1 Mon Nov 19 20:50:12 2007 +++ lutinutil/src/java/org/codelutin/util/OptionParser.java Wed Nov 28 01:30:34 2007 @@ -18,21 +18,12 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *##%*/ -/* * -* OptionParser.java -* -* Created: 22 août 2003 -* -* @author Benjamin Poussin -* @version $Revision: 1.1 $ -* -* Mise a jour: $Date: 2007-11-19 20:50:12 $ -* par : $Author: tchemit $ -*/ - package org.codelutin.util; import org.apache.commons.beanutils.ConvertUtils; +import org.apache.commons.logging.LogFactory; +import static org.codelutin.i18n.I18n._; +import static org.codelutin.util.OptionDefinitionParser.LUTINUTIL_PARSERDEF_PRINT_DETAIL_OPTION_HEAD; import java.io.IOException; import java.io.Writer; @@ -45,491 +36,676 @@ import java.util.TreeMap; /** - * Parser d'options de la ligne de commande. + * Parser abstrait d'options de la ligne de commande. + *

+ * Pour implanter le parseur il suffit de surcharger cette classe et d'y ajouter + * l'annotation {@link OptionParserAnnotation} au niveau de la classe, en y + * indiquant la définition du parser (pour le moment uniquement le nom + * complet de l'usine de définitions d'options + * {@link OptionParserAnnotation#factoryName()}). + *

+ * Ensuite on utilise la méthode publique statique {@link #newParser(String, ClassLoader)} + * pour obtenir une nouvelle instance de parser à partir du nom de sa classe et + * de son classLoader. + *

+ * Enfin sur le parseur retourné, on invoque la méthode {@link #doParse(String[])} + * pour effectuer le parsing des arguments passés. * - * @author poussin * @author chemit + * @see OptionParserAnnotation */ -public class OptionParser { - public static OptionParser doParse(String name, - String fullyQualifiedFactoryName, - String... args) - throws OptionParserException { +public abstract class OptionParser { - OptionParser parser; + /** + * Annotation pour définir le paramètrage d'un parseur d'option + * + * @author chemit + */ + @java.lang.annotation.Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME) - parser = new OptionParser(name); + @java.lang.annotation.Target(value = java.lang.annotation.ElementType.TYPE) - Map indexAlias = parser.init(fullyQualifiedFactoryName); - List contexts = null; + public static @interface OptionParserAnnotation { - if (args.length > 0) { + /** @return le nom de l'usine de définitions d'options utilisée */ + public String factoryName(); - // detection des contexts d'options - contexts = parser.detectOptionContexts(indexAlias, args); + /** @return la liste des clef d'options disponibles */ + public String[] keys(); + + } + + public static OptionParser newParser(String className, ClassLoader loader) { + + Class parserClass; + OptionParser parser; + try { + // find parser class + parserClass = Class.forName(className, true, loader); + // instanciate parser, load factory and init ParserContext + parser = (OptionParser) parserClass.newInstance(); + return parser; + } catch (ClassNotFoundException e) { + throw new IllegalArgumentException(e); + } catch (IllegalAccessException e) { + throw new IllegalArgumentException(e); + } catch (InstantiationException e) { + throw new IllegalArgumentException(e); } + } - // verification de la coherence des options détectée par rapport aux - // definitions des options - parser.checkPostParsing(contexts); + static org.apache.commons.logging.Log log = LogFactory.getLog(OptionParser.class); - if (contexts != null && !contexts.isEmpty()) { - for (OptionContext context : contexts) { - parser.instanciateOption(context); - } - // on vide les contextes - contexts.clear(); + /** + * le context du parseur : contient tous les index nécessaires pendant le + * parsing. + */ + protected final ParserContext context; + + public OptionParser() { + + // find the fullyQualifiedFactoryName via OptionParserAnnotation + // annotation + OptionParserAnnotation def = getClass().getAnnotation(OptionParserAnnotation.class); + if (def == null) { + //TODO + throw new IllegalArgumentException("could not found annotation " + OptionParserAnnotation.class + " in Parser " + this); } - return parser; + String fullyQualifiedFactoryName = def.factoryName(); + + String[] keys = def.keys(); + + // obtain instance of factory + OptionDefinitionFactory definitionFactory = + OptionDefinitionFactory.newFactory( + fullyQualifiedFactoryName, + getClass().getClassLoader() + ); + // create parser context + context = new ParserContext(keys, definitionFactory.getDefinitions()); } + public OptionParser doParse(String... args) throws OptionParserException { + if (args.length > 0) { + // detect OptionContexts + prepareContexts(args); + } - /** l'usine de définitions d'options */ - protected OptionDefinitionFactory definitionFactory; + // check coherence on detected OptionContexts TODO Finish me + checkPostParsing(); - /** La liste des arguments qui ne sont pas des options */ - protected List arguments; + if (context.optionContexts != null) { + for (OptionContext optionContext : context.optionContexts) { + // instanciate option from his context + instanciateOption(optionContext); + } + } + //TODO Report errors from context before suppress context - /** le nom à afficher pour l'usage */ - protected String name; + context.clear(); - /** - * la liste des options, la cle est le nom de l'option, et la valeur - * est la liste des options qui ont été lues grâce à celle-ci. - */ - protected Map> options; + // return parser + return this; + } - /** liste a plat de toutes les options lus. */ - protected List

+ * Prépare avec les contexts d'options. * - * @param indexAlias la talbe des alias disponibles - * @param args la liste des arguments à scanner - * @return la liste des contextes d'option trouvée en parcourant toute la - * liste des arguments donnés + * @param args la liste des arguments à scanner */ - protected List detectOptionContexts(Map - indexAlias, String... args) { - List result = new ArrayList(); + protected void prepareContexts(String... args) { - OptionContext context = null; + OptionContext optionContext = null; for (int i = 0; i < args.length; i++) { String argument = args[i]; if (argument.startsWith("-")) { - // an alias detected - OptionDefinition newOption = indexAlias.get(argument); + // alias detected + OptionDefinition newOption = context.indexAlias.get(argument); if (newOption == null) { // unknown option - arguments.add(argument); + context.arguments.add(argument); + //TODO This is a fatal error, no ? + //throw new OptionParserException(); continue; } - if (context != null) { - // close the current context and add it to result - result.add(context); - } - context = new OptionContext(newOption, argument, i); + // close the current optionContexts and add it to result + context.addOptionContext(optionContext); + + + optionContext = new OptionContext(newOption, argument, i); continue; } - if (context == null || !context.addArgument(argument, i)) { - // no option opened or argumnet not accepted - arguments.add(argument); + if (optionContext == null || !optionContext.addArgument(context, argument, i)) { + // no option opened or argument not accepted + context.arguments.add(argument); } } - if (context != null) { - // close the current context and add it to result - result.add(context); - } + // close the current optionContexts and add it to result + context.addOptionContext(optionContext); - return result; } /** * Cette méthode permet d'instancier une option à partir des valeurs - * détectées dans le contexte d'option. + * détectées dans le contexte d'option. + *

+ * Le context a été vérifié auparavant et on sait que l'option et ses + * arguments sont valides. *

* * @param context le contexte d'une option parsée */ - public void instanciateOption(OptionContext context) { - - OptionDefinition definition = context.definition; - - String optionName = definition.getName(); + protected void instanciateOption(OptionContext context) { int position = 0; List arguments = new ArrayList(); - for (OptionArgumentContext entry : context.argumentsFound) { + for (OptionArgumentContext argumentContext : context.argumentsFound) { + // get argument from command line + String argument = argumentContext.getArgument(); - String argument = entry.getArgument(); + // get argument definition + OptionArgumentDefinition argumentDefinition = argumentContext.getDefinition(); - OptionArgumentDefinition optionArgumentDefinition = entry.getDefinition(); + // extract value as String for the argument type + String valueStr = argumentDefinition.getType().extractArgumentValue(argument); - boolean mandatory = optionArgumentDefinition.getPosition() > -1; + // convert to matching type from argument definition value type + Object value = ConvertUtils.convert(valueStr, argumentDefinition.getValueType().getClazz()); - // extract value - String valueStr = optionArgumentDefinition.getType().extractArgumentValue(argument); + // add argument to option + addOptionArgument(position, argument, argumentDefinition, value, arguments); - Object value = ConvertUtils.convert(valueStr, optionArgumentDefinition.getValueType().getClazz()); + if (argumentDefinition.isMandatory()) { + // increments next mandatory position + position++; + } + } + // finally instanciate option and store it + addOption(context, arguments); + } - OptionArgument optionArgument = new OptionArgument(optionArgumentDefinition.key, position, argument, value); - // on sauvegarde l'argument dans l'option - arguments.add(optionArgument); + protected void checkPostParsing() throws OptionParserException { + // build a map from context + Map> map; + map = new HashMap>(); + for (OptionContext optionContext : context.optionContexts) { + OptionDefinition definition = optionContext.definition; + List list = map.get(definition); + if (list == null) { + map.put(definition, list = new ArrayList()); + } + list.add(optionContext); + } - if (mandatory) { - position++; + for (OptionDefinition definition : context.definitions) { + List contextList = map.get(definition); + int min = definition.getMin(); + int max = definition.getMax(); + + if ((contextList == null || contextList.isEmpty()) && min > 0) { + // fatal error mandatory option + throw new OptionParserException(""); + } + + if (min > 0 && (contextList == null || contextList.size() < min)) { + // fatal error required more (min>found) + throw new OptionParserException(""); + } + + if (max > 0 && (contextList != null && contextList.size() > max)) { + // fatal error required less (found>max) + throw new OptionParserException(""); + } + //TODO check each argument option + if (contextList != null) { + for (OptionContext optionContext : contextList) { + checkPostParsingOption(optionContext); + } + } + } + // clear map + map.clear(); + } + + protected void checkPostParsingOption(OptionContext optionContext) { + + int max = context.getMaxMandatories(optionContext.definition); + + for (int i = 0; i < max; i++) { + // check cardinalite + + } + + for (OptionArgumentContext argumentContext : optionContext.argumentsFound) { + // check there is no mandatory argument missing + if (argumentContext == null) { + //TODO } } + } + protected void addOption(OptionContext optionContext, List arguments) { Option option; + OptionKeyFactory.OptionKey key; + + OptionDefinition definition = optionContext.definition; + + key = OptionKeyFactory.getOptionKey(definition.getName()); + if (arguments.isEmpty()) { - option = new Option(definition, context.alias); + option = new Option(key, definition, optionContext.alias); } else { - option = new Option(definition, context.alias, arguments.toArray(new OptionArgument[arguments.size()])); + option = new Option(key, definition, optionContext.alias, + arguments.toArray(new OptionArgument[arguments.size()])); } - List