/**
 * Retrieves text files from the classloader and displays them on-screen.
 *
 * <p>Respects normal Roo conventions such as all resources should appear under the same package as
 * the bundle itself etc.
 *
 * @author Ben Alex
 * @since 1.1.1
 */
public abstract class MessageDisplayUtils {

  private static Logger LOGGER = HandlerUtils.getLogger(MessageDisplayUtils.class);

  /**
   * Same as {@link #displayFile(String, Class, boolean)} except it passes false as the final
   * argument.
   *
   * @param fileName the simple filename (required)
   * @param owner the class which owns the file (required)
   */
  public static void displayFile(final String fileName, final Class<?> owner) {
    displayFile(fileName, owner, false);
  }

  /**
   * Displays the requested file via the LOGGER API.
   *
   * <p>Each file must available from the classloader of the "owner". It must also be in the same
   * package as the class of the "owner". So if the owner is com.foo.Bar, and the file is called
   * "hello.txt", the file must appear in the same bundle as com.foo.Bar and be available from the
   * resource path "/com/foo/Hello.txt".
   *
   * @param fileName the simple filename (required)
   * @param owner the class which owns the file (required)
   * @param important if true, it will display with a higher importance color where possible
   */
  public static void displayFile(
      final String fileName, final Class<?> owner, final boolean important) {
    final Level level = important ? Level.SEVERE : Level.FINE;
    final String owningPackage = owner.getPackage().getName().replace('.', '/');
    final String fullResourceName = "/" + owningPackage + "/" + fileName;
    final InputStream inputStream = owner.getClassLoader().getResourceAsStream(fullResourceName);
    if (inputStream == null) {
      throw new IllegalStateException("Could not locate '" + fileName + "'");
    }
    try {
      final String message =
          FileCopyUtils.copyToString(new InputStreamReader(new BufferedInputStream(inputStream)));
      LOGGER.log(level, message);
    } catch (final Exception e) {
      throw new IllegalStateException(e);
    } finally {
      IOUtils.closeQuietly(inputStream);
    }
  }
}
/**
 * Implementation of operations this add-on offers.
 *
 * @since 1.1
 */
@Component // Use these Apache Felix annotations to register your commands class in the Roo
           // container
@Service
public class EnversOperationsImpl extends AbstractOperations implements EnversOperations {

  private static Logger LOG = HandlerUtils.getLogger(EnversCommands.class);

  /**
   * Use ProjectOperations to install new dependencies, plugins, properties, etc into the project
   * configuration
   */
  @Reference private ProjectOperations projectOperations;

  /** Use PathResolver to get paths for copying files */
  @Reference private PathResolver pathResolver;

  /**
   * Use TypeLocationService to find types which are annotated with a given annotation in the
   * project
   */
  @Reference private TypeLocationService typeLocationService;

  /** Use TypeManagementService to change types */
  @Reference private TypeManagementService typeManagementService;

  @Reference private MetadataService metadataService;

  /** {@inheritDoc} */
  public boolean isCommandAvailable() {
    // Check if a project has been created
    return projectOperations.isFocusedProjectAvailable();
  }

  /** {@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());
  }

  public void addViews(final JavaType type) {
    String targetPath = pathResolver.getFocusedIdentifier(Path.SRC_MAIN_WEBAPP, "/WEB-INF/views/");
    EnversViewManager viewManager = new EnversViewManager(type, targetPath);
    viewManager.addViews();
  }

  /** {@inheritDoc} */
  public void setup() {
    // Install the add-on Google code repository needed to get the annotation
    // projectOperations.addRepository("", new Repository("Envers Roo add-on repository", "Envers
    // Roo add-on repository", "https://roo-envers.googlecode.com/svn/repo"));

    List<Dependency> dependencies = new ArrayList<Dependency>();

    // Install the dependency on the add-on jar
    // TODO move this to configuration.xml ?
    dependencies.add(
        new Dependency(
            "de.eightbitboy.roo.envers",
            "de.eightbitboy.roo.envers",
            "0.1.0",
            DependencyType.JAR,
            DependencyScope.PROVIDED));

    // Install dependencies defined in external XML file
    for (Element dependencyElement :
        XmlUtils.findElements(
            "/configuration/batch/dependencies/dependency",
            XmlUtils.getConfiguration(getClass()))) {
      dependencies.add(new Dependency(dependencyElement));
    }

    // Add all new dependencies to pom.xml
    projectOperations.addDependencies("", dependencies);

    // Modify tags to create views for entity revisions
    this.modifyTags(); // TODO use modified tags for audited entities only
  }

  private void modifyTags() {
    // TODO modify tags instead of overwriting them!!!
    String targetPath =
        pathResolver.getFocusedIdentifier(Path.SRC_MAIN_WEBAPP, "/WEB-INF/tags/form/fields");

    LOG.info("disabled copying of tags");
    // copyDirectoryContents("tags/*.*", targetPath, true);
  }
}
/**
 * Implementation of {@link MailOperationsImpl}.
 *
 * @author Stefan Schmidt
 * @since 1.0
 */
@Component
@Service
public class MailOperationsImpl implements MailOperations {

  // Constants
  private static final int PRIVATE_TRANSIENT = Modifier.PRIVATE | Modifier.TRANSIENT;
  private static final AnnotatedJavaType STRING = new AnnotatedJavaType(JavaType.STRING);
  private static final String LOCAL_MESSAGE_VARIABLE = "mailMessage";
  private static final String SPRING_TASK_NS = "http://www.springframework.org/schema/task";
  private static final String SPRING_TASK_XSD =
      "http://www.springframework.org/schema/task/spring-task-3.0.xsd";
  private static final String TEMPLATE_MESSAGE_FIELD = "templateMessage";
  private static final Logger log = HandlerUtils.getLogger(MailOperationsImpl.class);

  // Fields
  @Reference private FileManager fileManager;
  @Reference private ProjectOperations projectOperations;
  @Reference private PropFileOperations propFileOperations;
  @Reference private TypeManagementService typeManagementService;
  @Reference private TypeLocationService typeLocationService;

  public boolean isInstallEmailAvailable() {
    return projectOperations.isProjectAvailable();
  }

  public boolean isManageEmailAvailable() {
    return projectOperations.isProjectAvailable()
        && fileManager.exists(getApplicationContextPath());
  }

  /**
   * Returns the canonical path of the user project's applicationContext.xml file.
   *
   * @return a non-blank path
   */
  private String getApplicationContextPath() {
    return projectOperations
        .getPathResolver()
        .getIdentifier(Path.SPRING_CONFIG_ROOT, "applicationContext.xml");
  }

  public void installEmail(
      final String hostServer,
      final MailProtocol protocol,
      final String port,
      final String encoding,
      final String username,
      final String password) {
    Assert.hasText(hostServer, "Host server name required");

    final String contextPath = getApplicationContextPath();
    final Document document = XmlUtils.readXml(fileManager.getInputStream(contextPath));
    final Element root = document.getDocumentElement();

    boolean installDependencies = true;
    final Map<String, String> props = new HashMap<String, String>();

    Element mailBean =
        XmlUtils.findFirstElement(
            "/beans/bean[@class = 'org.springframework.mail.javamail.JavaMailSenderImpl']", root);
    if (mailBean != null) {
      root.removeChild(mailBean);
      installDependencies = false;
    }

    mailBean = document.createElement("bean");
    mailBean.setAttribute("class", "org.springframework.mail.javamail.JavaMailSenderImpl");
    mailBean.setAttribute("id", "mailSender");

    final Element property = document.createElement("property");
    property.setAttribute("name", "host");
    property.setAttribute("value", "${email.host}");
    mailBean.appendChild(property);
    root.appendChild(mailBean);
    props.put("email.host", hostServer);

    if (protocol != null) {
      final Element pElement = document.createElement("property");
      pElement.setAttribute("value", "${email.protocol}");
      pElement.setAttribute("name", "protocol");
      mailBean.appendChild(pElement);
      props.put("email.protocol", protocol.getProtocol());
    }

    if (StringUtils.hasText(port)) {
      final Element pElement = document.createElement("property");
      pElement.setAttribute("name", "port");
      pElement.setAttribute("value", "${email.port}");
      mailBean.appendChild(pElement);
      props.put("email.port", port);
    }

    if (StringUtils.hasText(encoding)) {
      final Element pElement = document.createElement("property");
      pElement.setAttribute("name", "defaultEncoding");
      pElement.setAttribute("value", "${email.encoding}");
      mailBean.appendChild(pElement);
      props.put("email.encoding", encoding);
    }

    if (StringUtils.hasText(username)) {
      final Element pElement = document.createElement("property");
      pElement.setAttribute("name", "username");
      pElement.setAttribute("value", "${email.username}");
      mailBean.appendChild(pElement);
      props.put("email.username", username);
    }

    if (StringUtils.hasText(password)) {
      final Element pElement = document.createElement("property");
      pElement.setAttribute("name", "password");
      pElement.setAttribute("value", "${email.password}");
      mailBean.appendChild(pElement);
      props.put("email.password", password);

      if (SMTP.equals(protocol)) {
        final Element javaMailProperties = document.createElement("property");
        javaMailProperties.setAttribute("name", "javaMailProperties");
        final Element securityProps = document.createElement("props");
        javaMailProperties.appendChild(securityProps);
        final Element prop = document.createElement("prop");
        prop.setAttribute("key", "mail.smtp.auth");
        prop.setTextContent("true");
        securityProps.appendChild(prop);
        final Element prop2 = document.createElement("prop");
        prop2.setAttribute("key", "mail.smtp.starttls.enable");
        prop2.setTextContent("true");
        securityProps.appendChild(prop2);
        mailBean.appendChild(javaMailProperties);
      }
    }

    DomUtils.removeTextNodes(root);

    fileManager.createOrUpdateTextFileIfRequired(
        contextPath, XmlUtils.nodeToString(document), false);

    if (installDependencies) {
      updateConfiguration();
    }

    propFileOperations.addProperties(
        Path.SPRING_CONFIG_ROOT, "email.properties", props, true, true);
  }

  public void configureTemplateMessage(final String from, final String subject) {
    final String contextPath = getApplicationContextPath();
    final Document document = XmlUtils.readXml(fileManager.getInputStream(contextPath));
    final Element root = document.getDocumentElement();

    final Map<String, String> props = new HashMap<String, String>();

    if (StringUtils.hasText(from) || StringUtils.hasText(subject)) {
      Element smmBean = getSimpleMailMessageBean(root);
      if (smmBean == null) {
        smmBean = document.createElement("bean");
        smmBean.setAttribute("class", "org.springframework.mail.SimpleMailMessage");
        smmBean.setAttribute("id", "templateMessage");
      }

      if (StringUtils.hasText(from)) {
        Element smmProperty = XmlUtils.findFirstElement("//property[@name='from']", smmBean);
        if (smmProperty != null) {
          smmBean.removeChild(smmProperty);
        }
        smmProperty = document.createElement("property");
        smmProperty.setAttribute("value", "${email.from}");
        smmProperty.setAttribute("name", "from");
        smmBean.appendChild(smmProperty);
        props.put("email.from", from);
      }

      if (StringUtils.hasText(subject)) {
        Element smmProperty = XmlUtils.findFirstElement("//property[@name='subject']", smmBean);
        if (smmProperty != null) {
          smmBean.removeChild(smmProperty);
        }
        smmProperty = document.createElement("property");
        smmProperty.setAttribute("value", "${email.subject}");
        smmProperty.setAttribute("name", "subject");
        smmBean.appendChild(smmProperty);
        props.put("email.subject", subject);
      }

      root.appendChild(smmBean);

      DomUtils.removeTextNodes(root);

      fileManager.createOrUpdateTextFileIfRequired(
          contextPath, XmlUtils.nodeToString(document), false);
    }

    if (props.size() > 0) {
      propFileOperations.addProperties(
          Path.SPRING_CONFIG_ROOT, "email.properties", props, true, true);
    }
  }

  /**
   * Finds the SimpleMailMessage bean in the Spring XML file with the given root element
   *
   * @param root
   * @return <code>null</code> if there is no such bean
   */
  private Element getSimpleMailMessageBean(final Element root) {
    return XmlUtils.findFirstElement(
        "/beans/bean[@class = 'org.springframework.mail.SimpleMailMessage']", root);
  }

  public void injectEmailTemplate(
      final JavaType targetType, final JavaSymbolName fieldName, final boolean async) {
    Assert.notNull(targetType, "Java type required");
    Assert.notNull(fieldName, "Field name required");

    final List<AnnotationMetadataBuilder> annotations = new ArrayList<AnnotationMetadataBuilder>();
    annotations.add(new AnnotationMetadataBuilder(AUTOWIRED));

    // Obtain the physical type and its mutable class details
    final String declaredByMetadataId = PhysicalTypeIdentifier.createIdentifier(targetType);
    ClassOrInterfaceTypeDetails existing = typeLocationService.findClassOrInterface(targetType);
    if (existing == null) {
      log.warning(
          "Aborting: Unable to find metadata for target type '"
              + targetType.getFullyQualifiedTypeName()
              + "'");
      return;
    }
    final ClassOrInterfaceTypeDetailsBuilder classOrInterfaceTypeDetailsBuilder =
        new ClassOrInterfaceTypeDetailsBuilder(existing);

    // Add the MailSender field
    final FieldMetadataBuilder mailSenderFieldBuilder =
        new FieldMetadataBuilder(
            declaredByMetadataId, PRIVATE_TRANSIENT, annotations, fieldName, MAIL_SENDER);
    classOrInterfaceTypeDetailsBuilder.addField(mailSenderFieldBuilder.build());

    // Add the "sendMessage" method
    classOrInterfaceTypeDetailsBuilder.addMethod(
        getSendMethod(fieldName, async, declaredByMetadataId, classOrInterfaceTypeDetailsBuilder));
    typeManagementService.createOrUpdateTypeOnDisk(classOrInterfaceTypeDetailsBuilder.build());
  }

  /**
   * Generates the "send email" method to be added to the domain type
   *
   * @param mailSenderName the name of the MailSender field (required)
   * @param async whether to send the email asynchronously
   * @param targetClassMID the MID of the class to receive the method
   * @param mutableTypeDetails the type to which the method is being added (required)
   * @return a non-<code>null</code> method
   */
  private MethodMetadata getSendMethod(
      final JavaSymbolName mailSenderName,
      final boolean async,
      final String targetClassMID,
      final ClassOrInterfaceTypeDetailsBuilder classOrInterfaceTypeDetailsBuilder) {
    final String contextPath = getApplicationContextPath();
    final Document document = XmlUtils.readXml(fileManager.getInputStream(contextPath));
    final Element root = document.getDocumentElement();

    // Make a builder for the created method's body
    final InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder();

    // Collect the types and names of the created method's parameters
    final PairList<AnnotatedJavaType, JavaSymbolName> parameters =
        new PairList<AnnotatedJavaType, JavaSymbolName>();

    if (getSimpleMailMessageBean(root) == null) {
      // There's no SimpleMailMessage bean; use a local variable
      bodyBuilder.appendFormalLine(
          "org.springframework.mail.SimpleMailMessage "
              + LOCAL_MESSAGE_VARIABLE
              + " = new org.springframework.mail.SimpleMailMessage();");
      // Set the from address
      parameters.add(STRING, new JavaSymbolName("mailFrom"));
      bodyBuilder.appendFormalLine(LOCAL_MESSAGE_VARIABLE + ".setFrom(mailFrom);");
      // Set the subject
      parameters.add(STRING, new JavaSymbolName("subject"));
      bodyBuilder.appendFormalLine(LOCAL_MESSAGE_VARIABLE + ".setSubject(subject);");
    } else {
      // A SimpleMailMessage bean exists; auto-wire it into the entity and use it as a template
      final List<AnnotationMetadataBuilder> smmAnnotations =
          Arrays.asList(new AnnotationMetadataBuilder(AUTOWIRED));
      final FieldMetadataBuilder smmFieldBuilder =
          new FieldMetadataBuilder(
              targetClassMID,
              PRIVATE_TRANSIENT,
              smmAnnotations,
              new JavaSymbolName(TEMPLATE_MESSAGE_FIELD),
              SIMPLE_MAIL_MESSAGE);
      classOrInterfaceTypeDetailsBuilder.addField(smmFieldBuilder.build());
      // Use the injected bean as a template (for thread safety)
      bodyBuilder.appendFormalLine(
          "org.springframework.mail.SimpleMailMessage "
              + LOCAL_MESSAGE_VARIABLE
              + " = new org.springframework.mail.SimpleMailMessage("
              + TEMPLATE_MESSAGE_FIELD
              + ");");
    }

    // Set the to address
    parameters.add(STRING, new JavaSymbolName("mailTo"));
    bodyBuilder.appendFormalLine(LOCAL_MESSAGE_VARIABLE + ".setTo(mailTo);");

    // Set the message body
    parameters.add(STRING, new JavaSymbolName("message"));
    bodyBuilder.appendFormalLine(LOCAL_MESSAGE_VARIABLE + ".setText(message);");

    bodyBuilder.newLine();
    bodyBuilder.appendFormalLine(mailSenderName + ".send(" + LOCAL_MESSAGE_VARIABLE + ");");

    final MethodMetadataBuilder methodBuilder =
        new MethodMetadataBuilder(
            targetClassMID,
            Modifier.PUBLIC,
            new JavaSymbolName("sendMessage"),
            JavaType.VOID_PRIMITIVE,
            parameters.getKeys(),
            parameters.getValues(),
            bodyBuilder);

    if (async) {
      if (DomUtils.findFirstElementByName("task:annotation-driven", root) == null) {
        // Add asynchronous email support to the application
        if (!StringUtils.hasText(root.getAttribute("xmlns:task"))) {
          // Add the "task" namespace to the Spring config file
          root.setAttribute("xmlns:task", SPRING_TASK_NS);
          root.setAttribute(
              "xsi:schemaLocation",
              root.getAttribute("xsi:schemaLocation")
                  + "  "
                  + SPRING_TASK_NS
                  + " "
                  + SPRING_TASK_XSD);
        }
        root.appendChild(
            new XmlElementBuilder("task:annotation-driven", document)
                .addAttribute("executor", "asyncExecutor")
                .addAttribute("mode", "aspectj")
                .build());
        root.appendChild(
            new XmlElementBuilder("task:executor", document)
                .addAttribute("id", "asyncExecutor")
                .addAttribute("pool-size", "${executor.poolSize}")
                .build());
        // Write out the new Spring config file
        fileManager.createOrUpdateTextFileIfRequired(
            contextPath, XmlUtils.nodeToString(document), false);
        // Update the email properties file
        propFileOperations.addPropertyIfNotExists(
            Path.SPRING_CONFIG_ROOT, "email.properties", "executor.poolSize", "10", true);
      }
      methodBuilder.addAnnotation(new AnnotationMetadataBuilder(ASYNC));
    }
    return methodBuilder.build();
  }

  private void updateConfiguration() {
    final Element configuration = XmlUtils.getConfiguration(getClass());

    final List<Dependency> dependencies = new ArrayList<Dependency>();
    final List<Element> emailDependencies =
        XmlUtils.findElements("/configuration/email/dependencies/dependency", configuration);
    for (final Element dependencyElement : emailDependencies) {
      dependencies.add(new Dependency(dependencyElement));
    }
    projectOperations.addDependencies(dependencies);
  }
}
/**
 * Implementation of {@link I18nOperations}.
 *
 * @author Sergio Clares
 * @author Juan Carlos García
 * @since 2.0
 */
@Component
@Service
public class I18nOperationsImpl implements I18nOperations {

  private static final Logger LOGGER = HandlerUtils.getLogger(I18nOperationsImpl.class);

  // ------------ OSGi component attributes ----------------
  private BundleContext context;

