Пример #1
0
  public static JavaType lookupTargetType(
      final ClassOrInterfaceTypeDetails cid, final boolean proxy) {
    JavaType stringBasedAnnotation = SERVICE_NAME;
    JavaType classBasedAnnotation = SERVICE;
    if (proxy) {
      stringBasedAnnotation = PROXY_FOR_NAME;
      classBasedAnnotation = PROXY_FOR;
    }
    AnnotationMetadata serviceNameAnnotation =
        MemberFindingUtils.getAnnotationOfType(cid.getAnnotations(), stringBasedAnnotation);
    if (serviceNameAnnotation != null) {
      AnnotationAttributeValue<String> serviceNameAttributeValue =
          serviceNameAnnotation.getAttribute("value");
      if (serviceNameAttributeValue != null) {
        return new JavaType(serviceNameAttributeValue.getValue());
      }
    }

    serviceNameAnnotation =
        MemberFindingUtils.getAnnotationOfType(cid.getAnnotations(), classBasedAnnotation);
    if (serviceNameAnnotation != null) {
      AnnotationAttributeValue<JavaType> serviceAttributeValue =
          serviceNameAnnotation.getAttribute("value");
      if (serviceAttributeValue != null) {
        return serviceAttributeValue.getValue();
      }
    }

    return null;
  }
