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

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

    builder.getImportRegistrationResolver().addImport(LIST);

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

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

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

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

    MethodMetadataBuilder methodBuilder =
        new MethodMetadataBuilder(
            getId(), Modifier.PUBLIC, methodName, JavaType.VOID_PRIMITIVE, bodyBuilder);
    methodBuilder.setAnnotations(annotations);
    return methodBuilder;
  }
  /**
   * Constructor
   *
   * @param metadataId (required)
   * @param aspectName (required)
   * @param governorPhysicalTypeMetadata (required)
   * @param parent can be <code>null</code>
   * @param projectMetadata (required)
   * @param crudAnnotationValues the CRUD-related annotation values (required)
   * @param plural the plural form of the entity (required)
   * @param identifierField the entity's identifier field (required)
   * @param entityName the JPA entity name (required)
   */
  public JpaActiveRecordMetadata(
      final String metadataId,
      final JavaType aspectName,
      final PhysicalTypeMetadata governorPhysicalTypeMetadata,
      final JpaActiveRecordMetadata parent,
      final JpaCrudAnnotationValues crudAnnotationValues,
      final String plural,
      final FieldMetadata identifierField,
      final String entityName,
      final boolean isGaeEnabled) {
    super(metadataId, aspectName, governorPhysicalTypeMetadata);
    Assert.isTrue(
        isValid(metadataId),
        "Metadata identification string '" + metadataId + "' does not appear to be a valid");
    Assert.notNull(crudAnnotationValues, "CRUD-related annotation values required");
    Assert.notNull(identifierField, "Identifier required for '" + metadataId + "'");
    Assert.hasText(entityName, "Entity name required for '" + metadataId + "'");
    Assert.hasText(plural, "Plural required for '" + metadataId + "'");

    if (!isValid()) {
      return;
    }

    this.crudAnnotationValues = crudAnnotationValues;
    this.entityName = entityName;
    this.identifierField = identifierField;
    this.isGaeEnabled = isGaeEnabled;
    this.parent = parent;
    this.plural = StringUtils.capitalize(plural);

    // Determine the entity's "entityManager" field, which is guaranteed to be accessible to the
    // ITD.
    builder.addField(getEntityManagerField());

    // Add helper methods
    builder.addMethod(getPersistMethod());
    builder.addMethod(getRemoveMethod());
    builder.addMethod(getFlushMethod());
    builder.addMethod(getClearMethod());
    builder.addMethod(getMergeMethod());

    // Add static methods
    builder.addMethod(getEntityManagerMethod());
    builder.addMethod(getCountMethod());
    builder.addMethod(getFindAllMethod());
    builder.addMethod(getFindMethod());
    builder.addMethod(getFindEntriesMethod());

    builder.putCustomData(CustomDataKeys.DYNAMIC_FINDER_NAMES, getDynamicFinders());

    // Create a representation of the desired output ITD
    itdTypeDetails = builder.build();
  }
  /** @return a test for the find entries method, if available and requested (may return null) */
  private MethodMetadataBuilder getFindEntriesMethodTest(
      final MemberTypeAdditions countMethod, final MemberTypeAdditions findEntriesMethod) {
    if (!annotationValues.isFindEntries() || countMethod == null || findEntriesMethod == null) {
      // User does not want this method, or core dependencies are missing
      return null;
    }

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

    builder.getImportRegistrationResolver().addImport(LIST);

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

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

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

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

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

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

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

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

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

    countMethod.copyAdditionsTo(builder, governorTypeDetails);

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    persistMethod.copyAdditionsTo(builder, governorTypeDetails);

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

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

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

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

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

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

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

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

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

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

    MethodMetadataBuilder methodBuilder =
        new MethodMetadataBuilder(
            getId(), Modifier.PUBLIC, methodName, JavaType.VOID_PRIMITIVE, bodyBuilder);
    methodBuilder.setAnnotations(annotations);
    return methodBuilder;
  }
  private TemplateDataDictionary buildMirrorDataDictionary(
      final GwtType type,
      final ClassOrInterfaceTypeDetails mirroredType,
      final ClassOrInterfaceTypeDetails proxy,
      final Map<GwtType, JavaType> mirrorTypeMap,
      final Map<JavaSymbolName, GwtProxyProperty> clientSideTypeMap,
      final String moduleName) {
    JavaType proxyType = proxy.getName();
    JavaType javaType = mirrorTypeMap.get(type);

    TemplateDataDictionary dataDictionary = TemplateDictionary.create();

    // Get my locator and
    JavaType entity = mirroredType.getName();
    final String entityName = entity.getFullyQualifiedTypeName();
    String metadataIdentificationString = mirroredType.getDeclaredByMetadataId();
    final JavaType idType = persistenceMemberLocator.getIdentifierType(entity);
    Assert.notNull(idType, "Identifier type is not available for entity '" + entityName + "'");

    final MethodParameter entityParameter = new MethodParameter(entity, "proxy");
    ClassOrInterfaceTypeDetails request = gwtTypeService.lookupRequestFromProxy(proxy);

    MemberTypeAdditions persistMethodAdditions =
        layerService.getMemberTypeAdditions(
            metadataIdentificationString,
            CustomDataKeys.PERSIST_METHOD.name(),
            entity,
            idType,
            LAYER_POSITION,
            entityParameter);
    Assert.notNull(
        persistMethodAdditions, "Persist method is not available for entity '" + entityName + "'");
    String persistMethodSignature = getRequestMethodCall(request, persistMethodAdditions);
    dataDictionary.setVariable("persistMethodSignature", persistMethodSignature);

    MemberTypeAdditions removeMethodAdditions =
        layerService.getMemberTypeAdditions(
            metadataIdentificationString,
            CustomDataKeys.REMOVE_METHOD.name(),
            entity,
            idType,
            LAYER_POSITION,
            entityParameter);
    Assert.notNull(
        removeMethodAdditions, "Remove method is not available for entity '" + entityName + "'");
    String removeMethodSignature = getRequestMethodCall(request, removeMethodAdditions);
    dataDictionary.setVariable("removeMethodSignature", removeMethodSignature);

    MemberTypeAdditions countMethodAdditions =
        layerService.getMemberTypeAdditions(
            metadataIdentificationString,
            CustomDataKeys.COUNT_ALL_METHOD.name(),
            entity,
            idType,
            LAYER_POSITION);
    Assert.notNull(
        countMethodAdditions, "Count method is not available for entity '" + entityName + "'");
    dataDictionary.setVariable("countEntitiesMethod", countMethodAdditions.getMethodName());

    for (GwtType reference : type.getReferences()) {
      addReference(dataDictionary, reference, mirrorTypeMap);
    }

    addImport(dataDictionary, proxyType.getFullyQualifiedTypeName());

    String pluralMetadataKey =
        PluralMetadata.createIdentifier(
            mirroredType.getName(),
            PhysicalTypeIdentifier.getPath(mirroredType.getDeclaredByMetadataId()));
    PluralMetadata pluralMetadata = (PluralMetadata) metadataService.get(pluralMetadataKey);
    String plural = pluralMetadata.getPlural();

    final String simpleTypeName = mirroredType.getName().getSimpleTypeName();
    final JavaPackage topLevelPackage = projectOperations.getTopLevelPackage(moduleName);
    dataDictionary.setVariable("className", javaType.getSimpleTypeName());
    dataDictionary.setVariable("packageName", javaType.getPackage().getFullyQualifiedPackageName());
    dataDictionary.setVariable("placePackage", GwtPath.SCAFFOLD_PLACE.packageName(topLevelPackage));
    dataDictionary.setVariable(
        "scaffoldUiPackage", GwtPath.SCAFFOLD_UI.packageName(topLevelPackage));
    dataDictionary.setVariable(
        "sharedScaffoldPackage", GwtPath.SHARED_SCAFFOLD.packageName(topLevelPackage));
    dataDictionary.setVariable("uiPackage", GwtPath.MANAGED_UI.packageName(topLevelPackage));
    dataDictionary.setVariable("name", simpleTypeName);
    dataDictionary.setVariable("pluralName", plural);
    dataDictionary.setVariable("nameUncapitalized", StringUtils.uncapitalize(simpleTypeName));
    dataDictionary.setVariable("proxy", proxyType.getSimpleTypeName());
    dataDictionary.setVariable("pluralName", plural);
    dataDictionary.setVariable(
        "proxyRenderer", GwtProxyProperty.getProxyRendererType(topLevelPackage, proxyType));

    String proxyFields = null;
    GwtProxyProperty primaryProperty = null;
    GwtProxyProperty secondaryProperty = null;
    GwtProxyProperty dateProperty = null;
    Set<String> importSet = new HashSet<String>();

    for (GwtProxyProperty gwtProxyProperty : clientSideTypeMap.values()) {
      // Determine if this is the primary property.
      if (primaryProperty == null) {
        // Choose the first available field.
        primaryProperty = gwtProxyProperty;
      } else if (gwtProxyProperty.isString() && !primaryProperty.isString()) {
        // Favor String properties over other types.
        secondaryProperty = primaryProperty;
        primaryProperty = gwtProxyProperty;
      } else if (secondaryProperty == null) {
        // Choose the next available property.
        secondaryProperty = gwtProxyProperty;
      } else if (gwtProxyProperty.isString() && !secondaryProperty.isString()) {
        // Favor String properties over other types.
        secondaryProperty = gwtProxyProperty;
      }

      // Determine if this is the first date property.
      if (dateProperty == null && gwtProxyProperty.isDate()) {
        dateProperty = gwtProxyProperty;
      }

      if (gwtProxyProperty.isProxy() || gwtProxyProperty.isCollectionOfProxy()) {
        if (proxyFields != null) {
          proxyFields += ", ";
        } else {
          proxyFields = "";
        }
        proxyFields += "\"" + gwtProxyProperty.getName() + "\"";
      }

      dataDictionary.addSection("fields").setVariable("field", gwtProxyProperty.getName());
      if (!isReadOnly(gwtProxyProperty.getName(), mirroredType)) {
        dataDictionary
            .addSection("editViewProps")
            .setVariable("prop", gwtProxyProperty.forEditView());
      }

      TemplateDataDictionary propertiesSection = dataDictionary.addSection("properties");
      propertiesSection.setVariable("prop", gwtProxyProperty.getName());
      propertiesSection.setVariable(
          "propId", proxyType.getSimpleTypeName() + "_" + gwtProxyProperty.getName());
      propertiesSection.setVariable("propGetter", gwtProxyProperty.getGetter());
      propertiesSection.setVariable("propType", gwtProxyProperty.getType());
      propertiesSection.setVariable("propFormatter", gwtProxyProperty.getFormatter());
      propertiesSection.setVariable("propRenderer", gwtProxyProperty.getRenderer());
      propertiesSection.setVariable("propReadable", gwtProxyProperty.getReadableName());

      if (!isReadOnly(gwtProxyProperty.getName(), mirroredType)) {
        TemplateDataDictionary editableSection = dataDictionary.addSection("editableProperties");
        editableSection.setVariable("prop", gwtProxyProperty.getName());
        editableSection.setVariable(
            "propId", proxyType.getSimpleTypeName() + "_" + gwtProxyProperty.getName());
        editableSection.setVariable("propGetter", gwtProxyProperty.getGetter());
        editableSection.setVariable("propType", gwtProxyProperty.getType());
        editableSection.setVariable("propFormatter", gwtProxyProperty.getFormatter());
        editableSection.setVariable("propRenderer", gwtProxyProperty.getRenderer());
        editableSection.setVariable("propBinder", gwtProxyProperty.getBinder());
        editableSection.setVariable("propReadable", gwtProxyProperty.getReadableName());
      }

      dataDictionary.setVariable("proxyRendererType", proxyType.getSimpleTypeName() + "Renderer");

      if (gwtProxyProperty.isProxy()
          || gwtProxyProperty.isEnum()
          || gwtProxyProperty.isCollectionOfProxy()) {
        TemplateDataDictionary section =
            dataDictionary.addSection(
                gwtProxyProperty.isEnum() ? "setEnumValuePickers" : "setProxyValuePickers");
        section.setVariable("setValuePicker", gwtProxyProperty.getSetValuePickerMethod());
        section.setVariable("setValuePickerName", gwtProxyProperty.getSetValuePickerMethodName());
        section.setVariable("valueType", gwtProxyProperty.getValueType().getSimpleTypeName());
        section.setVariable("rendererType", gwtProxyProperty.getProxyRendererType());
        if (gwtProxyProperty.isProxy() || gwtProxyProperty.isCollectionOfProxy()) {
          String propTypeName =
              StringUtils.uncapitalize(
                  gwtProxyProperty.isCollectionOfProxy()
                      ? gwtProxyProperty
                          .getPropertyType()
                          .getParameters()
                          .get(0)
                          .getSimpleTypeName()
                      : gwtProxyProperty.getPropertyType().getSimpleTypeName());
          propTypeName = propTypeName.substring(0, propTypeName.indexOf("Proxy"));
          section.setVariable("requestInterface", propTypeName + "Request");
          section.setVariable(
              "findMethod", "find" + StringUtils.capitalize(propTypeName) + "Entries(0, 50)");
        }
        maybeAddImport(dataDictionary, importSet, gwtProxyProperty.getPropertyType());
        maybeAddImport(dataDictionary, importSet, gwtProxyProperty.getValueType());
        if (gwtProxyProperty.isCollectionOfProxy()) {
          maybeAddImport(
              dataDictionary, importSet, gwtProxyProperty.getPropertyType().getParameters().get(0));
          maybeAddImport(dataDictionary, importSet, gwtProxyProperty.getSetEditorType());
        }
      }
    }

    dataDictionary.setVariable("proxyFields", proxyFields);

    // Add a section for the mobile properties.
    if (primaryProperty != null) {
      dataDictionary.setVariable("primaryProp", primaryProperty.getName());
      dataDictionary.setVariable("primaryPropGetter", primaryProperty.getGetter());
      dataDictionary.setVariable(
          "primaryPropBuilder", primaryProperty.forMobileListView("primaryRenderer"));
      TemplateDataDictionary section = dataDictionary.addSection("mobileProperties");
      section.setVariable("prop", primaryProperty.getName());
      section.setVariable("propGetter", primaryProperty.getGetter());
      section.setVariable("propType", primaryProperty.getType());
      section.setVariable("propRenderer", primaryProperty.getRenderer());
      section.setVariable("propRendererName", "primaryRenderer");
    } else {
      dataDictionary.setVariable("primaryProp", "id");
      dataDictionary.setVariable("primaryPropGetter", "getId");
      dataDictionary.setVariable("primaryPropBuilder", "");
    }
    if (secondaryProperty != null) {
      dataDictionary.setVariable(
          "secondaryPropBuilder", secondaryProperty.forMobileListView("secondaryRenderer"));
      TemplateDataDictionary section = dataDictionary.addSection("mobileProperties");
      section.setVariable("prop", secondaryProperty.getName());
      section.setVariable("propGetter", secondaryProperty.getGetter());
      section.setVariable("propType", secondaryProperty.getType());
      section.setVariable("propRenderer", secondaryProperty.getRenderer());
      section.setVariable("propRendererName", "secondaryRenderer");
    } else {
      dataDictionary.setVariable("secondaryPropBuilder", "");
    }
    if (dateProperty != null) {
      dataDictionary.setVariable("datePropBuilder", dateProperty.forMobileListView("dateRenderer"));
      TemplateDataDictionary section = dataDictionary.addSection("mobileProperties");
      section.setVariable("prop", dateProperty.getName());
      section.setVariable("propGetter", dateProperty.getGetter());
      section.setVariable("propType", dateProperty.getType());
      section.setVariable("propRenderer", dateProperty.getRenderer());
      section.setVariable("propRendererName", "dateRenderer");
    } else {
      dataDictionary.setVariable("datePropBuilder", "");
    }
    return dataDictionary;
  }
 String getSetValuePickerMethodName() {
   return "set" + StringUtils.capitalize(getName()) + "PickerValues";
 }