/**
* MailArchiver is an application that provides services for storing and managing e-mail messages through a Web Services SOAP interface.
* Copyright (C) 2012 Marcio Andre Scholl Levien and Fernando Alberto Reuter Wendt and Jose Ronaldo Nogueira Fonseca Junior
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
/******************************************************************************\
*
* This product was developed by
*
* SERVIÇO FEDERAL DE PROCESSAMENTO DE DADOS (SERPRO),
*
* a government company established under Brazilian law (5.615/70),
* at Department of Development of Porto Alegre.
*
\******************************************************************************/
package serpro.mailarchiver.util;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.util.Set;
import java.util.TreeSet;
import javax.annotation.PostConstruct;
import bsh.ConsoleInterface;
import com.eventrouter.annotation.AnnotationProcessor;
import com.eventrouter.annotation.Subscribe;
import com.eventrouter.SubscriptionRegistry;
import org.springframework.beans.factory.annotation.Configurable;
import org.vaadin.console.Console;
import org.vaadin.console.Console.Command;
import serpro.mailarchiver.view.BaseApplication;
@Configurable
public class ConsoleMediator implements ConsoleInterface, Console.Handler {
private static final Logger log = Logger.getLocalLogger();
@PostConstruct
private void postConstruct() {
SubscriptionRegistry subscriptionRegistry = BaseApplication.getInstance().getDispatchContext().getSubscriptionRegistry();
AnnotationProcessor processor = new AnnotationProcessor(subscriptionRegistry);
processor.process(this);
}
private static final long serialVersionUID = 1L;
private static final String ENTER = System.getProperty("line.separator");
private static final String EOT = new String(new char[] {'\004'});
private static final String LF = new String(new char[] {'\012'});
private static final String CR = new String(new char[] {'\015'});
private static final String ESC = new String(new char[] {'\033'});
enum AnsiEsc {
Reset (0),
FontWeightBolder(1),
FontWeightNormal(22),
FontWeightLighter(2),
FontStyleItalic(3),
TextDecorationUnderline(4),
TextDecorationUnderlineOff(24),
TextDecorationBlinkSlow(5),
TextDecorationBlinkRapid(6),
TextDecorationBlinkOff(25),
TextDecorationLineThrough(9),
TextDecorationLineThroughOff(29),
TextDecorationOverline(53),
TextDecorationOverlineOff(55),
ColorBlack(30),
ColorRed(31),
ColorGreen(32),
ColorYellow(33),
ColorBlue(34),
ColorPurple(35),
ColorTeal(36),
ColorSilver(37),
BackgroundColorBlack(40),
BackgroundColorRed(41),
BackgroundColorGreen(42),
BackgroundColorYellow(43),
BackgroundColorBlue(44),
BackgroundColorPurple(45),
BackgroundColorTeal(46),
BackgroundColorSilver(47);
@Override
public String toString() {
return ESC + '[' + code + 'm';
}
private final int code;
AnsiEsc(int code) {
this.code = code;
}
}
private final Console console;
private MyReader interpreterIn;
private PrintStream interpreterOut;
private PrintStream interpreterErr;
private ByteArrayOutputStream interpreterOutByteArray;
private StringBuffer interpreterOutBuffer;
public ConsoleMediator(Console console) {
this.console = console;
this.console.setConvertANSIToCSS(true);
this.console.setHandler(this);
interpreterIn = new MyReader();
interpreterOutByteArray = new ByteArrayOutputStream();
interpreterOutBuffer = new StringBuffer();
try {
interpreterOut = new PrintStream(interpreterOutByteArray, false, "UTF-8") {
@Override
public void flush() {
super.flush();
appendOutput();
}
};
interpreterErr = new PrintStream(interpreterOutByteArray, false, "UTF-8") {
@Override
public void flush() {
super.flush();
appendOutput();
}
};
}
catch (UnsupportedEncodingException ex) {
log.error(ex);
}
}
//
@Override
public Reader getIn() {
return interpreterIn;
}
@Override
public PrintStream getOut() {
return interpreterOut;
}
@Override
public PrintStream getErr() {
return interpreterErr;
}
@Override
public void println(Object o) {
String s = String.valueOf(o);
interpreterOut.print(AnsiEsc.ColorYellow.toString() + s + ENTER);
interpreterOut.flush();
}
@Override
public void print(Object o) {
String s = String.valueOf(o);
if("bsh % ".equals(s)) {
interpreterOut.print(EOT);
}
else {
interpreterOut.print(AnsiEsc.ColorYellow.toString() + s);
}
interpreterOut.flush();
}
@Override
public void error(Object o) {
String s = String.valueOf(o);
if(s.endsWith(LF)) {
s = s.substring(0, s.length() - 1);
if(s.endsWith(CR)) {
s = s.substring(0, s.length() - 1);
}
}
interpreterErr.print(AnsiEsc.ColorRed.toString() + s);
interpreterErr.flush();
}
//
//
/**
* Called when user has entered input to the Console and presses enter
* to execute it.
*/
@Override
public void inputReceived(Console console, String lastInput) {
boolean run = false;
if(lastInput.isEmpty()) {
lastInput = ";";
run = true;
}
else {
for(int i = lastInput.length() - 1; i >= 0; i--) {
char c = lastInput.charAt(i);
if(Character.isSpaceChar(c)) {
continue;
}
if(c == ';') {
run = true;
}
break;
}
}
interpreterIn.put(lastInput + ENTER);
if(!run) {
console.setPs("> ");
console.prompt();
console.focusInput();
}
}
/**
* Called when user uses TAB to complete the command entered into the
* Console input.
*/
@Override
public Set getSuggestions(Console console, String lastInput) {
//TODO ?
Set suggestions = new TreeSet();
return suggestions;
}
/**
* Handle an exception during a Command execution.
*/
@Override
public void handleException(Console console, Exception e, Command cmd, String[] argv) {
//TODO ?
log.error(e);
}
/**
* Handle situation where a command could not be found.
*/
@Override
public void commandNotFound(Console console, String[] argv) {
//TODO ?
}
//
private void appendOutput() {
if(interpreterOutByteArray.size() > 0) {
try {
String content = interpreterOutByteArray.toString("UTF-8");
interpreterOutByteArray.reset();
if(!content.isEmpty()) {
interpreterOutBuffer.append(content);
}
}
catch (UnsupportedEncodingException ex) {
log.error(ex);
}
}
}
@Subscribe({"/mailarchiver/refresh"})
private void refresh() {
if(interpreterOutBuffer.length() > 0) {
String content = interpreterOutBuffer.toString();
interpreterOutBuffer.setLength(0);
boolean eot = false;
if(content.endsWith(EOT)) {
eot = true;
content = content.substring(0, content.length() - 1);
if(content.endsWith(LF)) {
content = content.substring(0, content.length() - 1);
if(content.endsWith(CR)) {
content = content.substring(0, content.length() - 1);
}
}
}
if(!content.isEmpty()) {
console.print(content);
}
if(eot) {
console.setPs("{bsh}> ");
console.prompt();
console.focusInput();
}
}
}
private static class MyReader extends Reader {
private final StringBuilder buffer = new StringBuilder();
public void put(String value) {
synchronized(buffer) {
buffer.append(value);
buffer.notifyAll();
}
}
@Override
public int read(char[] cbuf, int off, int len) throws IOException {
int n;
synchronized(buffer) {
while(buffer.length() == 0) {
try {
buffer.wait();
}
catch (InterruptedException ex) {
log.error(ex);
}
}
n = Math.min(len, buffer.length());
for(int i = 0; i < n; i++) {
cbuf[off++] = buffer.charAt(i);
}
buffer.delete(0, n);
}
return n;
}
@Override
public void close() throws IOException {
}
}
}