Posted by kohsuke
on May 16, 2005 at 4:02 PM PDT
This morning I saw that one of our beloved JAXB users :-) is having a trouble compiling MathML with JAXB 2.0. This is the record of my trouble-shooting this, in the hope that this will be useful to others who face similar issues in MathML or other schemas.
Step 1: Download
The first thing I did was to download the MathML schema into my local disk. You can compile a remote schema by running XJC like:
$ xjc.sh -proxy webcache.sfbay.sun.com:8080 http://www.w3.org/Math/XMLSchema/mathml2/mathml2.xsd
But when you are going to compile schemas multiple times, it's often easier to first download them locally. Sometimes this ends up in more work as you need to fix inter-schema dependencie, but in this case MathML uses relative URLs for referencing other schemas, and therefore I only needed to download the tgz archive and run it locally.
Step 2: Property name "Class" is reserved
The first thing I did was to run it without any option to see how it goes:
$ xjc.sh mathml2/mathml2.xsd
This gave me a tons of the following errors.:
[ERROR] Property name "Class" is reserved by java.lang.Object.
line 26 of file:/C:/kohsuke/Sun/JAXB/jaxb-unit/schemas/individual/MathML2/common/common-attribs.xsd
If you go to the line 26 of this file, you'll see that this is the attribute called 'class'
The problem is that this attribute calls for the "getClass" and "setClass" methods to be generated on the generated class, but "getClass" will collide with the same method on the Object class. To get around this error, we need to rename the methods generated from this attribute.
To do this, you need to write a "binding customization", which change the way XJC behaves. You can do this either inline or externally; I'll show you both in here, but you only need to do either one of them.
Doing it inline means modifying the schema directly. You first put the following declaration on the element.
This signals that XJC that you are using 2.0 customizations. Then you add the following declaration:
<xs:attribute name="class" type="xs:NMTOKENS">
<jaxb:property name="clazz" />
This tells XJC to map this attribute to a property named "clazz" (so you'll see getClazz and setClazz.)
Doing it externally means writing it in a separate file, like this:
Notice that it contains the same information but in a different way. I needed to use an XPath to point to the place where I want to attach a customization. This is harder to do, but sometimes you can't modify the schema inline.
Now that I see this bug, I think that the JAXB spec should be smart enough to automatically resolve this collision by renaming it to "Clazz" or something. Hopefully the spec team will have time to incorporate this into the spec.
Step 3: Property "MiOrMoOrMn" is already defined
With the new binding file at my hand, I run XJC again, this time like this:
$ xjc.sh mathml2/mathml2.xsd -b binding.xjb
... which gave me this:
parsing a schema...
[ERROR] Property "MiOrMoOrMn" is already defined.
line 132 of file:/C:/kohsuke/Sun/JAXB/jaxb-unit/schemas/individual/MathML2/presentation/scripts.xsd
[ERROR] The following location is relevant to the above error
line 138 of file:/C:/kohsuke/Sun/JAXB/jaxb-unit/schemas/individual/MathML2/presentation/scripts.xsd
If you go to line 132 of scripts.xsd, you'll see that it's a somewhat complicated content model definition:
This is a standard technique in designing a schema. When you want to say "in this element, B can occur arbitrary times, but C can occur only up to once", you write this as B*,(C,B*)?. This, however, confuses JAXB, because it tries to bind the first B to its own property, then C to its own property, then the second B to its own property, and we end up having a collision again.
In this particlar case, B isn't a single element but it's a choice of large number of elements abstracted away in s, so they are hard to see. But if you see the same content model refering to the same element/group twice in a different place, you can suspect this.
In this case, you'd probably want the whole thing to map to a single list so that you can retain the order those elements show up in the document. You can do this by putting the same customization on the whole "mmultiscripts.content" model group, like this (or you can do it externally with XPath):
<jaxb:property name="content" />
It would be nice if the JAXB spec is (once again) smart enough to detect this and bind it appropriately, but this is harder. I plan to think about this more carefully and see if we can do something about this.
Step 4: Miller Time!
Anyhow, with this change in the customization, I run XJC yet again:
$ xjc.sh mathml2/mathml2.xsd -b binding.xjb
... and this time it worked and generated all the classes.
If you've come this far, it calls for a little celebration. With this satisfaction, I'm heading home today!
In case you just want to compile MathML, here's the binding file you can use by cut&paste: