Index: maven-commandline-plugin/src/java/org/codelutin/util/generator/OptionImplGenerator.java diff -u /dev/null maven-commandline-plugin/src/java/org/codelutin/util/generator/OptionImplGenerator.java:1.1 --- /dev/null Fri Dec 14 18:55:08 2007 +++ maven-commandline-plugin/src/java/org/codelutin/util/generator/OptionImplGenerator.java Fri Dec 14 18:55:03 2007 @@ -0,0 +1,123 @@ +/* +* ##% Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 Code Lutin, +* Benjamin Poussin, Tony Chemit +* +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +* ##% */ +package org.codelutin.util.generator; + +import javassist.CannotCompileException; +import javassist.ClassPool; +import javassist.CtClass; +import javassist.NotFoundException; +import org.apache.commons.lang.StringUtils; +import org.codelutin.log.LutinLog; +import org.codelutin.log.LutinLogFactory; +import org.codelutin.util.Option; +import org.codelutin.util.OptionArgumentType; +import org.codelutin.util.OptionArgumentValueType; +import org.codelutin.util.OptionDefinitionParser.OptionArgumentContext; +import org.codelutin.util.OptionDefinitionParser.OptionGroupArgumentContext; + +import java.io.IOException; +import java.util.List; + +/** + * Permet de générer une implantation d'option. + * + * @author chemit + * @see org.codelutin.util.OptionDefinitionParser + */ +public class OptionImplGenerator extends AbstractGenerator { + + private static final LutinLog log = LutinLogFactory.getLutinLog(OptionImplGenerator.class); + + public OptionImplGenerator(String out, long timestamp) throws NotFoundException { + super(out, timestamp); + } + + @SuppressWarnings({"unchecked"}) + public Class generate(ClassPool pool, CtClass optionClazz, String className, List groups) throws NotFoundException, CannotCompileException, IOException { + CtClass impl = null; + try { + backupPreviousGeneratedClass(pool,className); + + // create class from the super one + impl = pool.makeClass(className, optionClazz); + // add accessors + for (OptionGroupArgumentContext optionGroupArgumentContext : groups) { + int position = optionGroupArgumentContext.getPos(); + for (OptionArgumentContext optionArgumentContext : optionGroupArgumentContext.getArguments()) { + generateAccessor(impl, position, optionArgumentContext); + } + } + writeClass(impl); + // reload it (it was pruned) + impl = reloadClass(pool, impl); + log.info(impl.getName()); + return impl.toClass(); + } finally { + detachClazz(impl); + } + } + + + protected void generateAccessor(CtClass impl, int position, OptionArgumentContext argumentA) throws CannotCompileException { + String key = argumentA.getKey(); + String argumentKeyCap = StringUtils.capitalize(key); + String typeAsStr = argumentA.getValueType().getClazz().getName(); + String positionType = position + "," + typeAsStr + ".class"; + String positionTypeKey = positionType + ",\"" + argumentA.getKey() + "\""; + boolean booleanValueType = argumentA.getValueType() == OptionArgumentValueType._boolean; + boolean multi = argumentA.getMax() == -1 || argumentA.getMax() > 1; + String prefix = "public "; + if (booleanValueType || argumentA.getType() == OptionArgumentType.constant) { + typeAsStr = "Boolean"; + } + if (typeAsStr.startsWith("java.lang.")) { + typeAsStr = typeAsStr.substring("java.lang.".length()); + } + prefix += typeAsStr; + switch (argumentA.getType()) { + case constant: + // add a boolean getter + addMethod(impl, prefix, buildGetterMethodName(true, argumentKeyCap), + "return Boolean.valueOf(\"" + key + "\".equals(getConstantArgumentValue(" + position + ")));"); + break; + case valued: + // add a typed getter T getArgument + addMethod(impl, prefix, buildGetterMethodName(booleanValueType, argumentKeyCap), + "return (" + typeAsStr + ") getValuedArgumentValue(" + positionType + ");"); + if (multi) { + // add a typed getter T[] getArguments + addMethod(impl, prefix + "[]", buildGetterMethodName(false, argumentKeyCap + "s"), + "return (" + typeAsStr + "[]) getValuedArgumentValues(" + positionType + ");"); + } + break; + case namedAndValued: + // add a typed getter T getArgument + addMethod(impl, prefix, buildGetterMethodName(booleanValueType, argumentKeyCap), + "return (" + typeAsStr + ") getNamedAndValuedArgumentValue(" + positionTypeKey + ");"); + if (multi) { + // add a typed getter T[] getArguments + addMethod(impl, prefix + "[]", buildGetterMethodName(false, argumentKeyCap + "s"), + "return (" + typeAsStr + "[]) getNamedAndValuedArgumentValues(" + positionTypeKey + ");"); + } + break; + } + } + +} \ No newline at end of file Index: maven-commandline-plugin/src/java/org/codelutin/util/generator/OptionParserGenerator.java diff -u /dev/null maven-commandline-plugin/src/java/org/codelutin/util/generator/OptionParserGenerator.java:1.1 --- /dev/null Fri Dec 14 18:55:08 2007 +++ maven-commandline-plugin/src/java/org/codelutin/util/generator/OptionParserGenerator.java Fri Dec 14 18:55:03 2007 @@ -0,0 +1,182 @@ +/* +* ##% Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 Code Lutin, +* Benjamin Poussin, Tony Chemit +* +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +* ##% */ +package org.codelutin.util.generator; + +import javassist.ClassPool; +import javassist.CtClass; +import javassist.Modifier; +import javassist.bytecode.AnnotationsAttribute; +import static javassist.bytecode.AnnotationsAttribute.visibleTag; +import javassist.bytecode.ClassFile; +import javassist.bytecode.ConstPool; +import javassist.bytecode.annotation.Annotation; +import org.codelutin.log.LutinLog; +import org.codelutin.log.LutinLogFactory; +import org.codelutin.util.AnnotationConverter; +import org.codelutin.util.Option; +import org.codelutin.util.OptionDefinitionParser; +import org.codelutin.util.OptionDefinitionParser.OptionContext; +import org.codelutin.util.OptionParser; +import org.codelutin.util.OptionParserAnnotationHelper.ApplicationA; +import org.codelutin.util.OptionParserAnnotationHelper.OptionA; +import org.codelutin.util.StringUtil; + +import java.util.HashMap; +import java.util.Map; + +/** + * Permet de générer un {@link org.codelutin.util.OptionParser} à  partir des résultats du parsing + * d'un {@link org.codelutin.util.OptionDefinitionParser}. + *

+ * La génération du parseur s'effectue en deux temps. On crée d'abord la classe + * du générateur avec des constantes (une constante string pour chaque option). + *

+ * Ensuite on générate les implantations d'options (si requis {@link #generateOptions}). + *

+ * On complète dans le contexte du parseur de définition les implantations d'options {@link OptionA#impl()}. + *

+ * On transforme le contexte de parseur en une annotation {@link ApplicationA}, + * que l'on rajoute sur le le parseur généré. + * + * @author chemit + */ +public class OptionParserGenerator extends AbstractGenerator { + + private static final LutinLog log = LutinLogFactory.getLutinLog(OptionParserGenerator.class); + + /** prefix of option field in OptionParser implementation */ + private static final String PREFIX_OPTION_FIELD = "OPTION_"; + + /** flag to generate or not specialized Options */ + protected boolean generateOptions; + + public OptionParserGenerator(String out, boolean generateOptions,long timestamp) { + super(out, timestamp); + this.generateOptions = generateOptions; + } + + public boolean isGenerateOptions() { + return generateOptions; + } + + public void setGenerateOptions(boolean generateOptions) { + this.generateOptions = generateOptions; + } + + public ApplicationA generate(ClassPool pool, String parserFullyQualifiedName, OptionDefinitionParser parser) throws Exception { + + OptionImplGenerator optionImplGenerator = null; + Map map = null; + CtClass clazz = null; + CtClass optionClass = null; + + if (generateOptions) { + map = new HashMap(); + optionImplGenerator = new OptionImplGenerator(out, timestamp); + optionClass = pool.get(Option.class.getName()); + } + + try { + backupPreviousGeneratedClass(pool,parserFullyQualifiedName); + + // create new implementation of OptionParser with constant fields + clazz = generateClass(pool, parserFullyQualifiedName, parser, map); + log.info(clazz.getName()); + if (generateOptions) { + String name; + Class impl; + String className = clazz.getName(); + if (className.endsWith("Parser")) { + className = className.substring(0,className.length()-6); + } + // generate OptionImpls and set context with impl + for (OptionContext context : parser.getOptions()) { + name = className + '_' + map.get(context.getKey()); + impl = optionImplGenerator.generate(pool, optionClass, name, context.getGroups()); + // now we have the impl generated to be pushed in context + context.setImpl(impl); + } + } + + // obtain definition annotation from parser + ApplicationA anno = parser.toAnnotation(); + + // add ApplicationA annotation to OptionParser + addAnnotation(pool, clazz, anno); + + return anno; + } finally { + detachClazz(clazz, optionClass); + if (map != null) { + map.clear(); + } + } + } + + /** + * @param pool the class pool + * @param parserFullyQualifiedName the fully qualified name fo class to gen + * @param parser the parser to use + * @param map the map to contain Option.key <-> Option.CONSTANT_NAME + * @return the OptionParser implementation class created + * @throws Exception if any problem + */ + protected CtClass generateClass(ClassPool pool, String parserFullyQualifiedName, OptionDefinitionParser parser, Map map) throws Exception { + + CtClass superClazz = null; + + try { + // obtain the super class from classpath + superClazz = pool.get(OptionParser.class.getName()); + // create new implementation of OptionParser + CtClass clazz = pool.makeClass(parserFullyQualifiedName, superClazz); + // no more abstract, but now only public + clazz.setModifiers(Modifier.PUBLIC); + // add one constant String fiel for each option + for (OptionContext context : parser.getOptions()) { + String key = context.getKey(); + String name = StringUtil.convertToConstantName(key); + String fieldName = PREFIX_OPTION_FIELD + name; + if (map != null) { + map.put(key, name); + } + // add a constant field in OptionParser with key of option + addField(clazz, "public static final String " + fieldName + "= \"" + key + "\";"); + } + //log.info(_("commandline.parser.build.info", clazz.getName())); + return clazz; + } finally { + detachClazz(superClazz); + } + } + + protected void addAnnotation(ClassPool pool, CtClass clazz, ApplicationA anno) throws Exception { + ClassFile cf = clazz.getClassFile(); + ConstPool constPool = cf.getConstPool(); + // convert it to a javassist annotation + Annotation annotation = AnnotationConverter.convert(anno, constPool, pool); + // store it as annotation of generated OptionParser class + AnnotationsAttribute attr = new AnnotationsAttribute(constPool, visibleTag); + attr.setAnnotation(annotation); + cf.addAttribute(attr); + writeClass(clazz, cf); + } + +} \ No newline at end of file