implement jterminal in source

This commit is contained in:
ccureau 2017-08-30 10:34:50 -05:00
parent 18ce120984
commit be72c2ff09
20 changed files with 2066 additions and 0 deletions

View File

@ -0,0 +1,5 @@
eclipse.preferences.version=1
encoding//src/main/java=UTF-8
encoding//src/main/resources=UTF-8
encoding//src/test/java=UTF-8
encoding/<project>=UTF-8

View File

@ -0,0 +1,5 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
org.eclipse.jdt.core.compiler.compliance=1.7
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
org.eclipse.jdt.core.compiler.source=1.7

View File

@ -0,0 +1,4 @@
activeProfiles=
eclipse.preferences.version=1
resolveWorkspaceProjects=true
version=1

View File

@ -0,0 +1,92 @@
/*
* Copyright (c) 2009-2011 Graham Edgecombe.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
package com.loomcom.symon.jterminal;
/**
* A {@link TerminalModel} which implements some common behaviour.
* @author Graham Edgecombe
*/
public abstract class AbstractTerminalModel implements TerminalModel {
@Override
public void clear() {
int rows = getRows(), columns = getColumns();
for (int column = 0; column < columns; column++) {
for (int row = 0; row < rows; row++) {
setCell(column, row, null);
}
}
}
@Override
public void moveCursorBack(int n) {
if (n < 0) {
throw new IllegalArgumentException("n must be positive");
}
int cursorColumn = getCursorColumn() - n;
if (cursorColumn < 0) {
cursorColumn = 0;
}
setCursorColumn(cursorColumn);
}
@Override
public void moveCursorForward(int n) {
if (n < 0) {
throw new IllegalArgumentException("n must be positive");
}
int columns = getColumns();
int cursorColumn = getCursorColumn() + n;
if (cursorColumn >= columns) {
cursorColumn = columns - 1;
}
setCursorColumn(cursorColumn);
}
@Override
public void moveCursorDown(int n) {
if (n < 0) {
throw new IllegalArgumentException("n must be positive");
}
int bufferSize = getBufferSize();
int cursorRow = getCursorRow() + n;
if (cursorRow >= bufferSize) {
cursorRow = bufferSize - 1;
}
setCursorRow(cursorRow);
}
@Override
public void moveCursorUp(int n) {
if (n < 0) {
throw new IllegalArgumentException("n must be positive");
}
int cursorRow = getCursorRow() - n;
if (cursorRow < 0) {
cursorRow = 0;
}
setCursorRow(cursorRow);
}
}

View File

@ -0,0 +1,198 @@
/*
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
* Portions Copyright (c) 2009-2011 Graham Edgecombe
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.loomcom.symon.jterminal;
import com.loomcom.symon.jterminal.vt100.Vt100TerminalModel;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.event.AdjustmentEvent;
import java.awt.event.AdjustmentListener;
import javax.swing.JComponent;
import javax.swing.JScrollBar;
/**
* Swing terminal emulation component
* @author Seth J. Morabito
*
*/
public class JTerminal extends JComponent {
private static final long serialVersionUID = 2871625194146986567L;
private int borderWidth = 0;
private JScrollBar scrollBar;
/**
* The terminal emulation model
*/
private TerminalModel model;
/**
* The font this terminal uses
*/
private Font font;
/**
* The cell width in pixels
*/
private int cellWidth;
/**
* The cell height in pixels
*/
private int cellHeight;
/**
* Max descender for font
*/
private int maxDescender;
public JTerminal(Font font) {
this(new Vt100TerminalModel(), font);
}
public JTerminal(TerminalModel model, Font font) {
setModel(model);
setFont(font);
init();
}
public void setBorderWidth(int borderWidth) {
this.borderWidth = borderWidth;
revalidate();
}
public int getBorderWidth() {
return borderWidth;
}
public void setFont(Font font) {
this.font = font;
setCellWidthAndHeight(font);
revalidate();
}
public Font getFont() {
return font;
}
public void setModel(TerminalModel model) {
if (model == null) {
throw new NullPointerException("model");
}
this.model = model;
}
public TerminalModel getModel() {
return model;
}
public void println(String str) {
if (str == null) {
throw new NullPointerException("str");
}
print(str.concat("\r\n"));
}
public void print(String str) {
model.print(str);
}
public Dimension getMinimumSize() {
return new Dimension(model.getColumns() * cellWidth + borderWidth * 2,
model.getRows() * cellHeight + borderWidth * 2);
}
public Dimension getMaximumSize() {
return getMinimumSize();
}
public Dimension getPreferredSize() {
return getMinimumSize();
}
public void paint(Graphics g) {
g.setFont(font);
int width = model.getColumns();
int height = model.getBufferSize();
g.setColor(model.getDefaultBackgroundColor());
g.fillRect(0, 0, width * cellWidth + borderWidth * 2, height * cellHeight + borderWidth * 2);
int start = scrollBar == null ? 0 : scrollBar.getValue();
for (int y = start; y < height; y++) {
for (int x = 0; x < width; x++) {
TerminalCell cell = model.getCell(x, y);
boolean cursorHere = (model.getCursorRow() == y) && (model.getCursorColumn() == x);
if ((cursorHere) && (cell == null)) {
cell = new TerminalCell(' ', model.getDefaultBackgroundColor(), model.getDefaultForegroundColor());
}
if (cell != null) {
int px = x * cellWidth + borderWidth;
int py = (y - start) * cellHeight + borderWidth;
g.setColor(cursorHere ? cell.getForegroundColor() : cell.getBackgroundColor());
g.fillRect(px, py, cellWidth, cellHeight);
g.setColor(cursorHere ? cell.getBackgroundColor() : cell.getForegroundColor());
g.drawChars(new char[] { cell.getCharacter() }, 0, 1, px, py + cellHeight - maxDescender);
}
}
}
}
private void init() {
setLayout(new BorderLayout(0, 0));
int rows = model.getRows();
int bufferSize = model.getBufferSize();
if (bufferSize > rows) {
scrollBar = new JScrollBar(1, 0, rows, 0, bufferSize + 1);
scrollBar.addAdjustmentListener(new AdjustmentListener() {
public void adjustmentValueChanged(AdjustmentEvent evt) {
repaint();
}
});
add("After", scrollBar);
}
repaint();
}
private void setCellWidthAndHeight(Font font) {
FontMetrics metrics = getFontMetrics(font);
cellWidth = metrics.charWidth('W');
cellHeight = metrics.getHeight();
maxDescender = metrics.getMaxDescent();
}
}