  private ServiceInstaceManager serviceInstaceManager = new ServiceInstaceManager();

  protected void activate(final ComponentContext context) {
    this.context = context.getBundleContext();
    serviceInstaceManager.activate(this.context);
  }

  @Override
  public boolean isInstallLanguageCommandAvailable() {
    return getProjectOperations().isFeatureInstalled(FeatureNames.MVC);
  }

  @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);
  }

  /**
   * Add labels to all installed languages
   *
   * @param moduleName
   * @param labels
   */
  @Override
  public void addOrUpdateLabels(String moduleName, final Map<String, String> labels) {
    final LogicalPath resourcesPath = LogicalPath.getInstance(Path.SRC_MAIN_RESOURCES, moduleName);
    final String targetDirectory = getPathResolver().getIdentifier(resourcesPath, "");

    Set<I18n> supportedLanguages = getI18nSupport().getSupportedLanguages();
    for (I18n i18n : supportedLanguages) {
      String messageBundle =
          String.format("messages_%s.properties", i18n.getLocale().getLanguage());
      String bundlePath =
          String.format(
              "%s%s%s", targetDirectory, AntPathMatcher.DEFAULT_PATH_SEPARATOR, messageBundle);

      if (getFileManager().exists(bundlePath)) {
        getPropFilesManager().addProperties(resourcesPath, messageBundle, labels, true, false);
      }
    }

    // Allways update english message bundles
    getPropFilesManager().addProperties(resourcesPath, "messages.properties", labels, true, false);
  }

  /**
   * Return a list of installed languages in the provided application module.
   *
   * @param moduleName the module name to search for installed languages.
   * @return a list with the available languages.
   */
  @Override
  public List<I18n> getInstalledLanguages(String moduleName) {

    final LogicalPath resourcesPath = LogicalPath.getInstance(Path.SRC_MAIN_RESOURCES, moduleName);
    final String targetDirectory = getPathResolver().getIdentifier(resourcesPath, "");

    // Create list for installed languages
    List<I18n> installedLanguages = new ArrayList<I18n>();

    // Get all available languages
    Set<I18n> supportedLanguages = getI18nSupport().getSupportedLanguages();
    for (I18n i18n : supportedLanguages) {
      String messageBundle =
          String.format("messages_%s.properties", i18n.getLocale().getLanguage());
      String bundlePath =
          String.format(
              "%s%s%s", targetDirectory, AntPathMatcher.DEFAULT_PATH_SEPARATOR, messageBundle);

      if (getFileManager().exists(bundlePath)) {
        installedLanguages.add(i18n);
      }
    }

    // Always add English language as default
    installedLanguages.add(new EnglishLanguage());

    return Collections.unmodifiableList(installedLanguages);
  }

  /**
   * This method gets all implementations of ControllerMVCResponseService interface to be able to
   * locate all ControllerMVCResponseService. Uses param installed to obtain only the installed or
   * not installed response types.
   *
   * @param installed indicates if returned responseType should be installed or not.
   * @return Map with responseTypes identifier and the ControllerMVCResponseService implementation
   */
  private List<ControllerMVCResponseService> getControllerMVCResponseTypes(boolean installed) {
    List<ControllerMVCResponseService> responseTypes =
        new ArrayList<ControllerMVCResponseService>();

    try {
      ServiceReference<?>[] references =
          this.context.getAllServiceReferences(ControllerMVCResponseService.class.getName(), null);

      for (ServiceReference<?> ref : references) {
        ControllerMVCResponseService responseTypeService =
            (ControllerMVCResponseService) this.context.getService(ref);
        boolean isAbleToInstall = false;
        for (Pom module : getProjectOperations().getPoms()) {
          if (responseTypeService.isInstalledInModule(module.getModuleName()) == installed) {
            isAbleToInstall = true;
            break;
          }
        }
        if (isAbleToInstall) {
          responseTypes.add(responseTypeService);
        }
      }
      return responseTypes;

    } catch (InvalidSyntaxException e) {
      LOGGER.warning("Cannot load ControllerMVCResponseService on I18nOperationsImpl.");
      return null;
    }
  }

  // Get OSGi services

  private TypeLocationService getTypeLocationService() {
    return serviceInstaceManager.getServiceInstance(this, TypeLocationService.class);
  }

  private I18nSupport getI18nSupport() {
    return serviceInstaceManager.getServiceInstance(this, I18nSupport.class);
  }

  private PathResolver getPathResolver() {
    return serviceInstaceManager.getServiceInstance(this, PathResolver.class);
  }

  private FileManager getFileManager() {
    return serviceInstaceManager.getServiceInstance(this, FileManager.class);
  }

  private ProjectOperations getProjectOperations() {
    return serviceInstaceManager.getServiceInstance(this, ProjectOperations.class);
  }

  private PropFilesManagerService getPropFilesManager() {
    return serviceInstaceManager.getServiceInstance(this, PropFilesManagerService.class);
  }

  public ApplicationConfigService getApplicationConfigService() {
    return serviceInstaceManager.getServiceInstance(this, ApplicationConfigService.class);
  }

  public MetadataService getMetadataService() {
    return serviceInstaceManager.getServiceInstance(this, MetadataService.class);
  }

  public TypeManagementService getTypeManagementService() {
    return serviceInstaceManager.getServiceInstance(this, TypeManagementService.class);
  }

  public PluralService getPluralService() {
    return serviceInstaceManager.getServiceInstance(this, PluralService.class);
  }
}
@Component
@Service
public class AndroidProjectOperationsImpl implements AndroidProjectOperations {

  private static final Logger LOGGER = HandlerUtils.getLogger(AndroidProjectOperationsImpl.class);

  private static final DocumentBuilderFactory FACTORY;

  static {
    FACTORY = DocumentBuilderFactory.newInstance();
    FACTORY.setNamespaceAware(true);
  }

  private static final String ANDROID_NS = "http://schemas.android.com/apk/res/android";
  private static final String XML_EXTENSION = ".xml";
  private static final String WIDGET_PACKAGE = "android.widget";
  private static final String ID_PREFIX = "@+id/";
  private static final String STRINGS = "strings";

  public static DocumentBuilder newDocumentBuilder() {
    try {
      return FACTORY.newDocumentBuilder();
    } catch (final ParserConfigurationException e) {
      throw new IllegalStateException(e);
    }
  }

  @Reference TypeLocationService typeLocationService;
  @Reference TypeManagementService typeManagementService;
  @Reference ProjectOperations projectOperations;
  @Reference JpaOperations jpaOperations;
  @Reference FileManager fileManager;
  @Reference PathResolver pathResolver;
  @Reference AndroidTypeService androidTypeService;
  @Reference MetadataService metadataService;

  @Override
  public boolean isActivityAvailable() {
    return projectOperations.isFocusedProjectAvailable();
  }

  @Override
  public boolean isViewAvailable() {
    return projectOperations.isFocusedProjectAvailable()
        && typeLocationService.findClassesOrInterfaceDetailsWithAnnotation(ROO_ACTIVITY).size() > 0;
  }

  @Override
  public void layout(
      final String name,
      final Dimension height,
      final Dimension width,
      final Orientation orientation) {
    final Document document = newDocumentBuilder().newDocument();

    final Element layoutElem = document.createElement("LinearLayout");
    layoutElem.setAttribute("xmlns:android", ANDROID_NS);
    layoutElem.setAttribute("android:layout_height", height.value());
    layoutElem.setAttribute("android:layout_width", width.value());
    layoutElem.setAttribute("android:orientation", orientation.value());
    document.appendChild(layoutElem);

    final String layoutPath =
        pathResolver.getFocusedIdentifier(Path.ROOT, LAYOUT_PATH + SEP + name + XML_EXTENSION);
    if (fileManager.exists(layoutPath)) {
      LOGGER.severe("Layout '" + name + "' already exists");
      return;
    }
    final MutableFile file = fileManager.createFile(layoutPath);
    final OutputStream output = file.getOutputStream();
    XmlUtils.writeFormattedXml(output, document);
    try {
      output.close();
    } catch (IOException e) {
      LOGGER.severe("Error closing stream: " + e.getMessage());
      return;
    }
    //        fileManager.createOrUpdateTextFileIfRequired(layoutPath,
    //                XmlUtils.nodeToString(document), true);
  }

  @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);
  }

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

  @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 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 permission(final Permission permission) {
    Validate.notNull(permission, "Permission type may not be null");
    final String moduleName = projectOperations.getFocusedModuleName();
    androidTypeService.addPermission(moduleName, permission.permissionName());
  }

  @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);
  }

  @Override
  public void dependency(final AndroidProjectDependency dependency) {
    final String moduleName = projectOperations.getFocusedModuleName();
    final Element configuration = XmlUtils.getConfiguration(getClass());

    for (final Element propertyElement :
        XmlUtils.findElements(
            "/configuration/" + dependency.getTag() + "/properties/property", configuration)) {
      final Property property = new Property(propertyElement);
      projectOperations.addProperty(moduleName, property);
    }

    final List<AndroidDependency> dependencies = new ArrayList<AndroidDependency>();
    for (final Element dependencyElement :
        XmlUtils.findElements(
            "/configuration/" + dependency.getTag() + "/dependencies/dependency", configuration)) {
      dependencies.add(new AndroidDependency(dependencyElement));
    }
    projectOperations.removeDependencies(moduleName, dependencies);
    metadataService.evict(ProjectMetadata.getProjectIdentifier(moduleName));
    androidTypeService.addDependencies(moduleName, dependencies);
  }
}
/**
 * Provides field creation operations support for DTO classes by implementing FieldCreatorProvider.
 *
 * @author Sergio Clares
 * @since 2.0
 */
@Component
@Service
public class DtoFieldCreatorProvider implements FieldCreatorProvider {

  protected static final Logger LOGGER = HandlerUtils.getLogger(FieldCommands.class);

  // ------------ OSGi component attributes ----------------//
  private BundleContext context;

