public boolean isMethodDeclaredByAnother(
     final JavaSymbolName methodName,
     final List<JavaType> parameterTypes,
     final String declaredByMetadataId) {
   final MethodMetadata method = getMethod(methodName, parameterTypes);
   return method != null && !method.getDeclaredByMetadataId().equals(declaredByMetadataId);
 }
  /** @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;
  }
  // @Trace
  public void compile(ClassOrInterfaceTypeDetails type) {
    List<? extends MethodMetadata> methods = type.getDeclaredMethods();
    if (methods == null || methods.size() == 0) return;

    Iterator<? extends MethodMetadata> it = methods.iterator();
    while (it.hasNext()) {
      MethodMetadata method = it.next();

      // See if check
      ObjexCheckAnnotation check = ObjexCheckAnnotation.get(method.getAnnotations());
      if (check != null) {
        if (!checkNoArguments(method)) continue;
        if (!checkBooleanReturn(method)) continue;

        if (check.isChildObjectCheck()) addChildValidateMethod(check.getMessage(), method);
        else if (check.isPostObjectCheck())
          addInterObjectValidateMethod(check.getMessage(), method);
        else addIntraObjectValidateMethod(check.getMessage(), method);

        continue;
      }

      // See if enrich
      ObjexEnrichAnnotation enrich = ObjexEnrichAnnotation.get(method.getAnnotations());
      if (enrich != null) {
        if (!checkNoArguments(method)) break;

        if (enrich.isPostObjectCheck()) addInterObjectEnrichMethod(method);
        else addIntraObjectEnrichMethod(method);

        continue;
      }
    }
  }
  private List<MethodMetadata> locateAccessors(
      final JavaType javaType,
      final MemberDetails memberDetails,
      final String metadataIdentificationString) {
    final SortedSet<MethodMetadata> locatedAccessors =
        new TreeSet<MethodMetadata>(
            new Comparator<MethodMetadata>() {
              public int compare(final MethodMetadata l, final MethodMetadata r) {
                return l.getMethodName().compareTo(r.getMethodName());
              }
            });

    for (MethodMetadata method : memberDetails.getMethods()) {
      // Exclude cyclic self-references (ROO-325)
      if (BeanInfoUtils.isAccessorMethod(method)
          && !method.getReturnType().equals(javaType)
          && !method.getMethodName().getSymbolName().equals("getDisplayString")) {
        locatedAccessors.add(method);
        // Track any changes to that method (eg it goes away)
        metadataDependencyRegistry.registerDependency(
            method.getDeclaredByMetadataId(), metadataIdentificationString);
      }
    }

    return new ArrayList<MethodMetadata>(locatedAccessors);
  }
  /** @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();
  }
 public List<MethodMetadata> getMethodsWithTag(final Object tagKey) {
   Validate.notNull(tagKey, "Custom data key required");
   final List<MethodMetadata> result = new ArrayList<MethodMetadata>();
   for (final MethodMetadata method : getMethods()) {
     if (method.getCustomData().keySet().contains(tagKey)) {
       result.add(method);
     }
   }
   return result;
 }
 public MethodMetadata getMethod(
     final JavaSymbolName methodName, final List<JavaType> parameters, final String excludingMid) {
   for (final MemberHoldingTypeDetails memberHoldingTypeDetails : details) {
     final MethodMetadata method =
         MemberFindingUtils.getDeclaredMethod(memberHoldingTypeDetails, methodName, parameters);
     if (method != null && !method.getDeclaredByMetadataId().equals(excludingMid)) {
       return method;
     }
   }
   return null;
 }
 private String getRequestMethodCall(
     final ClassOrInterfaceTypeDetails request, final MemberTypeAdditions memberTypeAdditions) {
   final String methodName = memberTypeAdditions.getMethodName();
   final MethodMetadata requestMethod = MemberFindingUtils.getMethod(request, methodName);
   String requestMethodCall = memberTypeAdditions.getMethodName();
   if (requestMethod != null) {
     if (INSTANCE_REQUEST
         .getFullyQualifiedTypeName()
         .equals(requestMethod.getReturnType().getFullyQualifiedTypeName())) {
       requestMethodCall = requestMethodCall + "().using";
     }
   }
   return requestMethodCall;
 }
  private List<Token> tokenize(
      MemberDetails memberDetails, JavaSymbolName finderName, String plural) {
    String simpleTypeName = getConcreteJavaType(memberDetails).getSimpleTypeName();
    String finder = finderName.getSymbolName();

    // Just in case it starts with findBy we can remove it here
    String findBy = "find" + plural + "By";
    if (finder.startsWith(findBy)) {
      finder = finder.substring(findBy.length());
    }

    // If finder still contains the findBy sequence it is most likely a wrong finder (ie someone
    // pasted the finder string accidentally twice
    if (finder.contains(findBy)) {
      throw new InvalidFinderException(
          "Dynamic finder definition for '"
              + finderName.getSymbolName()
              + "' in "
              + simpleTypeName
              + ".java is invalid");
    }

    SortedSet<FieldToken> fieldTokens = new TreeSet<FieldToken>();
    for (MethodMetadata methodMetadata : getLocatedMutators(memberDetails)) {
      FieldMetadata fieldMetadata =
          BeanInfoUtils.getFieldForPropertyName(
              memberDetails, methodMetadata.getParameterNames().get(0));

      // If we did find a field matching the first parameter name of the mutator method we can add
      // it to the finder ITD
      if (fieldMetadata != null) {
        fieldTokens.add(new FieldToken(fieldMetadata));
      }
    }

    List<Token> tokens = new ArrayList<Token>();

    while (finder.length() > 0) {
      Token token = getFirstToken(fieldTokens, finder, finderName.getSymbolName(), simpleTypeName);
      if (token != null) {
        if (token instanceof FieldToken || token instanceof ReservedToken) {
          tokens.add(token);
        }
        finder = finder.substring(token.getValue().length());
      }
    }

    return tokens;
  }
 private MethodMetadata methodExists(
     JavaSymbolName methodName, List<AnnotatedJavaType> paramTypes) {
   // We have no access to method parameter information, so we scan by name alone and treat any
   // match as authoritative
   // We do not scan the superclass, as the caller is expected to know we'll only scan the current
   // class
   for (MethodMetadata method : governorTypeDetails.getDeclaredMethods()) {
     if (method.getMethodName().equals(methodName)
         && method.getParameterTypes().equals(paramTypes)) {
       // Found a method of the expected name; we won't check method parameters though
       return method;
     }
   }
   return null;
 }
  /**
   * 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();
  }
 private void doModification(final MethodMetadata method, final CustomData customData) {
   MemberHoldingTypeDetails memberHoldingTypeDetails =
       memberHoldingTypeDetailsMap.get(method.getDeclaredByMetadataId());
   if (memberHoldingTypeDetails != null) {
     MethodMetadata matchedMethod =
         memberHoldingTypeDetails.getMethod(
             method.getMethodName(),
             AnnotatedJavaType.convertFromAnnotatedJavaTypes(method.getParameterTypes()));
     if (matchedMethod != null
         && !matchedMethod.getCustomData().keySet().containsAll(customData.keySet())) {
       TypeDetailsBuilder typeDetailsBuilder = getTypeDetailsBuilder(memberHoldingTypeDetails);
       typeDetailsBuilder.addDataToMethod(method, customData);
       changed = true;
     }
   }
 }
 public void addDataToMethod(final MethodMetadata replacement, final CustomData customData) {
   // If the MIDs don't match then the proposed can't be a replacement
   if (!replacement.getDeclaredByMetadataId().equals(getDeclaredByMetadataId())) {
     return;
   }
   for (MethodMetadataBuilder existingMethod : getDeclaredMethods()) {
     if (existingMethod.getMethodName().equals(replacement.getMethodName())) {
       if (AnnotatedJavaType.convertFromAnnotatedJavaTypes(existingMethod.getParameterTypes())
           .equals(
               AnnotatedJavaType.convertFromAnnotatedJavaTypes(
                   replacement.getParameterTypes()))) {
         for (Object key : customData.keySet()) {
           existingMethod.putCustomData(key, customData.get(key));
         }
         break;
       }
     }
   }
 }
 private MethodMetadataBuilder getIdMethod(
     String declaredById, JavaType targetType, MethodMetadata idAccessor) {
   InvocableMemberBodyBuilder invocableMemberBodyBuilder =
       InvocableMemberBodyBuilder.getInstance();
   invocableMemberBodyBuilder.append(
       "return "
           + StringUtils.uncapitalize(targetType.getSimpleTypeName())
           + "."
           + idAccessor.getMethodName()
           + "();");
   MethodMetadataBuilder getIdMethod =
       new MethodMetadataBuilder(
           declaredById,
           Modifier.PUBLIC,
           new JavaSymbolName("getId"),
           GwtUtils.convertPrimitiveType(idAccessor.getReturnType(), true),
           invocableMemberBodyBuilder);
   getIdMethod.addParameter(StringUtils.uncapitalize(targetType.getSimpleTypeName()), targetType);
   return getIdMethod;
 }
  public Set<JavaType> getPersistentFieldTypes(
      final JavaType thisType, final PersistenceMemberLocator persistenceMemberLocator) {
    final MethodMetadata identifierAccessor =
        persistenceMemberLocator.getIdentifierAccessor(thisType);
    final MethodMetadata versionAccessor = persistenceMemberLocator.getVersionAccessor(thisType);

    final Set<JavaType> fieldTypes = new LinkedHashSet<JavaType>();
    for (final MethodMetadata method : getMethods()) {
      // Not interested in non-accessor methods or persistence identifiers
      // and version fields
      if (!BeanInfoUtils.isAccessorMethod(method)
          || method.hasSameName(identifierAccessor, versionAccessor)) {
        continue;
      }

      // Not interested in fields that are JPA transient fields or
      // immutable fields
      final FieldMetadata field = BeanInfoUtils.getFieldForJavaBeanMethod(this, method);
      if (field == null
          || field.getCustomData().keySet().contains(CustomDataKeys.TRANSIENT_FIELD)
          || !BeanInfoUtils.hasAccessorAndMutator(field, this)) {
        continue;
      }
      final JavaType returnType = method.getReturnType();
      if (returnType.isCommonCollectionType()) {
        for (final JavaType genericType : returnType.getParameters()) {
          fieldTypes.add(genericType);
        }
      } else {
        if (!field.getCustomData().keySet().contains(EMBEDDED_FIELD)) {
          fieldTypes.add(returnType);
        }
      }
    }
    return fieldTypes;
  }
 private MethodMetadataBuilder getVersionMethod(
     String declaredById, JavaType targetType, MethodMetadata versionAccessor) {
   InvocableMemberBodyBuilder invocableMemberBodyBuilder =
       InvocableMemberBodyBuilder.getInstance();
   invocableMemberBodyBuilder.append(
       "return "
           + StringUtils.uncapitalize(targetType.getSimpleTypeName())
           + "."
           + versionAccessor.getMethodName()
           + "();");
   MethodMetadataBuilder getIdMethodBuilder =
       new MethodMetadataBuilder(
           declaredById,
           Modifier.PUBLIC,
           new JavaSymbolName("getVersion"),
           JavaType.OBJECT,
           invocableMemberBodyBuilder);
   getIdMethodBuilder.addParameter(
       StringUtils.uncapitalize(targetType.getSimpleTypeName()), targetType);
   return getIdMethodBuilder;
 }
  /** @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;
  }
  private void addOptionalIntegrationTestClassIntroductions() {
    // Add the GAE test helper field if the user did not define it on the
    // governor directly
    final JavaType helperType = GAE_LOCAL_SERVICE_TEST_HELPER;
    FieldMetadata helperField = governorTypeDetails.getField(new JavaSymbolName("helper"));
    if (helperField != null) {
      Assert.isTrue(
          helperField
              .getFieldType()
              .getFullyQualifiedTypeName()
              .equals(helperType.getFullyQualifiedTypeName()),
          "Field 'helper' on '"
              + destination.getFullyQualifiedTypeName()
              + "' must be of type '"
              + helperType.getFullyQualifiedTypeName()
              + "'");
    } else {
      // Add the field via the ITD
      String initializer =
          "new LocalServiceTestHelper(new com.google.appengine.tools.development.testing.LocalDatastoreServiceTestConfig())";
      FieldMetadataBuilder fieldBuilder =
          new FieldMetadataBuilder(
              getId(),
              Modifier.PRIVATE | Modifier.STATIC | Modifier.FINAL,
              new JavaSymbolName("helper"),
              helperType,
              initializer);
      builder.addField(fieldBuilder);
    }

    // Prepare setUp method signature
    JavaSymbolName setUpMethodName = new JavaSymbolName("setUp");
    MethodMetadata setUpMethod = getGovernorMethod(setUpMethodName, SETUP_PARAMETERS);
    if (setUpMethod != null) {
      Assert.notNull(
          MemberFindingUtils.getAnnotationOfType(setUpMethod.getAnnotations(), BEFORE_CLASS),
          "Method 'setUp' on '"
              + destination.getFullyQualifiedTypeName()
              + "' must be annotated with @BeforeClass");
    } else {
      // Add the method via the ITD
      List<AnnotationMetadataBuilder> annotations = new ArrayList<AnnotationMetadataBuilder>();
      annotations.add(new AnnotationMetadataBuilder(BEFORE_CLASS));

      InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder();
      bodyBuilder.appendFormalLine("helper.setUp();");

      MethodMetadataBuilder methodBuilder =
          new MethodMetadataBuilder(
              getId(),
              Modifier.PUBLIC | Modifier.STATIC,
              setUpMethodName,
              JavaType.VOID_PRIMITIVE,
              AnnotatedJavaType.convertFromJavaTypes(SETUP_PARAMETERS),
              new ArrayList<JavaSymbolName>(),
              bodyBuilder);
      methodBuilder.setAnnotations(annotations);
      builder.addMethod(methodBuilder);
    }

    // Prepare tearDown method signature
    JavaSymbolName tearDownMethodName = new JavaSymbolName("tearDown");
    MethodMetadata tearDownMethod = getGovernorMethod(tearDownMethodName, TEARDOWN_PARAMETERS);
    if (tearDownMethod != null) {
      Assert.notNull(
          MemberFindingUtils.getAnnotationOfType(tearDownMethod.getAnnotations(), AFTER_CLASS),
          "Method 'tearDown' on '"
              + destination.getFullyQualifiedTypeName()
              + "' must be annotated with @AfterClass");
    } else {
      // Add the method via the ITD
      List<AnnotationMetadataBuilder> annotations = new ArrayList<AnnotationMetadataBuilder>();
      annotations.add(new AnnotationMetadataBuilder(AFTER_CLASS));

      InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder();
      bodyBuilder.appendFormalLine("helper.tearDown();");

      MethodMetadataBuilder methodBuilder =
          new MethodMetadataBuilder(
              getId(),
              Modifier.PUBLIC | Modifier.STATIC,
              tearDownMethodName,
              JavaType.VOID_PRIMITIVE,
              AnnotatedJavaType.convertFromJavaTypes(TEARDOWN_PARAMETERS),
              new ArrayList<JavaSymbolName>(),
              bodyBuilder);
      methodBuilder.setAnnotations(annotations);
      builder.addMethod(methodBuilder);
    }
  }
  /** @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();
  }
 // @Trace
 private boolean checkNoArguments(MethodMetadata method) {
   if (method.getParameterNames() != null && method.getParameterNames().size() > 0) return false;
   return true;
 }
  /**
   * @return the static utility entityManager() method used by other methods to obtain entity
   *     manager and available as a utility for user code (never returns nulls)
   */
  public MethodMetadata getEntityManagerMethod() {
    if (parent != null) {
      // The parent is required to guarantee this is available
      return parent.getEntityManagerMethod();
    }

    // Method definition to find or build
    final JavaSymbolName methodName = new JavaSymbolName(ENTITY_MANAGER_METHOD_NAME);
    final JavaType[] parameterTypes = {};
    final JavaType returnType = ENTITY_MANAGER;

    // 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 InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder();

    if (Modifier.isAbstract(governorTypeDetails.getModifier())) {
      // Create an anonymous inner class that extends the abstract class (no-arg constructor is
      // available as this is a JPA entity)
      bodyBuilder.appendFormalLine(
          ENTITY_MANAGER.getNameIncludingTypeParameters(
                  false, builder.getImportRegistrationResolver())
              + " em = new "
              + destination.getSimpleTypeName()
              + "() {");
      // Handle any abstract methods in this class
      bodyBuilder.indent();
      for (final MethodMetadata method : governorTypeDetails.getMethods()) {
        if (Modifier.isAbstract(method.getModifier())) {
          final StringBuilder params = new StringBuilder();
          int i = -1;
          final List<AnnotatedJavaType> types = method.getParameterTypes();
          for (final JavaSymbolName name : method.getParameterNames()) {
            i++;
            if (i > 0) {
              params.append(", ");
            }
            final AnnotatedJavaType type = types.get(i);
            params.append(type.toString()).append(" ").append(name);
          }
          final int newModifier = method.getModifier() - Modifier.ABSTRACT;
          bodyBuilder.appendFormalLine(
              Modifier.toString(newModifier)
                  + " "
                  + method.getReturnType().getNameIncludingTypeParameters()
                  + " "
                  + method.getMethodName().getSymbolName()
                  + "("
                  + params.toString()
                  + ") {");
          bodyBuilder.indent();
          bodyBuilder.appendFormalLine("throw new UnsupportedOperationException();");
          bodyBuilder.indentRemove();
          bodyBuilder.appendFormalLine("}");
        }
      }
      bodyBuilder.indentRemove();
      bodyBuilder.appendFormalLine(
          "}." + getEntityManagerField().getFieldName().getSymbolName() + ";");
    } else {
      // Instantiate using the no-argument constructor (we know this is available as the entity must
      // comply with the JPA no-arg constructor requirement)
      bodyBuilder.appendFormalLine(
          ENTITY_MANAGER.getNameIncludingTypeParameters(
                  false, builder.getImportRegistrationResolver())
              + " em = new "
              + destination.getSimpleTypeName()
              + "()."
              + getEntityManagerField().getFieldName().getSymbolName()
              + ";");
    }

    bodyBuilder.appendFormalLine(
        "if (em == null) throw new IllegalStateException(\"Entity manager has not been injected (is the Spring Aspects JAR configured as an AJC/AJDT aspects library?)\");");
    bodyBuilder.appendFormalLine("return em;");
    final int modifier = Modifier.PUBLIC | Modifier.STATIC | Modifier.FINAL;

    final MethodMetadataBuilder methodBuilder =
        new MethodMetadataBuilder(
            getId(),
            modifier,
            methodName,
            returnType,
            AnnotatedJavaType.convertFromJavaTypes(parameterTypes),
            new ArrayList<JavaSymbolName>(),
            bodyBuilder);
    return methodBuilder.build();
  }
  private MethodMetadata getDelegateMethod(
      final JavaSymbolName methodName, final String methodDelegateName) {
    // Method definition to find or build
    final JavaType[] parameterTypes = {};

    // Locate user-defined method
    final MethodMetadata userMethod = getGovernorMethod(methodName, parameterTypes);
    if (userMethod != null) {
      return userMethod;
    }

    // Create the method
    final List<AnnotationMetadataBuilder> annotations = new ArrayList<AnnotationMetadataBuilder>();

    final InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder();

    // Address non-injected entity manager field
    final MethodMetadata entityManagerMethod = getEntityManagerMethod();
    Assert.notNull(entityManagerMethod, "Entity manager method should not have returned null");

    // Use the getEntityManager() method to acquire an entity manager (the method will throw an
    // exception if it cannot be acquired)
    final String entityManagerFieldName = getEntityManagerField().getFieldName().getSymbolName();
    bodyBuilder.appendFormalLine(
        "if (this."
            + entityManagerFieldName
            + " == null) this."
            + entityManagerFieldName
            + " = "
            + entityManagerMethod.getMethodName().getSymbolName()
            + "();");

    JavaType returnType = JavaType.VOID_PRIMITIVE;
    if ("flush".equals(methodDelegateName)) {
      addTransactionalAnnotation(annotations);
      bodyBuilder.appendFormalLine("this." + entityManagerFieldName + ".flush();");
    } else if ("clear".equals(methodDelegateName)) {
      addTransactionalAnnotation(annotations);
      bodyBuilder.appendFormalLine("this." + entityManagerFieldName + ".clear();");
    } else if ("merge".equals(methodDelegateName)) {
      addTransactionalAnnotation(annotations);
      returnType = new JavaType(destination.getSimpleTypeName());
      bodyBuilder.appendFormalLine(
          destination.getSimpleTypeName()
              + " merged = this."
              + entityManagerFieldName
              + ".merge(this);");
      bodyBuilder.appendFormalLine("this." + entityManagerFieldName + ".flush();");
      bodyBuilder.appendFormalLine("return merged;");
    } else if ("remove".equals(methodDelegateName)) {
      addTransactionalAnnotation(annotations);
      bodyBuilder.appendFormalLine("if (this." + entityManagerFieldName + ".contains(this)) {");
      bodyBuilder.indent();
      bodyBuilder.appendFormalLine("this." + entityManagerFieldName + ".remove(this);");
      bodyBuilder.indentRemove();
      bodyBuilder.appendFormalLine("} else {");
      bodyBuilder.indent();
      bodyBuilder.appendFormalLine(
          destination.getSimpleTypeName()
              + " attached = "
              + destination.getSimpleTypeName()
              + "."
              + getFindMethod().getMethodName().getSymbolName()
              + "(this."
              + identifierField.getFieldName().getSymbolName()
              + ");");
      bodyBuilder.appendFormalLine("this." + entityManagerFieldName + ".remove(attached);");
      bodyBuilder.indentRemove();
      bodyBuilder.appendFormalLine("}");
    } else {
      // Persist
      addTransactionalAnnotation(annotations, true);
      bodyBuilder.appendFormalLine(
          "this." + entityManagerFieldName + "." + methodDelegateName + "(this);");
    }

    final MethodMetadataBuilder methodBuilder =
        new MethodMetadataBuilder(
            getId(),
            Modifier.PUBLIC,
            methodName,
            returnType,
            AnnotatedJavaType.convertFromJavaTypes(parameterTypes),
            new ArrayList<JavaSymbolName>(),
            bodyBuilder);
    methodBuilder.setAnnotations(annotations);
    return methodBuilder.build();
  }
  /** @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;
  }
  /**
   * Obtains the "toString" method for this type, if available.
   *
   * <p>If the user provided a non-default name for "toString", that method will be returned.
   *
   * @return the "toString" method declared on this type or that will be introduced (or null if
   *     undeclared and not introduced)
   */
  public MethodMetadata getToStringMethod() {
    // Compute the relevant toString method name
    JavaSymbolName methodName = new JavaSymbolName("toString");
    if (!this.toStringMethod.equals("")) {
      methodName = new JavaSymbolName(this.toStringMethod);
    }

    // See if the type itself declared the method
    MethodMetadata result =
        MemberFindingUtils.getDeclaredMethod(governorTypeDetails, methodName, null);
    if (result != null) {
      return result;
    }

    // Decide whether we need to produce the toString method
    if (this.toStringMethod.equals("")) {
      return null;
    }

    InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder();
    bodyBuilder.appendFormalLine("StringBuilder sb = new StringBuilder();");

    /** Key: field name, Value: accessor name */
    Map<String, String> map = new LinkedHashMap<String, String>();

    /** Field names */
    List<String> order = new ArrayList<String>();

    Set<String> excludeFieldsSet = new LinkedHashSet<String>();
    if (excludeFields != null && excludeFields.length > 0) {
      Collections.addAll(excludeFieldsSet, excludeFields);
    }

    for (MethodMetadata accessor : locatedAccessors) {
      String accessorName = accessor.getMethodName().getSymbolName();
      String fieldName = BeanInfoUtils.getPropertyNameForJavaBeanMethod(accessor).getSymbolName();
      if (!excludeFieldsSet.contains(StringUtils.uncapitalize(fieldName))
          && !map.containsKey(fieldName)) {
        String accessorText = accessorName + "()";
        if (accessor.getReturnType().isCommonCollectionType()) {
          accessorText = accessorName + "() == null ? \"null\" : " + accessorName + "().size()";
        } else if (accessor.getReturnType().isArray()) {
          accessorText = "java.util.Arrays.toString(" + accessorName + "())";
        } else if (Calendar.class
            .getName()
            .equals(accessor.getReturnType().getFullyQualifiedTypeName())) {
          accessorText = accessorName + "() == null ? \"null\" : " + accessorName + "().getTime()";
        }
        map.put(fieldName, accessorText);
        order.add(fieldName);
      }
    }

    if (!order.isEmpty()) {
      int index = 0;
      int size = map.keySet().size();
      for (String fieldName : order) {
        index++;
        String accessorText = map.get(fieldName);
        StringBuilder string = new StringBuilder();
        string
            .append("sb.append(\"")
            .append(fieldName)
            .append(": \").append(")
            .append(accessorText)
            .append(")");
        if (index < size) {
          string.append(".append(\", \")");
        }
        string.append(";");
        bodyBuilder.appendFormalLine(string.toString());
      }

      bodyBuilder.appendFormalLine("return sb.toString();");

      MethodMetadataBuilder methodBuilder =
          new MethodMetadataBuilder(getId(), Modifier.PUBLIC, methodName, STRING, bodyBuilder);
      result = methodBuilder.build();
    }

    return result;
  }
 private boolean isMethodOfInterest(MethodMetadata method) {
   return method.getMethodName().getSymbolName().startsWith("set")
       && method.getModifier() == Modifier.PUBLIC;
 }
  public MetadataItem get(String metadataIdentificationString) {
    ProjectMetadata projectMetadata = projectOperations.getProjectMetadata();
    if (projectMetadata == null) {
      return null;
    }

    ClassOrInterfaceTypeDetails proxy = getGovernor(metadataIdentificationString);
    if (proxy == null) {
      return null;
    }

    AnnotationMetadata proxyAnnotation =
        GwtUtils.getFirstAnnotation(proxy, GwtUtils.PROXY_ANNOTATIONS);
    if (proxyAnnotation == null) {
      return null;
    }

    String locatorType = GwtUtils.getStringValue(proxyAnnotation.getAttribute("locator"));
    if (!StringUtils.hasText(locatorType)) {
      return null;
    }

    ClassOrInterfaceTypeDetails entity = gwtTypeService.lookupEntityFromProxy(proxy);
    if (entity == null) {
      return null;
    }

    MethodMetadata idAccessor = persistenceMemberLocator.getIdentifierAccessor(entity.getName());
    MethodMetadata versionAccessor = persistenceMemberLocator.getVersionAccessor(entity.getName());
    if (idAccessor == null || versionAccessor == null) {
      return null;
    }

    final JavaType idType = GwtUtils.convertPrimitiveType(idAccessor.getReturnType(), true);
    String locatorIdentifier = PhysicalTypeIdentifier.createIdentifier(new JavaType(locatorType));
    ClassOrInterfaceTypeDetailsBuilder locatorBuilder =
        new ClassOrInterfaceTypeDetailsBuilder(locatorIdentifier);
    AnnotationMetadataBuilder annotationMetadataBuilder =
        new AnnotationMetadataBuilder(RooJavaType.ROO_GWT_LOCATOR);
    annotationMetadataBuilder.addStringAttribute(
        "value", entity.getName().getFullyQualifiedTypeName());
    locatorBuilder.addAnnotation(annotationMetadataBuilder);
    annotationMetadataBuilder = new AnnotationMetadataBuilder(SpringJavaType.COMPONENT);
    locatorBuilder.addAnnotation(annotationMetadataBuilder);
    locatorBuilder.setName(new JavaType(locatorType));
    locatorBuilder.setModifier(Modifier.PUBLIC);
    locatorBuilder.setPhysicalTypeCategory(PhysicalTypeCategory.CLASS);
    locatorBuilder.addExtendsTypes(
        new JavaType(
            GwtUtils.LOCATOR.getFullyQualifiedTypeName(),
            0,
            DataType.TYPE,
            null,
            Arrays.asList(entity.getName(), idType)));
    locatorBuilder.addMethod(getCreateMethod(locatorIdentifier, entity.getName()));
    locatorBuilder.addMethod(
        getFindMethod(locatorBuilder, locatorIdentifier, entity.getName(), idType));
    locatorBuilder.addMethod(getDomainTypeMethod(locatorIdentifier, entity.getName()));
    locatorBuilder.addMethod(getIdMethod(locatorIdentifier, entity.getName(), idAccessor));
    locatorBuilder.addMethod(getIdTypeMethod(locatorIdentifier, entity.getName(), idType));
    locatorBuilder.addMethod(
        getVersionMethod(locatorIdentifier, entity.getName(), versionAccessor));

    typeManagementService.createOrUpdateTypeOnDisk(locatorBuilder.build());
    return null;
  }
 // @Trace
 private boolean checkBooleanReturn(MethodMetadata method) {
   if (JavaType.BOOLEAN_PRIMITIVE.equals(method.getReturnType())) return true;
   if (JavaType.BOOLEAN_OBJECT.equals(method.getReturnType())) return true;
   return false;
 }