package org.springframework.roo.addon.property.editor; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.builder.ToStringBuilder; 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.FieldMetadataBuilder; import org.springframework.roo.classpath.details.MethodMetadata; import org.springframework.roo.classpath.details.MethodMetadataBuilder; import org.springframework.roo.classpath.details.annotations.AnnotatedJavaType; 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.SpringJavaType; import org.springframework.roo.project.LogicalPath; /** * Metadata for {@link RooEditor}. * * @author Stefan Schmidt * @since 1.0 */ public class EditorMetadata extends AbstractItdTypeDetailsProvidingMetadataItem { private static final String PROVIDES_TYPE_STRING = EditorMetadata.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); } public EditorMetadata(final String identifier, final JavaType aspectName, final PhysicalTypeMetadata governorPhysicalTypeMetadata, final JavaType javaType, final JavaType idType, final MethodMetadata identifierAccessorMethod, final MethodMetadata findMethod) { super(identifier, aspectName, governorPhysicalTypeMetadata); Validate.isTrue(isValid(identifier), "Metadata identification string '" + identifier + "' does not appear to be a valid"); Validate.notNull(javaType, "Java type required"); Validate.notNull(idType, "Identifier field metadata required"); Validate.notNull(identifierAccessorMethod, "Identifier accessor metadata required"); if (!isValid() || findMethod == null) { valid = false; return; } // Only make the ITD cause PropertyEditorSupport to be subclasses if the // governor doesn't already subclass it final JavaType requiredSuperclass = JdkJavaType.PROPERTY_EDITOR_SUPPORT; if (!governorTypeDetails.extendsType(requiredSuperclass)) { builder.addImplementsType(requiredSuperclass); } builder.addField(getField()); builder.addMethod(getGetAsTextMethod(javaType, identifierAccessorMethod)); builder.addMethod(getSetAsTextMethod(javaType, idType, findMethod)); // Create a representation of the desired output ITD itdTypeDetails = builder.build(); } private FieldMetadataBuilder getField() { final JavaSymbolName fieldName = new JavaSymbolName("typeConverter"); // Locate user-defined field final FieldMetadata userField = governorTypeDetails.getField(fieldName); final JavaType fieldType = SpringJavaType.SIMPLE_TYPE_CONVERTER; if (userField != null) { Validate.isTrue( userField.getFieldType().equals(fieldType), "Field '" + fieldName + "' on '" + destination + "' must be of type '" + fieldType.getNameIncludingTypeParameters() + "'"); return new FieldMetadataBuilder(userField); } return new FieldMetadataBuilder(getId(), Modifier.PRIVATE, fieldName, fieldType, "new " + fieldType + "()"); } private MethodMetadataBuilder getGetAsTextMethod(final JavaType javaType, final MethodMetadata identifierAccessorMethod) { final JavaType returnType = JavaType.STRING; final JavaSymbolName methodName = new JavaSymbolName("getAsText"); final JavaType[] parameterTypes = {}; // Locate user-defined method final MethodMetadata userMethod = getGovernorMethod(methodName, parameterTypes); if (userMethod != null) { Validate.isTrue( userMethod.getReturnType().equals(returnType), "Method '" + methodName + "' on '" + destination + "' must return '" + returnType.getNameIncludingTypeParameters() + "'"); return new MethodMetadataBuilder(userMethod); } final List<JavaSymbolName> parameterNames = new ArrayList<JavaSymbolName>(); final InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder(); bodyBuilder.appendFormalLine("Object obj = getValue();"); bodyBuilder.appendFormalLine("if (obj == null) {"); bodyBuilder.indent(); bodyBuilder.appendFormalLine("return null;"); bodyBuilder.indentRemove(); bodyBuilder.appendFormalLine("}"); bodyBuilder .appendFormalLine("return (String) typeConverter.convertIfNecessary(((" + javaType.getNameIncludingTypeParameters(false, builder.getImportRegistrationResolver()) + ") obj)." + identifierAccessorMethod.getMethodName() + "(), String.class);"); return new MethodMetadataBuilder(getId(), Modifier.PUBLIC, methodName, returnType, AnnotatedJavaType.convertFromJavaTypes(parameterTypes), parameterNames, bodyBuilder); } private MethodMetadataBuilder getSetAsTextMethod(final JavaType javaType, final JavaType idType, final MethodMetadata findMethod) { final JavaType parameterType = JavaType.STRING; final List<JavaSymbolName> parameterNames = Arrays .asList(new JavaSymbolName("text")); final JavaSymbolName methodName = new JavaSymbolName("setAsText"); final JavaType returnType = JavaType.VOID_PRIMITIVE; // Locate user-defined method final MethodMetadata userMethod = getGovernorMethod(methodName, parameterType); if (userMethod != null) { Validate.isTrue( userMethod.getReturnType().equals(returnType), "Method '" + methodName + "' on '" + destination + "' must return '" + returnType.getNameIncludingTypeParameters() + "'"); return new MethodMetadataBuilder(userMethod); } final String identifierTypeName = idType .getNameIncludingTypeParameters(false, builder.getImportRegistrationResolver()); final InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder(); bodyBuilder .appendFormalLine("if (text == null || 0 == text.length()) {"); bodyBuilder.indent(); bodyBuilder.appendFormalLine("setValue(null);"); bodyBuilder.appendFormalLine("return;"); bodyBuilder.indentRemove(); bodyBuilder.appendFormalLine("}"); bodyBuilder.newLine(); bodyBuilder.appendFormalLine(identifierTypeName + " identifier = (" + identifierTypeName + ") typeConverter.convertIfNecessary(text, " + identifierTypeName + ".class);"); bodyBuilder.appendFormalLine("if (identifier == null) {"); bodyBuilder.indent(); bodyBuilder.appendFormalLine("setValue(null);"); bodyBuilder.appendFormalLine("return;"); bodyBuilder.indentRemove(); bodyBuilder.appendFormalLine("}"); bodyBuilder.newLine(); bodyBuilder.appendFormalLine("setValue(" + javaType.getNameIncludingTypeParameters(false, builder.getImportRegistrationResolver()) + "." + findMethod.getMethodName() + "(identifier));"); return new MethodMetadataBuilder(getId(), Modifier.PUBLIC, methodName, returnType, AnnotatedJavaType.convertFromJavaTypes(parameterType), parameterNames, 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(); } }