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)ref
is usually equals toimplClass
and both holdsCode Model
implClass
is 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/implClass
andtarget
? New field added toimplClass
should 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 oftarget
andimplClass
content?
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
CClassInfo
you just instantiate the property (for instancenew CAttributePropertyInfo(...)
) and then just add it to theCClassInfo
:myClassInfo.addProperty(myPropertyInfo);
.In the case of the
Copyable
plugin, if your plugin adds properties to the model and your plugin is invoked before theCopyable
plugin, 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
/implRef
are 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 whatimplRef
is, the JavaDoc says something about user-specified classes so I'm thinking aboutjaxb:class/@ref
binding. 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
clone
method fromCopyable
plugin), 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 inJDefinedClass
and 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()
producesFieldOutline
s based on the properties fromtarget
CClassInfo
. Forget theimplClass
, that's just a representation of theClassOutline
. So "union oftarget
andimplClass
" does not make much sense.