<commit_before>/**
 * Copyright 2013 Red Hat, Inc. and/or its affiliates.
 *
 * Licensed under the Eclipse Public License version 1.0, available at
 * http://www.eclipse.org/legal/epl-v10.html
 */

package org.jboss.forge.addon.shell.ui;

import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.jboss.aesh.console.AeshConsole;
import org.jboss.aesh.console.Buffer;
import org.jboss.aesh.console.command.CommandOperation;
import org.jboss.aesh.console.command.invocation.CommandInvocation;
import org.jboss.aesh.terminal.Key;
import org.jboss.forge.addon.convert.Converter;
import org.jboss.forge.addon.convert.ConverterFactory;
import org.jboss.forge.addon.ui.hints.InputType;
import org.jboss.forge.addon.ui.input.InputComponent;
import org.jboss.forge.addon.ui.input.ManyValued;
import org.jboss.forge.addon.ui.input.SelectComponent;
import org.jboss.forge.addon.ui.input.SingleValued;
import org.jboss.forge.addon.ui.input.UIPrompt;
import org.jboss.forge.addon.ui.util.InputComponents;
import org.jboss.forge.furnace.util.Lists;
import org.jboss.forge.furnace.util.Strings;

/**
 * Implementation of {@link UIPrompt}
 *
 * @author <a href="<EMAIL>"><NAME></a>
 */
public class ShellUIPromptImpl implements UIPrompt
{
   private final ShellContext context;
   private final AeshConsole console;
   private final ConverterFactory converterFactory;
   private final CommandInvocation commandInvocation;

   public ShellUIPromptImpl(ShellContext context, ConverterFactory converterFactory)
   {
      this.context = context;
      this.converterFactory = converterFactory;
      this.console = context.getProvider().getConsole();
      this.commandInvocation = (CommandInvocation) context.getAttributeMap()
               .get(CommandInvocation.class);
   }

   @Override
   public String prompt(String message)
   {
      if (isAcceptDefaultsEnabled())
      {
         return null;
      }
      PrintStream out = console.getShell().out();
      out.print(message + " ");
      String output = readInput(out, true);
      out.println();
      return output;
   }

   @Override
   public String promptSecret(String message)
   {
      if (isAcceptDefaultsEnabled())
      {
         return null;
      }
      PrintStream out = console.getShell().out();
      out.print(message + " ");
      String output = readInput(out, false);
      out.println();
      return output;
   }

   @Override
   public boolean promptBoolean(String message)
   {
      return promptBoolean(message, true);
   }

   @Override
   public boolean promptBoolean(String message, boolean defaultValue)
   {
      if (isAcceptDefaultsEnabled())
      {
         return defaultValue;
      }
      String suffix = (defaultValue) ? " [Y/n] " : " [y/N] ";
      String answer = prompt(message + suffix);
      if (Strings.isNullOrEmpty(answer))
      {
         return defaultValue;
      }
      else
      {
         if (defaultValue)
         {
            return !"N".equalsIgnoreCase(answer);
         }
         else
         {
            return "Y".equalsIgnoreCase(answer);
         }
      }
   }

   @SuppressWarnings("rawtypes")
   public Object promptValueFrom(InputComponent<?, ?> input)
   {
      Object value = null;
      if (input instanceof SingleValued)
      {
         if (input instanceof SelectComponent)
         {
            // UISelectOne
            SelectComponent select = (SelectComponent) input;
            value = promptSelectComponent(select, Collections.emptyList());
         }
         else
         {
            // UIInput
            value = promptInputComponent(input);
         }
      }
      else if (input instanceof ManyValued)
      {
         List<Object> inputValues = new ArrayList<>();
         Object promptValue;
         do
         {
            if (input instanceof SelectComponent)
            {
               // UISelectMany
               SelectComponent select = (SelectComponent) input;
               promptValue = promptSelectComponent(select, inputValues);
            }
            else
            {
               // UIInputMany
               promptValue = promptInputComponent(input);
            }
            if (promptValue != null)
            {
               inputValues.add(promptValue);
            }
         }
         while (promptValue != null);
         if (!inputValues.isEmpty())
         {
            value = inputValues;
         }
      }
      InputComponents.setValueFor(converterFactory, input, value);
      return input.getValue();
   }

   private Object promptInputComponent(InputComponent<?, ?> input)
   {
      Object value;
      String label = InputComponents.getLabelFor(input, false);
      String description = input.getDescription();
      if (!Strings.isNullOrEmpty(description))
      {
         description = " (" + description + "): ";
      }
      else
      {
         description = ": ";
      }
      String inputType = InputComponents.getInputType(input);
      if (InputType.SECRET.equals(inputType))
      {
         value = promptSecret(label + description);
      }
      else if (input.getValueType() == Boolean.class)
      {
         value = promptBoolean(label + description);
      }
      else
      {
         value = prompt(label + description);
      }
      return value;

   }

