Skip to content

Commit

Permalink
#151 extracted more common code to AnsiProcessor
Browse files Browse the repository at this point in the history
  • Loading branch information
hboutemy committed Jun 29, 2019
1 parent 7e6bd5b commit f9e85d0
Show file tree
Hide file tree
Showing 3 changed files with 255 additions and 481 deletions.
245 changes: 6 additions & 239 deletions jansi/src/main/java/org/fusesource/jansi/AnsiOutputStream.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
import java.io.OutputStream; // expected diff with AnsiPrintStream.java
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Iterator;

/**
* A ANSI print stream extracts ANSI escape codes written to
Expand Down Expand Up @@ -127,7 +126,7 @@ public synchronized void write(int data) throws IOException { // expected diff w
} else if ('=' == data) {
options.add('=');
} else {
reset(processEscapeCommand(options, data));
reset(ap.processEscapeCommand(options, data)); // expected diff with AnsiOutputStream.java
}
break;
default:
Expand All @@ -142,7 +141,7 @@ public synchronized void write(int data) throws IOException { // expected diff w
if (data == ';') {
state = LOOKING_FOR_NEXT_ARG;
} else {
reset(processEscapeCommand(options, data));
reset(ap.processEscapeCommand(options, data)); // expected diff with AnsiOutputStream.java
}
}
break;
Expand All @@ -155,7 +154,7 @@ public synchronized void write(int data) throws IOException { // expected diff w
if (data == ';') {
state = LOOKING_FOR_NEXT_ARG;
} else {
reset(processEscapeCommand(options, data));
reset(ap.processEscapeCommand(options, data)); // expected diff with AnsiOutputStream.java
}
}
break;
Expand Down Expand Up @@ -191,7 +190,7 @@ public synchronized void write(int data) throws IOException { // expected diff w
if (BEL == data) {
String value = new String(buffer, startOfValue, (pos - 1) - startOfValue, Charset.defaultCharset());
options.add(value);
reset(processOperatingSystemCommand(options));
reset(ap.processOperatingSystemCommand(options));
} else if (FIRST_ESC_CHAR == data) {
state = LOOKING_FOR_ST;
} else {
Expand All @@ -204,15 +203,15 @@ public synchronized void write(int data) throws IOException { // expected diff w
if (SECOND_ST_CHAR == data) {
String value = new String(buffer, startOfValue, (pos - 2) - startOfValue, Charset.defaultCharset());
options.add(value);
reset(processOperatingSystemCommand(options));
reset(ap.processOperatingSystemCommand(options));
} else {
state = LOOKING_FOR_OSC_PARAM;
}
break;

case LOOKING_FOR_CHARSET:
options.add(Character.valueOf((char) data));
reset(processCharsetSelect(options));
reset(ap.processCharsetSelect(options));
break;
}

Expand All @@ -237,238 +236,6 @@ private void reset(boolean skipBuffer) throws IOException { // expected diff wit
state = LOOKING_FOR_FIRST_ESC_CHAR;
}

/**
* Helper for processEscapeCommand() to iterate over integer options
* @param optionsIterator the underlying iterator
* @throws IOException if no more non-null values left
*/
private int getNextOptionInt(Iterator<Object> optionsIterator) throws IOException {
for (;;) {
if (!optionsIterator.hasNext())
throw new IllegalArgumentException();
Object arg = optionsIterator.next();
if (arg != null)
return (Integer) arg;
}
}

