org.gvnix.web.screen.roo.addon.AbstractPatternMetadata.java Source code

Java tutorial

Introduction

Here is the source code for org.gvnix.web.screen.roo.addon.AbstractPatternMetadata.java

Source

/*
 * gvNIX. Spring Roo based RAD tool for Conselleria d'Infraestructures i
 * Transport - Generalitat Valenciana Copyright (C) 2010, 2011 CIT - Generalitat
 * Valenciana
 *
 * This program is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program. If not, see <http://www.gnu.org/licenses/>.
 */
package org.gvnix.web.screen.roo.addon;

import static org.springframework.roo.model.JdkJavaType.LIST;

import java.beans.Introspector;
import java.lang.reflect.Modifier;
import java.util.AbstractMap.SimpleEntry;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.SortedMap;

import org.apache.commons.lang3.StringUtils;
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.addon.web.mvc.controller.scaffold.WebScaffoldAnnotationValues;
import org.springframework.roo.addon.web.mvc.controller.scaffold.WebScaffoldMetadata;
import org.springframework.roo.classpath.PhysicalTypeMetadata;
import org.springframework.roo.classpath.details.FieldMetadata;
import org.springframework.roo.classpath.details.FieldMetadataBuilder;
import org.springframework.roo.classpath.details.ItdTypeDetailsBuilder;
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.AnnotationMetadata;
import org.springframework.roo.classpath.details.annotations.AnnotationMetadataBuilder;
import org.springframework.roo.classpath.details.annotations.ArrayAttributeValue;
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.scanner.MemberDetails;
import org.springframework.roo.model.DataType;
import org.springframework.roo.model.EnumDetails;
import org.springframework.roo.model.ImportRegistrationResolver;
import org.springframework.roo.model.JavaSymbolName;
import org.springframework.roo.model.JavaType;
import org.springframework.roo.support.util.XmlUtils;

/**
 * This type produces metadata for a new ITD. It uses an
 * {@link ItdTypeDetailsBuilder} provided by
 * {@link AbstractItdTypeDetailsProvidingMetadataItem} to register a field in
 * the ITD and a new method.
 * 
 * @author scar Rovira (orovira at disid dot com) at <a
 *         href="http://www.disid.com">DiSiD Technologies S.L.</a> made for <a
 *         href="http://www.cit.gva.es">Conselleria d'Infraestructures i
 *         Transport</a>
 * @author Mario Martnez (mmartinez at disid dot com) at <a
 *         href="http://www.disid.com">DiSiD Technologies S.L.</a> made for <a
 *         href="http://www.cit.gva.es">Conselleria d'Infraestructures i
 *         Transport</a>
 * @since 0.8
 */
public abstract class AbstractPatternMetadata extends AbstractItdTypeDetailsProvidingMetadataItem {

    private static final String VALUE_TXT = "value";
    private static final String REQUIRED_TXT = "required";
    private static final String RQST_PARAM_PATH = "org.springframework.web.bind.annotation.RequestParam";
    private static final String RETURN_TXT = "return \"";
    private static final String ADD_ATTR = "uiModel.addAttribute(\"";
    private static final String CONCAT = "/\".concat(";
    private static final String QUOTE_COMMA = "\", ";

    private static final JavaSymbolName METHOD_SYMBOL_NAME = new JavaSymbolName("method");
    private static final StringAttributeValue PRODUCES_PARAM_MAPPING = new StringAttributeValue(
            new JavaSymbolName("produces"), "text/html");
    private static final String GVNIXPATTERN = "gvnixpattern";
    private static final SimpleEntry<JavaSymbolName, AnnotatedJavaType> MODEL_REQUEST_PARAM = new SimpleEntry<JavaSymbolName, AnnotatedJavaType>(
            new JavaSymbolName("uiModel"), new AnnotatedJavaType(new JavaType("org.springframework.ui.Model"),
                    new ArrayList<AnnotationMetadata>()));
    private static final SimpleEntry<JavaSymbolName, AnnotatedJavaType> HTTP_SERVLET_REQUEST_PARAM = new SimpleEntry<JavaSymbolName, AnnotatedJavaType>(
            new JavaSymbolName("httpServletRequest"), new AnnotatedJavaType(
                    new JavaType("javax.servlet.http.HttpServletRequest"), new ArrayList<AnnotationMetadata>()));
    private static final JavaType OBJECT_ERROR_TYPE = new JavaType("org.springframework.validation.ObjectError");
    private static final JavaType OBJECT_ERROR_LIST_TYPE = new JavaType(LIST.getFullyQualifiedTypeName(), 0,
            DataType.TYPE, null, Arrays.asList(OBJECT_ERROR_TYPE));
    private static final JavaSymbolName FILTER_BINDING_ERRORS_NAME = new JavaSymbolName("filterBindingErrors");
    private static final AnnotationMetadata VALID_ANNOTATION = new AnnotationMetadataBuilder(
            new JavaType("javax.validation.Valid")).build();
    private static final SimpleEntry<JavaSymbolName, AnnotatedJavaType> BINDING_RESULT = new SimpleEntry<JavaSymbolName, AnnotatedJavaType>(
            new JavaSymbolName("bindingResult"),
            new AnnotatedJavaType(new JavaType("org.springframework.validation.BindingResult"),
                    new ArrayList<AnnotationMetadata>()));
    private static final JavaSymbolName PAGE_PARAM_NAME = new JavaSymbolName("page");
    private static final JavaSymbolName SIZE_PARAM_NAME = new JavaSymbolName("size");
    private static final JavaSymbolName PARAMS_ATTRIBUTE_NAME = new JavaSymbolName("params");
    private static final JavaSymbolName VALUE_ATTRIBUTE_NAME = new JavaSymbolName(VALUE_TXT);
    private static final JavaType RQST_MAP_ANN_TYPE = new JavaType(
            "org.springframework.web.bind.annotation.RequestMapping");

    protected WebScaffoldMetadata webScaffoldMetadata;
    private SortedMap<JavaType, JavaTypeMetadataDetails> relatedEntities;
    protected JavaType entity;
    protected JavaTypeMetadataDetails entityTypeDetails;
    protected List<String> patterns;
    private SortedMap<JavaType, JavaTypeMetadataDetails> relatedFields;
    private Map<JavaType, Map<JavaSymbolName, DateTimeFormatDetails>> relatedDates;
    protected List<MethodMetadata> controllerMethods;
    private List<FieldMetadata> controllerFields;
    protected Map<JavaSymbolName, DateTimeFormatDetails> entityDateTypes;
    private String aspectPackage;

    public AbstractPatternMetadata(String mid, JavaType aspect, PhysicalTypeMetadata controllerMetadata,
            MemberDetails controllerDetails, WebScaffoldMetadata webScaffoldMetadata,
            List<StringAttributeValue> patterns, PhysicalTypeMetadata entityMetadata,
            SortedMap<JavaType, JavaTypeMetadataDetails> relatedEntities,
            SortedMap<JavaType, JavaTypeMetadataDetails> relatedFields,
            Map<JavaType, Map<JavaSymbolName, DateTimeFormatDetails>> relatedDates,
            Map<JavaSymbolName, DateTimeFormatDetails> entityDateTypes) {

        super(mid, aspect, controllerMetadata);

        // Required parameters: Web scaffold information and relatedEntities
        // details (entity and optional master entity)
        Validate.notNull(webScaffoldMetadata, "Web scaffold metadata required");
        Validate.notNull(relatedEntities, "Related relatedEntities type metadata details required");

        if (!isValid()) {

            // This metadata instance not be already produced at the time of
            // instantiation (will retry)
            return;
        }

        this.webScaffoldMetadata = webScaffoldMetadata;
        this.entity = entityMetadata.getMemberHoldingTypeDetails().getName();
        this.relatedEntities = relatedEntities;
        this.entityTypeDetails = relatedEntities.get(entity);
        this.entityDateTypes = entityDateTypes;

        this.controllerMethods = controllerDetails.getMethods();
        this.controllerFields = controllerDetails.getFields();

        Validate.notNull(entityTypeDetails, "Metadata holder required for form backing type: " + entity);
        Validate.notNull(entityTypeDetails.getPersistenceDetails(),
                "PersistenceMetadata details required for form backing type: " + entity);

        WebScaffoldAnnotationValues webScaffoldValues = new WebScaffoldAnnotationValues(controllerMetadata);
        if (webScaffoldValues.isPopulateMethods()) {

            filterAlreadyPopulatedTypes(relatedFields);
        }

        this.relatedFields = relatedFields;
        this.relatedDates = relatedDates;

        // TODO: Take care of attributes "create, update, delete" in
        // RooWebScaffold annotation
        List<String> definedPatternsList = new ArrayList<String>();
        for (StringAttributeValue definedPattern : patterns) {

            definedPatternsList.add(definedPattern.getValue());
        }

        this.aspectPackage = aspect.getPackage().getFullyQualifiedPackageName();

        this.patterns = Collections.unmodifiableList(definedPatternsList);

        builder.addField(getDefinedPatternField());

        List<String> tabularPatterns = getPatternTypeDefined(WebPatternType.tabular, this.patterns);
        if (!tabularPatterns.isEmpty()) {

            // TODO findAll method required on this pattern ?
            if (entityTypeDetails.getPersistenceDetails().getFindAllMethod() == null) {

                // TODO: If no find all method, all other patterns are not
                // generated ?
                return;
            }

            for (String tabularPattern : tabularPatterns) {

                builder.addMethod(getTabularMethod(tabularPattern));
            }

            builder.addMethod(getCreateListMethod());
            builder.addMethod(getUpdateListMethod());
            builder.addMethod(getDeleteListMethod());
            builder.addMethod(getFilterListMethod());
            builder.addMethod(getRefererRedirectMethod());
            builder.addMethod(getFilterBindingErrorsMethod());
        }

        List<String> registerPatterns = getPatternTypeDefined(WebPatternType.register, this.patterns);
        if (!registerPatterns.isEmpty()) {

            // TODO findEntries method required on this pattern ?
            if (entityTypeDetails.getPersistenceDetails().getFindEntriesMethod() == null) {

                // TODO: If no find entries method, all other patterns are not
                // generated ?
                return;
            }

            for (String registerPattern : registerPatterns) {

                builder.addMethod(getRegisterMethod(registerPattern));
                builder.addMethod(getDeleteMethod(registerPattern));
            }

            builder.addMethod(getRefererQueryMethod());
        }

        List<String> tabularEditPatterns = getPatternTypeDefined(WebPatternType.tabular_edit_register,
                this.patterns);
        if (!tabularEditPatterns.isEmpty()) {

            // TODO findAll method required on this pattern ?
            if (entityTypeDetails.getPersistenceDetails().getFindAllMethod() == null) {

                // TODO: If no find all method, all other patterns are not
                // generated ?
                return;
            }

            for (String tabularEditPattern : tabularEditPatterns) {

                builder.addMethod(getTabularMethod(tabularEditPattern));
            }

            builder.addMethod(getDeleteListMethod());
            builder.addMethod(getFilterListMethod());
            builder.addMethod(getRefererRedirectMethod());
            builder.addMethod(getRefererQueryMethod());
            builder.addMethod(getFilterBindingErrorsMethod());
        }
    }

