In my previous post, I described how you can create an XJC Plugin and how to use it from maven. I will briefly go over the steps one more time, before explaining how to find and instantiate lists.
Once a list is found, we can instantiate the field with the init() method. The rest of the expression is:
Creating the Plugin:
The steps are:- Create a new Maven project, and add the jaxb-xjc dependency to your pom.
- Create a Class that extends com.sun.tools.xjc.Plugin.
- Implement the method getOptionName() to return the Option name you want used to turn on the plugin.
- Implement the logic of the plugin in the run() method.
- Go to (or create) the folder src/main/resources/META-INF/services in your project.
- In folder services, create a file named "com.sun.tools.xjc.Plugin"
- In that file, add the fully qualified name of your Plugin class.
For the detailed steps check my previous post.
The Implementation
Override the run() method with the following:
In the run() method, we iterate over the fields in a class, and check if they are parameterized or generic. If they have exactly one type parameter, we conclude its a list.@Override public boolean run(Outline outline, Options opt, ErrorHandler errorHandler) throws SAXException { for (ClassOutline co : outline.getClasses()){ //2. Look through the fields defined in each of the classes Map<String, JFieldVar> fields = co.implClass.fields(); for (JFieldVar f : fields.values()){ JClass fClass = (JClass) f.type(); if (fClass.getTypeParameters()!=null && fClass.getTypeParameters().size()==1){ // f.type() is a list JType inner = fClass.getTypeParameters().get(0); f.init(JExpr._new(co.parent().getCodeModel() .ref(ArrayList.class).narrow(inner))); replaceGetterNoInst(co, f); } } } return true; }
Once a list is found, we can instantiate the field with the init() method. The rest of the expression is:
- JExpr is a factory class that helps generate several JExpressions
- _new() creates the new operator
- ref() returns a JType for the given class
- narrow() sets the parameter type.
One last thing to remember, the Plugin actually runs after XJC is done generating the classes. This means that the getter method for the field just initialized already exists. And as you probably know, the getter method for a List always checks if the field is null and if it is, it instantiates it.
Now this of course has no negative effect what so ever, however it is unnecessary. I therefore added the method replaceGetterNoInst(), which you've probably noticed in the code above.
What it does is find the getter for the list and remove it. Then add a new one that does not check if the List is null. Here it is:
/** * Replaces the getter of field f, in classOutline co with one that does not * check if the field is null. * * @param co the ClassOutline of the class in which the getter is replaced * @param f the Field for which the getter is replaced */ private void replaceGetterNoInst(ClassOutline co, JFieldVar f) { //Create the method name String get = "get"; String name = f.name().substring(0, 1).toUpperCase() + f.name().substring(1); String methodName = get+name; //Find and remove Old Getter! JMethod oldGetter = co.implClass.getMethod(methodName, new JType[0]); co.implClass.methods().remove(oldGetter); //Create New Getter JMethod getter = co.implClass.method(JMod.PUBLIC, f.type(), methodName); getter.body()._return(JExpr.ref(f.name())); }
Done!
So there you have it. If you'd like to take a look at the source code for the Plugin, check out my GitHub Repo at https://github.com/walmir/xjc-inst
If you've got any questions feel free to comment here. If you find bugs or have improvement ideas/requests please submit them using the issue tracker at github.