Re: JAXB fluent api plugin

View: New views
1 Messages — Rating Filter:   Alert me  

Parent Message unknown Re: JAXB fluent api plugin

by Hanson Char :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

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>
 * &lt;complexType name=&quot;concept&quot;&gt;
 *   &lt;complexContent&gt;
 *     &lt;sequence&gt;
 *       &lt;element name=&quot;id&quot; type=&quot;...&quot; /&gt;
 *       ...
 *       &lt;element name=&quot;relations&quot;&gt;
 *         &lt;complexType&gt;
 *           &lt;complexContent&gt;
 *             &lt;sequence&gt;
 *               &lt;element name=&quot;relation&quot; type=&quot;{...}relation&quot; maxOccurs=&quot;unbounded&quot;/&gt;
 *             &lt;/sequence&gt;
 *           &lt;/complexContent&gt;
 *         &lt;/complexType&gt;
 *       &lt;/element&gt;
 *     &lt;/sequence&gt;
 *   &lt;/complexContent&gt;
 * &lt;/complexType&gt;
 * </pre>
 *
 * the following code will be generated:
 *
 * <pre>
 * public class Concept {
 *   List&lt;Relation&gt; 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;
       }
}