package org.springframework.roo.addon.web.mvc.controller.addon.config;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.roo.addon.jpa.addon.entity.JpaEntityMetadata;
import org.springframework.roo.addon.jpa.addon.entity.JpaEntityMetadata.RelationInfo;
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.FieldMetadata;
import org.springframework.roo.classpath.details.FieldMetadataBuilder;
import org.springframework.roo.classpath.details.annotations.AnnotationMetadataBuilder;
import org.springframework.roo.classpath.itd.AbstractItdTypeDetailsProvidingMetadataItem;
import org.springframework.roo.classpath.operations.Cardinality;
import org.springframework.roo.metadata.MetadataIdentificationUtils;
import org.springframework.roo.model.JavaSymbolName;
import org.springframework.roo.model.JavaType;
import org.springframework.roo.project.LogicalPath;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
/**
* Metadata for {@link RooJsonMixin}.
*
* @author Jose Manuel Vivó
* @since 2.0
*/
public class JSONMixinMetadata extends AbstractItdTypeDetailsProvidingMetadataItem {
private static final JavaType JSON_DESERIALIZE = new JavaType(
"com.fasterxml.jackson.databind.annotation.JsonDeserialize");
private static final JavaType JSON_IDENTITY_INFO = new JavaType(
"com.fasterxml.jackson.annotation.JsonIdentityInfo");
private static final JavaType JSON_IGNORE = new JavaType(
"com.fasterxml.jackson.annotation.JsonIgnore");
private static final JavaType OBJECT_ID_GENERATORS_PROPERTY_GENERATOR = new JavaType(
"com.fasterxml.jackson.annotation.ObjectIdGenerators.PropertyGenerator");
private static final String PROVIDES_TYPE_STRING = JSONMixinMetadata.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 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 JSONMixinAnnotationValues annotationValues;
private final JpaEntityMetadata entityMetadata;
private JavaSymbolName propertyIdGenerator;
private final Map<FieldMetadata, JavaType> jsonDeserializerByField;
/**
* 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)
* @param annotationValues
* @param entityMetadata
* @param jsonDeserializerByEntity
*/
public JSONMixinMetadata(final String identifier, final JavaType aspectName,
final PhysicalTypeMetadata governorPhysicalTypeMetadata,
JSONMixinAnnotationValues annotationValues, JpaEntityMetadata entityMetadata,
Map<FieldMetadata, JavaType> jsonDeserializerByField) {
super(identifier, aspectName, governorPhysicalTypeMetadata);
this.annotationValues = annotationValues;
this.entityMetadata = entityMetadata;
this.jsonDeserializerByField = jsonDeserializerByField;
// Add @JsonIdentityInfo if has @OneToOne relation
for (RelationInfo info : entityMetadata.getRelationInfos().values()) {
if (info.cardinality == Cardinality.ONE_TO_ONE) {
this.propertyIdGenerator = entityMetadata.getCurrentIndentifierField().getFieldName();
AnnotationMetadataBuilder identifyInfo = new AnnotationMetadataBuilder(JSON_IDENTITY_INFO);
identifyInfo.addClassAttribute("generator", OBJECT_ID_GENERATORS_PROPERTY_GENERATOR);
identifyInfo.addStringAttribute("property", this.propertyIdGenerator.getSymbolName());
ensureGovernorIsAnnotated(identifyInfo);
break;
}
}
// Add ignore properties
for (RelationInfo info : entityMetadata.getRelationInfos().values()) {
if (info.cardinality == Cardinality.ONE_TO_MANY
|| info.cardinality == Cardinality.MANY_TO_MANY) {
ensureGovernorHasField(getIgnoreFieldFor(info.fieldMetadata));
}
}
// Add deserializers
for (Entry<FieldMetadata, JavaType> entry : jsonDeserializerByField.entrySet()) {
ensureGovernorHasField(getDeseralizerFieldFor(entry.getKey(), entry.getValue()));
}
// Build the ITD
itdTypeDetails = builder.build();
}
private FieldMetadataBuilder getDeseralizerFieldFor(FieldMetadata field, JavaType deserializer) {
AnnotationMetadataBuilder annotation = new AnnotationMetadataBuilder(JSON_DESERIALIZE);
annotation.addClassAttribute("using", deserializer);
return new FieldMetadataBuilder(getId(), Modifier.PRIVATE, Arrays.asList(annotation),
field.getFieldName(), field.getFieldType());
}
private FieldMetadataBuilder getIgnoreFieldFor(FieldMetadata field) {
return new FieldMetadataBuilder(getId(), Modifier.PRIVATE,
Arrays.asList(new AnnotationMetadataBuilder(JSON_IGNORE)), field.getFieldName(),
field.getFieldType());
}
public JavaType getEntity() {
return annotationValues.getEntity();
}
public JavaSymbolName getPropertyIdGenerator() {
return propertyIdGenerator;
}
@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();
}
}