Hi Dmitry,
In your example:
>...one can simply write:
>
>Concept concept = new Concept();
>concept.getRelationsAsList().add(new Relation());"
But couldn't this be achieved via the existing fluent API, like below ?
Concept concept = new Concept().withRelations(
new Relations().withRelation(new Relation()));
Hanson
2009/9/14 Dmitry Katsubo
<dma_k@...>
Dear Hanson!
I am writing to you because I have certain ideas about how to extend
fluent API plugin with some functionality which I find interesting.
I do attach a source code which express my ideas. JavaDoc includes some
examples of what I want to be generated as the output.
Please, let me know, if you find the idea interesting. I can also
prepare a patch for XjcFluentApiPlugin.java if you agree to include it
to this plugin.
Also I face some problems, which I have mentioned here:
http://forums.java.net/jive/thread.jspa?messageID=362952#268266
and I also need this extension for JAXB API (which is very easy to add):
https://jaxb.dev.java.net/issues/show_bug.cgi?id=561
How can I influence the developers team?
Any feedback is very welcome, as your opinion is important for me.
Thanks in advance!
package com.sun.tools.xjc.addon.list_getter;
import java.util.Collection;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.sun.codemodel.JClass;
import com.sun.codemodel.JDefinedClass;
import com.sun.codemodel.JExpr;
import com.sun.codemodel.JFieldVar;
import com.sun.codemodel.JMethod;
import com.sun.codemodel.JMod;
import com.sun.codemodel.JOp;
import com.sun.codemodel.JType;
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.Outline;
import org.xml.sax.ErrorHandler;
/**
* This plugin generates getters for the types, which contain collections. These types are usually nested classes, which
* are handy to be instantiated in the getter. For example, for the XSD below:
*
* <pre>
* <complexType name="concept">
* <complexContent>
* <sequence>
* <element name="id" type="..." />
* ...
* <element name="relations">
* <complexType>
* <complexContent>
* <sequence>
* <element name="relation" type="{...}relation" maxOccurs="unbounded"/>
* </sequence>
* </complexContent>
* </complexType>
* </element>
* </sequence>
* </complexContent>
* </complexType>
* </pre>
*
* the following code will be generated:
*
* <pre>
* public class Concept {
* List<Relation> getRelationsAsList() {
* if (relations == null) {
* relations = new Concept.Relations();
* }
*
* return relations.getRelation();
* }
* </pre>
*
* which makes the coding easier. Now instead of:
*
* <pre>
* Concept concept = new Concept();
* Concept.Relations relations = new Concept.Relations();
* concept.setRelations(relations);
* relations.getRelation().add(new Relation());
* </pre>
*
* one can simply write:
*
* <pre>
* Concept concept = new Concept();
* concept.getRelationsAsList().add(new Relation());
* </pre>
*
* @author <a href="mailto:dmitry.katsubo@...">Dmitry Katsubo</a>
*/
public class ListGetterPlugin extends Plugin {
private static final Logger logger = Logger.getLogger(ListGetterPlugin.class.getName());
@Override
public String getOptionName() {
return "Xlist-getter";
}
@Override
public String getUsage() {
return " -Xlist-getter : generate getters for lists";
}
private static final String GETTER_METHOD_PREFIX = "get";
private static final String GETTER_METHOD_SUFFIX = "AsList";
@Override
public boolean run(Outline outline, Options opt, ErrorHandler errorHandler) {
logger.setLevel(opt.verbose ? Level.INFO : Level.WARNING);
// For all generated classes:
for (final ClassOutline classOutline : outline.getClasses()) {
final JDefinedClass targetClass = classOutline.implClass;
// Check annotations for targetClass: name should not be empty for XmlType:
// @XmlType(name = "someName")
logger.info("Checking the outline class " + targetClass.name());
for (final JFieldVar field : targetClass.fields().values()) {
// If this class field is not type of class (e.g. primitive or array), then we skip it:
if (!(field.type() instanceof JDefinedClass)) {
continue;
}
if (field.name().startsWith("_")) {
continue;
}
final JDefinedClass fieldType = (JDefinedClass) field.type();
logger.info("Checking the class " + fieldType.fullName() + " of the field " + field.name());
int count = 0;
for (final JMethod fieldTypeMethod : fieldType.methods()) {
// For each getter method of the field's class we
JType methodReturnType = getReturnTypeOfListGetter(fieldTypeMethod);
if (methodReturnType == null) {
continue;
}
final JMethod jgetterMethod = targetClass.method(JMod.PUBLIC, methodReturnType,
GETTER_METHOD_PREFIX + firstLetterToUpperCase(field.name()) + GETTER_METHOD_SUFFIX
+ (count++ == 0 ? "" : "_" + count));
jgetterMethod.body()._if(JOp.eq(field, JExpr._null()))._then().assign(JExpr.ref(null, field),
JExpr._new(fieldType));
jgetterMethod.body()._return(JExpr.invoke(field, fieldTypeMethod));
}
}
}
return false;
}
private static String firstLetterToUpperCase(String name) {
return name.substring(0, 1).toUpperCase() + name.substring(1);
}
/**
* The same as {@link com.sun.tools.xjc.addon.fluent_api.XjcFluentApiPlugin#isListGetterMethod(JMethod)} but also
* returns the return type of this method.
*/
private static JType getReturnTypeOfListGetter(JMethod method) {
final int mods = method.mods().getValue();
// check if it is a non-static public method
if ((mods & JMod.STATIC) == 1 || (mods & JMod.PUBLIC) == 0) {
return null;
}
final String methodName = method.name();
// See if the method name looks like a getter method
if (methodName.length() <= GETTER_METHOD_PREFIX.length() || !methodName.startsWith(GETTER_METHOD_PREFIX)) {
return null;
}
// A list getter method will have no argument.
if (method.listParams().length > 0) {
return null;
}
// See if the return type of the method is a Collection:
JType methodReturnType = method.type();
if (!(methodReturnType instanceof JClass)) {
return null;
}
JClass classType = (JClass) methodReturnType;
do {
logger.info("Checking return type " + classType.fullName() + " of the method " + method.name());
if (classType.fullName().startsWith(Collection.class.getName())
|| classType.fullName().startsWith(List.class.getName())) {
return methodReturnType;
}
classType = classType._extends();
} while (classType != null);
return null;
}
}