My question concerns writing JAXB plugins, in particular ClassOutline internals.
In com.sun.tools.xjc.outline.ClassOutline there are fields:
- target
- ref
- implClass
- implRef
Code:
/**
* This {@link ClassOutline} holds information about this {@link CClassInfo}.
*/
public final @NotNull CClassInfo target;
/**
* The exposed aspect of the a bean.
*
* implClass is always assignable to this type.
* <p>
* Usually this is the public content interface, but
* it could be the same as the implClass.
*/
public final @NotNull JDefinedClass ref;
/**
* The implementation aspect of a bean.
* The actual place where fields/methods should be generated into.
*/
public final @NotNull JDefinedClass implClass;
/**
* The implementation class that shall be used for reference.
* <p>
* Usually this field holds the same value as the {@link #implClass} method,
* but sometimes it holds the user-specified implementation class
* when it is specified.
* <p>
* This is the type that needs to be used for generating fields.
*/
public final @NotNull JClass implRef;
As far as I know (SO Answer):
target- holds information inModel, which represents parsed and analysed schema file (.xsd)refis usually equals toimplClassand both holdsCode ModelimplClassis the right place to put newly generated fields, method, etc.implRef- what is it?
I want to add new field to class described by ClassOutline, so the code looks like this:
JDefinedClass dstClass = classOutline.ref;
JFieldVar dstField = dstClass.field(srcField.mods().getValue(),
srcField.type(), srcField.name());
It works great, until there is another plugin which works after above code is executed and uses com.sun.tools.xjc.outline.ClassOutline.getDeclaredFields() method.
Imagine - Plugin1 creates new fields and then CopyablePlugin is executed and wants to add clone() method, which copy every field. But CopyablePlugin doesn't see fields newly generated by Plugin1 - because to retrieve all fields from ClassOutline the CopyablePlugin uses com.sun.tools.xjc.outline.ClassOutline.getDeclaredFields() method which looks like:
/**
* Gets all the {@link FieldOutline}s newly declared
* in this class.
*/
public final FieldOutline[] getDeclaredFields() {
List<CPropertyInfo> props = target.getProperties();
// ...
Notice, that getDeclaredFields() retrieves properties from ClassOutline.target field (this's the Model - parsed XSD schema) and completely ignores code generated to ClassOutline.implClass.
Is it a bug or a feature?
For now I found workaround. The same field is also added as property to target:
classOutline.target.addProperty(prop);
Questions
- Could you explain me, what is the role of
ref/implClass/implRef? - Where I should generate completely new fields/method? Into
ref/implClass? - Does it necessary to keep consistency between
ref/implClassandtarget? New field added toimplClassshould be also added totarget, right? - Is
com.sun.tools.xjc.outline.ClassOutline.getDeclaredFields()correct? Or How properly retrieve all fields from ClassOutline? Maybe this should be union oftargetandimplClasscontent?
I would advise against adding fields to
ClassOutline. It is too late and is too much trouble. Believe me, I've tried it.What I found to be much easier and much more elegant is adding properties to
CClassInfo. It is one stage earlier, here you manipulate the model. Then on the next step the outline will be generated from the model so you don't even have to care about adding anything toClassOutline.To add property to the
CClassInfoyou just instantiate the property (for instancenew CAttributePropertyInfo(...)) and then just add it to theCClassInfo:myClassInfo.addProperty(myPropertyInfo);.In the case of the
Copyableplugin, if your plugin adds properties to the model and your plugin is invoked before theCopyableplugin, the latter will see the newly added fields. This is exactly why I recommend augmenting the model, not hack the outline or the code model.Now to your questions.
ref/implClass/implRefare all normally just the target generated class. The separation is due to some generation modi where XJC generated "public interface" and "implementing class" separately. I don't know whatimplRefis, the JavaDoc says something about user-specified classes so I'm thinking aboutjaxb:class/@refbinding. Never dealt with it, though.The best would be to augment the model (if you want to add new properties/fields). If you want to add some model-unrelated code (like
clonemethod fromCopyableplugin), I'l just add it toclassOutline.implClass.Technically there is no need to keep the code model (
classOutline.implClass) and the model (classOutline.target) in sync. If you don't, this won't break XJC. I can imagine that this might break some XJC plugins. For instance, a plugin may iterate over fields inJDefinedClassand try to find corresponding model properties. But the danger is rather theoretical.ClassOutline.getDeclaredFields()is correct, but it only gives you fields declared in this class - without fields from the superclass. But it's quite trivial to gather all of the fields recursively.getDeclaredFields()producesFieldOutlines based on the properties fromtargetCClassInfo. Forget theimplClass, that's just a representation of theClassOutline. So "union oftargetandimplClass" does not make much sense.