  @Reference private TypeLocationService typeLocationService;
  @Reference private MemberDetailsScanner memberDetailsScanner;
  @Reference private ProjectOperations projectOperations;
  @Reference private TypeManagementService typeManagementService;

  private Converter<JavaType> javaTypeConverter;

  protected void activate(final ComponentContext context) {
    this.context = context.getBundleContext();
  }

  protected void deactivate(final ComponentContext context) {
    this.context = null;
  }

  @Override
  public boolean isValid(JavaType javaType) {
    ClassOrInterfaceTypeDetails cid = typeLocationService.getTypeDetails(javaType);
    if (cid.getAnnotation(RooJavaType.ROO_DTO) != null) {
      return true;
    }

    return false;
  }

  @Override
  public boolean isFieldManagementAvailable() {
    Set<ClassOrInterfaceTypeDetails> dtoClasses =
        typeLocationService.findClassesOrInterfaceDetailsWithAnnotation(RooJavaType.ROO_DTO);
    if (!dtoClasses.isEmpty()) {
      return true;
    }
    return false;
  }

  @Override
  public boolean isFieldEmbeddedAvailable() {
    return false;
  }

  @Override
  public boolean isFieldReferenceAvailable() {
    return false;
  }

  @Override
  public boolean isFieldCollectionAvailable() {
    return false;
  }

  @Override
  public boolean isColumnMandatoryForFieldBoolean(ShellContext shellContext) {
    return false;
  }

  @Override
  public boolean isColumnVisibleForFieldBoolean(ShellContext shellContext) {
    return false;
  }

  @Override
  public boolean isTransientVisibleForFieldBoolean(ShellContext shellContext) {
    return false;
  }

  @Override
  public boolean isAssertFalseVisibleForFieldBoolean(ShellContext shellContext) {
    String param = shellContext.getParameters().get("assertTrue");
    if (param != null) {
      return false;
    }
    return true;
  }

  @Override
  public boolean isAssertTrueVisibleForFieldBoolean(ShellContext shellContext) {
    String param = shellContext.getParameters().get("assertFalse");
    if (param != null) {
      return false;
    }
    return true;
  }

  @Override
  public boolean isColumnMandatoryForFieldDate(ShellContext shellContext) {
    return false;
  }

  @Override
  public boolean isColumnVisibleForFieldDate(ShellContext shellContext) {
    return false;
  }

  @Override
  public boolean isPersistenceTypeVisibleForFieldDate(ShellContext shellContext) {
    return false;
  }

  @Override
  public boolean isTransientVisibleForFieldDate(ShellContext shellContext) {
    return false;
  }

  @Override
  public boolean isFutureVisibleForFieldDate(ShellContext shellContext) {
    String past = shellContext.getParameters().get("past");
    if (past != null) {
      return false;
    }
    return true;
  }

  @Override
  public boolean isPastVisibleForFieldDate(ShellContext shellContext) {
    String past = shellContext.getParameters().get("future");
    if (past != null) {
      return false;
    }
    return true;
  }

  @Override
  public boolean areDateAndTimeFormatVisibleForFieldDate(ShellContext shellContext) {
    String dateTimeFormatPattern = shellContext.getParameters().get("dateTimeFormatPattern");
    if (dateTimeFormatPattern != null) {
      return false;
    }
    return true;
  }

  @Override
  public boolean isDateTimeFormatPatternVisibleForFieldDate(ShellContext shellContext) {
    String dateFormat = shellContext.getParameters().get("dateFormat");
    String timeFormat = shellContext.getParameters().get("timeFormat");
    if (dateFormat == null && timeFormat == null) {
      return true;
    }
    return false;
  }

  @Override
  public boolean isNotNullVisibleForFieldDate(ShellContext shellContext) {
    String antagonistParam = shellContext.getParameters().get("nullRequired");
    if (antagonistParam != null) {
      return false;
    }
    return true;
  }

  @Override
  public boolean isNullRequiredVisibleForFieldDate(ShellContext shellContext) {
    String antagonistParam = shellContext.getParameters().get("notNull");
    if (antagonistParam != null) {
      return false;
    }
    return true;
  }

  @Override
  public boolean isColumnMandatoryForFieldEnum(ShellContext shellContext) {
    return false;
  }

  @Override
  public boolean isColumnVisibleForFieldEnum(ShellContext shellContext) {
    return false;
  }

  @Override
  public boolean isEnumTypeVisibleForFieldEnum(ShellContext shellContext) {
    return false;
  }

  @Override
  public boolean isTransientVisibleForFieldEnum(ShellContext shellContext) {
    return false;
  }

  @Override
  public boolean isNotNullVisibleForFieldEnum(ShellContext shellContext) {
    String antagonistParam = shellContext.getParameters().get("nullRequired");
    if (antagonistParam != null) {
      return false;
    }
    return true;
  }

  @Override
  public boolean isNullRequiredVisibleForFieldEnum(ShellContext shellContext) {
    String antagonistParam = shellContext.getParameters().get("notNull");
    if (antagonistParam != null) {
      return false;
    }
    return true;
  }

  @Override
  public boolean isColumnMandatoryForFieldNumber(ShellContext shellContext) {
    return false;
  }

  @Override
  public boolean isColumnVisibleForFieldNumber(ShellContext shellContext) {
    return false;
  }

  @Override
  public boolean isUniqueVisibleForFieldNumber(ShellContext shellContext) {
    return false;
  }

  @Override
  public boolean isTransientVisibleForFieldNumber(ShellContext shellContext) {
    return false;
  }

  @Override
  public boolean isNullRequiredVisibleForFieldNumber(ShellContext shellContext) {

    // Check if `notNull`is specified
    String notNullParam = shellContext.getParameters().get("notNull");
    if (notNullParam != null) {
      return false;
    }

    // Check if type is primitive
    String typeValue = shellContext.getParameters().get("type");
    if (StringUtils.isNotBlank(typeValue)) {
      JavaType numberType =
          getJavaTypeConverter().convertFromText(typeValue, JavaType.class, "java-number");
      if (numberType.isPrimitive()) {
        return false;
      }
    }
    return true;
  }

  @Override
  public boolean isNotNullVisibleForFieldNumber(ShellContext shellContext) {
    String antagonistParam = shellContext.getParameters().get("nullRequired");
    if (antagonistParam != null) {
      return false;
    }
    return true;
  }

  @Override
  public boolean isColumnMandatoryForFieldReference(ShellContext shellContext) {
    return false;
  }

  @Override
  public boolean isJoinColumnNameVisibleForFieldReference(ShellContext shellContext) {
    return false;
  }

  @Override
  public boolean isReferencedColumnNameVisibleForFieldReference(ShellContext shellContext) {
    return false;
  }

  @Override
  public boolean isFetchVisibleForFieldReference(ShellContext shellContext) {
    return false;
  }

  @Override
  public boolean isCascadeTypeVisibleForFieldReference(ShellContext shellContext) {
    return false;
  }

  @Override
  public boolean isNotNullVisibleForFieldReference(ShellContext shellContext) {
    String antagonistParam = shellContext.getParameters().get("nullRequired");
    if (antagonistParam != null) {
      return false;
    }
    return true;
  }

  @Override
  public boolean isNullRequiredVisibleForFieldReference(ShellContext shellContext) {
    String antagonistParam = shellContext.getParameters().get("notNull");
    if (antagonistParam != null) {
      return false;
    }
    return true;
  }

  @Override
  public boolean isJoinTableMandatoryForFieldSet(ShellContext shellContext) {
    return false;
  }

  @Override
  public boolean areJoinTableParamsVisibleForFieldSet(ShellContext shellContext) {
    return false;
  }

  @Override
  public boolean isMappedByVisibleForFieldSet(ShellContext shellContext) {
    return false;
  }

  @Override
  public boolean isCardinalityVisibleForFieldSet(ShellContext shellContext) {
    return false;
  }

  @Override
  public boolean isFetchVisibleForFieldSet(ShellContext shellContext) {
    return false;
  }

  @Override
  public boolean isJoinTableVisibleForFieldSet(ShellContext shellContext) {
    return false;
  }

  @Override
  public boolean areOptionalParametersVisibleForFieldSet(ShellContext shellContext) {
    return false;
  }

  @Override
  public boolean areJoinTableParamsMandatoryForFieldSet(ShellContext shellContext) {
    return false;
  }

  @Override
  public boolean isJoinColumnNameMandatoryForFieldSet(ShellContext shellContext) {
    return false;
  }

  @Override
  public boolean isJoinColumnNameVisibleForFieldSet(ShellContext shellContext) {
    return false;
  }

  @Override
  public boolean isReferencedColumnNameVisibleForFieldSet(ShellContext shellContext) {
    return false;
  }

  @Override
  public boolean isNotNullVisibleForFieldSet(ShellContext shellContext) {
    String antagonistParam = shellContext.getParameters().get("nullRequired");
    if (antagonistParam != null) {
      return false;
    }
    return true;
  }

  @Override
  public boolean isNullRequiredVisibleForFieldSet(ShellContext shellContext) {
    String antagonistParam = shellContext.getParameters().get("notNull");
    if (antagonistParam != null) {
      return false;
    }
    return true;
  }

  @Override
  public boolean isJoinColumnNameMandatoryForFieldList(ShellContext shellContext) {
    return false;
  }

  @Override
  public boolean isJoinColumnNameVisibleForFieldList(ShellContext shellContext) {
    return false;
  }

  @Override
  public boolean isReferencedColumnNameVisibleForFieldList(ShellContext shellContext) {
    return false;
  }

  @Override
  public boolean areOptionalParametersVisibleForFieldList(ShellContext shellContext) {
    return false;
  }

  @Override
  public boolean areJoinTableParamsVisibleForFieldList(ShellContext shellContext) {
    return false;
  }

  @Override
  public boolean isJoinTableMandatoryForFieldList(ShellContext shellContext) {
    return false;
  }

  @Override
  public boolean areJoinTableParamsMandatoryForFieldList(ShellContext shellContext) {
    return false;
  }

