Hi Keith, Arnaud et
al.,
Part of my recent
work has been to replace DTOs in an application with Castor-generated
classes. To do this, I needed to implement the following "standard"
methods in each generated class, in addition to equals():
- a full-argument constructor
- a copy-constructor
- hashCode()
- toString() for debug
- clone()
(There is an overlap
between usage of the copy-constructor and clone()).
Attached are methods
for generating this code which I have inserted into
org.exolab.castor.builder.SourceFactory. I hooked into these methods from
SourceFactory.createSourceCode(XMLBindingComponent, SGStateInfo) as
follows:
//create equals() method?if (component.hasEquals())createEqualsMethod(jClass, component.hasHashcode());//create hashCode() method?if (component.hasHashcode())createHashCodeMethod(jClass, component.hasEquals());if (component.hasToString())createToStringMethod(jClass);if (component.hasClone()) {jClass.addInterface("Cloneable");createCloneMethod(jClass);}if (component.hasFullConstructor())createFullConstructor(jClass);if (component.hasCopyConstructor())createCopyConstructor(jClass);
I hope you can make
use of these.
Regards
Dean
|
intelli WHERE www.intelliwhere.com |
|
Dr. Dean
Chalker Distinguished Design Engineer (R&D) [EMAIL PROTECTED] IntelliWhere Division, Intergraph Corporation Australia Phone: 61-7-3510 8918 Fax: 61-7-3510 8901 |
/**
* Create a 'hashCode' method on the given
* JClass
* @param jclass the Jclass in which we create the hashCode method
* @param hasEquals <code>true</code> if the equals() method is also being
* generated
*/
public static void createHashCodeMethod(JClass jclass, boolean hasEquals) {
if (jclass == null)
throw new IllegalArgumentException("JClass must not be null");
JField[] fields = jclass.getFields();
JMethod jMethod = new JMethod(JType.Int, "hashCode");
jMethod.setComment("Override the java.lang.Object.hashCode method");
if (!hasEquals) {
jMethod.setComment("Note: equals() has not been overriden");
}
jclass.addMethod(jMethod);
JSourceCode jsc = jMethod.getSourceCode();
// implemented from Effective Java by JOshua Bloch: item 8
jsc.add("int result = 17;");
for (int i = 0; i <fields.length; i++) {
JField temp = fields[i];
//Be careful to arrayList....
String name = temp.getName();
JType type = temp.getType();
if (type.isPrimitive()) {
if (!temp.isAuxiliary()) {
jsc.add("result = 37*result + (_has" + name + " ? 0 : 1);");
//check first if the field
//is not null. This can occur while comparing
//two objects that contains non-mandatory fields.
jsc.add("if (_has" + name + ")");
if (type == JType.Boolean) {
jsc.indent();
jsc.add("result = 37*result + (" + name + " ? 0 :
1);");
jsc.unindent();
} else if (type == JType.Byte || type == JType.Char ||
type == JType.Short) {
jsc.indent();
jsc.add("result = 37*result + (int)" + name + ";");
jsc.unindent();
} else if (type == JType.Int) {
jsc.indent();
jsc.add("result = 37*result + " + name + ";");
jsc.unindent();
} else if (type == JType.Long) {
jsc.indent();
jsc.add("result = 37*result + (int)(" + name + " ^ ("
+ name + " >>> 32));");
jsc.unindent();
} else if (type == JType.Float) {
jsc.indent();
jsc.add("result = 37*result + Float.floatToIntBits(" +
name +");");
jsc.unindent();
} else if (type == JType.Double) {
jsc.append(" {");
jsc.indent();
jsc.add("long " + name + "L =
Double.doubleToLongBits(" + name + ");");
jsc.add("result = 37*result + (int)(" + name + "L ^ ("
+ name + "L >>> 32));");
jsc.unindent();
jsc.add("}");
}
}
} else {
jsc.add("result = 37*result + ((" + name + " != null) ? " + name +
".hashCode() : 0);");
}
}
jsc.add("return result;");
}//createHashcodeMethod
/**
* Create a 'toString' method on the given
* JClass
* @param jclass the Jclass in which we create the toString method
*/
public static void createToStringMethod(JClass jclass) {
if (jclass == null)
throw new IllegalArgumentException("JClass must not be null");
JField[] fields = jclass.getFields();
JMethod jMethod = new JMethod(new JClass("java.lang.String"), "toString");
jMethod.setComment("Override the java.lang.Object.toString method");
jclass.addMethod(jMethod);
JSourceCode jsc = jMethod.getSourceCode();
jsc.add("return \"[\"");
jsc.indent();
for (int i = 0; i <fields.length; i++) {
JField temp = fields[i];
//Be careful to arrayList....
String name = temp.getName();
JType type = temp.getType();
if (type.isPrimitive()) {
if (!temp.isAuxiliary()) {
jsc.add("+ \"");
if (i != 0) {
jsc.append(" ");
}
jsc.append(name + ":\"");
//check first if the field
//is not null. This can occur while comparing
//two objects that contains non-mandatory fields.
jsc.append(" + (_has" + name + " ? ");
if (type == JType.Boolean) {
jsc.append("(" + name + " ? \"true\" : \"false\")");
} else if (type == JType.Byte) {
jsc.append("Integer.toString(" + name + ", 16)");
} else if (type == JType.Char) {
jsc.append("new Character(" + name + ").toString()");
} else if (type == JType.Short) {
jsc.append("Short.toString(" + name + ")");
} else if (type == JType.Int) {
jsc.append("Integer.toString(" + name + ")");
} else if (type == JType.Long) {
jsc.append("Long.toString(" + name + ")");
jsc.append("");
} else if (type == JType.Float) {
jsc.append("Float.toString(" + name + ")");
} else if (type == JType.Double) {
jsc.append("Double.toString(" + name + ")");
}
jsc.append(" : \"null\")");
}
} else {
jsc.add("+ \"");
if (i != 0) {
jsc.append(" ");
}
jsc.append(name + ":\" + " + name);
}
}
jsc.add("+ \"]\";");
jsc.unindent();
}//createToStringMethod
/**
* Create a 'clone' method on the given
* JClass
* @param jclass the Jclass in which we create the clone method
*/
public static void createCloneMethod(JClass jclass) {
if (jclass == null)
throw new IllegalArgumentException("JClass must not be null");
JField[] fields = jclass.getFields();
JMethod jMethod = new JMethod(new JClass("java.lang.Object"), "clone");
jMethod.setComment("Override the java.lang.Object.clone method");
jclass.addMethod(jMethod);
JSourceCode jsc = jMethod.getSourceCode();
jsc.add("try {");
jsc.indent();
jsc.add(jclass.getLocalName() + " result = (" + jclass.getLocalName() +
")super.clone();");
for (int i = 0; i <fields.length; i++) {
JField temp = fields[i];
//Be careful to arrayList....
String name = temp.getName();
JType type = temp.getType();
if (!type.isPrimitive()) {
// @todo need to figure out the immutable classes
if (!type.getName().equals("java.lang.String")
&& !type.getName().equals("java.math.BigDecimal")
&& !type.getName().equals("byte")) {
jsc.add("if(" + name + " != null)");
jsc.indent();
jsc.add("result." + name + " = (" + type.getName() +
")this." + name + ".clone();");
jsc.unindent();
}
}
}
jsc.add("return result;");
jsc.unindent();
jsc.add("} catch (CloneNotSupportedException e) {");
jsc.indent();
jsc.add("throw new RuntimeException(e.getMessage());");
jsc.unindent();
jsc.add("}");
}//createCloneMethod
/**
* Create a full-argument constructor on the given
* JClass
* @param jclass the Jclass in which we create the constructor
*/
public static void createFullConstructor(JClass jclass) {
if (jclass == null)
throw new IllegalArgumentException("JClass must not be null");
JField[] fields = jclass.getFields();
int paramCount = 0;
for (int i = 0; i <fields.length; i++) {
JField temp = fields[i];
if (!temp.isAuxiliary()) {
++paramCount;
}
}
JParameter[] params = new JParameter[paramCount];
for (int i = 0, p = 0; i <fields.length; i++) {
JField temp = fields[i];
String name = temp.getName();
// parameters are named without the leading underscore
String paramName = name.substring(1);
JType type = temp.getType();
if (!temp.isAuxiliary()) {
params[p++] = new JParameter(type, paramName);
}
}
JConstructor jConstructor = jclass.createConstructor(params);
JSourceCode jsc = jConstructor.getSourceCode();
for (int i = 0, p = 0; i <fields.length; i++) {
JField temp = fields[i];
String name = temp.getName();
// parameters are named without the leading underscore
String paramName = name.substring(1);
JType type = temp.getType();
if (!temp.isAuxiliary()) {
jsc.add("this." + name + " = " + paramName + ";");
if (type.isPrimitive()) {
jsc.add("this._has" + name + " = true;");
}
}
}
}//createFullConstructor
/**
* Create a copy constructor on the given
* JClass
* @param jclass the Jclass in which we create the constructor
*/
public static void createCopyConstructor(JClass jclass) {
if (jclass == null)
throw new IllegalArgumentException("JClass must not be null");
JField[] fields = jclass.getFields();
String paramName = jclass.getLocalName().toLowerCase();
JParameter[] params = new JParameter[]{
new JParameter(jclass, paramName),
new JParameter(JClass.Boolean, "deepCopy")
};
JConstructor jConstructor = jclass.createConstructor(params);
JSourceCode jsc = jConstructor.getSourceCode();
// determine whether we need to generate deep-copy code
boolean needDeepCopy = false;
for (int i = 0, p = 0; i < fields.length; i++) {
JType type = fields[i].getType();
// @todo need to figure out the immutable classes
if (!type.isPrimitive()
&& !type.getName().equals("java.lang.String")
&& !type.getName().equals("java.math.BigDecimal")
&& !type.getName().equals("byte")) {
needDeepCopy = true;
break;
}
}
if (needDeepCopy) {
jsc.add("if (deepCopy) {");
jsc.indent();
// this loop generate initialisation code for fields that are
being deep-copied
for (int i = 0, p = 0; i < fields.length; i++) {
JField temp = fields[i];
String name = temp.getName();
JType type = temp.getType();
//Collection needs a specific handling
if ( (type.getName().equals("java.util.Vector")) ||
(type.getName().equals("java.util.ArrayList")) ) {
// if we are dealing with a Vector or an ArrayList
// we retrieve the type included in this Collection
// - but there should be an easier way
int listLocat = name.lastIndexOf("List");
String tempName = name;
if (listLocat != -1)
tempName = tempName.substring(0,listLocat);
if (tempName.charAt(0) == '_')
tempName = tempName.substring(1);
String methodName = JavaNaming.toJavaClassName(tempName);
methodName = "get"+methodName;
JMethod method = jclass.getMethod(methodName,0);
//@todo handle the Item introduced in with the group handling
if (method == null)
continue;
String componentName = method.getReturnType().getName();
// for primitive (immutable) components, generate code like:
// this._xList =
(java.util.ArrayList)arg._xList.clone();
// @todo handle primitives better
if (componentName.equals("boolean")
|| componentName.equals("byte")
|| componentName.equals("int")
|| componentName.equals("long")
|| componentName.equals("float")
|| componentName.equals("double")
|| componentName.equals("java.lang.String")
||
componentName.equals("java.math.BigDecimal")) {
jsc.add("this." + name + " = (" + type
+ ")" + name + ".clone();");
} else {
// generate code like:
// this._xList = new
java.util.ArrayList(arg._xList.size());
// java.util.Iterator _xListIter
= arg._xList.iterator();
// while (_sListIter.hasNext()) {
// this._xList.add(new
X((X)_xListIter.next(), true));
// }
jsc.add("");
jsc.add("this." + name + " = new " + type + "(" +
paramName + "." + name + ".size());");
jsc.add("java.util.Iterator " + name + "Iter = " +
paramName + "." + name + ".iterator();");
jsc.add("while (" + name + "Iter.hasNext()) {");
jsc.indent();
// @todo need to determine which are
generated components, and which are not
jsc.add("this." + name + ".add(new " + componentName +
"((" + componentName + ")" + name + "Iter.next(), true));");
jsc.unindent();
jsc.add("}");
jsc.add("");
}
// @todo need to figure out the immutable classes
} else if (!type.isPrimitive()
&& !type.getName().equals("java.lang.String")
&& !type.getName().equals("java.math.BigDecimal")
&& !type.getName().equals("byte")) {
// generate code like:
// if (arg._x != null)
// this._x = new X(arg._x, true);
jsc.add("if (" + paramName + "." + name + " != null)");
jsc.indent();
jsc.add("this." + name + " = new " + type + "(" + paramName +
"." + name + ", true);");
jsc.unindent();
}
}
jsc.unindent();
jsc.add("} else {");
jsc.indent();
// this loop generate initialisation code for fields that
could be deep-copied but aren't
for (int i = 0, p = 0; i < fields.length; i++) {
JField temp = fields[i];
String name = temp.getName();
JType type = temp.getType();
if (!type.isPrimitive()
&& !type.getName().equals("java.lang.String")
&& !type.getName().equals("java.math.BigDecimal")
&& !type.getName().equals("byte")) {
// generate code like:
// this._x = arg._x;
jsc.add("this." + name + " = " + paramName + "." + name +
";");
}
}
jsc.unindent();
jsc.add("}");
}
// this loop generate initialisation code for fields that don't need
to be deep-copied
for (int i = 0, p = 0; i < fields.length; i++) {
JField temp = fields[i];
String name = temp.getName();
JType type = temp.getType();
if (type.isPrimitive()
|| type.getName().equals("java.lang.String")
|| type.getName().equals("java.math.BigDecimal")
|| type.getName().equals("byte")) {
// generate code like:
// this._x = arg._x;
jsc.add("this." + name + " = " + paramName + "." + name + ";");
}
}
}//createCopyConstructor