View File

@ -0,0 +1,96 @@
/*
* Copyright (c) 2009-2011 Graham Edgecombe.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
package com.loomcom.symon.jterminal;
import java.awt.Color;
/**
* Represents a single terminal cell which contains a character, background
* color and foreground color.
* @author Graham Edgecombe
*/
public class TerminalCell {
/**
* The character.
*/
private final char character;
/**
* The background color.
*/
private final Color backgroundColor;
/**
* The foreground color.
*/
private final Color foregroundColor;
/**
* Creates a terminal cell with the specified character, background color
* and foreground color.
* @param character The character.
* @param backgroundColor The background color.
* @param foregroundColor The foreground color.
* @throws NullPointerException if the background or foreground color(s)
* are {@code null}.
*/
public TerminalCell(char character, Color backgroundColor, Color foregroundColor) {
if (backgroundColor == null) {
throw new NullPointerException("backgroundColor");
}
if (foregroundColor == null) {
throw new NullPointerException("foregroundColor");
}
this.character = character;
this.backgroundColor = backgroundColor;
this.foregroundColor = foregroundColor;
}
/**
* Gets the character.
* @return The character.
*/
public char getCharacter() {
return character;
}
/**
* Gets the background color.
* @return The background color.
*/
public Color getBackgroundColor() {
return backgroundColor;
}
/**
* Gets the foreground color.
* @return The foreground color.
*/
public Color getForegroundColor() {
return foregroundColor;
}
}

View File

@ -0,0 +1,167 @@
/*
* Copyright (c) 2009-2011 Graham Edgecombe.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
package com.loomcom.symon.jterminal;
import java.awt.Color;
import com.loomcom.symon.jterminal.bell.BellStrategy;
/**
* Model for terminals - defines methods for getting/setting cells, printing
* text to a terminal and getting the size of the terminal and buffer.
*/
public interface TerminalModel {
/**
* Gets the bell strategy.
* @return The bell strategy.
*/
public BellStrategy getBellStrategy();
/**
* Sets the bell strategy.
* @param strategy The bell strategy.
* @throws NullPointerException if the strategy is {@code null}.
*/
public void setBellStrategy(BellStrategy strategy);
/**
* Clears the terminal.
*/
public void clear();
/**
* Moves the cursor back n characters.
* @param n The number of characters.
* @throws IllegalArgumentException if n is not positive.
*/
public void moveCursorBack(int n);
/**
* Moves the cursor forward n characters.
* @param n The number of characters.
* @throws IllegalArgumentException if n is not positive.
*/
public void moveCursorForward(int n);
/**
* Moves the cursor down n characters.
* @param n The number of characters.
* @throws IllegalArgumentException if n is not positive.
*/
public void moveCursorDown(int n);
/**
* Moves the cursor up n characters.
* @param n The number of characters.
* @throws IllegalArgumentException if n is not positive.
*/
public void moveCursorUp(int n);
/**
* Sets a cell.
* @param column The column.
* @param row The row.
* @param cell The cell.
* @throws IndexOutOfBoundsException if the column and/or row number(s) are
* out of bounds.
*/
public void setCell(int column, int row, TerminalCell cell);
/**
* Gets a cell.
* @param column The column.
* @param row The row.
* @return The cell.
* @throws IndexOutOfBoundsException if the column and/or row number(s) are
* out of bounds.
*/
public TerminalCell getCell(int column, int row);
/**
* Prints the specified string to the terminal at the cursor position,
* interpreting any escape sequences/special ASCII codes the model may
* support. Lines will be wrapped if necessary.
* @param str The string to print.
* @throws NullPointerException if the string is {@code null}.
*/
public void print(String str);
/**
* Gets the number of columns.
* @return The number of columns.
*/
public int getColumns();
/**
* Gets the number of rows.
* @return The number of rows.
*/
public int getRows();
/**
* Gets the buffer size.
* @return The buffer size.
*/
public int getBufferSize();
/**
* Gets the cursor row.
* @return The cursor row.
*/
public int getCursorRow();
/**
* Sets the cursor row.
* @param row The cursor row.
* @throws IllegalArgumentException if the row is out of the valid range.
*/
public void setCursorRow(int row);
/**
* Gets the cursor column.
* @return The cursor column.
*/
public int getCursorColumn();
/**
* Sets the cursor column.
* @param column The cursor column.
* @throws IllegalArgumentException if the column is out of the valid range.
*/
public void setCursorColumn(int column);
/**
* Gets the default background color.
* @return The default background color.
*/
public Color getDefaultBackgroundColor();
/**
* Gets the default foreground color.
* @return The default foreground color.
*/
public Color getDefaultForegroundColor();
}

