/**
*
*/
package com.sun.tools.xjc.addon;
import java.io.IOException;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXException;
import com.sun.codemodel.JBlock;
import com.sun.codemodel.JClass;
import com.sun.codemodel.JExpr;
import com.sun.codemodel.JFieldVar;
import com.sun.codemodel.JInvocation;
import com.sun.codemodel.JMethod;
import com.sun.codemodel.JMod;
import com.sun.codemodel.JType;
import com.sun.tools.xjc.BadCommandLineException;
import com.sun.tools.xjc.Options;
import com.sun.tools.xjc.Plugin;
import com.sun.tools.xjc.outline.ClassOutline;
import com.sun.tools.xjc.outline.FieldOutline;
import com.sun.tools.xjc.outline.Outline;
/**
* This plugin adds the function getListFields(), which returns an array of all list fields a generated class defines.
* Optionally it can define String properties for list fields and generate a method returning the list of the names of
* the properties.
*
* @author Martin Pecka
*/
public class EnumerateListFieldsPlugin extends Plugin
{
/** Whether to generate property Strings. */
protected boolean generateProperties = false;
/** Whether to generate getListProperties() method. */
protected boolean enumerateProperties = false;
@Override
public String getOptionName()
{
return "Xenumerate-list-fields";
}
@Override
public String getUsage()
{
return " -Xenumerate-list-fields:\tadd function getListFields(), which returns an array of all list fields a generated class (and its generated superclasses) defines"
+ " -Xenumerate-list-fields-generate-properties:\tgenerate a String property for each list field"
+ " -Xenumerate-list-fields-enumerate-properties:\tadd function getListProperties(), which returns an array of property Strings of all list fields a generated class (and its generated superclasses) defines; requires -Xenumerate-list-fields-generate-properties to be specified.";
}
@Override
public List<String> getCustomizationURIs()
{
return Collections.singletonList("http://www.mff.cuni.cz/~peckam/java/origamist/jaxb/plugins");
}
@Override
public int parseArgument(Options opt, String[] args, int i) throws BadCommandLineException, IOException
{
int superRes = super.parseArgument(opt, args, i);
if (superRes > 0)
return superRes;
String arg = args[i];
if (arg.equals("-Xenumerate-list-fields-generate-properties")) {
generateProperties = true;
return 1;
} else if (arg.equals("-Xenumerate-list-fields-enumerate-properties")) {
enumerateProperties = true;
return 1;
}
return 0;
}
@Override
public boolean run(Outline model, Options opt, ErrorHandler errorHandler) throws SAXException
{
if (enumerateProperties && !generateProperties) {
throw new SAXException("Option -Xenumerate-list-fields-enumerate-properties requires option "
+ "-Xenumerate-list-fields-generate-properties to be specified, but it wasn't.");
}
JType returnType = model.getCodeModel().ref(List.class).erasure()
.narrow(model.getCodeModel().ref(Object.class).wildcard()).array();
JType propertyReturnType = model.getCodeModel().ref(String.class).array();
for (ClassOutline co : model.getClasses()) {
List<FieldOutline> listFields = new LinkedList<FieldOutline>();
List<FieldOutline> listFieldsDeclaredInThisClass = new LinkedList<FieldOutline>();
ClassOutline clazz = co;
while (clazz != null) {
for (FieldOutline fo : clazz.getDeclaredFields()) {
if (fo.getRawType().erasure() instanceof JClass
&& model.getCodeModel().ref(List.class)
.isAssignableFrom((JClass) fo.getRawType().erasure())) {
listFields.add(fo);
if (co == clazz)
listFieldsDeclaredInThisClass.add(fo);
}
}
clazz = clazz.getSuperClass();
}
if (generateProperties) {
for (FieldOutline fo : listFieldsDeclaredInThisClass) {
String propertyConstantName = getPropertyName(fo);
JFieldVar propertyConstant = co.implClass.field(JMod.PUBLIC | JMod.STATIC | JMod.FINAL,
String.class, propertyConstantName,
JExpr.lit(fo.getPropertyInfo().getName(false) + ":" + co.implClass.fullName()));
propertyConstant.javadoc().clear();
propertyConstant.javadoc().add("Property " + fo.getPropertyInfo().getName(false));
}
}
if (listFields.size() > 0) {
JMethod method = co.implClass.method(JMod.PUBLIC, returnType, "getListFields");
JBlock body = method.body();
JInvocation array = JExpr._new(returnType);
for (FieldOutline fo : listFields) {
String fieldName = fo.getPropertyInfo().getName(false);
// we need to call the list getter to be 100% sure the list field will be properly initialized
// if we used the field, calling this method from a super constructor will result in initializing
// the list with null, which we don't want
String getterName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
array.arg(JExpr.invoke(getterName));
}
body._return(array);
method.javadoc().addReturn()
.add("Return an array of all list fields defined on this class and its superclasses.");
if (enumerateProperties) {
method = co.implClass.method(JMod.PUBLIC, propertyReturnType, "getListProperties");
body = method.body();
array = JExpr._new(propertyReturnType);
for (FieldOutline fo : listFields) {
String propertyName = getPropertyName(fo);
array.arg(JExpr.ref(propertyName));
}
body._return(array);
method.javadoc()
.addReturn()
.add("Return an array of property names of all list fields defined on this class and its "
+ "superclasses. The order of the property names corresponds with the order of getListFields().");
}
}
}
return true;
}
/**
* Return the name of the property constant for the given field.
*
* @param fo The field to get the property name of.
* @return The name of the property constant for the given field.
*/
protected String getPropertyName(FieldOutline fo)
{
return fo.getPropertyInfo().getName(false).replaceAll("([A-Z])", "_$1").toUpperCase() + "_PROPERTY";
}
}