   @SuppressWarnings({ "unchecked", "rawtypes" })
   private Object promptSelectComponent(SelectComponent select, List<Object> existingItems)
   {
      if (isAcceptDefaultsEnabled())
      {
         return select.hasDefaultValue() ? select.getValue() : null;
      }
      
      PrintStream out = console.getShell().out();
      String label = InputComponents.getLabelFor(select, false);
      String description = select.getDescription();
      if (!Strings.isNullOrEmpty(description))
      {
         description = " (" + description + "):";
      }
      else
      {
         description = ":";
      }
      Object value = null;
      Converter<Object, String> itemLabelConverter = InputComponents.getItemLabelConverter(converterFactory,
               select);
      List<Object> items = new ArrayList<>(Lists.toList(select.getValueChoices()));
      items.removeAll(existingItems);
      if (items.isEmpty())
      {
         return null;
      }
      out.println();
      for (int i = 0; i < items.size(); i++)
      {
         Object item = items.get(i);
         String itemLabel = itemLabelConverter.convert(item);
         out.printf("[%s] - %s", i, itemLabel);
         out.println();
      }
      out.println();
      int idx;
      try
      {
         idx = Integer.parseInt(prompt(label + description + " [0-" + (items.size() - 1) + "]"));
      }
      catch (NumberFormatException nfe)
      {
         idx = -1;
      }
      if (idx > -1 && idx < items.size())
      {
         value = items.get(idx);
      }
      return value;
   }

