package org.springframework.roo.addon.javabean.addon; import static org.springframework.roo.model.JavaType.STRING; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.builder.ToStringBuilder; import org.springframework.roo.addon.javabean.annotations.RooToString; import org.springframework.roo.classpath.PhysicalTypeIdentifierNamingUtils; import org.springframework.roo.classpath.PhysicalTypeMetadata; import org.springframework.roo.classpath.details.FieldMetadata; import org.springframework.roo.classpath.details.MethodMetadataBuilder; import org.springframework.roo.classpath.itd.AbstractItdTypeDetailsProvidingMetadataItem; import org.springframework.roo.classpath.itd.InvocableMemberBodyBuilder; import org.springframework.roo.metadata.MetadataIdentificationUtils; import org.springframework.roo.model.JavaSymbolName; import org.springframework.roo.model.JavaType; import org.springframework.roo.model.JdkJavaType; import org.springframework.roo.model.JpaJavaType; import org.springframework.roo.project.LogicalPath; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.List; /** * Metadata for {@link RooToString}. * * @author Ben Alex * @since 1.0 */ public class ToStringMetadata extends AbstractItdTypeDetailsProvidingMetadataItem { private static final String PROVIDES_TYPE_STRING = ToStringMetadata.class.getName(); private static final String PROVIDES_TYPE = MetadataIdentificationUtils .create(PROVIDES_TYPE_STRING); public static String createIdentifier(final JavaType javaType, final LogicalPath path) { return PhysicalTypeIdentifierNamingUtils.createIdentifier(PROVIDES_TYPE_STRING, javaType, path); } public static JavaType getJavaType(final String metadataIdentificationString) { return PhysicalTypeIdentifierNamingUtils.getJavaType(PROVIDES_TYPE_STRING, metadataIdentificationString); } public static String getMetadataIdentiferType() { return PROVIDES_TYPE; } public static LogicalPath getPath(final String metadataIdentificationString) { return PhysicalTypeIdentifierNamingUtils.getPath(PROVIDES_TYPE_STRING, metadataIdentificationString); } public static boolean isValid(final String metadataIdentificationString) { return PhysicalTypeIdentifierNamingUtils.isValid(PROVIDES_TYPE_STRING, metadataIdentificationString); } private final ToStringAnnotationValues annotationValues; private final List<FieldMetadata> fields; /** * Constructor * * @param identifier * @param aspectName * @param governorPhysicalTypeMetadata * @param annotationValues */ public ToStringMetadata(final String identifier, final JavaType aspectName, final PhysicalTypeMetadata governorPhysicalTypeMetadata, final ToStringAnnotationValues annotationValues, List<FieldMetadata> fields) { super(identifier, aspectName, governorPhysicalTypeMetadata); Validate.isTrue(isValid(identifier), "Metadata identification string '%s' does not appear to be a valid", identifier); Validate.notNull(annotationValues, "Annotation values required"); this.annotationValues = annotationValues; this.fields = fields; // Generate the toString() method builder.addMethod(getToStringMethod()); // Create a representation of the desired output ITD itdTypeDetails = builder.build(); } /** * Obtains the "toString" method for this type, if available. * <p> * If the user provided a non-default name for "toString", that method will * be returned. * * @return the "toString" method declared on this type or that will be * introduced (or null if undeclared and not introduced) */ private MethodMetadataBuilder getToStringMethod() { final String toStringMethod = annotationValues.getToStringMethod(); if (StringUtils.isBlank(toStringMethod)) { return null; } // Compute the relevant toString method name final JavaSymbolName methodName = new JavaSymbolName(toStringMethod); // See if the type itself declared the method if (governorHasMethod(methodName)) { return null; } // Get excludeFields attribute value final String[] excludeFields = annotationValues.getExcludeFields(); // Get all fields from class List<FieldMetadata> affectedFields = new ArrayList<FieldMetadata>(); for (FieldMetadata field : fields) { // Exclude non-common java. fields if (!field.getFieldType().getFullyQualifiedTypeName().startsWith("java.math") && !field.getFieldType().getFullyQualifiedTypeName().startsWith("java.lang") && !field.getFieldType().getFullyQualifiedTypeName().startsWith("java.util")) { continue; } // Exclude List/Set fields, even if they don't have relations if (field.getFieldType().equals(JdkJavaType.LIST) || field.getFieldType().equals(JdkJavaType.SET) || field.getFieldType().equals(JdkJavaType.MAP)) { continue; } // Exclude fields with relations from toString generation if (field.getAnnotation(JpaJavaType.MANY_TO_MANY) != null || field.getAnnotation(JpaJavaType.MANY_TO_ONE) != null || field.getAnnotation(JpaJavaType.ONE_TO_MANY) != null || field.getAnnotation(JpaJavaType.ONE_TO_ONE) != null) { continue; } // Check if field must be excluded manually by "excludeFields" attribute boolean exclude = false; if (excludeFields != null && excludeFields.length > 0) { for (int i = 0; i < excludeFields.length; i++) { if (excludeFields[i].equals(field.getFieldName().getSymbolName())) { exclude = true; } } } // Exclude field if necessary if (!exclude) { affectedFields.add(field); } } final InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder(); // Build toString method body // return "Entity {" + "fieldName1='" + fieldName1 + '\'' + ", fieldName2='" + fieldName2 + '\''+ "}" + super.toString(); bodyBuilder.appendFormalLine(String.format("return \"%s {\" + ", governorTypeDetails.getType() .getSimpleTypeName())); for (int i = 0; i < affectedFields.size(); i++) { bodyBuilder.appendIndent(); StringBuffer fieldString = new StringBuffer(); fieldString.append("\""); if (i != 0) { fieldString.append(", "); } fieldString.append(affectedFields.get(i).getFieldName()).append("='\"").append(" + ") .append(affectedFields.get(i).getFieldName()).append(" + '\\''").append(" + "); if (i == affectedFields.size() - 1) { fieldString.append("\"}\" + ").append("super.toString();"); } // Append next field line bodyBuilder.appendFormalLine(fieldString.toString()); } if (affectedFields.isEmpty()) { bodyBuilder.appendFormalLine("\"}\" + super.toString();"); } return new MethodMetadataBuilder(getId(), Modifier.PUBLIC, methodName, STRING, bodyBuilder); } @Override public String toString() { final ToStringBuilder builder = new ToStringBuilder(this); builder.append("identifier", getId()); builder.append("valid", valid); builder.append("aspectName", aspectName); builder.append("destinationType", destination); builder.append("governor", governorPhysicalTypeMetadata.getId()); builder.append("itdTypeDetails", itdTypeDetails); return builder.toString(); } }