Index: lutinutil/src/java/org/codelutin/util/OptionDefinitionParser.java diff -u lutinutil/src/java/org/codelutin/util/OptionDefinitionParser.java:1.6 lutinutil/src/java/org/codelutin/util/OptionDefinitionParser.java:1.7 --- lutinutil/src/java/org/codelutin/util/OptionDefinitionParser.java:1.6 Thu Nov 29 23:39:31 2007 +++ lutinutil/src/java/org/codelutin/util/OptionDefinitionParser.java Sun Dec 2 07:16:39 2007 @@ -24,17 +24,20 @@ * Created: 22 août 2003 * * @author Benjamin Poussin -* @version $Revision: 1.6 $ +* @version $Revision: 1.7 $ * -* Mise a jour: $Date: 2007-11-29 23:39:31 $ +* Mise a jour: $Date: 2007-12-02 07:16:39 $ * par : $Author: tchemit $ */ package org.codelutin.util; import static org.codelutin.i18n.I18n._; +import org.codelutin.log.LutinLog; +import org.codelutin.log.LutinLogFactory; import static org.codelutin.util.OptionArgumentValueType._newfile; import static org.codelutin.util.OptionArgumentValueType._string; +import org.codelutin.util.OptionParserAnnotationHelper.OptionParserA; import java.io.BufferedInputStream; import java.io.File; @@ -47,9 +50,7 @@ import java.util.Collection; import java.util.Collections; import java.util.List; -import java.util.Map; import java.util.Properties; -import java.util.TreeMap; /** * Parser de définition d'options de la ligne de commande. @@ -60,12 +61,12 @@ *

* La classe contient une méthode statique publique : *

- * {@link #doParse(File)} pour lancer un parsing sur le fichier de définitions + * {@link #doParse(org.codelutin.util.OptionDefinitionParser.TypeSource,Object)} pour lancer un parsing sur le fichier de définitions * donné. *

*

*

- * Dans ce fichier chaque option est représentée par deux lignes : + * Dans ce fichier chaque option est représentée par deux entrées : *

* optionName.definition={0,2} --option|-o * [groupArgumentFacultatif] @@ -77,112 +78,199 @@ public class OptionDefinitionParser { - private static final String DEFINITION_KEY_SUFFIX = ".definition"; - - private static final String DESCRIPTION_KEY_SUFFIX = ".description"; + /** + * permet de définir le type de source pour lire les définitions à analyser. + *

+ * Pour le moment seul le mode properties est implanté. + * + * @author chemit + */ + public enum TypeSource { + /** pour lire à partir d'un fichier de propriétés (mode par défaut). */ + properties, + /** pour lire à partir d'un fichier xml (pas implanter). */ + xml, + /** pour lire depuis un InputReader (pas implanter) */ + console + } - private static final int DEFINITION_KEY_PREFIX_LENGTH = - DEFINITION_KEY_SUFFIX.length(); + protected static final LutinLog log = LutinLogFactory.getLutinLog(OptionDefinitionParser.class); private static final Character[] openingChars = {'(', '{', '[', '<'}; private static final Character[] closingChars = {')', '}', ']', '>'}; - protected static final String LUTINUTIL_PARSERDEF_PRINT_DETAIL_OPTION_HEAD = - "lutinutil.parserdef.printDetail.option.head"; - /** * Effectue la parsing d'un fichier contenant les définitions des options. *

- * Retourne le parseur utilisé pour le parsing avec les définitions d'options - * validées {@link #getDefinitions()} et les non validées - * {@link #getUnvalidDefinitions()} pour les non validées. + * Retourne le parseur utilisé après le parsing. + *

+ * A ce stade, aunce definition ou annotation n'as été crée, on connait + * uniquement les contexts d'options valides. + *

+ * Pour obtenir la liste des définitions valides : {@link #toDefinitions()} + *

+ * Pour obtenir l'annotation OptionParserA de définition du parseur : + * {@link #toAnnotation()} * - * @param propertiesFilePath le fichier contenant les définitions des - * options + * @param type le type de source à traiter. + * @param source la source contenant les définitions des options * @return le parseur après parsing du fichier - * @throws IOException si erreur pendant la lecture du fichier */ - public static OptionDefinitionParser doParse(File propertiesFilePath) - throws IOException { - - OptionDefinitionParser parser; - - parser = new OptionDefinitionParser(propertiesFilePath); - - parser.parse(); - + public static OptionDefinitionParser doParse(TypeSource type, Object source) { + // instanciate parser + OptionDefinitionParser parser = new OptionDefinitionParser(); + // init parser context reading from source + ParserContext context; + switch (type) { + case properties: + context = parser.initProperties((File) source); + break; + case console: + case xml: + throw new IllegalArgumentException(_("lutinutil.parserdef.notimplemented.sourceType", type, parser)); + default: + throw new IllegalArgumentException(_("lutinutil.parserdef.unknown.sourceType", type, parser, Arrays.toString(TypeSource.values()))); + } + // parse and build option contexts + for (int i = 0, j = context.keys.length; i < j; i++) { + String key = context.keys[i]; + String definition = context.definitions[i]; + String description = context.descriptions[i]; + int nbErrors = parser.errors.size(); + if (!context.preOptionCheck(key, definition, description) && nbErrors < parser.errors.size()) { + // we don't treate this option, keep a fake option definition + // for error reporting + parser.unvalidDefinitions.add(new SyntaxUnvalidOptionDefinition(key, description, definition)); + continue; + } + OptionContext optionContext = context.parseOption(key, definition, description); + context.addSon(optionContext); + } + // keep valid option's contexts + parser.safeContexts = context.contexts.toArray(new OptionContext[context.contexts.size()]); + // clean context + context.clear(); return parser; } - /** lists of definitions found while parsing */ - protected List definitions; + /** list of safe contexts available after parsing */ + protected OptionContext[] safeContexts; /** lists of unvalid definitions found while parsing */ protected List unvalidDefinitions; - /** le path du fichier qui contient les définitions d'options */ - protected File propertiesFile; - - /** list of exceptions found while parsing */ + /** list of errors found while parsing */ protected List errors; - /** collection of definition names found in properties file */ - protected List definitionNames; - - protected List argumentKeyUsed; + /** @return la liste des définitions marqués comme invalides. */ + public OptionDefinition[] getUnvalidDefinitions() { + return unvalidDefinitions.toArray(new OptionDefinition[unvalidDefinitions.size()]); + } - public OptionDefinition[] getDefinitions() { - return definitions.toArray(new OptionDefinition[definitions.size()]); + /** + * @return la liste des définitions valides. + * @deprecated on ne devrait pas utiliser cette méthode : il n'y a aucune + * raison d'attaquer les définitions à partir d'ici : c'est la + * responsabilité du parseur d'options... + */ + public OptionDefinition[] toDefinitions() { + if (hasFailed()) { + return new OptionDefinition[0]; + } + OptionDefinition[] result; + result = OptionParserUtil.ToDefinitionWalker.getInstance().run(safeContexts); + return result; } - public OptionDefinition[] getUnvalidDefinitions() { - return unvalidDefinitions.toArray(new OptionDefinition[unvalidDefinitions.size()]); + /** + * @return l'annotation de définition du parseur d'options à partir des + * options reconnues comme valides. + */ + public OptionParserA toAnnotation() { + if (hasFailed()) { + return null; + } + OptionParserA result; + result = OptionParserUtil.ToAnnotationWalker.getInstance().run(safeContexts); + return result; } + /** + * @return true si des erreurs ont été détectés pendant l'analyse + * des définitions d'options. + */ public boolean hasFailed() { return errors != null && !errors.isEmpty(); } + /** + * @return la liste des erreurs rencontrées pendant l'analyse des définitions + * d'options. + */ public List getErrors() { return errors; } - public void printErrors(Writer writer) throws IOException { + /** + * Imprime dans un writer les erreurs rencontrées pendant l'analyse. + * + * @param w le writer à utiliser + * @throws IOException si problèmes d'écriture dans le writer + */ + public void printErrors(Writer w) throws IOException { if (!hasFailed()) { return; } - writer.append(_("lutinutil.parserdef.printError.head", errors.size())); + w.append(_("lutinutil.parserdef.printError.head", errors.size())); for (int i = 0, j = errors.size(); i < j; i++) { - writer.append(_("lutinutil.parserdef.printError.error", - i + 1, j, errors.get(i))); + w.append(_("lutinutil.parserdef.printError.error", i + 1, j, errors.get(i))); } } - public void printDetails(Writer writer) throws IOException { - int size = definitions.size(); - writer.append(_("lutinutil.parserdef.printDetail.parser.head", this, String.valueOf(size))); - for (int i = 0; i < size; i++) { - OptionDefinition definition = definitions.get(i); - //String csq = _("lutinutil.parserdef.printDetail.option.head",String.valueOf(i + 1), String.valueOf(size)); - writer.append("\noption ").append(String.valueOf(i + 1)).append(" /").append(String.valueOf(size)).append(" : \'").append(definition.toString()).append('\''); - definition.printDetail(writer); - } + protected OptionDefinitionParser() { + this.errors = new ArrayList(); + this.unvalidDefinitions = new ArrayList(); } - protected OptionDefinitionParser(File propertiesFile) { - this.propertiesFile = propertiesFile; + protected boolean checkCardinalite(int min, int max, String definition, String description) { + if (min < 0) { + addError(_("lutinutil.parser.min.can.not.be.negative", min, definition, description), null); + return false; + } + if (max == 0) { + addError(_("lutinutil.parser.max.can.not.be.zero", max, definition, description), null); + return false; + } + if (max < -1) { // on vérifie que la cardinalité max >-2 + addError(_("lutinutil.parser.max.too.low", max, definition, description), null); + return false; + } + if (max > -1 && max < min) { // on vérifie que la cardinalité max==-1 || max >= min + addError(_("lutinutil.parser.max.lowest.than.min", max, min, definition, description), null); + return false; + } + return true; } - protected Map init() { + /** + * Initialise un contexte de parseur à partir d'un fichier de propriétés. + * + * @param source la source où lire les définitions (un fichier de propriétés) + * @return un contexte de parseur avec les définitions lues + */ + protected ParserContext initProperties(File source) { + + final String DEFINITION_KEY_SUFFIX = ".definition"; + final String DESCRIPTION_KEY_SUFFIX = ".description"; + final int DEFINITION_KEY_PREFIX_LENGTH = DEFINITION_KEY_SUFFIX.length(); // load properties file Properties properties = new Properties(); InputStream stream = null; try { - try { - stream = new BufferedInputStream(new FileInputStream(propertiesFile)); + stream = new BufferedInputStream(new FileInputStream(source)); properties.load(stream); } finally { stream.close(); @@ -190,581 +278,477 @@ } catch (IOException e) { throw new RuntimeException( - _("lutinutil.error.could.not.load.properties.file", - propertiesFile, e.getMessage()), e); + _("lutinutil.error.could.not.load.properties.file", source, e.getMessage()), e); } - this.errors = new ArrayList(); - this.definitions = new ArrayList(); - this.unvalidDefinitions = new ArrayList(); - this.argumentKeyUsed = new ArrayList(); - - Map result = new TreeMap(); - this.definitionNames = new ArrayList(); + // récupération des clefs d'options + List keys = new ArrayList(); for (Object o : properties.keySet()) { String key = o.toString(); if (!key.endsWith(DEFINITION_KEY_SUFFIX)) { continue; } String s = key.substring(0, key.length() - DEFINITION_KEY_PREFIX_LENGTH); - definitionNames.add(s); - result.put(key, properties.getProperty(key)); - String descriptionKey = s + DESCRIPTION_KEY_SUFFIX; - result.put(descriptionKey, properties.getProperty(descriptionKey)); + keys.add(s); } - Collections.sort(definitionNames); - return result; + // on trie les clefs d'options une seule fois + // ensuite on travaille sur cet ordre établi. + Collections.sort(keys); + + List definitions = new ArrayList(); + List descriptions = new ArrayList(); + + // récupérations des définitions et descriptions des options + for (String optionKey : keys) { + definitions.add(properties.getProperty(optionKey + DEFINITION_KEY_SUFFIX)); + descriptions.add(properties.getProperty(optionKey + DESCRIPTION_KEY_SUFFIX)); + } + + // construction du context de parsing + ParserContext context = new ParserContext( + keys.toArray(new String[keys.size()]), + definitions.toArray(new String[keys.size()]), + descriptions.toArray(new String[keys.size()]) + ); + keys.clear(); + definitions.clear(); + descriptions.clear(); + properties.clear(); + return context; } - protected void parse() throws IOException { + protected void addError(String content, Exception e) { + OptionDefinitionParserException e1; + if (e == null) { + e1 = new OptionDefinitionParserException(content); + } else { + e1 = new OptionDefinitionParserException(content, e); + } + errors.add(e1); + } - Map properties = init(); + /** + * Cette classe contient les index utilisés pendant le parsing. + *

+ * Elle est de visibilité package, car elle n'a pas à être utilisée ailleurs. + * + * @author chemit + */ + class ParserContext extends OptionParserUtil.ParserContext { - try { + protected final String[] keys; - for (String definitionName : definitionNames) { + protected final String[] definitions; + protected final String[] descriptions; - String definitionStr = properties.get(definitionName + DEFINITION_KEY_SUFFIX); - String description = properties.get(definitionName + DESCRIPTION_KEY_SUFFIX); + /** la liste des clefs d'arguments déjà rencontrées pendant le parsing */ + protected final List argumentKeyUsed; + /** la liste des clefs d'option déjà rencontrées pendant le parsing */ + protected final List optionKeyUsed; + /** la liste des alias d'option déjà rencontrées pendant le parsing */ + protected final List aliaKeyUsed; + + ParserContext(String[] keys, String[] definitions, String[] descriptions) { + super(null, true); + this.keys = keys; + this.definitions = definitions; + this.descriptions = descriptions; + this.argumentKeyUsed = new ArrayList(); + this.optionKeyUsed = new ArrayList(); + this.aliaKeyUsed = new ArrayList(); + } + + OptionContext parseOption(String key, String definition, String description) { + log.info('[' + key + "][" + definition + ']'); + + String[] strings = StringUtil.split(openingChars, closingChars, definition, " "); + List tokens = new ArrayList(Arrays.asList(strings)); + + // by default, option is optional + int min = 0, max = 1; + String token = tokens.get(0).trim(); + // alias + String[] alias = token.split("\\|"); + tokens.remove(0); - if (!preOptionParsingCheck(definitionStr, description)) { - // we don't treate this one, add a SyntaxUnvalidOptionDefinition definition - unvalidDefinitions.add(new SyntaxUnvalidOptionDefinition(description, definitionStr)); - continue; + if (!tokens.isEmpty()) { + token = tokens.get(0).trim(); + if (token.startsWith("{") || token.startsWith("+") || token.startsWith("*")) { + Object[] tmp = StringUtil.parseCardinalite(token, false); + min = (Integer) tmp[1]; + max = (Integer) tmp[2]; + tokens.remove(0); } - int nbErrors = errors.size(); + } - // build option definition - OptionDefinition definition = parseDefinition(definitionName, description, definitionStr); + if (!preOptionCheck2(alias, definition, description, min, max)) { + return null; + } + OptionContext context; + context = new OptionContext(this, key, description, min, max, alias); - if (nbErrors < errors.size() || definition instanceof SyntaxUnvalidOptionDefinition || !postOptionParsingCheck(definition, definitions)) { - // don't add the unvalid option - unvalidDefinitions.add(definition); - } else { - definitions.add(definition); + if (!tokens.isEmpty()) { + // l'option possede des arguments + for (String groupArgument : tokens) { + OptionGroupArgumentContext group = context.parseGroup(groupArgument); + context.addSon(group); } } - } finally { - if (properties != null) { - properties.clear(); + return context; + } + + protected void postAddSonHook(OptionContext context) { + aliaKeyUsed.addAll(Arrays.asList(context.alias)); + optionKeyUsed.add(context.key); + argumentKeyUsed.clear(); + } + + /** + * Examine une définition d'option avant parsing. + * + * @param key la clef de l'option à vérifier + * @param definition la définition de l'option à parser + * @param description la descrption de l'option + * @return valid value after check + */ + boolean preOptionCheck(String key, String definition, String description) { + // check definition is not empty + if (definition == null || definition.isEmpty() || definition.trim().isEmpty()) { + addError(_("lutinutil.parserdef.unvalid.syntax.empty.option.definition", definition, description), null); + unvalidate(); + return false; + } + // check description is not empty + if (description == null || description.isEmpty() || description.trim().isEmpty()) { + addError(_("lutinutil.parserdef.unvalid.syntax.empty.option.description", definition, description), null); + unvalidate(); + } + // check definition contains alias ? + if (definition.trim().indexOf('-') != 0) { + addError(_("lutinutil.parserdef.unvalid.syntax.unfound.alias.in.option", definition, description), null); + unvalidate(); + } + // on vérifier que le nom n'est pas dupliqué + if (optionKeyUsed.contains(key)) { + addError(_("lutinutil.parserdef.duplicated.option.name", key, definition), null); + unvalidate(); + } + // check same number {[< to >]} in definition + if (!StringUtil.checkEnclosure(definition, '{', '}')) { + addError(_("lutinutil.parserdef.unvalid.syntax.underbrace.option", definition, description), null); + unvalidate(); + } + if (!StringUtil.checkEnclosure(definition, '<', '>')) { + addError(_("lutinutil.parserdef.unvalid.syntax.lesser.option", definition, description), null); + unvalidate(); } + if (!StringUtil.checkEnclosure(definition, '[', ']')) { + addError(_("lutinutil.parserdef.unvalid.syntax.caret.option", definition, description), null); + unvalidate(); + } + return valid; } - } - protected OptionDefinition parseDefinition(String definitionName, String description, String definition) { + boolean preOptionCheck2(String[] alias, String definition, String description, int min, int max) { - argumentKeyUsed.clear(); + if (alias.length > 4) { + addError(_("lutinutil.parserdef.too.much.alias.option", Arrays.toString(alias), definition), null); + unvalidate(); + } - // assume when arrives here, that the gloabl option definition is fine : - // says : definition is not empty nor null - // says : definition has a first valid alias + // on vérifier que les alias commencent tous par '-' et ne sont + // pas déjà utilisés + for (String alia : alias) { + if (!alia.startsWith("-")) { + addError(_("lutinutil.parserdef.unvalid.syntax.alias.option", alia, definition), null); + unvalidate(); + } + if (aliaKeyUsed.contains(alia)) { + addError(_("lutinutil.parserdef.duplicated.option.alias", alia, definition), null); + unvalidate(); + } + } + if (!checkCardinalite(min, max, definition, description)) { + unvalidate(); + } + return valid; + } - List tokens; + void clear() { + super.clear(); + optionKeyUsed.clear(); + argumentKeyUsed.clear(); + aliaKeyUsed.clear(); + } + } - tokens = new ArrayList(Arrays.asList(StringUtil.split(openingChars, closingChars, definition, " "))); + /** + * Cette classe contient une option à analyser. + *

+ * Elle est de visibilité package, car elle n'a pas à être utilisée ailleurs. + * + * @author chemit + */ + class OptionContext extends OptionParserUtil.ParserContext { + protected final String description; + protected final String key; + protected final int min; + protected final int max; + protected final String[] alias; - // by default, option is mandatory - int min = 0; - int max = 1; - Collection argumentDefinitions = new ArrayList(); - boolean safe = true; - String token; + protected Class impl = Option.class; + protected int currentPos = 0; - token = tokens.get(0).trim(); + OptionContext(ParserContext parent, String key, String description, + int min, int max, String[] alias) { + super(parent, true); + this.description = description; + this.key = key; + this.min = min; + this.max = max; + this.alias = alias; + } - // alias - String[] alias = token.split("\\|"); - tokens.remove(0); + OptionGroupArgumentContext parseGroup(String groupDefinition) { + log.info('[' + groupDefinition + ']'); - if (!tokens.isEmpty()) { + // on supprime tout espace en début ou fin de chaine de définition + groupDefinition = groupDefinition.trim(); - token = tokens.get(0).trim(); + if (!preGroupCheck(groupDefinition)) { + // check failed + return null; + } - if (token.startsWith("{") || token.startsWith("+") || token.startsWith("*")) { + OptionGroupArgumentContext groupContext; + boolean mandatory = groupDefinition.startsWith("<"); - // option cardinalité (non obligatoire, par défaut {0,1}) + // on supprime les délimitateurs de début et fin de chaine de définition + groupDefinition = groupDefinition.substring(1, groupDefinition.length() - 1); - Object[] tmp = parseCardinalite(token, false); + groupContext = new OptionGroupArgumentContext(this, mandatory ? currentPos : -1); - min = (Integer) tmp[1]; - max = (Integer) tmp[2]; + // on split pour obtenir la définition de chaque argument + String[] argumentDefinitions = groupDefinition.split("\\|"); - tokens.remove(0); - } + for (String argumentDefinition : argumentDefinitions) { - if (!tokens.isEmpty()) { - // l'option possède des arguments + OptionArgumentContext argumentContext = groupContext.parseArgument(argumentDefinition); - // on split tout d'abord les espaces pour obtenir les groupement - // d'arguments - int position = 0; - // les definitions d"arguments déjàé trouvées dans le groupement - Collection groupArgumentDefinitions; - for (String groupArgument : tokens.toArray(new String[tokens.size()])) { - - groupArgumentDefinitions = new ArrayList(); - - if (groupArgument.startsWith("<")) { - // il s'agit d'un groupe d'arguments obligatoires - if (!parseOptionGroupArguments(groupArgument, - position, groupArgumentDefinitions)) { - safe = false; - break; - } - argumentDefinitions.addAll(groupArgumentDefinitions); - position++; - continue; - } - if (groupArgument.startsWith("[")) { - // il s'agit d'un groupe d'arguments facultatifs - if (!parseOptionGroupArguments(groupArgument, - -1, groupArgumentDefinitions)) { - safe = false; - break; - } - argumentDefinitions.addAll(groupArgumentDefinitions); - continue; - } - addError(_("lutinutil.parserdef.unvalid.syntax.unknown.group.of.arguments", - groupArgument)); + groupContext.addSon(argumentContext); + + if (!groupContext.valid) { + break; } } + return groupContext; } - // construction de la définition complête de l'option - return safe ? new OptionDefinition( - definitionName, description, definition, - min, max, alias, - argumentDefinitions.toArray(new OptionArgumentDefinition[argumentDefinitions.size()]) - ) : new SyntaxUnvalidOptionDefinition(description, definition); + boolean preGroupCheck(String definition) { + definition = definition.trim(); + if (!(definition.startsWith("<") || definition.startsWith("[")) || + !(definition.endsWith(">") || definition.endsWith("]"))) { + addError(_("lutinutil.parserdef.unvalid.syntax.unknown.group.of.arguments", definition), null); + unvalidate(); + } + return valid; + } } /** - * Permet de construire un dictionnaire d'OptionArgument utilisables pour - * la définition d'un argument d'une option donné. + * Cette classe contient un groupe d'arguments d'une option à analyser. *

- * La définition d'un argument d'une option respecte la syntaxe suivante : - * - *

  • - * <argument_0|argument_1|argument_2|...|argument_n - * > - * pour un argument obligatoire - *
  • - *
  • - * [argument_0|argument_1|argument_2|...|argument_n] - * - * pour un argument facultatif. - *
  • - * - * Chaque argument_i peut être de la forme : - * - *

    - * On ne traite pas ici du caractère optional ou obligatoire de - * l'argument de l'option - * et on suppose de plus que la définition commence et termine bien - * par un délimitateur accepté + * Elle est de visibilité package, car elle n'a pas à être utilisée ailleurs. * - * @param groupDefinition la définition de l'argument de l'option - * @param position la position du prochain argument - * obligatoire - * @param definitionsFound la liste des arguments detectes dans ce - * groupe - * @return true si le parsing s'est bien déroulé, - * false autrement. + * @author chemit */ - protected boolean parseOptionGroupArguments( - String groupDefinition, int position, - Collection definitionsFound) { - - boolean result = true; - - boolean mandatory = position > -1; + class OptionGroupArgumentContext extends OptionParserUtil.ParserContext { - // la liste des type déjà enregistrés - Collection valueTypesFound = - new ArrayList(); + protected final int min; + protected final int max; + protected final int pos; - // on supprime tout espace en début ou fin de chaine de définition - groupDefinition = groupDefinition.trim(); + /** la liste des type valués déjà rencontrés dans le groupe */ + protected final Collection valueTypesFound; - // on supprime les délimitateurs de début et fin de chaine de définition - groupDefinition = groupDefinition.substring(1, groupDefinition.length() - 1); + OptionGroupArgumentContext(OptionContext parent, int pos) { + super(parent, true); + this.pos = pos; + this.min = pos == -1 ? 0 : 1; + this.max = 1; + this.valueTypesFound = new ArrayList(); + } - // on split pour obtenir la définition de chaque argument - String[] argumentDefinitions = groupDefinition.split("\\|"); + protected void postAddSonHook(OptionArgumentContext context) { + if (context.type == OptionArgumentType.valued) { + // on sauvegarde le type (pour les conflits possibles) + valueTypesFound.add(context.valueType); + } + // keep the key used + parent.parent.argumentKeyUsed.add(context.key); + } - for (String argumentDefinition : argumentDefinitions) { + OptionArgumentContext parseArgument(String argumentDefinition) { + log.info('[' + argumentDefinition + ']'); + OptionArgumentContext result; OptionArgumentType type = OptionArgumentType.findType(argumentDefinition); //contient la clef et le type String[] keyAndType = type.explodeDefinition(argumentDefinition); - //contient le type (String), min(Integer), repetittionMax(Integer) + //contient le type (String), min(Integer), max(Integer) Object[] typeAndMinAndMax; + boolean mandatory = pos > -1; + if (type == OptionArgumentType.constant) { // la clef contient la cardinalite - typeAndMinAndMax = parseCardinalite(keyAndType[0], mandatory); + typeAndMinAndMax = StringUtil.parseCardinalite(keyAndType[0], mandatory); // on recupère la clef nettoye keyAndType[0] = (String) typeAndMinAndMax[0]; // on pousse le type connu typeAndMinAndMax[0] = keyAndType[1].substring(1); } else { - typeAndMinAndMax = parseCardinalite(keyAndType[1], mandatory); + typeAndMinAndMax = StringUtil.parseCardinalite(keyAndType[1], mandatory); } // recherche de la classe d'implantation - OptionArgumentValueType valueType = - OptionArgumentValueType.findTypeFromDefinition( - (String) typeAndMinAndMax[0]); + OptionArgumentValueType valueType; + valueType = OptionArgumentValueType.findTypeFromDefinition((String) typeAndMinAndMax[0]); // check coherence of the new required argument definition - if (!checkOptionArgument( + if (!preArgumentCheck( argumentDefinition, - groupDefinition, + toString(), type, valueType, keyAndType[0], - valueTypesFound, - definitionsFound, (Integer) typeAndMinAndMax[1], (Integer) typeAndMinAndMax[2], mandatory)) { - result = false; - continue; - } - - if (type == OptionArgumentType.valued) { - // on sauvegarde le type (pour les conflits possibles) - valueTypesFound.add(valueType); + return null; } - // instanciate the new argument definition - OptionArgumentDefinition definition = new OptionArgumentDefinition( + result = new OptionArgumentContext(this, type, valueType, keyAndType[0], (Integer) typeAndMinAndMax[1], - (Integer) typeAndMinAndMax[2], - position + (Integer) typeAndMinAndMax[2] ); - // keep the key used - argumentKeyUsed.add(definition.getKey()); - - // register the new argument definition - definitionsFound.add(definition); + return result; } - return result; - } - /** - * @param key la valeur dont on cherche la cardinalité - * @param mandatory flag to say if this is a mandatory element or not. - * @return un tableau contenant 3 object : le texte donné sans les - * informations de cardinalité, la répétitionMin, la répétitionMax. - */ - protected Object[] parseCardinalite(String key, boolean mandatory) { - Object[] result = new Object[3]; - int repetitionMin; - int repetitionMax; - String cleanKey; - - if (key.endsWith("+")) { - // cardinalité + = {1,-1} - repetitionMin = 1; - repetitionMax = -1; - cleanKey = key.substring(0, key.length() - 1); - } else { - if (key.endsWith("*")) { - // cardinalité * = {0,-1} - repetitionMin = 0; - repetitionMax = -1; - cleanKey = key.substring(0, key.length() - 1); + boolean preArgumentCheck(String definition, + String groupementDefinition, + OptionArgumentType type, + OptionArgumentValueType valueType, + String key, + int min, + int max, + boolean mandatory) { + + if (type == null) { + addError(_("lutinutil.parserdef.unvalid.syntax.unknown.argument.type", definition, groupementDefinition), null); + unvalidate(); + } + if (valueType == null) { + addError(_("lutinutil.parserdef.unvalid.syntax.unknown.value.type", definition, groupementDefinition), null); + unvalidate(); + } + if (key == null) { + addError(_("lutinutil.parserdef.unfound.key", definition, groupementDefinition), null); + unvalidate(); + } + if (!checkCardinalite(min, max, definition, groupementDefinition)) { + unvalidate(); + } + if (parent.parent.argumentKeyUsed.contains(key)) { + addError(_("lutinutil.parserdef.duplicated.argument.key", key, definition, groupementDefinition), null); + unvalidate(); + } + // un argument de type constant ne peut pas avoir de cardinalite autre que {1,1} + if (type == OptionArgumentType.constant) { + if (mandatory && !(min == 1 && max == 1)) { + addError(_("lutinutil.parserdef.const.argument.mandatory.cardinalite", min, max, definition, groupementDefinition), null); + unvalidate(); + } + if (!mandatory && !(min == 0 && max == 1)) { + addError(_("lutinutil.parserdef.const.argument.optional.cardinalite", min, max, definition, groupementDefinition), null); + unvalidate(); + } } else { - - if (key.endsWith("}")) { - // cardinalité spécifiée - int index = key.indexOf("{"); - cleanKey = key.substring(0, index); - String tmp = key.substring(index + 1, key.length() - 1); - int comaIndex = tmp.indexOf(','); - if (comaIndex == -1) { - // cardinalité unique - repetitionMax = repetitionMin = Integer.valueOf(tmp); - } else { - repetitionMin = Integer.valueOf(tmp.substring(0, comaIndex)); - repetitionMax = Integer.valueOf(tmp.substring(comaIndex + 1)); - } - } else { - // pas de cardinalité - // rien à faire (on prend les valeurs par defaut) - cleanKey = key; - if (mandatory) { - repetitionMin = 1; - repetitionMax = 1; - } else { - repetitionMin = 0; - repetitionMax = 1; - } + if (mandatory && min < 1) { + addError(_("lutinutil.parserdef.argument.mandatory.cardinalite", min, max, definition, groupementDefinition), null); + unvalidate(); } - } - } - result[0] = cleanKey.trim().toLowerCase(); - result[1] = repetitionMin; - result[2] = repetitionMax; - return result; - } - - /** - * Examine une définition d'option avant parsing. - * - * @param definition la définition de l'option à parser - * @param description la descrption de l'option - * @return true si la définition est syntaxiquement valide - */ - protected boolean preOptionParsingCheck(String definition, String description) { - // check definition is not empty - if (definition == null || definition.isEmpty() || definition.trim().isEmpty()) { - addError(_("lutinutil.parserdef.unvalid.syntax.empty.option.definition", - definition, description)); - return false; - } - // check description is not empty - if (description == null || description.isEmpty() || description.trim().isEmpty()) { - addError(_("lutinutil.parserdef.unvalid.syntax.empty.option.description", - definition, description)); - return false; - } - // check defintion contains alias ? - if (definition.trim().indexOf('-') != 0) { - addError(_("lutinutil.parserdef.unvalid.syntax.unfound.alias.in.option", - definition, description)); - return false; - } - // check same number {[< to >]} in definition - if (!StringUtil.checkEnclosure(definition, '{', '}')) { - addError(_("lutinutil.parserdef.unvalid.syntax.underbrace.option", - definition, description)); - return false; - } - if (!StringUtil.checkEnclosure(definition, '<', '>')) { - addError(_("lutinutil.parserdef.unvalid.syntax.lesser.option", - definition, description)); - return false; - } - if (!StringUtil.checkEnclosure(definition, '[', ']')) { - addError(_("lutinutil.parserdef.unvalid.syntax.caret.option", - definition, description)); - return false; - } - // what else ? - return true; - } - - protected boolean postOptionParsingCheck(OptionDefinition definition, - List definitions) { - - if (definition.getAlias().length > 4) { - addError(_("lutinutil.parserdef.too.much.alias.option", - Arrays.toString(definition.getAlias()), - definition.getDefinition())); - return false; - } - - List alias = new ArrayList(); - // on vérifier que les alias commencent tous par '-' - for (String alia : definition.getAlias()) { - - if (!alia.startsWith("-")) { - addError(_("lutinutil.parserdef.unvalid.syntax.alias.option", - alia, definition.getDefinition())); - return false; - } - } - - for (OptionDefinition optionDefinition : definitions) { - // on vérifier que le nom n'est pas dupliqué - if (optionDefinition.getName().equals(definition.getName())) { - addError(_("lutinutil.parserdef.duplicated.option.name", - definition.getName(), definition.getDefinition(), - optionDefinition.getDefinition())); - return false; - } - // on vérifie les conflits sur les alias - alias.addAll(Arrays.asList(optionDefinition.getAlias())); - for (String alia : definition.getAlias()) { - if (alias.contains(alia)) { - addError(_("lutinutil.parserdef.duplicated.option.alias", - alia, definition.getDefinition(), - optionDefinition.getDefinition())); - return false; + if (!mandatory && min > 0) { + addError(_("lutinutil.parserdef.argument.optional.cardinalite", min, max, definition, groupementDefinition), null); + unvalidate(); } } - } - - if (!checkCardinalite(definition.getMin(), definition.getMax(), - definition.getDefinition(), definition.getDescription())) { - return false; - } - - // do the rest! - return true; - } - protected boolean checkCardinalite(int min, int max, String definition, - String description) { - if (min < 0) { - addError(_("lutinutil.parserdef.min.can.not.be.negative", min, - definition, description)); - return false; - } - if (max == 0) { - addError(_("lutinutil.parserdef.max.can.not.be.zero", max, - definition, description)); - return false; - } - // on vérifie que la cardinalité max >-2 - if (max < -1) { - addError(_("lutinutil.parserdef.max.too.low", max, - definition, description)); - return false; - } - // on vérifie que la cardinalité max==-1 || max >= min - if (max > -1 && max < min) { - addError(_("lutinutil.parserdef.max.lowest.than.min", - max, min, definition, description)); - return false; - } - return true; - } - - protected boolean checkOptionArgument(String definition, - String groupementDefinition, - OptionArgumentType type, - OptionArgumentValueType valueType, - String key, - Collection valueTypesUsed, - Collection argumentsFound, - int min, - int max, - boolean mandatory) { - - if (type == null) { - addError(_("lutinutil.parserdef.unvalid.syntax.unknown.argument.type", - definition, groupementDefinition)); - return false; - } - if (valueType == null) { - addError(_("lutinutil.parserdef.unvalid.syntax.unknown.value.type", - definition, groupementDefinition)); - return false; - } - if (key == null) { - addError(_("lutinutil.parserdef.unfound.key", - definition, groupementDefinition)); - return false; - } - if (!checkCardinalite(min, max, definition, groupementDefinition)) { - return false; - } - if (argumentKeyUsed.contains(key)) { - addError(_("lutinutil.parserdef.duplicated.argument.key", - key, definition, groupementDefinition)); - return false; - } - // un argument de type constant ne peut pas avoir de cardinalite autre que {1,1} - if (type == OptionArgumentType.constant) { - if (mandatory && !(min == 1 && max == 1)) { - addError(_("lutinutil.parserdef.const.argument.mandatory.cardinalite", - min, max, definition, groupementDefinition)); - return false; - } - if (!mandatory && !(min == 0 && max == 1)) { - addError(_("lutinutil.parserdef.const.argument.optional.cardinalite", - min, max, definition, groupementDefinition)); - return false; - } - } else { - if (mandatory && min < 1) { - addError(_("lutinutil.parserdef.argument.mandatory.cardinalite", - min, max, definition, groupementDefinition)); - return false; - } - if (!mandatory && min > 0) { - addError(_("lutinutil.parserdef.argument.optional.cardinalite", - min, max, definition, groupementDefinition)); - return false; + if (type == OptionArgumentType.valued) { + if (((valueType == _newfile) && valueTypesFound.contains(_string)) || + ((valueType == _string) && valueTypesFound.contains(_newfile))) { + addError(_("lutinutil.parserdef.string.and.newfile.in.same.group", definition, groupementDefinition), null); + unvalidate(); + } + // on vérifier qu'un type n'est pas dupliqué + if (valueTypesFound.contains(valueType)) { + addError(_("lutinutil.parserdef.duplicated.argument.valued.type", valueType, definition, groupementDefinition), null); + unvalidate(); + } } + return valid; } - // on verifie que la clef n'est pas déjà enregistrée - /*for (OptionArgumentDefinition optionArgumentDefinition : argumentsFound) { - if (optionArgumentDefinition.getKey().equals(key)) { - addError(_("lutinutil.parserdef.duplicated.argument.key", - key, definition, groupementDefinition)); - return false; - } - }*/ - if (type == OptionArgumentType.valued) { - if (((valueType == _newfile) && valueTypesUsed.contains(_string)) || - ((valueType == _string) && valueTypesUsed.contains(_newfile))) { - addError(_("lutinutil.parserdef.string.and.newfile.in.same.group", - definition, groupementDefinition)); - return false; - - } - // on vérifier qu'un type n'est pas dupliqué - if (valueTypesUsed.contains(valueType)) { - addError(_("lutinutil.parserdef.duplicated.argument.valued.type", - valueType, definition, groupementDefinition)); - return false; - } + @Override + void clear() { + super.clear(); + valueTypesFound.clear(); } - return true; } - protected void addError(String content) { - registerError(content, null); - } + /** + * Cette classe contient le context d'un argument d'un groupe d'une option.. + *

    + * Elle est de visibilité package, car elle n'a pas à être utilisée ailleurs. + * + * @author chemit + */ + class OptionArgumentContext extends OptionParserUtil.ParserContext { - protected void registerError(String content, Exception e) { - OptionDefinitionParserException e1; - if (e == null) { - e1 = new OptionDefinitionParserException(content); - } else { - e1 = new OptionDefinitionParserException(content, e); + protected final OptionArgumentType type; + protected final OptionArgumentValueType valueType; + protected final int min; + protected final int max; + protected final String key; + + OptionArgumentContext(OptionGroupArgumentContext parent, + OptionArgumentType type, + OptionArgumentValueType valueType, + String key, Integer min, Integer max) { + super(parent, false); + this.type = type; + this.valueType = valueType; + this.key = key; + this.min = min; + this.max = max; } - errors.add(e1); } /** - * To contain a syntax unvalid OptionDefinition + * To contain a syntax unvalid OptionDefinition. * * @author chemit */ - protected static class SyntaxUnvalidOptionDefinition extends OptionDefinition { - private SyntaxUnvalidOptionDefinition(String description, String definition) { - super(null, description, definition, 0, 0, null, null); + protected static class SyntaxUnvalidOptionDefinition extends OptionDefinition { + private SyntaxUnvalidOptionDefinition(String key, String description, String definition) { + super(key + " : " + definition, description, 0, 0, null, null, null); } - } + } }