   private String readInput(PrintStream out, boolean echo)
   {
      String output;
      try
      {
         StringBuilder sb = new StringBuilder();
         Key inputKey;
         do
         {
            CommandOperation input = commandInvocation.getInput();
            inputKey = input.getInputKey();
            if (inputKey == Key.CTRL_C || inputKey == Key.CTRL_D)<commit_msg>FORGE-2142: ShellUIPromptImpl now allows unselecting values and has a nicer UI<commit_after>/**
 * Copyright 2013 Red Hat, Inc. and/or its affiliates.
 *
 * Licensed under the Eclipse Public License version 1.0, available at
 * http://www.eclipse.org/legal/epl-v10.html
 */

package org.jboss.forge.addon.shell.ui;

import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.jboss.aesh.console.AeshConsole;
import org.jboss.aesh.console.Buffer;
import org.jboss.aesh.console.command.CommandOperation;
import org.jboss.aesh.console.command.invocation.CommandInvocation;
import org.jboss.aesh.terminal.Key;
import org.jboss.forge.addon.convert.Converter;
import org.jboss.forge.addon.convert.ConverterFactory;
import org.jboss.forge.addon.ui.hints.InputType;
import org.jboss.forge.addon.ui.input.InputComponent;
import org.jboss.forge.addon.ui.input.ManyValued;
import org.jboss.forge.addon.ui.input.SelectComponent;
import org.jboss.forge.addon.ui.input.SingleValued;
import org.jboss.forge.addon.ui.input.UIPrompt;
import org.jboss.forge.addon.ui.util.InputComponents;
import org.jboss.forge.furnace.util.Lists;
import org.jboss.forge.furnace.util.Strings;

/**
 * Implementation of {@link UIPrompt}
 *
 * @author <a href="<EMAIL>"><NAME></a>
 */
public class ShellUIPromptImpl implements UIPrompt
{
   private final ShellContext context;
   private final AeshConsole console;
   private final ConverterFactory converterFactory;
   private final CommandInvocation commandInvocation;

   public ShellUIPromptImpl(ShellContext context, ConverterFactory converterFactory)
   {
      this.context = context;
      this.converterFactory = converterFactory;
      this.console = context.getProvider().getConsole();
      this.commandInvocation = (CommandInvocation) context.getAttributeMap()
               .get(CommandInvocation.class);
   }

   @Override
   public String prompt(String message)
   {
      if (isAcceptDefaultsEnabled())
      {
         return null;
      }
      PrintStream out = console.getShell().out();
      out.print(message + " ");
      String output = readInput(out, true);
      out.println();
      return output;
   }

   @Override
   public String promptSecret(String message)
   {
      if (isAcceptDefaultsEnabled())
      {
         return null;
      }
      PrintStream out = console.getShell().out();
      out.print(message + " ");
      String output = readInput(out, false);
      out.println();
      return output;
   }

   @Override
   public boolean promptBoolean(String message)
   {
      return promptBoolean(message, true);
   }

   @Override
   public boolean promptBoolean(String message, boolean defaultValue)
   {
      if (isAcceptDefaultsEnabled())
      {
         return defaultValue;
      }
      String suffix = (defaultValue) ? " [Y/n] " : " [y/N] ";
      String answer = prompt(message + suffix);
      if (Strings.isNullOrEmpty(answer))
      {
         return defaultValue;
      }
      else
      {
         if (defaultValue)
         {
            return !"N".equalsIgnoreCase(answer);
         }
         else
         {
            return "Y".equalsIgnoreCase(answer);
         }
      }
   }

   @SuppressWarnings("rawtypes")
   public Object promptValueFrom(InputComponent<?, ?> input)
   {
      Object value = null;
      if (input instanceof SingleValued)
      {
         if (input instanceof SelectComponent)
         {
            // UISelectOne
            SelectComponent select = (SelectComponent) input;
            value = promptSelectComponent(select, Collections.emptyList());
         }
         else
         {
            // UIInput
            value = promptInputComponent(input);
         }
      }
      else if (input instanceof ManyValued)
      {
         List<Object> inputValues = new ArrayList<>();
         Object promptValue;
         do
         {
            if (input instanceof SelectComponent)
            {
               // UISelectMany
               SelectComponent select = (SelectComponent) input;
               promptValue = promptSelectComponent(select, inputValues);
            }
            else
            {
               // UIInputMany
               promptValue = promptInputComponent(input);
            }
            if (promptValue != null)
            {
               if (inputValues.contains(promptValue))
               {
                  inputValues.remove(promptValue);
               }
               else
               {
                  inputValues.add(promptValue);
               }
            }
         }
         while (promptValue != null);
         if (!inputValues.isEmpty())
         {
            value = inputValues;
         }
      }
      InputComponents.setValueFor(converterFactory, input, value);
      return input.getValue();
   }

   private Object promptInputComponent(InputComponent<?, ?> input)
   {
      Object value;
      String label = InputComponents.getLabelFor(input, false);
      String description = input.getDescription();
      if (!Strings.isNullOrEmpty(description))
      {
         description = " (" + description + "): ";
      }
      else
      {
         description = ": ";
      }
      String inputType = InputComponents.getInputType(input);
      if (InputType.SECRET.equals(inputType))
      {
         value = promptSecret(label + description);
      }
      else if (input.getValueType() == Boolean.class)
      {
         value = promptBoolean(label + description);
      }
      else
      {
         value = prompt(label + description);
      }
      return value;

   }

   @SuppressWarnings({ "unchecked", "rawtypes" })
   private Object promptSelectComponent(SelectComponent select, List<Object> existingItems)
   {
      if (isAcceptDefaultsEnabled())
      {
         return select.hasDefaultValue() ? select.getValue() : null;
      }

      PrintStream out = console.getShell().out();
      String label = InputComponents.getLabelFor(select, false);
      String description = select.getDescription();
      if (!Strings.isNullOrEmpty(description))
      {
         description = " (" + description + "):";
      }
      else
      {
         description = ":";
      }
      Object value = null;
      Converter<Object, String> itemLabelConverter = InputComponents.getItemLabelConverter(converterFactory,
               select);
      List<Object> items = Lists.toList(select.getValueChoices());
      out.println();
      for (int i = 0; i < items.size(); i++)
      {
         Object item = items.get(i);
         String itemLabel = itemLabelConverter.convert(item);
         Object separator = existingItems.contains(item) ? "(x)" : "( )";
         out.printf("[%s] %s %s", i, separator, itemLabel);
         out.println();
      }
      out.println();
      int idx;
      try
      {
         out.println("Press <ENTER> to confirm, or <CTRL>+C to cancel.");
         idx = Integer.parseInt(prompt(label + description + " [0-" + (items.size() - 1) + "]"));
      }
      catch (NumberFormatException nfe)
      {
         idx = -1;
      }
      if (idx > -1 && idx < items.size())
      {
         value = items.get(idx);
      }
      return value;
   }

   private String readInput(PrintStream out, boolean echo)
   {
      String output;
      try
      {
         StringBuilder sb = new StringBuilder();
         Key inputKey;
         do
         {
            CommandOperation input = commandInvocation.getInput();
            inputKey = input.getInputKey();
            if (inputKey == Key.CTRL_C || inputKey == Key.CTRL_D)
{'fraction_non_alphanumeric': 0.11014843172812143, 'fraction_numerical': 0.0020149103364900263, 'mean_word_length': 2.168085106382979, 'pattern_counts': {'":': 4, '<': 32, '<?xml version=': 0, '>': 30, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2500327', 'n_tokens_mistral': 4197, 'n_tokens_neox': 4073, 'n_words': 849}