    protected MethodMetadata getDeleteMethod(String patternName) {
        // Specify the desired method name
        JavaSymbolName methodName = new JavaSymbolName("deletePattern" + patternName);

        FieldMetadata formBackingObjectIdField = entityTypeDetails.getPersistenceDetails().getIdentifierField();
        // Define method parameter types
        List<AnnotatedJavaType> methodParamTypes = new ArrayList<AnnotatedJavaType>();

        List<AnnotationAttributeValue<?>> reqParamAttrPathVar = new ArrayList<AnnotationAttributeValue<?>>();
        reqParamAttrPathVar.add(new StringAttributeValue(VALUE_ATTRIBUTE_NAME,
                formBackingObjectIdField.getFieldName().getSymbolName()));
        List<AnnotationMetadata> methodAttrPathVarAnnotations = new ArrayList<AnnotationMetadata>();
        AnnotationMetadataBuilder methodAttPathVarAnnotation = new AnnotationMetadataBuilder(
                new JavaType("org.springframework.web.bind.annotation.PathVariable"), reqParamAttrPathVar);
        methodAttrPathVarAnnotations.add(methodAttPathVarAnnotation.build());
        methodParamTypes
                .add(new AnnotatedJavaType(formBackingObjectIdField.getFieldType(), methodAttrPathVarAnnotations));

        methodParamTypes.add(getPatternRequestParam(false).getValue());

        List<AnnotationAttributeValue<?>> reqParamAttrPage = new ArrayList<AnnotationAttributeValue<?>>();
        reqParamAttrPage.add(new StringAttributeValue(VALUE_ATTRIBUTE_NAME, "page"));
        reqParamAttrPage.add(new BooleanAttributeValue(new JavaSymbolName(REQUIRED_TXT), false));
        List<AnnotationMetadata> methodAttrPageAnnotations = new ArrayList<AnnotationMetadata>();
        AnnotationMetadataBuilder methodAttrPageAnnotation = new AnnotationMetadataBuilder(
                new JavaType(RQST_PARAM_PATH), reqParamAttrPage);
        methodAttrPageAnnotations.add(methodAttrPageAnnotation.build());
        methodParamTypes.add(new AnnotatedJavaType(JavaType.INT_OBJECT, methodAttrPageAnnotations));

        List<AnnotationAttributeValue<?>> reqParamAttrSize = new ArrayList<AnnotationAttributeValue<?>>();
        reqParamAttrSize.add(new StringAttributeValue(VALUE_ATTRIBUTE_NAME, "size"));
        reqParamAttrSize.add(new BooleanAttributeValue(new JavaSymbolName(REQUIRED_TXT), false));
        List<AnnotationMetadata> methodAttrSizeAnnotations = new ArrayList<AnnotationMetadata>();
        AnnotationMetadataBuilder methodAttrSizeAnnotation = new AnnotationMetadataBuilder(
                new JavaType(RQST_PARAM_PATH), reqParamAttrSize);
        methodAttrSizeAnnotations.add(methodAttrSizeAnnotation.build());
        methodParamTypes.add(new AnnotatedJavaType(JavaType.INT_OBJECT, methodAttrSizeAnnotations));

        Entry<JavaSymbolName, AnnotatedJavaType> modelRequestParam = MODEL_REQUEST_PARAM;
        methodParamTypes.add(modelRequestParam.getValue());
        Entry<JavaSymbolName, AnnotatedJavaType> httpServletRequest = HTTP_SERVLET_REQUEST_PARAM;
        methodParamTypes.add(httpServletRequest.getValue());

        MethodMetadata method = methodExists(methodName, methodParamTypes);
        if (method != null) {
            // If it already exists, just return null and omit its
            // generation via the ITD
            return null;
        }

        // Define method parameter names
        // id, pattern, page, size, uiModel, httpServletRequest
        List<JavaSymbolName> methodParamNames = new ArrayList<JavaSymbolName>();
        methodParamNames.add(formBackingObjectIdField.getFieldName());
        Entry<JavaSymbolName, AnnotatedJavaType> patternRequestParam = getPatternRequestParam(false);
        methodParamNames.add(patternRequestParam.getKey());
        methodParamNames.add(PAGE_PARAM_NAME);
        methodParamNames.add(SIZE_PARAM_NAME);
        methodParamNames.add(modelRequestParam.getKey());
        methodParamNames.add(httpServletRequest.getKey());

        // Create method body
        InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder();
        String entityNamePlural = entityTypeDetails.getPlural();

        bodyBuilder.appendFormalLine("delete(".concat(formBackingObjectIdField.getFieldName().getSymbolName())
                .concat(", page, size, uiModel);"));
        bodyBuilder.appendFormalLine(RETURN_TXT.concat("redirect:/").concat(entityNamePlural.toLowerCase())
                .concat("?gvnixform&\" + refererQuery(" + httpServletRequest.getKey() + ");"));

        MethodMetadataBuilder methodBuilder = new MethodMetadataBuilder(getId(), Modifier.PUBLIC, methodName,
                JavaType.STRING, methodParamTypes, methodParamNames, bodyBuilder);

        // Get Method RequestMapping annotation
        List<AnnotationAttributeValue<?>> requestMappingAttributes = new ArrayList<AnnotationAttributeValue<?>>();
        requestMappingAttributes.add(new StringAttributeValue(VALUE_ATTRIBUTE_NAME,
                "/{" + formBackingObjectIdField.getFieldName().getSymbolName() + "}"));
        List<AnnotationAttributeValue<? extends Object>> paramValues = new ArrayList<AnnotationAttributeValue<? extends Object>>();
        paramValues.add(getPatternParamRequestMapping(patternName));
        requestMappingAttributes.add(new ArrayAttributeValue<AnnotationAttributeValue<? extends Object>>(
                PARAMS_ATTRIBUTE_NAME, paramValues));
        requestMappingAttributes.add(getMethodRequestMapping(RequestMethod.DELETE));
        requestMappingAttributes.add(PRODUCES_PARAM_MAPPING);
        AnnotationMetadataBuilder requestMapping = new AnnotationMetadataBuilder(RQST_MAP_ANN_TYPE,
                requestMappingAttributes);
        List<AnnotationMetadataBuilder> annotations = new ArrayList<AnnotationMetadataBuilder>();
        annotations.add(requestMapping);

        methodBuilder.setAnnotations(annotations);

        method = methodBuilder.build();
        controllerMethods.add(method);
        return method;
    }

    /**
     * Get refererQuery method.
     * <p>
     * Get referer url from request header and return query part without "form"
     * parameter. Remove "form" parameter is required to avoid mapping with
     * create and update Roo patterns.
     * </p>
     * 
     * @return Referer url query part without "form" param
     */
    protected MethodMetadata getRefererQueryMethod() {

        // Specify the desired method name
        JavaSymbolName methodName = new JavaSymbolName("refererQuery");

        // Define method parameter types
        List<AnnotatedJavaType> methodParamTypes = new ArrayList<AnnotatedJavaType>();

        Entry<JavaSymbolName, AnnotatedJavaType> httpServletRequest = HTTP_SERVLET_REQUEST_PARAM;
        methodParamTypes.add(httpServletRequest.getValue());

        MethodMetadata method = methodExists(methodName, methodParamTypes);
        if (method != null) {
            // If it already exists, just return null and omit its
            // generation via the ITD
            return null;
        }

        // Define method parameter names httpServletRequest
        List<JavaSymbolName> methodParamNames = new ArrayList<JavaSymbolName>();
        methodParamNames.add(httpServletRequest.getKey());

        // Create method body
        InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder();

        bodyBuilder.appendFormalLine("String url = \"\";");
        bodyBuilder
                .appendFormalLine("String referer = " + httpServletRequest.getKey() + ".getHeader(\"Referer\");");

        JavaType stringUtils = new JavaType("org.springframework.util.StringUtils");
        bodyBuilder.appendFormalLine("if ("
                .concat(stringUtils.getNameIncludingTypeParameters(false, builder.getImportRegistrationResolver()))
                .concat(".hasText(referer)) {"));
        bodyBuilder.indent();
        bodyBuilder.appendFormalLine("try {");
        bodyBuilder.indent();
        JavaType netURL = new JavaType("java.net.URL");
        bodyBuilder.appendFormalLine("String[] params = new "
                .concat(netURL.getNameIncludingTypeParameters(false, builder.getImportRegistrationResolver()))
                .concat("(referer).getQuery().split(\"&\");"));
        bodyBuilder.appendFormalLine("for (String param : params) {");
        bodyBuilder.indent();
        bodyBuilder
                .appendFormalLine("if (!param.equals(\"" + getFormParamRequestMapping(true).getValue() + "\")) {");
        bodyBuilder.indent();
        bodyBuilder.appendFormalLine("url = url.concat(param).concat(\"&\");");
        bodyBuilder.indentRemove();
        bodyBuilder.appendFormalLine("}");
        bodyBuilder.indentRemove();
        bodyBuilder.appendFormalLine("}");
        bodyBuilder.appendFormalLine("if (url.endsWith(\"&\")) { url = url.substring(0, url.length() - 1); }");
        bodyBuilder.indentRemove();
        JavaType exception = new JavaType("java.lang.Exception");
        bodyBuilder.appendFormalLine("} catch ( "
                .concat(exception.getNameIncludingTypeParameters(false, builder.getImportRegistrationResolver()))
                .concat(" e ) {"));
        bodyBuilder.appendFormalLine("}");
        bodyBuilder.indentRemove();
        bodyBuilder.appendFormalLine("}");
        bodyBuilder.appendFormalLine("return url;");

        MethodMetadataBuilder methodBuilder = new MethodMetadataBuilder(getId(), 0, methodName, JavaType.STRING,
                methodParamTypes, methodParamNames, bodyBuilder);

        method = methodBuilder.build();
        controllerMethods.add(method);
        return method;
    }

