private MethodMetadataBuilder getFinderMethod(final FinderMetadataDetails finderMetadataDetails) {
    Validate.notNull(finderMetadataDetails, "Method metadata required for finder");
    final JavaSymbolName finderMethodName =
        new JavaSymbolName(
            finderMetadataDetails.getFinderMethodMetadata().getMethodName().getSymbolName());

    final List<AnnotatedJavaType> parameterTypes = new ArrayList<AnnotatedJavaType>();
    final List<JavaSymbolName> parameterNames = new ArrayList<JavaSymbolName>();

    final InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder();
    final StringBuilder methodParams = new StringBuilder();

    boolean dateFieldPresent = !dateTypes.isEmpty();
    for (final FieldMetadata field : finderMetadataDetails.getFinderMethodParamFields()) {
      final JavaSymbolName fieldName = field.getFieldName();
      final List<AnnotationMetadata> annotations = new ArrayList<AnnotationMetadata>();
      final List<AnnotationAttributeValue<?>> attributes =
          new ArrayList<AnnotationAttributeValue<?>>();
      attributes.add(
          new StringAttributeValue(
              new JavaSymbolName("value"), uncapitalize(fieldName.getSymbolName())));
      if (field.getFieldType().equals(JavaType.BOOLEAN_PRIMITIVE)
          || field.getFieldType().equals(JavaType.BOOLEAN_OBJECT)) {
        attributes.add(new BooleanAttributeValue(new JavaSymbolName("required"), false));
      }
      final AnnotationMetadataBuilder requestParamAnnotation =
          new AnnotationMetadataBuilder(REQUEST_PARAM, attributes);
      annotations.add(requestParamAnnotation.build());
      if (field.getFieldType().equals(DATE) || field.getFieldType().equals(CALENDAR)) {
        dateFieldPresent = true;
        final AnnotationMetadata annotation =
            MemberFindingUtils.getAnnotationOfType(field.getAnnotations(), DATE_TIME_FORMAT);
        if (annotation != null) {
          getShortName(DATE_TIME_FORMAT);
          annotations.add(annotation);
        }
      }
      parameterNames.add(fieldName);
      parameterTypes.add(new AnnotatedJavaType(field.getFieldType(), annotations));

      if (field.getFieldType().equals(JavaType.BOOLEAN_OBJECT)) {
        methodParams.append(fieldName + " == null ? Boolean.FALSE : " + fieldName + ", ");
      } else {
        methodParams.append(fieldName + ", ");
      }
    }

    if (methodParams.length() > 0) {
      methodParams.delete(methodParams.length() - 2, methodParams.length());
    }

    final List<AnnotationAttributeValue<?>> firstResultAttributes =
        new ArrayList<AnnotationAttributeValue<?>>();
    firstResultAttributes.add(new StringAttributeValue(new JavaSymbolName("value"), "page"));
    firstResultAttributes.add(new BooleanAttributeValue(new JavaSymbolName("required"), false));
    final AnnotationMetadataBuilder firstResultAnnotation =
        new AnnotationMetadataBuilder(REQUEST_PARAM, firstResultAttributes);

    final List<AnnotationAttributeValue<?>> maxResultsAttributes =
        new ArrayList<AnnotationAttributeValue<?>>();
    maxResultsAttributes.add(new StringAttributeValue(new JavaSymbolName("value"), "size"));
    maxResultsAttributes.add(new BooleanAttributeValue(new JavaSymbolName("required"), false));
    final AnnotationMetadataBuilder maxResultAnnotation =
        new AnnotationMetadataBuilder(REQUEST_PARAM, maxResultsAttributes);

    final List<AnnotationAttributeValue<?>> sortFieldNameAttributes =
        new ArrayList<AnnotationAttributeValue<?>>();
    sortFieldNameAttributes.add(
        new StringAttributeValue(new JavaSymbolName("value"), "sortFieldName"));
    sortFieldNameAttributes.add(new BooleanAttributeValue(new JavaSymbolName("required"), false));
    final AnnotationMetadataBuilder sortFieldNameAnnotation =
        new AnnotationMetadataBuilder(REQUEST_PARAM, sortFieldNameAttributes);

    final List<AnnotationAttributeValue<?>> sortOrderAttributes =
        new ArrayList<AnnotationAttributeValue<?>>();
    sortOrderAttributes.add(new StringAttributeValue(new JavaSymbolName("value"), "sortOrder"));
    sortOrderAttributes.add(new BooleanAttributeValue(new JavaSymbolName("required"), false));
    final AnnotationMetadataBuilder sortOrderAnnotation =
        new AnnotationMetadataBuilder(REQUEST_PARAM, sortOrderAttributes);

    parameterTypes.add(
        new AnnotatedJavaType(
            new JavaType(Integer.class.getName()), firstResultAnnotation.build()));
    parameterTypes.add(
        new AnnotatedJavaType(new JavaType(Integer.class.getName()), maxResultAnnotation.build()));
    parameterTypes.add(
        new AnnotatedJavaType(
            new JavaType(String.class.getName()), sortFieldNameAnnotation.build()));
    parameterTypes.add(
        new AnnotatedJavaType(new JavaType(String.class.getName()), sortOrderAnnotation.build()));

    parameterTypes.add(new AnnotatedJavaType(MODEL));
    if (getGovernorMethod(
            finderMethodName, AnnotatedJavaType.convertFromAnnotatedJavaTypes(parameterTypes))
        != null) {
      return null;
    }

    final List<JavaSymbolName> newParamNames = new ArrayList<JavaSymbolName>();
    newParamNames.addAll(parameterNames);
    newParamNames.add(new JavaSymbolName("page"));
    newParamNames.add(new JavaSymbolName("size"));
    newParamNames.add(new JavaSymbolName("sortFieldName"));
    newParamNames.add(new JavaSymbolName("sortOrder"));
    newParamNames.add(new JavaSymbolName("uiModel"));

    final List<AnnotationAttributeValue<?>> requestMappingAttributes =
        new ArrayList<AnnotationAttributeValue<?>>();
    requestMappingAttributes.add(
        new StringAttributeValue(
            new JavaSymbolName("params"),
            "find="
                + finderMetadataDetails
                    .getFinderMethodMetadata()
                    .getMethodName()
                    .getSymbolName()
                    .replaceFirst("find" + javaTypeMetadataHolder.getPlural(), "")));
    requestMappingAttributes.add(
        new EnumAttributeValue(
            new JavaSymbolName("method"),
            new EnumDetails(REQUEST_METHOD, new JavaSymbolName("GET"))));
    final AnnotationMetadataBuilder requestMapping =
        new AnnotationMetadataBuilder(REQUEST_MAPPING, requestMappingAttributes);
    final List<AnnotationMetadataBuilder> annotations = new ArrayList<AnnotationMetadataBuilder>();
    annotations.add(requestMapping);

    bodyBuilder.appendFormalLine("if (page != null || size != null) {");
    bodyBuilder.indent();
    bodyBuilder.appendFormalLine("int sizeNo = size == null ? 10 : size.intValue();");
    bodyBuilder.appendFormalLine(
        "final int firstResult = page == null ? 0 : (page.intValue() - 1) * sizeNo;");
    String methodParamsString = methodParams.toString();
    if (StringUtils.isNotBlank(methodParamsString)) {
      methodParamsString.concat(", ");
    }
    bodyBuilder.appendFormalLine(
        "uiModel.addAttribute(\""
            + javaTypeMetadataHolder.getPlural().toLowerCase()
            + "\", "
            + getShortName(formBackingType)
            + "."
            + finderMetadataDetails.getFinderMethodMetadata().getMethodName().getSymbolName()
            + "("
            + methodParamsString
            + "sortFieldName, sortOrder).setFirstResult(firstResult).setMaxResults(sizeNo).getResultList());");

    char[] methodNameArray =
        finderMetadataDetails
            .getFinderMethodMetadata()
            .getMethodName()
            .getSymbolName()
            .toCharArray();
    methodNameArray[0] = Character.toUpperCase(methodNameArray[0]);
    String countMethodName = "count" + new String(methodNameArray);

    bodyBuilder.appendFormalLine(
        "float nrOfPages = (float) "
            + getShortName(formBackingType)
            + "."
            + countMethodName
            + "("
            + methodParamsString
            + ") / sizeNo;");
    bodyBuilder.appendFormalLine(
        "uiModel.addAttribute(\"maxPages\", (int) ((nrOfPages > (int) nrOfPages || nrOfPages == 0.0) ? nrOfPages + 1 : nrOfPages));");
    bodyBuilder.indentRemove();
    bodyBuilder.appendFormalLine("} else {");
    bodyBuilder.indent();
    bodyBuilder.appendFormalLine(
        "uiModel.addAttribute(\""
            + javaTypeMetadataHolder.getPlural().toLowerCase()
            + "\", "
            + getShortName(formBackingType)
            + "."
            + finderMetadataDetails.getFinderMethodMetadata().getMethodName().getSymbolName()
            + "("
            + methodParamsString
            + "sortFieldName, sortOrder).getResultList());");
    bodyBuilder.indentRemove();
    bodyBuilder.appendFormalLine("}");

    if (dateFieldPresent) {
      bodyBuilder.appendFormalLine("addDateTimeFormatPatterns(uiModel);");
    }
    bodyBuilder.appendFormalLine("return \"" + controllerPath + "/list\";");

    final MethodMetadataBuilder methodBuilder =
        new MethodMetadataBuilder(
            getId(),
            Modifier.PUBLIC,
            finderMethodName,
            JavaType.STRING,
            parameterTypes,
            newParamNames,
            bodyBuilder);
    methodBuilder.setAnnotations(annotations);
    return methodBuilder;
  }
  private MethodMetadataBuilder getFinderFormMethod(final FinderMetadataDetails finder) {
    Validate.notNull(finder, "Method metadata required for finder");
    final JavaSymbolName finderFormMethodName =
        new JavaSymbolName(
            finder.getFinderMethodMetadata().getMethodName().getSymbolName() + "Form");

    final List<JavaType> methodParameterTypes = new ArrayList<JavaType>();
    final List<JavaSymbolName> methodParameterNames = new ArrayList<JavaSymbolName>();
    final List<JavaType> finderParameterTypes =
        AnnotatedJavaType.convertFromAnnotatedJavaTypes(
            finder.getFinderMethodMetadata().getParameterTypes());

    final InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder();

    boolean needModel = false;
    for (final JavaType finderParameterType : finderParameterTypes) {
      JavaTypeMetadataDetails typeMd = specialDomainTypes.get(finderParameterType);
      JavaTypePersistenceMetadataDetails javaTypePersistenceMetadataHolder = null;
      if (finderParameterType.isCommonCollectionType()) {
        typeMd = specialDomainTypes.get(finderParameterType.getParameters().get(0));
        if (typeMd != null && typeMd.isApplicationType()) {
          javaTypePersistenceMetadataHolder = typeMd.getPersistenceDetails();
        }
      } else if (typeMd != null && typeMd.isEnumType()) {
        bodyBuilder.appendFormalLine(
            "uiModel.addAttribute(\""
                + typeMd.getPlural().toLowerCase()
                + "\", java.util.Arrays.asList("
                + getShortName(finderParameterType)
                + ".class.getEnumConstants()));");
      } else if (typeMd != null && typeMd.isApplicationType()) {
        javaTypePersistenceMetadataHolder = typeMd.getPersistenceDetails();
      }
      if (typeMd != null
          && javaTypePersistenceMetadataHolder != null
          && javaTypePersistenceMetadataHolder.getFindAllMethod() != null) {
        bodyBuilder.appendFormalLine(
            "uiModel.addAttribute(\""
                + typeMd.getPlural().toLowerCase()
                + "\", "
                + javaTypePersistenceMetadataHolder.getFindAllMethod().getMethodCall()
                + ");");
      }
      needModel = true;
    }
    if (finderParameterTypes.contains(DATE) || finderParameterTypes.contains(CALENDAR)) {
      bodyBuilder.appendFormalLine("addDateTimeFormatPatterns(uiModel);");
    }
    bodyBuilder.appendFormalLine(
        "return \""
            + controllerPath
            + "/"
            + finder.getFinderMethodMetadata().getMethodName().getSymbolName()
            + "\";");

    if (needModel) {
      methodParameterTypes.add(MODEL);
      methodParameterNames.add(new JavaSymbolName("uiModel"));
    }

    if (governorHasMethod(finderFormMethodName, methodParameterTypes)) {
      return null;
    }

    final List<AnnotationAttributeValue<?>> requestMappingAttributes =
        new ArrayList<AnnotationAttributeValue<?>>();
    final List<StringAttributeValue> arrayValues = new ArrayList<StringAttributeValue>();
    arrayValues.add(
        new StringAttributeValue(
            new JavaSymbolName("value"),
            "find="
                + finder
                    .getFinderMethodMetadata()
                    .getMethodName()
                    .getSymbolName()
                    .replaceFirst("find" + javaTypeMetadataHolder.getPlural(), "")));
    arrayValues.add(new StringAttributeValue(new JavaSymbolName("value"), "form"));
    requestMappingAttributes.add(
        new ArrayAttributeValue<StringAttributeValue>(new JavaSymbolName("params"), arrayValues));
    requestMappingAttributes.add(
        new EnumAttributeValue(
            new JavaSymbolName("method"),
            new EnumDetails(REQUEST_METHOD, new JavaSymbolName("GET"))));
    final AnnotationMetadataBuilder requestMapping =
        new AnnotationMetadataBuilder(REQUEST_MAPPING, requestMappingAttributes);
    final List<AnnotationMetadataBuilder> annotations = new ArrayList<AnnotationMetadataBuilder>();
    annotations.add(requestMapping);

    final MethodMetadataBuilder methodBuilder =
        new MethodMetadataBuilder(
            getId(),
            Modifier.PUBLIC,
            finderFormMethodName,
            JavaType.STRING,
            AnnotatedJavaType.convertFromJavaTypes(methodParameterTypes),
            methodParameterNames,
            bodyBuilder);
    methodBuilder.setAnnotations(annotations);
    return methodBuilder;
  }