  @Override
  public boolean isMappedByVisibleForFieldList(ShellContext shellContext) {
    return false;
  }

  @Override
  public boolean isCardinalityVisibleForFieldList(ShellContext shellContext) {
    return false;
  }

  @Override
  public boolean isFetchVisibleForFieldList(ShellContext shellContext) {
    return false;
  }

  @Override
  public boolean isJoinTableVisibleForFieldList(ShellContext shellContext) {
    return false;
  }

  @Override
  public boolean isNotNullVisibleForFieldList(ShellContext shellContext) {
    String antagonistParam = shellContext.getParameters().get("nullRequired");
    if (antagonistParam != null) {
      return false;
    }
    return true;
  }

  @Override
  public boolean isNullRequiredVisibleForFieldList(ShellContext shellContext) {
    String antagonistParam = shellContext.getParameters().get("notNull");
    if (antagonistParam != null) {
      return false;
    }
    return true;
  }

  @Override
  public boolean isColumnMandatoryForFieldString(ShellContext shellContext) {
    return false;
  }

  @Override
  public boolean isColumnVisibleForFieldString(ShellContext shellContext) {
    return false;
  }

  @Override
  public boolean isUniqueVisibleForFieldString(ShellContext shellContext) {
    return false;
  }

  @Override
  public boolean isTransientVisibleForFieldString(ShellContext shellContext) {
    return false;
  }

  @Override
  public boolean isLobVisibleForFieldString(ShellContext shellContext) {
    return false;
  }

  @Override
  public boolean isNotNullVisibleForFieldString(ShellContext shellContext) {
    String antagonistParam = shellContext.getParameters().get("nullRequired");
    if (antagonistParam != null) {
      return false;
    }
    return true;
  }

  @Override
  public boolean isNullRequiredVisibleForFieldString(ShellContext shellContext) {
    String antagonistParam = shellContext.getParameters().get("notNull");
    if (antagonistParam != null) {
      return false;
    }
    return true;
  }

  @Override
  public boolean isColumnMandatoryForFieldFile(ShellContext shellContext) {
    return false;
  }

  @Override
  public boolean isColumnVisibleForFieldFile(ShellContext shellContext) {
    return false;
  }

  public boolean isColumnMandatoryForFieldOther(ShellContext shellContext) {
    return false;
  }

  public boolean isColumnVisibleForFieldOther(ShellContext shellContext) {
    return false;
  }

  public boolean isTransientVisibleForFieldOther(ShellContext shellContext) {
    return false;
  }

  @Override
  public boolean isNotNullVisibleForFieldOther(ShellContext shellContext) {
    String antagonistParam = shellContext.getParameters().get("nullRequired");
    if (antagonistParam != null) {
      return false;
    }
    return true;
  }

  @Override
  public boolean isNullRequiredVisibleForFieldOther(ShellContext shellContext) {
    String antagonistParam = shellContext.getParameters().get("notNull");
    if (antagonistParam != null) {
      return false;
    }
    return true;
  }

  @Override
  public void createBooleanField(
      ClassOrInterfaceTypeDetails javaTypeDetails,
      boolean primitive,
      JavaSymbolName fieldName,
      boolean notNull,
      boolean assertFalse,
      boolean assertTrue,
      String column,
      String comment,
      String value,
      boolean permitReservedWords,
      boolean transientModifier) {

    createBooleanField(
        javaTypeDetails,
        primitive,
        fieldName,
        notNull,
        assertFalse,
        assertTrue,
        column,
        comment,
        value,
        permitReservedWords,
        transientModifier,
        null);
  }

  @Override
  public void createBooleanField(
      ClassOrInterfaceTypeDetails javaTypeDetails,
      boolean primitive,
      JavaSymbolName fieldName,
      boolean notNull,
      boolean assertFalse,
      boolean assertTrue,
      String column,
      String comment,
      String value,
      boolean permitReservedWords,
      boolean transientModifier,
      List<AnnotationMetadataBuilder> extraAnnotations) {

    final String physicalTypeIdentifier = javaTypeDetails.getDeclaredByMetadataId();
    final BooleanField fieldDetails =
        new BooleanField(
            physicalTypeIdentifier,
            primitive ? JavaType.BOOLEAN_PRIMITIVE : JavaType.BOOLEAN_OBJECT,
            fieldName);
    fieldDetails.setNotNull(notNull);
    fieldDetails.setAssertFalse(assertFalse);
    fieldDetails.setAssertTrue(assertTrue);
    if (column != null) {
      fieldDetails.setColumn(column);
    }
    if (comment != null) {
      fieldDetails.setComment(comment);
    }
    if (value != null) {
      fieldDetails.setValue(value);
    }

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

    insertField(fieldDetails, permitReservedWords, false);
  }

  @Override
  public void createDateField(
      ClassOrInterfaceTypeDetails javaTypeDetails,
      JavaType fieldType,
      JavaSymbolName fieldName,
      boolean notNull,
      boolean nullRequired,
      boolean future,
      boolean past,
      DateFieldPersistenceType persistenceType,
      String column,
      String comment,
      DateTime dateFormat,
      DateTime timeFormat,
      String pattern,
      String value,
      boolean permitReservedWords,
      boolean transientModifier) {
    createDateField(
        javaTypeDetails,
        fieldType,
        fieldName,
        notNull,
        nullRequired,
        future,
        past,
        persistenceType,
        column,
        comment,
        dateFormat,
        timeFormat,
        pattern,
        value,
        permitReservedWords,
        transientModifier,
        null);
  }

  @Override
  public void createDateField(
      ClassOrInterfaceTypeDetails javaTypeDetails,
      JavaType fieldType,
      JavaSymbolName fieldName,
      boolean notNull,
      boolean nullRequired,
      boolean future,
      boolean past,
      DateFieldPersistenceType persistenceType,
      String column,
      String comment,
      DateTime dateFormat,
      DateTime timeFormat,
      String pattern,
      String value,
      boolean permitReservedWords,
      boolean transientModifier,
      List<AnnotationMetadataBuilder> extraAnnotations) {
    final String physicalTypeIdentifier = javaTypeDetails.getDeclaredByMetadataId();
    final DateField fieldDetails = new DateField(physicalTypeIdentifier, fieldType, fieldName);
    fieldDetails.setNotNull(notNull);
    fieldDetails.setNullRequired(nullRequired);
    fieldDetails.setFuture(future);
    fieldDetails.setPast(past);
    if (JdkJavaType.isDateField(fieldType)) {
      fieldDetails.setPersistenceType(persistenceType);
    }
    if (column != null) {
      fieldDetails.setColumn(column);
    }
    if (comment != null) {
      fieldDetails.setComment(comment);
    }
    if (dateFormat != null) {
      fieldDetails.setDateFormat(dateFormat);
    }
    if (timeFormat != null) {
      fieldDetails.setTimeFormat(timeFormat);
    }
    if (pattern != null) {
      fieldDetails.setPattern(pattern);
    }
    if (value != null) {
      fieldDetails.setValue(value);
    }

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

    insertField(fieldDetails, permitReservedWords, false);
  }

  @Override
  public void createEmbeddedField(
      JavaType typeName,
      JavaType fieldType,
      JavaSymbolName fieldName,
      boolean permitReservedWords) {
    createEmbeddedField(typeName, fieldType, fieldName, permitReservedWords, null);
  }

  @Override
  public void createEmbeddedField(
      JavaType typeName,
      JavaType fieldType,
      JavaSymbolName fieldName,
      boolean permitReservedWords,
      List<AnnotationMetadataBuilder> extraAnnotations) {
    throw new IllegalArgumentException(
        "'field embedded' command is not available for DTO classes.");
  }

  @Override
  public void createEnumField(
      ClassOrInterfaceTypeDetails cid,
      JavaType fieldType,
      JavaSymbolName fieldName,
      String column,
      boolean notNull,
      boolean nullRequired,
      EnumType enumType,
      String comment,
      boolean permitReservedWords,
      boolean transientModifier) {
    createEnumField(
        cid,
        fieldType,
        fieldName,
        column,
        notNull,
        nullRequired,
        enumType,
        comment,
        permitReservedWords,
        transientModifier,
        null);
  }

  @Override
  public void createEnumField(
      ClassOrInterfaceTypeDetails cid,
      JavaType fieldType,
      JavaSymbolName fieldName,
      String column,
      boolean notNull,
      boolean nullRequired,
      EnumType enumType,
      String comment,
      boolean permitReservedWords,
      boolean transientModifier,
      List<AnnotationMetadataBuilder> extraAnnotations) {

    final String physicalTypeIdentifier = cid.getDeclaredByMetadataId();
    final EnumField fieldDetails = new EnumField(physicalTypeIdentifier, fieldType, fieldName);
    if (column != null) {
      fieldDetails.setColumn(column);
    }
    fieldDetails.setNotNull(notNull);
    fieldDetails.setNullRequired(nullRequired);
    if (enumType != null) {
      fieldDetails.setEnumType(enumType);
    }
    if (comment != null) {
      fieldDetails.setComment(comment);
    }

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

    insertField(fieldDetails, permitReservedWords, false);
  }

  @Override
  public void createNumericField(
      ClassOrInterfaceTypeDetails javaTypeDetails,
      JavaType fieldType,
      boolean primitive,
      Set<String> legalNumericPrimitives,
      JavaSymbolName fieldName,
      boolean notNull,
      boolean nullRequired,
      String decimalMin,
      String decimalMax,
      Integer digitsInteger,
      Integer digitsFraction,
      Long min,
      Long max,
      String column,
      String comment,
      boolean unique,
      String value,
      boolean permitReservedWords,
      boolean transientModifier) {

    createNumericField(
        javaTypeDetails,
        fieldType,
        primitive,
        legalNumericPrimitives,
        fieldName,
        notNull,
        nullRequired,
        decimalMin,
        decimalMax,
        digitsInteger,
        digitsFraction,
        min,
        max,
        column,
        comment,
        unique,
        value,
        permitReservedWords,
        transientModifier,
        null);
  }

