Package org.codehaus.annogen.generate

Source Code of org.codehaus.annogen.generate.Annogen

/*   Copyright 2004 The Apache Software Foundation
*
*   Licensed under the Apache License, Version 2.0 (the "License");
*   you may not use this file except in compliance with the License.
*   You may obtain a copy of the License at
*
*       http://www.apache.org/licenses/LICENSE-2.0
*
*   Unless required by applicable law or agreed to in writing, software
*   distributed under the License is distributed on an "AS IS" BASIS,
*   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*   See the License for the specific language governing permissions and
*  limitations under the License.
*/
package org.codehaus.annogen.generate;

import org.codehaus.annogen.generate.internal.joust.FileWriterFactory;
import org.codehaus.annogen.generate.internal.joust.JavaOutputStream;
import org.codehaus.annogen.generate.internal.joust.SourceJavaOutputStream;
import org.codehaus.annogen.generate.internal.joust.Variable;
import org.codehaus.annogen.generate.internal.joust.WriterFactory;
import org.codehaus.annogen.override.internal.AnnoBeanBase;
import org.codehaus.annogen.override.internal.DefaultAnnoBeanMapping;
import org.codehaus.annogen.view.internal.reflect.ReflectAnnogenTigerDelegate;
import org.codehaus.annogen.view.internal.reflect.ReflectAnnogenTigerDelegate;
import org.codehaus.jam.JAnnotation;
import org.codehaus.jam.JAnnotationValue;
import org.codehaus.jam.JClass;
import org.codehaus.jam.JMethod;
import org.codehaus.jam.JamService;
import org.codehaus.jam.JamServiceFactory;
import org.codehaus.jam.JamServiceParams;
import org.codehaus.jam.internal.JamLoggerImpl;

import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.lang.reflect.Modifier;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

/**
* Code generator for AnnoBeans which can be driven programmatically
* or from the command line.  This is wrapped by Annogentask - if you use ant
* for your build, it's probably easirer to use that instead.
*
* @author Patrick Calahan <email: codehaus-at-bea-dot-com>
*/
public class Annogen {

  // ========================================================================
  // main() method

  //FIXME need to clean up and add parameters, add usage statement
  public static void main(String[] args) {
    try {
    JamServiceFactory jsf = JamServiceFactory.getInstance();
    JamServiceParams params = jsf.createServiceParams();
    Annogen ag = new Annogen();
    for(int i=0; i<args.length; i++) {
      if (args[i].equals("-d")) {
        i++;
        ag.setOutputDir(new File(args[i]));
        i++;
      } else {
        File f = new File(args[i]);
        if (f.isDirectory()) {
          File[] fs = f.listFiles();
          for(int j=0; j<fs.length; j++) params.includeSourceFile(fs[j]);
        } else {
          params.includeSourceFile(f);
        }
      }
    }
      JamService js = jsf.createService(params);
      ag.addAnnotationClasses(js.getAllClasses());
      ag.doCodegen();
    } catch(IOException ioe) {
      ioe.printStackTrace();
      System.out.flush();
      System.exit(-1);
    }
  }


  // ========================================================================
  // Constants

  public static final String SETTER_PREFIX = "set_";
  private static final String FIELD_PREFIX = "_";

  private static final String BASE_CLASS = AnnoBeanBase.class.getName();

  //the name of the 'annobeanClasssname' element of an AnnogenInfo annotation
  private static final String ANNOGENINFO_ANNOBEAN_CLASSNAME = "annoBeanClass";

  // ========================================================================
  // Variables

  private List mClassesLeftTodo = null;
  private Collection mClassesDone = null;
  private JavaOutputStream mJoust = null;
  private Map mDeclaredJclass2beanClassname = new HashMap();
  private boolean mImplementAnnotationTypes = true;
  private ReflectAnnogenTigerDelegate mTiger;

  // ========================================================================
  // Constructors

  public Annogen() {
    mTiger = ReflectAnnogenTigerDelegate.create(new JamLoggerImpl());
    if (mTiger == null) {
      throw new IllegalStateException
        ("The annogen code generator must be run under JDK 1.5 or later.");
    }
    mClassesLeftTodo = new LinkedList();
    mClassesDone = new HashSet();
  }

  // ========================================================================
  // Public methods

  public void addAnnotationClasses(JClass[] classes) {
    for(int i=0; i<classes.length; i++) {
      if (classes[i].isAnnotationType()) {
        JAnnotation ann = classes[i].getAnnotation(mTiger.getAnnogenInfoClass());
        if (ann == null) {
          warn("Ignoring "+classes[i].getQualifiedName()+
               " because it does not declare an @AnnogenInfo annotation.");
          continue; //REVIEW should this be an error instead?
        }
        JAnnotationValue val = ann.getValue(ANNOGENINFO_ANNOBEAN_CLASSNAME);
        if (val == null) {  //should not happen - it's not an optional value
          warn("Ignoring "+classes[i].getQualifiedName()+
               " because its @AnnogenInfo annotation does specify an "+
               ANNOGENINFO_ANNOBEAN_CLASSNAME);
          continue; //REVIEW should this be an error instead?
        }
        mDeclaredJclass2beanClassname.put(classes[i],val.asString());
        mClassesLeftTodo.add(classes[i]);
      } else {
        warn("Ignoring "+classes[i].getQualifiedName()+
             " because it is not an annotation type.");
      }
    }
  }

