private JavaPackage getDestinationPackage(
      final Database database, final Set<ClassOrInterfaceTypeDetails> managedEntities) {
    JavaPackage destinationPackage = database.getDestinationPackage();
    if (destinationPackage == null) {
      if (!managedEntities.isEmpty() && !database.hasMultipleSchemas()) {
        // Take the package of the first one
        destinationPackage = managedEntities.iterator().next().getName().getPackage();
      }
    }

    // Fall back to project's top level package
    if (destinationPackage == null) {
      destinationPackage = projectOperations.getFocusedTopLevelPackage();
    }
    return destinationPackage;
  }
 /**
  * Indicates whether active record CRUD methods should be generated for the given entities (this
  * being an all or nothing decision)
  *
  * @param database the database being reverse-engineered (required)
  * @param managedEntities any existing DB-managed entities in the user project (can be <code>null
  *     </code> or empty)
  * @return see above
  */
 private boolean isActiveRecord(
     final Database database, final Collection<ClassOrInterfaceTypeDetails> managedEntities) {
   if (CollectionUtils.isEmpty(managedEntities)) {
     // There are no existing entities; use the given setting
     return database.isActiveRecord();
   }
   /*
    * There are one or more existing entities; preserve the existing
    * decision, based on the first such entity. This saves the user from
    * having to enter the same value for the "activeRecord" option each
    * time they run the database reverse engineer command.
    */
   return managedEntities.iterator().next().getAnnotation(ROO_JPA_ACTIVE_RECORD) != null;
 }
  private Table updateOrDeleteManagedEntity(
      final ClassOrInterfaceTypeDetails managedEntity, final Database database) {
    // Update the attributes of the existing JPA-related annotation
    final AnnotationMetadata jpaAnnotation = getJpaAnnotation(managedEntity);
    Validate.validState(
        jpaAnnotation != null,
        "Neither @%s nor @%s found on existing DBRE-managed entity %s",
        ROO_JPA_ACTIVE_RECORD.getSimpleTypeName(),
        ROO_JPA_ENTITY.getSimpleTypeName(),
        managedEntity.getName().getFullyQualifiedTypeName());

    // Find table in database using 'table' and 'schema' attributes from the
    // JPA annotation
    final AnnotationAttributeValue<?> tableAttribute =
        jpaAnnotation.getAttribute(new JavaSymbolName("table"));
    final String errMsg =
        "Unable to maintain database-managed entity "
            + managedEntity.getName().getFullyQualifiedTypeName()
            + " because its associated table could not be found";
    Validate.notNull(tableAttribute, errMsg);
    final String tableName = (String) tableAttribute.getValue();
    Validate.notBlank(tableName, errMsg);

    final AnnotationAttributeValue<?> schemaAttribute =
        jpaAnnotation.getAttribute(new JavaSymbolName("schema"));
    final String schemaName = schemaAttribute != null ? (String) schemaAttribute.getValue() : null;

    final Table table = database.getTable(tableName, schemaName);
    if (table == null) {
      // Table is missing and probably has been dropped so delete managed
      // type and its identifier if applicable
      deleteManagedType(managedEntity, "no database table called '" + tableName + "'");
      return null;
    }

    table.setIncludeNonPortableAttributes(database.isIncludeNonPortableAttributes());
    table.setDisableVersionFields(database.isDisableVersionFields());
    table.setDisableGeneratedIdentifiers(database.isDisableGeneratedIdentifiers());

    // Update the @RooJpaEntity/@RooJpaActiveRecord attributes
    final AnnotationMetadataBuilder jpaAnnotationBuilder =
        new AnnotationMetadataBuilder(jpaAnnotation);
    final Set<JavaSymbolName> attributesToDeleteIfPresent = new LinkedHashSet<JavaSymbolName>();
    manageIdentifier(
        managedEntity.getName(), jpaAnnotationBuilder, attributesToDeleteIfPresent, table);

    // Manage versionField attribute
    final AnnotationAttributeValue<?> versionFieldAttribute =
        jpaAnnotation.getAttribute(new JavaSymbolName(VERSION_FIELD));
    if (versionFieldAttribute == null) {
      if (hasVersionField(table)) {
        attributesToDeleteIfPresent.add(new JavaSymbolName(VERSION_FIELD));
      } else {
        jpaAnnotationBuilder.addStringAttribute(VERSION_FIELD, "");
      }
    } else {
      final String versionFieldValue = (String) versionFieldAttribute.getValue();
      if (hasVersionField(table)
          && (StringUtils.isBlank(versionFieldValue) || VERSION.equals(versionFieldValue))) {
        attributesToDeleteIfPresent.add(new JavaSymbolName(VERSION_FIELD));
      }
    }

    final AnnotationAttributeValue<?> sequenceNameFieldAttribute =
        jpaAnnotation.getAttribute(new JavaSymbolName(SEQUENCE_NAME_FIELD));
    if (sequenceNameFieldAttribute == null) {
      if (!table.isDisableGeneratedIdentifiers()) {
        attributesToDeleteIfPresent.add(new JavaSymbolName(SEQUENCE_NAME_FIELD));
      } else {
        jpaAnnotationBuilder.addStringAttribute(SEQUENCE_NAME_FIELD, "");
      }
    } else {
      final String sequenceNameFieldValue = (String) sequenceNameFieldAttribute.getValue();
      if (!table.isDisableGeneratedIdentifiers() && ("".equals(sequenceNameFieldValue))) {
        attributesToDeleteIfPresent.add(new JavaSymbolName(SEQUENCE_NAME_FIELD));
      }
    }

    // Update the annotation on disk
    final ClassOrInterfaceTypeDetailsBuilder cidBuilder =
        new ClassOrInterfaceTypeDetailsBuilder(managedEntity);
    cidBuilder.updateTypeAnnotation(jpaAnnotationBuilder.build(), attributesToDeleteIfPresent);
    typeManagementService.createOrUpdateTypeOnDisk(cidBuilder.build());
    return table;
  }
  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);
  }