Пример #2
0
  private String getDateTimeFormat() {
    String format = "DateTimeFormat.getFormat(DateTimeFormat.PredefinedFormat.DATE_SHORT)";
    if (annotations == null || annotations.isEmpty()) {
      return format;
    }

    String style = "";
    AnnotationMetadata annotation =
        MemberFindingUtils.getAnnotationOfType(annotations, DATE_TIME_FORMAT);
    if (annotation != null) {
      AnnotationAttributeValue<?> attr = annotation.getAttribute(new JavaSymbolName("style"));
      if (attr != null) {
        style = (String) attr.getValue();
      }
    }
    if (StringUtils.hasText(style)) {
      if (style.equals("S")) {
        format = "DateTimeFormat.getFormat(DateTimeFormat.PredefinedFormat.DATE_TIME_SHORT)";
      } else if (style.equals("M")) {
        format = "DateTimeFormat.getFormat(DateTimeFormat.PredefinedFormat.DATE_TIME_MEDIUM)";
      } else if (style.equals("F")) {
        format = "DateTimeFormat.getFormat(DateTimeFormat.PredefinedFormat.DATE_TIME_FULL)";
      } else if (style.equals("S-")) {
        format = "DateTimeFormat.getFormat(DateTimeFormat.PredefinedFormat.DATE_SHORT)";
      } else if (style.equals("M-")) {
        format = "DateTimeFormat.getFormat(DateTimeFormat.PredefinedFormat.DATE_MEDIUM)";
      } else if (style.equals("F-")) {
        format = "DateTimeFormat.getFormat(DateTimeFormat.PredefinedFormat.DATE_FULL)";
      }
    }
    return format;
  }
  @Override
  public void restResource(
      final JavaType type, final boolean hide, final String path, final String rel) {

    final ClassOrInterfaceTypeDetails typeDetails = typeLocationService.getTypeDetails(type);
    Validate.notNull(typeDetails, "The repository specified, '" + type + "'doesn't exist");

    final AnnotationMetadataBuilder annotationBuilder =
        new AnnotationMetadataBuilder(REST_RESOURCE);
    if (hide) {
      annotationBuilder.addBooleanAttribute("exported", false);
    }
    if (!StringUtils.isEmpty(path)) {
      annotationBuilder.addStringAttribute("path", path);
    }
    if (!StringUtils.isEmpty(rel)) {
      annotationBuilder.addStringAttribute("rel", rel);
    }

    final ClassOrInterfaceTypeDetailsBuilder cidBuilder =
        new ClassOrInterfaceTypeDetailsBuilder(typeDetails);

    if (MemberFindingUtils.getAnnotationOfType(typeDetails.getAnnotations(), REST_RESOURCE)
        != null) {
      cidBuilder.removeAnnotation(REST_RESOURCE);
    }

    cidBuilder.addAnnotation(annotationBuilder);

    typeManagementService.createOrUpdateTypeOnDisk(cidBuilder.build());
  }
 /**
  * Indicates whether the web JSON ITD should introduce any required layer components (services,
  * repositories, etc.). This information is necessary for so long as AspectJ does not allow the
  * same field to be introduced into a given Java class by more than one ITD.
  *
  * @param governor the governor, i.e. the controller (required)
  * @return see above
  */
 private boolean introduceLayerComponents(final PhysicalTypeMetadata governor) {
   // If no MVC ITD is going to be created, we have to introduce any
   // required layer components
   return MemberFindingUtils.getAnnotationOfType(
           governor.getMemberHoldingTypeDetails().getAnnotations(), ROO_WEB_SCAFFOLD)
       == null;
 }
  /**
   * 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();
    }
  }
  /** {@inheritDoc} */
  public void annotateType(final JavaType type, final JavaPackage javaPackage) {
    // TODO check for existing controller

    // Use Roo's Assert type for null checks
    Validate.notNull(type, "Java type required");
    Validate.notNull(javaPackage, "Java package required, web mvc all command package required");

    // Obtain ClassOrInterfaceTypeDetails for this java type
    ClassOrInterfaceTypeDetails entityDetails = typeLocationService.getTypeDetails(type);

    // Test if the annotation already exists on the target type
    if (entityDetails != null
        && MemberFindingUtils.getAnnotationOfType(
                entityDetails.getAnnotations(), new JavaType(RooEnvers.class.getName()))
            == null) {
      ClassOrInterfaceTypeDetailsBuilder classOrInterfaceTypeDetailsBuilder =
          new ClassOrInterfaceTypeDetailsBuilder(entityDetails);

      // Create JavaType instance for the add-ons trigger annotation
      JavaType rooEnvers = new JavaType(RooEnvers.class.getName());

      // Create Annotation metadata
      AnnotationMetadataBuilder annotationBuilder = new AnnotationMetadataBuilder(rooEnvers);

      // Add annotation to target type
      classOrInterfaceTypeDetailsBuilder.addAnnotation(annotationBuilder.build());

      // Save changes to disk
      typeManagementService.createOrUpdateTypeOnDisk(classOrInterfaceTypeDetailsBuilder.build());
    }

    // Get details for existing controller
    JavaType typeController =
        new JavaType(
            javaPackage.getFullyQualifiedPackageName()
                + "."
                + type.getSimpleTypeName()
                + "Controller");
    ClassOrInterfaceTypeDetails typeControllerDetails =
        typeLocationService.getTypeDetails(typeController);

    // Add annotation @RooEnversController to existing controller
    ClassOrInterfaceTypeDetailsBuilder classOrInterfaceTypeDetailsBuilder =
        new ClassOrInterfaceTypeDetailsBuilder(typeControllerDetails);
    JavaType rooEnversController =
        new JavaType("de.eightbitboy.roo.envers.controller.RooEnversController");

    final List<AnnotationAttributeValue<?>> rooEnversControllerAttributes =
        new ArrayList<AnnotationAttributeValue<?>>();
    rooEnversControllerAttributes.add(new ClassAttributeValue(new JavaSymbolName("type"), type));

    AnnotationMetadataBuilder annotationBuilder =
        new AnnotationMetadataBuilder(rooEnversController, rooEnversControllerAttributes);
    classOrInterfaceTypeDetailsBuilder.addAnnotation(annotationBuilder.build());

    typeManagementService.createOrUpdateTypeOnDisk(classOrInterfaceTypeDetailsBuilder.build());
  }
 public MethodMetadata getMethod(final JavaSymbolName methodName) {
   for (final MemberHoldingTypeDetails memberHoldingTypeDetails : details) {
     final MethodMetadata md =
         MemberFindingUtils.getDeclaredMethod(memberHoldingTypeDetails, methodName);
     if (md != null) {
       return md;
     }
   }
   return null;
 }
Пример #8
0
 public static AnnotationMetadata getFirstAnnotation(
     final ClassOrInterfaceTypeDetails cid, final JavaType... annotationTypes) {
   for (JavaType annotationType : annotationTypes) {
     AnnotationMetadata annotationMetadata =
         MemberFindingUtils.getAnnotationOfType(cid.getAnnotations(), annotationType);
     if (annotationMetadata != null) {
       return annotationMetadata;
     }
   }
   return null;
 }
 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;
 }
