private TemplateDataDictionary buildStandardDataDictionary(
     final GwtType type, final String moduleName) {
   final JavaType javaType = new JavaType(getFullyQualifiedTypeName(type, moduleName));
   final TemplateDataDictionary dataDictionary = TemplateDictionary.create();
   for (final GwtType reference : type.getReferences()) {
     addReference(dataDictionary, reference, moduleName);
   }
   dataDictionary.setVariable("className", javaType.getSimpleTypeName());
   dataDictionary.setVariable("packageName", javaType.getPackage().getFullyQualifiedPackageName());
   dataDictionary.setVariable(
       "placePackage",
       GwtPath.SCAFFOLD_PLACE.packageName(projectOperations.getTopLevelPackage(moduleName)));
   dataDictionary.setVariable(
       "sharedScaffoldPackage",
       GwtPath.SHARED_SCAFFOLD.packageName(projectOperations.getTopLevelPackage(moduleName)));
   dataDictionary.setVariable(
       "sharedGaePackage",
       GwtPath.SHARED_GAE.packageName(projectOperations.getTopLevelPackage(moduleName)));
   return dataDictionary;
 }
  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;
  }
  /**
   * Appends the presented class to the end of the presented body declarations. The body
   * declarations appear within the presented compilation unit. This is used to progressively build
   * inner types.
   *
   * @param compilationUnit the work-in-progress compilation unit (required)
   * @param enclosingCompilationUnitServices
   * @param cid the new class to add (required)
   * @param parent the class body declarations a subclass should be added to (may be null, which
   *     denotes a top-level type within the compilation unit)
   */
  private void updateOutput(
      final CompilationUnit compilationUnit,
      CompilationUnitServices enclosingCompilationUnitServices,
      final ClassOrInterfaceTypeDetails cid,
      final List<BodyDeclaration> parent) {
    // Append the new imports this class declares
    Validate.notNull(
        compilationUnit.getImports(),
        "Compilation unit imports should be non-null when producing type '%s'",
        cid.getName());
    for (final ImportMetadata importType : cid.getRegisteredImports()) {
      ImportDeclaration importDeclaration;

      if (!importType.isAsterisk()) {
        NameExpr typeToImportExpr;
        if (importType.getImportType().getEnclosingType() == null) {
          typeToImportExpr =
              new QualifiedNameExpr(
                  new NameExpr(
                      importType.getImportType().getPackage().getFullyQualifiedPackageName()),
                  importType.getImportType().getSimpleTypeName());
        } else {
          typeToImportExpr =
              new QualifiedNameExpr(
                  new NameExpr(
                      importType.getImportType().getEnclosingType().getFullyQualifiedTypeName()),
                  importType.getImportType().getSimpleTypeName());
        }

        importDeclaration = new ImportDeclaration(typeToImportExpr, importType.isStatic(), false);
      } else {
        importDeclaration =
            new ImportDeclaration(
                new NameExpr(importType.getImportPackage().getFullyQualifiedPackageName()),
                importType.isStatic(),
                importType.isAsterisk());
      }

      JavaParserCommentMetadataBuilder.updateCommentsToJavaParser(
          importDeclaration, importType.getCommentStructure());

      compilationUnit.getImports().add(importDeclaration);
    }

    // Create a class or interface declaration to represent this actual type
    final int javaParserModifier = JavaParserUtils.getJavaParserModifier(cid.getModifier());
    TypeDeclaration typeDeclaration;
    ClassOrInterfaceDeclaration classOrInterfaceDeclaration;

    // Implements handling
    final List<ClassOrInterfaceType> implementsList = new ArrayList<ClassOrInterfaceType>();
    for (final JavaType current : cid.getImplementsTypes()) {
      implementsList.add(JavaParserUtils.getResolvedName(cid.getName(), current, compilationUnit));
    }

    if (cid.getPhysicalTypeCategory() == PhysicalTypeCategory.INTERFACE
        || cid.getPhysicalTypeCategory() == PhysicalTypeCategory.CLASS) {
      final boolean isInterface = cid.getPhysicalTypeCategory() == PhysicalTypeCategory.INTERFACE;

      if (parent == null) {
        // Top level type
        typeDeclaration =
            new ClassOrInterfaceDeclaration(
                javaParserModifier,
                isInterface,
                cid.getName()
                    .getNameIncludingTypeParameters()
                    .replace(cid.getName().getPackage().getFullyQualifiedPackageName() + ".", ""));
        classOrInterfaceDeclaration = (ClassOrInterfaceDeclaration) typeDeclaration;
      } else {
        // Inner type
        typeDeclaration =
            new ClassOrInterfaceDeclaration(
                javaParserModifier, isInterface, cid.getName().getSimpleTypeName());
        classOrInterfaceDeclaration = (ClassOrInterfaceDeclaration) typeDeclaration;

        if (cid.getName().getParameters().size() > 0) {
          classOrInterfaceDeclaration.setTypeParameters(new ArrayList<TypeParameter>());

          for (final JavaType param : cid.getName().getParameters()) {
            NameExpr pNameExpr =
                JavaParserUtils.importTypeIfRequired(
                    cid.getName(), compilationUnit.getImports(), param);
            final String tempName =
                StringUtils.replace(pNameExpr.toString(), param.getArgName() + " extends ", "", 1);
            pNameExpr = new NameExpr(tempName);
            final ClassOrInterfaceType pResolvedName =
                JavaParserUtils.getClassOrInterfaceType(pNameExpr);
            classOrInterfaceDeclaration
                .getTypeParameters()
                .add(
                    new TypeParameter(
                        param.getArgName().getSymbolName(),
                        Collections.singletonList(pResolvedName)));
          }
        }
      }

      // Superclass handling
      final List<ClassOrInterfaceType> extendsList = new ArrayList<ClassOrInterfaceType>();
      for (final JavaType current : cid.getExtendsTypes()) {
        if (!OBJECT.equals(current)) {
          extendsList.add(JavaParserUtils.getResolvedName(cid.getName(), current, compilationUnit));
        }
      }
      if (extendsList.size() > 0) {
        classOrInterfaceDeclaration.setExtends(extendsList);
      }

      // Implements handling
      if (implementsList.size() > 0) {
        classOrInterfaceDeclaration.setImplements(implementsList);
      }
    } else {
      typeDeclaration = new EnumDeclaration(javaParserModifier, cid.getName().getSimpleTypeName());
    }
    typeDeclaration.setMembers(new ArrayList<BodyDeclaration>());

    Validate.notNull(
        typeDeclaration.getName(), "Missing type declaration name for '%s'", cid.getName());

    // If adding a new top-level type, must add it to the compilation unit
    // types
    Validate.notNull(
        compilationUnit.getTypes(),
        "Compilation unit types must not be null when attempting to add '%s'",
        cid.getName());

    if (parent == null) {
      // Top-level class
      compilationUnit.getTypes().add(typeDeclaration);
    } else {
      // Inner class
      parent.add(typeDeclaration);
    }

    // If the enclosing CompilationUnitServices was not provided a default
    // CompilationUnitServices needs to be created
    if (enclosingCompilationUnitServices == null) {
      // Create a compilation unit so that we can use JavaType*Metadata
      // static methods directly
      enclosingCompilationUnitServices =
          new CompilationUnitServices() {
            @Override
            public JavaPackage getCompilationUnitPackage() {
              return cid.getName().getPackage();
            }

            @Override
            public JavaType getEnclosingTypeName() {
              return cid.getName();
            }

            @Override
            public List<ImportDeclaration> getImports() {
              return compilationUnit.getImports();
            }

            @Override
            public List<TypeDeclaration> getInnerTypes() {
              return compilationUnit.getTypes();
            }

            @Override
            public PhysicalTypeCategory getPhysicalTypeCategory() {
              return cid.getPhysicalTypeCategory();
            }
          };
    }

    final CompilationUnitServices finalCompilationUnitServices = enclosingCompilationUnitServices;
    // A hybrid CompilationUnitServices must be provided that references the
    // enclosing types imports and package
    final CompilationUnitServices compilationUnitServices =
        new CompilationUnitServices() {
          @Override
          public JavaPackage getCompilationUnitPackage() {
            return finalCompilationUnitServices.getCompilationUnitPackage();
          }

          @Override
          public JavaType getEnclosingTypeName() {
            return cid.getName();
          }

          @Override
          public List<ImportDeclaration> getImports() {
            return finalCompilationUnitServices.getImports();
          }

          @Override
          public List<TypeDeclaration> getInnerTypes() {
            return compilationUnit.getTypes();
          }

          @Override
          public PhysicalTypeCategory getPhysicalTypeCategory() {
            return cid.getPhysicalTypeCategory();
          }
        };

    // Add type annotations
    final List<AnnotationExpr> annotations = new ArrayList<AnnotationExpr>();
    typeDeclaration.setAnnotations(annotations);
    for (final AnnotationMetadata candidate : cid.getAnnotations()) {
      JavaParserAnnotationMetadataBuilder.addAnnotationToList(
          compilationUnitServices, annotations, candidate);
    }

    // ROO-3834: Generating default Javadoc inside class if class doesn't contains JavaDoc
    List<Comment> classComments = compilationUnit.getComments();
    if (classComments == null || classComments.isEmpty()) {
      CommentStructure defaultCommentStructure = new CommentStructure();

      String defaultComment =
          "/**\n * = "
              .concat(cid.getType().getSimpleTypeName())
              .concat("\n *\n * TODO Auto-generated class documentation\n *\n */\n");

      defaultCommentStructure.addComment(
          new JavadocComment(defaultComment), CommentLocation.BEGINNING);

      if (annotations.isEmpty()) {
        JavaParserCommentMetadataBuilder.updateCommentsToJavaParser(
            typeDeclaration, defaultCommentStructure);
      } else {
        // If exists some annotation, include comment before the existing annotations
        AnnotationExpr firstAnnotation = annotations.get(0);
        JavaParserCommentMetadataBuilder.updateCommentsToJavaParser(
            firstAnnotation, defaultCommentStructure);
      }
    }

    // Add enum constants and interfaces
    if (typeDeclaration instanceof EnumDeclaration && cid.getEnumConstants().size() > 0) {
      final EnumDeclaration enumDeclaration = (EnumDeclaration) typeDeclaration;

      final List<EnumConstantDeclaration> constants = new ArrayList<EnumConstantDeclaration>();
      enumDeclaration.setEntries(constants);

      for (final JavaSymbolName constant : cid.getEnumConstants()) {
        addEnumConstant(constants, constant);
      }

      // Implements handling
      if (implementsList.size() > 0) {
        enumDeclaration.setImplements(implementsList);
      }
    }

    // Add fields
    for (final FieldMetadata candidate : cid.getDeclaredFields()) {
      JavaParserFieldMetadataBuilder.addField(
          compilationUnitServices, typeDeclaration.getMembers(), candidate);
    }

    // Add constructors
    for (final ConstructorMetadata candidate : cid.getDeclaredConstructors()) {
      JavaParserConstructorMetadataBuilder.addConstructor(
          compilationUnitServices, typeDeclaration.getMembers(), candidate, null);
    }

    // Add methods
    for (final MethodMetadata candidate : cid.getDeclaredMethods()) {
      JavaParserMethodMetadataBuilder.addMethod(
          compilationUnitServices, typeDeclaration.getMembers(), candidate, null);
    }

    // Add inner types
    for (final ClassOrInterfaceTypeDetails candidate : cid.getDeclaredInnerTypes()) {
      updateOutput(
          compilationUnit, compilationUnitServices, candidate, typeDeclaration.getMembers());
    }

    final HashSet<String> imported = new HashSet<String>();
    final ArrayList<ImportDeclaration> imports = new ArrayList<ImportDeclaration>();
    for (final ImportDeclaration importDeclaration : compilationUnit.getImports()) {
      JavaPackage importPackage = null;
      JavaType importType = null;
      if (importDeclaration.isAsterisk()) {
        importPackage = new JavaPackage(importDeclaration.getName().toString());
      } else {
        importType = new JavaType(importDeclaration.getName().toString());
        importPackage = importType.getPackage();
      }

      if (importPackage.equals(cid.getName().getPackage()) && importDeclaration.isAsterisk()) {
        continue;
      }

      if (importPackage.equals(cid.getName().getPackage())
          && importType != null
          && importType.getEnclosingType() == null) {
        continue;
      }

      if (importType != null && importType.equals(cid.getName())) {
        continue;
      }

      if (!imported.contains(importDeclaration.getName().toString())) {
        imports.add(importDeclaration);
        imported.add(importDeclaration.getName().toString());
      }
    }

    Collections.sort(
        imports,
        new Comparator<ImportDeclaration>() {
          @Override
          public int compare(
              final ImportDeclaration importDeclaration,
              final ImportDeclaration importDeclaration1) {
            return importDeclaration
                .getName()
                .toString()
                .compareTo(importDeclaration1.getName().toString());
          }
        });

    compilationUnit.setImports(imports);
  }