    protected List<AnnotationMetadataBuilder> getRequestMappingAnnotationCreateUpdate(RequestMethod requestMethod,
            String patternName) {
        // Get Method RequestMapping annotation
        List<AnnotationAttributeValue<?>> requestMappingAttributes = new ArrayList<AnnotationAttributeValue<?>>();
        List<AnnotationAttributeValue<? extends Object>> paramValues = new ArrayList<AnnotationAttributeValue<? extends Object>>();
        paramValues.add(getPatternParamRequestMapping(patternName));
        requestMappingAttributes.add(new ArrayAttributeValue<AnnotationAttributeValue<? extends Object>>(
                PARAMS_ATTRIBUTE_NAME, paramValues));
        requestMappingAttributes.add(getMethodRequestMapping(requestMethod));
        requestMappingAttributes.add(PRODUCES_PARAM_MAPPING);
        AnnotationMetadataBuilder requestMapping = new AnnotationMetadataBuilder(RQST_MAP_ANN_TYPE,
                requestMappingAttributes);
        List<AnnotationMetadataBuilder> annotations = new ArrayList<AnnotationMetadataBuilder>();
        annotations.add(requestMapping);
        return annotations;
    }

    /**
     * Enumeration of HTTP Request method types
     */
    public enum RequestMethod {
        DELETE, PUT, POST, GET;
    }

    /**
     * Enumeration of some persistence method names
     */
    public enum PersistenceMethod {
        REMOVE("remove"), PERSIST("persist"), MERGE("merge");

        private String name;

        private PersistenceMethod(String name) {
            this.name = name;
        }

        public String getName() {
            return this.name;
        }
    }

    /**
     * If there is patterns of given WebPatternType defined in GvNIXPattern it
     * returns its names, empty list otherwise
     * 
     * @param patternType
     * @param definedPatternsList
     * @return Some type pattern names list
     */
    protected List<String> getPatternTypeDefined(WebPatternType patternType, List<String> definedPatternsList) {
        List<String> patternList = new ArrayList<String>();
        for (String definedPattern : definedPatternsList) {
            if (definedPattern.split("=")[1].equalsIgnoreCase(patternType.name())) {
                patternList.add(definedPattern.split("=")[0]);
            }
        }
        return patternList;
    }

    protected FieldMetadataBuilder getGvNIXPatternField() {
        return new FieldMetadataBuilder(getId(), Modifier.PRIVATE | Modifier.STATIC | Modifier.FINAL,
                new JavaSymbolName("PATTERN_PARAM_NAME"), JavaType.STRING, "\"" + GVNIXPATTERN + "\"");
    }

    protected FieldMetadata getDefinedPatternField() {
        String definedPatternIds = "";
        for (String definedPattern : patterns) {
            if (definedPatternIds.length() > 0) {
                definedPatternIds = definedPatternIds.concat(", ");
            }
            definedPatternIds = definedPatternIds.concat("\"").concat(definedPattern.split("=")[0]).concat("\"");
        }

        FieldMetadataBuilder fmb = null;

        JavaSymbolName fieldName = new JavaSymbolName("definedPatterns");
        JavaType stringArray = new JavaType(JavaType.STRING.getFullyQualifiedTypeName(), 1, DataType.TYPE, null,
                null);

        FieldMetadata field = fieldExists(fieldName, stringArray);
        if (field != null) {
            String initializer = field.getFieldInitializer();
            initializer = initializer.replace("{", "").replace("}", "").replace(" ", "").replace("\"", "");
            for (String pattern : initializer.split(",")) {
                if (!definedPatternIds.contains(pattern)) {
                    if (definedPatternIds.length() > 0) {
                        definedPatternIds = definedPatternIds.concat(", ");
                    }
                    definedPatternIds = definedPatternIds.concat("\"").concat(pattern).concat("\"");
                }
            }
            fmb = new FieldMetadataBuilder(field);
            fmb.setFieldInitializer("{ ".concat(definedPatternIds).concat(" }"));
        }

        if (fmb == null) {
            fmb = new FieldMetadataBuilder(getId(), Modifier.PRIVATE | Modifier.STATIC | Modifier.FINAL, fieldName,
                    stringArray, "{ ".concat(definedPatternIds).concat(" }"));
        }

        field = fmb.build();
        controllerFields.add(field);
        return field;
    }

    private FieldMetadata fieldExists(JavaSymbolName fieldName, JavaType fieldType) {
        for (FieldMetadata fieldMetadata : controllerFields) {
            if (fieldMetadata.getFieldName().equals(fieldName) && fieldType.equals(fieldMetadata.getFieldType())) {
                return fieldMetadata;
            }
        }
        return null;
    }

    protected void addStaticFields() {
        FieldMetadataBuilder field = new FieldMetadataBuilder(getId(),
                Modifier.PRIVATE | Modifier.STATIC | Modifier.FINAL, new JavaSymbolName("INDEX_PARAM_NAME"),
                JavaType.STRING, "\"index\"");
        builder.addField(field);

        field = new FieldMetadataBuilder(getId(), Modifier.PRIVATE | Modifier.STATIC | Modifier.FINAL,
                new JavaSymbolName("FORM_PARAM_NAME"), JavaType.STRING, "\"gvnixform\"");
        builder.addField(field);

        field = new FieldMetadataBuilder(getId(), Modifier.PRIVATE | Modifier.STATIC | Modifier.FINAL,
                new JavaSymbolName("REFERER_PARAM_NAME"), JavaType.STRING, "\"Referer\"");
        builder.addField(field);
    }

    /**
     * Returns the method handling requests of a given gvnixpattern of type
     * tabular
     * 
     * @return
     */
    protected MethodMetadata getTabularMethod(String patternName) {
        // Specify the desired method name
        JavaSymbolName methodName = new JavaSymbolName("tabular" + patternName);

        // Define method parameter types
        // @RequestParam(value =
        // "fu_org_gvnix_tiendavirtual_domain_ProductoPage", required = false)
        // Integer page,
        // @RequestParam(value =
        // "fu_org_gvnix_tiendavirtual_domain_ProductoPageSize", required =
        // false) Integer pageSize,
        // @RequestParam(value = "gvnixpattern", required = true) String
        // pattern, HttpServletRequest req, Model uiModel)
        List<AnnotatedJavaType> methodParamTypes = new ArrayList<AnnotatedJavaType>();

        methodParamTypes.add(getPageRequestParam().getValue());
        methodParamTypes.add(getPageSizeRequestParam().getValue());
        methodParamTypes.add(getPatternRequestParam(true).getValue());
        Entry<JavaSymbolName, AnnotatedJavaType> modelRequestParam = MODEL_REQUEST_PARAM;
        methodParamTypes.add(modelRequestParam.getValue());
        Entry<JavaSymbolName, AnnotatedJavaType> httpServletRequest = HTTP_SERVLET_REQUEST_PARAM;
        methodParamTypes.add(httpServletRequest.getValue());

        MethodMetadata method = methodExists(methodName, methodParamTypes);
        if (method != null) {
            // If it already exists, just return null and omit its
            // generation via the ITD
            return null;
        }

        // Define method parameter names
        // pattern, uiModel, httpServletRequest
        List<JavaSymbolName> methodParamNames = new ArrayList<JavaSymbolName>();
        Entry<JavaSymbolName, AnnotatedJavaType> patternRequestParam = getPatternRequestParam(false);
        Entry<JavaSymbolName, AnnotatedJavaType> pageRequestParam = getPageRequestParam();
        Entry<JavaSymbolName, AnnotatedJavaType> pageSizeRequestParam = getPageSizeRequestParam();

        methodParamNames.add(pageRequestParam.getKey());
        methodParamNames.add(pageSizeRequestParam.getKey());
        methodParamNames.add(patternRequestParam.getKey());
        methodParamNames.add(modelRequestParam.getKey());
        methodParamNames.add(httpServletRequest.getKey());

        // Create method body
        InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder();
        String entityNamePlural = entityTypeDetails.getPlural();

        List<JavaType> typeParams = new ArrayList<JavaType>();
        typeParams.add(entity);

        // Add date validation pattern to model if some date type field exists
        if (!entityDateTypes.isEmpty()) {
            bodyBuilder.appendFormalLine("addDateTimeFormatPatterns(uiModel);");
        }

        bodyBuilder.appendFormalLine("int sizeNo = pageSize == null ? 10 : pageSize.intValue();");
        bodyBuilder.appendFormalLine("float nrOfPages = (float) "
                .concat(entity.getNameIncludingTypeParameters(false, builder.getImportRegistrationResolver()))
                .concat(".")
                .concat(entityTypeDetails.getPersistenceDetails().getCountMethod().getMethodName().concat("()"))
                .concat(" / sizeNo;"));
        bodyBuilder.appendFormalLine(
                "uiModel.addAttribute(\"maxPages\", (int) ((nrOfPages > (int) nrOfPages || nrOfPages == 0.0) ? nrOfPages + 1 : nrOfPages));");
        bodyBuilder.appendFormalLine(
                "final int firstResult = page == null || page < 1 ? 0 : (page.intValue() - 1) * sizeNo;");
        JavaType javaUtilList = new JavaType("java.util.List", 0, DataType.TYPE, null, typeParams);
        bodyBuilder.appendFormalLine(javaUtilList
                .getNameIncludingTypeParameters(false, builder.getImportRegistrationResolver()).concat(" ")
                .concat(entityNamePlural.toLowerCase()).concat(" = ")
                .concat(entity.getNameIncludingTypeParameters(false, builder.getImportRegistrationResolver()))
                .concat(".").concat(entityTypeDetails.getPersistenceDetails().getFindEntriesMethod().getMethodName()
                        .concat("(firstResult, sizeNo);")));

        bodyBuilder.appendFormalLine("if (".concat(entityNamePlural.toLowerCase()).concat(
                ".isEmpty() && httpServletRequest.getSession().getAttribute(\"dialogMessage\") == null) {"));
        bodyBuilder.indent();
        bodyBuilder.appendFormalLine(ADD_ATTR.concat(entityNamePlural.toLowerCase()).concat("Tab\", null);"));

        addBodyLinesForDialogMessage(bodyBuilder, DialogType.Info, "message_entitynotfound_problemdescription");

        bodyBuilder.appendFormalLine(
                RETURN_TXT.concat(entityNamePlural.toLowerCase()).concat(CONCAT + GVNIXPATTERN + ");"));
        bodyBuilder.indentRemove();
        bodyBuilder.appendFormalLine("}");

        // May we need to populate some Model Attributes with the data of
        // related entities
        addBodyLinesPopulatingRelatedEntitiesData(bodyBuilder);

        bodyBuilder.appendFormalLine(ADD_ATTR.concat(entityNamePlural.toLowerCase()).concat("Tab\", ")
                .concat(entityNamePlural.toLowerCase()).concat(");"));
        bodyBuilder.appendFormalLine(
                RETURN_TXT.concat(entityNamePlural.toLowerCase()).concat(CONCAT + GVNIXPATTERN + ");"));

        MethodMetadataBuilder methodBuilder = new MethodMetadataBuilder(getId(), Modifier.PUBLIC, methodName,
                JavaType.STRING, methodParamTypes, methodParamNames, bodyBuilder);

        // Get Method RequestMapping annotation
        List<AnnotationAttributeValue<?>> requestMappingAttributes = new ArrayList<AnnotationAttributeValue<?>>();
        List<AnnotationAttributeValue<? extends Object>> paramValues = new ArrayList<AnnotationAttributeValue<? extends Object>>();
        paramValues.add(getPatternParamRequestMapping(patternName));
        paramValues.add(getFormParamRequestMapping(false));
        paramValues.add(new StringAttributeValue(VALUE_ATTRIBUTE_NAME, "!find"));
        requestMappingAttributes.add(new ArrayAttributeValue<AnnotationAttributeValue<? extends Object>>(
                PARAMS_ATTRIBUTE_NAME, paramValues));
        requestMappingAttributes.add(getMethodRequestMapping(RequestMethod.GET));
        requestMappingAttributes.add(PRODUCES_PARAM_MAPPING);
        AnnotationMetadataBuilder requestMapping = new AnnotationMetadataBuilder(RQST_MAP_ANN_TYPE,
                requestMappingAttributes);
        List<AnnotationMetadataBuilder> annotations = new ArrayList<AnnotationMetadataBuilder>();
        annotations.add(requestMapping);
        methodBuilder.setAnnotations(annotations);

        method = methodBuilder.build();
        controllerMethods.add(method);
        return method;
    }