Пример #10
0
 public boolean isRequestingAnnotatedWith(
     final AnnotationMetadata annotationMetadata, final String requestingMid) {
   for (final MemberHoldingTypeDetails memberHoldingTypeDetails : details) {
     if (MemberFindingUtils.getAnnotationOfType(
             memberHoldingTypeDetails.getAnnotations(), annotationMetadata.getAnnotationType())
         != null) {
       if (memberHoldingTypeDetails.getDeclaredByMetadataId().equals(requestingMid)) {
         return true;
       }
     }
   }
   return false;
 }
 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;
 }
Пример #12
0
 public static boolean getBooleanAnnotationValue(
     final ClassOrInterfaceTypeDetails target,
     final JavaType annotationType,
     final String attributeName,
     final boolean valueIfNull) {
   AnnotationMetadata annotation =
       MemberFindingUtils.getAnnotationOfType(target.getAnnotations(), annotationType);
   if (annotation == null) {
     return valueIfNull;
   }
   AnnotationAttributeValue<?> attributeValue = annotation.getAttribute(attributeName);
   if (attributeValue != null && attributeValue instanceof BooleanAttributeValue) {
     BooleanAttributeValue booleanAttributeValue = (BooleanAttributeValue) attributeValue;
     return booleanAttributeValue.getValue();
   }
   return valueIfNull;
 }
Пример #13
0
  public String getFormatter() {
    if (isCollectionOfProxy()) {
      return getCollectionRenderer() + ".render";
    } else if (isDate()) {
      return getDateTimeFormat() + ".format";
    } else if (type.equals(JavaType.INT_OBJECT)
        || type.equals(JavaType.FLOAT_OBJECT)
        || type.equals(JavaType.DOUBLE_OBJECT)
        || type.equals(BIG_INTEGER)
        || type.equals(BIG_DECIMAL)) {
      String formatter = "String.valueOf";
      if (annotations == null || annotations.isEmpty()) {
        return formatter;
      }

      AnnotationMetadata annotation =
          MemberFindingUtils.getAnnotationOfType(annotations, NUMBER_FORMAT);
      if (annotation != null) {
        AnnotationAttributeValue<?> attr = annotation.getAttribute(new JavaSymbolName("style"));
        if (attr != null) {
          String style = attr.getValue().toString();
          if ("org.springframework.format.annotation.NumberFormat.Style.CURRENCY".equals(style)) {
            formatter = "NumberFormat.getCurrencyFormat().format";
          } else if ("org.springframework.format.annotation.NumberFormat.Style.PERCENT"
              .equals(style)) {
            formatter = "NumberFormat.getPercentFormat().format";
          } else {
            formatter = "NumberFormat.getDecimalFormat().format";
          }
        } else {
          formatter = "NumberFormat.getDecimalFormat().format";
        }
      }
      return formatter;
    } else if (isProxy()) {
      return getProxyRendererType() + ".instance().render";
    } else {
      return "String.valueOf";
    }
  }
Пример #14
0
  private MethodMetadata getToJsonMethod() {
    // Compute the relevant method name
    JavaSymbolName methodName = getToJsonMethodName();
    if (methodName == null) {
      return null;
    }

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

    InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder();
    String serializer =
        new JavaType("flexjson.JSONSerializer")
            .getNameIncludingTypeParameters(false, builder.getImportRegistrationResolver());
    String root =
        annotationValues.getRootName() != null && annotationValues.getRootName().length() > 0
            ? ".rootName(\"" + annotationValues.getRootName() + "\")"
            : "";
    bodyBuilder.appendFormalLine(
        "return new "
            + serializer
            + "()"
            + root
            + ".exclude(\"*.class\")"
            + (annotationValues.isDeepSerialize() ? ".deepSerialize(this)" : ".serialize(this)")
            + ";");

    MethodMetadataBuilder methodBuilder =
        new MethodMetadataBuilder(
            getId(), Modifier.PUBLIC, methodName, new JavaType("java.lang.String"), bodyBuilder);
    methodBuilder.putCustomData(CustomDataJsonTags.TO_JSON_METHOD, null);
    return methodBuilder.build();
  }
