Customizing the data model
An example earlier in this tutorial showed some simple CodeGen customizations. Now that you've seen how CodeGen handles the HR-XML TimeCard schema with default settings, it's time to look into some more powerful customizations.
Customizing the data model
The data-model code generated by CodeGen using default settings has some weaknesses.
For one thing, the schema type names all end with Type, and
this carries over to the corresponding generated class names, making the names longer
than necessary. The package name generated from the schema namespace, org.hrxml.ns, is reasonable, but it would be better to have a package name that indicates the data model is specifically for TimeCard documents.
Listing 11 shows another awkward aspect of the generated data-model classes, in which a java.math.BigInteger is used to represent an xs:integer type. This is the most accurate representation for xs:integer using standard Java classes, but BigInteger is awkward to use compared to simple int primitive or java.lang.Integer object types. Unfortunately, schemas are often written using the xs:integer type even when xs:int would be more appropriate, so developers can get stuck with BigInteger values in the generated code. That's definitely the case here, where the actual values allowed for GenderCode are all single digits (as shown by the original schema fragment at the bottom of the listing).
Listing 11. xs:integer generation example
/**
* Schema fragment(s) for this class:
* <pre>
* <xs:element xmlns:xs="http://www.w3.org/2001/XMLSchema" type="xs:integer"
* name="GenderCode"/>
* </pre>
*/
public class GenderCode
{
private BigInteger genderCode;
/**
* Get the 'GenderCode' element value.
*
* @return value
*/
public BigInteger getGenderCode() {
return genderCode;
}
/**
* Set the 'GenderCode' element value.
*
* @param genderCode
*/
public void setGenderCode(BigInteger genderCode) {
this.genderCode = genderCode;
}
}
<xsd:simpleType name="GenderCodeType">
<xsd:annotation>
<xsd:documentation>Must conform to ISO 5218 - Representation of Human Sexes
(0 - Not Known; 1 - Male; 2 - Female; 9 - Not specified)</xsd:documentation>
</xsd:annotation>
<xsd:restriction base="xsd:integer">
<xsd:pattern value="[0129]"/>
</xsd:restriction>
</xsd:simpleType>
|
Listing 12 shows a customization to improve these aspects of the generated data
model. The package="org.hrxml.timecard" attribute gives the
Java package to be used for the generated classes. The
type-substitutions="xs:integer xs:int" attribute defines
schema type substitutions to be applied by CodeGen, in this case using the xs:int type
wherever xs:integer is referenced in the schema. You can define multiple pairs of
substitutions by just adding more type names to the list, using spaces as separators
between the pairs as well as between the type names in each pair.
The nested name-converter element configures the handling of
XML names being converted to Java names. In this case, the
strip-suffixes="Type" attribute tells CodeGen to remove
Type wherever it occurs at the end of a name. You can provide
multiple alternatives to be stripped with this attribute, as a space-separated list. You
can also use a strip-prefixes attribute to remove unnecessary
leading text from names, along with several other forms of customizations. It's even
possible to replace the default name conversion class with your own implementation, if
you want to do something really special in the name conversions. For the full details on
these name-converter options, see the JiBX CodeGen
documentation.
Finally, the nested class-decorator element adds a decorator
to the code generation sequence. In this case, the decorator is a predefined one provided
as part of the CodeGen distribution, which adds useful support methods for collection
values. Any configured code generation decorators are called in sequence by CodeGen as
it constructs the source code for data model classes, and have the opportunity to modify
or add to the field, method, and class constructs generated by CodeGen. Each of these
constructs is passed to the decorator as an Abstract Syntax Tree (AST) component, using
the Eclipse AST implementation. The supplied decorators (including the
org.jibx.schema.codegen.extend.CollectionMethodsDecorator
decorator used here to add methods, and a
org.jibx.schema.codegen.extend.SerializableDecorator used
to add the java.io.Serializable interface and optionally a
version id to data model classes) provide examples of working with the Eclipse AST to
extend CodeGen, so the source code of these classes is a great starting point for
writing your own decorators.
Listing 12. TimeCard customizations example
<schema-set xmlns:xs="http://www.w3.org/2001/XMLSchema" package="org.hrxml.timecard"
type-substitutions="xs:integer xs:int">
<name-converter strip-suffixes="Type"/>
<class-decorator class="org.jibx.schema.codegen.extend.CollectionMethodsDecorator"/>
</schema-set> |
You can try out the Listing 12 customization using the custgen1
Ant target, or use the custom1 target to run the complete
sequence of generate, compile, bind, and test. Listing 13 shows the result of applying
the customizations. The TimeCardType class name has changed
to just TimeCard, and in addition to the
List get and set methods there are now added size, add,
indexed get, and clear methods. In the GenderCode class, the
BigInteger reference has been replaced with a simple int primitive type.
Listing 13. Customized data model
/**
* Schema fragment(s) for this class:
* <pre>
* ...
* </pre>
*/
public class TimeCard
{
...
private List<ReportedTime> reportedTimeList = new ArrayList<ReportedTime>();
...
/**
* Get the list of 'ReportedTime' element items.
*
* @return list
*/
public List<ReportedTime> getReportedTimes() {
return reportedTimeList;
}
/**
* Set the list of 'ReportedTime' element items.
*
* @param list
*/
public void setReportedTimes(List<ReportedTime> list) {
reportedTimeList = list;
}
/**
* Get the number of 'ReportedTime' element items.
* @return count
*/
public int sizeReportedTime() {
return reportedTimeList.size();
}
/**
* Add a 'ReportedTime' element item.
* @param item
*/
public void addReportedTime(ReportedTime item) {
reportedTimeList.add(item);
}
/**
* Get 'ReportedTime' element item by position.
* @return item
* @param index
*/
public ReportedTime getReportedTime(int index) {
return reportedTimeList.get(index);
}
/**
* Remove all 'ReportedTime' element items.
*/
public void clearReportedTime() {
reportedTimeList.clear();
}
...
}
/**
* Schema fragment(s) for this class:
* <pre>
* <xs:element xmlns:xs="http://www.w3.org/2001/XMLSchema" type="xs:int"
* name="GenderCode"/>
* </pre>
*/
public class GenderCode
{
private int genderCode;
/**
* Get the 'GenderCode' element value.
*
* @return value
*/
public int getGenderCode() {
return genderCode;
}
/**
* Set the 'GenderCode' element value.
*
* @param genderCode
*/
public void setGenderCode(int genderCode) {
this.genderCode = genderCode;
}
} |
 |
Eliminating unused definitions
In the first customization example, using the original simple schema, you saw how to control the type definitions included in the generated data model by using generate-all="false" to disable generating every global definition and an includes list to force generating specific definitions. Listing 14 shows a modified customization for the TimeCard schema that adds these attributes, with only the TimeCard element to be included in the generated data model (along with everything used by the TimeCard representation, of course).
Listing 14. Customization with only TimeCard components
<schema-set xmlns:xs="http://www.w3.org/2001/XMLSchema" package="org.hrxml.timecard"
type-substitutions="xs:integer xs:int" generate-all="false">
<name-converter strip-suffixes="Type"/>
<class-decorator class="org.jibx.schema.codegen.extend.CollectionMethodsDecorator"/>
<schema name="TimeCard.xsd" includes="TimeCard"/>
</schema-set> |
You can use the custgen2 Ant target to try this customization with CodeGen, or use the custom2 target to run the complete sequence of generate, compile, bind, and test. This change reduces the number of top-level classes in the data model from 15 to 10 — not a bad start on simplifying the data model.
|