Reputation: 5356
I am attempting to generate Java Value Objects using com.sun.codemodel.JCodeModel
.
I have managed to generate hashcode()
and equals()
methods but I am struggling with toString()
;
I require the following toString()
implementation
return "ClassName [field1 = " + field1 + ", field2 = " + field2 ... ", fieldN = " + fieldN + "]";
How do I create a JCodeModel
JExpression
that contains JExpr.lit(field1.name())
concatenated with JExpr.ref(fieldVar.name())
?
All i've managed to do is generate a string literal resembling:-
return "ClassName [field1 = field1 + field2 = field2 ... fieldN = + fieldN + ]";
Heres my skeleton method so far:-
final Map<String, JFieldVar> fields = jclass.fields();
final JMethod toString = jclass.method(JMod.PUBLIC, String.class, "toString");
final Set<String> excludes = new HashSet<String>(Arrays.asList(ruleFactory.getGenerationConfig().getToStringExcludes()));
final JBlock body = toString.body();
for (JFieldVar fieldVar : fields.values()) {
if (excludes.contains(fieldVar.name()) || ((fieldVar.mods().getValue() & JMod.STATIC) == JMod.STATIC)) {
continue;
}
??????????????
}
body._return(?????????);
toString.annotate(Override.class);
Upvotes: 2
Views: 780
Reputation: 54639
The key point here is likely that you can combine multiple JExpression
objects with the +
operator by using the JExpression#plus
method.
Here is an example that contains the definition of a simple example class, and a method to generically generate the toString
method:
import java.util.Arrays;
import java.util.Collection;
import java.util.Map;
import com.sun.codemodel.CodeWriter;
import com.sun.codemodel.JBlock;
import com.sun.codemodel.JCodeModel;
import com.sun.codemodel.JDefinedClass;
import com.sun.codemodel.JExpr;
import com.sun.codemodel.JExpression;
import com.sun.codemodel.JFieldVar;
import com.sun.codemodel.JMethod;
import com.sun.codemodel.JMod;
import com.sun.codemodel.writer.SingleStreamCodeWriter;
public class CodeModelToStringTest
{
public static void main(String[] args) throws Exception
{
JCodeModel codeModel = new JCodeModel();
JDefinedClass definedClass = codeModel._class("com.example.Example");
definedClass.field(JMod.PUBLIC, String.class, "exampleString");
definedClass.field(JMod.PROTECTED, int.class, "exampleInt");
definedClass.field(JMod.PRIVATE, float.class, "exampleFloat");
definedClass.field(JMod.PRIVATE, String.class, "excludedString");
definedClass.field(JMod.STATIC, String.class, "staticString");
createToStringMethod(definedClass, Arrays.asList("excludedString"));
CodeWriter codeWriter = new SingleStreamCodeWriter(System.out);
codeModel.build(codeWriter);
}
private static void createToStringMethod(
JDefinedClass definedClass,
Collection<String> excludedFieldNames)
{
Map<String, JFieldVar> fields = definedClass.fields();
JMethod toString =
definedClass.method(JMod.PUBLIC, String.class, "toString");
toString.annotate(Override.class);
JBlock body = toString.body();
JExpression expression = JExpr.lit(definedClass.name() + " [");
boolean first = true;
for (JFieldVar fieldVar : fields.values())
{
if ((fieldVar.mods().getValue() & JMod.STATIC) == JMod.STATIC)
{
continue;
}
if (excludedFieldNames.contains(fieldVar.name()))
{
continue;
}
if (!first)
{
expression = expression.plus(JExpr.lit(", "));
}
expression = expression.plus(JExpr.lit(fieldVar.name()+" = "));
expression = expression.plus(JExpr.ref(fieldVar.name()));
first = false;
}
expression = expression.plus(JExpr.lit("]"));
body._return(expression);
}
}
The generated class with the toString
method is shown here:
package com.example;
public class Example {
public String exampleString;
protected int exampleInt;
private float exampleFloat;
private String excludedString;
static String staticString;
@Override
public String toString() {
return ((((((((("Example ["+"exampleString = ")+ exampleString)+", ")+"exampleInt = ")+ exampleInt)+", ")+"exampleFloat = ")+ exampleFloat)+"]");
}
}
The fact that CodeModel inserts (
brackets )
around each binary operation causes the code to not look so pretty. But it's understandable: Otherwise, they would have to take operator precedences into account, and the usage and the code generation itself would likely be far harder.
However, the result of this toString
method will be
Example [exampleString = null, exampleInt = 0, exampleFloat = 0.0]
which should be what you expected, based on your examples.
Upvotes: 4