@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());
  }
  private void createIdentifierClass(final JavaType identifierType) {
    final List<AnnotationMetadataBuilder> identifierAnnotations =
        new ArrayList<AnnotationMetadataBuilder>();

    final AnnotationMetadataBuilder identifierBuilder =
        new AnnotationMetadataBuilder(ROO_IDENTIFIER);
    identifierBuilder.addBooleanAttribute(DB_MANAGED.getSymbolName(), true);
    identifierAnnotations.add(identifierBuilder);

    // Produce identifier itself
    final String declaredByMetadataId =
        PhysicalTypeIdentifier.createIdentifier(
            identifierType, projectOperations.getPathResolver().getFocusedPath(Path.SRC_MAIN_JAVA));
    final ClassOrInterfaceTypeDetailsBuilder cidBuilder =
        new ClassOrInterfaceTypeDetailsBuilder(
            declaredByMetadataId,
            Modifier.PUBLIC | Modifier.FINAL,
            identifierType,
            PhysicalTypeCategory.CLASS);
    cidBuilder.setAnnotations(identifierAnnotations);
    typeManagementService.createOrUpdateTypeOnDisk(cidBuilder.build());

    shell.flash(
        Level.FINE,
        "Created " + identifierType.getFullyQualifiedTypeName(),
        DbreDatabaseListenerImpl.class.getName());
    shell.flash(Level.FINE, "", DbreDatabaseListenerImpl.class.getName());
  }
 public final List<AnnotationMetadata> buildAnnotations() {
   List<AnnotationMetadata> result = new ArrayList<AnnotationMetadata>();
   for (AnnotationMetadataBuilder builder : annotations) {
     result.add(builder.build());
   }
   return result;
 }
 public void removeAnnotation(JavaType annotationType) {
   for (AnnotationMetadataBuilder annotationMetadataBuilder : annotations) {
     if (annotationMetadataBuilder.getAnnotationType().equals(annotationType)) {
       annotations.remove(annotationMetadataBuilder);
       break;
     }
   }
 }
  /**
   * Locates the entity manager field that should be used.
   *
   * <p>If a parent is defined, it must provide the field.
   *
   * <p>We generally expect the field to be named "entityManager" and be of type
   * javax.persistence.EntityManager. We also require it to be public or protected, and annotated
   * with @PersistenceContext. If there is an existing field which doesn't meet these latter
   * requirements, we add an underscore prefix to the "entityManager" name and try again, until such
   * time as we come up with a unique name that either meets the requirements or the name is not
   * used and we will create it.
   *
   * @return the entity manager field (never returns null)
   */
  public FieldMetadata getEntityManagerField() {
    if (parent != null) {
      // The parent is required to guarantee this is available
      return parent.getEntityManagerField();
    }

    // Need to locate it ourself
    int index = -1;
    while (true) {
      // Compute the required field name
      index++;
      final JavaSymbolName fieldSymbolName =
          new JavaSymbolName(StringUtils.repeat("_", index) + "entityManager");
      final FieldMetadata candidate = governorTypeDetails.getField(fieldSymbolName);
      if (candidate != null) {
        // Verify if candidate is suitable

        if (!Modifier.isPublic(candidate.getModifier())
            && !Modifier.isProtected(candidate.getModifier())
            && (Modifier.TRANSIENT != candidate.getModifier())) {
          // Candidate is not public and not protected and not simply a transient field (in which
          // case subclasses
          // will see the inherited field), so any subsequent subclasses won't be able to see it.
          // Give up!
          continue;
        }

        if (!candidate.getFieldType().equals(ENTITY_MANAGER)) {
          // Candidate isn't an EntityManager, so give up
          continue;
        }

        if (MemberFindingUtils.getAnnotationOfType(candidate.getAnnotations(), PERSISTENCE_CONTEXT)
            == null) {
          // Candidate doesn't have a PersistenceContext annotation, so give up
          continue;
        }

        // If we got this far, we found a valid candidate
        return candidate;
      }

      // Candidate not found, so let's create one
      final List<AnnotationMetadataBuilder> annotations =
          new ArrayList<AnnotationMetadataBuilder>();
      final AnnotationMetadataBuilder annotationBuilder =
          new AnnotationMetadataBuilder(PERSISTENCE_CONTEXT);
      if (StringUtils.hasText(crudAnnotationValues.getPersistenceUnit())) {
        annotationBuilder.addStringAttribute("unitName", crudAnnotationValues.getPersistenceUnit());
      }
      annotations.add(annotationBuilder);

      final FieldMetadataBuilder fieldBuilder =
          new FieldMetadataBuilder(
              getId(), Modifier.TRANSIENT, annotations, fieldSymbolName, ENTITY_MANAGER);
      return fieldBuilder.build();
    }
  }
  /** {@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());
  }
  /**
   * Creates a new DBRE-managed entity from the given table
   *
   * @param javaType the name of the entity to be created (required)
   * @param table the table from which to create the entity (required)
   * @param activeRecord whether to create "active record" CRUD methods in the new entity
   * @return the newly created entity
   */
  private ClassOrInterfaceTypeDetails createNewManagedEntityFromTable(
      final JavaType javaType, final Table table, final boolean activeRecord) {
    // Create type annotations for new entity
    final List<AnnotationMetadataBuilder> annotations = new ArrayList<AnnotationMetadataBuilder>();
    annotations.add(new AnnotationMetadataBuilder(ROO_JAVA_BEAN));
    annotations.add(new AnnotationMetadataBuilder(ROO_TO_STRING));

    // Find primary key from db metadata and add identifier attributes to
    // @RooJpaEntity
    final AnnotationMetadataBuilder jpaAnnotationBuilder =
        new AnnotationMetadataBuilder(activeRecord ? ROO_JPA_ACTIVE_RECORD : ROO_JPA_ENTITY);
    manageIdentifier(javaType, jpaAnnotationBuilder, new HashSet<JavaSymbolName>(), table);

    if (!hasVersionField(table)) {
      jpaAnnotationBuilder.addStringAttribute(VERSION_FIELD, "");
    }
    if (table.isDisableGeneratedIdentifiers()) {
      jpaAnnotationBuilder.addStringAttribute(SEQUENCE_NAME_FIELD, "");
    }

    jpaAnnotationBuilder.addStringAttribute("table", table.getName());
    if (!DbreModelService.NO_SCHEMA_REQUIRED.equals(table.getSchema().getName())) {
      jpaAnnotationBuilder.addStringAttribute("schema", table.getSchema().getName());
    }

    annotations.add(jpaAnnotationBuilder);

    // Add @RooDbManaged
    annotations.add(getRooDbManagedAnnotation());

    final JavaType superclass = OBJECT;
    final List<JavaType> extendsTypes = new ArrayList<JavaType>();
    extendsTypes.add(superclass);

    // Create entity class
    final String declaredByMetadataId =
        PhysicalTypeIdentifier.createIdentifier(
            javaType, projectOperations.getPathResolver().getFocusedPath(Path.SRC_MAIN_JAVA));
    final ClassOrInterfaceTypeDetailsBuilder cidBuilder =
        new ClassOrInterfaceTypeDetailsBuilder(
            declaredByMetadataId, Modifier.PUBLIC, javaType, PhysicalTypeCategory.CLASS);
    cidBuilder.setExtendsTypes(extendsTypes);
    cidBuilder.setAnnotations(annotations);

    final ClassOrInterfaceTypeDetails entity = cidBuilder.build();
    typeManagementService.createOrUpdateTypeOnDisk(entity);

    shell.flash(
        Level.FINE,
        "Created " + javaType.getFullyQualifiedTypeName(),
        DbreDatabaseListenerImpl.class.getName());
    shell.flash(Level.FINE, "", DbreDatabaseListenerImpl.class.getName());

    return entity;
  }
 private void addTransactionalAnnotation(
     final List<AnnotationMetadataBuilder> annotations, final boolean isPersistMethod) {
   final AnnotationMetadataBuilder transactionalBuilder =
       new AnnotationMetadataBuilder(TRANSACTIONAL);
   if (StringUtils.hasText(crudAnnotationValues.getTransactionManager())) {
     transactionalBuilder.addStringAttribute(
         "value", crudAnnotationValues.getTransactionManager());
   }
   if (isGaeEnabled && isPersistMethod) {
     transactionalBuilder.addEnumAttribute(
         "propagation", new EnumDetails(PROPAGATION, new JavaSymbolName("REQUIRES_NEW")));
   }
   annotations.add(transactionalBuilder);
 }
  @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());
  }
  @Test
  public void testSimpleClassAddAnnotation() throws Exception {

    // Set up
    final File file = getResource(SIMPLE_CLASS_FILE_PATH);
    final String fileContents = getResourceContents(file);

    final ClassOrInterfaceTypeDetails simpleInterfaceDetails =
        typeParsingService.getTypeFromString(
            fileContents, SIMPLE_CLASS_DECLARED_BY_MID, SIMPLE_CLASS_TYPE);

    final AnnotationMetadataBuilder annotationBuilder =
        new AnnotationMetadataBuilder(
            new JavaType("org.springframework.roo.addon.tostring.RooToString"));
    final ClassOrInterfaceTypeDetails newSimpleInterfaceDetails =
        addAnnotation(simpleInterfaceDetails, annotationBuilder.build());

    // Invoke
    final String result =
        typeParsingService.updateAndGetCompilationUnitContents(
            file.getCanonicalPath(), newSimpleInterfaceDetails);

    saveResult(file, result, "-addedAnnotation");

    checkSimpleClass(result);

    assertTrue(result.contains("import org.springframework.roo.addon.tostring.RooToString;"));
    assertTrue(result.contains("@RooToString"));

    // Invoke again
    final ClassOrInterfaceTypeDetails simpleInterfaceDetails2 =
        typeParsingService.getTypeFromString(
            result, SIMPLE_CLASS_DECLARED_BY_MID, SIMPLE_CLASS_TYPE);

    final String result2 =
        typeParsingService.updateAndGetCompilationUnitContents(
            file.getCanonicalPath(), simpleInterfaceDetails2);

    saveResult(file, result2, "-addedAnnotation2");

    checkSimpleClass(result2);

    assertTrue(result2.contains("import org.springframework.roo.addon.tostring.RooToString;"));
    assertTrue(result2.contains("@RooToString"));
  }
  @Override
  public void activity(
      final JavaType name,
      final String layout,
      final boolean main,
      final boolean noTitle,
      final boolean fullscreen) {
    if (noTitle) {
      Validate.isTrue(fullscreen == false, "Options 'noTitle' and 'fullscreen' are mutex");
    }
    if (fullscreen) {
      Validate.isTrue(noTitle == false, "Options 'noTitle' and 'fullscreen' are mutex");
    }

    if (!StringUtils.isEmpty(layout)) {
      final String layoutPath =
          pathResolver.getFocusedIdentifier(Path.ROOT, LAYOUT_PATH + SEP + layout + XML_EXTENSION);
      if (!fileManager.exists(layoutPath)) {
        LOGGER.info("Layout '" + layout + "' does not exist");
        layout(layout, Dimension.FILL_PARENT, Dimension.FILL_PARENT, Orientation.VERTICAL);
      }
    }

    final List<AnnotationMetadataBuilder> annotations = new ArrayList<AnnotationMetadataBuilder>();
    final AnnotationMetadataBuilder activityAnnotationBuilder =
        new AnnotationMetadataBuilder(ROO_ACTIVITY);
    if (!StringUtils.isEmpty(layout)) {
      activityAnnotationBuilder.addStringAttribute("value", layout);
    }
    if (noTitle) {
      activityAnnotationBuilder.addBooleanAttribute(RooActivity.NO_TITLE_ATTRIBUTE, noTitle);
    }
    if (fullscreen) {
      activityAnnotationBuilder.addBooleanAttribute(RooActivity.FULLSCREEN_ATTRIBUTE, fullscreen);
    }
    annotations.add(activityAnnotationBuilder);

    jpaOperations.newEntity(name, false, ANDROID_ACTIVITY, annotations);

    androidTypeService.addActvity(
        projectOperations.getFocusedModuleName(), name.getFullyQualifiedTypeName(), main);
  }
  public void setupService(
      final JavaType interfaceType, final JavaType classType, final JavaType domainType) {
    Assert.notNull(interfaceType, "Interface type required");
    Assert.notNull(classType, "Class type required");
    Assert.notNull(domainType, "Domain type required");

    String interfaceIdentifier =
        pathResolver.getFocusedCanonicalPath(Path.SRC_MAIN_JAVA, interfaceType);
    String classIdentifier = pathResolver.getFocusedCanonicalPath(Path.SRC_MAIN_JAVA, classType);

    if (fileManager.exists(interfaceIdentifier) || fileManager.exists(classIdentifier)) {
      return; // Type exists already - nothing to do
    }

    // First build interface type
    AnnotationMetadataBuilder interfaceAnnotationMetadata =
        new AnnotationMetadataBuilder(ROO_SERVICE);
    interfaceAnnotationMetadata.addAttribute(
        new ArrayAttributeValue<ClassAttributeValue>(
            new JavaSymbolName("domainTypes"),
            Arrays.asList(new ClassAttributeValue(new JavaSymbolName("foo"), domainType))));
    String interfaceMdId =
        PhysicalTypeIdentifier.createIdentifier(
            interfaceType, pathResolver.getPath(interfaceIdentifier));
    ClassOrInterfaceTypeDetailsBuilder interfaceTypeBuilder =
        new ClassOrInterfaceTypeDetailsBuilder(
            interfaceMdId, Modifier.PUBLIC, interfaceType, PhysicalTypeCategory.INTERFACE);
    interfaceTypeBuilder.addAnnotation(interfaceAnnotationMetadata.build());
    typeManagementService.createOrUpdateTypeOnDisk(interfaceTypeBuilder.build());

    // Second build the implementing class
    String classMdId =
        PhysicalTypeIdentifier.createIdentifier(classType, pathResolver.getPath(classIdentifier));
    ClassOrInterfaceTypeDetailsBuilder classTypeBuilder =
        new ClassOrInterfaceTypeDetailsBuilder(
            classMdId, Modifier.PUBLIC, classType, PhysicalTypeCategory.CLASS);
    classTypeBuilder.addImplementsType(interfaceType);
    typeManagementService.createOrUpdateTypeOnDisk(classTypeBuilder.build());
  }
  @Override
  public void fragment(final JavaType name, final String layout, final boolean support) {

    if (!StringUtils.isEmpty(layout)) {
      final String layoutPath =
          pathResolver.getFocusedIdentifier(Path.ROOT, LAYOUT_PATH + SEP + layout + XML_EXTENSION);
      if (!fileManager.exists(layoutPath)) {
        LOGGER.info("Layout '" + layout + "' does not exist");
        layout(layout, Dimension.FILL_PARENT, Dimension.FILL_PARENT, Orientation.VERTICAL);
      }
    }

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

    jpaOperations.newEntity(
        name, false, support ? ANDROID_SUPPORT_FRAGMENT : ANDROID_FRAGMENT, annotations);
  }
  private void manageIdentifier(
      final JavaType javaType,
      final AnnotationMetadataBuilder jpaAnnotationBuilder,
      final Set<JavaSymbolName> attributesToDeleteIfPresent,
      final Table table) {
    final JavaType identifierType = getIdentifierType(javaType);
    final PhysicalTypeMetadata identifierPhysicalTypeMetadata =
        getPhysicalTypeMetadata(identifierType);

    // Process primary keys and add 'identifierType' attribute
    final int pkCount = table.getPrimaryKeyCount();
    if (pkCount == 1) {
      // Table has one primary key
      // Check for redundant, managed identifier class and delete if found
      if (isIdentifierDeletable(identifierType)) {
        deleteJavaType(
            identifierType, "the " + table.getName() + " table has only one primary key");
      }

      attributesToDeleteIfPresent.add(new JavaSymbolName(IDENTIFIER_TYPE));

      // We don't need a PK class, so we just tell the
      // JpaActiveRecordProvider via IdentifierService the column name,
      // field type and field name to use
      final List<Identifier> identifiers = getIdentifiersFromPrimaryKeys(table);
      identifierResults.put(javaType, identifiers);
    } else if (pkCount == 0 || pkCount > 1) {
      // Table has either no primary keys or more than one primary key so
      // create a composite key

      // Check if identifier class already exists and if not, create it
      if (identifierPhysicalTypeMetadata == null
          || !identifierPhysicalTypeMetadata.isValid()
          || identifierPhysicalTypeMetadata.getMemberHoldingTypeDetails() == null) {
        createIdentifierClass(identifierType);
      }

      jpaAnnotationBuilder.addClassAttribute(IDENTIFIER_TYPE, identifierType);

      // We need a PK class, so we tell the IdentifierMetadataProvider via
      // IdentifierService the various column names, field types and field
      // names to use
      // For tables with no primary keys, create a composite key using all
      // the table's columns
      final List<Identifier> identifiers =
          pkCount == 0 ? getIdentifiersFromColumns(table) : getIdentifiersFromPrimaryKeys(table);
      identifierResults.put(identifierType, identifiers);
    }
  }
  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;
  }
  public MetadataItem get(String metadataIdentificationString) {
    ProjectMetadata projectMetadata = projectOperations.getProjectMetadata();
    if (projectMetadata == null) {
      return null;
    }

    ClassOrInterfaceTypeDetails proxy = getGovernor(metadataIdentificationString);
    if (proxy == null) {
      return null;
    }

    AnnotationMetadata proxyAnnotation =
        GwtUtils.getFirstAnnotation(proxy, GwtUtils.PROXY_ANNOTATIONS);
    if (proxyAnnotation == null) {
      return null;
    }

    String locatorType = GwtUtils.getStringValue(proxyAnnotation.getAttribute("locator"));
    if (!StringUtils.hasText(locatorType)) {
      return null;
    }

    ClassOrInterfaceTypeDetails entity = gwtTypeService.lookupEntityFromProxy(proxy);
    if (entity == null) {
      return null;
    }

    MethodMetadata idAccessor = persistenceMemberLocator.getIdentifierAccessor(entity.getName());
    MethodMetadata versionAccessor = persistenceMemberLocator.getVersionAccessor(entity.getName());
    if (idAccessor == null || versionAccessor == null) {
      return null;
    }

    final JavaType idType = GwtUtils.convertPrimitiveType(idAccessor.getReturnType(), true);
    String locatorIdentifier = PhysicalTypeIdentifier.createIdentifier(new JavaType(locatorType));
    ClassOrInterfaceTypeDetailsBuilder locatorBuilder =
        new ClassOrInterfaceTypeDetailsBuilder(locatorIdentifier);
    AnnotationMetadataBuilder annotationMetadataBuilder =
        new AnnotationMetadataBuilder(RooJavaType.ROO_GWT_LOCATOR);
    annotationMetadataBuilder.addStringAttribute(
        "value", entity.getName().getFullyQualifiedTypeName());
    locatorBuilder.addAnnotation(annotationMetadataBuilder);
    annotationMetadataBuilder = new AnnotationMetadataBuilder(SpringJavaType.COMPONENT);
    locatorBuilder.addAnnotation(annotationMetadataBuilder);
    locatorBuilder.setName(new JavaType(locatorType));
    locatorBuilder.setModifier(Modifier.PUBLIC);
    locatorBuilder.setPhysicalTypeCategory(PhysicalTypeCategory.CLASS);
    locatorBuilder.addExtendsTypes(
        new JavaType(
            GwtUtils.LOCATOR.getFullyQualifiedTypeName(),
            0,
            DataType.TYPE,
            null,
            Arrays.asList(entity.getName(), idType)));
    locatorBuilder.addMethod(getCreateMethod(locatorIdentifier, entity.getName()));
    locatorBuilder.addMethod(
        getFindMethod(locatorBuilder, locatorIdentifier, entity.getName(), idType));
    locatorBuilder.addMethod(getDomainTypeMethod(locatorIdentifier, entity.getName()));
    locatorBuilder.addMethod(getIdMethod(locatorIdentifier, entity.getName(), idAccessor));
    locatorBuilder.addMethod(getIdTypeMethod(locatorIdentifier, entity.getName(), idType));
    locatorBuilder.addMethod(
        getVersionMethod(locatorIdentifier, entity.getName(), versionAccessor));

    typeManagementService.createOrUpdateTypeOnDisk(locatorBuilder.build());
    return null;
  }
  public boolean updateTypeAnnotation(
      AnnotationMetadata annotation, Set<JavaSymbolName> attributesToDeleteIfPresent) {
    boolean hasChanged = false;

    // We are going to build a replacement AnnotationMetadata.
    // This variable tracks the new attribute values the replacement will hold.
    Map<JavaSymbolName, AnnotationAttributeValue<?>> replacementAttributeValues =
        new LinkedHashMap<JavaSymbolName, AnnotationAttributeValue<?>>();

    AnnotationMetadataBuilder existingBuilder =
        MemberFindingUtils.getDeclaredTypeAnnotation(this, annotation.getAnnotationType());

    if (existingBuilder == null) {
      // Not already present, so just go and add it
      for (JavaSymbolName incomingAttributeName : annotation.getAttributeNames()) {
        // Do not copy incoming attributes which exist in the attributesToDeleteIfPresent Set
        if (attributesToDeleteIfPresent == null
            || !attributesToDeleteIfPresent.contains(incomingAttributeName)) {
          AnnotationAttributeValue<?> incomingValue =
              annotation.getAttribute(incomingAttributeName);
          replacementAttributeValues.put(incomingAttributeName, incomingValue);
        }
      }

      AnnotationMetadataBuilder replacement =
          new AnnotationMetadataBuilder(
              annotation.getAnnotationType(),
              new ArrayList<AnnotationAttributeValue<?>>(replacementAttributeValues.values()));
      addAnnotation(replacement);
      return true;
    }

    AnnotationMetadata existing = existingBuilder.build();

    // Copy the existing attributes into the new attributes
    for (JavaSymbolName existingAttributeName : existing.getAttributeNames()) {
      if (attributesToDeleteIfPresent != null
          && attributesToDeleteIfPresent.contains(existingAttributeName)) {
        hasChanged = true;
      } else {
        AnnotationAttributeValue<?> existingValue = existing.getAttribute(existingAttributeName);
        replacementAttributeValues.put(existingAttributeName, existingValue);
      }
    }

    // Now we ensure every incoming attribute replaces the existing
    for (JavaSymbolName incomingAttributeName : annotation.getAttributeNames()) {
      AnnotationAttributeValue<?> incomingValue = annotation.getAttribute(incomingAttributeName);

      // Add this attribute to the end of the list if the attribute is not already present
      if (replacementAttributeValues.keySet().contains(incomingAttributeName)) {
        // There was already an attribute. Need to determine if this new attribute value is
        // materially different
        AnnotationAttributeValue<?> existingValue =
            replacementAttributeValues.get(incomingAttributeName);
        Assert.notNull(existingValue, "Existing value should have been provided by earlier loop");
        if (!existingValue.equals(incomingValue)) {
          replacementAttributeValues.put(incomingAttributeName, incomingValue);
          hasChanged = true;
        }
      } else if (attributesToDeleteIfPresent == null
          || !attributesToDeleteIfPresent.contains(incomingAttributeName)) {
        // This is a new attribute that does not already exist, so add it to the end of the
        // replacement attributes
        replacementAttributeValues.put(incomingAttributeName, incomingValue);
        hasChanged = true;
      }
    }
    // Were there any material changes?
    if (!hasChanged) {
      return false;
    }

    // Make a new AnnotationMetadata representing the replacement
    AnnotationMetadataBuilder replacement =
        new AnnotationMetadataBuilder(
            annotation.getAnnotationType(),
            new ArrayList<AnnotationAttributeValue<?>>(replacementAttributeValues.values()));
    annotations.remove(existingBuilder);
    addAnnotation(replacement);

    return true;
  }
  /** @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;
  }
  /** Adds the JUnit and Spring type level annotations if needed */
  private void addRequiredIntegrationTestClassIntroductions(final JavaType dodGovernor) {
    // Add an @RunWith(SpringJunit4ClassRunner) annotation to the type, if
    // the user did not define it on the governor directly
    if (MemberFindingUtils.getAnnotationOfType(governorTypeDetails.getAnnotations(), RUN_WITH)
        == null) {
      AnnotationMetadataBuilder runWithBuilder = new AnnotationMetadataBuilder(RUN_WITH);
      runWithBuilder.addClassAttribute(
          "value", "org.springframework.test.context.junit4.SpringJUnit4ClassRunner");
      builder.addAnnotation(runWithBuilder);
    }

    // Add an @ContextConfiguration("classpath:/applicationContext.xml")
    // annotation to the type, if the user did not define it on the governor
    // directly
    if (MemberFindingUtils.getAnnotationOfType(
            governorTypeDetails.getAnnotations(), CONTEXT_CONFIGURATION)
        == null) {
      AnnotationMetadataBuilder contextConfigurationBuilder =
          new AnnotationMetadataBuilder(CONTEXT_CONFIGURATION);
      contextConfigurationBuilder.addStringAttribute(
          "locations", "classpath:/META-INF/spring/applicationContext*.xml");
      builder.addAnnotation(contextConfigurationBuilder);
    }

    // Add an @Transactional, if the user did not define it on the governor
    // directly
    if (annotationValues.isTransactional()
        && MemberFindingUtils.getAnnotationOfType(
                governorTypeDetails.getAnnotations(), TRANSACTIONAL)
            == null) {
      AnnotationMetadataBuilder transactionalBuilder = new AnnotationMetadataBuilder(TRANSACTIONAL);
      if (StringUtils.hasText(transactionManager)
          && !"transactionManager".equals(transactionManager)) {
        transactionalBuilder.addStringAttribute("value", transactionManager);
      }
      builder.addAnnotation(transactionalBuilder);
    }

    // Add the data on demand field if the user did not define it on the
    // governor directly
    FieldMetadata field = governorTypeDetails.getField(new JavaSymbolName("dod"));
    if (field != null) {
      Assert.isTrue(
          field.getFieldType().equals(dodGovernor),
          "Field 'dod' on '"
              + destination.getFullyQualifiedTypeName()
              + "' must be of type '"
              + dodGovernor.getFullyQualifiedTypeName()
              + "'");
      Assert.notNull(
          MemberFindingUtils.getAnnotationOfType(field.getAnnotations(), AUTOWIRED),
          "Field 'dod' on '"
              + destination.getFullyQualifiedTypeName()
              + "' must be annotated with @Autowired");
    } else {
      // Add the field via the ITD
      List<AnnotationMetadataBuilder> annotations = new ArrayList<AnnotationMetadataBuilder>();
      annotations.add(new AnnotationMetadataBuilder(AUTOWIRED));
      FieldMetadataBuilder fieldBuilder =
          new FieldMetadataBuilder(
              getId(), Modifier.PRIVATE, annotations, new JavaSymbolName("dod"), dodGovernor);
      builder.addField(fieldBuilder);
    }

    builder.getImportRegistrationResolver().addImport(ASSERT);
  }
  @Override
  public void createStringField(
      ClassOrInterfaceTypeDetails cid,
      JavaSymbolName fieldName,
      boolean notNull,
      boolean nullRequired,
      String decimalMin,
      String decimalMax,
      Integer sizeMin,
      Integer sizeMax,
      String regexp,
      String column,
      String comment,
      boolean unique,
      String value,
      boolean lob,
      boolean permitReservedWords,
      boolean transientModifier,
      List<AnnotationMetadataBuilder> extraAnnotations) {

    final String physicalTypeIdentifier = cid.getDeclaredByMetadataId();
    final StringField fieldDetails = new StringField(physicalTypeIdentifier, fieldName);
    fieldDetails.setNotNull(notNull);
    fieldDetails.setNullRequired(nullRequired);
    if (decimalMin != null) {
      fieldDetails.setDecimalMin(decimalMin);
    }
    if (decimalMax != null) {
      fieldDetails.setDecimalMax(decimalMax);
    }
    if (sizeMin != null) {
      fieldDetails.setSizeMin(sizeMin);
    }
    if (sizeMax != null) {
      fieldDetails.setSizeMax(sizeMax);
    }
    if (regexp != null) {
      fieldDetails.setRegexp(regexp.replace("\\", "\\\\"));
    }
    if (column != null) {
      fieldDetails.setColumn(column);
    }
    if (comment != null) {
      fieldDetails.setComment(comment);
    }
    if (unique) {
      fieldDetails.setUnique(true);
    }
    if (value != null) {
      fieldDetails.setValue(value);
    }

    if (lob) {
      fieldDetails
          .getInitedAnnotations()
          .add(new AnnotationMetadataBuilder("javax.persistence.Lob"));

      // ROO-3722: Add LAZY load in @Lob fields using @Basic
      AnnotationMetadataBuilder basicAnnotation =
          new AnnotationMetadataBuilder("javax.persistence.Basic");
      basicAnnotation.addEnumAttribute(
          "fetch",
          new EnumDetails(new JavaType("javax.persistence.FetchType"), new JavaSymbolName("LAZY")));
      fieldDetails.getInitedAnnotations().add(basicAnnotation);
    }

    if (extraAnnotations != null && !extraAnnotations.isEmpty()) {
      fieldDetails.addAnnotations(extraAnnotations);
    }

    insertField(fieldDetails, permitReservedWords, false);
  }
 public boolean updateTypeAnnotation(AnnotationMetadataBuilder annotationBuilder) {
   return updateTypeAnnotation(annotationBuilder.build());
 }
  @Override
  public void installLanguage(final I18n language, final boolean useAsDefault, final Pom module) {

    // Check if provided module match with application modules features
    Validate.isTrue(
        getTypeLocationService().hasModuleFeature(module, ModuleFeatureName.APPLICATION),
        "ERROR: Provided module doesn't match with application modules features. "
            + "Execute this operation again and provide a valid application module.");

    Validate.notNull(language, "ERROR: You should provide a valid language code.");

    if (language.getLocale() == null) {
      LOGGER.warning("ERROR: Provided language is not valid.");
      return;
    }

    final LogicalPath resourcesPath =
        LogicalPath.getInstance(Path.SRC_MAIN_RESOURCES, module.getModuleName());

    final String targetDirectory = getPathResolver().getIdentifier(resourcesPath, "");

    // Getting message.properties file
    String messageBundle = "";

    if (language.getLocale().equals(Locale.ENGLISH)) {
      messageBundle = targetDirectory + "messages.properties";
    } else {
      messageBundle =
          targetDirectory
              .concat("messages_")
              .concat(language.getLocale().getLanguage().concat(".properties"));
    }

    if (!getFileManager().exists(messageBundle)) {
      InputStream inputStream = null;
      OutputStream outputStream = null;
      try {
        inputStream = language.getMessageBundle();
        outputStream = getFileManager().createFile(messageBundle).getOutputStream();
        IOUtils.copy(inputStream, outputStream);
      } catch (final Exception e) {
        throw new IllegalStateException(
            "Encountered an error during copying of message bundle MVC JSP addon.", e);
      } finally {
        IOUtils.closeQuietly(inputStream);
        IOUtils.closeQuietly(outputStream);
      }
    }

    // Install flag
    final String flagGraphic =
        targetDirectory
            .concat("static/public/img/")
            .concat(language.getLocale().getLanguage())
            .concat(".png");
    if (!getFileManager().exists(flagGraphic)) {
      InputStream inputStream = null;
      OutputStream outputStream = null;
      try {
        inputStream = language.getFlagGraphic();
        outputStream = getFileManager().createFile(flagGraphic).getOutputStream();
        IOUtils.copy(inputStream, outputStream);
      } catch (final Exception e) {
        throw new IllegalStateException(
            "Encountered an error during copying of flag graphic for MVC JSP addon.", e);
      } finally {
        IOUtils.closeQuietly(inputStream);
        IOUtils.closeQuietly(outputStream);
      }
    }

    // Update @WebMvcConfiguration annotation defining defaultLanguage
    // attribute
    if (useAsDefault) {

      // Obtain all existing configuration classes annotated with
      // @RooWebMvcConfiguration
      Set<ClassOrInterfaceTypeDetails> configurationClasses =
          getTypeLocationService()
              .findClassesOrInterfaceDetailsWithAnnotation(RooJavaType.ROO_WEB_MVC_CONFIGURATION);

      for (ClassOrInterfaceTypeDetails configurationClass : configurationClasses) {
        // If configuration class is located in the provided module
        if (configurationClass.getType().getModule().equals(module.getModuleName())) {
          ClassOrInterfaceTypeDetailsBuilder cidBuilder =
              new ClassOrInterfaceTypeDetailsBuilder(configurationClass);
          AnnotationMetadataBuilder annotation =
              cidBuilder.getDeclaredTypeAnnotation(RooJavaType.ROO_WEB_MVC_CONFIGURATION);
          annotation.addStringAttribute("defaultLanguage", language.getLocale().getLanguage());

          // Update configuration class
          getTypeManagementService().createOrUpdateTypeOnDisk(cidBuilder.build());
        }
      }

      LOGGER.log(
          Level.INFO,
          String.format(
              "INFO: Default language of your project has been changed to %s.",
              language.getLanguage()));
    }

    // Get all controllers and update its message bundles
    Set<ClassOrInterfaceTypeDetails> controllers =
        getTypeLocationService()
            .findClassesOrInterfaceDetailsWithAnnotation(RooJavaType.ROO_CONTROLLER);
    for (ClassOrInterfaceTypeDetails controller : controllers) {
      getMetadataService().evictAndGet(ControllerMetadata.createIdentifier(controller));
    }

    // Add application property
    getApplicationConfigService()
        .addProperty(
            module.getModuleName(), "spring.messages.fallback-to-system-locale", "false", "", true);
  }
 private AnnotationMetadataBuilder getRooDbManagedAnnotation() {
   final AnnotationMetadataBuilder rooDbManagedBuilder =
       new AnnotationMetadataBuilder(ROO_DB_MANAGED);
   rooDbManagedBuilder.addBooleanAttribute("automaticallyDelete", true);
   return rooDbManagedBuilder;
 }
  @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 MethodMetadataBuilder getFinderMethod(final FinderMetadataDetails finderMetadataDetails) {
    Validate.notNull(finderMetadataDetails, "Method metadata required for finder");
    final JavaSymbolName finderMethodName =
        new JavaSymbolName(
            finderMetadataDetails.getFinderMethodMetadata().getMethodName().getSymbolName());

    final List<AnnotatedJavaType> parameterTypes = new ArrayList<AnnotatedJavaType>();
    final List<JavaSymbolName> parameterNames = new ArrayList<JavaSymbolName>();

    final InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder();
    final StringBuilder methodParams = new StringBuilder();

    boolean dateFieldPresent = !dateTypes.isEmpty();
    for (final FieldMetadata field : finderMetadataDetails.getFinderMethodParamFields()) {
      final JavaSymbolName fieldName = field.getFieldName();
      final List<AnnotationMetadata> annotations = new ArrayList<AnnotationMetadata>();
      final List<AnnotationAttributeValue<?>> attributes =
          new ArrayList<AnnotationAttributeValue<?>>();
      attributes.add(
          new StringAttributeValue(
              new JavaSymbolName("value"), uncapitalize(fieldName.getSymbolName())));
      if (field.getFieldType().equals(JavaType.BOOLEAN_PRIMITIVE)
          || field.getFieldType().equals(JavaType.BOOLEAN_OBJECT)) {
        attributes.add(new BooleanAttributeValue(new JavaSymbolName("required"), false));
      }
      final AnnotationMetadataBuilder requestParamAnnotation =
          new AnnotationMetadataBuilder(REQUEST_PARAM, attributes);
      annotations.add(requestParamAnnotation.build());
      if (field.getFieldType().equals(DATE) || field.getFieldType().equals(CALENDAR)) {
        dateFieldPresent = true;
        final AnnotationMetadata annotation =
            MemberFindingUtils.getAnnotationOfType(field.getAnnotations(), DATE_TIME_FORMAT);
        if (annotation != null) {
          getShortName(DATE_TIME_FORMAT);
          annotations.add(annotation);
        }
      }
      parameterNames.add(fieldName);
      parameterTypes.add(new AnnotatedJavaType(field.getFieldType(), annotations));

      if (field.getFieldType().equals(JavaType.BOOLEAN_OBJECT)) {
        methodParams.append(fieldName + " == null ? Boolean.FALSE : " + fieldName + ", ");
      } else {
        methodParams.append(fieldName + ", ");
      }
    }

    if (methodParams.length() > 0) {
      methodParams.delete(methodParams.length() - 2, methodParams.length());
    }

    final List<AnnotationAttributeValue<?>> firstResultAttributes =
        new ArrayList<AnnotationAttributeValue<?>>();
    firstResultAttributes.add(new StringAttributeValue(new JavaSymbolName("value"), "page"));
    firstResultAttributes.add(new BooleanAttributeValue(new JavaSymbolName("required"), false));
    final AnnotationMetadataBuilder firstResultAnnotation =
        new AnnotationMetadataBuilder(REQUEST_PARAM, firstResultAttributes);

    final List<AnnotationAttributeValue<?>> maxResultsAttributes =
        new ArrayList<AnnotationAttributeValue<?>>();
    maxResultsAttributes.add(new StringAttributeValue(new JavaSymbolName("value"), "size"));
    maxResultsAttributes.add(new BooleanAttributeValue(new JavaSymbolName("required"), false));
    final AnnotationMetadataBuilder maxResultAnnotation =
        new AnnotationMetadataBuilder(REQUEST_PARAM, maxResultsAttributes);

    final List<AnnotationAttributeValue<?>> sortFieldNameAttributes =
        new ArrayList<AnnotationAttributeValue<?>>();
    sortFieldNameAttributes.add(
        new StringAttributeValue(new JavaSymbolName("value"), "sortFieldName"));
    sortFieldNameAttributes.add(new BooleanAttributeValue(new JavaSymbolName("required"), false));
    final AnnotationMetadataBuilder sortFieldNameAnnotation =
        new AnnotationMetadataBuilder(REQUEST_PARAM, sortFieldNameAttributes);

    final List<AnnotationAttributeValue<?>> sortOrderAttributes =
        new ArrayList<AnnotationAttributeValue<?>>();
    sortOrderAttributes.add(new StringAttributeValue(new JavaSymbolName("value"), "sortOrder"));
    sortOrderAttributes.add(new BooleanAttributeValue(new JavaSymbolName("required"), false));
    final AnnotationMetadataBuilder sortOrderAnnotation =
        new AnnotationMetadataBuilder(REQUEST_PARAM, sortOrderAttributes);

    parameterTypes.add(
        new AnnotatedJavaType(
            new JavaType(Integer.class.getName()), firstResultAnnotation.build()));
    parameterTypes.add(
        new AnnotatedJavaType(new JavaType(Integer.class.getName()), maxResultAnnotation.build()));
    parameterTypes.add(
        new AnnotatedJavaType(
            new JavaType(String.class.getName()), sortFieldNameAnnotation.build()));
    parameterTypes.add(
        new AnnotatedJavaType(new JavaType(String.class.getName()), sortOrderAnnotation.build()));

    parameterTypes.add(new AnnotatedJavaType(MODEL));
    if (getGovernorMethod(
            finderMethodName, AnnotatedJavaType.convertFromAnnotatedJavaTypes(parameterTypes))
        != null) {
      return null;
    }

    final List<JavaSymbolName> newParamNames = new ArrayList<JavaSymbolName>();
    newParamNames.addAll(parameterNames);
    newParamNames.add(new JavaSymbolName("page"));
    newParamNames.add(new JavaSymbolName("size"));
    newParamNames.add(new JavaSymbolName("sortFieldName"));
    newParamNames.add(new JavaSymbolName("sortOrder"));
    newParamNames.add(new JavaSymbolName("uiModel"));

    final List<AnnotationAttributeValue<?>> requestMappingAttributes =
        new ArrayList<AnnotationAttributeValue<?>>();
    requestMappingAttributes.add(
        new StringAttributeValue(
            new JavaSymbolName("params"),
            "find="
                + finderMetadataDetails
                    .getFinderMethodMetadata()
                    .getMethodName()
                    .getSymbolName()
                    .replaceFirst("find" + javaTypeMetadataHolder.getPlural(), "")));
    requestMappingAttributes.add(
        new EnumAttributeValue(
            new JavaSymbolName("method"),
            new EnumDetails(REQUEST_METHOD, new JavaSymbolName("GET"))));
    final AnnotationMetadataBuilder requestMapping =
        new AnnotationMetadataBuilder(REQUEST_MAPPING, requestMappingAttributes);
    final List<AnnotationMetadataBuilder> annotations = new ArrayList<AnnotationMetadataBuilder>();
    annotations.add(requestMapping);

    bodyBuilder.appendFormalLine("if (page != null || size != null) {");
    bodyBuilder.indent();
    bodyBuilder.appendFormalLine("int sizeNo = size == null ? 10 : size.intValue();");
    bodyBuilder.appendFormalLine(
        "final int firstResult = page == null ? 0 : (page.intValue() - 1) * sizeNo;");
    String methodParamsString = methodParams.toString();
    if (StringUtils.isNotBlank(methodParamsString)) {
      methodParamsString.concat(", ");
    }
    bodyBuilder.appendFormalLine(
        "uiModel.addAttribute(\""
            + javaTypeMetadataHolder.getPlural().toLowerCase()
            + "\", "
            + getShortName(formBackingType)
            + "."
            + finderMetadataDetails.getFinderMethodMetadata().getMethodName().getSymbolName()
            + "("
            + methodParamsString
            + "sortFieldName, sortOrder).setFirstResult(firstResult).setMaxResults(sizeNo).getResultList());");

    char[] methodNameArray =
        finderMetadataDetails
            .getFinderMethodMetadata()
            .getMethodName()
            .getSymbolName()
            .toCharArray();
    methodNameArray[0] = Character.toUpperCase(methodNameArray[0]);
    String countMethodName = "count" + new String(methodNameArray);

    bodyBuilder.appendFormalLine(
        "float nrOfPages = (float) "
            + getShortName(formBackingType)
            + "."
            + countMethodName
            + "("
            + methodParamsString
            + ") / sizeNo;");
    bodyBuilder.appendFormalLine(
        "uiModel.addAttribute(\"maxPages\", (int) ((nrOfPages > (int) nrOfPages || nrOfPages == 0.0) ? nrOfPages + 1 : nrOfPages));");
    bodyBuilder.indentRemove();
    bodyBuilder.appendFormalLine("} else {");
    bodyBuilder.indent();
    bodyBuilder.appendFormalLine(
        "uiModel.addAttribute(\""
            + javaTypeMetadataHolder.getPlural().toLowerCase()
            + "\", "
            + getShortName(formBackingType)
            + "."
            + finderMetadataDetails.getFinderMethodMetadata().getMethodName().getSymbolName()
            + "("
            + methodParamsString
            + "sortFieldName, sortOrder).getResultList());");
    bodyBuilder.indentRemove();
    bodyBuilder.appendFormalLine("}");

    if (dateFieldPresent) {
      bodyBuilder.appendFormalLine("addDateTimeFormatPatterns(uiModel);");
    }
    bodyBuilder.appendFormalLine("return \"" + controllerPath + "/list\";");

    final MethodMetadataBuilder methodBuilder =
        new MethodMetadataBuilder(
            getId(),
            Modifier.PUBLIC,
            finderMethodName,
            JavaType.STRING,
            parameterTypes,
            newParamNames,
            bodyBuilder);
    methodBuilder.setAnnotations(annotations);
    return methodBuilder;
  }