package org.exolab.castor.builder.factory;
import org.castor.xml.JavaNaming;
import org.exolab.castor.builder.AnnotationBuilder;
import org.exolab.castor.builder.info.FieldInfo;
import org.exolab.castor.builder.info.nature.XMLInfoNature;
import org.exolab.castor.builder.types.XSType;
import org.exolab.javasource.JClass;
import org.exolab.javasource.JDocComment;
import org.exolab.javasource.JDocDescriptor;
import org.exolab.javasource.JField;
import org.exolab.javasource.JMethod;
import org.exolab.javasource.JModifiers;
import org.exolab.javasource.JParameter;
import org.exolab.javasource.JPrimitiveType;
import org.exolab.javasource.JSourceCode;
import org.exolab.javasource.JType;
import org.exolab.javasource.Java5HacksHelper;
/**
* This factory takes a FieldInfo and generates the suitable JFields
* (and optional the getter and setter methods) into the JClass.
*/
public class FieldMemberAndAccessorFactory {
/**
* The {@link JavaNaming} to use.
*/
private JavaNaming _javaNaming;;
/**
* Creates a factory that offers public methods to create the
* field initialization code as well as the getter/setter methods.
*
* @param naming JavaNaming to use
*/
public FieldMemberAndAccessorFactory(final JavaNaming naming) {
_javaNaming = naming;
}
/**
* Creates the field initialization code in a constructor.
*
* @param fieldInfo the fieldInfo to translate
* @param jsc the JSourceCode in which to add the source to
*/
public void generateInitializerCode(final FieldInfo fieldInfo, final JSourceCode jsc) {
//set the default value
XMLInfoNature xmlNature = new XMLInfoNature(fieldInfo);
if (!xmlNature.getSchemaType().isPrimitive()) {
String value = fieldInfo.getDefaultValue();
boolean dateTime = xmlNature.getSchemaType().isDateTime();
if (value == null) {
value = fieldInfo.getFixedValue();
}
if (value != null) {
StringBuilder buffer = new StringBuilder(50);
//date/time constructors throw ParseException that
//needs to be catched in the constructor--> not the prettiest solution
//when mulitple date/time in a class.
if (dateTime) {
jsc.add("try {");
jsc.indent();
}
/*
* fieldInfo.getWriteMethodName() will either prefix the method
* with 'add' (for multivalued fields) or 'set'!
* @see FieldInfo#getWriteMethodeName()
*/
buffer.append(fieldInfo.getWriteMethodName());
//buffer.append(FieldInfo.METHOD_PREFIX_SET);
//buffer.append(fieldInfo.getMethodSuffix());
buffer.append('(');
buffer.append(value);
buffer.append(");");
jsc.add(buffer.toString());
if (dateTime) {
jsc.unindent();
jsc.add("} catch (java.text.ParseException pe) {");
jsc.indent();
jsc.add("throw new IllegalStateException(pe.getMessage());");
jsc.unindent();
jsc.add("}");
}
}
}
} //-- generateInitializerCode
/**
* Adds the suitable JField to the JClass.
*
* @param fieldInfo the fieldInfo to translate
* @param jClass the jclass the jField will be added to
*/
public final void createJavaField(final FieldInfo fieldInfo, final JClass jClass) {
XMLInfoNature xmlNature = new XMLInfoNature(fieldInfo);
XSType type = xmlNature.getSchemaType();
JType jType = type.getJType();
JField field = new JField(type.getJType(), fieldInfo.getName());
if (xmlNature.getSchemaType().isDateTime()) {
field.setDateTime(true);
}
if (fieldInfo.isStatic() || fieldInfo.isFinal()) {
JModifiers modifiers = field.getModifiers();
modifiers.setFinal(fieldInfo.isFinal());
modifiers.setStatic(fieldInfo.isStatic());
}
if (!(fieldInfo.getVisibility().equals("private"))) {
JModifiers modifiers = field.getModifiers();
if (fieldInfo.getVisibility().equals("protected")) {
modifiers.makeProtected();
} else if (fieldInfo.getVisibility().equals("public")) {
modifiers.makePublic();
}
}
//-- set init String
if (fieldInfo.getDefaultValue() != null) {
field.setInitString(fieldInfo.getDefaultValue());
}
if (fieldInfo.getFixedValue() != null && !xmlNature.getSchemaType().isDateTime()) {
field.setInitString(fieldInfo.getFixedValue());
}
//-- set Javadoc comment
if (fieldInfo.getComment() != null) {
field.setComment(fieldInfo.getComment());
}
jClass.addField(field);
//-- special supporting fields
//-- has_field
if ((!type.isEnumerated()) && (jType.isPrimitive())) {
field = new JField(JType.BOOLEAN, "_has" + fieldInfo.getName());
field.setComment("keeps track of state for field: " + fieldInfo.getName());
jClass.addField(field);
}
//-- save default value for primitives
//-- not yet finished
/*
if (type.isPrimitive()) {
field = new JField(jType, "_DEFAULT" + name.toUpperCase());
JModifiers modifiers = field.getModifiers();
modifiers.setFinal(true);
modifiers.setStatic(true);
if (_default != null)
field.setInitString(_default);
jClass.addField(field);
}
*/
} //-- createJavaField
/**
* Adds the getter/setter for this field to the jClass.
*
* @param fieldInfo the fieldInfo to translate
* @param jClass the jclass the jField will be added to
* @param useJava50 java version flag
*/
public void createAccessMethods(final FieldInfo fieldInfo,
final JClass jClass, final boolean useJava50, final AnnotationBuilder[] annotationBuilders) {
if ((fieldInfo.getMethods() & FieldInfo.READ_METHOD) > 0) {
createGetterMethod(fieldInfo, jClass, useJava50, annotationBuilders);
}
if ((fieldInfo.getMethods() & FieldInfo.WRITE_METHOD) > 0) {
createSetterMethod(fieldInfo, jClass, useJava50);
}
if (fieldInfo.requiresHasAndDeleteMethods()) {
createHasAndDeleteMethods(fieldInfo, jClass);
}
} //-- createAccessMethods
/**
* Creates the Javadoc comments for the getter method associated with this
* FieldInfo.
*
* @param fieldInfo the fieldInfo to translate
* @param jDocComment the JDocComment to add the Javadoc comments to.
*/
private void createGetterComment(final FieldInfo fieldInfo,
final JDocComment jDocComment) {
String fieldName = fieldInfo.getName();
//-- remove '_' if necessary
if (fieldName.indexOf('_') == 0) {
fieldName = fieldName.substring(1);
}
String mComment = "Returns the value of field '" + fieldName + "'.";
if ((fieldInfo.getComment() != null) && (fieldInfo.getComment().length() > 0)) {
mComment += " The field '" + fieldName + "' has the following description: ";
// XDoclet support - Add a couple newlines if it's a doclet tag
if (fieldInfo.getComment().startsWith("@")) {
mComment += "\n\n";
}
mComment += fieldInfo.getComment();
}
jDocComment.setComment(mComment);
} //-- createGetterComment
/**
* Creates the getter methods for this FieldInfo.
*
* @param fieldInfo the fieldInfo to translate
* @param jClass the JClass to add the methods to
* @param useJava50
* true if source code is supposed to be generated for Java 5
*/
private void createGetterMethod(final FieldInfo fieldInfo,
final JClass jClass, final boolean useJava50, AnnotationBuilder[] annotationBuilders) {
JMethod method = null;
JSourceCode jsc = null;
String mname = fieldInfo.getMethodSuffix();
XSType xsType = new XMLInfoNature(fieldInfo).getSchemaType();
JType jType = xsType.getJType();
//-- create get method
method = new JMethod(fieldInfo.getReadMethodName(), jType,
"the value of field '" + mname + "'.");
// if (useJava50) {
// Java5HacksHelper.addOverrideAnnotations(method.getSignature());
// }
for (int i = 0; i < annotationBuilders.length; i++) {
AnnotationBuilder annotationBuilder = annotationBuilders[i];
annotationBuilder.addFieldGetterAnnotations(fieldInfo, method);
}
jClass.addMethod(method);
createGetterComment(fieldInfo, method.getJDocComment());
jsc = method.getSourceCode();
jsc.add("return this.");
jsc.append(fieldInfo.getName());
jsc.append(";");
if (xsType.getType() == XSType.BOOLEAN_TYPE) {
// -- create is<Property>t method
method = new JMethod(fieldInfo.getIsMethodName(), jType,
"the value of field '" + mname + "'.");
// if (useJava50) {
// Java5HacksHelper.addOverrideAnnotations(method.getSignature());
// }
jClass.addMethod(method);
createGetterComment(fieldInfo, method.getJDocComment());
jsc = method.getSourceCode();
jsc.add("return this.");
jsc.append(fieldInfo.getName());
jsc.append(";");
}
} //-- createGetterMethod
/**
* Creates the "has" and "delete" methods for this field associated with
* this FieldInfo. These methods are typically only needed for primitive
* types which cannot be assigned a null value.
*
* @param fieldInfo the fieldInfo to translate
* @param jClass the JClass to add the methods to
*/
private void createHasAndDeleteMethods(final FieldInfo fieldInfo,
final JClass jClass) {
JMethod method = null;
JSourceCode jsc = null;
String mname = fieldInfo.getMethodSuffix();
XSType xsType = new XMLInfoNature(fieldInfo).getSchemaType();
xsType.getJType();
//-- create hasMethod
method = new JMethod(fieldInfo.getHasMethodName(), JType.BOOLEAN,
"true if at least one " + mname + " has been added");
jClass.addMethod(method);
jsc = method.getSourceCode();
jsc.add("return this._has");
String fieldName = fieldInfo.getName();
jsc.append(fieldName);
jsc.append(";");
//-- create delete method
method = new JMethod(fieldInfo.getDeleteMethodName());
jClass.addMethod(method);
jsc = method.getSourceCode();
jsc.add("this._has");
jsc.append(fieldName);
jsc.append("= false;");
//-- bound properties
if (fieldInfo.isBound()) {
//notify listeners
jsc.add("notifyPropertyChangeListeners(\"");
if (fieldName.startsWith("_")) {
jsc.append(fieldName.substring(1));
} else {
jsc.append(fieldName);
}
jsc.append("\", ");
//-- 'this.' ensures this refers to the class member not the parameter
jsc.append(xsType.createToJavaObjectCode("this." + fieldName));
jsc.append(", null");
jsc.append(");");
}
} //-- createHasAndDeleteMethods
/**
* Creates the Javadoc comments for the setter method associated with this
* FieldInfo.
*
* @param fieldInfo the fieldInfo to translate
* @param jDocComment the JDocComment to add the Javadoc comments to.
*/
private void createSetterComment(final FieldInfo fieldInfo,
final JDocComment jDocComment) {
String fieldName = fieldInfo.getName();
//-- remove '_' if necessary
if (fieldName.indexOf('_') == 0) {
fieldName = fieldName.substring(1);
}
String atParam = "the value of field '" + fieldName + "'.";
String mComment = "Sets " + atParam;
if ((fieldInfo.getComment() != null) && (fieldInfo.getComment().length() > 0)) {
mComment += " The field '" + fieldName + "' has the following description: ";
// XDoclet support - Add a couple newlines if it's a doclet tag
if (fieldInfo.getComment().startsWith("@")) {
mComment += "\n\n";
}
mComment += fieldInfo.getComment();
}
jDocComment.setComment(mComment);
JDocDescriptor paramDesc = jDocComment.getParamDescriptor(fieldName);
if (paramDesc == null) {
paramDesc = JDocDescriptor.createParamDesc(fieldName, null);
jDocComment.addDescriptor(paramDesc);
}
paramDesc.setDescription(atParam);
} //-- createSetterComment
/**
* Creates the setter (mutator) method(s) for this FieldInfo.
*
* @param fieldInfo the fieldInfo to translate
* @param jClass the JClass to add the methods to
* @param useJava50 true if source code is supposed to be generated for Java 5
*/
private void createSetterMethod(final FieldInfo fieldInfo,
final JClass jClass, final boolean useJava50) {
JMethod method = null;
JSourceCode jsc = null;
XMLInfoNature xmlNature = new XMLInfoNature(fieldInfo);
String mname = fieldInfo.getMethodSuffix();
XSType xsType = xmlNature.getSchemaType();
JType jType = xsType.getJType();
//-- create set method
/*
* fieldInfo.getWriteMethodName() will either prefix the method
* with 'add' (for multivalued fields) or 'set'!
* @see FieldInfo#getWriteMethodeName()
*/
method = new JMethod(fieldInfo.getWriteMethodName());
jClass.addMethod(method);
String paramName = fieldInfo.getName();
//-- make parameter name pretty,
//-- simply for aesthetic beauty
if (paramName.indexOf('_') == 0) {
String tempName = paramName.substring(1);
if (_javaNaming.isValidJavaIdentifier(tempName)) {
paramName = tempName;
}
}
method.addParameter(new JParameter(jType, paramName));
// if (useJava50) {
// Java5HacksHelper.addOverrideAnnotations(method.getSignature()); // DAB Java 5.0 hack
// }
createSetterComment(fieldInfo, method.getJDocComment());
jsc = method.getSourceCode();
String fieldName = fieldInfo.getName();
//-- bound properties
if (fieldInfo.isBound()) {
// save old value
jsc.add("java.lang.Object old");
jsc.append(mname);
jsc.append(" = ");
//-- 'this.' ensures this refers to the class member not the parameter
jsc.append(xsType.createToJavaObjectCode("this." + fieldName));
jsc.append(";");
}
//-- set new value
jsc.add("this.");
jsc.append(fieldName);
jsc.append(" = ");
jsc.append(paramName);
jsc.append(";");
if (fieldInfo.getFieldInfoReference() != null) {
jsc.add("this.");
jsc.append(fieldInfo.getFieldInfoReference().getName());
jsc.append(" = ");
JType referencedJType = new XMLInfoNature(fieldInfo.getFieldInfoReference()).getSchemaType().getJType();
if (referencedJType.isPrimitive()) {
jsc.append(paramName);
} else if (jType.isPrimitive()) {
JPrimitiveType primitive = (JPrimitiveType) jType;
jsc.append("new ");
jsc.append(primitive.getWrapperName());
jsc.append("(");
jsc.append(paramName);
jsc.append(")");
} else {
jsc.append(paramName);
}
jsc.append(";");
}
//-- hasProperty
if (fieldInfo.requiresHasAndDeleteMethods()) {
jsc.add("this._has");
jsc.append(fieldName);
jsc.append(" = true;");
}
//-- bound properties
if (fieldInfo.isBound()) {
//notify listeners
jsc.add("notifyPropertyChangeListeners(\"");
if (fieldName.startsWith("_")) {
jsc.append(fieldName.substring(1));
} else {
jsc.append(fieldName);
}
jsc.append("\", old");
jsc.append(mname);
jsc.append(", ");
//-- 'this.' ensures this refers to the class member not the parameter
jsc.append(xsType.createToJavaObjectCode("this." + fieldName));
jsc.append(");");
}
} //-- createSetterMethod
/**
* Returns the javaNaming.
*
* @return the javaNaming instance
*/
public JavaNaming getJavaNaming() {
return _javaNaming;
}
}