Code generation using Javadoc

Extend Javadoc by creating custom doclets

Page 2 of 2
public class SimpleCodeMaker extends CodeMaker {
  public void make(ClassDoc classdoc) {
    Log.log("creating description file",Log.INFO,this);
    setFile("genclasses/" + classdoc.name() + ".txt");
    processMethods(classdoc);
    processFields(classdoc);
    endFile();
  }
  private void processMethods(ClassDoc classdoc) {
    MethodDoc[] methods = classdoc.methods();
    out("Methods");
    out("-------");
    for (int j=0; j<methods.length; j++) {
      out("Method: name = " + methods[j].name());
    }
  }
  ...
}

Organizing multiple Makers

Commonly you need to have many different output formats generated at the same time. So instead of creating an individual doclet for each code generator, you can allow a collection of code generators to run at the same time. You can easily achieve that goal by using the Composite design pattern, which lets you create a CompositeMaker that allows the invoker of a Maker to treat a collection of makers the same way it treats an individual CodeMaker. The CompositeMaker class implements the Maker interface and maintains a collection of code generators. You add a Maker to the composite using the method addMaker:

public boolean addMaker(Maker cm) {
    return m_makers.add(cm);
}

The run method then simply executes the make method on its collection of Makers:

public void make(ClassDoc classdoc) {
  for (Iterator i = m_makers.iterator(); i.hasNext();) {
    Maker maker = (Maker)i.next();
    maker.make(classdoc);
  }
}

Sample CodeMakers

The sample code generators provided, JavaCodeMaker and CppCodeMaker, create simple getters and setters for the data fields defined in the metadata Java class. The SqlCodeMaker creates a simple table definition for use in a database.

Using the sample code generators for Java, C++, and SQL, the implementation of the doclet's start method now looks as follows:

public static boolean start(RootDoc root) {
  ClassDoc[] classes = root.classes();
  //Set up CodeMakers to run
  CompositeMaker codemakers =
    new CompositeMaker("Simple Java/CPP/SQL Makers");
  codemakers.addCodeMaker(new JavaCodeMaker("Java"));
  codemakers.addCodeMaker(new CppCodeMaker("C++"));
  codemakers.addCodeMaker(new SqlCodeMaker("SQL"));
  //Iterate through all classes and execute the "make" method the
  //composite codemaker.
  for (int i=0; i < classes.length; i++ ) {
    ClassDoc classdoc = classes[i];
    codemakers.make(classdoc);
  }
  return true;
}

To demonstrate the use of custom Javadoc tags, I've used two custom tags, @enum and @primarykey. The @enum tag is used to create an enumerated type for a specific field in Java and C++. The SQL code generator uses the @primarykey tag to identify the field that will be used as the primary key in the SQL table.

In the Doclet API, the method tags(String nameOfTag) on the FieldDoc class lets you generate code relating to your custom tag set. That method allows you to ask for a Javadoc tag by name, as in Tags[] tags = fielddoc.tags("enum"). The method makeEnums in the Java and C++ code generators shows the use of the tags method. In the implementation of JavaCodeMaker, enumerations are simply implemented as a "static final int". See Resources for more information on implementing enumerated types in Java in a type-safe manner.

In the case of JavaCodeMaker, the metadata description of the OrderType field is defined as:

/**
 * The order qualifier that determines such things as the amount of
 * time in which to leave an order in and at what price to execute an 
order.
 *
 * @enum MARKET LIMIT STOP ALL_OR_NONE FILL_OR_KILL
 */
private int OrderType;

That description results in the following methods, fields, and enumerations in the generated Java source code file:

public int getOrderType()
  {
    return m_OrderType;
  }
  public void setOrderType(int val)
  {
    m_OrderType = val;
  }
private int m_OrderType;
public static final int MARKET = 0;
public static final int LIMIT = 1;
public static final int STOP = 2;
public static final int ALL_OR_NONE = 3;
public static final int FILL_OR_KILL = 4;

Conclusion

Using these examples as a starting point, you can easily develop more-elaborate code generators for use within the J2EE architecture, or other custom frameworks for your own purposes.

Mark Pollack is a Java developer at TIBCO Finance Technology where he designs and implements Java-based trading systems for the financial industry in New York City. Over the past year and a half he has been involved with several projects that needed a well-defined and performance-optimized message/object specifications across a variety of distributed processes. He started Java programming in 1997 at Brookhaven National Laboratory, where he worked for two years in the computing division as a postdoctorate researcher on the PHENIX experiment.

Learn more about this topic

Related:
| 1 2 Page 2