Пример #15
0
 public static List<String> getAnnotationValues(
     final ClassOrInterfaceTypeDetails target,
     final JavaType annotationType,
     final String attributeName) {
   List<String> values = new ArrayList<String>();
   AnnotationMetadata annotation =
       MemberFindingUtils.getAnnotationOfType(target.getAnnotations(), annotationType);
   if (annotation == null) {
     return values;
   }
   AnnotationAttributeValue<?> attributeValue = annotation.getAttribute(attributeName);
   if (attributeValue != null && attributeValue instanceof ArrayAttributeValue) {
     @SuppressWarnings("unchecked")
     ArrayAttributeValue<StringAttributeValue> arrayAttributeValue =
         (ArrayAttributeValue<StringAttributeValue>) attributeValue;
     for (StringAttributeValue value : arrayAttributeValue.getValue()) {
       values.add(value.getValue());
     }
   } else if (attributeValue != null && attributeValue instanceof StringAttributeValue) {
     StringAttributeValue stringAttributeVale = (StringAttributeValue) attributeValue;
     values.add(stringAttributeVale.getValue());
   }
   return values;
 }
  @Override
  protected ItdTypeDetailsProvidingMetadataItem getMetadata(
      final String metadataIdentificationString,
      final JavaType aspectName,
      final PhysicalTypeMetadata governorPhysicalTypeMetadata,
      final String itdFilename) {
    // We need to parse the annotation, which we expect to be present
    final WebJsonAnnotationValues annotationValues =
        new WebJsonAnnotationValues(governorPhysicalTypeMetadata);
    if (!annotationValues.isAnnotationFound()
        || annotationValues.getJsonObject() == null
        || governorPhysicalTypeMetadata.getMemberHoldingTypeDetails() == null) {
      return null;
    }

    // Lookup the form backing object's metadata
    final JavaType jsonObject = annotationValues.getJsonObject();
    final ClassOrInterfaceTypeDetails jsonTypeDetails =
        typeLocationService.getTypeDetails(jsonObject);
    if (jsonTypeDetails == null) {
      return null;
    }
    final LogicalPath jsonObjectPath =
        PhysicalTypeIdentifier.getPath(jsonTypeDetails.getDeclaredByMetadataId());
    final JsonMetadata jsonMetadata =
        (JsonMetadata)
            metadataService.get(JsonMetadata.createIdentifier(jsonObject, jsonObjectPath));
    if (jsonMetadata == null) {
      return null;
    }

    final PhysicalTypeMetadata backingObjectPhysicalTypeMetadata =
        (PhysicalTypeMetadata)
            metadataService.get(
                PhysicalTypeIdentifier.createIdentifier(
                    jsonObject, typeLocationService.getTypePath(jsonObject)));
    Validate.notNull(
        backingObjectPhysicalTypeMetadata,
        "Unable to obtain physical type metadata for type %s",
        jsonObject.getFullyQualifiedTypeName());
    final MemberDetails formBackingObjectMemberDetails =
        getMemberDetails(backingObjectPhysicalTypeMetadata);
    final MemberHoldingTypeDetails backingMemberHoldingTypeDetails =
        MemberFindingUtils.getMostConcreteMemberHoldingTypeDetailsWithTag(
            formBackingObjectMemberDetails, CustomDataKeys.PERSISTENT_TYPE);
    if (backingMemberHoldingTypeDetails == null) {
      return null;
    }

    // We need to be informed if our dependent metadata changes
    metadataDependencyRegistry.registerDependency(
        backingMemberHoldingTypeDetails.getDeclaredByMetadataId(), metadataIdentificationString);

    final Set<FinderMetadataDetails> finderDetails =
        webMetadataService.getDynamicFinderMethodsAndFields(
            jsonObject, formBackingObjectMemberDetails, metadataIdentificationString);
    if (finderDetails == null) {
      return null;
    }
    final Map<MethodMetadataCustomDataKey, MemberTypeAdditions> persistenceAdditions =
        webMetadataService.getCrudAdditions(jsonObject, metadataIdentificationString);
    final JavaTypePersistenceMetadataDetails javaTypePersistenceMetadataDetails =
        webMetadataService.getJavaTypePersistenceMetadataDetails(
            jsonObject, getMemberDetails(jsonObject), metadataIdentificationString);
    final PluralMetadata pluralMetadata =
        (PluralMetadata)
            metadataService.get(
                PluralMetadata.createIdentifier(
                    jsonObject, typeLocationService.getTypePath(jsonObject)));
    if (persistenceAdditions.isEmpty()
        || javaTypePersistenceMetadataDetails == null
        || pluralMetadata == null) {
      return null;
    }

    // Maintain a list of entities that are being tested
    managedEntityTypes.put(jsonObject, metadataIdentificationString);

    return new WebJsonMetadata(
        metadataIdentificationString,
        aspectName,
        governorPhysicalTypeMetadata,
        annotationValues,
        persistenceAdditions,
        javaTypePersistenceMetadataDetails.getIdentifierField(),
        pluralMetadata.getPlural(),
        finderDetails,
        jsonMetadata,
        introduceLayerComponents(governorPhysicalTypeMetadata));
  }
