package org.springframework.roo.addon.json; import static org.springframework.roo.model.JavaType.STRING; import static org.springframework.roo.model.JdkJavaType.ARRAY_LIST; import static org.springframework.roo.model.JdkJavaType.COLLECTION; import static org.springframework.roo.model.JdkJavaType.LIST; import java.lang.reflect.Modifier; import java.util.Arrays; import java.util.Collection; import java.util.List; import org.apache.commons.lang3.StringUtils; 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.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.DataType; import org.springframework.roo.model.JavaSymbolName; import org.springframework.roo.model.JavaType; import org.springframework.roo.project.LogicalPath; /** * Metadata to be triggered by {@link RooJson} annotation * * @author Stefan Schmidt * @since 1.1 */ public class JsonMetadata extends AbstractItdTypeDetailsProvidingMetadataItem { private static final JavaType JSON_DESERIALIZER = new JavaType( "flexjson.JSONDeserializer"); private static final JavaType JSON_SERIALIZER = new JavaType( "flexjson.JSONSerializer"); private static final String PROVIDES_TYPE_STRING = JsonMetadata.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 JsonAnnotationValues annotationValues; private String typeNamePlural; public JsonMetadata(final String identifier, final JavaType aspectName, final PhysicalTypeMetadata governorPhysicalTypeMetadata, final String typeNamePlural, final JsonAnnotationValues annotationValues) { super(identifier, aspectName, governorPhysicalTypeMetadata); Validate.notNull(annotationValues, "Annotation values required"); Validate.notBlank(typeNamePlural, "Plural of the target type required"); Validate.isTrue(isValid(identifier), "Metadata identification string '" + identifier + "' does not appear to be a valid"); if (!isValid()) { return; } this.annotationValues = annotationValues; this.typeNamePlural = typeNamePlural; builder.addMethod(getToJsonMethod()); builder.addMethod(getFromJsonMethod()); builder.addMethod(getToJsonArrayMethod()); builder.addMethod(getFromJsonArrayMethod()); // Create a representation of the desired output ITD itdTypeDetails = builder.build(); } private MethodMetadataBuilder getFromJsonArrayMethod() { // Compute the relevant method name final JavaSymbolName methodName = getFromJsonArrayMethodName(); if (methodName == null) { return null; } final JavaType parameterType = JavaType.STRING; if (governorHasMethod(methodName, parameterType)) { return null; } final String list = LIST.getNameIncludingTypeParameters(false, builder.getImportRegistrationResolver()); final String arrayList = ARRAY_LIST.getNameIncludingTypeParameters( false, builder.getImportRegistrationResolver()); final String bean = destination.getSimpleTypeName(); final InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder(); final String deserializer = JSON_DESERIALIZER .getNameIncludingTypeParameters(false, builder.getImportRegistrationResolver()); bodyBuilder.appendFormalLine("return new " + deserializer + "<" + list + "<" + bean + ">>().use(null, " + arrayList + ".class).use(\"values\", " + bean + ".class).deserialize(json);"); final List<JavaSymbolName> parameterNames = Arrays .asList(new JavaSymbolName("json")); final JavaType collection = new JavaType( COLLECTION.getFullyQualifiedTypeName(), 0, DataType.TYPE, null, Arrays.asList(destination)); final MethodMetadataBuilder methodBuilder = new MethodMetadataBuilder( getId(), Modifier.PUBLIC | Modifier.STATIC, methodName, collection, AnnotatedJavaType.convertFromJavaTypes(parameterType), parameterNames, bodyBuilder); methodBuilder.putCustomData(CustomDataJsonTags.FROM_JSON_ARRAY_METHOD, null); return methodBuilder; } public JavaSymbolName getFromJsonArrayMethodName() { final String methodLabel = annotationValues.getFromJsonArrayMethod(); if (StringUtils.isBlank(methodLabel)) { return null; } return new JavaSymbolName(methodLabel.replace("<TypeNamePlural>", typeNamePlural)); } private MethodMetadataBuilder getFromJsonMethod() { final JavaSymbolName methodName = getFromJsonMethodName(); if (methodName == null) { return null; } final JavaType parameterType = JavaType.STRING; if (governorHasMethod(methodName, parameterType)) { return null; } final InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder(); final String deserializer = JSON_DESERIALIZER .getNameIncludingTypeParameters(false, builder.getImportRegistrationResolver()); bodyBuilder.appendFormalLine("return new " + deserializer + "<" + destination.getSimpleTypeName() + ">().use(null, " + destination.getSimpleTypeName() + ".class).deserialize(json);"); final List<JavaSymbolName> parameterNames = Arrays .asList(new JavaSymbolName("json")); final MethodMetadataBuilder methodBuilder = new MethodMetadataBuilder( getId(), Modifier.PUBLIC | Modifier.STATIC, methodName, destination, AnnotatedJavaType.convertFromJavaTypes(parameterType), parameterNames, bodyBuilder); methodBuilder.putCustomData(CustomDataJsonTags.FROM_JSON_METHOD, null); return methodBuilder; } public JavaSymbolName getFromJsonMethodName() { final String methodLabel = annotationValues.getFromJsonMethod(); if (StringUtils.isBlank(methodLabel)) { return null; } // Compute the relevant method name return new JavaSymbolName(methodLabel.replace("<TypeName>", destination.getSimpleTypeName())); } private MethodMetadataBuilder getToJsonArrayMethod() { // Compute the relevant method name final JavaSymbolName methodName = getToJsonArrayMethodName(); if (methodName == null) { return null; } final JavaType parameterType = new JavaType(Collection.class.getName(), 0, DataType.TYPE, null, Arrays.asList(destination)); // See if the type itself declared the method if (governorHasMethod(methodName, parameterType)) { return null; } final List<JavaSymbolName> parameterNames = Arrays .asList(new JavaSymbolName("collection")); final InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder(); final String serializer = JSON_SERIALIZER .getNameIncludingTypeParameters(false, builder.getImportRegistrationResolver()); final String root = annotationValues.getRootName() != null && annotationValues.getRootName().length() > 0 ? ".rootName(\"" + annotationValues.getRootName() + "\")" : ""; bodyBuilder .appendFormalLine("return new " + serializer + "()" + root + ".exclude(\"*.class\")" + (annotationValues.isDeepSerialize() ? ".deepSerialize(collection)" : ".serialize(collection)") + ";"); final MethodMetadataBuilder methodBuilder = new MethodMetadataBuilder( getId(), Modifier.PUBLIC | Modifier.STATIC, methodName, STRING, AnnotatedJavaType.convertFromJavaTypes(parameterType), parameterNames, bodyBuilder); methodBuilder.putCustomData(CustomDataJsonTags.TO_JSON_ARRAY_METHOD, null); return methodBuilder; } public JavaSymbolName getToJsonArrayMethodName() { final String methodLabel = annotationValues.getToJsonArrayMethod(); if (StringUtils.isBlank(methodLabel)) { return null; } return new JavaSymbolName(methodLabel); } private MethodMetadataBuilder getToJsonMethod() { // Compute the relevant method name final JavaSymbolName methodName = getToJsonMethodName(); if (methodName == null) { return null; } // See if the type itself declared the method if (governorHasMethod(methodName)) { return null; } final InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder(); final String serializer = JSON_SERIALIZER .getNameIncludingTypeParameters(false, builder.getImportRegistrationResolver()); final String root = annotationValues.getRootName() != null && annotationValues.getRootName().length() > 0 ? ".rootName(\"" + annotationValues.getRootName() + "\")" : ""; bodyBuilder.appendFormalLine("return new " + serializer + "()" + root + ".exclude(\"*.class\")" + (annotationValues.isDeepSerialize() ? ".deepSerialize(this)" : ".serialize(this)") + ";"); final MethodMetadataBuilder methodBuilder = new MethodMetadataBuilder( getId(), Modifier.PUBLIC, methodName, STRING, bodyBuilder); methodBuilder.putCustomData(CustomDataJsonTags.TO_JSON_METHOD, null); return methodBuilder; } public JavaSymbolName getToJsonMethodName() { final String methodLabel = annotationValues.getToJsonMethod(); if (StringUtils.isBlank(methodLabel)) { return null; } return new JavaSymbolName(methodLabel); } @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(); } }