Index: maven-commandline-plugin/src/java/org/codelutin/util/Generator.java diff -u maven-commandline-plugin/src/java/org/codelutin/util/Generator.java:1.5 maven-commandline-plugin/src/java/org/codelutin/util/Generator.java:1.6 --- maven-commandline-plugin/src/java/org/codelutin/util/Generator.java:1.5 Sun Dec 2 07:16:30 2007 +++ maven-commandline-plugin/src/java/org/codelutin/util/Generator.java Wed Dec 5 02:56:56 2007 @@ -13,22 +13,20 @@ package org.codelutin.util; -import javassist.*; -import javassist.bytecode.AnnotationsAttribute; -import javassist.bytecode.ClassFile; -import javassist.bytecode.ConstPool; -import javassist.bytecode.annotation.Annotation; -import org.apache.commons.lang.StringUtils; +import javassist.CannotCompileException; +import javassist.ClassClassPath; +import javassist.ClassPath; +import javassist.ClassPool; +import javassist.CtClass; +import javassist.CtMethod; +import javassist.NotFoundException; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugin.logging.Log; +import org.codelutin.i18n.I18n; import static org.codelutin.i18n.I18n._; -import org.codelutin.util.OptionDefinitionParser.OptionContext; -import org.codelutin.util.OptionParserAnnotationHelper.OptionA; -import org.codelutin.util.OptionParserAnnotationHelper.OptionArgumentA; -import org.codelutin.util.OptionParserAnnotationHelper.OptionGroupArgumentA; -import org.codelutin.util.OptionParserAnnotationHelper.OptionParserA; +import org.codelutin.util.OptionParserAnnotationHelper.ApplicationA; import java.io.BufferedWriter; import java.io.File; @@ -36,8 +34,6 @@ import java.io.IOException; import java.io.StringWriter; import java.io.Writer; -import java.util.HashMap; -import java.util.Map; /** * Permet de générer la factory de définitions d'options de ligne de commande, @@ -49,6 +45,12 @@ */ public class Generator extends AbstractMojo { + static { + // init I18N + I18n.init(System.getProperty("user.language"), + System.getProperty("user.country")); + } + protected Log log = getLog(); /** @@ -57,312 +59,223 @@ * @readonly */ protected String projectName; - /** * @description Répertoire de sortie des classes. * @parameter expression="${commandline.out}" default-value="${project.build.outputDirectory}" */ protected File out; - /** * @description nom du paquetage utilisé pour la génération des classes * @parameter expression="${commandline.parserFullyQualifiedName}" * @required */ protected String parserFullyQualifiedName; - + /** + * @description type de source à utiliser par le parser + * @parameter expression="${commandline.typeSource}" default-value="properties" + * @required + */ + protected String typeSource; /** * @description Chemin du fichier de propriétés contenant les définitions d'options à utiliser pour générer la factory de définitions d'options. - * @parameter expression="${commandline.propertiesFile}" default-value="${basedir}/src/resources/${project.name}_options.properties" + * @parameter expression="${commandline.source}" default-value="${basedir}/src/resources/${project.name}_options.properties" */ - protected File propertiesFile; - + protected File source; /** * @description Target rst file. * @parameter expression="${commandLine.rstFilePath}" default-value="${basedir}/src/site/fr/rst/${project.name}_usage.rst" */ protected File rstFilePath; - /** - * @description flag to show details of parsing. - * @parameter expression="${commandLine.showDetails}" default-value="false" + * @description flag to generate rst file. + * @parameter expression="${commandLine.generateRst}" default-value="true" */ - protected boolean showDetails; - + protected boolean generateRst; + /** + * @description flag to generate or not specialized Option + * @parameter expression="${commandLine.generateOptionImplementation}" default-value="false" + */ + protected boolean generateOptionImplementation; /** * @description flag to show errors of parsing. * @parameter expression="${commandLine.showError}" default-value="false" */ protected boolean showErrors; - /** * @description flag to backup already generated class. * @parameter expression="${commandLine.backupClass}" default-value="false" */ protected boolean backupClass; - /** - * @description flag to generate or not specialized Option - * @parameter expression="${commandLine.generateOptionImplementation}" default-value="false" - */ - protected boolean generateOptionImplementation; public void execute() throws MojoExecutionException, MojoFailureException { + // get source (make sure to be on a absolute path) + source = new File(source.getAbsolutePath()); - CtClass clazz = null; - OptionDefinitionParser parser = null; + if (isUpToDate()) { + log.info(_("commandline.parser.uptodate",parserFullyQualifiedName)); + return; + } + CtClass clazz = null; try { - propertiesFile = new File(propertiesFile.getAbsolutePath()); - if (!out.exists()) { - out.mkdirs(); - } + // parse source + OptionDefinitionParser parser = doParse(); - // do parse definitions - parser = OptionDefinitionParser.doParse(OptionDefinitionParser.TypeSource.properties, propertiesFile); - log.info(_("commandline.parser.result.info", parser, parser.safeContexts.length)); - - // create new root class pool - ClassPool pool = new ClassPool(null); - - if (backupClass) { - // backup previously generated classes if asked - backupPreviousGeneratedClass(pool, parserFullyQualifiedName); - } + // init Class pool and make backups of generated class (@see backupClass) + ClassPool pool = initClassPool(); - // we need the classpath of OptionParser, since - // we load it as super class of generated classe, make sure pool - // know it. - pool.appendClassPath(new ClassClassPath(OptionParser.class)); - pool.appendClassPath(out.getAbsolutePath()); + // instanciate a OptionParser generator + OptionParserGenerator parserGenerator = new OptionParserGenerator( + out.getAbsolutePath(), parserFullyQualifiedName, + generateOptionImplementation + ); + ApplicationA anno; + try { + // generate OptionParser implementation and return applicationA + anno = parserGenerator.generateOptionParser(pool, parser); + } catch (Exception e) { + throw new MojoExecutionException(e.getMessage(), e); + } - // generate OptionParser implementation - OptionParserA anno = generateOptionParser(pool, parser); + // build usage rst file (@see generateRst) + generateRst(anno); - if (rstFilePath != null) { - // build usage rst file - generateRst(anno); - } + // show errors (@see showErrors) + showErrors(parser); - if (showErrors) { - if (parser.hasFailed()) { - Writer writer = new StringWriter(); - parser.printErrors(writer); - String lines = writer.toString(); - for (String s : lines.split("\n")) { - log.info(_("commandline.showErrors.info") + ' ' + s); - } - writer.flush(); - writer.close(); - } else { - log.info(_("commandline.showErrors.no.error.info")); - } - } - if (showDetails) { - StringBuilder sb; - sb = new StringBuilder(parser.toString()); - String lines = sb.toString(); - for (String s : lines.split("\n")) { - log.info(_("commandline.showDetails.info") + ' ' + s); - } - } if (parser.hasFailed()) { - throw new RuntimeException(_("commandline.parser.parsing.error", parser, parser.errors.size())); + throw new RuntimeException(_("commandline.parser.parsing.error", parser, parser.errors.length)); } - } catch (Exception e) { - String s = _("commandline.parser.fatal.error", parser, e); - //log.error(s); - e.printStackTrace(); - throw new MojoFailureException(s); } finally { detachClazz(clazz); } } - @SuppressWarnings({"unchecked"}) - protected OptionParserA generateOptionParser(ClassPool pool, OptionDefinitionParser parser) throws Exception { + protected OptionDefinitionParser doParse() throws MojoFailureException { - CtClass superClazz = pool.get(OptionParser.class.getName()); - - // create new implementation of OptionParser - CtClass clazz = pool.makeClass(parserFullyQualifiedName, superClazz); - - CtClass optionClazz = pool.get(Option.class.getName()); + // make sure out exists + if (!out.exists()) { + out.mkdirs(); + } + // get type of source for parser + OptionDefinitionParser.TypeSource typeP = null; try { - // no more abstract, but now public - clazz.setModifiers(Modifier.PUBLIC); - - ClassFile cf = clazz.getClassFile(); - ConstPool constPool = cf.getConstPool(); + typeP = OptionDefinitionParser.TypeSource.valueOf(typeSource); + } catch (Exception e) { + // ignore here (will be treate by parser) + } + // do parse definitions + try { + OptionDefinitionParser parser = OptionDefinitionParser.doParse(typeP, source); + log.info(_("commandline.parser.result.info", parser, parser.options.length)); + log.info(_("commandline.parser.result2.info", parser, parser.arguments.length)); + return parser; + } catch (IOException e) { + throw new MojoFailureException(e.getMessage()); + } + } - // we must create first the impl of Option (for the moment empty) - // to push the impl class in OptionParserA annotation. - Map map = new HashMap(); - for (OptionContext safeContext : parser.safeContexts) { - String key = safeContext.key; - String fieldName = StringUtil.convertToConstantName(key); - map.put(key, fieldName); - // add a constant field in OptionParser with key of option - generateOptionKeyField(parser, clazz, fieldName, key); - // generate Option impl - CtClass resultCLass = generateOptionImpl0(pool, clazz, fieldName, optionClazz); - // reload it - resultCLass = pool.get(resultCLass.getName()); - // now we keep the impl generated to be pushed in OptionParserA - safeContext.impl = resultCLass.toClass(); + protected boolean isUpToDate() { + //TOOD check if parser exists, and is not older than source + long newtimestamp = source.lastModified(); + int lastIndex = parserFullyQualifiedName.lastIndexOf('.'); + String path; + if (lastIndex > -1) { + String c = File.separator; + if (c.equals("\\")) { + c = c + c; } + path = parserFullyQualifiedName.replaceAll("\\.", c); - // add OptionParser annotation on implementation class - AnnotationsAttribute attribute = new AnnotationsAttribute(constPool, AnnotationsAttribute.visibleTag); + } else { + path = parserFullyQualifiedName; + } + File f = new File(out, path+".class"); + return f.exists() && newtimestamp <= f.lastModified(); + } - OptionParserA anno = parser.toAnnotation(); + protected ClassPool initClassPool() throws MojoFailureException { + // create new root class pool + ClassPool pool = new ClassPool(null); - Annotation annotation = AnnotationConverter.convert(anno, constPool, pool); - attribute.setAnnotation(annotation); - cf.addAttribute(attribute); - - // mark to generate java5 code (for annotations) - cf.setVersionToJava5(); - - // save it in out - clazz.writeFile(out.getAbsolutePath()); - - for (OptionA definition : anno.options()) { - String fieldName = map.get(definition.key()); - String className = clazz.getPackageName() + ".Option_" + fieldName; - generateOptionImpl(pool, className, definition); - log.info(_("commandline.parser.impl.build.info", fieldName, className, this)); - } - map.clear(); - log.info(_("commandline.parser.build.info", clazz.getName())); - return anno; - } finally { - detachClazz(clazz, superClazz, optionClazz); + // backup previously generated classes if asked + //TODO Backup also OptionImpl ? + try { + backupPreviousGeneratedClass(pool, parserFullyQualifiedName); + // we need the classpath of OptionParser, since we load it as super + // class of generated classe, make sure pool know it. + pool.appendClassPath(new ClassClassPath(OptionParser.class)); + // add also where classes are generated to make possible hot later load + // (while tests for example). + pool.appendClassPath(out.getAbsolutePath()); + return pool; + } catch (Exception e) { + throw new MojoFailureException(e.getMessage()); } } - protected void generateRst(OptionParserA definitions) throws IOException { - // asume where coming here rstFilePath is not null - assert rstFilePath != null; + protected void generateRst(ApplicationA applicationA) throws MojoFailureException { + if (rstFilePath == null) { + return; + } if (!rstFilePath.getParentFile().exists()) { rstFilePath.getParentFile().mkdirs(); } - Writer writer = new BufferedWriter(new FileWriter(rstFilePath)); - log.info(_("commandline.generateRstFile.info", rstFilePath)); + Writer w; try { - String txt = _("commandline.generateRstFile.head", projectName); - writer.append(txt); - for (int i = 0, j = txt.length() - 1; i < j; i++) { - writer.append('='); - } - writer.append('\n'); - String prefix = _("commandline.generateRstFile.option.head") + ' '; - writer.append(OptionParserUtil.toString(definitions,prefix)); - } finally { - if (writer != null) { - writer.flush(); - writer.close(); - } + w = new BufferedWriter(new FileWriter(rstFilePath)); + } catch (IOException e) { + throw new MojoFailureException(e.getMessage()); } - } - - private void generateOptionKeyField(OptionDefinitionParser parser, CtClass clazz, String fieldName, String key) throws CannotCompileException { - log.info(_("commandline.parser.field.build.info", fieldName, key, parser)); - CtField fld = CtField.make("public static final String " + fieldName + "= \"" + key + "\";", clazz); - clazz.addField(fld); - } - - private CtClass generateOptionImpl0(ClassPool pool, CtClass clazz, String fieldName, CtClass optionClazz) throws NotFoundException, CannotCompileException, IOException { - String clazzName = clazz.getPackageName() + ".Option_" + fieldName; - CtClass impl = pool.makeClass(clazzName, optionClazz); + log.info(_("commandline.generateRstFile.info", rstFilePath)); try { - // add constructors (following super ones) - CtConstructor[] superCtConstructor = clazz.getConstructors(); - for (CtConstructor ctConstructor : superCtConstructor) { - CtConstructor newConstructor = new CtConstructor(ctConstructor.getParameterTypes(), impl); - impl.addConstructor(newConstructor); - } - impl.writeFile(out.getAbsolutePath()); - return impl; + String head = _("commandline.generateRstFile.head", projectName); + String prefixO = _("commandline.generateRstFile.options.head", projectName); + String prefixA = _("commandline.generateRstFile.arguments.head", projectName); + String prefix = _("commandline.generateRstFile.prefix") + ' '; + w.append(OptionParserUtil.toString(applicationA, head, prefixO, prefixA, prefix)); + } catch (IOException e) { + throw new MojoFailureException(e.getMessage()); } finally { - detachClazz(impl); + if (w != null) { + try { + w.flush(); + w.close(); + } catch (IOException e) { + throw new MojoFailureException(e.getMessage()); + } + } } } - private void generateOptionImpl(ClassPool pool, String clazzName, OptionA optionA) throws NotFoundException, CannotCompileException, IOException { - CtClass impl = pool.get(clazzName); - // be sure no modification were done on the class - if (impl.isFrozen()) { - impl.detach(); - impl = pool.get(clazzName); - } - try { - for (OptionGroupArgumentA groupArgumentA : optionA.groups()) { - int position = groupArgumentA.pos(); - for (OptionArgumentA argumentA : groupArgumentA.arguments()) { - String argumentKey = argumentA.key(); - String argumentKeyCap = StringUtils.capitalize(argumentKey); - OptionArgumentValueType valueType = argumentA.valueType(); - String typeAsStr = valueType.getClazz().getName(); - boolean isBooleanValueType = valueType == OptionArgumentValueType._boolean; - String prefix = "public "; - if (isBooleanValueType || argumentA.type() == OptionArgumentType.constant) { - typeAsStr = "Boolean"; - } - if (typeAsStr.startsWith("java.lang.")) { - typeAsStr = typeAsStr.substring("java.lang.".length()); - } - prefix += typeAsStr; - String positionTypeParameters = position + "," + typeAsStr + ".class"; - String positionTypeKeyParameters = positionTypeParameters + ",\"" + argumentKey + "\""; - boolean multi = argumentA.max() == -1 || argumentA.max() > 1; - switch (argumentA.type()) { - case constant: - // add a boolean getter - addMethod(impl, prefix, - buildMethodName(true, argumentKeyCap), - "return Boolean.valueOf(\"" + argumentKey + "\".equals(getConstantArgumentValue(" + position + ")));"); - break; - case namedAndValued: - // add a typed getter T getArgument - addMethod(impl, prefix, - buildMethodName(isBooleanValueType, argumentKeyCap), - "return (" + typeAsStr + ") getNamedAndValuedArgumentValue(" + positionTypeKeyParameters + ");"); - if (multi) { - // add a typed getter T[] getArguments - addMethod(impl, prefix + "[]", - buildMethodName(false, argumentKeyCap + "s"), - "return (" + typeAsStr + "[]) getNamedAndValuedArgumentValues(" + positionTypeKeyParameters + ");"); - } - break; - case valued: - // add a typed getter T getArgument - addMethod(impl, prefix, - buildMethodName(isBooleanValueType, argumentKeyCap), - "return (" + typeAsStr + ") getValuedArgumentValue(" + positionTypeParameters + ");"); - if (multi) { - // add a typed getter T[] getArguments - addMethod(impl, prefix + "[]", - buildMethodName(false, argumentKeyCap + "s"), - "return (" + typeAsStr + "[]) getValuedArgumentValues(" + positionTypeParameters + ");"); - } - break; + protected void showErrors(OptionDefinitionParser parser) throws MojoFailureException { + if (showErrors) { + if (parser.hasFailed()) { + try { + Writer writer = new StringWriter(); + parser.printErrors(writer); + String lines = writer.toString(); + for (String s : lines.split("\n")) { + log.info(_("commandline.showErrors.info") + ' ' + s); } + writer.flush(); + writer.close(); + } catch (IOException e) { + throw new MojoFailureException(e.getMessage()); } + } else { + log.info(_("commandline.showErrors.no.error.info")); } - // save file - impl.writeFile(out.getAbsolutePath()); - } finally { - detachClazz(impl); } } - private String buildMethodName(boolean booleanValueType, String methodName) { + protected String buildMethodName(boolean booleanValueType, String methodName) { methodName = (booleanValueType ? "is" : "get") + methodName; return methodName; } - private void addMethod(CtClass impl, String prefix, String methodName, String body) throws CannotCompileException { + protected void addMethod(CtClass impl, String prefix, String methodName, String body) throws CannotCompileException { CtMethod method; String b = prefix + " " + methodName + "() {" + body + "}"; //log.user(b); @@ -370,7 +283,7 @@ impl.addMethod(method); } - private void detachClazz(CtClass... impl) { + protected void detachClazz(CtClass... impl) { for (CtClass ctClass : impl) { if (ctClass != null) { ctClass.detach(); @@ -379,6 +292,9 @@ } protected void backupPreviousGeneratedClass(ClassPool pool, String... classNames) throws NotFoundException, CannotCompileException, IOException { + if (!backupClass) { + return; + } ClassPath cp = pool.insertClassPath(out.getAbsolutePath()); String SUFFIX = "_" + System.currentTimeMillis(); for (String name : classNames) { Index: maven-commandline-plugin/src/java/org/codelutin/util/OptionParserGenerator.java diff -u /dev/null maven-commandline-plugin/src/java/org/codelutin/util/OptionParserGenerator.java:1.1 --- /dev/null Wed Dec 5 02:57:01 2007 +++ maven-commandline-plugin/src/java/org/codelutin/util/OptionParserGenerator.java Wed Dec 5 02:56:56 2007 @@ -0,0 +1,359 @@ +/** + * ##% Copyright (C) 2002, 2007 Code Lutin 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; + +import javassist.CannotCompileException; +import javassist.ClassPool; +import javassist.CtClass; +import javassist.CtConstructor; +import javassist.CtField; +import javassist.CtMethod; +import javassist.Modifier; +import javassist.NotFoundException; +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.apache.commons.lang.StringUtils; +import static org.codelutin.i18n.I18n._; +import org.codelutin.log.LutinLog; +import org.codelutin.log.LutinLogFactory; +import org.codelutin.util.OptionDefinitionParser.OptionArgumentContext; +import org.codelutin.util.OptionDefinitionParser.OptionContext; +import org.codelutin.util.OptionParserAnnotationHelper.ApplicationA; +import org.codelutin.util.OptionParserAnnotationHelper.ArgumentA; +import org.codelutin.util.OptionParserAnnotationHelper.GroupArgumentA; +import org.codelutin.util.OptionParserAnnotationHelper.OptionA; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +/** + * Permet de générer un {@link OptionParser} à partir des résultats du parsing + * d'un {@link OptionDefinitionParser}. + * + * @author chemit + */ +public class OptionParserGenerator { + + private static final LutinLog log = LutinLogFactory.getLutinLog(OptionParserGenerator.class); + + /** prefix of option field in OptionParser implementation */ + private static final String PREFIX_OPTION_FIELD = "OPTION_"; + + /** prefix of argument field in OptionParser implementation */ + private static final String PREFIX_ARGUMENT_FIELD = "ARGUMENT_"; + + /** chemin du classpath où générer les classes compilées */ + protected final String out; + + /** nom du parseur à générer */ + protected final String parserFullyQualifiedName; + + /** flag to generate or not specialized Option */ + protected final boolean generateOptionImplementation; + + protected OptionDefinitionParser parser; + protected ClassPool pool; + + public OptionParserGenerator(String out, String parserFullyQualifiedName, boolean generateOptionImplementation) { + this.out = out; + this.parserFullyQualifiedName = parserFullyQualifiedName; + this.generateOptionImplementation = generateOptionImplementation; + } + + protected ApplicationA generateOptionParser(ClassPool pool, OptionDefinitionParser parser) throws Exception { + this.pool = pool; + this.parser = parser; + CtClass superClazz = pool.get(OptionParser.class.getName()); + CtClass optionClazz = pool.get(Option.class.getName()); + // create new implementation of OptionParser + CtClass clazz = pool.makeClass(parserFullyQualifiedName, superClazz); + try { + // no more abstract, but now only public + clazz.setModifiers(Modifier.PUBLIC); + ClassFile cf = clazz.getClassFile(); + ConstPool constPool = cf.getConstPool(); + + // generate parser constants + Map map = generateParserConstants(clazz); + + // generate OptionImpl and push in parser contexts the + // class of impl of Option (@see OptionA.impl()) + generateOptionImpls(pool, optionClazz, clazz, map); + + // add a property in OptionParser to contains special option + generateApplicationArgumentsProperty(clazz, map); + + // add ApplicationA annotation to OptionParser + ApplicationA anno = addApplicationAnnotation(pool, clazz, cf, constPool); + + // generate OptionImpl accessors + generateOptionImplAccessors(pool, clazz, map, anno); + //clazz.writeFile(out); + map.clear(); + log.info(_("commandline.parser.build.info", clazz.getName())); + return anno; + } finally { + detachClazz(clazz, superClazz, optionClazz); + this.parser = null; + this.pool = null; + } + } + + private ApplicationA addApplicationAnnotation(ClassPool pool, CtClass clazz, ClassFile cf, ConstPool constPool) throws Exception { + // obtain definition anno from parser + ApplicationA anno = parser.toAnnotation(); + // 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); + // mark to generate java5 code (for annotations) + cf.setVersionToJava5(); + // save class in out classpath + clazz.writeFile(out); + return anno; + } + + protected Map generateParserConstants(CtClass clazz) throws CannotCompileException, NotFoundException, IOException { + Map map = new HashMap(); + for (OptionArgumentContext context : parser.arguments) { + String key = context.key; + String name = PREFIX_ARGUMENT_FIELD + StringUtil.convertToConstantName(key); + // add a constant field in OptionParser with key of argument + generateOptionKeyField(clazz, name, key); + } + + String key = OptionParser.APPLICATION_ARGUMENTS_FIELD_NAME; + String name = StringUtil.convertToConstantName(key); + map.put(key, name); + + for (OptionContext context : parser.options) { + key = context.key; + name = StringUtil.convertToConstantName(key); + map.put(key, name); + // add a constant field in OptionParser with key of option + generateOptionKeyField(clazz, PREFIX_OPTION_FIELD + name, key); + } + return map; + } + + @SuppressWarnings({"unchecked"}) + protected void generateOptionImpls(ClassPool pool, CtClass optionClazz, CtClass clazz, Map map) throws NotFoundException, CannotCompileException, IOException { + // generate Option impl for special Option Option_ApplicationArguments + // which contains application's arguments + CtClass resultCLass = generateOptionImplClass(pool, clazz, map.get(OptionParser.APPLICATION_ARGUMENTS_FIELD_NAME), optionClazz); + // reload it + resultCLass = pool.get(resultCLass.getName()); + // now we have the impl generated to be pushed in context + parser.argumentImpl = resultCLass.toClass(); + // we must create first the impl of Option (for the moment empty) + // to push the impl class in ApplicationA annotation. + for (OptionContext context : parser.options) { + String name = map.get(context.key); + if (generateOptionImplementation) { + // generate Option impl + resultCLass = generateOptionImplClass(pool, clazz, name, optionClazz); + // reload it + resultCLass = pool.get(resultCLass.getName()); + // now we have the impl generated to be pushed in context + context.impl = resultCLass.toClass(); + detachClazz(resultCLass); + } else { + // use default Option impl + context.impl = Option.class; + } + } + } + + protected void generateApplicationArgumentsProperty(CtClass clazz, Map map) throws CannotCompileException { + String fieldName = OptionParser.APPLICATION_ARGUMENTS_FIELD_NAME; + String className = clazz.getName() + '_' + map.get(fieldName); + //CtField fld = CtField.make("protected " + className + ' ' + fieldName + ";", clazz); + //fld.setModifiers(Modifier.PROTECTED); + //clazz.addField(fld); + CtMethod method; + StringBuilder builder; + builder = new StringBuilder(); + builder.append("public ").append(className).append(" get"); + builder.append(StringUtils.capitalize(fieldName)); + builder.append("() { return (").append(className).append(")").append(fieldName).append(";}"); + method = CtMethod.make(builder.toString(), clazz); + System.out.println(builder); + clazz.addMethod(method); + //method = CtNewMethod.getter("get" + StringUtils.capitalize(fieldName), fld); + //method.setModifiers(Modifier.PROTECTED); + //builder = new StringBuilder(); + //builder.append("public void set").append(StringUtils.capitalize(fieldName)).append("(").append(Option.class.getName()); + //builder.append(" option) {this.").append(fieldName).append("=(").append(className).append(")option ;}"); + //System.out.println(builder); + //method = CtMethod.make(builder.toString(), clazz); + //clazz.addMethod(method); + //method = CtNewMethod.setter("set" + StringUtils.capitalize(fieldName), fld); + //method.setModifiers(Modifier.PROTECTED); + //clazz.addMethod(method); + } + + protected void generateOptionImplAccessors(ClassPool pool, CtClass clazz, Map map, ApplicationA anno) throws NotFoundException, CannotCompileException, IOException { + // generate accessors for application arguments + String name = map.get(OptionParser.APPLICATION_ARGUMENTS_FIELD_NAME); + String className = clazz.getName() + '_' + name; + generateApplicationOptionImplMethods(pool, className, anno.arguments()); + log.info(_("commandline.parser.impl.build.info", OptionParser.APPLICATION_ARGUMENTS_FIELD_NAME, className)); + + if (generateOptionImplementation) { + // generate all Options accessors + for (OptionA definition : anno.options()) { + name = map.get(definition.key()); + className = clazz.getName() + '_' + name; + generateOptionImplMethods(pool, className, definition); + log.info(_("commandline.parser.impl.build.info", definition.key(), className)); + } + } + } + + protected void generateOptionKeyField(CtClass clazz, String fieldName, String key) throws CannotCompileException { + log.info(_("commandline.parser.field.build.info", fieldName, key, parser)); + CtField fld = CtField.make("public static final String " + fieldName + "= \"" + key + "\";", clazz); + clazz.addField(fld); + } + + protected CtClass generateOptionImplClass(ClassPool pool, CtClass clazz, String fieldName, CtClass optionClazz) throws NotFoundException, CannotCompileException, IOException { + String clazzName = clazz.getName() + '_' + fieldName; + CtClass impl = pool.makeClass(clazzName, optionClazz); + try { + // add constructors (following super ones) + CtConstructor[] superCtConstructor = clazz.getConstructors(); + for (CtConstructor ctConstructor : superCtConstructor) { + CtConstructor newConstructor = new CtConstructor(ctConstructor.getParameterTypes(), impl); + impl.addConstructor(newConstructor); + } + impl.writeFile(out); + return impl; + } finally { + detachClazz(impl); + } + } + + protected void generateOptionImplMethods(ClassPool pool, String clazzName, OptionA optionA) throws NotFoundException, CannotCompileException, IOException { + CtClass impl = pool.get(clazzName); + // be sure no modification were done on the class + if (impl.isFrozen()) { + impl.detach(); + impl = pool.get(clazzName); + } + try { + for (GroupArgumentA groupArgumentA : optionA.groups()) { + int position = groupArgumentA.pos(); + for (ArgumentA argumentA : groupArgumentA.arguments()) { + generateOptionArgumentAccessor(impl, position, argumentA); + } + } + // save file + impl.writeFile(out); + } finally { + detachClazz(impl); + } + } + + protected void generateApplicationOptionImplMethods(ClassPool pool, String clazzName, ArgumentA[] argumentAs) throws NotFoundException, CannotCompileException, IOException { + CtClass impl = pool.get(clazzName); + // be sure no modification were done on the class + if (impl.isFrozen()) { + impl.detach(); + impl = pool.get(clazzName); + } + try { + for (ArgumentA argumentA : argumentAs) { + generateOptionArgumentAccessor(impl, 0, argumentA); + } + // save file + impl.writeFile(out); + } finally { + detachClazz(impl); + } + } + + protected void generateOptionArgumentAccessor(CtClass impl, int position, ArgumentA argumentA) throws CannotCompileException { + String key = argumentA.key(); + String argumentKeyCap = StringUtils.capitalize(key); + String typeAsStr = argumentA.valueType().getClazz().getName(); + String positionType = position + "," + typeAsStr + ".class"; + String positionTypeKey = positionType + ",\"" + argumentA.key() + "\""; + boolean booleanValueType = argumentA.valueType() == OptionArgumentValueType._boolean; + //String typeAsStr = argumentA.valueType().getClazz().getName(); + boolean multi = argumentA.max() == -1 || argumentA.max() > 1; + String prefix = "public "; + if (booleanValueType || argumentA.type() == OptionArgumentType.constant) { + typeAsStr = "Boolean"; + } + if (typeAsStr.startsWith("java.lang.")) { + typeAsStr = typeAsStr.substring("java.lang.".length()); + } + prefix += typeAsStr; + switch (argumentA.type()) { + case constant: + // add a boolean getter + addMethod(impl, prefix, buildMethodName(true, argumentKeyCap), + "return Boolean.valueOf(\"" + key + "\".equals(getConstantArgumentValue(" + position + ")));"); + break; + case valued: + // add a typed getter T getArgument + addMethod(impl, prefix, buildMethodName(booleanValueType, argumentKeyCap), + "return (" + typeAsStr + ") getValuedArgumentValue(" + positionType + ");"); + if (multi) { + // add a typed getter T[] getArguments + addMethod(impl, prefix + "[]", buildMethodName(false, argumentKeyCap + "s"), + "return (" + typeAsStr + "[]) getValuedArgumentValues(" + positionType + ");"); + } + break; + case namedAndValued: + // add a typed getter T getArgument + addMethod(impl, prefix, buildMethodName(booleanValueType, argumentKeyCap), + "return (" + typeAsStr + ") getNamedAndValuedArgumentValue(" + positionTypeKey + ");"); + if (multi) { + // add a typed getter T[] getArguments + addMethod(impl, prefix + "[]", buildMethodName(false, argumentKeyCap + "s"), + "return (" + typeAsStr + "[]) getNamedAndValuedArgumentValues(" + positionTypeKey + ");"); + } + break; + } + } + + protected String buildMethodName(boolean booleanValueType, String methodName) { + methodName = (booleanValueType ? "is" : "get") + methodName; + return methodName; + } + + protected void addMethod(CtClass impl, String prefix, String methodName, String body) throws CannotCompileException { + CtMethod method; + String b = prefix + " " + methodName + "() {" + body + "}"; + //log.user(b); + method = CtMethod.make(b, impl); + impl.addMethod(method); + } + + protected void detachClazz(CtClass... impl) { + for (CtClass ctClass : impl) { + if (ctClass != null) { + ctClass.detach(); + } + } + } + +} \ No newline at end of file