View File

@ -0,0 +1,39 @@
/*
* Copyright (c) 2009-2011 Graham Edgecombe.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
package com.loomcom.symon.jterminal.bell;
import java.awt.Toolkit;
/**
* A {@link BellStrategy} which calls {@link Toolkit#beep()}.
* @author Graham Edgecombe
*/
public class BeepBellStrategy implements BellStrategy {
@Override
public void soundBell() {
Toolkit.getDefaultToolkit().beep();
}
}

View File

@ -0,0 +1,37 @@
/*
* Copyright (c) 2009-2011 Graham Edgecombe.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
package com.loomcom.symon.jterminal.bell;
/**
* A 'strategy' used to sound the bell (US-ASCII character {@code 7}).
* @author Graham Edgecombe
*/
public interface BellStrategy {
/**
* Sounds the bell.
*/
public void soundBell();
}

View File

@ -0,0 +1,37 @@
/*
* Copyright (c) 2009-2011 Graham Edgecombe.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
package com.loomcom.symon.jterminal.bell;
/**
* A {@link BellStrategy} which does nothing.
* @author Graham Edgecombe
*/
public class NopBellStrategy implements BellStrategy {
@Override
public void soundBell() {
/* ignore */
}
}

View File

@ -0,0 +1,28 @@
/*
* Copyright (c) 2009-2011 Graham Edgecombe.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
/**
* Contains bell strategy classes, which define how an application should
* respond to the US-ASCII {@code BEL} character.
*/
package com.loomcom.symon.jterminal.bell;

View File

@ -0,0 +1,27 @@
/*
* Copyright (c) 2009-2011 Graham Edgecombe.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
/**
* Contains core JTerminal classes.
*/
package com.loomcom.symon.jterminal;

View File

@ -0,0 +1,77 @@
/*
* Copyright (c) 2009-2011 Graham Edgecombe.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
package com.loomcom.symon.jterminal.vt100;
/**
* Represents an ANSI control sequence.
* @author Graham Edgecombe
*/
public class AnsiControlSequence {
/**
* The command character.
*/
private final char command;
/**
* The parameters.
*/
private final String[] parameters;
/**
* Creates an ANSI control sequence with the specified command and
* parameters.
* @param command The command character.
* @param parameters The parameters array.
* @throws NullPointerException if the parameters array is {@code null}.
*/
public AnsiControlSequence(char command, String[] parameters) {
if (parameters == null) {
throw new NullPointerException("parameters");
}
this.command = command;
if (parameters.length == 1 && parameters[0].equals("")) {
this.parameters = new String[0];
} else {
this.parameters = parameters.clone();
}
}
/**
* Gets the command character.
* @return The command character.
*/
public char getCommand() {
return command;
}
/**
* Gets the parameters array.
* @return The parameters array.
*/
public String[] getParameters() {
return parameters;
}
}

View File

@ -0,0 +1,44 @@
/*
* Copyright (c) 2009-2011 Graham Edgecombe.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
package com.loomcom.symon.jterminal.vt100;
/**
* An interface which classes may use to listen to events from a
* {@link AnsiControlSequenceParser}.
*/
public interface AnsiControlSequenceListener {
/**
* Called when a control sequence has been parsed.
* @param seq The control sequence.
*/
public void parsedControlSequence(AnsiControlSequence seq);
/**
* Called when a string has been parsed.
* @param str The string.
*/
public void parsedString(String str);
}

View File

