package org.springframework.roo.addon.web.mvc.controller.addon.config; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.List; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.builder.ToStringBuilder; import org.springframework.roo.addon.jpa.addon.entity.JpaEntityMetadata; import org.springframework.roo.addon.layers.service.addon.ServiceMetadata; import org.springframework.roo.addon.web.mvc.controller.annotations.config.RooJsonMixin; import org.springframework.roo.classpath.PhysicalTypeIdentifier; import org.springframework.roo.classpath.PhysicalTypeIdentifierNamingUtils; import org.springframework.roo.classpath.PhysicalTypeMetadata; import org.springframework.roo.classpath.details.ClassOrInterfaceTypeDetails; import org.springframework.roo.classpath.details.ConstructorMetadata; import org.springframework.roo.classpath.details.ConstructorMetadataBuilder; 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.details.annotations.AnnotationMetadataBuilder; 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.SpringJavaType; import org.springframework.roo.model.SpringletsJavaType; import org.springframework.roo.project.LogicalPath; /** * Metadata for {@link RooJsonMixin}. * * @author Jose Manuel Vivó * @since 2.0 */ public class EntityDeserializerMetadata extends AbstractItdTypeDetailsProvidingMetadataItem { private static final JavaType JSON_COMPONENT = new JavaType( "org.springframework.boot.jackson.JsonComponent"); private static final String PROVIDES_TYPE_STRING = EntityDeserializerMetadata.class.getName(); private static final String PROVIDES_TYPE = MetadataIdentificationUtils .create(PROVIDES_TYPE_STRING); private static final JavaType JSON_PARSER = new JavaType("com.fasterxml.jackson.core.JsonParser"); private static final JavaType DESERIALIZATION_CONTEXT = new JavaType( "com.fasterxml.jackson.databind.DeserializationContext"); private static final JavaType OBJECT_CODEC = new JavaType( "com.fasterxml.jackson.core.ObjectCodec"); private static final JavaType JSON_NODE = new JavaType("com.fasterxml.jackson.databind.JsonNode"); private static final JavaType IO_EXCEPTION = new JavaType("java.io.IOException"); public static String createIdentifier(final JavaType javaType, final LogicalPath path) { return PhysicalTypeIdentifierNamingUtils.createIdentifier(PROVIDES_TYPE_STRING, javaType, path); } public static String createIdentifier(ClassOrInterfaceTypeDetails details) { final LogicalPath logicalPath = PhysicalTypeIdentifier.getPath(details.getDeclaredByMetadataId()); return createIdentifier(details.getType(), logicalPath); } 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 EntityDeserializerAnnotationValues annotationValues; private final JpaEntityMetadata entityMetadata; private ServiceMetadata serviceMetadata; private FieldMetadata serviceField; private FieldMetadata conversionServiceField; private ConstructorMetadata constructor; private MethodMetadata deserializeObjectMethod; /** * Constructor * * @param identifier the identifier for this item of metadata (required) * @param aspectName the Java type of the ITD (required) * @param governorPhysicalTypeMetadata the governor, which is expected to * contain a {@link ClassOrInterfaceTypeDetails} (required) * */ public EntityDeserializerMetadata(final String identifier, final JavaType aspectName, final PhysicalTypeMetadata governorPhysicalTypeMetadata, EntityDeserializerAnnotationValues annotationValues, JpaEntityMetadata entityMetadata, ServiceMetadata serviceMetadata) { super(identifier, aspectName, governorPhysicalTypeMetadata); this.annotationValues = annotationValues; this.entityMetadata = entityMetadata; this.serviceMetadata = serviceMetadata; // Add annotation ensureGovernorIsAnnotated(new AnnotationMetadataBuilder(JSON_COMPONENT)); // extends JsonObjectDeserializer<Entity> /* * Moved to java as there were compilation problems when Mixin * uses @JsonDeserialize(using=EntityDeserializer.class) annotation * (requires extend of JsonDeseralizer) */ // ensureGovernorExtends(JavaType.wrapperOf(JSON_OBJECT_DESERIALIZER, // entityMetadata.getDestination())); this.serviceField = getFieldFor(getId(), serviceMetadata.getDestination()); ensureGovernorHasField(new FieldMetadataBuilder(serviceField)); this.conversionServiceField = getFieldFor(getId(), SpringJavaType.CONVERSION_SERVICE); ensureGovernorHasField(new FieldMetadataBuilder(conversionServiceField)); this.constructor = getConstructor(getId(), serviceField, conversionServiceField); ensureGovernorHasConstructor(new ConstructorMetadataBuilder(constructor)); this.deserializeObjectMethod = getDeserializeMethod(); ensureGovernorHasMethod(new MethodMetadataBuilder(this.deserializeObjectMethod)); // Build the ITD itdTypeDetails = builder.build(); } private MethodMetadata getDeserializeMethod() { // Define methodName final JavaSymbolName methodName = new JavaSymbolName("deserializeObject"); // Adding parameter types List<AnnotatedJavaType> parameterTypes = new ArrayList<AnnotatedJavaType>(); parameterTypes.add(AnnotatedJavaType.convertFromJavaType(JSON_PARSER)); parameterTypes.add(AnnotatedJavaType.convertFromJavaType(DESERIALIZATION_CONTEXT)); parameterTypes.add(AnnotatedJavaType.convertFromJavaType(OBJECT_CODEC)); parameterTypes.add(AnnotatedJavaType.convertFromJavaType(JSON_NODE)); MethodMetadata existingMethod = getGovernorMethod(methodName, AnnotatedJavaType.convertFromAnnotatedJavaTypes(parameterTypes)); if (existingMethod != null) { return existingMethod; } // Adding parameter names final List<JavaSymbolName> parameterNames = new ArrayList<JavaSymbolName>(); parameterNames.add(new JavaSymbolName("jsonParser")); parameterNames.add(new JavaSymbolName("context")); parameterNames.add(new JavaSymbolName("codec")); parameterNames.add(new JavaSymbolName("tree")); // Adding annotations final List<AnnotationMetadataBuilder> annotations = new ArrayList<AnnotationMetadataBuilder>(); // Generate body InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder(); final JavaType idType = getTypeToUseAsIdentifier(entityMetadata.getCurrentIndentifierField().getFieldType()); // String idText = tree.asText(); bodyBuilder.appendFormalLine("String idText = tree.asText();"); // Long id = conversionService.convert(idText, Long.class); bodyBuilder.appendFormalLine("%s %s = %s.convert(idText, %s.class);", getNameOfJavaType(idType), entityMetadata.getCurrentIndentifierField().getFieldName(), conversionServiceField.getFieldName(), getNameOfJavaType(idType)); final String entityItemName = StringUtils.uncapitalize(entityMetadata.getDestination().getSimpleTypeName()); // Product product = productSercie.findOne(id); bodyBuilder.appendFormalLine("%s %s = %s.%s(%s);", getNameOfJavaType(entityMetadata.getDestination()), entityItemName, serviceField.getFieldName(), serviceMetadata.getCurrentFindOneMethod().getMethodName(), entityMetadata.getCurrentIndentifierField().getFieldName()); // if (product == null) { bodyBuilder.appendFormalLine("if (%s == null) {", entityItemName); // throw new NotFoundException("Product not found"); bodyBuilder.indent(); bodyBuilder.appendFormalLine("throw new %s(\"%s not found\");", getNameOfJavaType(SpringletsJavaType.SPRINGLETS_NOT_FOUND_EXCEPTION), entityMetadata .getDestination().getSimpleTypeName()); bodyBuilder.indentRemove(); // } bodyBuilder.appendFormalLine("}"); // return product; bodyBuilder.appendFormalLine("return %s;", entityItemName); MethodMetadataBuilder methodBuilder = new MethodMetadataBuilder(getId(), Modifier.PUBLIC, methodName, entityMetadata.getDestination(), parameterTypes, parameterNames, bodyBuilder); methodBuilder.setAnnotations(annotations); methodBuilder.addThrowsType(IO_EXCEPTION); return methodBuilder.build(); } private JavaType getTypeToUseAsIdentifier(JavaType type) { if (type.isPrimitive()) { if (JavaType.INT_PRIMITIVE.equals(type)) { return JavaType.INT_OBJECT; } else if (JavaType.LONG_PRIMITIVE.equals(type)) { return JavaType.LONG_OBJECT; } else if (JavaType.FLOAT_PRIMITIVE.equals(type)) { return JavaType.FLOAT_OBJECT; } else if (JavaType.DOUBLE_PRIMITIVE.equals(type)) { return JavaType.DOUBLE_OBJECT; } } return type; } /** * This method returns service field included on controller that it * represents the service spent as parameter * * @param service * Searched service * @return The field that represents the service spent as parameter */ public static FieldMetadata getFieldFor(String declaredByMetadataId, JavaType service) { // Generating service field name String fieldName = new JavaSymbolName(service.getSimpleTypeName()).getSymbolNameUnCapitalisedFirstLetter(); return new FieldMetadataBuilder(declaredByMetadataId, Modifier.PRIVATE, new ArrayList<AnnotationMetadataBuilder>(), new JavaSymbolName(fieldName), service).build(); } public static ConstructorMetadata getConstructor(String declaredByMetadataId, FieldMetadata serviceField, FieldMetadata conversionServiceField) { InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder(); // Generating constructor ConstructorMetadataBuilder constructor = new ConstructorMetadataBuilder(declaredByMetadataId); constructor.setModifier(Modifier.PUBLIC); constructor.addAnnotation(new AnnotationMetadataBuilder(SpringJavaType.AUTOWIRED)); // add Service to constructor String serviceFieldName = serviceField.getFieldName().getSymbolName(); AnnotatedJavaType serviceParameter = new AnnotatedJavaType(serviceField.getFieldType(), new AnnotationMetadataBuilder( SpringJavaType.LAZY).build()); constructor.addParameterName(serviceField.getFieldName()); constructor.addParameterType(serviceParameter); // Generating body bodyBuilder.appendFormalLine("this.%1$s = %1$s;", serviceFieldName); // add Conversion service to constructor String conversionServiceFieldName = conversionServiceField.getFieldName().getSymbolName(); constructor.addParameter(conversionServiceFieldName, conversionServiceField.getFieldType()); // Generating body bodyBuilder.appendFormalLine("this.%1$s = %1$s;", conversionServiceFieldName); // Adding body constructor.setBodyBuilder(bodyBuilder); return constructor.build(); } public JavaType getEntity() { return annotationValues.getEntity(); } @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(); } }