/**
*
* @param options
* @param command
* @return true if the escape command was processed.
*/
private boolean processEscapeCommand(ArrayList<Object> options, int command) throws IOException { // expected diff with AnsiPrintStream.java
try {
switch (command) {
case 'A':
ap.processCursorUp(optionInt(options, 0, 1));
return true;
case 'B':
ap.processCursorDown(optionInt(options, 0, 1));
return true;
case 'C':
ap.processCursorRight(optionInt(options, 0, 1));
return true;
case 'D':
ap.processCursorLeft(optionInt(options, 0, 1));
return true;
case 'E':
ap.processCursorDownLine(optionInt(options, 0, 1));
return true;
case 'F':
ap.processCursorUpLine(optionInt(options, 0, 1));
return true;
case 'G':
ap.processCursorToColumn(optionInt(options, 0));
return true;
case 'H':
case 'f':
ap.processCursorTo(optionInt(options, 0, 1), optionInt(options, 1, 1));
return true;
case 'J':
ap.processEraseScreen(optionInt(options, 0, 0));
return true;
case 'K':
ap.processEraseLine(optionInt(options, 0, 0));
return true;
case 'L':
ap.processInsertLine(optionInt(options, 0, 1));
return true;
case 'M':
ap.processDeleteLine(optionInt(options, 0, 1));
return true;
case 'S':
ap.processScrollUp(optionInt(options, 0, 1));
return true;
case 'T':
ap.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;
Iterator<Object> optionsIterator = options.iterator();
while (optionsIterator.hasNext()) {
Object next = optionsIterator.next();
if (next != null) {
count++;
int value = (Integer) next;
if (30 <= value && value <= 37) {
ap.processSetForegroundColor(value - 30);
} else if (40 <= value && value <= 47) {
ap.processSetBackgroundColor(value - 40);
} else if (90 <= value && value <= 97) {
ap.processSetForegroundColor(value - 90, true);
} else if (100 <= value && value <= 107) {
ap.processSetBackgroundColor(value - 100, true);
} else if (value == 38 || value == 48) {
// extended color like `esc[38;5;<index>m` or `esc[38;2;<r>;<g>;<b>m`
int arg2or5 = getNextOptionInt(optionsIterator);
if (arg2or5 == 2) {
// 24 bit color style like `esc[38;2;<r>;<g>;<b>m`
int r = getNextOptionInt(optionsIterator);
int g = getNextOptionInt(optionsIterator);
int b = getNextOptionInt(optionsIterator);
if (r >= 0 && r <= 255 && g >= 0 && g <= 255 && b >= 0 && b <= 255) {
if (value == 38)
ap.processSetForegroundColorExt(r, g, b);
else
ap.processSetBackgroundColorExt(r, g, b);
} else {
throw new IllegalArgumentException();
}
}
else if (arg2or5 == 5) {
// 256 color style like `esc[38;5;<index>m`
int paletteIndex = getNextOptionInt(optionsIterator);
if (paletteIndex >= 0 && paletteIndex <= 255) {
if (value == 38)
ap.processSetForegroundColorExt(paletteIndex);
else
ap.processSetBackgroundColorExt(paletteIndex);
} else {
throw new IllegalArgumentException();
}
}
else {
throw new IllegalArgumentException();
}
} else {
switch (value) {
case 39:
ap.processDefaultTextColor();
break;
case 49:
ap.processDefaultBackgroundColor();
break;
case 0:
ap.processAttributeRest();
break;
default:
ap.processSetAttribute(value);
}
}
}
}
if (count == 0) {
ap.processAttributeRest();
}
return true;
case 's':
ap.processSaveCursorPosition();
return true;
case 'u':
ap.processRestoreCursorPosition();
return true;

default:
if ('a' <= command && 'z' <= command) {
ap.processUnknownExtension(options, command);
return true;
}
if ('A' <= command && 'Z' <= command) {
ap.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<Object> options) throws IOException { // expected diff with AnsiPrintStream.java
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:
ap.processChangeIconNameAndWindowTitle(label);
return true;
case 1:
ap.processChangeIconName(label);
return true;
case 2:
ap.processChangeWindowTitle(label);
return true;

default:
// not exactly unknown, but not supported through dedicated process methods:
ap.processUnknownOperatingSystemCommand(command, label);
return true;
}
} catch (IllegalArgumentException ignore) {
}
return false;
}

/**
* Process character set sequence.
* @param options set of options
* @return true if the charcter set select command was processed.
*/
private boolean processCharsetSelect(ArrayList<Object> options) {
int set = optionInt(options, 0);
char seq = ((Character) options.get(1)).charValue();
ap.processCharsetSelect(set, seq);
return true;
}

private int optionInt(ArrayList<Object> 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;
}

private int optionInt(ArrayList<Object> options, int index, int defaultValue) {
if (options.size() > index) {
Object value = options.get(index);
if (value == null) {
return defaultValue;
}
return (Integer) value;
}
return defaultValue;
}

@Override
public void close() throws IOException { // expected diff with AnsiPrintStream.java
write(RESET_CODE); // expected diff with AnsiPrintStream.java
Expand Down
Loading

0 comments on commit f9e85d0

Please sign in to comment.