@ -0,0 +1,156 @@
/*
* Copyright (c) 2009-2011 Graham Edgecombe.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
package com.loomcom.symon.jterminal.vt100;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
/**
* A class which parses {@link AnsiControlSequence}s from {@link String}(s).
* @author Graham Edgecombe
*/
public class AnsiControlSequenceParser {
/**
* The multi-byte control sequence introducer.
*/
private static final char[] MULTI_CSI = new char[] { 27, '[' };
/**
* The single-byte control sequence introducer.
*/
private static final char SINGLE_CSI = 155;
/**
* The buffer of data from the last call to {@link #parse()}. This is
* populated with data if an escape sequence is not complete.
*/
private StringBuilder buffer = new StringBuilder();
/**
* The ANSI control sequence listener.
*/
private final AnsiControlSequenceListener listener;
/**
* Creates the ANSI control sequence parser.
* @param listener The listener.
*/
public AnsiControlSequenceParser(AnsiControlSequenceListener listener) {
this.listener = listener;
}
/**
* Parses the specified string.
* @param str The string to parse.
*/
public void parse(String str) {
if (buffer.length() > 0) {
str = buffer.toString().concat(str);
buffer = new StringBuilder();
}
Reader reader = new StringReader(str);
try {
try {
parse(reader);
} finally {
reader.close();
}
} catch (IOException ex) {
/* ignore */
}
}
/**
* Parses characters from the specified character reader.
* @param reader The character reader.
* @throws IOException if an I/O error occurs.
*/
private void parse(Reader reader) throws IOException {
StringBuilder text = new StringBuilder();
int character;
while ((character = reader.read()) != -1) {
boolean introducedControlSequence = false;
if (character == SINGLE_CSI) {
introducedControlSequence = true;
} else if (character == MULTI_CSI[0]) {
int nextCharacter = reader.read();
if (nextCharacter == -1) {
buffer.append((char) character);
break;
} else if (nextCharacter == MULTI_CSI[1]) {
introducedControlSequence = true;
} else {
text.append((char) character);
text.append((char) nextCharacter);
}
} else {
text.append((char) character);
}
if (introducedControlSequence) {
if (text.length() > 0) {
listener.parsedString(text.toString());
text = new StringBuilder();
}
parseControlSequence(reader);
}
}
if (text.length() > 0) {
listener.parsedString(text.toString());
}
}
/**
* Parses a control sequence.
* @param reader The character reader.
* @throws IOException if an I/O error occurs.
*/
private void parseControlSequence(Reader reader) throws IOException {
boolean finishedSequence = false;
StringBuilder parameters = new StringBuilder();
int character;
while ((character = reader.read()) != -1) {
if ((character >= 'a' && character <= 'z') || (character >= 'A' && character <= 'Z')) {
String[] array = parameters.toString().split(";");
AnsiControlSequence seq = new AnsiControlSequence((char) character, array);
listener.parsedControlSequence(seq);
finishedSequence = true;
break;
} else {
parameters.append((char) character);
}
}
if (!finishedSequence) {
// not an ideal solution if they used the two byte CSI, but it's
// easier and cleaner than keeping track of it
buffer.append((char) SINGLE_CSI);
buffer.append(parameters);
}
}
}

View File

@ -0,0 +1,70 @@
/*
* Copyright (c) 2009-2011 Graham Edgecombe.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
package com.loomcom.symon.jterminal.vt100;
import java.awt.Color;
/**
* Contains colors used by the SGR ANSI escape sequence.
* @author Graham Edgecombe
*/
final class SgrColor {
/**
* An array of normal intensity colors.
*/
public static final Color[] COLOR_NORMAL = new Color[] {
new Color(0, 0, 0),
new Color(128, 0, 0),
new Color(0, 128, 0),
new Color(128, 128, 0),
new Color(0, 0, 128),
new Color(128, 0, 128),
new Color(0, 128, 128),
new Color(192, 192, 192)
};
/**
* An array of bright intensity colors.
*/
public static final Color[] COLOR_BRIGHT = new Color[] {
new Color(128, 128, 128),
new Color(255, 0, 0),
new Color(0, 255, 0),
new Color(255, 255, 0),
new Color(0, 0, 255),
new Color(0, 0, 255),
new Color(255, 0, 255),
new Color(0, 255, 255),
new Color(255, 255, 255)
};
/**
* Default private constructor to prevent instantiation.
*/
private SgrColor() {
}
}

View File