    protected MethodMetadata getRegisterMethod(String patternName) {
        // Specify the desired method name
        JavaSymbolName methodName = new JavaSymbolName("register" + patternName);

        // Define method parameter types
        // @RequestParam(value = "gvnixpattern", required = true) String
        // pattern, HttpServletRequest req, Model uiModel)
        List<AnnotatedJavaType> methodParamTypes = new ArrayList<AnnotatedJavaType>();

        List<AnnotationAttributeValue<?>> reqParamAttrIndex = new ArrayList<AnnotationAttributeValue<?>>();
        reqParamAttrIndex.add(new StringAttributeValue(VALUE_ATTRIBUTE_NAME, "index"));
        reqParamAttrIndex.add(new BooleanAttributeValue(new JavaSymbolName(REQUIRED_TXT), true));
        List<AnnotationMetadata> methodAttrIndexAnnotations = new ArrayList<AnnotationMetadata>();
        AnnotationMetadataBuilder methodAttrIndexAnnotation = new AnnotationMetadataBuilder(
                new JavaType(RQST_PARAM_PATH), reqParamAttrIndex);
        methodAttrIndexAnnotations.add(methodAttrIndexAnnotation.build());

        methodParamTypes.add(new AnnotatedJavaType(JavaType.LONG_OBJECT, methodAttrIndexAnnotations));
        methodParamTypes.add(getPatternRequestParam(true).getValue());
        Entry<JavaSymbolName, AnnotatedJavaType> httpServletRequest = HTTP_SERVLET_REQUEST_PARAM;
        methodParamTypes.add(httpServletRequest.getValue());
        Entry<JavaSymbolName, AnnotatedJavaType> modelRequestParam = MODEL_REQUEST_PARAM;
        methodParamTypes.add(modelRequestParam.getValue());

        MethodMetadata method = methodExists(methodName, methodParamTypes);
        if (method != null) {
            // If it already exists, just return null and omit its
            // generation via the ITD
            return null;
        }

        // Define method parameter names
        // pattern, uiModel, httpServletRequest
        List<JavaSymbolName> methodParamNames = new ArrayList<JavaSymbolName>();
        methodParamNames.add(new JavaSymbolName("index"));
        Entry<JavaSymbolName, AnnotatedJavaType> patternRequestParam = getPatternRequestParam(false);
        methodParamNames.add(patternRequestParam.getKey());
        methodParamNames.add(httpServletRequest.getKey());
        methodParamNames.add(modelRequestParam.getKey());

        // Create method body
        InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder();
        String entityNamePlural = entityTypeDetails.getPlural();
        String entityName = uncapitalize(entity.getSimpleTypeName());

        List<JavaType> typeParams = new ArrayList<JavaType>();
        typeParams.add(entity);

        // Add date validation pattern to model if some date type field exists
        if (!entityDateTypes.isEmpty()) {
            bodyBuilder.appendFormalLine("addDateTimeFormatPatterns(uiModel);");
        }

        // Check entity count
        bodyBuilder.appendFormalLine("// Check entity count");
        bodyBuilder.appendFormalLine(JavaType.LONG_PRIMITIVE.getNameIncludingTypeParameters().concat(" count = ")
                .concat(entity.getSimpleTypeName()).concat(".")
                .concat(entityTypeDetails.getPersistenceDetails().getCountMethod().getMethodName()).concat("();"));

        bodyBuilder.appendFormalLine("if (count < 1) {");
        bodyBuilder.indent();
        bodyBuilder.appendFormalLine("// None register found");
        bodyBuilder.appendFormalLine(ADD_ATTR.concat(entityName.toLowerCase()).concat("\", null);"));

        addBodyLinesForDialogMessage(bodyBuilder, DialogType.Info, "message_entitynotfound_problemdescription");

        bodyBuilder.appendFormalLine(
                RETURN_TXT.concat(entityNamePlural.toLowerCase()).concat(CONCAT + GVNIXPATTERN + ");"));
        bodyBuilder.indentRemove();
        bodyBuilder.appendFormalLine("}");

        bodyBuilder.appendFormalLine(JavaType.LONG_PRIMITIVE.getNameIncludingTypeParameters()
                .concat(" curIndex = index == null ? 0 : index;"));

        bodyBuilder.appendFormalLine("// Check index");
        bodyBuilder.appendFormalLine("if (curIndex > count) {");
        bodyBuilder.indent();
        bodyBuilder.appendFormalLine("curIndex = count;");
        bodyBuilder.indentRemove();
        bodyBuilder.appendFormalLine("}");

        JavaType javaUtilList = new JavaType("java.util.List", 0, DataType.TYPE, null, typeParams);
        bodyBuilder.appendFormalLine(javaUtilList
                .getNameIncludingTypeParameters(false, builder.getImportRegistrationResolver()).concat(" ")
                .concat(entityNamePlural.toLowerCase()).concat(" = ")
                .concat(entity.getNameIncludingTypeParameters(false, builder.getImportRegistrationResolver()))
                .concat(".").concat(entityTypeDetails.getPersistenceDetails().getFindEntriesMethod().getMethodName()
                        .concat("((int)curIndex -1, 1);")));

        bodyBuilder.appendFormalLine("if (".concat(entityNamePlural.toLowerCase()).concat(
                ".isEmpty() && httpServletRequest.getSession().getAttribute(\"dialogMessage\") == null) {"));
        bodyBuilder.indent();
        bodyBuilder.appendFormalLine(ADD_ATTR.concat(entityName.toLowerCase()).concat("\", null);"));

        addBodyLinesForDialogMessage(bodyBuilder, DialogType.Info, "message_entitynotfound_problemdescription");

        bodyBuilder.appendFormalLine(
                RETURN_TXT.concat(entityNamePlural.toLowerCase()).concat(CONCAT + GVNIXPATTERN + ");"));
        bodyBuilder.indentRemove();
        bodyBuilder.appendFormalLine("}");

        bodyBuilder.appendFormalLine(entity.getSimpleTypeName().concat(" ").concat(entityName.toLowerCase())
                .concat(" = ").concat(entityNamePlural.toLowerCase()).concat(".get(0);"));

        bodyBuilder.appendFormalLine(ADD_ATTR.concat(entityName.toLowerCase()).concat(QUOTE_COMMA)
                .concat(entityName.toLowerCase()).concat(");"));

        bodyBuilder.appendFormalLine("uiModel.addAttribute(\"maxEntities".concat("\", count);"));

        bodyBuilder.appendFormalLine("uiModel.addAttribute(\"index".concat("\", curIndex);"));

        // May we need to populate some Model Attributes with the data of
        // related entities
        addBodyLinesPopulatingRelatedEntitiesData(bodyBuilder);

        // Add date validation pattern to model if some date type field exists
        addBodyLinesRegisteringRelatedEntitiesDateTypesFormat(bodyBuilder);

        bodyBuilder.appendFormalLine(
                RETURN_TXT.concat(entityNamePlural.toLowerCase()).concat(CONCAT + GVNIXPATTERN + ");"));

        MethodMetadataBuilder methodBuilder = new MethodMetadataBuilder(getId(), Modifier.PUBLIC, methodName,
                JavaType.STRING, methodParamTypes, methodParamNames, bodyBuilder);

        // Get Method RequestMapping annotation
        List<AnnotationAttributeValue<?>> requestMappingAttributes = new ArrayList<AnnotationAttributeValue<?>>();
        List<AnnotationAttributeValue<? extends Object>> paramValues = new ArrayList<AnnotationAttributeValue<? extends Object>>();
        paramValues.add(getPatternParamRequestMapping(patternName));
        paramValues.add(new StringAttributeValue(new JavaSymbolName("ignored"), "gvnixform"));
        paramValues.add(new StringAttributeValue(VALUE_ATTRIBUTE_NAME, "!find"));
        requestMappingAttributes.add(new ArrayAttributeValue<AnnotationAttributeValue<? extends Object>>(
                PARAMS_ATTRIBUTE_NAME, paramValues));
        requestMappingAttributes.add(getMethodRequestMapping(RequestMethod.GET));
        requestMappingAttributes.add(PRODUCES_PARAM_MAPPING);
        AnnotationMetadataBuilder requestMapping = new AnnotationMetadataBuilder(RQST_MAP_ANN_TYPE,
                requestMappingAttributes);
        List<AnnotationMetadataBuilder> annotations = new ArrayList<AnnotationMetadataBuilder>();
        annotations.add(requestMapping);
        methodBuilder.setAnnotations(annotations);

        method = methodBuilder.build();
        controllerMethods.add(method);
        return method;
    }

