/*
* Copyright 2005-2010 Ignis Software Tools Ltd. All rights reserved.
*/
package systemobject.terminal;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public abstract class Terminal {
Logger log = Logger.getLogger(Terminal.class.getName());
protected static final int IN_BUFFER_SIZE = 65536;
private StringBuffer result = new StringBuffer();
protected OutputStream out = null;
protected InputStream in = null;
protected int bufChar = 10;
protected long scrollEndTimeout = 200;
ArrayList<Prompt> prompts = new ArrayList<Prompt>();
public abstract void connect() throws IOException;
public abstract void disconnect() throws IOException;
public abstract boolean isConnected();
public abstract String getConnectionName();
private boolean delayedTyping = false;
private boolean asciiFilter = true;
private PrintStream printStream = System.out;
private long keyTypingDelay = 20;
private boolean ignoreBackSpace = false;
private String charSet = "ASCII";
/**
* create a filter input stream:<br>
* 1) add the current input stream to the given stream.<br>
* 2) set the new input stream to the given one.<br>
*
* @param input the filter stream
*/
public void addFilter(InOutInputStream input) {
input.setInputStream(in);
in = input;
}
/**
* send a given string to the terminal (no prompt waiting)
*
* @param command the command to send
* @param delayedTyping if True will sleep keyTypingDelay ms between each typed byte entered to the terminal
* @throws IOException
* @throws InterruptedException
*/
public synchronized void sendString(String command, boolean delayedTyping) throws IOException, InterruptedException{
byte[] buf = command.getBytes(charSet);
// Do not override if delayed typing was set to TRUE from elsewhere
if (this.delayedTyping != true) {
setDelayedTyping(delayedTyping);
}
if (isDelayedTyping()) {
for (int i = 0; i < buf.length; i++) {
out.write(buf[i]);
out.flush();
Thread.sleep(keyTypingDelay);
}
} else {
out.write(buf);
out.flush();
}
}
/**
* get the input buffer data
* @return a String of all data in the input buffer
* @throws Exception
*/
public String readInputBuffer() throws Exception {
int avail = in.available();
if (avail <= 0 ) {
return "";
}
byte[] bytes = new byte[avail];
in.read(bytes);
return new String(bytes, charSet);
}
/**
* add a remark to the result buffer
*
* @param remark the String to add
*/
public synchronized void addRemark(String remark) {
result.append(remark);
}
/**
* checks if there is more input in the buffer
*
* @return True if there isn't any new input after ${scrollEndTimeout} ms
* @throws Exception
*/
public synchronized boolean isScrallEnd() throws Exception{
if (scrollEndTimeout == 0) { // if set to 0 always return true.
return true;
}
int avil0 = in.available();
Thread.sleep(scrollEndTimeout);
int avil1 = in.available();
if (avil1 > bufChar) {
return false;
}
if (avil0 == avil1) { // no change after 1/2 time and avail under bufChar
return true;
}
Thread.sleep(scrollEndTimeout);
if (in.available() < bufChar) {
return true;
}
return false;
}
/**
* wait for <b>ALL</b> Strings in the given Array to be found, in the given time
*
* @param prompts the Strings to check
* @param timeout the time (in ms) before throwing a timeout exception
* @throws IOException
* @throws InterruptedException
*/
public synchronized void waitForPrompt(String[] prompts, long timeout) throws IOException, InterruptedException{
long startTime = System.currentTimeMillis();
StringBuffer sb = new StringBuffer();
while (true) {
if (timeout > 0) {
if (System.currentTimeMillis() - startTime > timeout) {
result.append(sb);
throw new IOException("timeout: " + timeout);
}
}
int avail = in.available();
if (avail > 0) {
while (avail > 0) {
int b = in.read();
if (b < 0) {
avail = in.available();
continue;
}
if (b == 8 && !isIgnoreBackSpace()) {
sb.append('B');
}
if (b >= 127 ||
b < 9 ||
(b >= 14 && b <= 31) ||
b == 11 ||
b == 12) { // not ascii byte will be ignored
avail = in.available();
continue;
}
sb.append((char)b);
if (printStream != null) {
printStream.print((char)b);
}
String bufString = sb.toString();
boolean allPromptsFound = true;
for (int i = 0; i < prompts.length; i++) {
if (bufString.indexOf(prompts[i]) < 0) {
allPromptsFound = false;
break;
}
}
if (allPromptsFound) {
result.append(sb);
return;
}
avail = in.available();
if (timeout > 0) {
if (System.currentTimeMillis() - startTime > timeout) {
result.append(sb);
throw new IOException("timeout: " + timeout);
}
}
}
} else {
Thread.sleep(10);
}
}
}
/**
* wait for one of the terminal defined prompts to be found in the input buffer
*
* @param timeout the time on which timeout exception will be thrown
* @return the found Prompt if any was found
* @throws IOException if Timeout was reached
* @throws InterruptedException
*/
public synchronized Prompt waitForPrompt(long timeout) throws IOException, InterruptedException{
long startTime = System.currentTimeMillis();
StringBuffer sb = new StringBuffer();
if (prompts == null || prompts.size() == 0) {
return null;
}
while (true) {
if (timeout > 0) {
if (System.currentTimeMillis() - startTime > timeout) {
result.append(sb);
throw new IOException("timeout: " + timeout);
}
}
int avail = in.available();
if (avail > 0) {
while (avail > 0) {
int b = in.read();
if (b < 0) {
avail = in.available();
continue;
}
if (b == 8 && !isIgnoreBackSpace()) {
sb.append('B');
}
if (asciiFilter) {
if (b >= 127 || b < 9 || (b >= 14 && b <= 31) || b == 11 || b == 12) { // not ascii byte will be ignored
avail = in.available(); // if the last value is a non-ascii character
continue;
}
}
sb.append((char)b);
if (printStream != null) {
printStream.print((char)b);
}
int promptArraySize = prompts.size();
boolean skipNonExact = false;
if (in.available() > 40 + bufChar) {
skipNonExact = true;
}
for (int j = 0; j < promptArraySize; j++) {
Prompt prompt = (Prompt)prompts.get(j);
if (prompt == null || prompt.getPrompt() == null) {
continue;
}
String bufString = sb.toString();
if (prompt.isRegularExpression()) {
Pattern p = prompt.getPattern();
Matcher m = p.matcher(bufString);
if (m.find()) {
result.append(sb);
return prompt;
}
} else {
// accelerate cases with long output
if (!prompt.dontWaitForScrollEnd() && skipNonExact) {
continue;
}
if (bufString.endsWith(prompt.getPrompt())) {
result.append(sb);
return prompt;
}
}
}
avail = in.available();
/**
* change the timeout to be activate in a state were there is endless amount of output
* from the cli.
*/
if (timeout > 0) {
if (System.currentTimeMillis() - startTime > timeout) {
result.append(sb);
throw new IOException("timeout: " + timeout);
}
}
}
} else {
Thread.sleep(10);
}
}
}
/**
* wait for one of the terminal Prompts for 20 seconds
*
* @return the found Prompt if any was found
* @throws IOException if no prompt was found after 20 seconds
* @throws InterruptedException
*/
public synchronized Prompt waitFor() throws IOException, InterruptedException{
return waitForPrompt(20000);
}
/**
* get all data gathered by the input buffer
*
* @return a String of all input data gathered by the terminal
*/
public synchronized String getResult() {
String toRetun = result.toString();
result = new StringBuffer();
return toRetun;
}
/**
* close input and output streams
*
* @throws IOException
*/
public void closeStreams() throws IOException{
if (in != null) {
in.close();
}
if (out != null) {
out.close();
}
}
/**
* add a given Prompt to the Terminal Prompts array
*
* @param promptString the prompt String to add
* @param isRegExp if True then Prompt will be marked as a regular expression
*/
public void addPrompt(String promptString, boolean isRegExp) {
Prompt prompt = new Prompt(promptString,isRegExp);
addPrompt(prompt);
}
/**
* add a given Prompt to the Terminal Prompts array
*
* @param prompt the prompt to add
*/
public void addPrompt(Prompt prompt) {
prompts.remove(prompt);
prompts.add(prompt);
}
/**
* locate the matching Terminal Prompt object by the given Prompt String
* @param prompt the Prompt String
* @return the Prompt object from the Terminal Prompts list or null if none was found
*/
public Prompt getPrompt(String prompt) {
for (int i = 0; i < prompts.size(); i++) {
Prompt p = (Prompt)prompts.get(i);
if (p.getPrompt().equals(prompt)) {
return p;
}
}
return null;
}
/**
* clear all Terminal Prompts
*/
public void removePrompts() {
prompts = new ArrayList<Prompt>();
}
/**
* get a clone of the Terminal Prompts list
* @return
*/
@SuppressWarnings("unchecked")
public ArrayList<Prompt> getPrompts() {
return (ArrayList<Prompt>)prompts.clone();
}
/**
* set the Terminal Prompts list
*
* @param prompts the Prompts to set
*/
public void setPrompts(ArrayList<Prompt> prompts) {
this.prompts = prompts;
}
public int getBufChar() {
return bufChar;
}
public void setBufChar(int bufChar) {
this.bufChar = bufChar;
}
/**
* the time (in ms) to wait for a terminal input to be received before declaring scroll end (no more input)
*
* @return
*/
public long getScrollEndTimeout() {
return scrollEndTimeout;
}
/**
* the time (in ms) to wait for a terminal input to be received before declaring scroll end (no more input)
*
* @param scrollEndTimeout
*/
public void setScrollEndTimeout(long scrollEndTimeout) {
this.scrollEndTimeout = scrollEndTimeout;
}
/**
* signals if input String should be typed char by char with 20ms delay or all at once
* @return
*/
public boolean isDelayedTyping() {
return delayedTyping;
}
/**
* if set to True then input String (command) will be typed char by char with 20ms delay<br>
* if set to False all String will be send at once
* default is false
*
* @param delayedTyping
*/
public void setDelayedTyping(boolean delayedTyping) {
this.delayedTyping = delayedTyping;
}
/**
* signals if ascii chars should be ignored when reading from the buffer
*
* @return
*/
public boolean isAsciiFilter() {
return asciiFilter;
}
/**
* if set to True then non ascii chars will be ignored
*
* @param asciiFilter
*/
public void setAsciiFilter(boolean asciiFilter) {
this.asciiFilter = asciiFilter;
}
/**
* Sets the print stream to which the stream of the connection
* will be dumped to.
* Set the print stream to System.out to dump terminal stream to the console,
* Set print stream to null to turn off stream dump.
*/
public void setPrintStream(PrintStream printStream) {
this.printStream = printStream;
}
/**
* the time (in ms) to sleep between each typed byte entered to the terminal
*
* @return
*/
public long getKeyTypingDelay() {
return keyTypingDelay;
}
/**
* the time (in ms) to sleep between each typed byte entered to the terminal
*
* @param keyTypingDelay
*/
public void setKeyTypingDelay(long keyTypingDelay) {
this.keyTypingDelay = keyTypingDelay;
}
/**
* Whether to ignore backspace characters or not
*
* @param ignoreBackSpace
*/
public boolean isIgnoreBackSpace() {
return ignoreBackSpace;
}
/**
* Whether to ignore backspace characters or not
*
* @param ignoreBackSpace
*/
public void setIgnoreBackSpace(boolean ignoreBackSpace) {
this.ignoreBackSpace = ignoreBackSpace;
}
public void setCharSet(String charSet) {
this.charSet = charSet;
}
public String getCharSet() {
return charSet;
}
public InputStream getIn() {
return in;
}
}