@ -0,0 +1,493 @@
/*
* Copyright (c) 2009-2011 Graham Edgecombe.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
package com.loomcom.symon.jterminal.vt100;
import java.awt.Color;
import com.loomcom.symon.jterminal.AbstractTerminalModel;
import com.loomcom.symon.jterminal.TerminalCell;
import com.loomcom.symon.jterminal.TerminalModel;
import com.loomcom.symon.jterminal.bell.BellStrategy;
import com.loomcom.symon.jterminal.bell.NopBellStrategy;
/**
* A VT100/ANSI-compatible terminal model.
* @author Graham Edgecombe
*/
public class Vt100TerminalModel extends AbstractTerminalModel {
/**
* A {@link AnsiControlSequenceListener} which modifies the
* {@link TerminalModel} appropriately when an event happens.
* @author Graham Edgecombe
*/
private class Vt100Listener implements AnsiControlSequenceListener {
/**
* The saved cursor row.
*/
private int savedCursorRow = -1;
/**
* The saved cursor column.
*/
private int savedCursorColumn = -1;
@Override
public void parsedControlSequence(AnsiControlSequence seq) {
char command = seq.getCommand();
String[] parameters = seq.getParameters();
switch (command) {
case 'A':
case 'B':
case 'C':
case 'D':
int n = 1;
if (parameters.length == 1) {
n = Integer.parseInt(parameters[0]);
}
if (command == 'A') {
moveCursorUp(n);
} else if (command == 'B') {
moveCursorDown(n);
} else if (command == 'C') {
moveCursorForward(n);
} else if (command == 'D') {
moveCursorBack(n);
}
break;
case 'E':
case 'F':
n = 1;
if (parameters.length == 1) {
n = Integer.parseInt(parameters[0]);
}
if (command == 'E') {
moveCursorDown(n);
} else if (command == 'F') {
moveCursorUp(n);
}
setCursorColumn(0);
break;
case 'G':
if (parameters.length == 1) {
n = Integer.parseInt(parameters[0]);
setCursorColumn(n - 1);
}
break;
case 'H':
case 'f':
if (parameters.length == 2) {
n = 1;
int m = 1;
if (parameters[0].length() > 0) {
n = Integer.parseInt(parameters[0]);
}
if (parameters[1].length() > 0) {
m = Integer.parseInt(parameters[1]);
}
setCursorRow(n - 1);
setCursorColumn(m - 1);
}
break;
case 'J':
n = 0;
if (parameters.length == 1) {
n = Integer.parseInt(parameters[0]);
}
if (n == 0) {
int row = cursorRow;
int column = cursorColumn;
while(row < rows) {
while(column < columns) {
cells[row][column] = null;
column++;
}
column = 0;
row++;
}
} else if (n == 1) {
int row = cursorRow;
int column = cursorColumn;
while(row >= 0) {
while(column >= 0) {
cells[row][column] = null;
column--;
}
column = columns - 1;
row--;
}
} else if (n == 2) {
clear();
}
break;
case 'K':
n = 0;
if (parameters.length == 1) {
n = Integer.parseInt(parameters[0]);
}
if (n == 0) {
for (int row = cursorRow; row < rows; row++) {
cells[row][cursorColumn] = null;
}
} else if (n == 1) {
for (int row = cursorRow; row >= 0; row--) {
cells[row][cursorColumn] = null;
}
} else if (n == 2) {
for (int column = 0; column < columns; column++) {
cells[cursorRow][column] = null;
}
}
break;
case 'm':
if (parameters.length == 0) {
parameters = new String[] { "0" };
}
for (String parameter : parameters) {
if (parameter.equals("0")) {
foregroundColor = DEFAULT_FOREGROUND_COLOR;
backgroundColor = DEFAULT_BACKGROUND_COLOR;
backgroundBold = DEFAULT_BACKGROUND_BOLD;
foregroundBold = DEFAULT_FOREGROUND_BOLD;
} else if (parameter.equals("2")) {
backgroundBold = true;
foregroundBold = true;
} else if (parameter.equals("22")) {
backgroundBold = false;
foregroundBold = false;
} else if ((parameter.startsWith("3") || parameter.startsWith("4")) && parameter.length() == 2) {
int color = Integer.parseInt(parameter.substring(1));
if (parameter.startsWith("3")) {
foregroundColor = color;
} else if (parameter.startsWith("4")) {
backgroundColor = color;
}
}
}
break;
case 'u':
if (savedCursorColumn != -1 && savedCursorRow != -1) {
cursorColumn = savedCursorColumn;
cursorRow = savedCursorRow;
}
break;
case 's':
savedCursorColumn = cursorColumn;
savedCursorRow = cursorRow;
break;
}
}
@Override
public void parsedString(String str) {
for (char ch : str.toCharArray()) {
switch (ch) {
case '\0':
continue;
case '\r':
cursorColumn = 0;
continue;
case '\n':
cursorRow++;
break;
case '\t':
while ((++cursorColumn % TAB_WIDTH) != 0);
continue;
case 127:
if (cursorColumn > 0) {
cells[cursorRow][--cursorColumn] = null;
}
continue;
case 7:
bellStrategy.soundBell();
continue;
}
if (cursorColumn >= columns) {
cursorColumn = 0;
cursorRow++;
}
if (cursorRow >= bufferSize) {
for (int i = 1; i < bufferSize; i++) {
System.arraycopy(cells[i], 0, cells[i - 1], 0, columns);
}
for (int i = 0; i < columns; i++) {
cells[bufferSize - 1][i] = null;
}
cursorRow--;
}
Color back = backgroundBold ? SgrColor.COLOR_BRIGHT[backgroundColor] : SgrColor.COLOR_NORMAL[backgroundColor];
Color fore = foregroundBold ? SgrColor.COLOR_BRIGHT[foregroundColor] : SgrColor.COLOR_NORMAL[foregroundColor];
if (ch != '\n') {
cells[cursorRow][cursorColumn++] = new TerminalCell(ch, back, fore);
}
}
}
}
/**
* The default number of columns.
*/
private static final int DEFAULT_COLUMNS = 80;
/**
* The default number of rows.
*/
private static final int DEFAULT_ROWS = 25;
/**
* The tab width in characters.
*/
private static final int TAB_WIDTH = 8;
/**
* The default foreground bold flag.
*/
private static final boolean DEFAULT_FOREGROUND_BOLD = false;
/**
* The default background bold flag.
*/
private static final boolean DEFAULT_BACKGROUND_BOLD = false;
/**
* The default foreground color.
*/
private static final int DEFAULT_FOREGROUND_COLOR = 7;
/**
* The default background color.
*/
private static final int DEFAULT_BACKGROUND_COLOR = 0;
/**
* The ANSI control sequence listener.
*/
private final AnsiControlSequenceListener listener = this.new Vt100Listener();
/**
* The ANSI control sequence parser.
*/
private final AnsiControlSequenceParser parser = new AnsiControlSequenceParser(listener);
/**
* The current bell strategy.
*/
private BellStrategy bellStrategy = new NopBellStrategy();
/**
* The array of cells.
*/
private TerminalCell[][] cells;
/**
* The number of columns.
*/
private int columns;
/**
* The number of rows.
*/
private int rows;
/**
* The buffer size.
*/
private int bufferSize;
/**
* The cursor row.
*/
private int cursorRow = 0;
/**
* The cursor column.
*/
private int cursorColumn = 0;
/**
* The current foreground bold flag.
*/
private boolean foregroundBold = DEFAULT_FOREGROUND_BOLD;
/**
* The current background bold flag.
*/
private boolean backgroundBold = DEFAULT_BACKGROUND_BOLD;
/**
* The current foreground color.
*/
private int foregroundColor = DEFAULT_FOREGROUND_COLOR;
/**
* The current background color.
*/
private int backgroundColor = DEFAULT_BACKGROUND_COLOR;
/**
* Creates the terminal model with the default number of columns and rows,
* and the default buffer size.
*/
public Vt100TerminalModel() {
this(DEFAULT_COLUMNS, DEFAULT_ROWS);
}
/**
* Creates the terminal model with the specified number of columns and
* rows. The buffer size is set to the number of rows.
* @param columns The number of columns.
* @param rows The number of rows.
* @throws IllegalArgumentException if the number of rows or columns is
* negative.
*/
public Vt100TerminalModel(int columns, int rows) {
this(columns, rows, rows);
}
/**
* Creates the terminal model with the specified number of columns and rows
* and the specified buffer size.
* @param columns The number of columns.
* @param rows The number of rows.
* @param bufferSize The buffer size.
* @throws IllegalArgumentException if the number of rows or columns is
* negative, or if the buffer size is less than the number of rows.
*/
public Vt100TerminalModel(int columns, int rows, int bufferSize) {
if (columns < 0 || rows < 0 || bufferSize < 0) {
throw new IllegalArgumentException("Zero or positive values only allowed for columns, rows and buffer size.");
}
if (bufferSize < rows) {
throw new IllegalArgumentException("The buffer is too small");
}
this.columns = columns;
this.rows = rows;
this.bufferSize = bufferSize;
init();
}
/**
* Initializes the terminal model.
*/
private void init() {
cells = new TerminalCell[bufferSize][columns];
}
@Override
public int getCursorRow() {
return cursorRow;
}
@Override
public void setCursorRow(int row) {
if (row < 0 || row >= bufferSize) {
throw new IllegalArgumentException("row out of range");
}
cursorRow = row;
}
@Override
public int getCursorColumn() {
return cursorColumn;
}
@Override
public void setCursorColumn(int column) {
if (column < 0 || column >= columns) {
throw new IllegalArgumentException("column out of range");
}
cursorColumn = column;
}
@Override
public TerminalCell getCell(int column, int row) {
if (column < 0 || row < 0 || column >= columns || row >= bufferSize) {
throw new IndexOutOfBoundsException();
}
return cells[row][column];
}
@Override
public void setCell(int column, int row, TerminalCell cell) {
if (column < 0 || row < 0 || column >= columns || row >= bufferSize) {
throw new IndexOutOfBoundsException();
}
cells[row][column] = cell;
}
@Override
public void print(String str) {
if (str == null) {
throw new NullPointerException("str");
}
parser.parse(str);
}
@Override
public int getColumns() {
return columns;
}
@Override
public int getRows() {
return rows;
}
@Override
public int getBufferSize() {
return bufferSize;
}
@Override
public BellStrategy getBellStrategy() {
return bellStrategy;
}
@Override
public void setBellStrategy(BellStrategy strategy) {
if (strategy == null) {
throw new NullPointerException("strategy");
}
this.bellStrategy = strategy;
}
@Override
public Color getDefaultBackgroundColor() {
final int bg = DEFAULT_BACKGROUND_COLOR;
return DEFAULT_BACKGROUND_BOLD ? SgrColor.COLOR_BRIGHT[bg] : SgrColor.COLOR_NORMAL[bg];
}
@Override
public Color getDefaultForegroundColor() {
final int fg = DEFAULT_FOREGROUND_COLOR;
return DEFAULT_FOREGROUND_BOLD ? SgrColor.COLOR_BRIGHT[fg] : SgrColor.COLOR_NORMAL[fg];
}
}