    /**
     * Adds body lines which populates required data for the related entities.
     * <p>
     * This lines are like Roo's ModelAttribute methods but registering the
     * model attributes directly in the method. The model attributes are set
     * with data (usually Sets) needed by the entity related entities.
     * 
     * @param bodyBuilder
     */
    protected void addBodyLinesPopulatingRelatedEntitiesData(InvocableMemberBodyBuilder bodyBuilder) {
        for (JavaType type : relatedFields.keySet()) {
            JavaTypeMetadataDetails javaTypeMd = relatedFields.get(type);
            JavaTypePersistenceMetadataDetails javaTypePersistenceMd = javaTypeMd.getPersistenceDetails();
            if (javaTypePersistenceMd != null && javaTypePersistenceMd.getFindAllMethod() != null) {
                bodyBuilder.appendFormalLine(ADD_ATTR.concat(javaTypeMd.getPlural().toLowerCase())
                        .concat(QUOTE_COMMA)
                        .concat(type.getNameIncludingTypeParameters(false, builder.getImportRegistrationResolver()))
                        .concat(".").concat(javaTypePersistenceMd.getFindAllMethod().getMethodName())
                        .concat("());"));
            } else if (javaTypeMd.isEnumType()) {
                JavaType arrays = new JavaType("java.util.Arrays");
                bodyBuilder.appendFormalLine(ADD_ATTR.concat(javaTypeMd.getPlural().toLowerCase())
                        .concat(QUOTE_COMMA)
                        .concat(arrays.getNameIncludingTypeParameters(false,
                                builder.getImportRegistrationResolver()))
                        .concat(".asList(")
                        .concat(type.getNameIncludingTypeParameters(false, builder.getImportRegistrationResolver()))
                        .concat(".class.getEnumConstants()));"));
            }
        }
    }

    /**
     * Adds body lines registering DateTime formats of the related entities.
     * <p>
     * If related entity has DateTime fields, we need to register, as model
     * attribute, the DateTime format in order to render, validate and send
     * those fields in the right format. This is similar to the Roo's
     * DateTimeFormatPatterns helper method.
     * 
     * @param bodyBuilder
     */
    private void addBodyLinesRegisteringRelatedEntitiesDateTypesFormat(InvocableMemberBodyBuilder bodyBuilder) {
        for (Entry<JavaType, Map<JavaSymbolName, DateTimeFormatDetails>> jTypeDTimeFormatDet : relatedDates
                .entrySet()) {

            String relatedEntityName = uncapitalize(jTypeDTimeFormatDet.getKey().getSimpleTypeName());
            for (Entry<JavaSymbolName, DateTimeFormatDetails> jSymNDTimeFormDet : jTypeDTimeFormatDet.getValue()
                    .entrySet()) {

                String pattern;
                if (jSymNDTimeFormDet.getValue().pattern != null) {
                    pattern = "\"" + jSymNDTimeFormDet.getValue().pattern + "\"";
                } else {
                    JavaType dateTimeFormat = new JavaType("org.joda.time.format.DateTimeFormat");
                    String dateTimeFormatSimple = dateTimeFormat.getNameIncludingTypeParameters(false,
                            builder.getImportRegistrationResolver());
                    JavaType localeContextHolder = new JavaType(
                            "org.springframework.context.i18n.LocaleContextHolder");
                    String localeContextHolderSimple = localeContextHolder.getNameIncludingTypeParameters(false,
                            builder.getImportRegistrationResolver());
                    pattern = dateTimeFormatSimple + ".patternForStyle(\"" + jSymNDTimeFormDet.getValue().style
                            + QUOTE_COMMA + localeContextHolderSimple + ".getLocale())";
                }
                bodyBuilder.appendFormalLine(ADD_ATTR + relatedEntityName + "_"
                        + jSymNDTimeFormDet.getKey().getSymbolName().toLowerCase() + "_date_format\", " + pattern
                        + ");");
            }
        }
    }

    enum DialogType {
        Error, Info, Alert, Suggest;
    }

    /**
     * Using the given bodyBuilder adds code lines for set a Session Attribute
     * with an instance of Dialog bean
     * 
     * @param bodyBuilder
     * @param dialogType
     * @param messageDescriptionCode
     */
    protected void addBodyLinesForDialogMessage(InvocableMemberBodyBuilder bodyBuilder, DialogType dialogType,
            String messageDescriptionCode) {

        // TODO Cdigo duplicado con mtodo contiguo

        JavaType httpSession = new JavaType("javax.servlet.http.HttpSession");
        bodyBuilder.appendFormalLine(
                httpSession.getNameIncludingTypeParameters(false, builder.getImportRegistrationResolver())
                        .concat(" session = " + HTTP_SERVLET_REQUEST_PARAM.getKey() + ".getSession();"));
        JavaType dialogJavaType = new JavaType(this.aspectPackage.concat(".dialog.Dialog"));
        JavaType dialogTypeJavaType = new JavaType(
                dialogJavaType.getFullyQualifiedTypeName().concat(".DialogType"));
        bodyBuilder.appendFormalLine(dialogJavaType
                .getNameIncludingTypeParameters(false, builder.getImportRegistrationResolver())
                .concat(" dialog = new Dialog(")
                .concat(dialogTypeJavaType
                        .getNameIncludingTypeParameters(false, builder.getImportRegistrationResolver()).concat(".")
                        .concat(dialogType.name()).concat(", \"message_").concat(dialogType.name().toLowerCase())
                        .concat("_title\", \"").concat(messageDescriptionCode).concat("\");")));
        bodyBuilder.appendFormalLine("session.setAttribute(\"dialogMessage\", dialog);");
    }

    /**
     * Using the given bodyBuilder adds code lines for set a Session Attribute
     * with an instance of Dialog bean
     * 
     * @param bodyBuilder
     * @param dialogType
     * @param messageDescriptionCode
     */
    protected void addBodyLinesForDialogBinding(InvocableMemberBodyBuilder bodyBuilder, DialogType dialogType,
            String messageDescriptionCode) {

        // TODO Cdigo duplicado con mtodo contiguo

        JavaType httpSession = new JavaType("javax.servlet.http.HttpSession");
        bodyBuilder.appendFormalLine(
                httpSession.getNameIncludingTypeParameters(false, builder.getImportRegistrationResolver())
                        .concat(" session = " + HTTP_SERVLET_REQUEST_PARAM.getKey() + ".getSession();"));
        JavaType dialogJavaType = new JavaType(this.aspectPackage.concat(".dialog.Dialog"));
        JavaType dialogTypeJavaType = new JavaType(
                dialogJavaType.getFullyQualifiedTypeName().concat(".DialogType"));
        bodyBuilder.appendFormalLine(
                dialogJavaType.getNameIncludingTypeParameters(false, builder.getImportRegistrationResolver())
                        .concat(" dialog = new Dialog(")
                        .concat(dialogTypeJavaType
                                .getNameIncludingTypeParameters(false, builder.getImportRegistrationResolver())
                                .concat(".").concat(dialogType.name()).concat(", \"message_")
                                .concat(dialogType.name().toLowerCase()).concat("_title\", \"")
                                .concat(messageDescriptionCode).concat("\", bindingResult.getFieldErrors());")));
        bodyBuilder.appendFormalLine("session.setAttribute(\"dialogMessage\", dialog);");
    }

    /**
     * Generates the MethodMedata of createList() method for ITD
     * 
     * @return
     */
    protected MethodMetadata getCreateListMethod() {
        // Here we're sure that Entity.createList() method exists

        // Specify the desired method name
        JavaSymbolName methodName = new JavaSymbolName("createList");

        // Define method annotations
        List<AnnotationAttributeValue<?>> requestMappingAttributes = getRequestMappingAttributes(
                RequestMethod.POST);
        // Define method parameter types
        List<AnnotatedJavaType> methodParamTypes = getMethodParameterTypes();

        // Check if a method with the same signature already exists in the
        // target type
        MethodMetadata method = methodExists(methodName, methodParamTypes);
        if (method != null) {
            // If it already exists, just return null and omit its
            // generation via the ITD
            return null;
        }

        // Define method parameter names
        List<JavaSymbolName> methodParamNames = getMethodParameterNames();

        // Create method body
        InvocableMemberBodyBuilder bodyBuilder = getMethodBodyBuilderBinding(PersistenceMethod.PERSIST);
        MethodMetadataBuilder methodBuilder = new MethodMetadataBuilder(getId(), Modifier.PUBLIC, methodName,
                JavaType.STRING, methodParamTypes, methodParamNames, bodyBuilder);

        // Get Method RequestMapping annotation
        AnnotationMetadataBuilder requestMapping = new AnnotationMetadataBuilder(RQST_MAP_ANN_TYPE,
                requestMappingAttributes);
        List<AnnotationMetadataBuilder> annotations = new ArrayList<AnnotationMetadataBuilder>();
        annotations.add(requestMapping);
        methodBuilder.setAnnotations(annotations);

        method = methodBuilder.build();
        controllerMethods.add(method);
        return method;
    }

