/**
   * Locates the entity manager field that should be used.
   *
   * <p>If a parent is defined, it must provide the field.
   *
   * <p>We generally expect the field to be named "entityManager" and be of type
   * javax.persistence.EntityManager. We also require it to be public or protected, and annotated
   * with @PersistenceContext. If there is an existing field which doesn't meet these latter
   * requirements, we add an underscore prefix to the "entityManager" name and try again, until such
   * time as we come up with a unique name that either meets the requirements or the name is not
   * used and we will create it.
   *
   * @return the entity manager field (never returns null)
   */
  public FieldMetadata getEntityManagerField() {
    if (parent != null) {
      // The parent is required to guarantee this is available
      return parent.getEntityManagerField();
    }

    // Need to locate it ourself
    int index = -1;
    while (true) {
      // Compute the required field name
      index++;
      final JavaSymbolName fieldSymbolName =
          new JavaSymbolName(StringUtils.repeat("_", index) + "entityManager");
      final FieldMetadata candidate = governorTypeDetails.getField(fieldSymbolName);
      if (candidate != null) {
        // Verify if candidate is suitable

        if (!Modifier.isPublic(candidate.getModifier())
            && !Modifier.isProtected(candidate.getModifier())
            && (Modifier.TRANSIENT != candidate.getModifier())) {
          // Candidate is not public and not protected and not simply a transient field (in which
          // case subclasses
          // will see the inherited field), so any subsequent subclasses won't be able to see it.
          // Give up!
          continue;
        }

        if (!candidate.getFieldType().equals(ENTITY_MANAGER)) {
          // Candidate isn't an EntityManager, so give up
          continue;
        }

        if (MemberFindingUtils.getAnnotationOfType(candidate.getAnnotations(), PERSISTENCE_CONTEXT)
            == null) {
          // Candidate doesn't have a PersistenceContext annotation, so give up
          continue;
        }

        // If we got this far, we found a valid candidate
        return candidate;
      }

      // Candidate not found, so let's create one
      final List<AnnotationMetadataBuilder> annotations =
          new ArrayList<AnnotationMetadataBuilder>();
      final AnnotationMetadataBuilder annotationBuilder =
          new AnnotationMetadataBuilder(PERSISTENCE_CONTEXT);
      if (StringUtils.hasText(crudAnnotationValues.getPersistenceUnit())) {
        annotationBuilder.addStringAttribute("unitName", crudAnnotationValues.getPersistenceUnit());
      }
      annotations.add(annotationBuilder);

      final FieldMetadataBuilder fieldBuilder =
          new FieldMetadataBuilder(
              getId(), Modifier.TRANSIENT, annotations, fieldSymbolName, ENTITY_MANAGER);
      return fieldBuilder.build();
    }
  }
  /** @return the find all method (may return null) */
  private MethodMetadata getFindAllMethod() {
    if ("".equals(crudAnnotationValues.getFindAllMethod())) {
      return null;
    }

    // Method definition to find or build
    final JavaSymbolName methodName =
        new JavaSymbolName(crudAnnotationValues.getFindAllMethod() + plural);
    final JavaType[] parameterTypes = {};
    final List<JavaSymbolName> parameterNames = new ArrayList<JavaSymbolName>();
    final JavaType returnType =
        new JavaType(
            LIST.getFullyQualifiedTypeName(), 0, DataType.TYPE, null, Arrays.asList(destination));

    // Locate user-defined method
    final MethodMetadata userMethod = getGovernorMethod(methodName, parameterTypes);
    if (userMethod != null) {
      Assert.isTrue(
          userMethod.getReturnType().equals(returnType),
          "Method '"
              + methodName
              + "' on '"
              + destination
              + "' must return '"
              + returnType.getNameIncludingTypeParameters()
              + "'");
      return userMethod;
    }

    // Create method
    final List<AnnotationMetadataBuilder> annotations = new ArrayList<AnnotationMetadataBuilder>();
    if (isGaeEnabled) {
      addTransactionalAnnotation(annotations);
    }

    final InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder();
    bodyBuilder.appendFormalLine(
        "return "
            + ENTITY_MANAGER_METHOD_NAME
            + "().createQuery(\"SELECT o FROM "
            + entityName
            + " o\", "
            + destination.getSimpleTypeName()
            + ".class).getResultList();");

    final MethodMetadataBuilder methodBuilder =
        new MethodMetadataBuilder(
            getId(),
            Modifier.PUBLIC | Modifier.STATIC,
            methodName,
            returnType,
            AnnotatedJavaType.convertFromJavaTypes(parameterTypes),
            parameterNames,
            bodyBuilder);
    methodBuilder.setAnnotations(annotations);
    return methodBuilder.build();
  }
 /** @return the clear method (never returns null) */
 private MethodMetadata getClearMethod() {
   if (parent != null) {
     final MethodMetadata found = parent.getClearMethod();
     if (found != null) {
       return found;
     }
   }
   if ("".equals(crudAnnotationValues.getClearMethod())) {
     return null;
   }
   return getDelegateMethod(new JavaSymbolName(crudAnnotationValues.getClearMethod()), "clear");
 }
 private void addTransactionalAnnotation(
     final List<AnnotationMetadataBuilder> annotations, final boolean isPersistMethod) {
   final AnnotationMetadataBuilder transactionalBuilder =
       new AnnotationMetadataBuilder(TRANSACTIONAL);
   if (StringUtils.hasText(crudAnnotationValues.getTransactionManager())) {
     transactionalBuilder.addStringAttribute(
         "value", crudAnnotationValues.getTransactionManager());
   }
   if (isGaeEnabled && isPersistMethod) {
     transactionalBuilder.addEnumAttribute(
         "propagation", new EnumDetails(PROPAGATION, new JavaSymbolName("REQUIRES_NEW")));
   }
   annotations.add(transactionalBuilder);
 }
 @Override
 public String toString() {
   final ToStringCreator tsc = new ToStringCreator(this);
   tsc.append("identifier", getId());
   tsc.append("valid", valid);
   tsc.append("aspectName", aspectName);
   tsc.append("destinationType", destination);
   tsc.append("finders", crudAnnotationValues.getFinders());
   tsc.append("governor", governorPhysicalTypeMetadata.getId());
   tsc.append("itdTypeDetails", itdTypeDetails);
   return tsc.toString();
 }
  /**
   * Finds (creating if necessary) the method that counts entities of this type
   *
   * @return the count method (never null)
   */
  private MethodMetadata getCountMethod() {
    // Method definition to find or build
    final JavaSymbolName methodName =
        new JavaSymbolName(crudAnnotationValues.getCountMethod() + plural);
    final JavaType[] parameterTypes = {};
    final List<JavaSymbolName> parameterNames = Collections.<JavaSymbolName>emptyList();

    // Locate user-defined method
    final MethodMetadata userMethod = getGovernorMethod(methodName, parameterTypes);
    if (userMethod != null) {
      Assert.isTrue(
          userMethod.getReturnType().equals(COUNT_RETURN_TYPE),
          "Method '"
              + methodName
              + "' on '"
              + destination
              + "' must return '"
              + COUNT_RETURN_TYPE.getNameIncludingTypeParameters()
              + "'");
      return userMethod;
    }

    // Create method
    final List<AnnotationMetadataBuilder> annotations = new ArrayList<AnnotationMetadataBuilder>();
    if (isGaeEnabled) {
      addTransactionalAnnotation(annotations);
    }

    final InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder();
    bodyBuilder.appendFormalLine(
        "return "
            + ENTITY_MANAGER_METHOD_NAME
            + "().createQuery(\"SELECT COUNT(o) FROM "
            + entityName
            + " o\", Long.class).getSingleResult();");

    final MethodMetadataBuilder methodBuilder =
        new MethodMetadataBuilder(
            getId(),
            Modifier.PUBLIC | Modifier.STATIC,
            methodName,
            COUNT_RETURN_TYPE,
            AnnotatedJavaType.convertFromJavaTypes(parameterTypes),
            parameterNames,
            bodyBuilder);
    methodBuilder.setAnnotations(annotations);
    return methodBuilder.build();
  }
 /** @return the dynamic, custom finders (never returns null, but may return an empty list) */
 public List<String> getDynamicFinders() {
   if (crudAnnotationValues.getFinders() == null) {
     return Collections.emptyList();
   }
   return Arrays.asList(crudAnnotationValues.getFinders());
 }
  /** @return the find (by ID) method (may return null) */
  public MethodMetadata getFindMethod() {
    if ("".equals(crudAnnotationValues.getFindMethod())) {
      return null;
    }

    // Method definition to find or build
    final String idFieldName = identifierField.getFieldName().getSymbolName();
    final JavaSymbolName methodName =
        new JavaSymbolName(crudAnnotationValues.getFindMethod() + destination.getSimpleTypeName());
    final JavaType parameterType = identifierField.getFieldType();
    final List<JavaSymbolName> parameterNames = Arrays.asList(new JavaSymbolName(idFieldName));
    final JavaType returnType = destination;

    // Locate user-defined method
    final MethodMetadata userMethod = getGovernorMethod(methodName, parameterType);
    if (userMethod != null) {
      Assert.isTrue(
          userMethod.getReturnType().equals(returnType),
          "Method '"
              + methodName
              + "' on '"
              + returnType
              + "' must return '"
              + returnType.getNameIncludingTypeParameters()
              + "'");
      return userMethod;
    }

    // Create method
    final List<AnnotationMetadataBuilder> annotations = new ArrayList<AnnotationMetadataBuilder>();
    if (isGaeEnabled) {
      addTransactionalAnnotation(annotations);
    }

    final InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder();

    if (JavaType.STRING.equals(identifierField.getFieldType())) {
      bodyBuilder.appendFormalLine(
          "if (" + idFieldName + " == null || " + idFieldName + ".length() == 0) return null;");
    } else if (!identifierField.getFieldType().isPrimitive()) {
      bodyBuilder.appendFormalLine("if (" + idFieldName + " == null) return null;");
    }

    bodyBuilder.appendFormalLine(
        "return "
            + ENTITY_MANAGER_METHOD_NAME
            + "().find("
            + returnType.getSimpleTypeName()
            + ".class, "
            + idFieldName
            + ");");

    final MethodMetadataBuilder methodBuilder =
        new MethodMetadataBuilder(
            getId(),
            Modifier.PUBLIC | Modifier.STATIC,
            methodName,
            returnType,
            AnnotatedJavaType.convertFromJavaTypes(parameterType),
            parameterNames,
            bodyBuilder);
    methodBuilder.setAnnotations(annotations);
    return methodBuilder.build();
  }
 /** @return the merge method (never returns null) */
 private MethodMetadata getMergeMethod() {
   if ("".equals(crudAnnotationValues.getMergeMethod())) {
     return null;
   }
   return getDelegateMethod(new JavaSymbolName(crudAnnotationValues.getMergeMethod()), "merge");
 }