Index: maven-commandline-plugin/src/java/org/codelutin/util/Generator.java diff -u maven-commandline-plugin/src/java/org/codelutin/util/Generator.java:1.3 maven-commandline-plugin/src/java/org/codelutin/util/Generator.java:1.4 --- maven-commandline-plugin/src/java/org/codelutin/util/Generator.java:1.3 Thu Nov 29 23:37:47 2007 +++ maven-commandline-plugin/src/java/org/codelutin/util/Generator.java Sun Dec 2 05:09:19 2007 @@ -1,47 +1,33 @@ -/* *##% - * Copyright (C) 2007 - * I18nPlugin, 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. - *##%*/ +/** + * ##% 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.*; import javassist.bytecode.AnnotationsAttribute; import javassist.bytecode.ClassFile; import javassist.bytecode.ConstPool; -import javassist.bytecode.FieldInfo; import javassist.bytecode.annotation.Annotation; -import javassist.bytecode.annotation.AnnotationMemberValue; -import javassist.bytecode.annotation.ArrayMemberValue; -import javassist.bytecode.annotation.ClassMemberValue; -import javassist.bytecode.annotation.EnumMemberValue; -import javassist.bytecode.annotation.IntegerMemberValue; -import javassist.bytecode.annotation.MemberValue; -import javassist.bytecode.annotation.StringMemberValue; import org.apache.commons.lang.StringUtils; 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.OptionParserAnnotationHelper.OptionArgumentDefinitionA; -import org.codelutin.util.OptionParserAnnotationHelper.OptionDefinitionA; +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 java.io.BufferedWriter; @@ -50,10 +36,7 @@ import java.io.IOException; import java.io.StringWriter; import java.io.Writer; -import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; -import java.util.List; import java.util.Map; /** @@ -65,19 +48,7 @@ * @phase compile */ public class Generator extends AbstractMojo { - static { - String language = System.getProperty("user.language"); - String country = System.getProperty("user.country"); - if (language != null && !"".equals(language)) { - if (country != null && !"".equals(country)) { - I18n.init(language, country); - } else { - I18n.init(language); - } - } - } - /** Log */ protected Log log = getLog(); /** @@ -135,60 +106,42 @@ */ protected boolean generateOptionImplementation; - /** le parseur de définition */ - protected OptionDefinitionParser parser; - - private CtClass optionParserAClazz; - private CtClass optionDefinitionAClazz; - private CtClass optionArgumentDefinitionAClazz; - private CtClass typeClazz; - private CtClass optionClazz; - private CtClass valueTypeClazz; - private ClassPool pool; public void execute() throws MojoExecutionException, MojoFailureException { CtClass clazz = null; + OptionDefinitionParser parser = null; + try { propertiesFile = new File(propertiesFile.getAbsolutePath()); - if (!out.exists()) { out.mkdirs(); } // do parse definitions - parser = OptionDefinitionParser.doParse(propertiesFile); - - log.info(_("commandline.parser.result.info", parser, parser.getDefinitions().length)); + parser = OptionDefinitionParser.doParse(OptionDefinitionParser.TypeSource.properties, propertiesFile); + log.info(_("commandline.parser.result.info", parser, parser.safeContexts.length)); - pool = new ClassPool(null); + // create new root class pool + ClassPool pool = new ClassPool(null); if (backupClass) { - // backup previously generated classes - backupPreviousGeneratedClass(parserFullyQualifiedName); + // backup previously generated classes if asked + 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)); - - // obtain some required class from pool - optionParserAClazz = pool.get(OptionParserA.class.getName()); - optionDefinitionAClazz = pool.get(OptionDefinitionA.class.getName()); - optionArgumentDefinitionAClazz = pool.get(OptionArgumentDefinitionA.class.getName()); - typeClazz = pool.get(OptionArgumentType.class.getName()); - optionClazz = pool.get(Option.class.getName()); - valueTypeClazz = pool.get(OptionArgumentValueType.class.getName()); - //TODO Make it works! - pool.importPackage(optionParserAClazz.getPackageName()); + pool.appendClassPath(out.getAbsolutePath()); // generate OptionParser implementation - generateOptionParser(pool); + generateOptionParser(pool, parser); if (rstFilePath != null) { // build usage rst file - generateRst(); + generateRst(parser.toDefinitions()); } if (showErrors) { @@ -206,15 +159,15 @@ } } if (showDetails) { - StringWriter sb; - parser.printDetails(sb = new StringWriter()); + 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.getErrors().size())); + throw new RuntimeException(_("commandline.parser.parsing.error", parser, parser.errors.size())); } } catch (Exception e) { String s = _("commandline.parser.fatal.error", parser, e); @@ -222,19 +175,19 @@ e.printStackTrace(); throw new MojoFailureException(s); } finally { - if (clazz != null) { - clazz.detach(); - } + detachClazz(clazz); } } - - protected void generateOptionParser(ClassPool pool) throws NotFoundException, CannotCompileException, IOException { + @SuppressWarnings({"unchecked"}) + protected void generateOptionParser(ClassPool pool, OptionDefinitionParser parser) throws Exception { 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()); try { // no more abstract, but now public clazz.setModifiers(Modifier.PUBLIC); @@ -242,78 +195,88 @@ ClassFile cf = clazz.getClassFile(); ConstPool constPool = cf.getConstPool(); - // add OptionParser annotation on implementation - AnnotationsAttribute attribute = new AnnotationsAttribute(constPool, AnnotationsAttribute.visibleTag); - Annotation annotation = new Annotation(constPool, optionParserAClazz); - ArrayMemberValue keyArray = new ArrayMemberValue(new StringMemberValue("", constPool), constPool); - List listKeys = new ArrayList(); - - for (OptionDefinition definition : parser.getDefinitions()) { - String name = definition.getName(); - listKeys.add(new StringMemberValue(name, constPool)); - generateOptionAnnotation(clazz, constPool, definition, name); + // 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(); } - keyArray.setValue(listKeys.toArray(new StringMemberValue[listKeys.size()])); - annotation.addMemberValue("keys", keyArray); + + // add OptionParser annotation on implementation class + AnnotationsAttribute attribute = new AnnotationsAttribute(constPool, AnnotationsAttribute.visibleTag); + + OptionParserA anno = parser.toAnnotation(); + + Annotation annotation = AnnotationConverter.convert(anno, constPool, pool); attribute.setAnnotation(annotation); cf.addAttribute(attribute); - //TODO why do we need this? + + // 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())); } finally { - if (clazz != null) { - clazz.detach(); + detachClazz(clazz, superClazz, optionClazz); + } + } + + protected void generateRst(OptionDefinition[] definitions) throws IOException { + // asume where coming here rstFilePath is not null + assert rstFilePath != null; + if (!rstFilePath.getParentFile().exists()) { + rstFilePath.getParentFile().mkdirs(); + } + Writer writer = new BufferedWriter(new FileWriter(rstFilePath)); + log.info(_("commandline.generateRstFile.info", rstFilePath)); + try { + String txt = _("commandline.generateRstFile.head", projectName); + writer.append(txt); + for (int i = 0, j = txt.length() - 1; i < j; i++) { + writer.append('='); } - if (superClazz != null) { - superClazz.detach(); + writer.append('\n'); + String prefix = _("commandline.generateRstFile.option.head") + ' '; + for (OptionDefinition definition : definitions) { + writer.append(prefix); + definition.printUsage(writer); + } + } finally { + if (writer != null) { + writer.flush(); + writer.close(); } } } - private void generateOptionAnnotation(CtClass clazz, ConstPool constPool, OptionDefinition definition, String name) throws CannotCompileException, NotFoundException, IOException { - - String fieldName = StringUtil.convertToConstantName(name); - log.info(_("commandline.parser.field.build.info", fieldName, name, parser)); - CtField fld; - fld = CtField.make("public static final String " + fieldName + "= \"" + name + "\";", clazz); + 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); - - FieldInfo fieldInfo = fld.getFieldInfo(); - ConstPool field_cp = fieldInfo.getConstPool(); - fieldInfo.addAttribute(new AnnotationsAttribute(field_cp, AnnotationsAttribute.visibleTag)); - AnnotationsAttribute attribute = (AnnotationsAttribute) fieldInfo.getAttribute(AnnotationsAttribute.visibleTag); - Annotation annotation = new Annotation(field_cp, optionDefinitionAClazz); - - annotation.addMemberValue("key", new StringMemberValue(name, field_cp)); - annotation.addMemberValue("description", new StringMemberValue(definition.getDescription(), field_cp)); - annotation.addMemberValue("definition", new StringMemberValue(definition.getDefinition(), field_cp)); - annotation.addMemberValue("min", new IntegerMemberValue(field_cp, definition.getMin())); - annotation.addMemberValue("max", new IntegerMemberValue(field_cp, definition.getMax())); - ArrayMemberValue value = new ArrayMemberValue(new StringMemberValue("", constPool), field_cp); - List tmp = new ArrayList(); - for (String alia : definition.getAlias()) { - tmp.add(new StringMemberValue(alia, field_cp)); - } - value.setValue(tmp.toArray(new MemberValue[tmp.size()])); - annotation.addMemberValue("alias", value); - - value = generateArgumentAnnotation(definition, fieldInfo, field_cp); - annotation.addMemberValue("arguments", value); - if (generateOptionImplementation) { - CtClass impl = generateOptionImpl(clazz, fieldName, definition); - annotation.addMemberValue("impl", new ClassMemberValue(impl.getName(), constPool)); - } - attribute.setAnnotation(annotation); } - private CtClass generateOptionImpl(CtClass clazz, String fieldName, OptionDefinition definition) throws NotFoundException, CannotCompileException, IOException { - String clazzName = clazz.getPackageName() + '.' + optionClazz.getSimpleName() + '_' + fieldName; + 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); - try { // add constructors (following super ones) CtConstructor[] superCtConstructor = clazz.getConstructors(); @@ -321,181 +284,122 @@ CtConstructor newConstructor = new CtConstructor(ctConstructor.getParameterTypes(), impl); impl.addConstructor(newConstructor); } - - for (OptionArgumentDefinition optionArgumentDefinition : definition.getArguments()) { - String argumentKey = optionArgumentDefinition.getKey(); - String argumentKeyCap = StringUtils.capitalize(argumentKey); - int position = optionArgumentDefinition.getPos(); - OptionArgumentValueType valueType = optionArgumentDefinition.getValueType(); - - String typeAsStr = valueType.getClazz().getName(); - boolean isBooleanValueType = valueType == OptionArgumentValueType._boolean; - String prefix = "public "; - if (isBooleanValueType|| optionArgumentDefinition.getType()== 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 = optionArgumentDefinition.getMax() == -1 || optionArgumentDefinition.getMax() > 1; - switch (optionArgumentDefinition.getType()) { - 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; - } - } - log.info(_("commandline.parser.impl.build.info", fieldName, impl.getName(), parser)); - impl.writeFile(out.getAbsolutePath()); return impl; } finally { - if (impl != null) { - impl.detach(); + detachClazz(impl); + } + } + + 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; + } + } } + // save file + impl.writeFile(out.getAbsolutePath()); + } finally { + detachClazz(impl); } } private String buildMethodName(boolean booleanValueType, String methodName) { - methodName = (booleanValueType?"is":"get")+methodName; + methodName = (booleanValueType ? "is" : "get") + methodName; return methodName; } private void addMethod(CtClass impl, String prefix, String methodName, String body) throws CannotCompileException { CtMethod method; String b = prefix + " " + methodName + "() {" + body + "}"; - log.debug(b); + //log.user(b); method = CtMethod.make(b, impl); impl.addMethod(method); } - private ArrayMemberValue generateArgumentAnnotation(OptionDefinition definition, FieldInfo fieldInfo, ConstPool field_cp) throws NotFoundException { - ArrayMemberValue value; - value = new ArrayMemberValue(new AnnotationMemberValue(field_cp), field_cp); - List annotationMemberValues = new ArrayList(); - - for (OptionArgumentDefinition optionArgumentDefinition : definition.getArguments()) { - AnnotationsAttribute attribute; - Annotation annotation; - attribute = (AnnotationsAttribute) fieldInfo.getAttribute(AnnotationsAttribute.visibleTag); - annotation = new Annotation(field_cp, optionArgumentDefinitionAClazz); - annotation.addMemberValue("key", new StringMemberValue(optionArgumentDefinition.getKey(), field_cp)); - EnumMemberValue enumMemberValue; - enumMemberValue = new EnumMemberValue(field_cp); - enumMemberValue.setType(typeClazz.getName()); - enumMemberValue.setValue(optionArgumentDefinition.getType().name()); - annotation.addMemberValue("type", enumMemberValue); - enumMemberValue = new EnumMemberValue(field_cp); - OptionArgumentValueType valueType = optionArgumentDefinition.getValueType(); - enumMemberValue.setType(valueTypeClazz.getName()); - enumMemberValue.setValue(valueType.name()); - annotation.addMemberValue("valueType", enumMemberValue); - annotation.addMemberValue("min", new IntegerMemberValue(field_cp, optionArgumentDefinition.getMin())); - annotation.addMemberValue("max", new IntegerMemberValue(field_cp, optionArgumentDefinition.getMax())); - annotation.addMemberValue("pos", new IntegerMemberValue(field_cp, optionArgumentDefinition.getPos())); - annotationMemberValues.add(new AnnotationMemberValue(annotation, field_cp)); - attribute.setAnnotation(annotation); - } - value.setValue(annotationMemberValues.toArray(new MemberValue[annotationMemberValues.size()])); - return value; - } - - protected void generateRst() throws IOException { - // asume where coming here rstFilePath is not null - assert rstFilePath != null; - if (!rstFilePath.getParentFile().exists()) { - rstFilePath.getParentFile().mkdirs(); - } - Writer writer = new BufferedWriter(new FileWriter(rstFilePath)); - log.info(_("commandline.generateRstFile.info", rstFilePath)); - 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") + ' '; - List keys = new ArrayList(); - Map map = new HashMap(); - for (OptionDefinition definition : parser.getDefinitions()) { - keys.add(definition.getName()); - map.put(definition.getName(), definition); - } - Collections.sort(keys); - - for (String key : keys) { - OptionDefinition definition = map.get(key); - writer.append(prefix); - definition.printUsage(writer); - } - } finally { - if (writer != null) { - writer.flush(); - writer.close(); + private void detachClazz(CtClass... impl) { + for (CtClass ctClass : impl) { + if (ctClass != null) { + ctClass.detach(); } } } - protected void backupPreviousGeneratedClass(String... classNames) throws NotFoundException, CannotCompileException, IOException { - + protected void backupPreviousGeneratedClass(ClassPool pool, String... classNames) throws NotFoundException, CannotCompileException, IOException { ClassPath cp = pool.insertClassPath(out.getAbsolutePath()); - String SUFFIX = "_" + System.currentTimeMillis(); - for (String className : classNames) { - backupClass(className, pool, SUFFIX); + for (String name : classNames) { + try { + CtClass clazz = pool.get(name); + if (clazz != null) { + // rename this clazz to be safe + String newClassName = name + SUFFIX; + clazz.replaceClassName(name, newClassName); + log.info(_("commandline.backupClass", name, newClassName)); + clazz.writeFile(out.getAbsolutePath()); + } + } catch (NotFoundException e) { + // we are safe : class does not exists in class path, + // so no collision is possible :) + } } cp.close(); pool.removeClassPath(cp); } - protected void backupClass(String name, ClassPool pool, String SUFFIX) throws CannotCompileException, IOException { - try { - CtClass clazz = pool.get(name); - if (clazz != null) { - // rename this clazz to be safe - String newClassName = name + SUFFIX; - clazz.replaceClassName(name, newClassName); - log.info(_("commandline.backupClass", name, newClassName)); - clazz.writeFile(out.getAbsolutePath()); - } - } catch (NotFoundException e) { - // we are safe - } - } - }