    /**
     * Generates the MethodMedata of updateList() method for ITD
     * 
     * @return
     */
    protected MethodMetadata getUpdateListMethod() {
        // Here we're sure that Entity.updateList() method exists

        // Specify the desired method name
        JavaSymbolName methodName = new JavaSymbolName("updateList");

        // Define method annotations
        List<AnnotationAttributeValue<?>> requestMappingAttributes = getRequestMappingAttributes(RequestMethod.PUT);
        // Define method parameter types
        List<AnnotatedJavaType> methodParamTypes = getMethodParameterTypes();

        // Check if a method with the same signature already exists in the
        // target type
        MethodMetadata method = methodExists(methodName, methodParamTypes);
        if (method != null) {
            // If it already exists, just return null and omit its
            // generation via the ITD
            return null;
        }

        // Define method parameter names
        List<JavaSymbolName> methodParamNames = getMethodParameterNames();

        // Create method body
        InvocableMemberBodyBuilder bodyBuilder = getMethodBodyBuilderBinding(PersistenceMethod.MERGE);
        MethodMetadataBuilder methodBuilder = new MethodMetadataBuilder(getId(), Modifier.PUBLIC, methodName,
                JavaType.STRING, methodParamTypes, methodParamNames, bodyBuilder);

        // Get Method RequestMapping annotation
        AnnotationMetadataBuilder requestMapping = new AnnotationMetadataBuilder(RQST_MAP_ANN_TYPE,
                requestMappingAttributes);
        List<AnnotationMetadataBuilder> annotations = new ArrayList<AnnotationMetadataBuilder>();
        annotations.add(requestMapping);
        methodBuilder.setAnnotations(annotations);

        method = methodBuilder.build();
        controllerMethods.add(method);
        return method;
    }

    /**
     * Generates the MethodMedata of deleteList() method for ITD
     * 
     * @return
     */
    protected MethodMetadata getDeleteListMethod() {
        // Here we're sure that Entity.deleteList() method exists

        // Specify the desired method name
        JavaSymbolName methodName = new JavaSymbolName("deleteList");

        // Define method annotations
        List<AnnotationAttributeValue<?>> requestMappingAttributes = getRequestMappingAttributes(
                RequestMethod.DELETE);
        // Define method parameter types
        List<AnnotatedJavaType> methodParamTypes = getMethodParameterTypes();

        // Check if a method with the same signature already exists in the
        // target type
        MethodMetadata method = methodExists(methodName, methodParamTypes);
        if (method != null) {
            // If it already exists, just return null and omit its
            // generation via the ITD
            return null;
        }

        // Define method parameter names
        List<JavaSymbolName> methodParamNames = getMethodParameterNames();

        // Create method body
        InvocableMemberBodyBuilder bodyBuilder = getMethodBodyBuilder(PersistenceMethod.REMOVE);
        MethodMetadataBuilder methodBuilder = new MethodMetadataBuilder(getId(), Modifier.PUBLIC, methodName,
                JavaType.STRING, methodParamTypes, methodParamNames, bodyBuilder);

        // Get Method RequestMapping annotation
        AnnotationMetadataBuilder requestMapping = new AnnotationMetadataBuilder(RQST_MAP_ANN_TYPE,
                requestMappingAttributes);
        List<AnnotationMetadataBuilder> annotations = new ArrayList<AnnotationMetadataBuilder>();
        annotations.add(requestMapping);
        methodBuilder.setAnnotations(annotations);

        method = methodBuilder.build();
        controllerMethods.add(method);
        return method;
    }

    /**
     * Generates the MethodMedata of filterList(EntityList) method for ITD
     * <p>
     * The generated method removes from the passed list the objects not
     * involved in the operation.
     * </p>
     * 
     * @return
     */
    protected MethodMetadata getFilterListMethod() {
        // Specify the desired method name
        JavaSymbolName methodName = new JavaSymbolName("filterList");

        // Define method parameter types
        List<AnnotatedJavaType> parameterTypes = new ArrayList<AnnotatedJavaType>();
        List<AnnotationMetadata> annotations = new ArrayList<AnnotationMetadata>();
        parameterTypes.add(new AnnotatedJavaType(getEntityList(), annotations));

        // Check if a method with the same signature already exists in the
        // target type
        MethodMetadata method = methodExists(methodName, parameterTypes);
        if (method != null) {
            // If it already exists, just return null and omit its
            // generation via the ITD
            return null;
        }

        // Define method parameter names
        List<JavaSymbolName> parameterNames = new ArrayList<JavaSymbolName>();
        parameterNames.add(new JavaSymbolName("entities"));

        // Create method body
        InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder();
        bodyBuilder.appendFormalLine(entity.getSimpleTypeName().concat("List list = new ")
                .concat(entity.getSimpleTypeName()).concat("List();"));
        bodyBuilder.appendFormalLine("for ( Integer select : entities.getSelected() ) {");

        bodyBuilder.indent();
        bodyBuilder.appendFormalLine("if ( select != null ) {");
        bodyBuilder.indent();
        bodyBuilder.appendFormalLine("list.getList().add(entities.getList().get(select));");
        bodyBuilder.indentRemove();
        bodyBuilder.appendFormalLine("}");
        bodyBuilder.indentRemove();

        bodyBuilder.appendFormalLine("}");
        bodyBuilder.appendFormalLine("return list;");

        MethodMetadataBuilder methodBuilder = new MethodMetadataBuilder(getId(), Modifier.PUBLIC, methodName,
                getEntityList(), parameterTypes, parameterNames, bodyBuilder);

        method = methodBuilder.build();
        controllerMethods.add(method);
        return method;
    }

    /**
     * Generates the MethodMedata of
     * getRefererRedirectViewName(HttpServletRequest) method for ITD
     * <p>
     * The generated method redirects to the Referer URL
     * </p>
     * 
     * @return
     */
    protected MethodMetadata getRefererRedirectMethod() {
        // Specify the desired method name
        JavaSymbolName methodName = new JavaSymbolName("getRefererRedirectViewName");

        // Define method parameter types
        List<AnnotatedJavaType> parameterTypes = new ArrayList<AnnotatedJavaType>();
        Entry<JavaSymbolName, AnnotatedJavaType> httpServletRequest = HTTP_SERVLET_REQUEST_PARAM;
        parameterTypes.add(httpServletRequest.getValue());

        // Check if a method with the same signature already exists in the
        // target type
        MethodMetadata method = methodExists(methodName, parameterTypes);
        if (method != null) {
            // If it already exists, just return null and omit its
            // generation via the ITD
            return null;
        }

        // Define method parameter names
        List<JavaSymbolName> parameterNames = new ArrayList<JavaSymbolName>();
        parameterNames.add(httpServletRequest.getKey());

        // Create method body
        InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder();

        bodyBuilder
                .appendFormalLine("String referer = " + httpServletRequest.getKey() + ".getHeader(\"Referer\");");

        JavaType stringUtils = new JavaType("org.springframework.util.StringUtils");
        bodyBuilder.appendFormalLine("if (!"
                .concat(stringUtils.getNameIncludingTypeParameters(false, builder.getImportRegistrationResolver()))
                .concat(".hasText(referer)) {"));
        bodyBuilder.indent();
        bodyBuilder.appendFormalLine("return null;");
        bodyBuilder.indentRemove();
        bodyBuilder.appendFormalLine("}");

        bodyBuilder.appendFormalLine("return \"redirect:\".concat(referer);");

        MethodMetadataBuilder methodBuilder = new MethodMetadataBuilder(getId(), Modifier.PUBLIC, methodName,
                JavaType.STRING, parameterTypes, parameterNames, bodyBuilder);

        method = methodBuilder.build();
        controllerMethods.add(method);
        return method;
    }

    /**
     * Returns the Attributes of the {@link RequestMapping} annotation for the
     * methods
     * <p>
     * Returns:<br>
     * <code>value="/list", method = RequestMethod.DELETE</code> as attributes
     * of RequestMapping annotation
     * </p>
     * 
     * @param requestMethod
     * @return
     */
    private List<AnnotationAttributeValue<?>> getRequestMappingAttributes(RequestMethod requestMethod) {

        // Define method annotations
        List<AnnotationAttributeValue<?>> requestMappingAttributes = new ArrayList<AnnotationAttributeValue<?>>();
        requestMappingAttributes.add(new StringAttributeValue(VALUE_ATTRIBUTE_NAME, "/list"));
        requestMappingAttributes.add(getMethodRequestMapping(requestMethod));
        requestMappingAttributes.add(PRODUCES_PARAM_MAPPING);

        return requestMappingAttributes;
    }

    /**
     * Returns the list of Parameter Types for the methods
     * <p>
     * Returns a list containing:<br/>
     * <code>@Valid formBackingObjectList, BindingResult, HttpServletRequest</cod>
     * </p>
     * 
     * @return
     */
    private List<AnnotatedJavaType> getMethodParameterTypes() {
        /*
         * Define method parameter types. (@Valid formBackingObjectList,
         * BindingResult, HttpServletRequest)
         */
        List<AnnotatedJavaType> parameterTypes = new ArrayList<AnnotatedJavaType>();

        parameterTypes.add(getEntity(getEntityList()).getValue());
        parameterTypes.add(BINDING_RESULT.getValue());
        parameterTypes.add(HTTP_SERVLET_REQUEST_PARAM.getValue());

        return parameterTypes;
    }

    protected JavaType getEntityList() {
        return new JavaType(
                entity.getFullyQualifiedTypeName().concat(".").concat(entity.getSimpleTypeName()).concat("List"));
    }

    /**
     * Returns the list of parameter names for the methods
     * <p>
     * Returns:<br/>
     * <code>entities, bindingResult, httpServletRequest</code>
     * </p>
     * 
     * @return
     */
    private List<JavaSymbolName> getMethodParameterNames() {

        // Define method parameter names (entities, bindingResult,
        // httpServletRequest)
        List<JavaSymbolName> parameterNames = new ArrayList<JavaSymbolName>();
        parameterNames.add(new JavaSymbolName("entities"));
        parameterNames.add(BINDING_RESULT.getKey());
        parameterNames.add(HTTP_SERVLET_REQUEST_PARAM.getKey());

        return parameterNames;
    }

