/** {@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());
  }
  @Override
  public void systemService(
      final JavaType type,
      JavaSymbolName fieldName,
      final SystemService service,
      final boolean addPermissions) {

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

    if (fieldName == null) {
      fieldName =
          new JavaSymbolName(
              StringUtils.uncapitalize(service.getServiceType().getSimpleTypeName()));
    }

    final String physicalTypeIdentifier = typeDetails.getDeclaredByMetadataId();

    final List<AnnotationMetadataBuilder> annotations =
        Arrays.asList(new AnnotationMetadataBuilder(ROO_SYSTEM_SERVICE));

    final FieldMetadataBuilder fieldBuilder =
        new FieldMetadataBuilder(
            physicalTypeIdentifier, 0, annotations, fieldName, service.getServiceType());
    typeManagementService.addField(fieldBuilder.build());

    if (addPermissions) {
      final String moduleName = projectOperations.getFocusedModuleName();
      for (Permission permission : service.getPermissions()) {
        androidTypeService.addPermission(moduleName, permission.permissionName());
      }
    }
  }
  @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());
  }
  @Override
  public boolean isValid(JavaType javaType) {
    ClassOrInterfaceTypeDetails cid = typeLocationService.getTypeDetails(javaType);
    if (cid.getAnnotation(RooJavaType.ROO_DTO) != null) {
      return true;
    }

    return false;
  }
  @Override
  public void resourceString(
      final JavaType type, final String name, final JavaSymbolName fieldName, final String value) {

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

    final DocumentBuilder builder = newDocumentBuilder();
    final String valuesPath =
        pathResolver.getFocusedIdentifier(Path.ROOT, VALUES_PATH + SEP + STRINGS + XML_EXTENSION);

    InputStream inputStream = null;
    Document document = null;
    try {
      inputStream = fileManager.getInputStream(valuesPath);
      document = builder.parse(inputStream);
    } catch (final Exception e) {
      LOGGER.severe("Error reading resource XML: " + e.getMessage());
    } finally {
      IOUtils.closeQuietly(inputStream);
    }

    if (document != null) {
      final Element root = document.getDocumentElement();

      final Element stringElem = XmlUtils.createTextElement(document, "string", value);
      final String id = StringUtils.isEmpty(name) ? fieldName.getSymbolName() : name;
      stringElem.setAttribute("name", id);
      root.appendChild(stringElem);

      fileManager.createOrUpdateTextFileIfRequired(
          valuesPath, XmlUtils.nodeToString(document), true);
    }

    final String physicalTypeIdentifier = typeDetails.getDeclaredByMetadataId();

    final List<AnnotationMetadataBuilder> annotations = new ArrayList<AnnotationMetadataBuilder>();
    final AnnotationMetadataBuilder annotationBuilder = new AnnotationMetadataBuilder(ROO_STRING);
    if (!StringUtils.isEmpty(name)) {
      annotationBuilder.addStringAttribute("value", name);
    }
    annotations.add(annotationBuilder);

    final FieldMetadataBuilder fieldBuilder =
        new FieldMetadataBuilder(physicalTypeIdentifier, 0, annotations, fieldName, STRING);
    typeManagementService.addField(fieldBuilder.build());
  }
  @Override
  public void view(
      final JavaType type,
      final String viewName,
      final String identifier,
      final JavaSymbolName fieldName,
      final Dimension height,
      final Dimension width) {

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

    final JavaType viewType =
        new JavaType(viewName.contains(".") ? viewName : WIDGET_PACKAGE + "." + viewName);

    final String layout =
        RequestFactoryUtils.getStringAnnotationValue(typeDetails, ROO_ACTIVITY, "value", "");
    if (!StringUtils.isEmpty(layout)) {
      final DocumentBuilder builder = newDocumentBuilder();
      final String layoutPath =
          pathResolver.getFocusedIdentifier(Path.ROOT, LAYOUT_PATH + SEP + layout + XML_EXTENSION);

      InputStream inputStream = null;
      Document document = null;
      try {
        inputStream = fileManager.getInputStream(layoutPath);
        document = builder.parse(inputStream);
      } catch (final Exception e) {
        LOGGER.severe("Error reading layout XML: " + e.getMessage());
      } finally {
        IOUtils.closeQuietly(inputStream);
      }

      if (document != null) {
        final Element root = document.getDocumentElement();

        final Element viewElem = document.createElement(viewType.getSimpleTypeName());
        final String id = StringUtils.isEmpty(identifier) ? fieldName.getSymbolName() : identifier;
        viewElem.setAttribute("android:id", ID_PREFIX + id);
        viewElem.setAttribute("android:layout_height", height.value());
        viewElem.setAttribute("android:layout_width", width.value());
        root.appendChild(viewElem);

        fileManager.createOrUpdateTextFileIfRequired(
            layoutPath, XmlUtils.nodeToString(document), true);
      }
    }

    final String physicalTypeIdentifier = typeDetails.getDeclaredByMetadataId();

    final List<AnnotationMetadataBuilder> annotations = new ArrayList<AnnotationMetadataBuilder>();
    final AnnotationMetadataBuilder annotationBuilder = new AnnotationMetadataBuilder(ROO_VIEW);
    if (!StringUtils.isEmpty(identifier)) {
      annotationBuilder.addStringAttribute("value", identifier);
    }
    annotations.add(annotationBuilder);

    final FieldMetadataBuilder fieldBuilder =
        new FieldMetadataBuilder(physicalTypeIdentifier, 0, annotations, fieldName, viewType);
    typeManagementService.addField(fieldBuilder.build());
  }
  private void reverseEngineer(final Database database) {
    final Set<ClassOrInterfaceTypeDetails> managedEntities =
        typeLocationService.findClassesOrInterfaceDetailsWithAnnotation(ROO_DB_MANAGED);
    // Determine whether to create "active record" CRUD methods
    database.setActiveRecord(isActiveRecord(database, managedEntities));

    // Lookup the relevant destination package if not explicitly given
    final JavaPackage destinationPackage = getDestinationPackage(database, managedEntities);

    // Set the destination package in the database
    database.setDestinationPackage(destinationPackage);

    // Get tables from database
    final Set<Table> tables = new LinkedHashSet<Table>(database.getTables());

    // Manage existing entities with @RooDbManaged annotation
    for (final ClassOrInterfaceTypeDetails managedEntity : managedEntities) {
      // Remove table from set as each managed entity is processed.
      // The tables that remain in the set will be used for creation of
      // new entities later
      final Table table = updateOrDeleteManagedEntity(managedEntity, database);
      if (table != null) {
        tables.remove(table);
      }
    }

    // Create new entities from tables
    final List<ClassOrInterfaceTypeDetails> newEntities =
        new ArrayList<ClassOrInterfaceTypeDetails>();
    for (final Table table : tables) {
      // Don't create types from join tables in many-to-many associations
      if (!table.isJoinTable()) {
        JavaPackage schemaPackage = destinationPackage;
        if (database.hasMultipleSchemas()) {
          schemaPackage =
              new JavaPackage(
                  destinationPackage.getFullyQualifiedPackageName()
                      + "."
                      + DbreTypeUtils.suggestPackageName(table.getSchema().getName()));
        }
        final JavaType javaType =
            DbreTypeUtils.suggestTypeNameForNewTable(table.getName(), schemaPackage);
        final boolean activeRecord = database.isActiveRecord() && !database.isRepository();
        if (typeLocationService.getTypeDetails(javaType) == null) {
          table.setIncludeNonPortableAttributes(database.isIncludeNonPortableAttributes());
          table.setDisableVersionFields(database.isDisableVersionFields());
          table.setDisableGeneratedIdentifiers(database.isDisableGeneratedIdentifiers());
          newEntities.add(createNewManagedEntityFromTable(javaType, table, activeRecord));
        }
      }
    }

    // Create repositories if required
    if (database.isRepository()) {
      for (final ClassOrInterfaceTypeDetails entity : newEntities) {
        final JavaType type = entity.getType();
        repositoryJpaOperations.setupRepository(
            new JavaType(type.getFullyQualifiedTypeName() + "Repository"), type);
      }
    }

    // Create services if required
    if (database.isService()) {
      for (final ClassOrInterfaceTypeDetails entity : newEntities) {
        final JavaType type = entity.getType();
        final String typeName = type.getFullyQualifiedTypeName();
        serviceOperations.setupService(
            new JavaType(typeName + "Service"),
            new JavaType(typeName + "ServiceImpl"),
            type,
            false,
            "",
            false,
            false);
      }
    }

    // Create integration tests if required
    if (database.isTestAutomatically()) {
      for (final ClassOrInterfaceTypeDetails entity : newEntities) {
        integrationTestOperations.newIntegrationTest(entity.getType());
      }
    }

    // Notify
    final List<ClassOrInterfaceTypeDetails> allEntities =
        new ArrayList<ClassOrInterfaceTypeDetails>();
    allEntities.addAll(newEntities);
    allEntities.addAll(managedEntities);
    notify(allEntities);
  }