79545151

Date: 2025-03-30 20:59:31
Score: 1.5
Natty:
Report link

There is a way to populate a text at the scanner.nextLine() prompt that can be edited by the user.

I'm using this in a console application that prompts for a message with a specific max length. If the user enters a longer message an error message will be shown and the entered text is populated at the next scanner.nextLine() prompt iteration, so the user must not enter the whole message again and can edit it instantly.

The "trick" is to set the entered message to the system clipboard and paste it to the console using keyPress() method of the Robot class.

First we will need a method to set a string to the system clipboard.

public static void setClipboard(String str) {

    // set passed string to clipboard
    Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(str), null);
}

Next there is a method needed that triggers the ctrl + v key press.

public static void pasteClipboard(int i) {

    // triggers ctrl + v key press and release after a delay of i milliseconds

    try {

        // list containing ctrl + v key events
        List<Integer> keys = new ArrayList<Integer>(List.of(KeyEvent.VK_CONTROL, KeyEvent.VK_V));

        Robot robot = new Robot();

        /*
         * Passed integer argument to set a delay in milliseconds
         * (a value of 50 is recommended) to make sure pasting the
         * clipboard AFTER the System.out print streams are written out.
         * 
         * QUESTIONS:
         * 
         * Without any delay the clipboard will be pasted BEFORE
         * the System.out print streams, starting from the 2nd iteration
         * though this method is called AFTER the print calls, but why?
         * 
         * Is there a way to achieve the correct behavior without any delay workarounds?
         *
         * The following has already been tried, always with the same faulty result:
         * 
         * - using a while loop instead of recursion
         * - flushed the output stream before calling this method
         * - separating the print calls in a thread and waiting til it's finished
         */

        robot.delay(i);

        // key press sequence: ctrl, v
        for (Integer key : keys) {
            robot.keyPress(key);
        }

        // reversing the keys list especially to release the ctrl key
        Collections.reverse(keys);

        // key release sequence: v, ctrl
        for (Integer key : keys) {
            robot.keyRelease(key);
        }

    } catch (AWTException e) {
        e.printStackTrace();
    }
}

Please note the comments in my code. Is anybody out there who can explain / answer my questions?

Now we are coming to the method including all that stuff like message output and the whole logic part of checking the users input. It's built as a recursive method but can be built as a while loop, too.

public static String enterMessageRecursion(Scanner input, String msg, int maxlen) {

    /*
     * ANSI escape codes are used for styling the output:
     * 
     * \033[1;97;41m = red background + white bold bright foreground
     * \033[1;97;42m = green background + white bold bright foreground
     * \033[1m       = bold
     * \033[0m       = reset to default
     */

    System.out.println(String.format("Please enter a message (%smax. %s chars%s):",
            "\033[1m", maxlen, "\033[0m"));

    if (!msg.isBlank())      // true if the user entered message is too long
        pasteClipboard(50);  // triggers ctrl + v key press with a delay of 50ms

    msg = input.nextLine();  // scanner awaits user input

    if (msg.length() > maxlen) {

        // set message to clipboard if it's greater than the max message length
        setClipboard(msg);

        /*
         * print out error message and continues with the next call of this method
         * (see return statement)
         */

        System.out.println(String.format("%sThe entered message is too long (%s chars).%s",
            "\033[1;97;41m", msg.length(), "\033[0m"));

    } else if (!(msg.isBlank() || msg.length() > maxlen)) {

        /*
         * At this point the message is not blank or not greater than the max message length,
         * means the entered message is valid. This is the end of the method and recursion.
         */

        System.out.print(String.format("%sThe entered message is valid (%s chars).%s",
            "\033[1;97;42m", msg.length(), "\033[0m"));
    }

    // recursion til message is not blank or not greater than the max message length
    return (msg.isBlank() || msg.length() > maxlen)
            ? enterMessageRecursion(input, msg, maxlen) : msg;
}

Finally we need a main method of course. Pretty straightforward and nothing exciting in here.

public static void main(String[] args) {

    String msg = "";  // user message
    int maxlen = 20;  // max message length (chars)

    Scanner input = new Scanner(System.in);

    enterMessageRecursion(input, msg, maxlen);

    input.close();
}

The result should look like this:

console output from Eclipse IDE

Important to know for adapting the code is that you need setClipboard() to set the clipboard to the string you want to populate at the scanner prompt. After printing out your prompt message right before your scanner.nextLine() call do pasteClipboard(50) for triggering the ctrl + v key press that should paste the string from the clipboard to the console. This string is editable by the user.

Reasons:
  • Blacklisted phrase (0.5): why?
  • Blacklisted phrase (1): Is there a way
  • Long answer (-1):
  • Has code block (-0.5):
  • Contains question mark (0.5):
  • Low reputation (1):
Posted by: burnie