    /**
     * Get the RequestParam annotation for gvnixpattern.
     * <p>
     * Key has the param name and value has the annotation.
     * </p>
     * <code>@RequestParam(value = "gvnixpattern", required = true) String gvnixpattern</code>
     * 
     * @param required Is this request param required ?
     * @return Request param name and annotation
     */
    protected Entry<JavaSymbolName, AnnotatedJavaType> getPatternRequestParam(boolean required) {

        // TODO Required can be always same value ?

        AnnotationMetadataBuilder gvnixpatternParamBuilder = new AnnotationMetadataBuilder(
                new JavaType(RQST_PARAM_PATH));
        gvnixpatternParamBuilder.addStringAttribute(VALUE_TXT, GVNIXPATTERN);
        gvnixpatternParamBuilder.addBooleanAttribute(REQUIRED_TXT, required);
        List<AnnotationMetadata> gvnixpatternParam = new ArrayList<AnnotationMetadata>();
        gvnixpatternParam.add(gvnixpatternParamBuilder.build());

        return new SimpleEntry<JavaSymbolName, AnnotatedJavaType>(new JavaSymbolName(GVNIXPATTERN),
                new AnnotatedJavaType(new JavaType("java.lang.String"), gvnixpatternParam));
    }

    /**
     * Get the RequestParam annotation for page pagination.
     * <p>
     * Key has the param name and value has the annotation.
     * </p>
     * <code>
     * 
     * @RequestParam(value = "fu_org_gvnix_tiendavirtual_domain_ProductoPage",
     *                     required = false) Integer page, </code>
     * @return Request param name and annotation
     */
    protected Entry<JavaSymbolName, AnnotatedJavaType> getPageRequestParam() {

        AnnotationMetadataBuilder gvnixpatternParamBuilder = new AnnotationMetadataBuilder(
                new JavaType(RQST_PARAM_PATH));
        gvnixpatternParamBuilder.addStringAttribute(VALUE_TXT,
                XmlUtils.convertId("fu:" + entity.getFullyQualifiedTypeName() + "Page"));
        gvnixpatternParamBuilder.addBooleanAttribute(REQUIRED_TXT, false);
        List<AnnotationMetadata> gvnixpatternParam = new ArrayList<AnnotationMetadata>();
        gvnixpatternParam.add(gvnixpatternParamBuilder.build());

        return new SimpleEntry<JavaSymbolName, AnnotatedJavaType>(new JavaSymbolName("page"),
                new AnnotatedJavaType(new JavaType("java.lang.Integer"), gvnixpatternParam));
    }

    /**
     * Get the RequestParam annotation for page size pagination.
     * <p>
     * Key has the param name and value has the annotation.
     * </p>
     * <code>
     * 
     * @RequestParam(value =
     *                     "fu_org_gvnix_tiendavirtual_domain_ProductoPageSize",
     *                     required = false) Integer pageSize, </code>
     * @return Request param name and annotation
     */
    protected Entry<JavaSymbolName, AnnotatedJavaType> getPageSizeRequestParam() {

        AnnotationMetadataBuilder gvnixpatternParamBuilder = new AnnotationMetadataBuilder(
                new JavaType(RQST_PARAM_PATH));
        gvnixpatternParamBuilder.addStringAttribute(VALUE_TXT,
                XmlUtils.convertId("fu:" + entity.getFullyQualifiedTypeName() + "PageSize"));
        gvnixpatternParamBuilder.addBooleanAttribute(REQUIRED_TXT, false);
        List<AnnotationMetadata> gvnixpatternParam = new ArrayList<AnnotationMetadata>();
        gvnixpatternParam.add(gvnixpatternParamBuilder.build());

        return new SimpleEntry<JavaSymbolName, AnnotatedJavaType>(new JavaSymbolName("pageSize"),
                new AnnotatedJavaType(new JavaType("java.lang.Integer"), gvnixpatternParam));
    }

    /**
     * Get the RequestParam annotation for uiModel.
     * <p>
     * Key has the param name and value has the annotation.
     * </p>
     * <code>Model uiModel</code>
     * 
     * @return Request param name and annotation
     */
    protected Entry<JavaSymbolName, AnnotatedJavaType> getModelRequestParam() {
        return MODEL_REQUEST_PARAM;
    }

    /**
     * Get request mapping annotation for a method.
     * <code>@RequestMapping(params = { "form", "gvnixpattern=AplicacionListados2", "gvnixreference" }, method = RequestMethod.GET)</code>
     * 
     * @param patternName Pattern name to match on request mapping with
     *        "gvnixpattern" attribute name.
     * @return
     */
    protected AnnotationMetadataBuilder getRequestMapping(String patternName, RequestMethod method) {

        List<AnnotationAttributeValue<?>> requestMappingAnnotation = getParamsRequestMapping(patternName);
        requestMappingAnnotation.add(getMethodRequestMapping(method));
        requestMappingAnnotation.add(PRODUCES_PARAM_MAPPING);
        return new AnnotationMetadataBuilder(RQST_MAP_ANN_TYPE, requestMappingAnnotation);
    }

    protected List<AnnotationAttributeValue<?>> getParamsRequestMapping(String patternName) {

        List<StringAttributeValue> requestMapingAnnotationParams = new ArrayList<StringAttributeValue>();
        requestMapingAnnotationParams.add(getFormParamRequestMapping(true));
        requestMapingAnnotationParams.add(getPatternParamRequestMapping(patternName));
        requestMapingAnnotationParams.add(getReferenceParamRequestMapping());
        List<AnnotationAttributeValue<?>> requestMappingAnnotation = new ArrayList<AnnotationAttributeValue<?>>();
        requestMappingAnnotation.add(new ArrayAttributeValue<StringAttributeValue>(PARAMS_ATTRIBUTE_NAME,
                requestMapingAnnotationParams));

        return requestMappingAnnotation;
    }

    protected EnumAttributeValue getMethodRequestMapping(RequestMethod method) {

        return new EnumAttributeValue(METHOD_SYMBOL_NAME,
                new EnumDetails(new JavaType("org.springframework.web.bind.annotation.RequestMethod"),
                        new JavaSymbolName(method.name())));
    }

    protected StringAttributeValue getReferenceParamRequestMapping() {

        // TODO Refactor
        return new StringAttributeValue(VALUE_ATTRIBUTE_NAME, "gvnixreference");
    }

    protected StringAttributeValue getPatternParamRequestMapping(String patternName) {

        // TODO Refactor
        return new StringAttributeValue(VALUE_ATTRIBUTE_NAME, GVNIXPATTERN + "=" + patternName);
    }

    protected StringAttributeValue getFormParamRequestMapping(boolean exists) {

        // TODO Refactor
        if (exists) {
            return new StringAttributeValue(VALUE_ATTRIBUTE_NAME, "form");
        } else {
            return new StringAttributeValue(VALUE_ATTRIBUTE_NAME, "!form");
        }
    }

    // TODO Refactor method name
    protected Entry<JavaSymbolName, AnnotatedJavaType> getEntity(JavaType name) {

        List<AnnotationMetadata> methodAttrValidAnnotations = new ArrayList<AnnotationMetadata>();
        methodAttrValidAnnotations.add(VALID_ANNOTATION);

        return new SimpleEntry<JavaSymbolName, AnnotatedJavaType>(
                new JavaSymbolName(name.getSimpleTypeName().toLowerCase()),
                new AnnotatedJavaType(new JavaType(name.getFullyQualifiedTypeName()), methodAttrValidAnnotations));
    }

    protected void getRequestParam(List<JavaSymbolName> methodParamNames,
            List<AnnotatedJavaType> methodParamTypes) {

        Entry<JavaSymbolName, AnnotatedJavaType> patternRequestParam = getPatternRequestParam(false);
        methodParamTypes.add(patternRequestParam.getValue());
        methodParamNames.add(patternRequestParam.getKey());

        Entry<JavaSymbolName, AnnotatedJavaType> validEntity = getEntity(entity);
        methodParamTypes.add(validEntity.getValue());
        methodParamNames.add(validEntity.getKey());

        methodParamTypes.add(BINDING_RESULT.getValue());
        methodParamNames.add(BINDING_RESULT.getKey());

        Entry<JavaSymbolName, AnnotatedJavaType> modelRequestParam = MODEL_REQUEST_PARAM;
        methodParamTypes.add(modelRequestParam.getValue());
        methodParamNames.add(modelRequestParam.getKey());

        Entry<JavaSymbolName, AnnotatedJavaType> httpServletRequest = HTTP_SERVLET_REQUEST_PARAM;
        methodParamTypes.add(httpServletRequest.getValue());
        methodParamNames.add(httpServletRequest.getKey());
    }

    /**
     * Returns the method body of the methods given a {@link PersistenceMethod}
     * <p>
     * Example:<br/>
     * <code>
     * if ( !bindingResult.hasErrors() ) {<br/>
     * &nbsp;&nbsp;Car.persist(filterList(entities));<br/>
     * }<br/>
     * return getRefererRedirectViewName(httpServletRequest);
     * </code>
     * 
     * @param persistenceMethod
     * @return
     */
    private InvocableMemberBodyBuilder getMethodBodyBuilder(PersistenceMethod persistenceMethod) {

        // TODO Cdigo duplicado con mtodo contiguo

        // Create the method body
        InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder();
        String bindingResutlParam = BINDING_RESULT.getKey().getSymbolName();

        bodyBuilder.appendFormalLine("// Filter binding errors to get only the related to selected rows");
        bodyBuilder.appendFormalLine(String.format("%s bindingErrors = %s(entities,%s);",
                OBJECT_ERROR_LIST_TYPE.getNameIncludingTypeParameters(false,
                        builder.getImportRegistrationResolver()),
                FILTER_BINDING_ERRORS_NAME.getSymbolName(), bindingResutlParam));

        // test if form has errors
        bodyBuilder.appendFormalLine("if (bindingErrors.isEmpty()) {");
        bodyBuilder.indent();
        bodyBuilder.appendFormalLine(
                entity.getNameIncludingTypeParameters(false, builder.getImportRegistrationResolver()).concat(".")
                        .concat(persistenceMethod.getName()).concat("(filterList(entities));"));
        bodyBuilder.indentRemove();
        bodyBuilder.appendFormalLine("} else {");
        bodyBuilder.indent();
        addBodyLinesForDialogMessage(bodyBuilder, DialogType.Error, "message_errorbinding_problemdescription");
        bodyBuilder.indentRemove();
        bodyBuilder.appendFormalLine("}");
        bodyBuilder.appendFormalLine(
                "return getRefererRedirectViewName(" + HTTP_SERVLET_REQUEST_PARAM.getKey() + ");");

        return bodyBuilder;
    }

