public void addEnumConstant(
      final String physicalTypeIdentifier, final JavaSymbolName constantName) {
    Assert.hasText(physicalTypeIdentifier, "Type identifier not provided");
    Assert.notNull(constantName, "Constant name required");

    // Obtain the physical type and itd mutable details
    PhysicalTypeMetadata ptm = (PhysicalTypeMetadata) metadataService.get(physicalTypeIdentifier);
    Assert.notNull(
        ptm,
        "Java source code unavailable for type "
            + PhysicalTypeIdentifier.getFriendlyName(physicalTypeIdentifier));
    PhysicalTypeDetails ptd = ptm.getMemberHoldingTypeDetails();
    Assert.notNull(
        ptd,
        "Java source code details unavailable for type "
            + PhysicalTypeIdentifier.getFriendlyName(physicalTypeIdentifier));
    ClassOrInterfaceTypeDetailsBuilder cidBuilder =
        new ClassOrInterfaceTypeDetailsBuilder((ClassOrInterfaceTypeDetails) ptd);

    // Ensure it's an enum
    Assert.isTrue(
        cidBuilder.getPhysicalTypeCategory() == PhysicalTypeCategory.ENUMERATION,
        PhysicalTypeIdentifier.getFriendlyName(physicalTypeIdentifier) + " is not an enum");

    cidBuilder.addEnumConstant(constantName);
    createOrUpdateTypeOnDisk(cidBuilder.build());
  }
  public void addField(final FieldMetadata field) {
    Assert.notNull(field, "Field metadata not provided");

    // Obtain the physical type and ITD mutable details
    PhysicalTypeMetadata ptm =
        (PhysicalTypeMetadata) metadataService.get(field.getDeclaredByMetadataId());
    Assert.notNull(
        ptm,
        "Java source code unavailable for type "
            + PhysicalTypeIdentifier.getFriendlyName(field.getDeclaredByMetadataId()));
    PhysicalTypeDetails ptd = ptm.getMemberHoldingTypeDetails();
    Assert.notNull(
        ptd,
        "Java source code details unavailable for type "
            + PhysicalTypeIdentifier.getFriendlyName(field.getDeclaredByMetadataId()));
    ClassOrInterfaceTypeDetailsBuilder cidBuilder =
        new ClassOrInterfaceTypeDetailsBuilder((ClassOrInterfaceTypeDetails) ptd);

    // Automatically add JSR 303 (Bean Validation API) support if there is
    // no current JSR 303 support but a JSR 303 annotation is present
    boolean jsr303Required = false;
    for (AnnotationMetadata annotation : field.getAnnotations()) {
      if (annotation
          .getAnnotationType()
          .getFullyQualifiedTypeName()
          .startsWith("javax.validation")) {
        jsr303Required = true;
        break;
      }
    }

    LogicalPath path = PhysicalTypeIdentifier.getPath(cidBuilder.getDeclaredByMetadataId());

    if (jsr303Required) {
      // It's more likely the version below represents a later version
      // than any specified in the user's own dependency list
      projectOperations.addDependency(
          path.getModule(), "javax.validation", "validation-api", "1.0.0.GA");
    }
    cidBuilder.addField(field);
    createOrUpdateTypeOnDisk(cidBuilder.build());
  }
  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) {
    final JavaType proxyType = proxy.getName();
    final JavaType javaType = mirrorTypeMap.get(type);

    final TemplateDataDictionary dataDictionary = TemplateDictionary.create();

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

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

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

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

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

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

    addImport(dataDictionary, proxyType.getFullyQualifiedTypeName());

    final String pluralMetadataKey =
        PluralMetadata.createIdentifier(
            mirroredType.getName(),
            PhysicalTypeIdentifier.getPath(mirroredType.getDeclaredByMetadataId()));
    final PluralMetadata pluralMetadata = (PluralMetadata) metadataService.get(pluralMetadataKey);
    final 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;
    final Set<String> importSet = new HashSet<String>();

    for (final 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());
      }

      final 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)) {
        final 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()) {
        final 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"));
      final 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"));
      final 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"));
      final 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;
  }
  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);
  }
 protected ProjectMetadata projectMetadata() {
   return (ProjectMetadata) _metadataService.get(ProjectMetadata.getProjectIdentifier());
 }