View File

@ -0,0 +1,27 @@
/*
* Copyright (c) 2009-2011 Graham Edgecombe.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
/**
* Contains a terminal model roughly compatible with the VT100/ANSI standard.
*/
package com.loomcom.symon.jterminal.vt100;

View File

@ -0,0 +1,231 @@
/*
* Copyright (c) 2009-2011 Graham Edgecombe.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
package com.loomcom.symon;
import static org.junit.Assert.*;
import java.util.ArrayList;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import com.loomcom.symon.jterminal.vt100.AnsiControlSequenceParser;
import com.loomcom.symon.jterminal.vt100.AnsiControlSequence;
import com.loomcom.symon.jterminal.vt100.AnsiControlSequenceListener;
/**
* A test for the {@link AnsiControlSequenceParser} class.
* @author Graham Edgecombe
*/
public class TestAnsiControlSequenceParser implements AnsiControlSequenceListener {
/**
* The current parser.
*/
private AnsiControlSequenceParser parser;
/**
* The list of objects returned through the
* {@link AnsiControlSequenceListener} interface.
*/
private List<Object> objects = new ArrayList<Object>();
/**
* Sets up the parser and object list.
*/
@Before
public void setUp() {
objects.clear();
parser = new AnsiControlSequenceParser(this);
}
/**
* Tests a broken sequence with the single byte CSI.
*/
@Test
public void testBrokenSingleSequence() {
parser.parse(new String(new char[] { 155 }));
parser.parse(new String(new char[] { 'u' }));
assertEquals(1, objects.size());
Object obj = objects.get(0);
assertEquals(AnsiControlSequence.class, obj.getClass());
AnsiControlSequence seq = (AnsiControlSequence) obj;
String[] params = seq.getParameters();
assertEquals('u', seq.getCommand());
assertEquals(0, params.length);
}
/**
* Tests a broken sequence with the double byte CSI.
*/
@Test
public void testBrokenDoubleSequence() {
char[] ch1 = { 27 };
char[] ch2 = { '[' };
char[] ch3 = { '3', '0', ';' };
char[] ch4 = { '4', '0', 'm' };
parser.parse(new String(ch1));
parser.parse(new String(ch2));
parser.parse(new String(ch3));
parser.parse(new String(ch4));
assertEquals(1, objects.size());
Object obj = objects.get(0);
assertEquals(AnsiControlSequence.class, obj.getClass());
AnsiControlSequence seq = (AnsiControlSequence) obj;
String[] params = seq.getParameters();
assertEquals('m', seq.getCommand());
assertEquals(2, params.length);
assertEquals("30", params[0]);
assertEquals("40", params[1]);
}
/**
* Tests an empty string.
*/
@Test
public void testEmpty() {
parser.parse("");
assertEquals(0, objects.size());
}
/**
* Tests a sequence embedded within some text.
*/
@Test
public void testTextAndSequence() {
char[] ch = { 'h', 'i', 155, 'u', 'b', 'y', 'e' };
parser.parse(new String(ch));
assertEquals(3, objects.size());
Object o1 = objects.get(0);
Object o2 = objects.get(1);
Object o3 = objects.get(2);
assertEquals(String.class, o1.getClass());
assertEquals(AnsiControlSequence.class, o2.getClass());
assertEquals(String.class, o3.getClass());
assertEquals("hi", o1);
assertEquals("bye", o3);
AnsiControlSequence seq = (AnsiControlSequence) o2;
String[] params = seq.getParameters();
assertEquals(0, params.length);
assertEquals('u', seq.getCommand());
}
/**
* Tests parameters within a sequence.
*/
@Test
public void testParameters() {
char[] ch = { 155, '3', '0', ';', '4', '0', 'm' };
parser.parse(new String(ch));
assertEquals(1, objects.size());
Object obj = objects.get(0);
assertEquals(AnsiControlSequence.class, obj.getClass());
AnsiControlSequence seq = (AnsiControlSequence) obj;
String[] params = seq.getParameters();
assertEquals('m', seq.getCommand());
assertEquals(2, params.length);
assertEquals("30", params[0]);
assertEquals("40", params[1]);
}
/**
* Tests with plain text.
*/
@Test
public void testText() {
parser.parse("Hello, World!");
assertEquals(1, objects.size());
Object obj = objects.get(0);
assertEquals(String.class, obj.getClass());
assertEquals(obj, "Hello, World!");
}
/**
* Tests with a single byte CSI.
*/
@Test
public void testSingleCsi() {
char[] ch = { 155, '6', 'n' };
parser.parse(new String(ch));
assertEquals(1, objects.size());
Object obj = objects.get(0);
assertEquals(AnsiControlSequence.class, obj.getClass());
AnsiControlSequence seq = (AnsiControlSequence) obj;
String[] params = seq.getParameters();
assertEquals('n', seq.getCommand());
assertEquals(1, params.length);
assertEquals("6", params[0]);
}
/**
* Tests with a double byte CSI.
*/
@Test
public void testDoubleCsi() {
char[] ch = { 27, '[', 's' };
parser.parse(new String(ch));
assertEquals(1, objects.size());
Object obj = objects.get(0);
assertEquals(AnsiControlSequence.class, obj.getClass());
AnsiControlSequence seq = (AnsiControlSequence) obj;
String[] params = seq.getParameters();
assertEquals('s', seq.getCommand());
assertEquals(0, params.length);
}
@Override
public void parsedControlSequence(AnsiControlSequence seq) {
objects.add(seq);
}
@Override
public void parsedString(String str) {
objects.add(str);
}
}