Пример #17
0
  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;
  }
  /**
   * 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 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);
    }
  }
  /** Adds the JUnit and Spring type level annotations if needed */
  private void addRequiredIntegrationTestClassIntroductions(final JavaType dodGovernor) {
    // Add an @RunWith(SpringJunit4ClassRunner) annotation to the type, if
    // the user did not define it on the governor directly
    if (MemberFindingUtils.getAnnotationOfType(governorTypeDetails.getAnnotations(), RUN_WITH)
        == null) {
      AnnotationMetadataBuilder runWithBuilder = new AnnotationMetadataBuilder(RUN_WITH);
      runWithBuilder.addClassAttribute(
          "value", "org.springframework.test.context.junit4.SpringJUnit4ClassRunner");
      builder.addAnnotation(runWithBuilder);
    }

    // Add an @ContextConfiguration("classpath:/applicationContext.xml")
    // annotation to the type, if the user did not define it on the governor
    // directly
    if (MemberFindingUtils.getAnnotationOfType(
            governorTypeDetails.getAnnotations(), CONTEXT_CONFIGURATION)
        == null) {
      AnnotationMetadataBuilder contextConfigurationBuilder =
          new AnnotationMetadataBuilder(CONTEXT_CONFIGURATION);
      contextConfigurationBuilder.addStringAttribute(
          "locations", "classpath:/META-INF/spring/applicationContext*.xml");
      builder.addAnnotation(contextConfigurationBuilder);
    }

    // Add an @Transactional, if the user did not define it on the governor
    // directly
    if (annotationValues.isTransactional()
        && MemberFindingUtils.getAnnotationOfType(
                governorTypeDetails.getAnnotations(), TRANSACTIONAL)
            == null) {
      AnnotationMetadataBuilder transactionalBuilder = new AnnotationMetadataBuilder(TRANSACTIONAL);
      if (StringUtils.hasText(transactionManager)
          && !"transactionManager".equals(transactionManager)) {
        transactionalBuilder.addStringAttribute("value", transactionManager);
      }
      builder.addAnnotation(transactionalBuilder);
    }

    // Add the data on demand field if the user did not define it on the
    // governor directly
    FieldMetadata field = governorTypeDetails.getField(new JavaSymbolName("dod"));
    if (field != null) {
      Assert.isTrue(
          field.getFieldType().equals(dodGovernor),
          "Field 'dod' on '"
              + destination.getFullyQualifiedTypeName()
              + "' must be of type '"
              + dodGovernor.getFullyQualifiedTypeName()
              + "'");
      Assert.notNull(
          MemberFindingUtils.getAnnotationOfType(field.getAnnotations(), AUTOWIRED),
          "Field 'dod' on '"
              + destination.getFullyQualifiedTypeName()
              + "' must be annotated with @Autowired");
    } else {
      // Add the field via the ITD
      List<AnnotationMetadataBuilder> annotations = new ArrayList<AnnotationMetadataBuilder>();
      annotations.add(new AnnotationMetadataBuilder(AUTOWIRED));
      FieldMetadataBuilder fieldBuilder =
          new FieldMetadataBuilder(
              getId(), Modifier.PRIVATE, annotations, new JavaSymbolName("dod"), dodGovernor);
      builder.addField(fieldBuilder);
    }

    builder.getImportRegistrationResolver().addImport(ASSERT);
  }
  public void notify(String upstreamDependency, String downstreamDependency) {
    ProjectMetadata projectMetadata = projectOperations.getProjectMetadata();
    if (projectMetadata == null) {
      return;
    }

    if (MetadataIdentificationUtils.isIdentifyingClass(downstreamDependency)) {
      Assert.isTrue(
          MetadataIdentificationUtils.getMetadataClass(upstreamDependency)
              .equals(
                  MetadataIdentificationUtils.getMetadataClass(
                      PhysicalTypeIdentifier.getMetadataIdentiferType())),
          "Expected class-level notifications only for PhysicalTypeIdentifier (not '"
              + upstreamDependency
              + "')");

      ClassOrInterfaceTypeDetails cid =
          typeLocationService.getTypeForIdentifier(upstreamDependency);
      boolean processed = false;
      if (MemberFindingUtils.getAnnotationOfType(cid.getAnnotations(), RooJavaType.ROO_GWT_REQUEST)
          != null) {
        ClassOrInterfaceTypeDetails proxy = gwtTypeService.lookupProxyFromRequest(cid);
        if (proxy != null) {
          JavaType typeName = PhysicalTypeIdentifier.getJavaType(proxy.getDeclaredByMetadataId());
          Path typePath = PhysicalTypeIdentifier.getPath(proxy.getDeclaredByMetadataId());
          downstreamDependency = GwtLocatorMetadata.createIdentifier(typeName, typePath);
          processed = true;
        }
      }
      if (!processed
          && MemberFindingUtils.getAnnotationOfType(cid.getAnnotations(), RooJavaType.ROO_GWT_PROXY)
              == null) {
        boolean found = false;
        for (ClassOrInterfaceTypeDetails classOrInterfaceTypeDetails :
            typeLocationService.findClassesOrInterfaceDetailsWithAnnotation(
                RooJavaType.ROO_GWT_PROXY)) {
          AnnotationMetadata annotationMetadata =
              GwtUtils.getFirstAnnotation(
                  classOrInterfaceTypeDetails, GwtUtils.ROO_PROXY_REQUEST_ANNOTATIONS);
          if (annotationMetadata != null) {
            AnnotationAttributeValue<?> attributeValue = annotationMetadata.getAttribute("value");
            if (attributeValue != null) {
              String mirrorName = GwtUtils.getStringValue(attributeValue);
              if (mirrorName != null
                  && cid.getName().getFullyQualifiedTypeName().equals(attributeValue.getValue())) {
                found = true;
                JavaType typeName =
                    PhysicalTypeIdentifier.getJavaType(
                        classOrInterfaceTypeDetails.getDeclaredByMetadataId());
                Path typePath =
                    PhysicalTypeIdentifier.getPath(
                        classOrInterfaceTypeDetails.getDeclaredByMetadataId());
                downstreamDependency = GwtLocatorMetadata.createIdentifier(typeName, typePath);
                break;
              }
            }
          }
        }
        if (!found) {
          return;
        }
      } else if (!processed) {
        // A physical Java type has changed, and determine what the corresponding local metadata
        // identification string would have been
        JavaType typeName = PhysicalTypeIdentifier.getJavaType(upstreamDependency);
        Path typePath = PhysicalTypeIdentifier.getPath(upstreamDependency);
        downstreamDependency = GwtLocatorMetadata.createIdentifier(typeName, typePath);
      }

      // We only need to proceed if the downstream dependency relationship is not already registered
      // (if it's already registered, the event will be delivered directly later on)
      if (metadataDependencyRegistry
          .getDownstream(upstreamDependency)
          .contains(downstreamDependency)) {
        return;
      }
    }

    // We should now have an instance-specific "downstream dependency" that can be processed by this
    // class
    Assert.isTrue(
        MetadataIdentificationUtils.getMetadataClass(downstreamDependency)
            .equals(MetadataIdentificationUtils.getMetadataClass(getProvidesType())),
        "Unexpected downstream notification for '"
            + downstreamDependency
            + "' to this provider (which uses '"
            + getProvidesType()
            + "'");

    metadataService.get(downstreamDependency, true);
  }