/** @return a test for the find all method, if available and requested (may return null) */
  private MethodMetadataBuilder getFindAllMethodTest(
      final MemberTypeAdditions findAllMethod, final MemberTypeAdditions countMethod) {
    if (!annotationValues.isFindAll() || findAllMethod == null || countMethod == null) {
      // User does not want this method, or core dependencies are missing
      return null;
    }

    // Prepare method signature
    JavaSymbolName methodName =
        new JavaSymbolName("test" + StringUtils.capitalize(findAllMethod.getMethodName()));
    if (governorHasMethod(methodName)) {
      return null;
    }

    builder.getImportRegistrationResolver().addImport(LIST);

    List<AnnotationMetadataBuilder> annotations = new ArrayList<AnnotationMetadataBuilder>();
    annotations.add(new AnnotationMetadataBuilder(TEST));

    final String entityName = annotationValues.getEntity().getSimpleTypeName();

    InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder();
    bodyBuilder.appendFormalLine(
        "Assert.assertNotNull(\"Data on demand for '"
            + entityName
            + "' failed to initialize correctly\", dod."
            + dataOnDemandMetadata.getRandomPersistentEntityMethod().getMethodName().getSymbolName()
            + "());");
    bodyBuilder.appendFormalLine("long count = " + countMethod.getMethodCall() + ";");
    bodyBuilder.appendFormalLine(
        "Assert.assertTrue(\"Too expensive to perform a find all test for '"
            + entityName
            + "', as there are \" + count + \" entries; set the findAllMaximum to exceed this value or set findAll=false on the integration test annotation to disable the test\", count < "
            + annotationValues.getFindAllMaximum()
            + ");");
    bodyBuilder.appendFormalLine(
        "List<" + entityName + "> result = " + findAllMethod.getMethodCall() + ";");
    bodyBuilder.appendFormalLine(
        "Assert.assertNotNull(\"Find all method for '"
            + entityName
            + "' illegally returned null\", result);");
    bodyBuilder.appendFormalLine(
        "Assert.assertTrue(\"Find all method for '"
            + entityName
            + "' failed to return any data\", result.size() > 0);");

    findAllMethod.copyAdditionsTo(builder, governorTypeDetails);
    countMethod.copyAdditionsTo(builder, governorTypeDetails);

    MethodMetadataBuilder methodBuilder =
        new MethodMetadataBuilder(
            getId(), Modifier.PUBLIC, methodName, JavaType.VOID_PRIMITIVE, bodyBuilder);
    methodBuilder.setAnnotations(annotations);
    return methodBuilder;
  }
  /** @return a test for the find entries method, if available and requested (may return null) */
  private MethodMetadataBuilder getFindEntriesMethodTest(
      final MemberTypeAdditions countMethod, final MemberTypeAdditions findEntriesMethod) {
    if (!annotationValues.isFindEntries() || countMethod == null || findEntriesMethod == null) {
      // User does not want this method, or core dependencies are missing
      return null;
    }

    // Prepare method signature
    JavaSymbolName methodName =
        new JavaSymbolName("test" + StringUtils.capitalize(findEntriesMethod.getMethodName()));
    if (governorHasMethod(methodName)) {
      return null;
    }

    builder.getImportRegistrationResolver().addImport(LIST);

    List<AnnotationMetadataBuilder> annotations = new ArrayList<AnnotationMetadataBuilder>();
    annotations.add(new AnnotationMetadataBuilder(TEST));

    final String entityName = annotationValues.getEntity().getSimpleTypeName();

    InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder();
    bodyBuilder.appendFormalLine(
        "Assert.assertNotNull(\"Data on demand for '"
            + entityName
            + "' failed to initialize correctly\", dod."
            + dataOnDemandMetadata.getRandomPersistentEntityMethod().getMethodName().getSymbolName()
            + "());");
    bodyBuilder.appendFormalLine("long count = " + countMethod.getMethodCall() + ";");
    bodyBuilder.appendFormalLine("if (count > 20) count = 20;");
    bodyBuilder.appendFormalLine("int firstResult = 0;");
    bodyBuilder.appendFormalLine("int maxResults = (int) count;");
    bodyBuilder.appendFormalLine(
        "List<" + entityName + "> result = " + findEntriesMethod.getMethodCall() + ";");
    bodyBuilder.appendFormalLine(
        "Assert.assertNotNull(\"Find entries method for '"
            + entityName
            + "' illegally returned null\", result);");
    bodyBuilder.appendFormalLine(
        "Assert.assertEquals(\"Find entries method for '"
            + entityName
            + "' returned an incorrect number of entries\", count, result.size());");

    findEntriesMethod.copyAdditionsTo(builder, governorTypeDetails);
    countMethod.copyAdditionsTo(builder, governorTypeDetails);

    MethodMetadataBuilder methodBuilder =
        new MethodMetadataBuilder(
            getId(), Modifier.PUBLIC, methodName, JavaType.VOID_PRIMITIVE, bodyBuilder);
    methodBuilder.setAnnotations(annotations);
    return methodBuilder;
  }
  /** @return a test for the find (by ID) method, if available and requested (may return null) */
  private MethodMetadataBuilder getFindMethodTest(
      final MemberTypeAdditions findMethod, final MethodMetadata identifierAccessorMethod) {
    if (!annotationValues.isFind() || findMethod == null || identifierAccessorMethod == null) {
      // User does not want this method
      return null;
    }

    // Prepare method signature
    JavaSymbolName methodName =
        new JavaSymbolName("test" + StringUtils.capitalize(findMethod.getMethodName()));
    if (governorHasMethod(methodName)) {
      return null;
    }

    builder.getImportRegistrationResolver().addImport(identifierAccessorMethod.getReturnType());

    List<AnnotationMetadataBuilder> annotations = new ArrayList<AnnotationMetadataBuilder>();
    annotations.add(new AnnotationMetadataBuilder(TEST));

    final String entityName = annotationValues.getEntity().getSimpleTypeName();

    InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder();
    bodyBuilder.appendFormalLine(
        entityName
            + " obj = dod."
            + dataOnDemandMetadata.getRandomPersistentEntityMethod().getMethodName().getSymbolName()
            + "();");
    bodyBuilder.appendFormalLine(
        "Assert.assertNotNull(\"Data on demand for '"
            + entityName
            + "' failed to initialize correctly\", obj);");
    bodyBuilder.appendFormalLine(
        identifierAccessorMethod.getReturnType().getSimpleTypeName()
            + " id = obj."
            + identifierAccessorMethod.getMethodName().getSymbolName()
            + "();");
    bodyBuilder.appendFormalLine(
        "Assert.assertNotNull(\"Data on demand for '"
            + entityName
            + "' failed to provide an identifier\", id);");
    bodyBuilder.appendFormalLine("obj = " + findMethod.getMethodCall() + ";");
    bodyBuilder.appendFormalLine(
        "Assert.assertNotNull(\"Find method for '"
            + entityName
            + "' illegally returned null for id '\" + id + \"'\", obj);");
    bodyBuilder.appendFormalLine(
        "Assert.assertEquals(\"Find method for '"
            + entityName
            + "' returned the incorrect identifier\", id, obj."
            + identifierAccessorMethod.getMethodName().getSymbolName()
            + "());");

    findMethod.copyAdditionsTo(builder, governorTypeDetails);

    MethodMetadataBuilder methodBuilder =
        new MethodMetadataBuilder(
            getId(), Modifier.PUBLIC, methodName, JavaType.VOID_PRIMITIVE, bodyBuilder);
    methodBuilder.setAnnotations(annotations);
    return methodBuilder;
  }
  /**
   * Asserts that the {@link RepositoryJpaLayerProvider} generates the expected call for the given
   * method with the given parameters
   *
   * @param expectedMethodCall
   * @param methodKey
   * @param callerParameters
   */
  private void assertMethodCall(
      final String expectedMethodCall,
      final MethodMetadataCustomDataKey methodKey,
      final MethodParameter... callerParameters) {
    // Set up
    setUpMockRepository();

    // Invoke
    final MemberTypeAdditions additions =
        layerProvider.getMemberTypeAdditions(
            CALLER_MID, methodKey.name(), mockTargetEntity, mockIdType, callerParameters);

    // Check
    assertEquals(expectedMethodCall, additions.getMethodCall());
  }
 private MethodMetadataBuilder getFindMethod(
     ClassOrInterfaceTypeDetailsBuilder locatorBuilder,
     String declaredById,
     JavaType targetType,
     JavaType idType) {
   MemberTypeAdditions findMethodAdditions =
       layerService.getMemberTypeAdditions(
           declaredById,
           PersistenceCustomDataKeys.FIND_METHOD.name(),
           targetType,
           idType,
           LAYER_POSITION,
           new MethodParameter(idType, "id"));
   InvocableMemberBodyBuilder invocableMemberBodyBuilder =
       InvocableMemberBodyBuilder.getInstance();
   invocableMemberBodyBuilder
       .append("return ")
       .append(findMethodAdditions.getMethodCall())
       .append(";");
   findMethodAdditions.copyAdditionsTo(locatorBuilder, locatorBuilder.build());
   MethodMetadataBuilder findMethodBuilder =
       new MethodMetadataBuilder(
           declaredById,
           Modifier.PUBLIC,
           new JavaSymbolName("find"),
           targetType,
           invocableMemberBodyBuilder);
   JavaType wildEntityType =
       new JavaType(
           targetType.getFullyQualifiedTypeName(),
           0,
           DataType.VARIABLE,
           JavaType.WILDCARD_EXTENDS,
           null);
   JavaType classParameterType =
       new JavaType(
           JavaType.CLASS.getFullyQualifiedTypeName(),
           0,
           DataType.TYPE,
           null,
           Arrays.asList(wildEntityType));
   findMethodBuilder.addParameter("clazz", classParameterType);
   findMethodBuilder.addParameter("id", idType);
   return findMethodBuilder;
 }
  /** @return a test for the count method, if available and requested (may return null) */
  private MethodMetadataBuilder getCountMethodTest(final MemberTypeAdditions countMethod) {
    if (!annotationValues.isCount() || countMethod == null) {
      // User does not want this method
      return null;
    }

    // Prepare method signature
    JavaSymbolName methodName =
        new JavaSymbolName("test" + StringUtils.capitalize(countMethod.getMethodName()));
    if (governorHasMethod(methodName)) {
      return null;
    }

    List<AnnotationMetadataBuilder> annotations = new ArrayList<AnnotationMetadataBuilder>();
    annotations.add(new AnnotationMetadataBuilder(TEST));

    final String entityName = annotationValues.getEntity().getSimpleTypeName();

    InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder();
    bodyBuilder.appendFormalLine(
        "Assert.assertNotNull(\"Data on demand for '"
            + entityName
            + "' failed to initialize correctly\", dod."
            + dataOnDemandMetadata.getRandomPersistentEntityMethod().getMethodName().getSymbolName()
            + "());");
    bodyBuilder.appendFormalLine("long count = " + countMethod.getMethodCall() + ";");
    bodyBuilder.appendFormalLine(
        "Assert.assertTrue(\"Counter for '"
            + entityName
            + "' incorrectly reported there were no entries\", count > 0);");

    countMethod.copyAdditionsTo(builder, governorTypeDetails);

    MethodMetadataBuilder methodBuilder =
        new MethodMetadataBuilder(
            getId(), Modifier.PUBLIC, methodName, JavaType.VOID_PRIMITIVE, bodyBuilder);
    methodBuilder.setAnnotations(annotations);
    return methodBuilder;
  }
  /** @return a test for the persist method, if available and requested (may return null) */
  private MethodMetadataBuilder getRemoveMethodTest(
      final MemberTypeAdditions removeMethod,
      final MemberTypeAdditions findMethod,
      final MemberTypeAdditions flushMethod,
      final MethodMetadata identifierAccessorMethod) {
    if (!annotationValues.isRemove()
        || removeMethod == null
        || findMethod == null
        || identifierAccessorMethod == null) {
      // User does not want this method or one of its core dependencies
      return null;
    }

    // Prepare method signature
    JavaSymbolName methodName =
        new JavaSymbolName("test" + StringUtils.capitalize(removeMethod.getMethodName()));
    if (governorHasMethod(methodName)) {
      return null;
    }

    builder.getImportRegistrationResolver().addImport(identifierAccessorMethod.getReturnType());

    List<AnnotationMetadataBuilder> annotations = new ArrayList<AnnotationMetadataBuilder>();
    annotations.add(new AnnotationMetadataBuilder(TEST));
    if (isGaeSupported) {
      AnnotationMetadataBuilder transactionalBuilder = new AnnotationMetadataBuilder(TRANSACTIONAL);
      if (StringUtils.hasText(transactionManager)
          && !"transactionManager".equals(transactionManager)) {
        transactionalBuilder.addStringAttribute("value", transactionManager);
      }
      transactionalBuilder.addEnumAttribute(
          "propagation", new EnumDetails(PROPAGATION, new JavaSymbolName("SUPPORTS")));
      annotations.add(transactionalBuilder);
    }

    final String entityName = annotationValues.getEntity().getSimpleTypeName();

    InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder();
    bodyBuilder.appendFormalLine(
        entityName
            + " obj = dod."
            + dataOnDemandMetadata.getRandomPersistentEntityMethod().getMethodName().getSymbolName()
            + "();");
    bodyBuilder.appendFormalLine(
        "Assert.assertNotNull(\"Data on demand for '"
            + entityName
            + "' failed to initialize correctly\", obj);");
    bodyBuilder.appendFormalLine(
        identifierAccessorMethod.getReturnType().getSimpleTypeName()
            + " id = obj."
            + identifierAccessorMethod.getMethodName().getSymbolName()
            + "();");
    bodyBuilder.appendFormalLine(
        "Assert.assertNotNull(\"Data on demand for '"
            + entityName
            + "' failed to provide an identifier\", id);");
    bodyBuilder.appendFormalLine("obj = " + findMethod.getMethodCall() + ";");
    bodyBuilder.appendFormalLine(removeMethod.getMethodCall() + ";");

    if (flushMethod != null) {
      bodyBuilder.appendFormalLine(flushMethod.getMethodCall() + ";");
      flushMethod.copyAdditionsTo(builder, governorTypeDetails);
    }

    bodyBuilder.appendFormalLine(
        "Assert.assertNull(\"Failed to remove '"
            + entityName
            + "' with identifier '\" + id + \"'\", "
            + findMethod.getMethodCall()
            + ");");

    removeMethod.copyAdditionsTo(builder, governorTypeDetails);
    findMethod.copyAdditionsTo(builder, governorTypeDetails);

    MethodMetadataBuilder methodBuilder =
        new MethodMetadataBuilder(
            getId(), Modifier.PUBLIC, methodName, JavaType.VOID_PRIMITIVE, bodyBuilder);
    methodBuilder.setAnnotations(annotations);
    return methodBuilder;
  }
  /** @return a test for the persist method, if available and requested (may return null) */
  private MethodMetadataBuilder getPersistMethodTest(
      final MemberTypeAdditions persistMethod,
      final MemberTypeAdditions flushMethod,
      final MethodMetadata identifierAccessorMethod) {
    if (!annotationValues.isPersist()
        || persistMethod == null
        || identifierAccessorMethod == null) {
      // User does not want this method
      return null;
    }

    // Prepare method signature
    JavaSymbolName methodName =
        new JavaSymbolName("test" + StringUtils.capitalize(persistMethod.getMethodName()));
    if (governorHasMethod(methodName)) {
      return null;
    }

    List<AnnotationMetadataBuilder> annotations = new ArrayList<AnnotationMetadataBuilder>();
    annotations.add(new AnnotationMetadataBuilder(TEST));

    final String entityName = annotationValues.getEntity().getSimpleTypeName();

    InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder();
    bodyBuilder.appendFormalLine(
        "Assert.assertNotNull(\"Data on demand for '"
            + entityName
            + "' failed to initialize correctly\", dod."
            + dataOnDemandMetadata.getRandomPersistentEntityMethod().getMethodName().getSymbolName()
            + "());");
    bodyBuilder.appendFormalLine(
        entityName
            + " obj = dod."
            + dataOnDemandMetadata.getNewTransientEntityMethod().getMethodName().getSymbolName()
            + "(Integer.MAX_VALUE);");
    bodyBuilder.appendFormalLine(
        "Assert.assertNotNull(\"Data on demand for '"
            + entityName
            + "' failed to provide a new transient entity\", obj);");

    if (!hasEmbeddedIdentifier) {
      bodyBuilder.appendFormalLine(
          "Assert.assertNull(\"Expected '"
              + entityName
              + "' identifier to be null\", obj."
              + identifierAccessorMethod.getMethodName().getSymbolName()
              + "());");
    }

    bodyBuilder.appendFormalLine(persistMethod.getMethodCall() + ";");
    if (flushMethod != null) {
      bodyBuilder.appendFormalLine(flushMethod.getMethodCall() + ";");
      flushMethod.copyAdditionsTo(builder, governorTypeDetails);
    }

    bodyBuilder.appendFormalLine(
        "Assert.assertNotNull(\"Expected '"
            + entityName
            + "' identifier to no longer be null\", obj."
            + identifierAccessorMethod.getMethodName().getSymbolName()
            + "());");

    persistMethod.copyAdditionsTo(builder, governorTypeDetails);

    MethodMetadataBuilder methodBuilder =
        new MethodMetadataBuilder(
            getId(), Modifier.PUBLIC, methodName, JavaType.VOID_PRIMITIVE, bodyBuilder);
    methodBuilder.setAnnotations(annotations);
    return methodBuilder;
  }
  /** @return a test for the merge method, if available and requested (may return null) */
  private MethodMetadataBuilder getMergeMethodTest(
      final MemberTypeAdditions mergeMethod,
      final MemberTypeAdditions findMethod,
      final MemberTypeAdditions flushMethod,
      final MethodMetadata versionAccessorMethod,
      final MethodMetadata identifierAccessorMethod) {
    if (!annotationValues.isMerge()
        || mergeMethod == null
        || versionAccessorMethod == null
        || findMethod == null
        || identifierAccessorMethod == null) {
      // User does not want this method, or core dependencies are missing
      return null;
    }

    // Prepare method signature
    JavaSymbolName methodName =
        new JavaSymbolName("test" + StringUtils.capitalize(mergeMethod.getMethodName()) + "Update");
    if (governorHasMethod(methodName)) {
      return null;
    }

    JavaType versionType = versionAccessorMethod.getReturnType();
    builder
        .getImportRegistrationResolver()
        .addImports(identifierAccessorMethod.getReturnType(), versionType);

    List<AnnotationMetadataBuilder> annotations = new ArrayList<AnnotationMetadataBuilder>();
    annotations.add(new AnnotationMetadataBuilder(TEST));

    String entityName = annotationValues.getEntity().getSimpleTypeName();

    InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder();
    bodyBuilder.appendFormalLine(
        entityName
            + " obj = dod."
            + dataOnDemandMetadata.getRandomPersistentEntityMethod().getMethodName().getSymbolName()
            + "();");
    bodyBuilder.appendFormalLine(
        "Assert.assertNotNull(\"Data on demand for '"
            + entityName
            + "' failed to initialize correctly\", obj);");
    bodyBuilder.appendFormalLine(
        identifierAccessorMethod.getReturnType().getSimpleTypeName()
            + " id = obj."
            + identifierAccessorMethod.getMethodName().getSymbolName()
            + "();");
    bodyBuilder.appendFormalLine(
        "Assert.assertNotNull(\"Data on demand for '"
            + entityName
            + "' failed to provide an identifier\", id);");
    bodyBuilder.appendFormalLine("obj = " + findMethod.getMethodCall() + ";");
    bodyBuilder.appendFormalLine(
        "boolean modified =  dod."
            + dataOnDemandMetadata.getModifyMethod().getMethodName().getSymbolName()
            + "(obj);");

    bodyBuilder.appendFormalLine(
        versionAccessorMethod.getReturnType().getSimpleTypeName()
            + " currentVersion = obj."
            + versionAccessorMethod.getMethodName().getSymbolName()
            + "();");

    String castStr = entityHasSuperclass ? "(" + entityName + ")" : "";
    bodyBuilder.appendFormalLine(
        entityName + " merged = " + castStr + mergeMethod.getMethodCall() + ";");

    if (flushMethod != null) {
      bodyBuilder.appendFormalLine(flushMethod.getMethodCall() + ";");
      flushMethod.copyAdditionsTo(builder, governorTypeDetails);
    }

    bodyBuilder.appendFormalLine(
        "Assert.assertEquals(\"Identifier of merged object not the same as identifier of original object\", merged."
            + identifierAccessorMethod.getMethodName().getSymbolName()
            + "(), id);");
    if (JdkJavaType.isDateField(versionType)) {
      bodyBuilder.appendFormalLine(
          "Assert.assertTrue(\"Version for '"
              + entityName
              + "' failed to increment on merge and flush directive\", (currentVersion != null && obj."
              + versionAccessorMethod.getMethodName().getSymbolName()
              + "().after(currentVersion)) || !modified);");
    } else {
      bodyBuilder.appendFormalLine(
          "Assert.assertTrue(\"Version for '"
              + entityName
              + "' failed to increment on merge and flush directive\", (currentVersion != null && obj."
              + versionAccessorMethod.getMethodName().getSymbolName()
              + "() > currentVersion) || !modified);");
    }
    mergeMethod.copyAdditionsTo(builder, governorTypeDetails);
    findMethod.copyAdditionsTo(builder, governorTypeDetails);

    MethodMetadataBuilder methodBuilder =
        new MethodMetadataBuilder(
            getId(), Modifier.PUBLIC, methodName, JavaType.VOID_PRIMITIVE, bodyBuilder);
    methodBuilder.setAnnotations(annotations);
    return methodBuilder;
  }