3

I'd like to create two separate JAXB packages from the same XSD where the implementation classes of one package are mutable and the other's are immutable. Naturally, I'd like every pair of implementation classes from the two packages to implement a shared interface.

For example, the following resulting structure would be ideal:

my.model.Model - Read only interface

my.model.impl.ModelImpl implements my.model.Model - Mutable implementation

my.model.immutable.Model implements my.model.Model - Immutable implementation

Creating the interfaces and mutable implementations is simple enough using the generateValueClass="true" option of the globalBindings XJB element.

As for the immutable implementation, It doesn't seem that XJC can accomplish this out-of-the-box, and I'd probably need to fork the immutable-xjc plugin for my own purposes.

Writing a XJC plugin which causes generated classes to implement a required interface is easily done (as is hiding the enum classes which are redundant for the immutable package), however, I'm quite stuck in trying change the JAXB model by removing existing fields and adding them with other types. Even given lexicore's useful answer here, it's still not clear to me where such changes should be make on the Codemodel model in order for XJC to pick up those changes and continue as usual, or even if such functionality is possible.

Is a comprehensive guide to using Codemodel in XJC plugins available anywhere?

Or better yet, is there a different way of accomplishing this?

roded
  • 466
  • 7
  • 17

1 Answers1

1

This is not quite trivial.

To the best of my knowledge, there is no real "guide" on XJC internals. I had to learn by doing. You might want to take a look at some of the existing XJC plugin to see how they accomplish things.

I'm also not quite sure why you'd nee to "remove existing fields and add them with other types". To me, this does not seem consistent with your task.

I'd decompose this task in three tasks:

  1. Generate immutable interfaces in a specified package.
  2. Automatically implement interfaces from a specified package.
  3. Generate immutable classes.

In the build I'd define 3 executions then:

  • Generate immutable interfaces (using 1).
  • Generate mutable classes (normal XJC + 2).
  • Generate immutable classes (3 + 2).

immutable-xjc probably already implements 3, so I'd write plugins for 1 and 2.

For 1 and 2, I think, I'd try to work on the CodeModel level. Just with the generated classes, not messing with the model and the outline.

In 1, you basically just need to "extract" a read-only interface from the JDefinedClass, this should be possible.

2 should be easier. For each com.acme.impl.Foo you'll need to add implements com.acme.Foo based on the package mapping com.acme.impl -> com.acme.

In practice you'll probably meet all kinds of problems for instance with generics etc., but this can probably not avoided with this task.

If you want a deeper dive, look at BeanGenerator and FieldRendererFactory. Those are responsible for generating outlines out of the model. So if you'd like to work on a deeper level (and not just on the CodeModel surface), you'd probably have to implement your own generator and field renderer factory. But that might be quite messy. Many central XJC classes in this area are package-protected so you'd either have to copy-paste or work around that. I think, working on the CodeModel level should be sufficient.

lexicore
  • 42,748
  • 17
  • 132
  • 221
  • @roded Outline is "rendering" of the model. So if you want a different "rendering" (like you do), outline would be a suitable place to capture this. You won't need to "manipulate" outline, you'd need to generate your own outline which would finally produce interfaces/classes in the code model as you want. Basically, outline is an intemediate step between the model and the code model. – lexicore Aug 19 '15 at 06:47