  @Override
  public void createNumericField(
      ClassOrInterfaceTypeDetails javaTypeDetails,
      JavaType fieldType,
      boolean primitive,
      Set<String> legalNumericPrimitives,
      JavaSymbolName fieldName,
      boolean notNull,
      boolean nullRequired,
      String decimalMin,
      String decimalMax,
      Integer digitsInteger,
      Integer digitsFraction,
      Long min,
      Long max,
      String column,
      String comment,
      boolean unique,
      String value,
      boolean permitReservedWords,
      boolean transientModifier,
      List<AnnotationMetadataBuilder> extraAnnotations) {

    final String physicalTypeIdentifier = javaTypeDetails.getDeclaredByMetadataId();
    if (primitive && legalNumericPrimitives.contains(fieldType.getFullyQualifiedTypeName())) {
      fieldType =
          new JavaType(fieldType.getFullyQualifiedTypeName(), 0, DataType.PRIMITIVE, null, null);
    }
    final NumericField fieldDetails =
        new NumericField(physicalTypeIdentifier, fieldType, fieldName);
    fieldDetails.setNotNull(notNull);
    fieldDetails.setNullRequired(nullRequired);
    if (decimalMin != null) {
      fieldDetails.setDecimalMin(decimalMin);
    }
    if (decimalMax != null) {
      fieldDetails.setDecimalMax(decimalMax);
    }
    if (digitsInteger != null) {
      fieldDetails.setDigitsInteger(digitsInteger);
    }
    if (digitsFraction != null) {
      fieldDetails.setDigitsFraction(digitsFraction);
    }
    if (min != null) {
      fieldDetails.setMin(min);
    }
    if (max != null) {
      fieldDetails.setMax(max);
    }
    if (column != null) {
      fieldDetails.setColumn(column);
    }
    if (comment != null) {
      fieldDetails.setComment(comment);
    }
    if (unique) {
      fieldDetails.setUnique(true);
    }
    if (value != null) {
      fieldDetails.setValue(value);
    }

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

    Validate.isTrue(
        fieldDetails.isDigitsSetCorrectly(),
        "Must specify both --digitsInteger and --digitsFractional for @Digits to be added");

    insertField(fieldDetails, permitReservedWords, false);
  }

  @Override
  public void createReferenceField(
      JavaType typeName,
      JavaType fieldType,
      JavaSymbolName fieldName,
      boolean aggregation,
      JavaSymbolName mappedBy,
      Cascade[] cascadeType,
      boolean notNull,
      String joinColumnName,
      String referencedColumnName,
      Fetch fetch,
      String comment,
      boolean permitReservedWords,
      Boolean orphanRemoval,
      boolean isForce) {
    throw new IllegalArgumentException(
        "'field reference' command is not available for DTO classes.");
  }

  @Override
  public void createSetField(
      JavaType typeName,
      JavaType fieldType,
      JavaSymbolName fieldName,
      Cardinality cardinality,
      Cascade[] cascadeType,
      boolean notNull,
      Integer sizeMin,
      Integer sizeMax,
      JavaSymbolName mappedBy,
      Fetch fetch,
      String comment,
      String joinColumn,
      String referencedColumn,
      String joinTable,
      String joinColumns,
      String referencedColumns,
      String inverseJoinColumns,
      String inverseReferencedColumns,
      boolean permitReservedWords,
      Boolean aggregation,
      Boolean orphanRemoval,
      boolean isForce) {
    throw new IllegalArgumentException("'field set' command is not available for DTO classes.");
  }

  @Override
  public void createListField(
      JavaType typeName,
      JavaType fieldType,
      JavaSymbolName fieldName,
      Cardinality cardinality,
      Cascade[] cascadeType,
      boolean notNull,
      Integer sizeMin,
      Integer sizeMax,
      JavaSymbolName mappedBy,
      Fetch fetch,
      String comment,
      String joinColumn,
      String referencedColumn,
      String joinTable,
      String joinColumns,
      String referencedColumns,
      String inverseJoinColumns,
      String inverseReferencedColumns,
      boolean permitReservedWords,
      Boolean aggregation,
      Boolean orphanRemoval,
      boolean isForce) {
    throw new IllegalArgumentException("'field list' command is not available for DTO classes.");
  }

  @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) {

    createStringField(
        cid,
        fieldName,
        notNull,
        nullRequired,
        decimalMin,
        decimalMax,
        sizeMin,
        sizeMax,
        regexp,
        column,
        comment,
        unique,
        value,
        lob,
        permitReservedWords,
        transientModifier,
        null);
  }

  @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);
  }

  @Override
  public void createFileField(
      ClassOrInterfaceTypeDetails cid,
      JavaSymbolName fieldName,
      UploadedFileContentType contentType,
      boolean autoUpload,
      boolean notNull,
      String column,
      boolean permitReservedWords) {
    createFileField(
        cid, fieldName, contentType, autoUpload, notNull, column, permitReservedWords, null);
  }

  @Override
  public void createFileField(
      ClassOrInterfaceTypeDetails cid,
      JavaSymbolName fieldName,
      UploadedFileContentType contentType,
      boolean autoUpload,
      boolean notNull,
      String column,
      boolean permitReservedWords,
      List<AnnotationMetadataBuilder> extraAnnotations) {

    final String physicalTypeIdentifier = cid.getDeclaredByMetadataId();
    final UploadedFileField fieldDetails =
        new UploadedFileField(physicalTypeIdentifier, fieldName, contentType);
    fieldDetails.setAutoUpload(autoUpload);
    fieldDetails.setNotNull(notNull);
    if (column != null) {
      fieldDetails.setColumn(column);
    }

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

    insertField(fieldDetails, permitReservedWords, false);
  }

  @Override
  public void createOtherField(
      ClassOrInterfaceTypeDetails cid,
      JavaType fieldType,
      JavaSymbolName fieldName,
      boolean notNull,
      boolean nullRequired,
      String comment,
      String column,
      boolean permitReservedWords,
      boolean transientModifier) {
    createOtherField(
        cid,
        fieldType,
        fieldName,
        notNull,
        nullRequired,
        comment,
        column,
        permitReservedWords,
        transientModifier,
        null);
  }

  @Override
  public void createOtherField(
      ClassOrInterfaceTypeDetails cid,
      JavaType fieldType,
      JavaSymbolName fieldName,
      boolean notNull,
      boolean nullRequired,
      String comment,
      String column,
      boolean permitReservedWords,
      boolean transientModifier,
      List<AnnotationMetadataBuilder> extraAnnotations) {

    final String physicalTypeIdentifier = cid.getDeclaredByMetadataId();
    final FieldDetails fieldDetails =
        new FieldDetails(physicalTypeIdentifier, fieldType, fieldName);
    fieldDetails.setNotNull(notNull);
    fieldDetails.setNullRequired(nullRequired);
    if (comment != null) {
      fieldDetails.setComment(comment);
    }
    if (column != null) {
      fieldDetails.setColumn(column);
    }

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

    insertField(fieldDetails, permitReservedWords, false);
  }

  public void insertField(
      final FieldDetails fieldDetails,
      final boolean permitReservedWords,
      final boolean transientModifier) {

    String module = null;
    if (!permitReservedWords) {
      ReservedWords.verifyReservedWordsNotPresent(fieldDetails.getFieldName());
      if (fieldDetails.getColumn() != null) {
        ReservedWords.verifyReservedWordsNotPresent(fieldDetails.getColumn());
      }
    }

    final List<AnnotationMetadataBuilder> annotations = fieldDetails.getInitedAnnotations();
    fieldDetails.decorateAnnotationsList(annotations);
    fieldDetails.setAnnotations(annotations);

    if (fieldDetails.getFieldType() != null) {
      module = fieldDetails.getFieldType().getModule();
    }

    fieldDetails.setModifiers(Modifier.PRIVATE);

    String initializer = null;
    if (fieldDetails instanceof CollectionField) {
      final CollectionField collectionField = (CollectionField) fieldDetails;
      module = collectionField.getGenericParameterTypeName().getModule();
      initializer = "new " + collectionField.getInitializer() + "()";
    } else if (fieldDetails instanceof DateField
        && fieldDetails.getFieldName().getSymbolName().equals("created")) {
      initializer = "new Date()";
    }

    // Format the passed-in comment (if given)
    formatFieldComment(fieldDetails);

    final FieldMetadataBuilder fieldBuilder = new FieldMetadataBuilder(fieldDetails);
    fieldBuilder.setFieldInitializer(initializer);
    typeManagementService.addField(fieldBuilder.build());

    if (module != null) {
      projectOperations.addModuleDependency(module);
    }
  }

  public void formatFieldComment(FieldDetails fieldDetails) {
    // If a comment was defined, we need to format it
    if (fieldDetails.getComment() != null) {

      // First replace all "" with the proper escape sequence \"
      String unescapedMultiLineComment = fieldDetails.getComment().replaceAll("\"\"", "\\\\\"");

      // Then unescape all characters
      unescapedMultiLineComment = StringEscapeUtils.unescapeJava(unescapedMultiLineComment);

      CommentFormatter commentFormatter = new CommentFormatter();
      String javadocComment = commentFormatter.formatStringAsJavadoc(unescapedMultiLineComment);

      fieldDetails.setComment(commentFormatter.format(javadocComment, 1));
    }
  }

  @Override
  public List<String> getFieldSetTypeAllPossibleValues(ShellContext shellContext) {
    // Get current value of class
    String currentText = shellContext.getParameters().get("type");

    List<String> allPossibleValues = new ArrayList<String>();

    // Getting all existing entities
    Set<ClassOrInterfaceTypeDetails> entitiesInProject =
        typeLocationService.findClassesOrInterfaceDetailsWithAnnotation(RooJavaType.ROO_JPA_ENTITY);
    for (ClassOrInterfaceTypeDetails entity : entitiesInProject) {
      String name = replaceTopLevelPackageString(entity, currentText);
      if (!allPossibleValues.contains(name)) {
        allPossibleValues.add(name);
      }
    }

    // Getting all existing dtos
    Set<ClassOrInterfaceTypeDetails> dtosInProject =
        typeLocationService.findClassesOrInterfaceDetailsWithAnnotation(RooJavaType.ROO_DTO);
    for (ClassOrInterfaceTypeDetails dto : dtosInProject) {
      String name = replaceTopLevelPackageString(dto, currentText);
      if (!allPossibleValues.contains(name)) {
        allPossibleValues.add(name);
      }
    }

    return allPossibleValues;
  }

  @Override
  public List<String> getFieldListTypeAllPossibleValues(ShellContext shellContext) {
    // Get current value of class
    String currentText = shellContext.getParameters().get("type");

    List<String> allPossibleValues = new ArrayList<String>();

    // Getting all existing entities
    Set<ClassOrInterfaceTypeDetails> entitiesInProject =
        typeLocationService.findClassesOrInterfaceDetailsWithAnnotation(RooJavaType.ROO_JPA_ENTITY);
    for (ClassOrInterfaceTypeDetails entity : entitiesInProject) {
      String name = replaceTopLevelPackageString(entity, currentText);
      if (!allPossibleValues.contains(name)) {
        allPossibleValues.add(name);
      }
    }

    // Getting all existing dtos
    Set<ClassOrInterfaceTypeDetails> dtosInProject =
        typeLocationService.findClassesOrInterfaceDetailsWithAnnotation(RooJavaType.ROO_DTO);
    for (ClassOrInterfaceTypeDetails dto : dtosInProject) {
      String name = replaceTopLevelPackageString(dto, currentText);
      if (!allPossibleValues.contains(name)) {
        allPossibleValues.add(name);
      }
    }

    return allPossibleValues;
  }

  @Override
  public List<String> getFieldEmbeddedAllPossibleValues(ShellContext shellContext) {

    // field embedded not used for DTO's
    return new ArrayList<String>();
  }

  /**
   * Replaces a JavaType fullyQualifiedName for a shorter name using '~' for TopLevelPackage
   *
   * @param cid ClassOrInterfaceTypeDetails of a JavaType
   * @param currentText String current text for option value
   * @return the String representing a JavaType with its name shortened
   */
  private String replaceTopLevelPackageString(ClassOrInterfaceTypeDetails cid, String currentText) {
    String javaTypeFullyQualilfiedName = cid.getType().getFullyQualifiedTypeName();
    String javaTypeString = "";
    String topLevelPackageString = "";

    // Add module value to topLevelPackage when necessary
    if (StringUtils.isNotBlank(cid.getType().getModule())
        && !cid.getType().getModule().equals(projectOperations.getFocusedModuleName())) {

      // Target module is not focused
      javaTypeString = cid.getType().getModule().concat(LogicalPath.MODULE_PATH_SEPARATOR);
      topLevelPackageString =
          projectOperations
              .getTopLevelPackage(cid.getType().getModule())
              .getFullyQualifiedPackageName();
    } else if (StringUtils.isNotBlank(cid.getType().getModule())
        && cid.getType().getModule().equals(projectOperations.getFocusedModuleName())
        && (currentText.startsWith(cid.getType().getModule())
            || cid.getType().getModule().startsWith(currentText))
        && StringUtils.isNotBlank(currentText)) {

      // Target module is focused but user wrote it
      javaTypeString = cid.getType().getModule().concat(LogicalPath.MODULE_PATH_SEPARATOR);
      topLevelPackageString =
          projectOperations
              .getTopLevelPackage(cid.getType().getModule())
              .getFullyQualifiedPackageName();
    } else {

      // Not multimodule project
      topLevelPackageString =
          projectOperations.getFocusedTopLevelPackage().getFullyQualifiedPackageName();
    }

    // Autocomplete with abbreviate or full qualified mode
    String auxString =
        javaTypeString.concat(
            StringUtils.replace(javaTypeFullyQualilfiedName, topLevelPackageString, "~"));
    if ((StringUtils.isBlank(currentText) || auxString.startsWith(currentText))
        && StringUtils.contains(javaTypeFullyQualilfiedName, topLevelPackageString)) {

      // Value is for autocomplete only or user wrote abbreviate value
      javaTypeString = auxString;
    } else {

      // Value could be for autocomplete or for validation
      javaTypeString = String.format("%s%s", javaTypeString, javaTypeFullyQualilfiedName);
    }

    return javaTypeString;
  }

  @SuppressWarnings("unchecked")
  public Converter<JavaType> getJavaTypeConverter() {
    if (javaTypeConverter == null) {

      // Get all Services implement JavaTypeConverter interface
      try {
        ServiceReference<?>[] references =
            this.context.getAllServiceReferences(Converter.class.getName(), null);

        for (ServiceReference<?> ref : references) {
          Converter<?> converter = (Converter<?>) this.context.getService(ref);
          if (converter.supports(JavaType.class, PROJECT)) {
            javaTypeConverter = (Converter<JavaType>) converter;
            return javaTypeConverter;
          }
        }

        return null;

      } catch (InvalidSyntaxException e) {
        LOGGER.warning("ERROR: Cannot load JavaTypeConverter on JpaFieldCreatorProvider.");
        return null;
      }
    } else {
      return javaTypeConverter;
    }
  }
}
/**
 * Implementation of {@link MavenOperations}.
 *
 * @author Ben Alex
 * @author Alan Stewart
 * @since 1.0
 */