View File

@ -0,0 +1,233 @@
/*
* Copyright (c) 2009-2011 Graham Edgecombe.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
package com.loomcom.symon;
import static org.junit.Assert.*;
import java.awt.Color;
import org.junit.Before;
import org.junit.Test;
import com.loomcom.symon.jterminal.TerminalCell;
import com.loomcom.symon.jterminal.TerminalModel;
import com.loomcom.symon.jterminal.bell.BellStrategy;
import com.loomcom.symon.jterminal.vt100.Vt100TerminalModel;
/**
* A test for the {@link Vt100TerminalModel} class.
* @author Graham Edgecombe
*/
public class TestVt100TerminalModel {
/**
* The terminal model.
*/
private TerminalModel model;
/**
* Sets up the terminal model.
*/
@Before
public void setUp(){
model = new Vt100TerminalModel();
}
/**
* Tests the word wrapping.
*/
@Test
public void testWordWrapping() {
int width = model.getColumns();
for (int i = 0; i < (width + 1); i++) {
model.print("h");
}
assertEquals('h', model.getCell(0, 1).getCharacter());
}
/**
* Tests special ASCII characters.
*/
@Test
public void testSpecialCharacters() {
model.print("\u0000");
assertNull(model.getCell(0, 0));
model.print("a\rb\rc");
assertEquals('c', model.getCell(0, 0).getCharacter());
model.print("\na");
assertEquals('c', model.getCell(0, 0).getCharacter());
assertEquals('a', model.getCell(1, 1).getCharacter());
model.print("\u007F");
assertNull(model.getCell(0, 1));
model.print("A\tB");
assertEquals('B', model.getCell(8, 1).getCharacter());
}
/**
* Tests that the terminal scrolls once the buffer is full.
*/
@Test
public void testBuffer() {
model = new Vt100TerminalModel(model.getColumns(), 2, 2);
model.print("This is line one.\r\n");
model.print("This is line two. XXXXXX\r\n");
model.print("And this is line three!");
assertEquals('A', model.getCell(0, 1).getCharacter());
assertNull(model.getCell(23, 1));
}
/**
* Tests the erase functionality.
*/
@Test
public void testErase() {
model.print("Hello");
model.print("\u009B2J");
for (int i = 0; i < 5; i++) {
assertNull(model.getCell(i, 0));
}
}
/**
* Tests moving the cursor.
*/
@Test
public void testMoveCursor() {
model.print("\u009B5B");
assertEquals(5, model.getCursorRow());
model.print("\u009B3A");
assertEquals(2, model.getCursorRow());
model.print("\u009B7C");
assertEquals(7, model.getCursorColumn());
model.print("\u009B4D");
assertEquals(3, model.getCursorColumn());
model.setCursorColumn(15);
model.setCursorRow(0);
model.print("\u009B3E");
assertEquals(0, model.getCursorColumn());
assertEquals(3, model.getCursorRow());
model.setCursorColumn(7);
model.print("\u009BF");
assertEquals(0, model.getCursorColumn());
assertEquals(2, model.getCursorRow());
model.print("\u009B4;8H");
assertEquals(3, model.getCursorRow());
assertEquals(7, model.getCursorColumn());
}
/**
* Tests the SGR escape sequence.
*/
@Test
public void testSgr() {
model.print("\u009B2;33;41mX");
TerminalCell cell = model.getCell(0, 0);
assertNotNull(cell);
assertEquals('X', cell.getCharacter());
assertEquals(Color.RED, cell.getBackgroundColor());
assertEquals(Color.YELLOW, cell.getForegroundColor());
model.print("\u009B0m\rX");
cell = model.getCell(0, 0);
assertNotNull(cell);
assertEquals('X', cell.getCharacter());
assertEquals(model.getDefaultBackgroundColor(), cell.getBackgroundColor());
assertEquals(model.getDefaultForegroundColor(), cell.getForegroundColor());
}
/**
* Tests saving and restoring the cursor.
*/
@Test
public void testSaveAndRestoreCursor() {
model.setCursorColumn(3);
model.setCursorRow(17);
model.print("\u009Bs");
model.setCursorColumn(5);
model.setCursorRow(23);
model.print("\u009Bu");
assertEquals(3, model.getCursorColumn());
assertEquals(17, model.getCursorRow());
}
/**
* Tests the printing of a simple message.
*/
@Test
public void testPrint() {
model.print("Hi");
assertEquals('H', model.getCell(0, 0).getCharacter());
assertEquals('i', model.getCell(1, 0).getCharacter());
assertNull(model.getCell(2, 0));
}
/**
* Tests that the bell is sounded.
*/
@Test
public void testBell() {
final int[] counter = new int[1];
BellStrategy strategy = new BellStrategy() {
/*
* (non-Javadoc)
* @see com.loomcom.symon.jterminal.bell.BellStrategy#soundBell()
*/
@Override
public void soundBell() {
counter[0]++;
}
};
model.setBellStrategy(strategy);
model.print("\u0007");
assertEquals(1, counter[0]);
model.print("Hello, \u0007World!\u0007\r\n");
assertEquals(3, counter[0]);
}
}