    /**
     * Returns the method body of the methods given a {@link PersistenceMethod}
     * <p>
     * Example:<br/>
     * <code>
     * if ( !bindingResult.hasErrors() ) {<br/>
     * &nbsp;&nbsp;Car.persist(filterList(entities));<br/>
     * }<br/>
     * return getRefererRedirectViewName(httpServletRequest);
     * </code>
     * 
     * @param persistenceMethod
     * @return
     */
    private InvocableMemberBodyBuilder getMethodBodyBuilderBinding(PersistenceMethod persistenceMethod) {

        // TODO Cdigo duplicado con mtodo contiguo

        // Create the method body
        InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder();
        String bindingResutlParam = BINDING_RESULT.getKey().getSymbolName();

        bodyBuilder.appendFormalLine("// Filter binding errors to get only the related to selected rows");
        bodyBuilder.appendFormalLine(String.format("%s bindingErrors = %s(entities,%s);",
                OBJECT_ERROR_LIST_TYPE.getNameIncludingTypeParameters(false,
                        builder.getImportRegistrationResolver()),
                FILTER_BINDING_ERRORS_NAME.getSymbolName(), bindingResutlParam));

        // test if form has errors
        bodyBuilder.appendFormalLine("if ( !" + BINDING_RESULT.getKey() + ".hasErrors() ) {");
        bodyBuilder.indent();
        bodyBuilder.appendFormalLine(
                entity.getNameIncludingTypeParameters(false, builder.getImportRegistrationResolver()).concat(".")
                        .concat(persistenceMethod.getName()).concat("(filterList(entities));"));
        bodyBuilder.indentRemove();
        bodyBuilder.appendFormalLine("} else {");
        bodyBuilder.indent();
        addBodyLinesForDialogBinding(bodyBuilder, DialogType.Error, "message_errorbinding_problemdescription");
        bodyBuilder.indentRemove();
        bodyBuilder.appendFormalLine("}");
        bodyBuilder.appendFormalLine(
                "return getRefererRedirectViewName(" + HTTP_SERVLET_REQUEST_PARAM.getKey() + ");");

        return bodyBuilder;
    }

    /**
     * Remove from <code>relatedFields</code> those types that are being
     * returned in any other populate method (these methods have ModelAttribute
     * method annotation)
     * 
     * @param relatedFields
     */
    private void filterAlreadyPopulatedTypes(SortedMap<JavaType, JavaTypeMetadataDetails> typesForPopulate) {

        Set<JavaType> keyTypesForPopulate = typesForPopulate.keySet();
        if (keyTypesForPopulate.isEmpty()) {
            return;
        }

        for (MethodMetadata method : controllerMethods) {
            JavaType returnType = method.getReturnType();
            if (returnType.isCommonCollectionType()) {
                for (JavaType genericType : returnType.getParameters()) {
                    if (typesForPopulate.keySet().contains(genericType)) {
                        typesForPopulate.remove(genericType);
                    }
                }
            } else if (typesForPopulate.keySet().contains(returnType)) {
                typesForPopulate.remove(returnType);
            }
        }
    }

    /**
     * Returns the method if exists or null otherwise. With this we assure that
     * a method is defined once in the Class
     * 
     * @param methodName
     * @param paramTypes
     * @return
     */
    protected MethodMetadata methodExists(JavaSymbolName methodName, List<AnnotatedJavaType> paramTypes) {
        List<JavaType> nonAnnotatedJavaTypes = AnnotatedJavaType.convertFromAnnotatedJavaTypes(paramTypes);
        for (MethodMetadata methodMetadata : controllerMethods) {
            if (methodMetadata.getMethodName().equals(methodName) && nonAnnotatedJavaTypes
                    .equals(AnnotatedJavaType.convertFromAnnotatedJavaTypes(methodMetadata.getParameterTypes()))) {
                return methodMetadata;
            }
        }
        return null;
    }

    protected String uncapitalize(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));
    }

    public WebScaffoldMetadata getWebScaffoldMetadata() {
        return this.webScaffoldMetadata;
    }

    public SortedMap<JavaType, JavaTypeMetadataDetails> getRelatedApplicationTypeMetadata() {
        return this.relatedEntities;
    }

    public List<String> getDefinedPatterns() {
        return patterns;
    }

    // Typically, no changes are required beyond this point

    @Override
    public String toString() {
        ToStringBuilder tsc = new ToStringBuilder(this);
        tsc.append("identifier", getId());
        tsc.append("valid", valid);
        tsc.append("aspectName", aspectName);
        tsc.append("destinationType", destination);
        tsc.append("governor", governorPhysicalTypeMetadata.getId());
        tsc.append("itdTypeDetails", itdTypeDetails);
        return tsc.toString();
    }

    /**
     * Generates the MethodMedata of
     * filterBindingErrors(entityList,BindingResult) method for ITD
     * <p>
     * The generated method returns a list of ObjectErrors related to selected
     * rows.
     * </p>
     * 
     * @return
     */
    protected MethodMetadata getFilterBindingErrorsMethod() {
        // Specify the desired method name
        JavaSymbolName methodName = FILTER_BINDING_ERRORS_NAME;
        ImportRegistrationResolver importResolver = builder.getImportRegistrationResolver();

        // Define method parameter types
        List<AnnotatedJavaType> parameterTypes = new ArrayList<AnnotatedJavaType>();
        List<AnnotationMetadata> annotations = new ArrayList<AnnotationMetadata>();
        parameterTypes.add(new AnnotatedJavaType(getEntityList(), annotations));
        parameterTypes.add(BINDING_RESULT.getValue());

        // Check if a method with the same signature already exists in the
        // target type
        MethodMetadata method = methodExists(methodName, parameterTypes);
        if (method != null) {
            // If it already exists, just return null and omit its
            // generation via the ITD
            return null;
        }

        // Define method parameter names
        List<JavaSymbolName> parameterNames = new ArrayList<JavaSymbolName>();
        String entitiesParamName = "entities";
        parameterNames.add(new JavaSymbolName(entitiesParamName));
        parameterNames.add(BINDING_RESULT.getKey());
        String bindingParamName = BINDING_RESULT.getKey().getSymbolName();

        JavaType returnType = OBJECT_ERROR_LIST_TYPE;

        // Create method body
        InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder();

        JavaType returnImplementedType = new JavaType("java.util.ArrayList", 0, DataType.TYPE, null,
                Arrays.asList(OBJECT_ERROR_TYPE));

        JavaType selecteItemsType = new JavaType(LIST.getFullyQualifiedTypeName(), 0, DataType.TYPE, null,
                Arrays.asList(JavaType.INT_OBJECT));

        bodyBuilder.appendFormalLine(String.format("%s result = new %s();",
                returnType.getNameIncludingTypeParameters(false, importResolver),
                returnImplementedType.getNameIncludingTypeParameters(false, importResolver)));

        bodyBuilder.appendFormalLine(String.format("%s selecteds = entities.getSelected();",
                selecteItemsType.getNameIncludingTypeParameters(false, importResolver)));

        bodyBuilder.appendFormalLine(String.format("for (%s objError : %s.getAllErrors()) {",
                OBJECT_ERROR_TYPE.getNameIncludingTypeParameters(false, importResolver), bindingParamName));

        bodyBuilder.indent();

        JavaType fieldValidationErrorType = new JavaType("org.springframework.validation.FieldError");

        String fieldValidationError = fieldValidationErrorType.getNameIncludingTypeParameters(false,
                importResolver);

        bodyBuilder.appendFormalLine(String.format("if (objError instanceof %s) {", fieldValidationError));
        bodyBuilder.indent();
        bodyBuilder.appendFormalLine(String
                .format("String[] str = ((%s)objError).getField().split(\"[\\\\[\\\\]]\");", fieldValidationError));

        bodyBuilder.appendFormalLine("if (str.length == 3) {");
        bodyBuilder.indent();
        bodyBuilder.appendFormalLine("Integer row = null;");
        bodyBuilder.appendFormalLine("try {");
        bodyBuilder.indent();
        bodyBuilder.appendFormalLine("// name == \"list[0]fieldName\"");
        bodyBuilder.appendFormalLine("row = Integer.parseInt(str[1]);");
        bodyBuilder.indentRemove();
        bodyBuilder.appendFormalLine("} catch (NumberFormatException e) {");
        bodyBuilder.indent();
        bodyBuilder.appendFormalLine("// unexpected name format: add error");
        bodyBuilder.appendFormalLine("result.add(objError);");
        bodyBuilder.appendFormalLine("continue;");
        bodyBuilder.indentRemove();
        bodyBuilder.appendFormalLine("}");
        bodyBuilder.appendFormalLine("if (selecteds.contains(row)) {");
        bodyBuilder.indent();
        bodyBuilder.appendFormalLine("result.add(objError);");
        bodyBuilder.appendFormalLine("continue;");
        bodyBuilder.indentRemove();
        bodyBuilder.appendFormalLine("} else {");
        bodyBuilder.indent();
        bodyBuilder.appendFormalLine("continue;");
        bodyBuilder.indentRemove();
        bodyBuilder.appendFormalLine("}");
        bodyBuilder.indentRemove();
        bodyBuilder.appendFormalLine("}");
        bodyBuilder.indentRemove();
        bodyBuilder.appendFormalLine("}");
        bodyBuilder.appendFormalLine("result.add(objError);");
        bodyBuilder.indentRemove();
        bodyBuilder.appendFormalLine("}");
        bodyBuilder.appendFormalLine("return result;");

        MethodMetadataBuilder methodBuilder = new MethodMetadataBuilder(getId(), Modifier.PUBLIC, methodName,
                returnType, parameterTypes, parameterNames, bodyBuilder);

        method = methodBuilder.build();
        controllerMethods.add(method);
        return method;
    }
}