@Component
@Service
public class MavenOperationsImpl extends AbstractProjectOperations implements MavenOperations {

  // Constants
  private static final Logger LOGGER = HandlerUtils.getLogger(MavenOperationsImpl.class);

  // Fields
  @Reference private FileManager fileManager;
  @Reference private ProcessManager processManager;

  public boolean isCreateProjectAvailable() {
    return !isProjectAvailable();
  }

  public String getProjectRoot() {
    return pathResolver.getRoot(Path.ROOT);
  }

  public void createProject(
      final JavaPackage topLevelPackage,
      final String projectName,
      final Integer majorJavaVersion,
      final GAV parentPom,
      final PackagingType packagingType) {
    Assert.isTrue(isCreateProjectAvailable(), "Project creation is unavailable at this time");
    final String javaVersion = getJavaVersion(majorJavaVersion);
    packagingType.createArtifacts(topLevelPackage, projectName, javaVersion, parentPom);
  }

  /**
   * Returns the project's target Java version in POM format
   *
   * @param majorJavaVersion the major version provided by the user; can be <code>null</code> to
   *     auto-detect it
   * @return a non-blank string
   */
  private final String getJavaVersion(final Integer majorJavaVersion) {
    if (majorJavaVersion != null && majorJavaVersion >= 5 && majorJavaVersion <= 7) {
      return "1." + majorJavaVersion;
    }

    // No valid version given; detect the major Java version to use
    final String ver = System.getProperty("java.version");
    if (ver.contains("1.7.")) {
      return "1.7";
    }
    if (ver.contains("1.6.")) {
      return "1.6";
    }
    // To be running Roo they must be on Java 5 or above
    return "1.5";
  }

  public void executeMvnCommand(final String extra) throws IOException {
    final File root = new File(getProjectRoot());
    Assert.isTrue(
        root.isDirectory() && root.exists(),
        "Project root does not currently exist as a directory ('" + root.getCanonicalPath() + "')");

    final String cmd = (File.separatorChar == '\\' ? "mvn.bat " : "mvn ") + extra;
    final Process p = Runtime.getRuntime().exec(cmd, null, root);

    // Ensure separate threads are used for logging, as per ROO-652
    final LoggingInputStream input = new LoggingInputStream(p.getInputStream(), processManager);
    final LoggingInputStream errors = new LoggingInputStream(p.getErrorStream(), processManager);

    p.getOutputStream()
        .close(); // Close OutputStream to avoid blocking by Maven commands that expect input, as
                  // per ROO-2034
    input.start();
    errors.start();

    try {
      if (p.waitFor() != 0) {
        LOGGER.warning("The command '" + cmd + "' did not complete successfully");
      }
    } catch (final InterruptedException e) {
      throw new IllegalStateException(e);
    }
  }

  private static class LoggingInputStream extends Thread {

    // Fields
    private final BufferedReader reader;
    private final ProcessManager processManager;

    /**
     * Constructor
     *
     * @param inputStream
     * @param processManager
     */
    public LoggingInputStream(final InputStream inputStream, final ProcessManager processManager) {
      this.reader = new BufferedReader(new InputStreamReader(inputStream));
      this.processManager = processManager;
    }

    @Override
    public void run() {
      ActiveProcessManager.setActiveProcessManager(processManager);
      String line;
      try {
        while ((line = reader.readLine()) != null) {
          if (line.startsWith("[ERROR]")) {
            LOGGER.severe(line);
          } else if (line.startsWith("[WARNING]")) {
            LOGGER.warning(line);
          } else {
            LOGGER.info(line);
          }
        }
      } catch (final IOException e) {
        if (e.getMessage().contains("No such file or directory")
            || // For *nix/Mac
            e.getMessage().contains("CreateProcess error=2")) { // For Windows
          LOGGER.severe(
              "Could not locate Maven executable; please ensure mvn command is in your path");
        }
      } finally {
        IOUtils.closeQuietly(reader);
        ActiveProcessManager.clearActiveProcessManager();
      }
    }
  }

  public boolean isCreateModuleAvailable() {
    // TODO Waiting on JTT's work for ROO-120 to find out if the currently focussed module is
    // POM-packaged
    return false;
  }

  public void createModule(
      final JavaPackage topLevelPackage,
      final String name,
      final GAV parent,
      final PackagingType packagingType) {
    Assert.isTrue(isCreateModuleAvailable(), "Cannot create modules at this time");
    final String moduleName = StringUtils.defaultIfEmpty(name, topLevelPackage.getLastElement());
    final GAV module =
        new GAV(topLevelPackage.getFullyQualifiedPackageName(), moduleName, parent.getVersion());
    final ProjectMetadata project = getProjectMetadata();
    // TODO create or update "modules" element of parent module's POM
    // Create the new module's directory, named by its artifactId (Maven standard practice)
    fileManager.createDirectory(moduleName);
    // Focus the new module so that artifacts created below go to the correct path(s)
    focus(module);
    packagingType.createArtifacts(topLevelPackage, name, "${java.version}", parent);
  }

