package org.springframework.roo.addon.web.mvc.controller.scaffold;
import static org.springframework.roo.classpath.customdata.CustomDataKeys.COUNT_ALL_METHOD;
import static org.springframework.roo.classpath.customdata.CustomDataKeys.FIND_ALL_METHOD;
import static org.springframework.roo.classpath.customdata.CustomDataKeys.FIND_ENTRIES_METHOD;
import static org.springframework.roo.classpath.customdata.CustomDataKeys.FIND_METHOD;
import static org.springframework.roo.classpath.customdata.CustomDataKeys.MERGE_METHOD;
import static org.springframework.roo.classpath.customdata.CustomDataKeys.PERSIST_METHOD;
import static org.springframework.roo.classpath.customdata.CustomDataKeys.REMOVE_METHOD;
import static org.springframework.roo.model.JavaType.INT_OBJECT;
import static org.springframework.roo.model.JavaType.STRING;
import static org.springframework.roo.model.JavaType.VOID_PRIMITIVE;
import static org.springframework.roo.model.JdkJavaType.ARRAYS;
import static org.springframework.roo.model.JdkJavaType.ARRAY_LIST;
import static org.springframework.roo.model.JdkJavaType.LIST;
import static org.springframework.roo.model.JdkJavaType.UNSUPPORTED_ENCODING_EXCEPTION;
import static org.springframework.roo.model.Jsr303JavaType.VALID;
import static org.springframework.roo.model.SpringJavaType.AUTOWIRED;
import static org.springframework.roo.model.SpringJavaType.BINDING_RESULT;
import static org.springframework.roo.model.SpringJavaType.CONVERSION_SERVICE;
import static org.springframework.roo.model.SpringJavaType.LOCALE_CONTEXT_HOLDER;
import static org.springframework.roo.model.SpringJavaType.MODEL;
import static org.springframework.roo.model.SpringJavaType.PATH_VARIABLE;
import static org.springframework.roo.model.SpringJavaType.REQUEST_MAPPING;
import static org.springframework.roo.model.SpringJavaType.REQUEST_METHOD;
import static org.springframework.roo.model.SpringJavaType.REQUEST_PARAM;
import static org.springframework.roo.model.SpringJavaType.URI_UTILS;
import static org.springframework.roo.model.SpringJavaType.WEB_UTILS;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.SortedMap;
import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.springframework.roo.addon.web.mvc.controller.details.DateTimeFormatDetails;
import org.springframework.roo.addon.web.mvc.controller.details.JavaTypeMetadataDetails;
import org.springframework.roo.addon.web.mvc.controller.details.JavaTypePersistenceMetadataDetails;
import org.springframework.roo.classpath.PhysicalTypeIdentifierNamingUtils;
import org.springframework.roo.classpath.PhysicalTypeMetadata;
import org.springframework.roo.classpath.customdata.tagkeys.MethodMetadataCustomDataKey;
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.MethodMetadata;
import org.springframework.roo.classpath.details.MethodMetadataBuilder;
import org.springframework.roo.classpath.details.annotations.AnnotatedJavaType;
import org.springframework.roo.classpath.details.annotations.AnnotationAttributeValue;
import org.springframework.roo.classpath.details.annotations.AnnotationMetadataBuilder;
import org.springframework.roo.classpath.details.annotations.BooleanAttributeValue;
import org.springframework.roo.classpath.details.annotations.EnumAttributeValue;
import org.springframework.roo.classpath.details.annotations.StringAttributeValue;
import org.springframework.roo.classpath.itd.AbstractItdTypeDetailsProvidingMetadataItem;
import org.springframework.roo.classpath.itd.InvocableMemberBodyBuilder;
import org.springframework.roo.classpath.layers.MemberTypeAdditions;
import org.springframework.roo.metadata.MetadataIdentificationUtils;
import org.springframework.roo.model.DataType;
import org.springframework.roo.model.EnumDetails;
import org.springframework.roo.model.JavaSymbolName;
import org.springframework.roo.model.JavaType;
import org.springframework.roo.project.LogicalPath;
/**
* Metadata for {@link RooWebScaffold}.
*
* @author Stefan Schmidt
* @author Andrew Swan
* @since 1.0
*/
public class WebScaffoldMetadata extends
AbstractItdTypeDetailsProvidingMetadataItem {
private static final JavaSymbolName CS_FIELD = new JavaSymbolName(
"conversionService");
private static final JavaType HTTP_SERVLET_REQUEST = new JavaType(
"javax.servlet.http.HttpServletRequest");
private static final StringAttributeValue PRODUCES_HTML = new StringAttributeValue(
new JavaSymbolName("produces"), "text/html");
private static final String PROVIDES_TYPE_STRING = WebScaffoldMetadata.class
.getName();
private static final String PROVIDES_TYPE = MetadataIdentificationUtils
.create(PROVIDES_TYPE_STRING);
private WebScaffoldAnnotationValues annotationValues;
private boolean compositePk;
private String controllerPath;
private Map<JavaSymbolName, DateTimeFormatDetails> dateTypes;
private String entityName;
private JavaType formBackingType;
private JavaTypeMetadataDetails javaTypeMetadataHolder;
/**
* Constructor
*
* @param identifier
* @param aspectName
* @param governorPhysicalType
* @param annotationValues
* @param idField
* @param specialDomainTypes
* @param dependentTypes
* @param dateTypes
* @param crudAdditions
* @param editableFieldTypes
*/
public WebScaffoldMetadata(
final String identifier,
final JavaType aspectName,
final PhysicalTypeMetadata governorPhysicalType,
final WebScaffoldAnnotationValues annotationValues,
final FieldMetadata idField,
final SortedMap<JavaType, JavaTypeMetadataDetails> specialDomainTypes,
final List<JavaTypeMetadataDetails> dependentTypes,
final Map<JavaSymbolName, DateTimeFormatDetails> dateTypes,
final Map<MethodMetadataCustomDataKey, MemberTypeAdditions> crudAdditions,
final Collection<JavaType> editableFieldTypes) {
super(identifier, aspectName, governorPhysicalType);
Validate.isTrue(isValid(identifier), "Metadata identification string '"
+ identifier + "' is invalid");
Validate.notNull(annotationValues, "Annotation values required");
Validate.notNull(specialDomainTypes,
"Special domain types map required");
Validate.notNull(dependentTypes, "Dependent types list required");
if (!isValid()) {
return;
}
this.annotationValues = annotationValues;
controllerPath = annotationValues.getPath();
this.dateTypes = dateTypes;
formBackingType = annotationValues.getFormBackingObject();
entityName = JavaSymbolName.getReservedWordSafeName(formBackingType)
.getSymbolName();
javaTypeMetadataHolder = specialDomainTypes.get(formBackingType);
Validate.notNull(javaTypeMetadataHolder,
"Metadata holder required for form backing type: "
+ formBackingType);
if (javaTypeMetadataHolder.getPersistenceDetails() != null
&& !javaTypeMetadataHolder.getPersistenceDetails()
.getRooIdentifierFields().isEmpty()) {
compositePk = true;
builder.addField(getField(CS_FIELD, CONVERSION_SERVICE));
builder.addConstructor(getConstructor());
}
// "create" methods
final MemberTypeAdditions persistMethod = crudAdditions
.get(PERSIST_METHOD);
if (annotationValues.isCreate() && persistMethod != null) {
builder.addMethod(getCreateMethod(persistMethod));
builder.addMethod(getCreateFormMethod(dependentTypes));
persistMethod.copyAdditionsTo(builder, governorTypeDetails);
}
// "list" method
final MemberTypeAdditions countAllMethod = crudAdditions
.get(COUNT_ALL_METHOD);
final MemberTypeAdditions findMethod = crudAdditions.get(FIND_METHOD);
final MemberTypeAdditions findAllMethod = crudAdditions
.get(FIND_ALL_METHOD);
final MemberTypeAdditions findEntriesMethod = crudAdditions
.get(FIND_ENTRIES_METHOD);
// "show" method
if (findMethod != null) {
builder.addMethod(getShowMethod(idField, findMethod));
findMethod.copyAdditionsTo(builder, governorTypeDetails);
}
if (countAllMethod != null && findAllMethod != null
&& findEntriesMethod != null) {
builder.addMethod(getListMethod(findAllMethod, countAllMethod,
findEntriesMethod));
countAllMethod.copyAdditionsTo(builder, governorTypeDetails);
findAllMethod.copyAdditionsTo(builder, governorTypeDetails);
findEntriesMethod.copyAdditionsTo(builder, governorTypeDetails);
}
// "update" methods
final MemberTypeAdditions updateMethod = crudAdditions
.get(MERGE_METHOD);
if (annotationValues.isUpdate() && updateMethod != null
&& findMethod != null) {
builder.addMethod(getUpdateMethod(updateMethod));
builder.addMethod(getUpdateFormMethod(idField, findMethod));
updateMethod.copyAdditionsTo(builder, governorTypeDetails);
}
// "delete" method
final MemberTypeAdditions deleteMethod = crudAdditions
.get(REMOVE_METHOD);
if (annotationValues.isDelete() && deleteMethod != null
&& findMethod != null) {
builder.addMethod(getDeleteMethod(idField, deleteMethod, findMethod));
deleteMethod.copyAdditionsTo(builder, governorTypeDetails);
}
if (!dateTypes.isEmpty()) {
builder.addMethod(getDateTimeFormatHelperMethod());
}
if (annotationValues.isCreate() || annotationValues.isUpdate()) {
builder.addMethod(getPopulateEditFormMethod(formBackingType,
specialDomainTypes.values(), editableFieldTypes));
builder.addMethod(getEncodeUrlPathSegmentMethod());
}
itdTypeDetails = builder.build();
}
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 WebScaffoldAnnotationValues getAnnotationValues() {
return annotationValues;
}
@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();
}
private ConstructorMetadataBuilder getConstructor() {
final ConstructorMetadata constructor = governorTypeDetails
.getDeclaredConstructor(Arrays.asList(CONVERSION_SERVICE));
if (constructor != null) {
return null;
}
final InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder();
bodyBuilder.appendFormalLine("super();");
bodyBuilder.appendFormalLine("this." + CS_FIELD + " = " + CS_FIELD
+ ";");
final ConstructorMetadataBuilder constructorBuilder = new ConstructorMetadataBuilder(
getId());
constructorBuilder.addAnnotation(new AnnotationMetadataBuilder(
AUTOWIRED));
constructorBuilder.addParameterType(AnnotatedJavaType
.convertFromJavaType(CONVERSION_SERVICE));
constructorBuilder.addParameterName(CS_FIELD);
constructorBuilder.setModifier(Modifier.PUBLIC);
constructorBuilder.setBodyBuilder(bodyBuilder);
return constructorBuilder;
}
private MethodMetadataBuilder getCreateFormMethod(
final List<JavaTypeMetadataDetails> dependentTypes) {
final JavaSymbolName methodName = new JavaSymbolName("createForm");
if (governorHasMethodWithSameName(methodName)) {
return null;
}
final List<JavaType> parameterTypes = Arrays.asList(MODEL);
final List<JavaSymbolName> parameterNames = Arrays
.asList(new JavaSymbolName("uiModel"));
final List<AnnotationMetadataBuilder> annotations = new ArrayList<AnnotationMetadataBuilder>();
final List<AnnotationAttributeValue<?>> requestMappingAttributes = new ArrayList<AnnotationAttributeValue<?>>();
requestMappingAttributes.add(new StringAttributeValue(
new JavaSymbolName("params"), "form"));
requestMappingAttributes.add(PRODUCES_HTML);
final AnnotationMetadataBuilder requestMapping = new AnnotationMetadataBuilder(
REQUEST_MAPPING, requestMappingAttributes);
annotations.add(requestMapping);
final InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder();
bodyBuilder.appendFormalLine("populateEditForm(uiModel, new "
+ getShortName(formBackingType) + "());");
boolean listAdded = false;
for (final JavaTypeMetadataDetails dependentType : dependentTypes) {
if (dependentType.getPersistenceDetails().getCountMethod() == null) {
continue;
}
if (!listAdded) {
final JavaType stringArrayType = new JavaType(
STRING.getFullyQualifiedTypeName(), 1, DataType.TYPE,
null, null);
final JavaType listType = new JavaType(
LIST.getFullyQualifiedTypeName(), 0, DataType.TYPE,
null, Arrays.asList(stringArrayType));
final JavaType arrayListType = new JavaType(
ARRAY_LIST.getFullyQualifiedTypeName(), 0,
DataType.TYPE, null, Arrays.asList(stringArrayType));
bodyBuilder.appendFormalLine(getShortName(listType)
+ " dependencies = new " + getShortName(arrayListType)
+ "();");
listAdded = true;
}
builder.getImportRegistrationResolver().addImport(
dependentType.getJavaType());
bodyBuilder.appendFormalLine("if ("
+ dependentType.getPersistenceDetails().getCountMethod()
.getMethodCall() + " == 0) {");
bodyBuilder.indent();
// Adding string array which has the fieldName at position 0 and the
// path at position 1
bodyBuilder.appendFormalLine("dependencies.add(new String[] { \""
+ dependentType.getJavaType().getSimpleTypeName()
.toLowerCase() + "\", \""
+ dependentType.getControllerPath() + "\" });");
bodyBuilder.indentRemove();
bodyBuilder.appendFormalLine("}");
}
if (listAdded) {
bodyBuilder
.appendFormalLine("uiModel.addAttribute(\"dependencies\", dependencies);");
}
bodyBuilder.appendFormalLine("return \"" + controllerPath
+ "/create\";");
final MethodMetadataBuilder methodBuilder = new MethodMetadataBuilder(
getId(), Modifier.PUBLIC, methodName, STRING,
AnnotatedJavaType.convertFromJavaTypes(parameterTypes),
parameterNames, bodyBuilder);
methodBuilder.setAnnotations(annotations);
return methodBuilder;
}
private MethodMetadataBuilder getCreateMethod(
final MemberTypeAdditions persistMethod) {
final JavaTypePersistenceMetadataDetails javaTypePersistenceMetadataHolder = javaTypeMetadataHolder
.getPersistenceDetails();
if (javaTypePersistenceMetadataHolder == null
|| javaTypePersistenceMetadataHolder
.getIdentifierAccessorMethod() == null) {
return null;
}
final JavaSymbolName methodName = new JavaSymbolName("create");
if (governorHasMethodWithSameName(methodName)) {
return null;
}
final AnnotationMetadataBuilder validAnnotation = new AnnotationMetadataBuilder(
VALID);
final List<AnnotatedJavaType> parameterTypes = Arrays
.asList(new AnnotatedJavaType(formBackingType, validAnnotation
.build()), new AnnotatedJavaType(BINDING_RESULT),
new AnnotatedJavaType(MODEL), new AnnotatedJavaType(
HTTP_SERVLET_REQUEST));
final List<JavaSymbolName> parameterNames = Arrays.asList(
new JavaSymbolName(entityName), new JavaSymbolName(
"bindingResult"), new JavaSymbolName("uiModel"),
new JavaSymbolName("httpServletRequest"));
final List<AnnotationAttributeValue<?>> requestMappingAttributes = new ArrayList<AnnotationAttributeValue<?>>();
requestMappingAttributes.add(new EnumAttributeValue(new JavaSymbolName(
"method"), new EnumDetails(REQUEST_METHOD, new JavaSymbolName(
"POST"))));
requestMappingAttributes.add(PRODUCES_HTML);
final AnnotationMetadataBuilder requestMapping = new AnnotationMetadataBuilder(
REQUEST_MAPPING, requestMappingAttributes);
final List<AnnotationMetadataBuilder> annotations = new ArrayList<AnnotationMetadataBuilder>();
annotations.add(requestMapping);
final InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder();
bodyBuilder.appendFormalLine("if (bindingResult.hasErrors()) {");
bodyBuilder.indent();
bodyBuilder.appendFormalLine("populateEditForm(uiModel, " + entityName
+ ");");
bodyBuilder.appendFormalLine("return \"" + controllerPath
+ "/create\";");
bodyBuilder.indentRemove();
bodyBuilder.appendFormalLine("}");
bodyBuilder.appendFormalLine("uiModel.asMap().clear();");
bodyBuilder.appendFormalLine(persistMethod.getMethodCall() + ";");
bodyBuilder.appendFormalLine("return \"redirect:/"
+ controllerPath
+ "/\" + encodeUrlPathSegment("
+ (compositePk ? "conversionService.convert(" : "")
+ entityName
+ "."
+ javaTypePersistenceMetadataHolder
.getIdentifierAccessorMethod().getMethodName() + "()"
+ (compositePk ? ", String.class)" : ".toString()")
+ ", httpServletRequest);");
final MethodMetadataBuilder methodBuilder = new MethodMetadataBuilder(
getId(), Modifier.PUBLIC, methodName, STRING, parameterTypes,
parameterNames, bodyBuilder);
methodBuilder.setAnnotations(annotations);
return methodBuilder;
}
private MethodMetadataBuilder getDateTimeFormatHelperMethod() {
final JavaSymbolName methodName = new JavaSymbolName(
"addDateTimeFormatPatterns");
if (governorHasMethodWithSameName(methodName)) {
return null;
}
final List<JavaType> parameterTypes = Arrays.asList(MODEL);
final List<JavaSymbolName> parameterNames = Arrays
.asList(new JavaSymbolName("uiModel"));
final InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder();
for (final Entry<JavaSymbolName, DateTimeFormatDetails> javaSymbolNameDateTimeFormatDetailsEntry : dateTypes
.entrySet()) {
String pattern;
if (javaSymbolNameDateTimeFormatDetailsEntry.getValue().pattern != null) {
pattern = "\""
+ javaSymbolNameDateTimeFormatDetailsEntry.getValue().pattern
+ "\"";
}
else {
final JavaType dateTimeFormat = new JavaType(
"org.joda.time.format.DateTimeFormat");
pattern = getShortName(dateTimeFormat)
+ ".patternForStyle(\""
+ javaSymbolNameDateTimeFormatDetailsEntry.getValue().style
+ "\", " + getShortName(LOCALE_CONTEXT_HOLDER)
+ ".getLocale())";
}
bodyBuilder.appendFormalLine("uiModel.addAttribute(\""
+ entityName
+ "_"
+ javaSymbolNameDateTimeFormatDetailsEntry.getKey()
.getSymbolName().toLowerCase() + "_date_format\", "
+ pattern + ");");
}
return new MethodMetadataBuilder(getId(), 0, methodName,
VOID_PRIMITIVE,
AnnotatedJavaType.convertFromJavaTypes(parameterTypes),
parameterNames, bodyBuilder);
}
private MethodMetadataBuilder getDeleteMethod(final FieldMetadata idField,
final MemberTypeAdditions deleteMethodAdditions,
final MemberTypeAdditions findMethod) {
final JavaTypePersistenceMetadataDetails javaTypePersistenceMetadataHolder = javaTypeMetadataHolder
.getPersistenceDetails();
if (javaTypePersistenceMetadataHolder == null) {
return null;
}
final JavaSymbolName methodName = new JavaSymbolName("delete");
if (governorHasMethodWithSameName(methodName)) {
return null;
}
final JavaSymbolName idFieldName = idField.getFieldName();
String fieldName = entityName;
String deleteMethodCall = deleteMethodAdditions.getMethodCall();
if (idFieldName.getSymbolName().equals(entityName)) {
fieldName += "_";
deleteMethodCall = fieldName + "."
+ deleteMethodAdditions.getMethodName() + "()";
}
final List<AnnotationAttributeValue<?>> attributes = new ArrayList<AnnotationAttributeValue<?>>();
attributes.add(new StringAttributeValue(new JavaSymbolName("value"),
idFieldName.getSymbolName()));
final AnnotationMetadataBuilder pathVariableAnnotation = new AnnotationMetadataBuilder(
PATH_VARIABLE, attributes);
final List<AnnotationAttributeValue<?>> firstResultAttributes = new ArrayList<AnnotationAttributeValue<?>>();
firstResultAttributes.add(new StringAttributeValue(new JavaSymbolName(
"value"), "page"));
firstResultAttributes.add(new BooleanAttributeValue(new JavaSymbolName(
"required"), false));
final AnnotationMetadataBuilder firstResultAnnotation = new AnnotationMetadataBuilder(
REQUEST_PARAM, firstResultAttributes);
final List<AnnotationAttributeValue<?>> maxResultsAttributes = new ArrayList<AnnotationAttributeValue<?>>();
maxResultsAttributes.add(new StringAttributeValue(new JavaSymbolName(
"value"), "size"));
maxResultsAttributes.add(new BooleanAttributeValue(new JavaSymbolName(
"required"), false));
final AnnotationMetadataBuilder maxResultAnnotation = new AnnotationMetadataBuilder(
REQUEST_PARAM, maxResultsAttributes);
final List<AnnotatedJavaType> parameterTypes = Arrays.asList(
new AnnotatedJavaType(javaTypePersistenceMetadataHolder
.getIdentifierType(), pathVariableAnnotation.build()),
new AnnotatedJavaType(new JavaType(Integer.class.getName()),
firstResultAnnotation.build()), new AnnotatedJavaType(
new JavaType(Integer.class.getName()),
maxResultAnnotation.build()), new AnnotatedJavaType(
MODEL));
final List<JavaSymbolName> parameterNames = Arrays.asList(idFieldName,
new JavaSymbolName("page"), new JavaSymbolName("size"),
new JavaSymbolName("uiModel"));
final List<AnnotationAttributeValue<?>> requestMappingAttributes = new ArrayList<AnnotationAttributeValue<?>>();
requestMappingAttributes.add(new StringAttributeValue(
new JavaSymbolName("value"), "/{" + idFieldName.getSymbolName()
+ "}"));
requestMappingAttributes.add(new EnumAttributeValue(new JavaSymbolName(
"method"), new EnumDetails(REQUEST_METHOD, new JavaSymbolName(
"DELETE"))));
requestMappingAttributes.add(PRODUCES_HTML);
final AnnotationMetadataBuilder requestMapping = new AnnotationMetadataBuilder(
REQUEST_MAPPING, requestMappingAttributes);
final List<AnnotationMetadataBuilder> annotations = new ArrayList<AnnotationMetadataBuilder>();
annotations.add(requestMapping);
final InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder();
bodyBuilder.appendFormalLine(getShortName(formBackingType) + " "
+ fieldName + " = " + findMethod.getMethodCall() + ";");
bodyBuilder.appendFormalLine(deleteMethodCall + ";");
bodyBuilder.appendFormalLine("uiModel.asMap().clear();");
bodyBuilder
.appendFormalLine("uiModel.addAttribute(\"page\", (page == null) ? \"1\" : page.toString());");
bodyBuilder
.appendFormalLine("uiModel.addAttribute(\"size\", (size == null) ? \"10\" : size.toString());");
bodyBuilder.appendFormalLine("return \"redirect:/" + controllerPath
+ "\";");
final MethodMetadataBuilder methodBuilder = new MethodMetadataBuilder(
getId(), Modifier.PUBLIC, methodName, STRING, parameterTypes,
parameterNames, bodyBuilder);
methodBuilder.setAnnotations(annotations);
return methodBuilder;
}
private MethodMetadataBuilder getEncodeUrlPathSegmentMethod() {
final JavaSymbolName methodName = new JavaSymbolName(
"encodeUrlPathSegment");
if (governorHasMethodWithSameName(methodName)) {
return null;
}
final List<JavaType> parameterTypes = Arrays.asList(STRING,
HTTP_SERVLET_REQUEST);
final List<JavaSymbolName> parameterNames = Arrays.asList(
new JavaSymbolName("pathSegment"), new JavaSymbolName(
"httpServletRequest"));
builder.getImportRegistrationResolver().addImport(
UNSUPPORTED_ENCODING_EXCEPTION);
final InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder();
bodyBuilder
.appendFormalLine("String enc = httpServletRequest.getCharacterEncoding();");
bodyBuilder.appendFormalLine("if (enc == null) {");
bodyBuilder.indent();
bodyBuilder.appendFormalLine("enc = " + getShortName(WEB_UTILS)
+ ".DEFAULT_CHARACTER_ENCODING;");
bodyBuilder.indentRemove();
bodyBuilder.appendFormalLine("}");
bodyBuilder.appendFormalLine("try {");
bodyBuilder.indent();
bodyBuilder.appendFormalLine("pathSegment = " + getShortName(URI_UTILS)
+ ".encodePathSegment(pathSegment, enc);");
bodyBuilder.indentRemove();
bodyBuilder.appendFormalLine("} catch ("
+ getShortName(UNSUPPORTED_ENCODING_EXCEPTION) + " uee) {}");
bodyBuilder.appendFormalLine("return pathSegment;");
return new MethodMetadataBuilder(getId(), 0, methodName, STRING,
AnnotatedJavaType.convertFromJavaTypes(parameterTypes),
parameterNames, bodyBuilder);
}
/**
* Returns the metadata for the "list" method that this ITD introduces into
* the controller.
*
* @param findAllAdditions
* @param countAllAdditions
* @param findEntriesAdditions
* @return <code>null</code> if no such method is to be introduced
*/
private MethodMetadataBuilder getListMethod(
final MemberTypeAdditions findAllAdditions,
final MemberTypeAdditions countAllAdditions,
final MemberTypeAdditions findEntriesAdditions) {
final JavaSymbolName methodName = new JavaSymbolName("list");
if (governorHasMethodWithSameName(methodName)) {
return null;
}
final List<AnnotationAttributeValue<?>> firstResultAttributes = new ArrayList<AnnotationAttributeValue<?>>();
firstResultAttributes.add(new StringAttributeValue(new JavaSymbolName(
"value"), "page"));
firstResultAttributes.add(new BooleanAttributeValue(new JavaSymbolName(
"required"), false));
final AnnotationMetadataBuilder firstResultAnnotation = new AnnotationMetadataBuilder(
REQUEST_PARAM, firstResultAttributes);
final List<AnnotationAttributeValue<?>> maxResultsAttributes = new ArrayList<AnnotationAttributeValue<?>>();
maxResultsAttributes.add(new StringAttributeValue(new JavaSymbolName(
"value"), "size"));
maxResultsAttributes.add(new BooleanAttributeValue(new JavaSymbolName(
"required"), false));
final AnnotationMetadataBuilder maxResultAnnotation = new AnnotationMetadataBuilder(
REQUEST_PARAM, maxResultsAttributes);
final List<AnnotatedJavaType> parameterTypes = Arrays
.asList(new AnnotatedJavaType(INT_OBJECT, firstResultAnnotation
.build()), new AnnotatedJavaType(INT_OBJECT,
maxResultAnnotation.build()), new AnnotatedJavaType(
MODEL));
final List<JavaSymbolName> parameterNames = Arrays.asList(
new JavaSymbolName("page"), new JavaSymbolName("size"),
new JavaSymbolName("uiModel"));
final List<AnnotationAttributeValue<?>> requestMappingAttributes = new ArrayList<AnnotationAttributeValue<?>>();
requestMappingAttributes.add(PRODUCES_HTML);
final List<AnnotationMetadataBuilder> annotations = new ArrayList<AnnotationMetadataBuilder>();
annotations.add(new AnnotationMetadataBuilder(REQUEST_MAPPING,
requestMappingAttributes));
final String plural = javaTypeMetadataHolder.getPlural().toLowerCase();
final InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder();
bodyBuilder.appendFormalLine("if (page != null || size != null) {");
bodyBuilder.indent();
bodyBuilder
.appendFormalLine("int sizeNo = size == null ? 10 : size.intValue();");
bodyBuilder
.appendFormalLine("final int firstResult = page == null ? 0 : (page.intValue() - 1) * sizeNo;");
bodyBuilder.appendFormalLine("uiModel.addAttribute(\"" + plural
+ "\", " + findEntriesAdditions.getMethodCall() + ");");
bodyBuilder.appendFormalLine("float nrOfPages = (float) "
+ countAllAdditions.getMethodCall() + " / sizeNo;");
bodyBuilder
.appendFormalLine("uiModel.addAttribute(\"maxPages\", (int) ((nrOfPages > (int) nrOfPages || nrOfPages == 0.0) ? nrOfPages + 1 : nrOfPages));");
bodyBuilder.indentRemove();
bodyBuilder.appendFormalLine("} else {");
bodyBuilder.indent();
bodyBuilder.appendFormalLine("uiModel.addAttribute(\"" + plural
+ "\", " + findAllAdditions.getMethodCall() + ");");
bodyBuilder.indentRemove();
bodyBuilder.appendFormalLine("}");
if (!dateTypes.isEmpty()) {
bodyBuilder.appendFormalLine("addDateTimeFormatPatterns(uiModel);");
}
bodyBuilder.appendFormalLine("return \"" + controllerPath + "/list\";");
final MethodMetadataBuilder methodBuilder = new MethodMetadataBuilder(
getId(), Modifier.PUBLIC, methodName, STRING, parameterTypes,
parameterNames, bodyBuilder);
methodBuilder.setAnnotations(annotations);
return methodBuilder;
}
private MethodMetadata getPopulateEditFormMethod(final JavaType entity,
final Collection<JavaTypeMetadataDetails> specialDomainTypes,
final Collection<JavaType> editableFieldTypes) {
final JavaSymbolName methodName = new JavaSymbolName("populateEditForm");
final JavaType[] parameterTypes = { MODEL, entity };
final List<JavaSymbolName> parameterNames = Arrays.asList(
new JavaSymbolName("uiModel"), new JavaSymbolName(entityName));
if (governorHasMethod(methodName, parameterTypes)) {
return null;
}
final InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder();
bodyBuilder.appendFormalLine("uiModel.addAttribute(\"" + entityName
+ "\", " + entityName + ");");
if (!dateTypes.isEmpty()) {
bodyBuilder.appendFormalLine("addDateTimeFormatPatterns(uiModel);");
}
if (annotationValues.isPopulateMethods()) {
for (final JavaTypeMetadataDetails domainType : specialDomainTypes) {
if (editableFieldTypes.contains(domainType.getJavaType())) {
final JavaTypePersistenceMetadataDetails persistenceDetails = domainType
.getPersistenceDetails();
final String modelAttribute = domainType.getPlural()
.toLowerCase();
if (persistenceDetails != null
&& persistenceDetails.getFindAllMethod() != null) {
bodyBuilder.appendFormalLine("uiModel.addAttribute(\""
+ modelAttribute
+ "\", "
+ persistenceDetails.getFindAllMethod()
.getMethodCall() + ");");
persistenceDetails.getFindAllMethod().copyAdditionsTo(
builder, governorTypeDetails);
}
else if (domainType.isEnumType()) {
bodyBuilder.appendFormalLine("uiModel.addAttribute(\""
+ modelAttribute + "\", "
+ getShortName(ARRAYS) + ".asList("
+ getShortName(domainType.getJavaType())
+ ".values())" + ");");
}
}
}
}
return new MethodMetadataBuilder(getId(), 0, methodName,
VOID_PRIMITIVE,
AnnotatedJavaType.convertFromJavaTypes(parameterTypes),
parameterNames, bodyBuilder).build();
}
private String getShortName(final JavaType type) {
return type.getNameIncludingTypeParameters(false,
builder.getImportRegistrationResolver());
}
private MethodMetadataBuilder getShowMethod(final FieldMetadata idField,
final MemberTypeAdditions findMethod) {
final JavaTypePersistenceMetadataDetails javaTypePersistenceMetadataHolder = javaTypeMetadataHolder
.getPersistenceDetails();
if (javaTypePersistenceMetadataHolder == null) {
return null;
}
final JavaSymbolName methodName = new JavaSymbolName("show");
if (governorHasMethodWithSameName(methodName)) {
return null;
}
final List<AnnotationAttributeValue<?>> attributes = new ArrayList<AnnotationAttributeValue<?>>();
final String idFieldName = idField.getFieldName().getSymbolName();
attributes.add(new StringAttributeValue(new JavaSymbolName("value"),
idFieldName));
final AnnotationMetadataBuilder pathVariableAnnotation = new AnnotationMetadataBuilder(
PATH_VARIABLE, attributes);
final List<AnnotatedJavaType> parameterTypes = Arrays.asList(
new AnnotatedJavaType(javaTypePersistenceMetadataHolder
.getIdentifierType(), pathVariableAnnotation.build()),
new AnnotatedJavaType(MODEL));
final List<JavaSymbolName> parameterNames = Arrays.asList(
new JavaSymbolName(idFieldName), new JavaSymbolName("uiModel"));
final List<AnnotationAttributeValue<?>> requestMappingAttributes = new ArrayList<AnnotationAttributeValue<?>>();
requestMappingAttributes.add(new StringAttributeValue(
new JavaSymbolName("value"), "/{" + idFieldName + "}"));
requestMappingAttributes.add(PRODUCES_HTML);
final AnnotationMetadataBuilder requestMapping = new AnnotationMetadataBuilder(
REQUEST_MAPPING, requestMappingAttributes);
final List<AnnotationMetadataBuilder> annotations = new ArrayList<AnnotationMetadataBuilder>();
annotations.add(requestMapping);
final InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder();
if (!dateTypes.isEmpty()) {
bodyBuilder.appendFormalLine("addDateTimeFormatPatterns(uiModel);");
}
bodyBuilder.appendFormalLine("uiModel.addAttribute(\""
+ entityName.toLowerCase() + "\", "
+ findMethod.getMethodCall() + ");");
bodyBuilder.appendFormalLine("uiModel.addAttribute(\"itemId\", "
+ (compositePk ? "conversionService.convert(" : "")
+ idFieldName + (compositePk ? ", String.class)" : "") + ");");
bodyBuilder.appendFormalLine("return \"" + controllerPath + "/show\";");
final MethodMetadataBuilder methodBuilder = new MethodMetadataBuilder(
getId(), Modifier.PUBLIC, methodName, STRING, parameterTypes,
parameterNames, bodyBuilder);
methodBuilder.setAnnotations(annotations);
return methodBuilder;
}
private MethodMetadataBuilder getUpdateFormMethod(
final FieldMetadata idField, final MemberTypeAdditions findMethod) {
final JavaTypePersistenceMetadataDetails javaTypePersistenceMetadataHolder = javaTypeMetadataHolder
.getPersistenceDetails();
if (javaTypePersistenceMetadataHolder == null) {
return null;
}
final JavaSymbolName methodName = new JavaSymbolName("updateForm");
if (governorHasMethodWithSameName(methodName)) {
return null;
}
final String idFieldName = idField.getFieldName().getSymbolName();
final List<AnnotationAttributeValue<?>> attributes = new ArrayList<AnnotationAttributeValue<?>>();
attributes.add(new StringAttributeValue(new JavaSymbolName("value"),
idFieldName));
final AnnotationMetadataBuilder pathVariableAnnotation = new AnnotationMetadataBuilder(
PATH_VARIABLE, attributes);
final List<AnnotatedJavaType> parameterTypes = Arrays.asList(
new AnnotatedJavaType(javaTypePersistenceMetadataHolder
.getIdentifierType(), pathVariableAnnotation.build()),
new AnnotatedJavaType(MODEL));
final List<JavaSymbolName> parameterNames = Arrays.asList(
new JavaSymbolName(idFieldName), new JavaSymbolName("uiModel"));
final List<AnnotationAttributeValue<?>> requestMappingAttributes = new ArrayList<AnnotationAttributeValue<?>>();
requestMappingAttributes.add(new StringAttributeValue(
new JavaSymbolName("value"), "/{" + idFieldName + "}"));
requestMappingAttributes.add(new StringAttributeValue(
new JavaSymbolName("params"), "form"));
requestMappingAttributes.add(PRODUCES_HTML);
final AnnotationMetadataBuilder requestMapping = new AnnotationMetadataBuilder(
REQUEST_MAPPING, requestMappingAttributes);
final List<AnnotationMetadataBuilder> annotations = new ArrayList<AnnotationMetadataBuilder>();
annotations.add(requestMapping);
final InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder();
bodyBuilder.appendFormalLine("populateEditForm(uiModel, "
+ findMethod.getMethodCall() + ");");
bodyBuilder.appendFormalLine("return \"" + controllerPath
+ "/update\";");
final MethodMetadataBuilder methodBuilder = new MethodMetadataBuilder(
getId(), Modifier.PUBLIC, methodName, STRING, parameterTypes,
parameterNames, bodyBuilder);
methodBuilder.setAnnotations(annotations);
return methodBuilder;
}
private MethodMetadataBuilder getUpdateMethod(
final MemberTypeAdditions updateMethod) {
final JavaTypePersistenceMetadataDetails javaTypePersistenceMetadataHolder = javaTypeMetadataHolder
.getPersistenceDetails();
if (javaTypePersistenceMetadataHolder == null
|| javaTypePersistenceMetadataHolder.getMergeMethod() == null) {
return null;
}
final JavaSymbolName methodName = new JavaSymbolName("update");
if (governorHasMethodWithSameName(methodName)) {
return null;
}
final AnnotationMetadataBuilder validAnnotation = new AnnotationMetadataBuilder(
VALID);
final List<AnnotatedJavaType> parameterTypes = Arrays
.asList(new AnnotatedJavaType(formBackingType, validAnnotation
.build()), new AnnotatedJavaType(BINDING_RESULT),
new AnnotatedJavaType(MODEL), new AnnotatedJavaType(
HTTP_SERVLET_REQUEST));
final List<JavaSymbolName> parameterNames = Arrays.asList(
new JavaSymbolName(entityName), new JavaSymbolName(
"bindingResult"), new JavaSymbolName("uiModel"),
new JavaSymbolName("httpServletRequest"));
final List<AnnotationAttributeValue<?>> requestMappingAttributes = new ArrayList<AnnotationAttributeValue<?>>();
requestMappingAttributes.add(new EnumAttributeValue(new JavaSymbolName(
"method"), new EnumDetails(REQUEST_METHOD, new JavaSymbolName(
"PUT"))));
requestMappingAttributes.add(PRODUCES_HTML);
final AnnotationMetadataBuilder requestMapping = new AnnotationMetadataBuilder(
REQUEST_MAPPING, requestMappingAttributes);
final List<AnnotationMetadataBuilder> annotations = new ArrayList<AnnotationMetadataBuilder>();
annotations.add(requestMapping);
final InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder();
bodyBuilder.appendFormalLine("if (bindingResult.hasErrors()) {");
bodyBuilder.indent();
bodyBuilder.appendFormalLine("populateEditForm(uiModel, " + entityName
+ ");");
bodyBuilder.appendFormalLine("return \"" + controllerPath
+ "/update\";");
bodyBuilder.indentRemove();
bodyBuilder.appendFormalLine("}");
bodyBuilder.appendFormalLine("uiModel.asMap().clear();");
bodyBuilder.appendFormalLine(updateMethod.getMethodCall() + ";");
bodyBuilder.appendFormalLine("return \"redirect:/"
+ controllerPath
+ "/\" + encodeUrlPathSegment("
+ (compositePk ? "conversionService.convert(" : "")
+ entityName
+ "."
+ javaTypePersistenceMetadataHolder
.getIdentifierAccessorMethod().getMethodName() + "()"
+ (compositePk ? ", String.class)" : ".toString()")
+ ", httpServletRequest);");
final MethodMetadataBuilder methodBuilder = new MethodMetadataBuilder(
getId(), Modifier.PUBLIC, methodName, STRING, parameterTypes,
parameterNames, bodyBuilder);
methodBuilder.setAnnotations(annotations);
return methodBuilder;
}
}