  public void setOutputDir(File dir) {
    WriterFactory wf = new FileWriterFactory(dir);
    setJavaOutputStream(new SourceJavaOutputStream(wf));
  }

  public void setJavaOutputStream(JavaOutputStream joust) {
    mJoust = joust;
  }

  public void doCodegen() throws IOException {
    while(mClassesLeftTodo.size() > 0) {
      JClass clazz = (JClass)mClassesLeftTodo.get(0);
      mClassesLeftTodo.remove(0);
      mClassesDone.add(clazz);
      doCodegen(clazz);
    }
  }

  public void setPre15CompatibilityMode(boolean b) {
    mImplementAnnotationTypes = b;
  }

  // ========================================================================
  // Private methods

  private void doCodegen(JClass clazz) throws IOException {
    {
      // figure out the name of the class to codegen and get started
      String annoBeanClassname = getAnnoBeanClassnameFor(clazz);
      if (annoBeanClassname == null) throw new IllegalStateException();
      int lastDot = annoBeanClassname.lastIndexOf('.');
      String simpleImplName = (lastDot == -1) ? annoBeanClassname :
        annoBeanClassname.substring(lastDot+1);
      String packageName = (lastDot == -1) ? null :
        annoBeanClassname.substring(0,lastDot);
      mJoust.startFile(packageName,simpleImplName);
    }
    JMethod[] methods = clazz.getDeclaredMethods();
    String[] implementInterface =
      (!mImplementAnnotationTypes ? null : new String[] {clazz.getQualifiedName()});
    mJoust.writeComment("THIS IS GENERATED CODE! DO NOT EDIT!\n\n\n"+
                        "Generated by "+getClass().getName()+
                        "\n on "+new Date()+"\n"+
                        "\n");
    mJoust.startClass(Modifier.PUBLIC,
                      BASE_CLASS,
                      implementInterface);
    // write the 'PROXY_FOR' field which we use at runtime to map back
    // to the annotation class that we are proxying for.  In the case
    // where we implement that interface (i.e. m14Compatible == false)
    // this is arguably redundant; we err on the side of most concrete
    // and direct by just always doing it this way.
    mJoust.writeField(Modifier.PUBLIC | Modifier.STATIC | Modifier.FINAL,
                      "java.lang.String",
                      DefaultAnnoBeanMapping.PROXY_FOR,
                      mJoust.getExpressionFactory().
                      createString(clazz.getQualifiedName()));

    for(int i=0; i<methods.length; i++) {
      String fieldName = FIELD_PREFIX+ methods[i].getSimpleName();
      JClass type = methods[i].getReturnType();
      String typeName = (!mImplementAnnotationTypes) ?
        getImplClassForIfGenerated(type) : type.getQualifiedName();
      Variable fieldVar =
        mJoust.writeField(Modifier.PRIVATE,typeName,fieldName,null);
      { // write the 'getter' implementation
        mJoust.startMethod(Modifier.PUBLIC,
                           typeName,
                           methods[i].getSimpleName(),
                           null, // no parameters
                           null, // no parameters
                           null // no throws
                           );
        mJoust.writeReturnStatement(fieldVar);
        mJoust.endMethodOrConstructor();
      }
      { // write the 'setter' implementation
        String[] paramTypeNames = new String[] {typeName};
        String[] paramNames = new String[] {"in"};
        Variable[] params = mJoust.startMethod(Modifier.PUBLIC,
                                               "void",
                                               SETTER_PREFIX+ methods[i].getSimpleName(),
                                               paramTypeNames,
                                               paramNames,
                                               null // no throws
        );
        mJoust.writeAssignmentStatement(fieldVar,params[0]);
        mJoust.endMethodOrConstructor();
      }
      { // check to see if we need to also do annogen for the field's type
        JClass c = clazz.forName(typeName);
        if (c.isAnnotationType()) {
          if (!mClassesLeftTodo.contains(c) && !mClassesDone.contains(c)) {
            mClassesLeftTodo.add(c);
          }
        }
      }
    }
    mJoust.endClassOrInterface();
    mJoust.endFile();
  }

  private void warn(String msg) {
    System.out.println("[Warning] "+msg);
  }


  // ========================================================================
  // Private methods

  private String getImplClassForIfGenerated(JClass clazz) {
    if (clazz.isArrayType()) {
      JClass comp = clazz.getArrayComponentType();
      StringWriter out = new StringWriter();
      out.write(getImplClassForIfGenerated(comp));
      for(int i=0; i<clazz.getArrayDimensions(); i++) out.write("[]");
      return out.toString();
    } else if (mClassesLeftTodo.contains(clazz) || mClassesDone.contains(clazz)) {
      return getAnnoBeanClassnameFor(clazz);
    }
    return clazz.getQualifiedName();
  }

  // ========================================================================
  // These methods keep the runtime in sync with codegen

  private String getAnnoBeanClassnameFor(JClass clazz) {
    return (String)mDeclaredJclass2beanClassname.get(clazz);
  }


}
TOP

Related Classes of org.codehaus.annogen.generate.Annogen

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.