void (JDefinedClass jclass) { Map<String, JFieldVar> fields = jclass.fields(); JMethod toString = jclass.method(JMod.PUBLIC, String.class, "toString"); //noinspection SSBasedInspection Set<String> excludes = new HashSet<>(Arrays.asList(ruleFactory.getGenerationConfig().getToStringExcludes())); JBlock body = toString.body(); // The following toString implementation roughly matches the commons ToStringBuilder for // backward compatibility JClass stringBuilderClass = jclass.owner().ref(StringBuilder.class); JVar sb = body.decl(stringBuilderClass, "sb", JExpr._new(stringBuilderClass)); // Write the header, e.g.: example.domain.MyClass@85e382a7[ body.add(sb .invoke("append").arg(jclass.dotclass().invoke("getName")) .invoke("append").arg(JExpr.lit('@')) .invoke("append").arg( jclass.owner().ref(Integer.class).staticInvoke("toHexString").arg( jclass.owner().ref(System.class).staticInvoke("identityHashCode").arg(JExpr._this()))) .invoke("append").arg(JExpr.lit('['))); // If this has a parent class, include its toString() if (!jclass._extends().fullName().equals(Object.class.getName())) { JVar baseLength = body.decl(jclass.owner().INT, "baseLength", sb.invoke("length")); JVar superString = body.decl(jclass.owner().ref(String.class), "superString", JExpr._super().invoke("toString")); JBlock superToStringBlock = body._if(superString.ne(JExpr._null()))._then(); // If super.toString() is in the Clazz@2ee6529d[field=10] format, extract the fields // from the wrapper JVar contentStart = superToStringBlock.decl(jclass.owner().INT, "contentStart", superString.invoke("indexOf").arg(JExpr.lit('['))); JVar contentEnd = superToStringBlock.decl(jclass.owner().INT, "contentEnd", superString.invoke("lastIndexOf").arg(JExpr.lit(']'))); JConditional superToStringInnerConditional = superToStringBlock._if( contentStart.gte(JExpr.lit(0)).cand(contentEnd.gt(contentStart))); superToStringInnerConditional._then().add( sb.invoke("append") .arg(superString) .arg(contentStart.plus(JExpr.lit(1))) .arg(contentEnd)); // Otherwise, just append super.toString() superToStringInnerConditional._else().add(sb.invoke("append").arg(superString)); // Append a comma if needed body._if(sb.invoke("length").gt(baseLength)) ._then().add(sb.invoke("append").arg(JExpr.lit(','))); } // For each included instance field, add to the StringBuilder in the field=value format for (JFieldVar fieldVar : fields.values()) { if (excludes.contains(fieldVar.name()) || (fieldVar.mods().getValue() & JMod.STATIC) == JMod.STATIC) { continue; } body.add(sb.invoke("append").arg(fieldVar.name())); body.add(sb.invoke("append").arg(JExpr.lit('='))); if (fieldVar.type().isPrimitive()) { body.add(sb.invoke("append").arg(JExpr.refthis(fieldVar.name()))); } else if (fieldVar.type().isArray()) { // Only primitive arrays are supported if (!fieldVar.type().elementType().isPrimitive()) { throw new UnsupportedOperationException("Only primitive arrays are supported"); } // Leverage Arrays.toString() body.add(sb.invoke("append") .arg(JOp.cond( JExpr.refthis(fieldVar.name()).eq(JExpr._null()), JExpr.lit("<null>"), jclass.owner().ref(Arrays.class).staticInvoke("toString") .arg(JExpr.refthis(fieldVar.name())) .invoke("replace").arg(JExpr.lit('[')).arg(JExpr.lit('{')) .invoke("replace").arg(JExpr.lit(']')).arg(JExpr.lit('}')) .invoke("replace").arg(JExpr.lit(", ")).arg(JExpr.lit(","))))); } else { body.add(sb.invoke("append") .arg(JOp.cond( JExpr.refthis(fieldVar.name()).eq(JExpr._null()), JExpr.lit("<null>"), JExpr.refthis(fieldVar.name())))); } body.add(sb.invoke("append").arg(JExpr.lit(','))); } // Add the trailer JConditional trailerConditional = body._if( sb.invoke("charAt").arg(sb.invoke("length").minus(JExpr.lit(1))) .eq(JExpr.lit(','))); trailerConditional._then().add( sb.invoke("setCharAt") .arg(sb.invoke("length").minus(JExpr.lit(1))) .arg(JExpr.lit(']'))); trailerConditional._else().add( sb.invoke("append").arg(JExpr.lit(']'))); body._return(sb.invoke("toString")); toString.annotate(Override.class); }