package org.springframework.roo.addon.web.mvc.jsp;
import static org.springframework.roo.model.JavaType.BOOLEAN_OBJECT;
import static org.springframework.roo.model.JavaType.BOOLEAN_PRIMITIVE;
import static org.springframework.roo.model.JavaType.DOUBLE_OBJECT;
import static org.springframework.roo.model.JavaType.FLOAT_OBJECT;
import static org.springframework.roo.model.JavaType.INT_OBJECT;
import static org.springframework.roo.model.JavaType.LONG_OBJECT;
import static org.springframework.roo.model.JavaType.SHORT_OBJECT;
import static org.springframework.roo.model.JavaType.STRING;
import static org.springframework.roo.model.JdkJavaType.BIG_DECIMAL;
import static org.springframework.roo.model.JdkJavaType.BIG_INTEGER;
import static org.springframework.roo.model.JdkJavaType.CALENDAR;
import static org.springframework.roo.model.JdkJavaType.DATE;
import static org.springframework.roo.model.Jsr303JavaType.DECIMAL_MAX;
import static org.springframework.roo.model.Jsr303JavaType.DECIMAL_MIN;
import static org.springframework.roo.model.Jsr303JavaType.FUTURE;
import static org.springframework.roo.model.Jsr303JavaType.MAX;
import static org.springframework.roo.model.Jsr303JavaType.MIN;
import static org.springframework.roo.model.Jsr303JavaType.NOT_NULL;
import static org.springframework.roo.model.Jsr303JavaType.PAST;
import static org.springframework.roo.model.Jsr303JavaType.PATTERN;
import static org.springframework.roo.model.Jsr303JavaType.SIZE;
import java.beans.Introspector;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import javax.xml.parsers.DocumentBuilder;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.springframework.roo.addon.web.mvc.controller.addon.details.FinderMetadataDetails;
import org.springframework.roo.addon.web.mvc.controller.addon.details.JavaTypeMetadataDetails;
import org.springframework.roo.addon.web.mvc.controller.addon.details.JavaTypePersistenceMetadataDetails;
import org.springframework.roo.addon.web.mvc.controller.addon.scaffold.WebScaffoldAnnotationValues;
import org.springframework.roo.classpath.customdata.CustomDataKeys;
import org.springframework.roo.classpath.details.FieldMetadata;
import org.springframework.roo.classpath.details.FieldMetadataBuilder;
import org.springframework.roo.classpath.details.MemberFindingUtils;
import org.springframework.roo.classpath.details.MethodMetadata;
import org.springframework.roo.classpath.details.annotations.AnnotationAttributeValue;
import org.springframework.roo.classpath.details.annotations.AnnotationMetadata;
import org.springframework.roo.model.JavaSymbolName;
import org.springframework.roo.model.JavaType;
import org.springframework.roo.model.JpaJavaType;
import org.springframework.roo.support.util.DomUtils;
import org.springframework.roo.support.util.XmlElementBuilder;
import org.springframework.roo.support.util.XmlRoundTripUtils;
import org.springframework.roo.support.util.XmlUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
/**
* Helper class which generates the contents of the various jsp documents
*
* @author Stefan Schmidt
* @since 1.1
*/
public class JspViewManager {
private static final String CREATED = "created";
private static final JavaSymbolName VALUE = new JavaSymbolName("value");
private final String controllerPath;
private final String entityName;
private final List<FieldMetadata> fields;
private final JavaType formBackingType;
private final JavaTypeMetadataDetails formBackingTypeMetadata;
private final JavaTypePersistenceMetadataDetails formBackingTypePersistenceMetadata;
private final Map<JavaType, JavaTypeMetadataDetails> relatedDomainTypes;
private final WebScaffoldAnnotationValues webScaffoldAnnotationValues;
/**
* Constructor
*
* @param fields can't be <code>null</code>
* @param webScaffoldAnnotationValues can't be <code>null</code>
* @param relatedDomainTypes can't be <code>null</code>
*/
public JspViewManager(final List<FieldMetadata> fields,
final WebScaffoldAnnotationValues webScaffoldAnnotationValues,
final Map<JavaType, JavaTypeMetadataDetails> relatedDomainTypes) {
Validate.notNull(fields, "List of fields required");
Validate.notNull(webScaffoldAnnotationValues, "Web scaffold annotation values required");
Validate.notNull(relatedDomainTypes, "Related domain types required");
this.fields = Collections.unmodifiableList(fields);
this.webScaffoldAnnotationValues = webScaffoldAnnotationValues;
formBackingType = webScaffoldAnnotationValues.getFormBackingObject();
this.relatedDomainTypes = relatedDomainTypes;
entityName = JavaSymbolName.getReservedWordSafeName(formBackingType).getSymbolName();
formBackingTypeMetadata = relatedDomainTypes.get(formBackingType);
Validate.notNull(formBackingTypeMetadata, "Form backing type metadata required");
formBackingTypePersistenceMetadata = formBackingTypeMetadata.getPersistenceDetails();
Validate.notNull(formBackingTypePersistenceMetadata,
"Persistence metadata required for form backing type");
Validate.notNull(webScaffoldAnnotationValues.getPath(),
"Path is not specified in the @RooWebScaffold annotation for '%s'",
webScaffoldAnnotationValues.getGovernorTypeDetails().getName());
if (webScaffoldAnnotationValues.getPath().startsWith("/")) {
controllerPath = webScaffoldAnnotationValues.getPath();
} else {
controllerPath = "/" + webScaffoldAnnotationValues.getPath();
}
}
private void addCommonAttributes(final FieldMetadata field, final Element fieldElement) {
AnnotationMetadata annotationMetadata;
if (field.getFieldType().equals(INT_OBJECT)
|| field.getFieldType().getFullyQualifiedTypeName().equals(int.class.getName())
|| field.getFieldType().equals(SHORT_OBJECT)
|| field.getFieldType().getFullyQualifiedTypeName().equals(short.class.getName())
|| field.getFieldType().equals(LONG_OBJECT)
|| field.getFieldType().getFullyQualifiedTypeName().equals(long.class.getName())
|| field.getFieldType().equals(BIG_INTEGER)) {
fieldElement.setAttribute("validationMessageCode", "field_invalid_integer");
} else if (isEmailField(field)) {
fieldElement.setAttribute("validationMessageCode", "field_invalid_email");
} else if (field.getFieldType().equals(DOUBLE_OBJECT)
|| field.getFieldType().getFullyQualifiedTypeName().equals(double.class.getName())
|| field.getFieldType().equals(FLOAT_OBJECT)
|| field.getFieldType().getFullyQualifiedTypeName().equals(float.class.getName())
|| field.getFieldType().equals(BIG_DECIMAL)) {
fieldElement.setAttribute("validationMessageCode", "field_invalid_number");
}
if ("field:input".equals(fieldElement.getTagName())
&& null != (annotationMetadata =
MemberFindingUtils.getAnnotationOfType(field.getAnnotations(), MIN))) {
final AnnotationAttributeValue<?> min = annotationMetadata.getAttribute(VALUE);
if (min != null) {
fieldElement.setAttribute("min", min.getValue().toString());
fieldElement.setAttribute("required", "true");
}
}
if ("field:input".equals(fieldElement.getTagName())
&& null != (annotationMetadata =
MemberFindingUtils.getAnnotationOfType(field.getAnnotations(), MAX))
&& !"field:textarea".equals(fieldElement.getTagName())) {
final AnnotationAttributeValue<?> maxA = annotationMetadata.getAttribute(VALUE);
if (maxA != null) {
fieldElement.setAttribute("max", maxA.getValue().toString());
}
}
if ("field:input".equals(fieldElement.getTagName())
&& null != (annotationMetadata =
MemberFindingUtils.getAnnotationOfType(field.getAnnotations(), DECIMAL_MIN))
&& !"field:textarea".equals(fieldElement.getTagName())) {
final AnnotationAttributeValue<?> decimalMin = annotationMetadata.getAttribute(VALUE);
if (decimalMin != null) {
fieldElement.setAttribute("decimalMin", decimalMin.getValue().toString());
fieldElement.setAttribute("required", "true");
}
}
if ("field:input".equals(fieldElement.getTagName())
&& null != (annotationMetadata =
MemberFindingUtils.getAnnotationOfType(field.getAnnotations(), DECIMAL_MAX))) {
final AnnotationAttributeValue<?> decimalMax = annotationMetadata.getAttribute(VALUE);
if (decimalMax != null) {
fieldElement.setAttribute("decimalMax", decimalMax.getValue().toString());
}
}
if (null != (annotationMetadata =
MemberFindingUtils.getAnnotationOfType(field.getAnnotations(), PATTERN))) {
final AnnotationAttributeValue<?> regexp =
annotationMetadata.getAttribute(new JavaSymbolName("regexp"));
if (regexp != null) {
fieldElement.setAttribute("validationRegex", regexp.getValue().toString());
}
}
if ("field:input".equals(fieldElement.getTagName())
&& null != (annotationMetadata =
MemberFindingUtils.getAnnotationOfType(field.getAnnotations(), SIZE))) {
final AnnotationAttributeValue<?> max =
annotationMetadata.getAttribute(new JavaSymbolName("max"));
if (max != null) {
fieldElement.setAttribute("max", max.getValue().toString());
}
final AnnotationAttributeValue<?> min =
annotationMetadata.getAttribute(new JavaSymbolName("min"));
if (min != null) {
fieldElement.setAttribute("min", min.getValue().toString());
fieldElement.setAttribute("required", "true");
}
}
if (null != (annotationMetadata =
MemberFindingUtils.getAnnotationOfType(field.getAnnotations(), NOT_NULL))) {
final String tagName = fieldElement.getTagName();
if (tagName.endsWith("textarea") || tagName.endsWith("input") || tagName.endsWith("datetime")
|| tagName.endsWith("textarea") || tagName.endsWith("select")
|| tagName.endsWith("reference")) {
fieldElement.setAttribute("required", "true");
}
}
if (field.getCustomData().keySet().contains(CustomDataKeys.COLUMN_FIELD)) {
@SuppressWarnings("unchecked")
final Map<String, Object> values =
(Map<String, Object>) field.getCustomData().get(CustomDataKeys.COLUMN_FIELD);
if (values.keySet().contains("nullable") && (Boolean) values.get("nullable") == false) {
fieldElement.setAttribute("required", "true");
}
}
// Disable form binding for nested fields (mainly PKs)
if (field.getFieldName().getSymbolName().contains(".")) {
fieldElement.setAttribute("disableFormBinding", "true");
}
}
private void createFieldsForCreateAndUpdate(final List<FieldMetadata> formFields,
final Document document, final Element root, final boolean isCreate) {
for (final FieldMetadata field : formFields) {
final String fieldName = field.getFieldName().getSymbolName();
JavaType fieldType = field.getFieldType();
AnnotationMetadata annotationMetadata;
// Ignoring java.util.Map field types (see ROO-194)
if (fieldType.equals(new JavaType(Map.class.getName()))) {
continue;
}
// Fields contained in the embedded Id type have been added
// separately to the field list
if (field.getCustomData().keySet().contains(CustomDataKeys.EMBEDDED_ID_FIELD)) {
continue;
}
fieldType = getJavaTypeForField(field);
final JavaTypeMetadataDetails typeMetadataHolder = relatedDomainTypes.get(fieldType);
JavaTypePersistenceMetadataDetails typePersistenceMetadataHolder = null;
if (typeMetadataHolder != null) {
typePersistenceMetadataHolder = typeMetadataHolder.getPersistenceDetails();
}
Element fieldElement = null;
if (fieldType.getFullyQualifiedTypeName().equals(Boolean.class.getName())
|| fieldType.getFullyQualifiedTypeName().equals(boolean.class.getName())) {
fieldElement = document.createElement("field:checkbox");
// Handle enum fields
} else if (typeMetadataHolder != null && typeMetadataHolder.isEnumType()) {
fieldElement =
new XmlElementBuilder("field:select", document)
.addAttribute("items", "${" + typeMetadataHolder.getPlural().toLowerCase() + "}")
.addAttribute("path", getPathForType(fieldType)).build();
} else if (field.getCustomData().keySet().contains(CustomDataKeys.ONE_TO_MANY_FIELD)) {
// OneToMany relationships are managed from the 'many' side of
// the relationship, therefore we provide a link to the relevant
// form the link URL is determined as a best effort attempt
// following Roo REST conventions, this link might be wrong if
// custom paths are used if custom paths are used the developer
// can adjust the path attribute in the field:reference tag
// accordingly
if (typePersistenceMetadataHolder != null) {
fieldElement =
new XmlElementBuilder("field:simple", document)
.addAttribute("messageCode", "entity_reference_not_managed")
.addAttribute("messageCodeAttribute",
new JavaSymbolName(fieldType.getSimpleTypeName()).getReadableSymbolName())
.build();
} else {
continue;
}
} else if (field.getCustomData().keySet().contains(CustomDataKeys.MANY_TO_ONE_FIELD)
|| field.getCustomData().keySet().contains(CustomDataKeys.MANY_TO_MANY_FIELD)
|| field.getCustomData().keySet().contains(CustomDataKeys.ONE_TO_ONE_FIELD)) {
final JavaType referenceType = getJavaTypeForField(field);
final JavaTypeMetadataDetails referenceTypeMetadata = relatedDomainTypes.get(referenceType);
if (referenceType != null && referenceTypeMetadata != null
&& referenceTypeMetadata.isApplicationType() && typePersistenceMetadataHolder != null) {
fieldElement =
new XmlElementBuilder("field:select", document)
.addAttribute("items",
"${" + referenceTypeMetadata.getPlural().toLowerCase() + "}")
.addAttribute(
"itemValue",
typePersistenceMetadataHolder.getIdentifierField().getFieldName()
.getSymbolName())
.addAttribute("path", "/" + getPathForType(getJavaTypeForField(field))).build();
if (field.getCustomData().keySet().contains(CustomDataKeys.MANY_TO_MANY_FIELD)) {
fieldElement.setAttribute("multiple", "true");
}
}
} else if (fieldType.equals(DATE) || fieldType.equals(CALENDAR)) {
if (fieldName.equals(CREATED)) {
continue;
}
// Only include the date picker for styles supported by Dojo
// (SMALL & MEDIUM)
fieldElement =
new XmlElementBuilder("field:datetime", document).addAttribute("dateTimePattern",
"${" + entityName + "_" + fieldName.toLowerCase() + "_date_format}").build();
if (null != MemberFindingUtils.getAnnotationOfType(field.getAnnotations(), FUTURE)) {
fieldElement.setAttribute("future", "true");
} else if (null != MemberFindingUtils.getAnnotationOfType(field.getAnnotations(), PAST)) {
fieldElement.setAttribute("past", "true");
}
} else if (field.getCustomData().keySet().contains(CustomDataKeys.LOB_FIELD)) {
fieldElement = new XmlElementBuilder("field:textarea", document).build();
}
if ((annotationMetadata =
MemberFindingUtils.getAnnotationOfType(field.getAnnotations(), SIZE)) != null) {
final AnnotationAttributeValue<?> max =
annotationMetadata.getAttribute(new JavaSymbolName("max"));
if (max != null) {
final int maxValue = (Integer) max.getValue();
if (fieldElement == null && maxValue > 30) {
fieldElement = new XmlElementBuilder("field:textarea", document).build();
}
}
}
// Use a default input field if no other criteria apply
if (fieldElement == null) {
fieldElement = document.createElement("field:input");
}
addCommonAttributes(field, fieldElement);
fieldElement.setAttribute("field", fieldName);
fieldElement.setAttribute(
"id",
XmlUtils.convertId("c:" + formBackingType.getFullyQualifiedTypeName() + "."
+ field.getFieldName().getSymbolName()));
// If identifier manually assigned, then add 'required=true'
if (formBackingTypePersistenceMetadata.getIdentifierField().getFieldName()
.equals(field.getFieldName())
&& field.getAnnotation(JpaJavaType.GENERATED_VALUE) == null) {
fieldElement.setAttribute("required", "true");
}
fieldElement.setAttribute("z", XmlRoundTripUtils.calculateUniqueKeyFor(fieldElement));
root.appendChild(fieldElement);
}
}
public Document getCreateDocument() {
final DocumentBuilder builder = XmlUtils.getDocumentBuilder();
final Document document = builder.newDocument();
// Add document namespaces
final Element div =
(Element) document.appendChild(new XmlElementBuilder("div", document)
.addAttribute("xmlns:form", "urn:jsptagdir:/WEB-INF/tags/form")
.addAttribute("xmlns:field", "urn:jsptagdir:/WEB-INF/tags/form/fields")
.addAttribute("xmlns:jsp", "http://java.sun.com/JSP/Page")
.addAttribute("xmlns:c", "http://java.sun.com/jsp/jstl/core")
.addAttribute("xmlns:spring", "http://www.springframework.org/tags")
.addAttribute("version", "2.0")
.addChild(
new XmlElementBuilder("jsp:directive.page", document).addAttribute("contentType",
"text/html;charset=UTF-8").build())
.addChild(
new XmlElementBuilder("jsp:output", document).addAttribute("omit-xml-declaration",
"yes").build()).build());
// Add form create element
final Element formCreate =
new XmlElementBuilder("form:create", document)
.addAttribute("id",
XmlUtils.convertId("fc:" + formBackingType.getFullyQualifiedTypeName()))
.addAttribute("modelAttribute", entityName).addAttribute("path", controllerPath)
.addAttribute("render", "${empty dependencies}").build();
if (!controllerPath.equalsIgnoreCase(formBackingType.getSimpleTypeName())) {
formCreate.setAttribute("path", controllerPath);
}
final List<FieldMetadata> formFields = new ArrayList<FieldMetadata>();
final List<FieldMetadata> fieldCopy = new ArrayList<FieldMetadata>(fields);
// Handle Roo identifiers
if (!formBackingTypePersistenceMetadata.getRooIdentifierFields().isEmpty()) {
final String identifierFieldName =
formBackingTypePersistenceMetadata.getIdentifierField().getFieldName().getSymbolName();
formCreate.setAttribute("compositePkField", identifierFieldName);
for (final FieldMetadata embeddedField : formBackingTypePersistenceMetadata
.getRooIdentifierFields()) {
final FieldMetadataBuilder fieldBuilder = new FieldMetadataBuilder(embeddedField);
fieldBuilder.setFieldName(new JavaSymbolName(identifierFieldName + "."
+ embeddedField.getFieldName().getSymbolName()));
for (int i = 0; i < fieldCopy.size(); i++) {
// Make sure form fields are not presented twice.
if (fieldCopy.get(i).getFieldName().equals(embeddedField.getFieldName())) {
fieldCopy.remove(i);
break;
}
}
formFields.add(fieldBuilder.build());
}
}
formFields.addAll(fieldCopy);
// If identifier manually assigned, show it in creation
if (formBackingTypePersistenceMetadata.getIdentifierField().getAnnotation(
JpaJavaType.GENERATED_VALUE) == null) {
formFields.add(formBackingTypePersistenceMetadata.getIdentifierField());
}
createFieldsForCreateAndUpdate(formFields, document, formCreate, true);
formCreate.setAttribute("z", XmlRoundTripUtils.calculateUniqueKeyFor(formCreate));
final Element dependency =
new XmlElementBuilder("form:dependency", document)
.addAttribute("id",
XmlUtils.convertId("d:" + formBackingType.getFullyQualifiedTypeName()))
.addAttribute("render", "${not empty dependencies}")
.addAttribute("dependencies", "${dependencies}").build();
dependency.setAttribute("z", XmlRoundTripUtils.calculateUniqueKeyFor(dependency));
div.appendChild(formCreate);
div.appendChild(dependency);
return document;
}
public Document getFinderDocument(final FinderMetadataDetails finderMetadataDetails) {
final DocumentBuilder builder = XmlUtils.getDocumentBuilder();
final Document document = builder.newDocument();
// Add document namespaces
final Element div =
(Element) document.appendChild(new XmlElementBuilder("div", document)
.addAttribute("xmlns:form", "urn:jsptagdir:/WEB-INF/tags/form")
.addAttribute("xmlns:field", "urn:jsptagdir:/WEB-INF/tags/form/fields")
.addAttribute("xmlns:jsp", "http://java.sun.com/JSP/Page")
.addAttribute("version", "2.0")
.addChild(
new XmlElementBuilder("jsp:directive.page", document).addAttribute("contentType",
"text/html;charset=UTF-8").build())
.addChild(
new XmlElementBuilder("jsp:output", document).addAttribute("omit-xml-declaration",
"yes").build()).build());
final Element formFind =
new XmlElementBuilder("form:find", document)
.addAttribute("id",
XmlUtils.convertId("ff:" + formBackingType.getFullyQualifiedTypeName()))
.addAttribute("path", controllerPath)
.addAttribute(
"finderName",
finderMetadataDetails.getFinderMethodMetadata().getMethodName().getSymbolName()
.replace("find" + formBackingTypeMetadata.getPlural(), "")).build();
formFind.setAttribute("z", XmlRoundTripUtils.calculateUniqueKeyFor(formFind));
div.appendChild(formFind);
for (final FieldMetadata field : finderMetadataDetails.getFinderMethodParamFields()) {
final JavaType type = field.getFieldType();
final JavaSymbolName paramName = field.getFieldName();
JavaSymbolName fieldName = null;
if (paramName.getSymbolName().startsWith("max")
|| paramName.getSymbolName().startsWith("min")) {
fieldName =
new JavaSymbolName(Introspector.decapitalize(StringUtils.capitalize(paramName
.getSymbolName().substring(3))));
} else {
fieldName = paramName;
}
// Ignoring java.util.Map field types (see ROO-194)
if (type.equals(new JavaType(Map.class.getName()))) {
continue;
}
Validate.notNull(paramName, "Could not find field '%s' in '%s'", paramName,
type.getFullyQualifiedTypeName());
Element fieldElement = null;
final JavaTypeMetadataDetails typeMetadataHolder =
relatedDomainTypes.get(getJavaTypeForField(field));
if (type.isCommonCollectionType()
&& relatedDomainTypes.containsKey(getJavaTypeForField(field))) {
final JavaTypeMetadataDetails collectionTypeMetadataHolder =
relatedDomainTypes.get(getJavaTypeForField(field));
final JavaTypePersistenceMetadataDetails typePersistenceMetadataHolder =
collectionTypeMetadataHolder.getPersistenceDetails();
if (typePersistenceMetadataHolder != null) {
fieldElement =
new XmlElementBuilder("field:select", document)
.addAttribute("required", "true")
.addAttribute("items",
"${" + collectionTypeMetadataHolder.getPlural().toLowerCase() + "}")
.addAttribute(
"itemValue",
typePersistenceMetadataHolder.getIdentifierField().getFieldName()
.getSymbolName())
.addAttribute("path", "/" + getPathForType(getJavaTypeForField(field))).build();
if (field.getCustomData().keySet().contains(CustomDataKeys.MANY_TO_MANY_FIELD)) {
fieldElement.setAttribute("multiple", "true");
}
}
} else if (typeMetadataHolder != null && typeMetadataHolder.isEnumType()
&& field.getCustomData().keySet().contains(CustomDataKeys.ENUMERATED_FIELD)) {
fieldElement =
new XmlElementBuilder("field:select", document).addAttribute("required", "true")
.addAttribute("items", "${" + typeMetadataHolder.getPlural().toLowerCase() + "}")
.addAttribute("path", "/" + getPathForType(type)).build();
} else if (type.equals(BOOLEAN_OBJECT) || type.equals(BOOLEAN_PRIMITIVE)) {
fieldElement = document.createElement("field:checkbox");
} else if (typeMetadataHolder != null && typeMetadataHolder.isApplicationType()) {
final JavaTypePersistenceMetadataDetails typePersistenceMetadataHolder =
typeMetadataHolder.getPersistenceDetails();
if (typePersistenceMetadataHolder != null) {
fieldElement =
new XmlElementBuilder("field:select", document)
.addAttribute("required", "true")
.addAttribute("items", "${" + typeMetadataHolder.getPlural().toLowerCase() + "}")
.addAttribute(
"itemValue",
typePersistenceMetadataHolder.getIdentifierField().getFieldName()
.getSymbolName()).addAttribute("path", "/" + getPathForType(type))
.build();
}
} else if (type.equals(DATE) || type.equals(CALENDAR)) {
fieldElement =
new XmlElementBuilder("field:datetime", document)
.addAttribute("required", "true")
.addAttribute(
"dateTimePattern",
"${" + entityName + "_" + fieldName.getSymbolName().toLowerCase()
+ "_date_format}").build();
}
if (fieldElement == null) {
fieldElement =
new XmlElementBuilder("field:input", document).addAttribute("required", "true").build();
}
addCommonAttributes(field, fieldElement);
fieldElement.setAttribute("disableFormBinding", "true");
fieldElement.setAttribute("field", paramName.getSymbolName());
fieldElement.setAttribute("id",
XmlUtils.convertId("f:" + formBackingType.getFullyQualifiedTypeName() + "." + paramName));
fieldElement.setAttribute("z", XmlRoundTripUtils.calculateUniqueKeyFor(fieldElement));
formFind.appendChild(fieldElement);
}
DomUtils.removeTextNodes(document);
return document;
}
private JavaType getJavaTypeForField(final FieldMetadata field) {
if (field.getFieldType().isCommonCollectionType()) {
// Currently there is no scaffolding available for Maps (see
// ROO-194)
if (field.getFieldType().equals(new JavaType(Map.class.getName()))) {
return null;
}
final List<JavaType> parameters = field.getFieldType().getParameters();
if (parameters.isEmpty()) {
throw new IllegalStateException("Unable to determine the parameter type for the "
+ field.getFieldName().getSymbolName() + " field in "
+ formBackingType.getSimpleTypeName());
}
return parameters.get(0);
}
return field.getFieldType();
}
public Document getListDocument() {
final DocumentBuilder builder = XmlUtils.getDocumentBuilder();
final Document document = builder.newDocument();
// Add document namespaces
final Element div =
new XmlElementBuilder("div", document)
.addAttribute("xmlns:page", "urn:jsptagdir:/WEB-INF/tags/form")
.addAttribute("xmlns:table", "urn:jsptagdir:/WEB-INF/tags/form/fields")
.addAttribute("xmlns:jsp", "http://java.sun.com/JSP/Page")
.addAttribute("version", "2.0")
.addChild(
new XmlElementBuilder("jsp:directive.page", document).addAttribute("contentType",
"text/html;charset=UTF-8").build())
.addChild(
new XmlElementBuilder("jsp:output", document).addAttribute("omit-xml-declaration",
"yes").build()).build();
document.appendChild(div);
final Element fieldTable =
new XmlElementBuilder("table:table", document)
.addAttribute("id",
XmlUtils.convertId("l:" + formBackingType.getFullyQualifiedTypeName()))
.addAttribute("data", "${" + formBackingTypeMetadata.getPlural().toLowerCase() + "}")
.addAttribute("path", controllerPath).build();
if (!webScaffoldAnnotationValues.isUpdate()) {
fieldTable.setAttribute("update", "false");
}
if (!webScaffoldAnnotationValues.isDelete()) {
fieldTable.setAttribute("delete", "false");
}
if (!formBackingTypePersistenceMetadata.getIdentifierField().getFieldName().getSymbolName()
.equals("id")) {
fieldTable.setAttribute("typeIdFieldName", formBackingTypePersistenceMetadata
.getIdentifierField().getFieldName().getSymbolName());
}
fieldTable.setAttribute("z", XmlRoundTripUtils.calculateUniqueKeyFor(fieldTable));
int fieldCounter = 0;
for (final FieldMetadata field : fields) {
if (++fieldCounter < 7) {
final Element columnElement =
new XmlElementBuilder("table:column", document)
.addAttribute(
"id",
XmlUtils.convertId("c:" + formBackingType.getFullyQualifiedTypeName() + "."
+ field.getFieldName().getSymbolName()))
.addAttribute("property", uncapitalize(field.getFieldName().getSymbolName()))
.build();
final String fieldName = uncapitalize(field.getFieldName().getSymbolName());
if (field.getFieldType().equals(DATE)) {
columnElement.setAttribute("date", "true");
columnElement.setAttribute("dateTimePattern",
"${" + entityName + "_" + fieldName.toLowerCase() + "_date_format}");
} else if (field.getFieldType().equals(CALENDAR)) {
columnElement.setAttribute("calendar", "true");
columnElement.setAttribute("dateTimePattern",
"${" + entityName + "_" + fieldName.toLowerCase() + "_date_format}");
} else if (field.getFieldType().isCommonCollectionType()
&& field.getCustomData().get(CustomDataKeys.ONE_TO_MANY_FIELD) != null) {
continue;
}
columnElement.setAttribute("z", XmlRoundTripUtils.calculateUniqueKeyFor(columnElement));
fieldTable.appendChild(columnElement);
}
}
// Create page:list element
final Element pageList =
new XmlElementBuilder("page:list", document)
.addAttribute("id",
XmlUtils.convertId("pl:" + formBackingType.getFullyQualifiedTypeName()))
.addAttribute("items", "${" + formBackingTypeMetadata.getPlural().toLowerCase() + "}")
.addChild(fieldTable).build();
pageList.setAttribute("z", XmlRoundTripUtils.calculateUniqueKeyFor(pageList));
div.appendChild(pageList);
return document;
}
private String getPathForType(final JavaType type) {
final JavaTypeMetadataDetails javaTypeMetadataHolder = relatedDomainTypes.get(type);
Validate.notNull(javaTypeMetadataHolder, "Unable to obtain metadata for type %s",
type.getFullyQualifiedTypeName());
return javaTypeMetadataHolder.getControllerPath();
}
public Document getShowDocument() {
final DocumentBuilder builder = XmlUtils.getDocumentBuilder();
final Document document = builder.newDocument();
// Add document namespaces
final Element div =
(Element) document.appendChild(new XmlElementBuilder("div", document)
.addAttribute("xmlns:page", "urn:jsptagdir:/WEB-INF/tags/form")
.addAttribute("xmlns:field", "urn:jsptagdir:/WEB-INF/tags/form/fields")
.addAttribute("xmlns:jsp", "http://java.sun.com/JSP/Page")
.addAttribute("version", "2.0")
.addChild(
new XmlElementBuilder("jsp:directive.page", document).addAttribute("contentType",
"text/html;charset=UTF-8").build())
.addChild(
new XmlElementBuilder("jsp:output", document).addAttribute("omit-xml-declaration",
"yes").build()).build());
final Element pageShow =
new XmlElementBuilder("page:show", document)
.addAttribute("id",
XmlUtils.convertId("ps:" + formBackingType.getFullyQualifiedTypeName()))
.addAttribute("object", "${" + entityName.toLowerCase() + "}")
.addAttribute("path", controllerPath).build();
if (!webScaffoldAnnotationValues.isCreate()) {
pageShow.setAttribute("create", "false");
}
if (!webScaffoldAnnotationValues.isUpdate()) {
pageShow.setAttribute("update", "false");
}
if (!webScaffoldAnnotationValues.isDelete()) {
pageShow.setAttribute("delete", "false");
}
pageShow.setAttribute("z", XmlRoundTripUtils.calculateUniqueKeyFor(pageShow));
// Add field:display elements for each field
for (final FieldMetadata field : fields) {
// Ignoring java.util.Map field types (see ROO-194)
if (field.getFieldType().equals(new JavaType(Map.class.getName()))) {
continue;
}
final String fieldName = uncapitalize(field.getFieldName().getSymbolName());
final Element fieldDisplay =
new XmlElementBuilder("field:display", document)
.addAttribute(
"id",
XmlUtils.convertId("s:" + formBackingType.getFullyQualifiedTypeName() + "."
+ field.getFieldName().getSymbolName()))
.addAttribute("object", "${" + entityName.toLowerCase() + "}")
.addAttribute("field", fieldName).build();
if (field.getFieldType().equals(DATE)) {
if (fieldName.equals(CREATED)) {
continue;
}
fieldDisplay.setAttribute("date", "true");
fieldDisplay.setAttribute("dateTimePattern",
"${" + entityName + "_" + fieldName.toLowerCase() + "_date_format}");
} else if (field.getFieldType().equals(CALENDAR)) {
fieldDisplay.setAttribute("calendar", "true");
fieldDisplay.setAttribute("dateTimePattern",
"${" + entityName + "_" + fieldName.toLowerCase() + "_date_format}");
} else if (field.getFieldType().isCommonCollectionType()
&& field.getCustomData().get(CustomDataKeys.ONE_TO_MANY_FIELD) != null) {
continue;
}
fieldDisplay.setAttribute("z", XmlRoundTripUtils.calculateUniqueKeyFor(fieldDisplay));
pageShow.appendChild(fieldDisplay);
}
div.appendChild(pageShow);
return document;
}
public Document getUpdateDocument() {
final DocumentBuilder builder = XmlUtils.getDocumentBuilder();
final Document document = builder.newDocument();
// Add document namespaces
final Element div =
(Element) document.appendChild(new XmlElementBuilder("div", document)
.addAttribute("xmlns:form", "urn:jsptagdir:/WEB-INF/tags/form")
.addAttribute("xmlns:field", "urn:jsptagdir:/WEB-INF/tags/form/fields")
.addAttribute("xmlns:jsp", "http://java.sun.com/JSP/Page")
.addAttribute("version", "2.0")
.addChild(
new XmlElementBuilder("jsp:directive.page", document).addAttribute("contentType",
"text/html;charset=UTF-8").build())
.addChild(
new XmlElementBuilder("jsp:output", document).addAttribute("omit-xml-declaration",
"yes").build()).build());
// Add form update element
final Element formUpdate =
new XmlElementBuilder("form:update", document)
.addAttribute("id",
XmlUtils.convertId("fu:" + formBackingType.getFullyQualifiedTypeName()))
.addAttribute("modelAttribute", entityName).build();
if (!controllerPath.equalsIgnoreCase(formBackingType.getSimpleTypeName())) {
formUpdate.setAttribute("path", controllerPath);
}
if (!"id".equals(formBackingTypePersistenceMetadata.getIdentifierField().getFieldName()
.getSymbolName())) {
formUpdate.setAttribute("idField", formBackingTypePersistenceMetadata.getIdentifierField()
.getFieldName().getSymbolName());
}
final MethodMetadata versionAccessorMethod =
formBackingTypePersistenceMetadata.getVersionAccessorMethod();
if (versionAccessorMethod == null) {
formUpdate.setAttribute("versionField", "none");
} else {
final String methodName = versionAccessorMethod.getMethodName().getSymbolName();
formUpdate.setAttribute("versionField", methodName.substring("get".length()));
}
// Filter out embedded ID fields as they represent the composite PK
// which is not to be updated.
final List<FieldMetadata> fieldCopy = new ArrayList<FieldMetadata>(fields);
for (final FieldMetadata embeddedField : formBackingTypePersistenceMetadata
.getRooIdentifierFields()) {
for (int i = 0; i < fieldCopy.size(); i++) {
// Make sure form fields are not presented twice.
if (fieldCopy.get(i).getFieldName().equals(embeddedField.getFieldName())) {
fieldCopy.remove(i);
}
}
}
createFieldsForCreateAndUpdate(fieldCopy, document, formUpdate, false);
formUpdate.setAttribute("z", XmlRoundTripUtils.calculateUniqueKeyFor(formUpdate));
div.appendChild(formUpdate);
return document;
}
private boolean isEmailField(final FieldMetadata field) {
return STRING.equals(field.getFieldType())
&& uncapitalize(field.getFieldName().getSymbolName()).contains("email");
}
private String uncapitalize(final String term) {
// [ROO-1790] this is needed to adhere to the JavaBean naming
// conventions (see JavaBean spec section 8.8)
return Introspector.decapitalize(StringUtils.capitalize(term));
}
}