diff --git a/jansi/src/main/java/org/fusesource/jansi/Ansi.java b/jansi/src/main/java/org/fusesource/jansi/Ansi.java index ded20ec9..317aa455 100644 --- a/jansi/src/main/java/org/fusesource/jansi/Ansi.java +++ b/jansi/src/main/java/org/fusesource/jansi/Ansi.java @@ -21,121 +21,127 @@ /** * Provides a fluent API for generating ANSI escape sequences. - * + * * @author Hiram Chirino * @since 1.0 */ public class Ansi { private static final char FIRST_ESC_CHAR = 27; - private static final char SECOND_ESC_CHAR = '['; - - public static enum Color { - BLACK(0, "BLACK"), - RED(1, "RED"), - GREEN(2, "GREEN"), - YELLOW(3, "YELLOW"), - BLUE(4, "BLUE"), - MAGENTA(5, "MAGENTA"), - CYAN(6, "CYAN"), - WHITE(7,"WHITE"), - DEFAULT(9,"DEFAULT"); - - private final int value; - private final String name; - - Color(int index, String name) { - this.value = index; - this.name = name; - } - - @Override - public String toString() { - return name; - } - - public int value() { - return value; - } - - public int fg() { - return value + 30; - } - - public int bg() { - return value + 40; - } - - public int fgBright() { - return value + 90; - } - - public int bgBright() { - return value + 100; - } - }; - - public static enum Attribute { - RESET ( 0, "RESET"), - INTENSITY_BOLD ( 1, "INTENSITY_BOLD"), - INTENSITY_FAINT ( 2, "INTENSITY_FAINT"), - ITALIC ( 3, "ITALIC_ON"), - UNDERLINE ( 4, "UNDERLINE_ON"), - BLINK_SLOW ( 5, "BLINK_SLOW"), - BLINK_FAST ( 6, "BLINK_FAST"), - NEGATIVE_ON ( 7, "NEGATIVE_ON"), - CONCEAL_ON ( 8, "CONCEAL_ON"), - STRIKETHROUGH_ON ( 9, "STRIKETHROUGH_ON"), - UNDERLINE_DOUBLE ( 21, "UNDERLINE_DOUBLE"), - INTENSITY_BOLD_OFF ( 22, "INTENSITY_BOLD_OFF"), - ITALIC_OFF ( 23, "ITALIC_OFF"), - UNDERLINE_OFF ( 24, "UNDERLINE_OFF"), - BLINK_OFF ( 25, "BLINK_OFF"), - NEGATIVE_OFF ( 27, "NEGATIVE_OFF"), - CONCEAL_OFF ( 28, "CONCEAL_OFF"), - STRIKETHROUGH_OFF ( 29, "STRIKETHROUGH_OFF"); - - private final int value; - private final String name; - - Attribute(int index, String name) { - this.value = index; - this.name = name; - } - - @Override - public String toString() { - return name; - } - - public int value() { - return value; - } - - }; - - public static enum Erase { - FORWARD(0, "FORWARD"), - BACKWARD(1, "BACKWARD"), - ALL(2, "ALL"); - - private final int value; - private final String name; - - Erase(int index, String name) { - this.value = index; - this.name = name; - } - - @Override - public String toString() { - return name; - } - - public int value() { - return value; - } - }; + private static final char SECOND_ESC_CHAR = '['; + + public static enum Color { + BLACK(0, "BLACK"), + RED(1, "RED"), + GREEN(2, "GREEN"), + YELLOW(3, "YELLOW"), + BLUE(4, "BLUE"), + MAGENTA(5, "MAGENTA"), + CYAN(6, "CYAN"), + WHITE(7, "WHITE"), + DEFAULT(9, "DEFAULT"); + + private final int value; + private final String name; + + Color(int index, String name) { + this.value = index; + this.name = name; + } + + @Override + public String toString() { + return name; + } + + public int value() { + return value; + } + + public int fg() { + return value + 30; + } + + public int bg() { + return value + 40; + } + + public int fgBright() { + return value + 90; + } + + public int bgBright() { + return value + 100; + } + } + + ; + + public static enum Attribute { + RESET(0, "RESET"), + INTENSITY_BOLD(1, "INTENSITY_BOLD"), + INTENSITY_FAINT(2, "INTENSITY_FAINT"), + ITALIC(3, "ITALIC_ON"), + UNDERLINE(4, "UNDERLINE_ON"), + BLINK_SLOW(5, "BLINK_SLOW"), + BLINK_FAST(6, "BLINK_FAST"), + NEGATIVE_ON(7, "NEGATIVE_ON"), + CONCEAL_ON(8, "CONCEAL_ON"), + STRIKETHROUGH_ON(9, "STRIKETHROUGH_ON"), + UNDERLINE_DOUBLE(21, "UNDERLINE_DOUBLE"), + INTENSITY_BOLD_OFF(22, "INTENSITY_BOLD_OFF"), + ITALIC_OFF(23, "ITALIC_OFF"), + UNDERLINE_OFF(24, "UNDERLINE_OFF"), + BLINK_OFF(25, "BLINK_OFF"), + NEGATIVE_OFF(27, "NEGATIVE_OFF"), + CONCEAL_OFF(28, "CONCEAL_OFF"), + STRIKETHROUGH_OFF(29, "STRIKETHROUGH_OFF"); + + private final int value; + private final String name; + + Attribute(int index, String name) { + this.value = index; + this.name = name; + } + + @Override + public String toString() { + return name; + } + + public int value() { + return value; + } + + } + + ; + + public static enum Erase { + FORWARD(0, "FORWARD"), + BACKWARD(1, "BACKWARD"), + ALL(2, "ALL"); + + private final int value; + private final String name; + + Erase(int index, String name) { + this.value = index; + this.name = name; + } + + @Override + public String toString() { + return name; + } + + public int value() { + return value; + } + } + + ; public static final String DISABLE = Ansi.class.getName() + ".disable"; @@ -153,14 +159,12 @@ public static void setDetector(final Callable detector) { public static boolean isDetected() { try { return detector.call(); - } - catch (Exception e) { + } catch (Exception e) { return true; } } - private static final InheritableThreadLocal holder = new InheritableThreadLocal() - { + private static final InheritableThreadLocal holder = new InheritableThreadLocal() { @Override protected Boolean initialValue() { return isDetected(); @@ -178,15 +182,13 @@ public static boolean isEnabled() { public static Ansi ansi() { if (isEnabled()) { return new Ansi(); - } - else { + } else { return new NoAnsi(); } } private static class NoAnsi - extends Ansi - { + extends Ansi { @Override public Ansi fg(Color color) { return this; @@ -314,32 +316,33 @@ public Ansi reset() { } } - private final StringBuilder builder; - private final ArrayList attributeOptions = new ArrayList(5); - - public Ansi() { - this(new StringBuilder()); - } + private final StringBuilder builder; + private final ArrayList attributeOptions = new ArrayList(5); - public Ansi(Ansi parent) { - this(new StringBuilder(parent.builder)); - attributeOptions.addAll(parent.attributeOptions); - } + public Ansi() { + this(new StringBuilder()); + } + + public Ansi(Ansi parent) { + this(new StringBuilder(parent.builder)); + attributeOptions.addAll(parent.attributeOptions); + } public Ansi(int size) { - this(new StringBuilder(size)); - } + this(new StringBuilder(size)); + } public Ansi(StringBuilder builder) { - this.builder = builder; - } + this.builder = builder; + } - public static Ansi ansi(StringBuilder builder) { - return new Ansi(builder); - } - public static Ansi ansi(int size) { - return new Ansi(size); - } + public static Ansi ansi(StringBuilder builder) { + return new Ansi(builder); + } + + public static Ansi ansi(int size) { + return new Ansi(size); + } public Ansi fg(Color color) { attributeOptions.add(color.fg()); @@ -350,60 +353,60 @@ public Ansi fgBlack() { return this.fg(Color.BLACK); } - public Ansi fgBlue() { + public Ansi fgBlue() { return this.fg(Color.BLUE); } - public Ansi fgCyan() { + public Ansi fgCyan() { return this.fg(Color.CYAN); } - public Ansi fgDefault() { + public Ansi fgDefault() { return this.fg(Color.DEFAULT); } - public Ansi fgGreen() { + public Ansi fgGreen() { return this.fg(Color.GREEN); } - public Ansi fgMagenta() { + public Ansi fgMagenta() { return this.fg(Color.MAGENTA); } - public Ansi fgRed() { + public Ansi fgRed() { return this.fg(Color.RED); } - public Ansi fgYellow() { + public Ansi fgYellow() { return this.fg(Color.YELLOW); } - public Ansi bg(Color color) { - attributeOptions.add(color.bg()); - return this; - } + public Ansi bg(Color color) { + attributeOptions.add(color.bg()); + return this; + } - public Ansi bgCyan() { + public Ansi bgCyan() { return this.fg(Color.CYAN); } - public Ansi bgDefault() { + public Ansi bgDefault() { return this.bg(Color.DEFAULT); } - public Ansi bgGreen() { + public Ansi bgGreen() { return this.bg(Color.GREEN); } - public Ansi bgMagenta() { + public Ansi bgMagenta() { return this.bg(Color.MAGENTA); } - public Ansi bgRed() { + public Ansi bgRed() { return this.bg(Color.RED); } - public Ansi bgYellow() { + public Ansi bgYellow() { return this.bg(Color.YELLOW); } @@ -416,91 +419,91 @@ public Ansi fgBrightBlack() { return this.fgBright(Color.BLACK); } - public Ansi fgBrightBlue() { + public Ansi fgBrightBlue() { return this.fgBright(Color.BLUE); } - public Ansi fgBrightCyan() { + public Ansi fgBrightCyan() { return this.fgBright(Color.CYAN); } - public Ansi fgBrightDefault() { + public Ansi fgBrightDefault() { return this.fgBright(Color.DEFAULT); } - public Ansi fgBrightGreen() { + public Ansi fgBrightGreen() { return this.fgBright(Color.GREEN); } - public Ansi fgBrightMagenta() { + public Ansi fgBrightMagenta() { return this.fgBright(Color.MAGENTA); } - public Ansi fgBrightRed() { + public Ansi fgBrightRed() { return this.fgBright(Color.RED); } - public Ansi fgBrightYellow() { + public Ansi fgBrightYellow() { return this.fgBright(Color.YELLOW); } - + public Ansi bgBright(Color color) { attributeOptions.add(color.bgBright()); return this; } - public Ansi bgBrightCyan() { + public Ansi bgBrightCyan() { return this.fgBright(Color.CYAN); } - public Ansi bgBrightDefault() { + public Ansi bgBrightDefault() { return this.bgBright(Color.DEFAULT); } - public Ansi bgBrightGreen() { + public Ansi bgBrightGreen() { return this.bgBright(Color.GREEN); } - public Ansi bgBrightMagenta() { + public Ansi bgBrightMagenta() { return this.bg(Color.MAGENTA); } - public Ansi bgBrightRed() { + public Ansi bgBrightRed() { return this.bgBright(Color.RED); } - public Ansi bgBrightYellow() { + public Ansi bgBrightYellow() { return this.bgBright(Color.YELLOW); } - public Ansi a(Attribute attribute) { - attributeOptions.add(attribute.value()); - return this; - } - - public Ansi cursor(final int x, final int y) { - return appendEscapeSequence('H', x, y); - } + public Ansi a(Attribute attribute) { + attributeOptions.add(attribute.value()); + return this; + } + + public Ansi cursor(final int x, final int y) { + return appendEscapeSequence('H', x, y); + } public Ansi cursorToColumn(final int x) { return appendEscapeSequence('G', x); } - public Ansi cursorUp(final int y) { - return appendEscapeSequence('A', y); - } + public Ansi cursorUp(final int y) { + return appendEscapeSequence('A', y); + } - public Ansi cursorDown(final int y) { - return appendEscapeSequence('B', y); - } + public Ansi cursorDown(final int y) { + return appendEscapeSequence('B', y); + } - public Ansi cursorRight(final int x) { - return appendEscapeSequence('C', x); - } + public Ansi cursorRight(final int x) { + return appendEscapeSequence('C', x); + } - public Ansi cursorLeft(final int x) { - return appendEscapeSequence('D', x); - } + public Ansi cursorLeft(final int x) { + return appendEscapeSequence('D', x); + } public Ansi cursorDownLine() { return appendEscapeSequence('E'); @@ -518,46 +521,46 @@ public Ansi cursorUpLine(final int n) { return appendEscapeSequence('F', n); } - public Ansi eraseScreen() { - return appendEscapeSequence('J',Erase.ALL.value()); - } + public Ansi eraseScreen() { + return appendEscapeSequence('J', Erase.ALL.value()); + } - public Ansi eraseScreen(final Erase kind) { - return appendEscapeSequence('J', kind.value()); - } + public Ansi eraseScreen(final Erase kind) { + return appendEscapeSequence('J', kind.value()); + } - public Ansi eraseLine() { - return appendEscapeSequence('K'); - } + public Ansi eraseLine() { + return appendEscapeSequence('K'); + } - public Ansi eraseLine(final Erase kind) { - return appendEscapeSequence('K', kind.value()); - } + public Ansi eraseLine(final Erase kind) { + return appendEscapeSequence('K', kind.value()); + } - public Ansi scrollUp(final int rows) { - return appendEscapeSequence('S', rows); - } + public Ansi scrollUp(final int rows) { + return appendEscapeSequence('S', rows); + } - public Ansi scrollDown(final int rows) { - return appendEscapeSequence('T', rows); - } + public Ansi scrollDown(final int rows) { + return appendEscapeSequence('T', rows); + } - public Ansi saveCursorPosition() { - return appendEscapeSequence('s'); - } + public Ansi saveCursorPosition() { + return appendEscapeSequence('s'); + } @Deprecated - public Ansi restorCursorPosition() { - return appendEscapeSequence('u'); - } + public Ansi restorCursorPosition() { + return appendEscapeSequence('u'); + } - public Ansi restoreCursorPosition() { - return appendEscapeSequence('u'); - } + public Ansi restoreCursorPosition() { + return appendEscapeSequence('u'); + } - public Ansi reset() { - return a(Attribute.RESET); - } + public Ansi reset() { + return a(Attribute.RESET); + } public Ansi bold() { return a(Attribute.INTENSITY_BOLD); @@ -567,88 +570,88 @@ public Ansi boldOff() { return a(Attribute.INTENSITY_BOLD_OFF); } - public Ansi a(String value) { - flushAttributes(); - builder.append(value); - return this; - } - - public Ansi a(boolean value) { - flushAttributes(); - builder.append(value); - return this; - } - - public Ansi a(char value) { - flushAttributes(); - builder.append(value); - return this; - } - - public Ansi a(char[] value, int offset, int len) { - flushAttributes(); - builder.append(value, offset, len); - return this; - } - - public Ansi a(char[] value) { - flushAttributes(); - builder.append(value); - return this; - } - - public Ansi a(CharSequence value, int start, int end) { - flushAttributes(); - builder.append(value, start, end); - return this; - } - - public Ansi a(CharSequence value) { - flushAttributes(); - builder.append(value); - return this; - } - - public Ansi a(double value) { - flushAttributes(); - builder.append(value); - return this; - } - - public Ansi a(float value) { - flushAttributes(); - builder.append(value); - return this; - } - - public Ansi a(int value) { - flushAttributes(); - builder.append(value); - return this; - } - - public Ansi a(long value) { - flushAttributes(); - builder.append(value); - return this; - } - - public Ansi a(Object value) { - flushAttributes(); - builder.append(value); - return this; - } - - public Ansi a(StringBuffer value) { - flushAttributes(); - builder.append(value); - return this; - } + public Ansi a(String value) { + flushAttributes(); + builder.append(value); + return this; + } + + public Ansi a(boolean value) { + flushAttributes(); + builder.append(value); + return this; + } + + public Ansi a(char value) { + flushAttributes(); + builder.append(value); + return this; + } + + public Ansi a(char[] value, int offset, int len) { + flushAttributes(); + builder.append(value, offset, len); + return this; + } + + public Ansi a(char[] value) { + flushAttributes(); + builder.append(value); + return this; + } + + public Ansi a(CharSequence value, int start, int end) { + flushAttributes(); + builder.append(value, start, end); + return this; + } + + public Ansi a(CharSequence value) { + flushAttributes(); + builder.append(value); + return this; + } + + public Ansi a(double value) { + flushAttributes(); + builder.append(value); + return this; + } + + public Ansi a(float value) { + flushAttributes(); + builder.append(value); + return this; + } + + public Ansi a(int value) { + flushAttributes(); + builder.append(value); + return this; + } + + public Ansi a(long value) { + flushAttributes(); + builder.append(value); + return this; + } + + public Ansi a(Object value) { + flushAttributes(); + builder.append(value); + return this; + } + + public Ansi a(StringBuffer value) { + flushAttributes(); + builder.append(value); + return this; + } public Ansi newline() { flushAttributes(); - builder.append(System.getProperty("line.separator")); - return this; + builder.append(System.getProperty("line.separator")); + return this; } public Ansi format(String pattern, Object... args) { @@ -658,12 +661,12 @@ public Ansi format(String pattern, Object... args) { } /** - * Uses the {@link AnsiRenderer} + * Uses the {@link AnsiRenderer} * to generate the ANSI escape sequences for the supplied text. * - * @param text text - * @return this - * + * @param text text + * @return this + * * @since 1.1 */ public Ansi render(final String text) { @@ -672,13 +675,13 @@ public Ansi render(final String text) { } /** - * String formats and renders the supplied arguments. Uses the {@link AnsiRenderer} + * String formats and renders the supplied arguments. Uses the {@link AnsiRenderer} * to generate the ANSI escape sequences. * - * @param text format - * @param args arguments - * @return this - * + * @param text format + * @param args arguments + * @return this + * * @since 1.1 */ public Ansi render(final String text, Object... args) { @@ -686,65 +689,65 @@ public Ansi render(final String text, Object... args) { return this; } - @Override - public String toString() { - flushAttributes(); - return builder.toString(); - } - - /////////////////////////////////////////////////////////////////// - // Private Helper Methods - /////////////////////////////////////////////////////////////////// - - private Ansi appendEscapeSequence(char command) { - flushAttributes(); - builder.append(FIRST_ESC_CHAR); - builder.append(SECOND_ESC_CHAR); - builder.append(command); - return this; - } - - private Ansi appendEscapeSequence(char command, int option) { - flushAttributes(); - builder.append(FIRST_ESC_CHAR); - builder.append(SECOND_ESC_CHAR); - builder.append(option); - builder.append(command); - return this; - } - - private Ansi appendEscapeSequence(char command, Object... options) { - flushAttributes(); - return _appendEscapeSequence(command, options); - } - - private void flushAttributes() { - if( attributeOptions.isEmpty() ) - return; - if (attributeOptions.size() == 1 && attributeOptions.get(0) == 0) { - builder.append(FIRST_ESC_CHAR); - builder.append(SECOND_ESC_CHAR); - builder.append('m'); - } else { - _appendEscapeSequence('m', attributeOptions.toArray()); - } - attributeOptions.clear(); - } - - private Ansi _appendEscapeSequence(char command, Object... options) { - builder.append(FIRST_ESC_CHAR); - builder.append(SECOND_ESC_CHAR); - int size = options.length; - for (int i = 0; i < size; i++) { - if (i != 0) { - builder.append(';'); - } - if (options[i] != null) { - builder.append(options[i]); - } - } - builder.append(command); - return this; - } + @Override + public String toString() { + flushAttributes(); + return builder.toString(); + } + + /////////////////////////////////////////////////////////////////// + // Private Helper Methods + /////////////////////////////////////////////////////////////////// + + private Ansi appendEscapeSequence(char command) { + flushAttributes(); + builder.append(FIRST_ESC_CHAR); + builder.append(SECOND_ESC_CHAR); + builder.append(command); + return this; + } + + private Ansi appendEscapeSequence(char command, int option) { + flushAttributes(); + builder.append(FIRST_ESC_CHAR); + builder.append(SECOND_ESC_CHAR); + builder.append(option); + builder.append(command); + return this; + } + + private Ansi appendEscapeSequence(char command, Object... options) { + flushAttributes(); + return _appendEscapeSequence(command, options); + } + + private void flushAttributes() { + if (attributeOptions.isEmpty()) + return; + if (attributeOptions.size() == 1 && attributeOptions.get(0) == 0) { + builder.append(FIRST_ESC_CHAR); + builder.append(SECOND_ESC_CHAR); + builder.append('m'); + } else { + _appendEscapeSequence('m', attributeOptions.toArray()); + } + attributeOptions.clear(); + } + + private Ansi _appendEscapeSequence(char command, Object... options) { + builder.append(FIRST_ESC_CHAR); + builder.append(SECOND_ESC_CHAR); + int size = options.length; + for (int i = 0; i < size; i++) { + if (i != 0) { + builder.append(';'); + } + if (options[i] != null) { + builder.append(options[i]); + } + } + builder.append(command); + return this; + } } diff --git a/jansi/src/main/java/org/fusesource/jansi/AnsiConsole.java b/jansi/src/main/java/org/fusesource/jansi/AnsiConsole.java index c3fdddbe..03ccf15f 100644 --- a/jansi/src/main/java/org/fusesource/jansi/AnsiConsole.java +++ b/jansi/src/main/java/org/fusesource/jansi/AnsiConsole.java @@ -28,13 +28,13 @@ /** * Provides consistent access to an ANSI aware console PrintStream. - * + * * @author Hiram Chirino * @since 1.0 */ public class AnsiConsole { - public static final PrintStream system_out = System.out; + public static final PrintStream system_out = System.out; public static final PrintStream out = new PrintStream(wrapOutputStream(system_out)); public static final PrintStream system_err = System.err; @@ -42,122 +42,123 @@ public class AnsiConsole { private static int installed; - private AnsiConsole() {} + private AnsiConsole() { + } - public static OutputStream wrapOutputStream(final OutputStream stream) { - return wrapOutputStream(stream, STDOUT_FILENO); - } + public static OutputStream wrapOutputStream(final OutputStream stream) { + return wrapOutputStream(stream, STDOUT_FILENO); + } - public static OutputStream wrapOutputStream(final OutputStream stream, int fileno) { + public static OutputStream wrapOutputStream(final OutputStream stream, int fileno) { // If the jansi.passthrough property is set, then don't interpret // any of the ansi sequences. - if( Boolean.getBoolean("jansi.passthrough") ) { - return stream; + if (Boolean.getBoolean("jansi.passthrough")) { + return stream; } // If the jansi.strip property is set, then we just strip the // the ansi escapes. - if( Boolean.getBoolean("jansi.strip") ) { - return new AnsiOutputStream(stream); + if (Boolean.getBoolean("jansi.strip")) { + return new AnsiOutputStream(stream); + } + + String os = System.getProperty("os.name"); + if (os.startsWith("Windows") && !isCygwin()) { + + // On windows we know the console does not interpret ANSI codes.. + try { + return new WindowsAnsiOutputStream(stream); + } catch (Throwable ignore) { + // this happens when JNA is not in the path.. or + // this happens when the stdout is being redirected to a file. + } + + // Use the ANSIOutputStream to strip out the ANSI escape sequences. + return new AnsiOutputStream(stream); } - String os = System.getProperty("os.name"); - if( os.startsWith("Windows") && !isCygwin() ) { - - // On windows we know the console does not interpret ANSI codes.. - try { - return new WindowsAnsiOutputStream(stream); - } catch (Throwable ignore) { - // this happens when JNA is not in the path.. or - // this happens when the stdout is being redirected to a file. - } - - // Use the ANSIOutputStream to strip out the ANSI escape sequences. - return new AnsiOutputStream(stream); - } - - // We must be on some Unix variant, including Cygwin or MSYS(2) on Windows... - try { - // If the jansi.force property is set, then we force to output - // the ansi escapes for piping it into ansi color aware commands (e.g. less -r) - boolean forceColored = Boolean.getBoolean("jansi.force"); - // If we can detect that stdout is not a tty.. then setup - // to strip the ANSI sequences.. - int rc = isatty(fileno); - if( !isCygwin() && !forceColored && rc == 0 ) { - return new AnsiOutputStream(stream); - } - - // These erros happen if the JNI lib is not available for your platform. + // We must be on some Unix variant, including Cygwin or MSYS(2) on Windows... + try { + // If the jansi.force property is set, then we force to output + // the ansi escapes for piping it into ansi color aware commands (e.g. less -r) + boolean forceColored = Boolean.getBoolean("jansi.force"); + // If we can detect that stdout is not a tty.. then setup + // to strip the ANSI sequences.. + int rc = isatty(fileno); + if (!isCygwin() && !forceColored && rc == 0) { + return new AnsiOutputStream(stream); + } + + // These erros happen if the JNI lib is not available for your platform. } catch (NoClassDefFoundError ignore) { - } catch (UnsatisfiedLinkError ignore) { - } - - // By default we assume your Unix tty can handle ANSI codes. - // Just wrap it up so that when we get closed, we reset the - // attributes. - return new FilterOutputStream(stream) { - @Override - public void close() throws IOException { - write(AnsiOutputStream.REST_CODE); - flush(); - super.close(); - } - }; - } - - private static boolean isCygwin() { - String term = System.getenv("TERM"); - return term != null && term.equals("xterm"); - } - - /** - * If the standard out natively supports ANSI escape codes, then this just - * returns System.out, otherwise it will provide an ANSI aware PrintStream - * which strips out the ANSI escape sequences or which implement the escape - * sequences. - * - * @return a PrintStream which is ANSI aware. - */ - public static PrintStream out() { - return out; - } + } catch (UnsatisfiedLinkError ignore) { + } + + // By default we assume your Unix tty can handle ANSI codes. + // Just wrap it up so that when we get closed, we reset the + // attributes. + return new FilterOutputStream(stream) { + @Override + public void close() throws IOException { + write(AnsiOutputStream.REST_CODE); + flush(); + super.close(); + } + }; + } + + private static boolean isCygwin() { + String term = System.getenv("TERM"); + return term != null && term.equals("xterm"); + } /** - * If the standard out natively supports ANSI escape codes, then this just - * returns System.err, otherwise it will provide an ANSI aware PrintStream - * which strips out the ANSI escape sequences or which implement the escape - * sequences. - * - * @return a PrintStream which is ANSI aware. - */ + * If the standard out natively supports ANSI escape codes, then this just + * returns System.out, otherwise it will provide an ANSI aware PrintStream + * which strips out the ANSI escape sequences or which implement the escape + * sequences. + * + * @return a PrintStream which is ANSI aware. + */ + public static PrintStream out() { + return out; + } + + /** + * If the standard out natively supports ANSI escape codes, then this just + * returns System.err, otherwise it will provide an ANSI aware PrintStream + * which strips out the ANSI escape sequences or which implement the escape + * sequences. + * + * @return a PrintStream which is ANSI aware. + */ public static PrintStream err() { return err; } - - /** - * Install Console.out to System.out. - */ - synchronized static public void systemInstall() { - installed++; - if( installed==1 ) { - System.setOut(out); + + /** + * Install Console.out to System.out. + */ + synchronized static public void systemInstall() { + installed++; + if (installed == 1) { + System.setOut(out); System.setErr(err); - } - } - - /** - * undo a previous {@link #systemInstall()}. If {@link #systemInstall()} was called - * multiple times, it {@link #systemUninstall()} must call the same number of times before - * it is actually uninstalled. - */ - synchronized public static void systemUninstall() { - installed--; - if( installed==0 ) { - System.setOut(system_out); + } + } + + /** + * undo a previous {@link #systemInstall()}. If {@link #systemInstall()} was called + * multiple times, it {@link #systemUninstall()} must call the same number of times before + * it is actually uninstalled. + */ + synchronized public static void systemUninstall() { + installed--; + if (installed == 0) { + System.setOut(system_out); System.setErr(system_err); - } - } - + } + } + } diff --git a/jansi/src/main/java/org/fusesource/jansi/AnsiOutputStream.java b/jansi/src/main/java/org/fusesource/jansi/AnsiOutputStream.java index d1d9338f..3fb76536 100644 --- a/jansi/src/main/java/org/fusesource/jansi/AnsiOutputStream.java +++ b/jansi/src/main/java/org/fusesource/jansi/AnsiOutputStream.java @@ -26,488 +26,491 @@ /** * A ANSI output stream extracts ANSI escape codes written to * an output stream. - * + * * For more information about ANSI escape codes, see: * http://en.wikipedia.org/wiki/ANSI_escape_code - * + * * This class just filters out the escape codes so that they are not * sent out to the underlying OutputStream. Subclasses should * actually perform the ANSI escape behaviors. - * + * * @author Hiram Chirino * @author Joris Kuipers * @since 1.0 */ public class AnsiOutputStream extends FilterOutputStream { - public static final byte [] REST_CODE = resetCode(); - - public AnsiOutputStream(OutputStream os) { - super(os); - } - - private final static int MAX_ESCAPE_SEQUENCE_LENGTH=100; - private byte[] buffer = new byte[MAX_ESCAPE_SEQUENCE_LENGTH]; - private int pos=0; - private int startOfValue; - private final ArrayList options = new ArrayList(); - - private static final int LOOKING_FOR_FIRST_ESC_CHAR = 0; - private static final int LOOKING_FOR_SECOND_ESC_CHAR = 1; - private static final int LOOKING_FOR_NEXT_ARG = 2; - private static final int LOOKING_FOR_STR_ARG_END = 3; - private static final int LOOKING_FOR_INT_ARG_END = 4; - private static final int LOOKING_FOR_OSC_COMMAND = 5; - private static final int LOOKING_FOR_OSC_COMMAND_END = 6; - private static final int LOOKING_FOR_OSC_PARAM = 7; - private static final int LOOKING_FOR_ST = 8; - - int state = LOOKING_FOR_FIRST_ESC_CHAR; - - private static final int FIRST_ESC_CHAR = 27; - private static final int SECOND_ESC_CHAR = '['; - private static final int SECOND_OSC_CHAR = ']'; - private static final int BEL = 7; - private static final int SECOND_ST_CHAR = '\\'; - - // TODO: implement to get perf boost: public void write(byte[] b, int off, int len) - - public void write(int data) throws IOException { - switch( state ) { - case LOOKING_FOR_FIRST_ESC_CHAR: - if (data == FIRST_ESC_CHAR) { - buffer[pos++] = (byte) data; - state = LOOKING_FOR_SECOND_ESC_CHAR; - } else { - out.write(data); - } - break; - - case LOOKING_FOR_SECOND_ESC_CHAR: - buffer[pos++] = (byte) data; - if( data == SECOND_ESC_CHAR ) { - state = LOOKING_FOR_NEXT_ARG; - } else if ( data == SECOND_OSC_CHAR ) { - state = LOOKING_FOR_OSC_COMMAND; - } else { - reset(false); - } - break; - - case LOOKING_FOR_NEXT_ARG: - buffer[pos++] = (byte)data; - if( '"' == data ) { - startOfValue=pos-1; - state = LOOKING_FOR_STR_ARG_END; - } else if( '0' <= data && data <= '9') { - startOfValue=pos-1; - state = LOOKING_FOR_INT_ARG_END; - } else if( ';' == data ) { - options.add(null); - } else if( '?' == data ) { - options.add(new Character('?')); - } else if( '=' == data ) { - options.add(new Character('=')); - } else { - reset( processEscapeCommand(options, data) ); - } - break; - default: - break; - - case LOOKING_FOR_INT_ARG_END: - buffer[pos++] = (byte)data; - if( !('0' <= data && data <= '9') ) { - String strValue = new String(buffer, startOfValue, (pos-1)-startOfValue, "UTF-8"); - Integer value = new Integer(strValue); - options.add(value); - if( data == ';' ) { - state = LOOKING_FOR_NEXT_ARG; - } else { - reset( processEscapeCommand(options, data) ); - } - } - break; - - case LOOKING_FOR_STR_ARG_END: - buffer[pos++] = (byte)data; - if( '"' != data ) { - String value = new String(buffer, startOfValue, (pos-1)-startOfValue, "UTF-8"); - options.add(value); - if( data == ';' ) { - state = LOOKING_FOR_NEXT_ARG; - } else { - reset( processEscapeCommand(options, data) ); - } - } - break; - - case LOOKING_FOR_OSC_COMMAND: - buffer[pos++] = (byte)data; - if( '0' <= data && data <= '9') { - startOfValue=pos-1; - state = LOOKING_FOR_OSC_COMMAND_END; - } else { - reset(false); - } - break; - - case LOOKING_FOR_OSC_COMMAND_END: - buffer[pos++] = (byte)data; - if ( ';' == data ) { - String strValue = new String(buffer, startOfValue, (pos-1)-startOfValue, "UTF-8"); - Integer value = new Integer(strValue); - options.add(value); - startOfValue=pos; - state = LOOKING_FOR_OSC_PARAM; - } else if ('0' <= data && data <= '9') { - // already pushed digit to buffer, just keep looking - } else { - // oops, did not expect this - reset(false); - } - break; - - case LOOKING_FOR_OSC_PARAM: - buffer[pos++] = (byte)data; - if ( BEL == data ) { - String value = new String(buffer, startOfValue, (pos-1)-startOfValue, "UTF-8"); - options.add(value); - reset( processOperatingSystemCommand(options) ); - } else if ( FIRST_ESC_CHAR == data ) { - state = LOOKING_FOR_ST; - } else { - // just keep looking while adding text - } - break; - - case LOOKING_FOR_ST: - buffer[pos++] = (byte)data; - if ( SECOND_ST_CHAR == data ) { - String value = new String(buffer, startOfValue, (pos-2)-startOfValue, "UTF-8"); - options.add(value); - reset( processOperatingSystemCommand(options) ); - } else { - state = LOOKING_FOR_OSC_PARAM; - } - break; - } - - // Is it just too long? - if( pos >= buffer.length ) { - reset(false); - } - } - - /** - * Resets all state to continue with regular parsing - * @param skipBuffer if current buffer should be skipped or written to out - * @throws IOException - */ - private void reset(boolean skipBuffer) throws IOException { - if( !skipBuffer ) { - out.write(buffer, 0, pos); - } - pos=0; - startOfValue=0; - options.clear(); - state = LOOKING_FOR_FIRST_ESC_CHAR; - } - - /** - * - * @param options - * @param command - * @return true if the escape command was processed. - */ - private boolean processEscapeCommand(ArrayList options, int command) throws IOException { - try { - switch(command) { - case 'A': - processCursorUp(optionInt(options, 0, 1)); - return true; - case 'B': - processCursorDown(optionInt(options, 0, 1)); - return true; - case 'C': - processCursorRight(optionInt(options, 0, 1)); - return true; - case 'D': - processCursorLeft(optionInt(options, 0, 1)); - return true; - case 'E': - processCursorDownLine(optionInt(options, 0, 1)); - return true; - case 'F': - processCursorUpLine(optionInt(options, 0, 1)); - return true; - case 'G': - processCursorToColumn(optionInt(options, 0)); - return true; - case 'H': - case 'f': - processCursorTo(optionInt(options, 0, 1), optionInt(options, 1, 1)); - return true; - case 'J': - processEraseScreen(optionInt(options, 0, 0)); - return true; - case 'K': - processEraseLine(optionInt(options, 0, 0)); - return true; - case 'S': - processScrollUp(optionInt(options, 0, 1)); - return true; - case 'T': - processScrollDown(optionInt(options, 0, 1)); - return true; - case 'm': - // Validate all options are ints... - for (Object next : options) { - if( next!=null && next.getClass()!=Integer.class) { - throw new IllegalArgumentException(); - } - } - - int count=0; - for (Object next : options) { - if( next!=null ) { - count++; - int value = ((Integer)next).intValue(); - if( 30 <= value && value <= 37 ) { - processSetForegroundColor(value-30); - } else if( 40 <= value && value <= 47 ) { - processSetBackgroundColor(value-40); - } else if ( 90 <= value && value <= 97 ) { - processSetForegroundColor(value-90, true); - } else if ( 100 <= value && value <= 107 ) { - processSetBackgroundColor(value-100, true); - } else { - switch ( value ) { - case 39: - processDefaultTextColor(); - break; - case 49: - processDefaultBackgroundColor(); - break; - case 0: - processAttributeRest(); - break; - default: - processSetAttribute(value); - } - } - } - } - if( count == 0 ) { - processAttributeRest(); - } - return true; - case 's': - processSaveCursorPosition(); - return true; - case 'u': - processRestoreCursorPosition(); - return true; - - default: - if( 'a' <= command && 'z' <=command ) { - processUnknownExtension(options, command); - return true; - } - if( 'A' <= command && 'Z' <=command ) { - processUnknownExtension(options, command); - return true; - } - return false; - } - } catch (IllegalArgumentException ignore) { - } - return false; - } - - /** - * - * @param options - * @return true if the operating system command was processed. - */ - private boolean processOperatingSystemCommand(ArrayList options) throws IOException { - int command = optionInt(options, 0); - String label = (String) options.get(1); - // for command > 2 label could be composed (i.e. contain ';'), but we'll leave - // it to processUnknownOperatingSystemCommand implementations to handle that - try { - switch (command) { - case 0: - processChangeIconNameAndWindowTitle(label); - return true; - case 1: - processChangeIconName(label); - return true; - case 2: - processChangeWindowTitle(label); - return true; - - default: - // not exactly unknown, but not supported through dedicated process methods: - processUnknownOperatingSystemCommand(command, label); - return true; - } - } catch (IllegalArgumentException ignore) { - } - return false; - } - - protected void processRestoreCursorPosition() throws IOException { - } - protected void processSaveCursorPosition() throws IOException { - } - protected void processScrollDown(int optionInt) throws IOException { - } - protected void processScrollUp(int optionInt) throws IOException { - } - - protected static final int ERASE_SCREEN_TO_END=0; - protected static final int ERASE_SCREEN_TO_BEGINING=1; - protected static final int ERASE_SCREEN=2; - - protected void processEraseScreen(int eraseOption) throws IOException { - } - - protected static final int ERASE_LINE_TO_END=0; - protected static final int ERASE_LINE_TO_BEGINING=1; - protected static final int ERASE_LINE=2; - - protected void processEraseLine(int eraseOption) throws IOException { - } - - protected static final int ATTRIBUTE_INTENSITY_BOLD = 1; // Intensity: Bold - protected static final int ATTRIBUTE_INTENSITY_FAINT = 2; // Intensity; Faint not widely supported - protected static final int ATTRIBUTE_ITALIC = 3; // Italic; on not widely supported. Sometimes treated as inverse. - protected static final int ATTRIBUTE_UNDERLINE = 4; // Underline; Single - protected static final int ATTRIBUTE_BLINK_SLOW = 5; // Blink; Slow less than 150 per minute - protected static final int ATTRIBUTE_BLINK_FAST = 6; // Blink; Rapid MS-DOS ANSI.SYS; 150 per minute or more - protected static final int ATTRIBUTE_NEGATIVE_ON = 7; // Image; Negative inverse or reverse; swap foreground and background - protected static final int ATTRIBUTE_CONCEAL_ON = 8; // Conceal on - protected static final int ATTRIBUTE_UNDERLINE_DOUBLE = 21; // Underline; Double not widely supported - protected static final int ATTRIBUTE_INTENSITY_NORMAL = 22; // Intensity; Normal not bold and not faint - protected static final int ATTRIBUTE_UNDERLINE_OFF = 24; // Underline; None - protected static final int ATTRIBUTE_BLINK_OFF = 25; // Blink; off - protected static final int ATTRIBUTE_NEGATIVE_Off = 27; // Image; Positive - protected static final int ATTRIBUTE_CONCEAL_OFF = 28; // Reveal conceal off - - protected void processSetAttribute(int attribute) throws IOException { - } - - protected static final int BLACK = 0; - protected static final int RED = 1; - protected static final int GREEN = 2; - protected static final int YELLOW = 3; - protected static final int BLUE = 4; - protected static final int MAGENTA = 5; - protected static final int CYAN = 6; - protected static final int WHITE = 7; - - protected void processSetForegroundColor(int color) throws IOException { - processSetForegroundColor(color, false); - } - - protected void processSetForegroundColor(int color, boolean bright) throws IOException { - } - - protected void processSetBackgroundColor(int color) throws IOException { - processSetBackgroundColor(color, false); - } - - protected void processSetBackgroundColor(int color, boolean bright) throws IOException { - } - - protected void processDefaultTextColor() throws IOException { - } - - protected void processDefaultBackgroundColor() throws IOException { - } - - protected void processAttributeRest() throws IOException { - } - - protected void processCursorTo(int row, int col) throws IOException { - } - - protected void processCursorToColumn(int x) throws IOException { - } - - protected void processCursorUpLine(int count) throws IOException { - } - - protected void processCursorDownLine(int count) throws IOException { - // Poor mans impl.. - for(int i=0; i < count; i++) { - out.write('\n'); - } - } - - protected void processCursorLeft(int count) throws IOException { - } - - protected void processCursorRight(int count) throws IOException { - // Poor mans impl.. - for(int i=0; i < count; i++) { - out.write(' '); - } - } - - protected void processCursorDown(int count) throws IOException { - } - - protected void processCursorUp(int count) throws IOException { - } - - protected void processUnknownExtension(ArrayList options, int command) { - } - - protected void processChangeIconNameAndWindowTitle(String label) { + public static final byte[] REST_CODE = resetCode(); + + public AnsiOutputStream(OutputStream os) { + super(os); + } + + private final static int MAX_ESCAPE_SEQUENCE_LENGTH = 100; + private byte[] buffer = new byte[MAX_ESCAPE_SEQUENCE_LENGTH]; + private int pos = 0; + private int startOfValue; + private final ArrayList options = new ArrayList(); + + private static final int LOOKING_FOR_FIRST_ESC_CHAR = 0; + private static final int LOOKING_FOR_SECOND_ESC_CHAR = 1; + private static final int LOOKING_FOR_NEXT_ARG = 2; + private static final int LOOKING_FOR_STR_ARG_END = 3; + private static final int LOOKING_FOR_INT_ARG_END = 4; + private static final int LOOKING_FOR_OSC_COMMAND = 5; + private static final int LOOKING_FOR_OSC_COMMAND_END = 6; + private static final int LOOKING_FOR_OSC_PARAM = 7; + private static final int LOOKING_FOR_ST = 8; + + int state = LOOKING_FOR_FIRST_ESC_CHAR; + + private static final int FIRST_ESC_CHAR = 27; + private static final int SECOND_ESC_CHAR = '['; + private static final int SECOND_OSC_CHAR = ']'; + private static final int BEL = 7; + private static final int SECOND_ST_CHAR = '\\'; + + // TODO: implement to get perf boost: public void write(byte[] b, int off, int len) + + public void write(int data) throws IOException { + switch (state) { + case LOOKING_FOR_FIRST_ESC_CHAR: + if (data == FIRST_ESC_CHAR) { + buffer[pos++] = (byte) data; + state = LOOKING_FOR_SECOND_ESC_CHAR; + } else { + out.write(data); + } + break; + + case LOOKING_FOR_SECOND_ESC_CHAR: + buffer[pos++] = (byte) data; + if (data == SECOND_ESC_CHAR) { + state = LOOKING_FOR_NEXT_ARG; + } else if (data == SECOND_OSC_CHAR) { + state = LOOKING_FOR_OSC_COMMAND; + } else { + reset(false); + } + break; + + case LOOKING_FOR_NEXT_ARG: + buffer[pos++] = (byte) data; + if ('"' == data) { + startOfValue = pos - 1; + state = LOOKING_FOR_STR_ARG_END; + } else if ('0' <= data && data <= '9') { + startOfValue = pos - 1; + state = LOOKING_FOR_INT_ARG_END; + } else if (';' == data) { + options.add(null); + } else if ('?' == data) { + options.add(new Character('?')); + } else if ('=' == data) { + options.add(new Character('=')); + } else { + reset(processEscapeCommand(options, data)); + } + break; + default: + break; + + case LOOKING_FOR_INT_ARG_END: + buffer[pos++] = (byte) data; + if (!('0' <= data && data <= '9')) { + String strValue = new String(buffer, startOfValue, (pos - 1) - startOfValue, "UTF-8"); + Integer value = new Integer(strValue); + options.add(value); + if (data == ';') { + state = LOOKING_FOR_NEXT_ARG; + } else { + reset(processEscapeCommand(options, data)); + } + } + break; + + case LOOKING_FOR_STR_ARG_END: + buffer[pos++] = (byte) data; + if ('"' != data) { + String value = new String(buffer, startOfValue, (pos - 1) - startOfValue, "UTF-8"); + options.add(value); + if (data == ';') { + state = LOOKING_FOR_NEXT_ARG; + } else { + reset(processEscapeCommand(options, data)); + } + } + break; + + case LOOKING_FOR_OSC_COMMAND: + buffer[pos++] = (byte) data; + if ('0' <= data && data <= '9') { + startOfValue = pos - 1; + state = LOOKING_FOR_OSC_COMMAND_END; + } else { + reset(false); + } + break; + + case LOOKING_FOR_OSC_COMMAND_END: + buffer[pos++] = (byte) data; + if (';' == data) { + String strValue = new String(buffer, startOfValue, (pos - 1) - startOfValue, "UTF-8"); + Integer value = new Integer(strValue); + options.add(value); + startOfValue = pos; + state = LOOKING_FOR_OSC_PARAM; + } else if ('0' <= data && data <= '9') { + // already pushed digit to buffer, just keep looking + } else { + // oops, did not expect this + reset(false); + } + break; + + case LOOKING_FOR_OSC_PARAM: + buffer[pos++] = (byte) data; + if (BEL == data) { + String value = new String(buffer, startOfValue, (pos - 1) - startOfValue, "UTF-8"); + options.add(value); + reset(processOperatingSystemCommand(options)); + } else if (FIRST_ESC_CHAR == data) { + state = LOOKING_FOR_ST; + } else { + // just keep looking while adding text + } + break; + + case LOOKING_FOR_ST: + buffer[pos++] = (byte) data; + if (SECOND_ST_CHAR == data) { + String value = new String(buffer, startOfValue, (pos - 2) - startOfValue, "UTF-8"); + options.add(value); + reset(processOperatingSystemCommand(options)); + } else { + state = LOOKING_FOR_OSC_PARAM; + } + break; + } + + // Is it just too long? + if (pos >= buffer.length) { + reset(false); + } + } + + /** + * Resets all state to continue with regular parsing + * @param skipBuffer if current buffer should be skipped or written to out + * @throws IOException + */ + private void reset(boolean skipBuffer) throws IOException { + if (!skipBuffer) { + out.write(buffer, 0, pos); + } + pos = 0; + startOfValue = 0; + options.clear(); + state = LOOKING_FOR_FIRST_ESC_CHAR; + } + + /** + * + * @param options + * @param command + * @return true if the escape command was processed. + */ + private boolean processEscapeCommand(ArrayList options, int command) throws IOException { + try { + switch (command) { + case 'A': + processCursorUp(optionInt(options, 0, 1)); + return true; + case 'B': + processCursorDown(optionInt(options, 0, 1)); + return true; + case 'C': + processCursorRight(optionInt(options, 0, 1)); + return true; + case 'D': + processCursorLeft(optionInt(options, 0, 1)); + return true; + case 'E': + processCursorDownLine(optionInt(options, 0, 1)); + return true; + case 'F': + processCursorUpLine(optionInt(options, 0, 1)); + return true; + case 'G': + processCursorToColumn(optionInt(options, 0)); + return true; + case 'H': + case 'f': + processCursorTo(optionInt(options, 0, 1), optionInt(options, 1, 1)); + return true; + case 'J': + processEraseScreen(optionInt(options, 0, 0)); + return true; + case 'K': + processEraseLine(optionInt(options, 0, 0)); + return true; + case 'S': + processScrollUp(optionInt(options, 0, 1)); + return true; + case 'T': + processScrollDown(optionInt(options, 0, 1)); + return true; + case 'm': + // Validate all options are ints... + for (Object next : options) { + if (next != null && next.getClass() != Integer.class) { + throw new IllegalArgumentException(); + } + } + + int count = 0; + for (Object next : options) { + if (next != null) { + count++; + int value = ((Integer) next).intValue(); + if (30 <= value && value <= 37) { + processSetForegroundColor(value - 30); + } else if (40 <= value && value <= 47) { + processSetBackgroundColor(value - 40); + } else if (90 <= value && value <= 97) { + processSetForegroundColor(value - 90, true); + } else if (100 <= value && value <= 107) { + processSetBackgroundColor(value - 100, true); + } else { + switch (value) { + case 39: + processDefaultTextColor(); + break; + case 49: + processDefaultBackgroundColor(); + break; + case 0: + processAttributeRest(); + break; + default: + processSetAttribute(value); + } + } + } + } + if (count == 0) { + processAttributeRest(); + } + return true; + case 's': + processSaveCursorPosition(); + return true; + case 'u': + processRestoreCursorPosition(); + return true; + + default: + if ('a' <= command && 'z' <= command) { + processUnknownExtension(options, command); + return true; + } + if ('A' <= command && 'Z' <= command) { + processUnknownExtension(options, command); + return true; + } + return false; + } + } catch (IllegalArgumentException ignore) { + } + return false; + } + + /** + * + * @param options + * @return true if the operating system command was processed. + */ + private boolean processOperatingSystemCommand(ArrayList options) throws IOException { + int command = optionInt(options, 0); + String label = (String) options.get(1); + // for command > 2 label could be composed (i.e. contain ';'), but we'll leave + // it to processUnknownOperatingSystemCommand implementations to handle that + try { + switch (command) { + case 0: + processChangeIconNameAndWindowTitle(label); + return true; + case 1: + processChangeIconName(label); + return true; + case 2: + processChangeWindowTitle(label); + return true; + + default: + // not exactly unknown, but not supported through dedicated process methods: + processUnknownOperatingSystemCommand(command, label); + return true; + } + } catch (IllegalArgumentException ignore) { + } + return false; + } + + protected void processRestoreCursorPosition() throws IOException { + } + + protected void processSaveCursorPosition() throws IOException { + } + + protected void processScrollDown(int optionInt) throws IOException { + } + + protected void processScrollUp(int optionInt) throws IOException { + } + + protected static final int ERASE_SCREEN_TO_END = 0; + protected static final int ERASE_SCREEN_TO_BEGINING = 1; + protected static final int ERASE_SCREEN = 2; + + protected void processEraseScreen(int eraseOption) throws IOException { + } + + protected static final int ERASE_LINE_TO_END = 0; + protected static final int ERASE_LINE_TO_BEGINING = 1; + protected static final int ERASE_LINE = 2; + + protected void processEraseLine(int eraseOption) throws IOException { + } + + protected static final int ATTRIBUTE_INTENSITY_BOLD = 1; // Intensity: Bold + protected static final int ATTRIBUTE_INTENSITY_FAINT = 2; // Intensity; Faint not widely supported + protected static final int ATTRIBUTE_ITALIC = 3; // Italic; on not widely supported. Sometimes treated as inverse. + protected static final int ATTRIBUTE_UNDERLINE = 4; // Underline; Single + protected static final int ATTRIBUTE_BLINK_SLOW = 5; // Blink; Slow less than 150 per minute + protected static final int ATTRIBUTE_BLINK_FAST = 6; // Blink; Rapid MS-DOS ANSI.SYS; 150 per minute or more + protected static final int ATTRIBUTE_NEGATIVE_ON = 7; // Image; Negative inverse or reverse; swap foreground and background + protected static final int ATTRIBUTE_CONCEAL_ON = 8; // Conceal on + protected static final int ATTRIBUTE_UNDERLINE_DOUBLE = 21; // Underline; Double not widely supported + protected static final int ATTRIBUTE_INTENSITY_NORMAL = 22; // Intensity; Normal not bold and not faint + protected static final int ATTRIBUTE_UNDERLINE_OFF = 24; // Underline; None + protected static final int ATTRIBUTE_BLINK_OFF = 25; // Blink; off + protected static final int ATTRIBUTE_NEGATIVE_Off = 27; // Image; Positive + protected static final int ATTRIBUTE_CONCEAL_OFF = 28; // Reveal conceal off + + protected void processSetAttribute(int attribute) throws IOException { + } + + protected static final int BLACK = 0; + protected static final int RED = 1; + protected static final int GREEN = 2; + protected static final int YELLOW = 3; + protected static final int BLUE = 4; + protected static final int MAGENTA = 5; + protected static final int CYAN = 6; + protected static final int WHITE = 7; + + protected void processSetForegroundColor(int color) throws IOException { + processSetForegroundColor(color, false); + } + + protected void processSetForegroundColor(int color, boolean bright) throws IOException { + } + + protected void processSetBackgroundColor(int color) throws IOException { + processSetBackgroundColor(color, false); + } + + protected void processSetBackgroundColor(int color, boolean bright) throws IOException { + } + + protected void processDefaultTextColor() throws IOException { + } + + protected void processDefaultBackgroundColor() throws IOException { + } + + protected void processAttributeRest() throws IOException { + } + + protected void processCursorTo(int row, int col) throws IOException { + } + + protected void processCursorToColumn(int x) throws IOException { + } + + protected void processCursorUpLine(int count) throws IOException { + } + + protected void processCursorDownLine(int count) throws IOException { + // Poor mans impl.. + for (int i = 0; i < count; i++) { + out.write('\n'); + } + } + + protected void processCursorLeft(int count) throws IOException { + } + + protected void processCursorRight(int count) throws IOException { + // Poor mans impl.. + for (int i = 0; i < count; i++) { + out.write(' '); + } + } + + protected void processCursorDown(int count) throws IOException { + } + + protected void processCursorUp(int count) throws IOException { + } + + protected void processUnknownExtension(ArrayList options, int command) { + } + + protected void processChangeIconNameAndWindowTitle(String label) { processChangeIconName(label); processChangeWindowTitle(label); - } - - protected void processChangeIconName(String label) { - } - - protected void processChangeWindowTitle(String label) { - } - - protected void processUnknownOperatingSystemCommand(int command, String param) { - } - - private int optionInt(ArrayList options, int index) { - if( options.size() <= index ) - throw new IllegalArgumentException(); - Object value = options.get(index); - if( value == null ) - throw new IllegalArgumentException(); - if( !value.getClass().equals(Integer.class) ) - throw new IllegalArgumentException(); - return ((Integer)value).intValue(); - } - - private int optionInt(ArrayList options, int index, int defaultValue) { - if( options.size() > index ) { - Object value = options.get(index); - if( value == null ) { - return defaultValue; - } - return ((Integer)value).intValue(); - } - return defaultValue; - } - - @Override - public void close() throws IOException { - write(REST_CODE); - flush(); - super.close(); - } - + } + + protected void processChangeIconName(String label) { + } + + protected void processChangeWindowTitle(String label) { + } + + protected void processUnknownOperatingSystemCommand(int command, String param) { + } + + private int optionInt(ArrayList options, int index) { + if (options.size() <= index) + throw new IllegalArgumentException(); + Object value = options.get(index); + if (value == null) + throw new IllegalArgumentException(); + if (!value.getClass().equals(Integer.class)) + throw new IllegalArgumentException(); + return ((Integer) value).intValue(); + } + + private int optionInt(ArrayList options, int index, int defaultValue) { + if (options.size() > index) { + Object value = options.get(index); + if (value == null) { + return defaultValue; + } + return ((Integer) value).intValue(); + } + return defaultValue; + } + + @Override + public void close() throws IOException { + write(REST_CODE); + flush(); + super.close(); + } + static private byte[] resetCode() { try { return new Ansi().reset().toString().getBytes("UTF-8"); diff --git a/jansi/src/main/java/org/fusesource/jansi/AnsiRenderWriter.java b/jansi/src/main/java/org/fusesource/jansi/AnsiRenderWriter.java index e9c5d708..cbb3d16c 100644 --- a/jansi/src/main/java/org/fusesource/jansi/AnsiRenderWriter.java +++ b/jansi/src/main/java/org/fusesource/jansi/AnsiRenderWriter.java @@ -31,8 +31,7 @@ * @since 1.1 */ public class AnsiRenderWriter - extends PrintWriter -{ + extends PrintWriter { public AnsiRenderWriter(final OutputStream out) { super(out); @@ -54,8 +53,7 @@ public AnsiRenderWriter(final Writer out, final boolean autoFlush) { public void write(final String s) { if (test(s)) { super.write(render(s)); - } - else { + } else { super.write(s); } } diff --git a/jansi/src/main/java/org/fusesource/jansi/AnsiRenderer.java b/jansi/src/main/java/org/fusesource/jansi/AnsiRenderer.java index 2bb52b84..6b78073c 100644 --- a/jansi/src/main/java/org/fusesource/jansi/AnsiRenderer.java +++ b/jansi/src/main/java/org/fusesource/jansi/AnsiRenderer.java @@ -44,8 +44,7 @@ * @author Hiram Chirino * @since 1.1 */ -public class AnsiRenderer -{ +public class AnsiRenderer { public static final String BEGIN_TOKEN = "@|"; private static final int BEGIN_TOKEN_LEN = 2; @@ -58,7 +57,8 @@ public class AnsiRenderer public static final String CODE_LIST_SEPARATOR = ","; - private AnsiRenderer() {} + private AnsiRenderer() { + } static public String render(final String input) throws IllegalArgumentException { StringBuffer buff = new StringBuffer(); @@ -71,20 +71,17 @@ static public String render(final String input) throws IllegalArgumentException if (j == -1) { if (i == 0) { return input; - } - else { + } else { buff.append(input.substring(i, input.length())); return buff.toString(); } - } - else { + } else { buff.append(input.substring(i, j)); k = input.indexOf(END_TOKEN, j); if (k == -1) { return input; - } - else { + } else { j += BEGIN_TOKEN_LEN; String spec = input.substring(j, k); @@ -110,12 +107,10 @@ static private String render(final String text, final String... codes) { if (code.isColor()) { if (code.isBackground()) { ansi = ansi.bg(code.getColor()); - } - else { + } else { ansi = ansi.fg(code.getColor()); } - } - else if (code.isAttribute()) { + } else if (code.isAttribute()) { ansi = ansi.a(code.getAttribute()); } } @@ -127,12 +122,11 @@ public static boolean test(final String text) { return text != null && text.contains(BEGIN_TOKEN); } - public static enum Code - { + public static enum Code { // // TODO: Find a better way to keep Code in sync with Color/Attribute/Erase // - + // Colors BLACK(Color.BLACK), RED(Color.RED), diff --git a/jansi/src/main/java/org/fusesource/jansi/AnsiString.java b/jansi/src/main/java/org/fusesource/jansi/AnsiString.java index df3f4508..94040111 100644 --- a/jansi/src/main/java/org/fusesource/jansi/AnsiString.java +++ b/jansi/src/main/java/org/fusesource/jansi/AnsiString.java @@ -28,8 +28,7 @@ * @since 1.1 */ public class AnsiString - implements CharSequence -{ + implements CharSequence { private final CharSequence encoded; private final CharSequence plain; @@ -50,8 +49,7 @@ private CharSequence chew(final CharSequence str) { out.write(str.toString().getBytes()); out.flush(); out.close(); - } - catch (IOException e) { + } catch (IOException e) { throw new RuntimeException(e); } diff --git a/jansi/src/main/java/org/fusesource/jansi/HtmlAnsiOutputStream.java b/jansi/src/main/java/org/fusesource/jansi/HtmlAnsiOutputStream.java index fcf7fe4f..41b79797 100644 --- a/jansi/src/main/java/org/fusesource/jansi/HtmlAnsiOutputStream.java +++ b/jansi/src/main/java/org/fusesource/jansi/HtmlAnsiOutputStream.java @@ -28,113 +28,113 @@ * @author Daniel Doubrovkine */ public class HtmlAnsiOutputStream extends AnsiOutputStream { - - private boolean concealOn = false; - - @Override - public void close() throws IOException { - closeAttributes(); - super.close(); - } - - private static final String[] ANSI_COLOR_MAP = { "black", "red", - "green", "yellow", "blue", "magenta", "cyan", "white", }; - - private static final byte[] BYTES_QUOT = """.getBytes(); - private static final byte[] BYTES_AMP = "&".getBytes(); - private static final byte[] BYTES_LT = "<".getBytes(); - private static final byte[] BYTES_GT = ">".getBytes(); - - public HtmlAnsiOutputStream(OutputStream os) { - super(os); - } - - private List closingAttributes = new ArrayList(); - - private void write(String s) throws IOException { - super.out.write(s.getBytes()); - } - - private void writeAttribute(String s) throws IOException { - write("<" + s + ">"); - closingAttributes.add(0, s.split(" ", 2)[0]); - } - - private void closeAttributes() throws IOException { - for (String attr : closingAttributes) { - write(""); - } - closingAttributes.clear(); - } - - public void write(int data) throws IOException { - switch(data) { - case 34: // " - out.write(BYTES_QUOT); - break; - case 38: // & - out.write(BYTES_AMP); - break; - case 60: // < - out.write(BYTES_LT); - break; - case 62: // > - out.write(BYTES_GT); - break; - default: - super.write(data); - } - } - - public void writeLine(byte[] buf, int offset, int len) throws IOException { - write(buf, offset, len); - closeAttributes(); - } - - @Override - protected void processSetAttribute(int attribute) throws IOException { - switch (attribute) { - case ATTRIBUTE_CONCEAL_ON: - write("\u001B[8m"); - concealOn = true; - break; - case ATTRIBUTE_INTENSITY_BOLD: - writeAttribute("b"); - break; - case ATTRIBUTE_INTENSITY_NORMAL: - closeAttributes(); - break; - case ATTRIBUTE_UNDERLINE: - writeAttribute("u"); - break; - case ATTRIBUTE_UNDERLINE_OFF: - closeAttributes(); - break; - case ATTRIBUTE_NEGATIVE_ON: - break; - case ATTRIBUTE_NEGATIVE_Off: - break; - default: - break; - } - } - - @Override - protected void processAttributeRest() throws IOException { - if (concealOn) { - write("\u001B[0m"); - concealOn = false; - } - closeAttributes(); - } - - @Override - protected void processSetForegroundColor(int color, boolean bright) throws IOException { - writeAttribute("span style=\"color: " + ANSI_COLOR_MAP[color] + ";\""); - } - - @Override - protected void processSetBackgroundColor(int color, boolean bright) throws IOException { - writeAttribute("span style=\"background-color: " + ANSI_COLOR_MAP[color] + ";\""); - } + + private boolean concealOn = false; + + @Override + public void close() throws IOException { + closeAttributes(); + super.close(); + } + + private static final String[] ANSI_COLOR_MAP = {"black", "red", + "green", "yellow", "blue", "magenta", "cyan", "white",}; + + private static final byte[] BYTES_QUOT = """.getBytes(); + private static final byte[] BYTES_AMP = "&".getBytes(); + private static final byte[] BYTES_LT = "<".getBytes(); + private static final byte[] BYTES_GT = ">".getBytes(); + + public HtmlAnsiOutputStream(OutputStream os) { + super(os); + } + + private List closingAttributes = new ArrayList(); + + private void write(String s) throws IOException { + super.out.write(s.getBytes()); + } + + private void writeAttribute(String s) throws IOException { + write("<" + s + ">"); + closingAttributes.add(0, s.split(" ", 2)[0]); + } + + private void closeAttributes() throws IOException { + for (String attr : closingAttributes) { + write(""); + } + closingAttributes.clear(); + } + + public void write(int data) throws IOException { + switch (data) { + case 34: // " + out.write(BYTES_QUOT); + break; + case 38: // & + out.write(BYTES_AMP); + break; + case 60: // < + out.write(BYTES_LT); + break; + case 62: // > + out.write(BYTES_GT); + break; + default: + super.write(data); + } + } + + public void writeLine(byte[] buf, int offset, int len) throws IOException { + write(buf, offset, len); + closeAttributes(); + } + + @Override + protected void processSetAttribute(int attribute) throws IOException { + switch (attribute) { + case ATTRIBUTE_CONCEAL_ON: + write("\u001B[8m"); + concealOn = true; + break; + case ATTRIBUTE_INTENSITY_BOLD: + writeAttribute("b"); + break; + case ATTRIBUTE_INTENSITY_NORMAL: + closeAttributes(); + break; + case ATTRIBUTE_UNDERLINE: + writeAttribute("u"); + break; + case ATTRIBUTE_UNDERLINE_OFF: + closeAttributes(); + break; + case ATTRIBUTE_NEGATIVE_ON: + break; + case ATTRIBUTE_NEGATIVE_Off: + break; + default: + break; + } + } + + @Override + protected void processAttributeRest() throws IOException { + if (concealOn) { + write("\u001B[0m"); + concealOn = false; + } + closeAttributes(); + } + + @Override + protected void processSetForegroundColor(int color, boolean bright) throws IOException { + writeAttribute("span style=\"color: " + ANSI_COLOR_MAP[color] + ";\""); + } + + @Override + protected void processSetBackgroundColor(int color, boolean bright) throws IOException { + writeAttribute("span style=\"background-color: " + ANSI_COLOR_MAP[color] + ";\""); + } } diff --git a/jansi/src/main/java/org/fusesource/jansi/WindowsAnsiOutputStream.java b/jansi/src/main/java/org/fusesource/jansi/WindowsAnsiOutputStream.java index 99798549..98fcb15f 100644 --- a/jansi/src/main/java/org/fusesource/jansi/WindowsAnsiOutputStream.java +++ b/jansi/src/main/java/org/fusesource/jansi/WindowsAnsiOutputStream.java @@ -43,289 +43,289 @@ /** * A Windows ANSI escape processor, uses JNA to access native platform * API's to change the console attributes. - * + * * @since 1.0 * @author Hiram Chirino * @author Joris Kuipers */ public final class WindowsAnsiOutputStream extends AnsiOutputStream { - - private static final long console = GetStdHandle(STD_OUTPUT_HANDLE); - - private static final short FOREGROUND_BLACK = 0; - private static final short FOREGROUND_YELLOW = (short) (FOREGROUND_RED|FOREGROUND_GREEN); - private static final short FOREGROUND_MAGENTA = (short) (FOREGROUND_BLUE|FOREGROUND_RED); - private static final short FOREGROUND_CYAN = (short) (FOREGROUND_BLUE|FOREGROUND_GREEN); - private static final short FOREGROUND_WHITE = (short) (FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE); - - private static final short BACKGROUND_BLACK = 0; - private static final short BACKGROUND_YELLOW = (short) (BACKGROUND_RED|BACKGROUND_GREEN); - private static final short BACKGROUND_MAGENTA = (short) (BACKGROUND_BLUE|BACKGROUND_RED); - private static final short BACKGROUND_CYAN = (short) (BACKGROUND_BLUE|BACKGROUND_GREEN); - private static final short BACKGROUND_WHITE = (short) (BACKGROUND_RED|BACKGROUND_GREEN|BACKGROUND_BLUE); - + + private static final long console = GetStdHandle(STD_OUTPUT_HANDLE); + + private static final short FOREGROUND_BLACK = 0; + private static final short FOREGROUND_YELLOW = (short) (FOREGROUND_RED | FOREGROUND_GREEN); + private static final short FOREGROUND_MAGENTA = (short) (FOREGROUND_BLUE | FOREGROUND_RED); + private static final short FOREGROUND_CYAN = (short) (FOREGROUND_BLUE | FOREGROUND_GREEN); + private static final short FOREGROUND_WHITE = (short) (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); + + private static final short BACKGROUND_BLACK = 0; + private static final short BACKGROUND_YELLOW = (short) (BACKGROUND_RED | BACKGROUND_GREEN); + private static final short BACKGROUND_MAGENTA = (short) (BACKGROUND_BLUE | BACKGROUND_RED); + private static final short BACKGROUND_CYAN = (short) (BACKGROUND_BLUE | BACKGROUND_GREEN); + private static final short BACKGROUND_WHITE = (short) (BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE); + private static final short[] ANSI_FOREGROUND_COLOR_MAP = { - FOREGROUND_BLACK, - FOREGROUND_RED, - FOREGROUND_GREEN, - FOREGROUND_YELLOW, - FOREGROUND_BLUE, - FOREGROUND_MAGENTA, - FOREGROUND_CYAN, - FOREGROUND_WHITE, + FOREGROUND_BLACK, + FOREGROUND_RED, + FOREGROUND_GREEN, + FOREGROUND_YELLOW, + FOREGROUND_BLUE, + FOREGROUND_MAGENTA, + FOREGROUND_CYAN, + FOREGROUND_WHITE, }; - + private static final short[] ANSI_BACKGROUND_COLOR_MAP = { - BACKGROUND_BLACK, - BACKGROUND_RED, - BACKGROUND_GREEN, - BACKGROUND_YELLOW, - BACKGROUND_BLUE, - BACKGROUND_MAGENTA, - BACKGROUND_CYAN, - BACKGROUND_WHITE, + BACKGROUND_BLACK, + BACKGROUND_RED, + BACKGROUND_GREEN, + BACKGROUND_YELLOW, + BACKGROUND_BLUE, + BACKGROUND_MAGENTA, + BACKGROUND_CYAN, + BACKGROUND_WHITE, }; - - private final CONSOLE_SCREEN_BUFFER_INFO info = new CONSOLE_SCREEN_BUFFER_INFO(); + + private final CONSOLE_SCREEN_BUFFER_INFO info = new CONSOLE_SCREEN_BUFFER_INFO(); private final short originalColors; - - private boolean negative; - private short savedX = -1; - private short savedY = -1; - - public WindowsAnsiOutputStream(OutputStream os) throws IOException { - super(os); - getConsoleInfo(); - originalColors = info.attributes; - } - - private void getConsoleInfo() throws IOException { - out.flush(); - if( GetConsoleScreenBufferInfo(console, info) == 0 ) { - throw new IOException("Could not get the screen info: "+WindowsSupport.getLastErrorMessage()); - } - if( negative ) { - info.attributes = invertAttributeColors(info.attributes); - } - } - - private void applyAttribute() throws IOException { - out.flush(); - short attributes = info.attributes; - if( negative ) { - attributes = invertAttributeColors(attributes); - } - if( SetConsoleTextAttribute(console, attributes) == 0 ) { - throw new IOException(WindowsSupport.getLastErrorMessage()); - } - } - - private short invertAttributeColors(short attributes) { - // Swap the the Foreground and Background bits. - int fg = 0x000F & attributes; - fg <<= 8; - int bg = 0X00F0 * attributes; - bg >>=8; - attributes = (short) ((attributes & 0xFF00) | fg | bg); - return attributes; - } - - private void applyCursorPosition() throws IOException { - if( SetConsoleCursorPosition(console, info.cursorPosition.copy()) == 0 ) { - throw new IOException(WindowsSupport.getLastErrorMessage()); - } - } - - @Override - protected void processEraseScreen(int eraseOption) throws IOException { - getConsoleInfo(); - int[] written = new int[1]; - switch(eraseOption) { - case ERASE_SCREEN: - COORD topLeft = new COORD(); - topLeft.x = 0; - topLeft.y = info.window.top; - int screenLength = info.window.height() * info.size.x; - FillConsoleOutputAttribute(console, originalColors, screenLength, topLeft, written); - FillConsoleOutputCharacterW(console, ' ', screenLength, topLeft, written); - break; - case ERASE_SCREEN_TO_BEGINING: - COORD topLeft2 = new COORD(); - topLeft2.x = 0; - topLeft2.y = info.window.top; - int lengthToCursor = (info.cursorPosition.y - info.window.top) * info.size.x - + info.cursorPosition.x; - FillConsoleOutputAttribute(console, originalColors, lengthToCursor, topLeft2, written); - FillConsoleOutputCharacterW(console, ' ', lengthToCursor, topLeft2, written); - break; - case ERASE_SCREEN_TO_END: - int lengthToEnd = (info.window.bottom - info.cursorPosition.y) * info.size.x + - (info.size.x - info.cursorPosition.x); - FillConsoleOutputAttribute(console, originalColors, lengthToEnd, info.cursorPosition.copy(), written); - FillConsoleOutputCharacterW(console, ' ', lengthToEnd, info.cursorPosition.copy(), written); - break; - default: - break; - } - } - - @Override - protected void processEraseLine(int eraseOption) throws IOException { - getConsoleInfo(); - int[] written = new int[1]; - switch(eraseOption) { - case ERASE_LINE: - COORD leftColCurrRow = info.cursorPosition.copy(); - leftColCurrRow.x = 0; - FillConsoleOutputAttribute(console, originalColors, info.size.x, leftColCurrRow, written); - FillConsoleOutputCharacterW(console, ' ', info.size.x, leftColCurrRow, written); - break; - case ERASE_LINE_TO_BEGINING: - COORD leftColCurrRow2 = info.cursorPosition.copy(); - leftColCurrRow2.x = 0; - FillConsoleOutputAttribute(console, originalColors, info.cursorPosition.x, leftColCurrRow2, written); - FillConsoleOutputCharacterW(console, ' ', info.cursorPosition.x, leftColCurrRow2, written); - break; - case ERASE_LINE_TO_END: - int lengthToLastCol = info.size.x - info.cursorPosition.x; - FillConsoleOutputAttribute(console, originalColors, lengthToLastCol, info.cursorPosition.copy(), written); - FillConsoleOutputCharacterW(console, ' ', lengthToLastCol, info.cursorPosition.copy(), written); - break; - default: - break; - } - } - - @Override - protected void processCursorLeft(int count) throws IOException { - getConsoleInfo(); - info.cursorPosition.x = (short) Math.max(0, info.cursorPosition.x-count); - applyCursorPosition(); - } - - @Override - protected void processCursorRight(int count) throws IOException { - getConsoleInfo(); - info.cursorPosition.x = (short)Math.min(info.window.width(), info.cursorPosition.x+count); - applyCursorPosition(); - } - - @Override - protected void processCursorDown(int count) throws IOException { - getConsoleInfo(); - info.cursorPosition.y = (short) Math.min(info.size.y, info.cursorPosition.y+count); - applyCursorPosition(); - } - - @Override - protected void processCursorUp(int count) throws IOException { - getConsoleInfo(); - info.cursorPosition.y = (short) Math.max(info.window.top, info.cursorPosition.y-count); - applyCursorPosition(); - } - - @Override - protected void processCursorTo(int row, int col) throws IOException { - getConsoleInfo(); - info.cursorPosition.y = (short) Math.max(info.window.top, Math.min(info.size.y, info.window.top+row-1)); - info.cursorPosition.x = (short) Math.max(0, Math.min(info.window.width(), col-1)); - applyCursorPosition(); - } - - @Override - protected void processCursorToColumn(int x) throws IOException { - getConsoleInfo(); - info.cursorPosition.x = (short) Math.max(0, Math.min(info.window.width(), x-1)); - applyCursorPosition(); - } - - @Override - protected void processSetForegroundColor(int color, boolean bright) throws IOException { - info.attributes = (short)((info.attributes & ~0x0007 ) | ANSI_FOREGROUND_COLOR_MAP[color]); - applyAttribute(); - } - - @Override - protected void processSetBackgroundColor(int color, boolean bright) throws IOException { - info.attributes = (short)((info.attributes & ~0x0070 ) | ANSI_BACKGROUND_COLOR_MAP[color]); - applyAttribute(); - } - - @Override - protected void processDefaultTextColor() throws IOException { - info.attributes = (short)((info.attributes & ~0x000F ) | (originalColors & 0xF)); - applyAttribute(); - } - - @Override - protected void processDefaultBackgroundColor() throws IOException { - info.attributes = (short)((info.attributes & ~0x00F0 ) | (originalColors & 0xF0)); - applyAttribute(); - } - - @Override - protected void processAttributeRest() throws IOException { - info.attributes = (short)((info.attributes & ~0x00FF ) | originalColors); + + private boolean negative; + private short savedX = -1; + private short savedY = -1; + + public WindowsAnsiOutputStream(OutputStream os) throws IOException { + super(os); + getConsoleInfo(); + originalColors = info.attributes; + } + + private void getConsoleInfo() throws IOException { + out.flush(); + if (GetConsoleScreenBufferInfo(console, info) == 0) { + throw new IOException("Could not get the screen info: " + WindowsSupport.getLastErrorMessage()); + } + if (negative) { + info.attributes = invertAttributeColors(info.attributes); + } + } + + private void applyAttribute() throws IOException { + out.flush(); + short attributes = info.attributes; + if (negative) { + attributes = invertAttributeColors(attributes); + } + if (SetConsoleTextAttribute(console, attributes) == 0) { + throw new IOException(WindowsSupport.getLastErrorMessage()); + } + } + + private short invertAttributeColors(short attributes) { + // Swap the the Foreground and Background bits. + int fg = 0x000F & attributes; + fg <<= 8; + int bg = 0X00F0 * attributes; + bg >>= 8; + attributes = (short) ((attributes & 0xFF00) | fg | bg); + return attributes; + } + + private void applyCursorPosition() throws IOException { + if (SetConsoleCursorPosition(console, info.cursorPosition.copy()) == 0) { + throw new IOException(WindowsSupport.getLastErrorMessage()); + } + } + + @Override + protected void processEraseScreen(int eraseOption) throws IOException { + getConsoleInfo(); + int[] written = new int[1]; + switch (eraseOption) { + case ERASE_SCREEN: + COORD topLeft = new COORD(); + topLeft.x = 0; + topLeft.y = info.window.top; + int screenLength = info.window.height() * info.size.x; + FillConsoleOutputAttribute(console, originalColors, screenLength, topLeft, written); + FillConsoleOutputCharacterW(console, ' ', screenLength, topLeft, written); + break; + case ERASE_SCREEN_TO_BEGINING: + COORD topLeft2 = new COORD(); + topLeft2.x = 0; + topLeft2.y = info.window.top; + int lengthToCursor = (info.cursorPosition.y - info.window.top) * info.size.x + + info.cursorPosition.x; + FillConsoleOutputAttribute(console, originalColors, lengthToCursor, topLeft2, written); + FillConsoleOutputCharacterW(console, ' ', lengthToCursor, topLeft2, written); + break; + case ERASE_SCREEN_TO_END: + int lengthToEnd = (info.window.bottom - info.cursorPosition.y) * info.size.x + + (info.size.x - info.cursorPosition.x); + FillConsoleOutputAttribute(console, originalColors, lengthToEnd, info.cursorPosition.copy(), written); + FillConsoleOutputCharacterW(console, ' ', lengthToEnd, info.cursorPosition.copy(), written); + break; + default: + break; + } + } + + @Override + protected void processEraseLine(int eraseOption) throws IOException { + getConsoleInfo(); + int[] written = new int[1]; + switch (eraseOption) { + case ERASE_LINE: + COORD leftColCurrRow = info.cursorPosition.copy(); + leftColCurrRow.x = 0; + FillConsoleOutputAttribute(console, originalColors, info.size.x, leftColCurrRow, written); + FillConsoleOutputCharacterW(console, ' ', info.size.x, leftColCurrRow, written); + break; + case ERASE_LINE_TO_BEGINING: + COORD leftColCurrRow2 = info.cursorPosition.copy(); + leftColCurrRow2.x = 0; + FillConsoleOutputAttribute(console, originalColors, info.cursorPosition.x, leftColCurrRow2, written); + FillConsoleOutputCharacterW(console, ' ', info.cursorPosition.x, leftColCurrRow2, written); + break; + case ERASE_LINE_TO_END: + int lengthToLastCol = info.size.x - info.cursorPosition.x; + FillConsoleOutputAttribute(console, originalColors, lengthToLastCol, info.cursorPosition.copy(), written); + FillConsoleOutputCharacterW(console, ' ', lengthToLastCol, info.cursorPosition.copy(), written); + break; + default: + break; + } + } + + @Override + protected void processCursorLeft(int count) throws IOException { + getConsoleInfo(); + info.cursorPosition.x = (short) Math.max(0, info.cursorPosition.x - count); + applyCursorPosition(); + } + + @Override + protected void processCursorRight(int count) throws IOException { + getConsoleInfo(); + info.cursorPosition.x = (short) Math.min(info.window.width(), info.cursorPosition.x + count); + applyCursorPosition(); + } + + @Override + protected void processCursorDown(int count) throws IOException { + getConsoleInfo(); + info.cursorPosition.y = (short) Math.min(info.size.y, info.cursorPosition.y + count); + applyCursorPosition(); + } + + @Override + protected void processCursorUp(int count) throws IOException { + getConsoleInfo(); + info.cursorPosition.y = (short) Math.max(info.window.top, info.cursorPosition.y - count); + applyCursorPosition(); + } + + @Override + protected void processCursorTo(int row, int col) throws IOException { + getConsoleInfo(); + info.cursorPosition.y = (short) Math.max(info.window.top, Math.min(info.size.y, info.window.top + row - 1)); + info.cursorPosition.x = (short) Math.max(0, Math.min(info.window.width(), col - 1)); + applyCursorPosition(); + } + + @Override + protected void processCursorToColumn(int x) throws IOException { + getConsoleInfo(); + info.cursorPosition.x = (short) Math.max(0, Math.min(info.window.width(), x - 1)); + applyCursorPosition(); + } + + @Override + protected void processSetForegroundColor(int color, boolean bright) throws IOException { + info.attributes = (short) ((info.attributes & ~0x0007) | ANSI_FOREGROUND_COLOR_MAP[color]); + applyAttribute(); + } + + @Override + protected void processSetBackgroundColor(int color, boolean bright) throws IOException { + info.attributes = (short) ((info.attributes & ~0x0070) | ANSI_BACKGROUND_COLOR_MAP[color]); + applyAttribute(); + } + + @Override + protected void processDefaultTextColor() throws IOException { + info.attributes = (short) ((info.attributes & ~0x000F) | (originalColors & 0xF)); + applyAttribute(); + } + + @Override + protected void processDefaultBackgroundColor() throws IOException { + info.attributes = (short) ((info.attributes & ~0x00F0) | (originalColors & 0xF0)); + applyAttribute(); + } + + @Override + protected void processAttributeRest() throws IOException { + info.attributes = (short) ((info.attributes & ~0x00FF) | originalColors); this.negative = false; - applyAttribute(); - } - - @Override - protected void processSetAttribute(int attribute) throws IOException { - switch(attribute) { - case ATTRIBUTE_INTENSITY_BOLD: - info.attributes = (short)(info.attributes | FOREGROUND_INTENSITY ); - applyAttribute(); - break; - case ATTRIBUTE_INTENSITY_NORMAL: - info.attributes = (short)(info.attributes & ~FOREGROUND_INTENSITY ); - applyAttribute(); - break; - - // Yeah, setting the background intensity is not underlining.. but it's best we can do - // using the Windows console API - case ATTRIBUTE_UNDERLINE: - info.attributes = (short)(info.attributes | BACKGROUND_INTENSITY ); - applyAttribute(); - break; - case ATTRIBUTE_UNDERLINE_OFF: - info.attributes = (short)(info.attributes & ~BACKGROUND_INTENSITY ); - applyAttribute(); - break; - - case ATTRIBUTE_NEGATIVE_ON: - negative = true; - applyAttribute(); - break; - case ATTRIBUTE_NEGATIVE_Off: - negative = false; - applyAttribute(); - break; - default: - break; - } - } - - @Override - protected void processSaveCursorPosition() throws IOException { - getConsoleInfo(); - savedX = info.cursorPosition.x; - savedY = info.cursorPosition.y; - } - - @Override - protected void processRestoreCursorPosition() throws IOException { - // restore only if there was a save operation first - if (savedX != -1 && savedY != -1) { - out.flush(); - info.cursorPosition.x = savedX; - info.cursorPosition.y = savedY; - applyCursorPosition(); - } - } - - @Override - protected void processChangeWindowTitle(String label) { - SetConsoleTitle(label); - } + applyAttribute(); + } + + @Override + protected void processSetAttribute(int attribute) throws IOException { + switch (attribute) { + case ATTRIBUTE_INTENSITY_BOLD: + info.attributes = (short) (info.attributes | FOREGROUND_INTENSITY); + applyAttribute(); + break; + case ATTRIBUTE_INTENSITY_NORMAL: + info.attributes = (short) (info.attributes & ~FOREGROUND_INTENSITY); + applyAttribute(); + break; + + // Yeah, setting the background intensity is not underlining.. but it's best we can do + // using the Windows console API + case ATTRIBUTE_UNDERLINE: + info.attributes = (short) (info.attributes | BACKGROUND_INTENSITY); + applyAttribute(); + break; + case ATTRIBUTE_UNDERLINE_OFF: + info.attributes = (short) (info.attributes & ~BACKGROUND_INTENSITY); + applyAttribute(); + break; + + case ATTRIBUTE_NEGATIVE_ON: + negative = true; + applyAttribute(); + break; + case ATTRIBUTE_NEGATIVE_Off: + negative = false; + applyAttribute(); + break; + default: + break; + } + } + + @Override + protected void processSaveCursorPosition() throws IOException { + getConsoleInfo(); + savedX = info.cursorPosition.x; + savedY = info.cursorPosition.y; + } + + @Override + protected void processRestoreCursorPosition() throws IOException { + // restore only if there was a save operation first + if (savedX != -1 && savedY != -1) { + out.flush(); + info.cursorPosition.x = savedX; + info.cursorPosition.y = savedY; + applyCursorPosition(); + } + } + + @Override + protected void processChangeWindowTitle(String label) { + SetConsoleTitle(label); + } } diff --git a/jansi/src/test/java/org/fusesource/jansi/AnsiConsoleExample.java b/jansi/src/test/java/org/fusesource/jansi/AnsiConsoleExample.java index c262484e..da90098b 100644 --- a/jansi/src/test/java/org/fusesource/jansi/AnsiConsoleExample.java +++ b/jansi/src/test/java/org/fusesource/jansi/AnsiConsoleExample.java @@ -20,29 +20,30 @@ import java.io.IOException; /** - * + * * @author Hiram Chirino */ public class AnsiConsoleExample { - private AnsiConsoleExample() {} + private AnsiConsoleExample() { + } - public static void main(String[] args) throws IOException { + public static void main(String[] args) throws IOException { String file = "src/test/resources/jansi.ans"; - if( args.length>0 ) - file = args[0]; + if (args.length > 0) + file = args[0]; // Allows us to disable ANSI processing. - if( "true".equals(System.getProperty("jansi", "true")) ) { - AnsiConsole.systemInstall(); + if ("true".equals(System.getProperty("jansi", "true"))) { + AnsiConsole.systemInstall(); } - - FileInputStream f = new FileInputStream(file); + + FileInputStream f = new FileInputStream(file); int c; - while( (c=f.read())>=0 ) { - System.out.write(c); + while ((c = f.read()) >= 0) { + System.out.write(c); } f.close(); - } - + } + } diff --git a/jansi/src/test/java/org/fusesource/jansi/AnsiConsoleExample2.java b/jansi/src/test/java/org/fusesource/jansi/AnsiConsoleExample2.java index b18af31f..30cbdb1e 100755 --- a/jansi/src/test/java/org/fusesource/jansi/AnsiConsoleExample2.java +++ b/jansi/src/test/java/org/fusesource/jansi/AnsiConsoleExample2.java @@ -18,35 +18,37 @@ import java.io.FileInputStream; import java.io.IOException; + import static org.fusesource.jansi.Ansi.*; /** - * + * * @author Hiram Chirino */ public class AnsiConsoleExample2 { - private AnsiConsoleExample2() {} + private AnsiConsoleExample2() { + } - public static void main(String[] args) throws IOException { + public static void main(String[] args) throws IOException { String file = "src/test/resources/jansi.ans"; - if( args.length>0 ) - file = args[0]; + if (args.length > 0) + file = args[0]; // Allows us to disable ANSI processing. - if( "true".equals(System.getProperty("jansi", "true")) ) { - AnsiConsole.systemInstall(); + if ("true".equals(System.getProperty("jansi", "true"))) { + AnsiConsole.systemInstall(); } - + System.out.print(ansi().reset().eraseScreen().cursor(1, 1)); System.out.print("======================================================================="); - FileInputStream f = new FileInputStream(file); + FileInputStream f = new FileInputStream(file); int c; - while( (c=f.read())>=0 ) { - System.out.write(c); + while ((c = f.read()) >= 0) { + System.out.write(c); } f.close(); System.out.println("======================================================================="); - } - + } + } diff --git a/jansi/src/test/java/org/fusesource/jansi/AnsiRenderWriterTest.java b/jansi/src/test/java/org/fusesource/jansi/AnsiRenderWriterTest.java index e9fe49e5..3871154b 100644 --- a/jansi/src/test/java/org/fusesource/jansi/AnsiRenderWriterTest.java +++ b/jansi/src/test/java/org/fusesource/jansi/AnsiRenderWriterTest.java @@ -29,8 +29,7 @@ * * @author Jason Dillon */ -public class AnsiRenderWriterTest -{ +public class AnsiRenderWriterTest { private ByteArrayOutputStream baos; private AnsiRenderWriter out; diff --git a/jansi/src/test/java/org/fusesource/jansi/AnsiRendererTest.java b/jansi/src/test/java/org/fusesource/jansi/AnsiRendererTest.java index d6159d09..8cd149db 100644 --- a/jansi/src/test/java/org/fusesource/jansi/AnsiRendererTest.java +++ b/jansi/src/test/java/org/fusesource/jansi/AnsiRendererTest.java @@ -30,8 +30,7 @@ * * @author Jason Dillon */ -public class AnsiRendererTest -{ +public class AnsiRendererTest { @Before public void setUp() { Ansi.setEnabled(true); @@ -78,7 +77,7 @@ public void testRender4() { .a(INTENSITY_BOLD).fg(RED).a("foo bar baz").reset() .toString(), str); } - + @Test public void testRender5() { // Check the ansi() render method. @@ -86,7 +85,7 @@ public void testRender5() { System.out.println(str); assertEquals(ansi().a(INTENSITY_BOLD).a("Hello").reset().toString(), str); } - + @Test public void testRenderNothing() { diff --git a/jansi/src/test/java/org/fusesource/jansi/AnsiStringTest.java b/jansi/src/test/java/org/fusesource/jansi/AnsiStringTest.java index bfebe41a..473349e7 100644 --- a/jansi/src/test/java/org/fusesource/jansi/AnsiStringTest.java +++ b/jansi/src/test/java/org/fusesource/jansi/AnsiStringTest.java @@ -25,8 +25,7 @@ * * @author Jason Dillon */ -public class AnsiStringTest -{ +public class AnsiStringTest { @Test public void testNotEncoded() throws Exception { AnsiString as = new AnsiString("foo"); diff --git a/jansi/src/test/java/org/fusesource/jansi/AnsiTest.java b/jansi/src/test/java/org/fusesource/jansi/AnsiTest.java index 49a666fa..018ee24a 100644 --- a/jansi/src/test/java/org/fusesource/jansi/AnsiTest.java +++ b/jansi/src/test/java/org/fusesource/jansi/AnsiTest.java @@ -26,13 +26,11 @@ * * @author Jason Dillon */ -public class AnsiTest -{ +public class AnsiTest { @Test public void testSetEnabled() throws Exception { Ansi.setEnabled(false); - new Thread() - { + new Thread() { @Override public void run() { assertEquals(false, Ansi.isEnabled()); @@ -40,8 +38,7 @@ public void run() { }.run(); Ansi.setEnabled(true); - new Thread() - { + new Thread() { @Override public void run() { assertEquals(true, Ansi.isEnabled()); diff --git a/jansi/src/test/java/org/fusesource/jansi/HtmlAnsiOutputStreamTest.java b/jansi/src/test/java/org/fusesource/jansi/HtmlAnsiOutputStreamTest.java index 108b3e2b..4d00f7de 100644 --- a/jansi/src/test/java/org/fusesource/jansi/HtmlAnsiOutputStreamTest.java +++ b/jansi/src/test/java/org/fusesource/jansi/HtmlAnsiOutputStreamTest.java @@ -28,60 +28,60 @@ */ public class HtmlAnsiOutputStreamTest { - @Test - public void testNoMarkup() throws IOException { - assertEquals("line", colorize("line")); - } + @Test + public void testNoMarkup() throws IOException { + assertEquals("line", colorize("line")); + } - @Test - public void testClear() throws IOException { - assertEquals("", colorize("")); - assertEquals("hello world", colorize("hello world")); - } + @Test + public void testClear() throws IOException { + assertEquals("", colorize("")); + assertEquals("hello world", colorize("hello world")); + } - @Test - public void testBold() throws IOException { - assertEquals("hello world", colorize("hello world")); - } + @Test + public void testBold() throws IOException { + assertEquals("hello world", colorize("hello world")); + } - @Test - public void testGreen() throws IOException { - assertEquals("hello world", - colorize("hello world")); - } + @Test + public void testGreen() throws IOException { + assertEquals("hello world", + colorize("hello world")); + } - @Test - public void testGreenOnWhite() throws IOException { - assertEquals("hello world", - colorize("hello world")); - } - - @Test - public void testEscapeHtml() throws IOException { - assertEquals(""", colorize("\"")); - assertEquals("&", colorize("&")); - assertEquals("<", colorize("<")); - assertEquals(">", colorize(">")); - assertEquals(""&<>", colorize("\"&<>")); - } + @Test + public void testGreenOnWhite() throws IOException { + assertEquals("hello world", + colorize("hello world")); + } - @Test - public void testResetOnOpen() throws IOException { - assertEquals("red", - colorize("red")); - } - - @Test - public void testUTF8Character() throws IOException { - assertEquals("\u3053\u3093\u306b\u3061\u306f", - colorize("\u3053\u3093\u306b\u3061\u306f")); - } + @Test + public void testEscapeHtml() throws IOException { + assertEquals(""", colorize("\"")); + assertEquals("&", colorize("&")); + assertEquals("<", colorize("<")); + assertEquals(">", colorize(">")); + assertEquals(""&<>", colorize("\"&<>")); + } - private String colorize(String text) throws IOException { - ByteArrayOutputStream os = new ByteArrayOutputStream(); - HtmlAnsiOutputStream hos = new HtmlAnsiOutputStream(os); - hos.write(text.getBytes("UTF-8")); - hos.close(); - return new String(os.toByteArray(), "UTF-8"); - } + @Test + public void testResetOnOpen() throws IOException { + assertEquals("red", + colorize("red")); + } + + @Test + public void testUTF8Character() throws IOException { + assertEquals("\u3053\u3093\u306b\u3061\u306f", + colorize("\u3053\u3093\u306b\u3061\u306f")); + } + + private String colorize(String text) throws IOException { + ByteArrayOutputStream os = new ByteArrayOutputStream(); + HtmlAnsiOutputStream hos = new HtmlAnsiOutputStream(os); + hos.write(text.getBytes("UTF-8")); + hos.close(); + return new String(os.toByteArray(), "UTF-8"); + } }