  public void focus(final GAV module) {
    Assert.notNull(module, "Specify the module to focus on");
    throw new UnsupportedOperationException(
        "Module focussing not implemented yet"); // TODO by JTT for ROO-120
  }
}
/**
 * @author Juan Carlos García
 * @since 2.0
 */
public class EntitiesServlet extends HttpServlet {

  private BundleContext context;
  private static final Logger LOGGER = HandlerUtils.getLogger(EntitiesServlet.class);

  private Shell shell;
  private TypeLocationService typeLocationService;
  private ProjectOperations projectOperations;

  public EntitiesServlet(BundleContext bContext) {
    this.context = bContext;
  }

  public void init() throws ServletException {}

  /**
   * Method to handle GET method request of /entities service
   *
   * @param request
   * @param response
   */
  public void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
    // Set response content type
    response.setContentType("application/json");
    response.setCharacterEncoding("utf-8");

    PrintWriter out = response.getWriter();

    String method = request.getPathInfo();

    if (method == null || method.equals("/")) {
      getAllEntities(out);
    } else {
      // Removing URL / from entity name
      getEntityDetails(method.substring(1), out);
    }
  }

  /**
   * Method to handle POST request of /entities service
   *
   * @param request
   * @param response
   */
  public void doPost(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
    // Set response content type
    response.setContentType("application/json");
    response.setCharacterEncoding("utf-8");

    PrintWriter out = response.getWriter();

    // Getting params
    String entityName = request.getParameter("entityName").trim();
    String extendsType = request.getParameter("extends");
    String isAbstract = request.getParameter("isAbstract");

    // Execute entity command
    // TODO: Replace with JPA command
    String entityCommand = "entity jpa --class " + entityName;

    // Adding abstract if needed
    if (isAbstract != null) {
      entityCommand = entityCommand.concat(" --abstract ").concat(isAbstract);
    }

    // Adding extends if needed
    if (extendsType != null && !"".equals(extendsType)) {
      entityCommand = entityCommand.concat(" --extends ").concat(extendsType);
    }

    boolean status = getShell().executeCommand(entityCommand);

    String message = "Created entity '" + entityName + "'!";
    if (!status) {
      message = "Error creating entity.";
    }

    // Returning JSON
    ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter();
    String json = ow.writeValueAsString(new Status(status, message));
    out.println(json);
  }

  public void destroy() {
    // do nothing.
  }

  /**
   * Method to obtain all created entities on current project
   *
   * @param out
   */
  private void getAllEntities(PrintWriter out) throws ServletException, IOException {

    List<Entity> allEntities = new ArrayList<Entity>();

    // Getting all entities
    Set<ClassOrInterfaceTypeDetails> entities =
        getTypeLocationService()
            .findClassesOrInterfaceDetailsWithAnnotation(new JavaType(RooJavaBean.class));

    Iterator<ClassOrInterfaceTypeDetails> it = entities.iterator();

    while (it.hasNext()) {
      // Getting entity
      ClassOrInterfaceTypeDetails entity = it.next();
      // Entity Name
      String entityName = entity.getName().getFullyQualifiedTypeName();
      String topLevelPackage = getProjectOperations().getFocusedTopLevelPackage().toString();

      // Replacing topLevelPackage with ~
      entityName = entityName.replace(topLevelPackage, "~");
      List<Field> entityFields = new ArrayList<Field>();
      for (FieldMetadata field : entity.getDeclaredFields()) {

        // Getting fields values
        String fieldName = field.getFieldName().getSymbolName();
        String fieldType = field.getFieldType().getSimpleTypeName();

        // Getting referenced class
        String referencedClass = "";
        if (field.getFieldType().getSimpleTypeName().equals("Set")
            || field.getFieldType().getSimpleTypeName().equals("List")) {
          referencedClass = field.getFieldType().getParameters().get(0).getSimpleTypeName();
        } else {
          AnnotationMetadata manyToOneAnnotation =
              field.getAnnotation(new JavaType("javax.persistence.ManyToOne"));
          if (manyToOneAnnotation != null) {
            referencedClass = field.getFieldType().getFullyQualifiedTypeName();
            referencedClass = referencedClass.replace(topLevelPackage, "~");
            fieldType = "Reference";
          }
        }

        // Creating entityField Object
        Field entityField = new Field(fieldName, fieldType, referencedClass);
        // Adding field to entity
        entityFields.add(entityField);
      }

      // Checking if current entity is abstract
      boolean isAbstractEntity = entity.isAbstract();

      // Adding extends type
      List<String> extendsTypes = new ArrayList<String>();
      for (JavaType extendsType : entity.getExtendsTypes()) {
        extendsTypes.add(extendsType.getFullyQualifiedTypeName().replace(topLevelPackage, "~"));
      }

      // Creating Entity Object
      allEntities.add(new Entity(entityName, extendsTypes, isAbstractEntity, entityFields));
    }

    // Returning JSON
    ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter();
    String json = ow.writeValueAsString(allEntities);
    out.println(json);
  }

  /**
   * Method to obtain details of some entity
   *
   * @param out
   */
  private void getEntityDetails(String entityName, PrintWriter out)
      throws ServletException, IOException {

    List<Entity> allEntities = new ArrayList<Entity>();

    // Getting all entities
    Set<ClassOrInterfaceTypeDetails> entities =
        getTypeLocationService()
            .findClassesOrInterfaceDetailsWithAnnotation(new JavaType(RooJavaBean.class));

    Iterator<ClassOrInterfaceTypeDetails> it = entities.iterator();

    while (it.hasNext()) {
      // Getting entity
      ClassOrInterfaceTypeDetails entity = it.next();

      // Entity Name
      String currentEntityName = entity.getName().getFullyQualifiedTypeName();
      String topLevelPackage = getProjectOperations().getFocusedTopLevelPackage().toString();

      // Replacing topLevelPackage with ~
      currentEntityName = currentEntityName.replace(topLevelPackage, "~");

      // Getting info only about selected entity
      if (entityName.equals(currentEntityName)) {
        List<Field> entityFields = new ArrayList<Field>();
        for (FieldMetadata field : entity.getDeclaredFields()) {

          // Getting fields values
          String fieldName = field.getFieldName().getSymbolName();
          String fieldType = field.getFieldType().getSimpleTypeName();

          // Getting referenced class
          String referencedClass = "";
          if (field.getFieldType().getSimpleTypeName().equals("Set")
              || field.getFieldType().getSimpleTypeName().equals("List")) {
            referencedClass = field.getFieldType().getParameters().get(0).getSimpleTypeName();
          } else {
            AnnotationMetadata manyToOneAnnotation =
                field.getAnnotation(new JavaType("javax.persistence.ManyToOne"));
            if (manyToOneAnnotation != null) {
              referencedClass = field.getFieldType().getFullyQualifiedTypeName();
              referencedClass = referencedClass.replace(topLevelPackage, "~");
              fieldType = "Reference";
            }
          }

          // Creating entityField Object
          Field entityField = new Field(fieldName, fieldType, referencedClass);
          // Adding field to entity
          entityFields.add(entityField);
        }

        // Checking if current entity is abstract
        boolean isAbstractEntity = entity.isAbstract();

        // Adding extends type
        List<String> extendsTypes = new ArrayList<String>();
        for (JavaType extendsType : entity.getExtendsTypes()) {
          extendsTypes.add(extendsType.getFullyQualifiedTypeName().replace(topLevelPackage, "~"));
        }

        // Creating Entity Object
        allEntities.add(
            new Entity(currentEntityName, extendsTypes, isAbstractEntity, entityFields));
      }
    }

    // Returning JSON
    ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter();
    String json = ow.writeValueAsString(allEntities);
    out.println(json);
  }

  /**
   * Method to get TypeLocationService Service implementation
   *
   * @return
   */
  public TypeLocationService getTypeLocationService() {
    if (typeLocationService == null) {
      // Get all TypeLocationService implement Shell interface
      try {
        ServiceReference<?>[] references =
            context.getAllServiceReferences(TypeLocationService.class.getName(), null);

        for (ServiceReference<?> ref : references) {
          typeLocationService = (TypeLocationService) context.getService(ref);
          return typeLocationService;
        }

        return null;

      } catch (InvalidSyntaxException e) {
        LOGGER.warning("Cannot load TypeLocationService on ProjectConfigurationController.");
        return null;
      }
    } else {
      return typeLocationService;
    }
  }

  /**
   * Method to get Shell Service implementation
   *
   * @return
   */
  public Shell getShell() {
    if (shell == null) {
      // Get all Services implement Shell interface
      try {
        ServiceReference<?>[] references =
            context.getAllServiceReferences(Shell.class.getName(), null);

        for (ServiceReference<?> ref : references) {
          shell = (Shell) context.getService(ref);
          return shell;
        }

        return null;

      } catch (InvalidSyntaxException e) {
        LOGGER.warning("Cannot load Shell on ProjectConfigurationController.");
        return null;
      }
    } else {
      return shell;
    }
  }

  /**
   * Method to get ProjectOperations Service implementation
   *
   * @return
   */
  public ProjectOperations getProjectOperations() {
    if (projectOperations == null) {
      // Get all Services implement ProjectOperations interface
      try {
        ServiceReference<?>[] references =
            this.context.getAllServiceReferences(ProjectOperations.class.getName(), null);

        for (ServiceReference<?> ref : references) {
          return (ProjectOperations) this.context.getService(ref);
        }

        return null;

      } catch (InvalidSyntaxException e) {
        LOGGER.warning("Cannot load ProjectOperations on EntitiesController.");
        return null;
      }
    } else {
      return projectOperations;
    }
  }
}