Friday, September 28, 2012

"Rich" console output from Java

A while back - before the graphical terminals were available, in the old VT time - we used to build forms from scratch, using ANSI escape sequences. That was quite limited, but it was not that ugly, some times...
Linux still supports this kind of thing, and from Java, you can definitely add any effect to the text you spit out using System.out or System.err.
Surprisingly, it's another story on Windows, you have to mention the ansi.sys file in the config.nt one, which magically disappeared in Windows 7.
There is a solution to this problem, at jansi.fusesource.org.
It's small library available for free, which allows you to write the usual ANSI codes, whatever the system you run on is.
You can define your own codes:
  public final static char ESC = '\u001b'; // (char) 27;

  public final static String ANSI_BLACK   = "0";
  public final static String ANSI_RED     = "1";
  public final static String ANSI_GREEN   = "2";
  public final static String ANSI_YELLOW  = "3";
  public final static String ANSI_BLUE    = "4";
  public final static String ANSI_MAGENTA = "5";
  public final static String ANSI_CYAN    = "6";
  public final static String ANSI_WHITE   = "7";

  public static final String ANSI_CLS         = ESC + "[2J";
  public static final String ANSI_HOME        = ESC + "[H"; // 0,0
  public static final String ANSI_HEAD        = ESC + "[1G"; // Start of current line, position 1

  public static final String ANSI_NORMAL      = ESC + "[0m";
  public static final String ANSI_BOLD        = ESC + "[1m";
  public static final String ANSI_FAINT       = ESC + "[2m";
  public static final String ANSI_ITALIC      = ESC + "[3m";
  public static final String ANSI_UNDERLINE   = ESC + "[4m";
  public static final String ANSI_BLINK       = ESC + "[5m";
  public static final String ANSI_BLINK_FAST  = ESC + "[6m";
...
and run like this:
    AnsiConsole.systemInstall();
    AnsiConsole.out.println(ANSI_CLS);
    AnsiConsole.out.println(ANSI_NORMAL + "Normal text and " +
                            ANSI_WHITEONBLUE + "bold" +
                            ANSI_NORMAL + " text.");
    AnsiConsole.out.println(ANSI_NORMAL + "Normal " +
                            ansiSetTextColor(ANSI_YELLOW) + "yellow" +
                            ANSI_NORMAL + " text and " +
                            ansiSetTextAndBackgroundColor(ANSI_WHITE, ANSI_BLACK) +
                            "bold" + ANSI_NORMAL + " text.");
    
    System.out.println(ANSI_NORMAL + "Normal text and " +
                       ANSI_WHITEONBLUE + "bold" +
                       ANSI_NORMAL + " text.");
    System.out.println(ANSI_NORMAL + "Normal " +
                       ansiSetTextColor(ANSI_YELLOW) + "yellow" +
                       ANSI_NORMAL + " text and " +
                       ansiSetTextAndBackgroundColor(ANSI_WHITE, ANSI_BLACK) +
                       "bold" + ANSI_NORMAL + " text.");
or even easier, use some static classes and methods provided by Jansi:
    AnsiConsole.systemInstall();
    System.out.println( ansi().eraseScreen().fg(RED).a("Hello").fg(GREEN).a(" World").reset() );
    System.out.println( ansi().bg(CYAN).fg(RED).bold().a("Hello").fg(GREEN).a(" World").reset() );
    System.out.println();
    System.out.println();
    System.out.print( ansi().fg(WHITE).a("Hello World") );
    System.out.flush();
    try { Thread.sleep(1000); } catch (Exception ex) {}
    System.out.println( ansi().eraseLine(Erase.ALL).cursorLeft("Hello World".length()).fg(YELLOW).a("Hello World, again").reset() );
    AnsiConsole.systemUninstall();
And the output is the same on Windows, on Linux,...

By the way, this is what Gradle is using for their output.