From: <sy...@us...> - 2012-12-23 19:34:53
|
Revision: 22620 http://jedit.svn.sourceforge.net/jedit/?rev=22620&view=rev Author: synh Date: 2012-12-23 19:34:41 +0000 (Sun, 23 Dec 2012) Log Message: ----------- Remade data handling subsystem: + added three new data handlers: * SimpleOutputStreamTask - public basic class class to handle the data from running process * ParsingOutputStreamTask - descendant of SimpleOutputStreamTask, which outputs using error and ANSI escape parser * SimpleInputStreamTask - public basic class to handle the data from user + modified OutputStreamTask, InputStreamTask, ErrorStreamTask and ConsoleProcessTask with the new data handlers Modified Paths: -------------- plugins/Console/trunk/console/ConsoleProcessTask.java plugins/Console/trunk/console/ErrorStreamTask.java plugins/Console/trunk/console/InputStreamTask.java plugins/Console/trunk/console/OutputStreamTask.java plugins/Console/trunk/console/StreamTask.java plugins/Console/trunk/docs/CHANGELOG.txt Added Paths: ----------- plugins/Console/trunk/console/ParsingOutputStreamTask.java plugins/Console/trunk/console/SimpleInputStreamTask.java plugins/Console/trunk/console/SimpleOutputStreamTask.java Modified: plugins/Console/trunk/console/ConsoleProcessTask.java =================================================================== --- plugins/Console/trunk/console/ConsoleProcessTask.java 2012-12-21 23:31:35 UTC (rev 22619) +++ plugins/Console/trunk/console/ConsoleProcessTask.java 2012-12-23 19:34:41 UTC (rev 22620) @@ -42,7 +42,7 @@ { // {{{ private data members /** The running subprocess */ - Process process; + private Process process; private Console console; private SystemShell.ConsoleState consoleState; @@ -271,10 +271,10 @@ } // }}} // {{{ ConsoleProcess constructor - ConsoleProcessTask(final Console console, - final Output output, + ConsoleProcessTask(Console console, + Output output, Output error, - final String[] args, + String[] args, Map<String, String> env, SystemShell.ConsoleState consoleState, boolean foreground, @@ -349,11 +349,26 @@ } else { - stderr = new ErrorStreamTask(this, process.getErrorStream(), errorColor); + stderr = new ErrorStreamTask(this, + process.getErrorStream(), + error, + errorColor + ); } - stdout = new OutputStreamTask(this, process.getInputStream(), plainColor); - stdin = new InputStreamTask(this, process.getOutputStream(), userInput); + stdout = new OutputStreamTask(this, + process.getInputStream(), + output, + plainColor, + console, + currentDirectory + ); + + stdin = new InputStreamTask(this, + process.getOutputStream(), + userInput + ); + // run child threads if (!merge) stderr.start(); stdout.start(); @@ -361,7 +376,11 @@ } else // we need stderr only { - stderr = new ErrorStreamTask(this, process.getErrorStream(), errorColor); + stderr = new ErrorStreamTask(this, + process.getErrorStream(), + error, + errorColor + ); // run child thread stderr.start(); } @@ -400,19 +419,8 @@ // main waiting loop boolean working = true; - while (working) + do { - // pause 100 ms - try - { - Thread.sleep(100); - } - catch(InterruptedException ie) - { - waitForStreams = false; - throw new Exception( ie.toString(), ie ); - } - // check an exit code of the working process try { @@ -430,12 +438,27 @@ // check state's flags of the current thread working &= !currentThread.isInterrupted() && !stopFlag; - if (working && detachFlag && foreground) + if (working) { - doDetach(); - setTaskState(STATE_UPDATE); + if (detachFlag && foreground) + { + doDetach(); + setTaskState(STATE_UPDATE); + } + + // pause 100 ms + try + { + Thread.sleep(100); + } + catch(InterruptedException ie) + { + waitForStreams = false; + throw new Exception( ie.toString(), ie ); + } } - } + + } while (working); /* waitForStreams == false -> some error has appeared * * isInterrupted == true -> current thread is interrupted, * Modified: plugins/Console/trunk/console/ErrorStreamTask.java =================================================================== --- plugins/Console/trunk/console/ErrorStreamTask.java 2012-12-21 23:31:35 UTC (rev 22619) +++ plugins/Console/trunk/console/ErrorStreamTask.java 2012-12-23 19:34:41 UTC (rev 22620) @@ -23,117 +23,54 @@ package console; -// {{{ imports +// {{{ Imports import java.awt.Color; import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.UnsupportedEncodingException; - -import org.gjt.sp.jedit.jEdit; import org.gjt.sp.util.Log; // }}} - -class ErrorStreamTask extends StreamTask +class ErrorStreamTask extends SimpleOutputStreamTask { - //{{{ Private members private ConsoleProcessTask process; - private InputStream in; - private Color errorColor; - //}}} - //{{{ ErrorThread constructor - ErrorStreamTask(ConsoleProcessTask process, InputStream in, Color errorColor) + // {{{ constructor + ErrorStreamTask(ConsoleProcessTask process, InputStream in, Output output, Color defaultColor) { + super(in, output, defaultColor); + this.process = process; - this.in = in; - this.errorColor = errorColor; - } //}}} + + setName("Error thread"); + } // }}} - //{{{ run() method - public void run() + // {{{ beforeWorking() method + @Override + protected void beforeWorking() throws Exception { - InputStreamReader isr = null; - try + process.streamStart(this); + } // }}} + + // {{{ afterWorking() method + @Override + protected void afterWorking() + { + process.streamFinish(this); + } // }}} + + // {{{ exception_dumpToLog() method + @Override + protected void exception_dumpToLog(Exception e) + { + if ( process.isForeground() ) { - isr = new InputStreamReader(in, jEdit.getProperty("console.encoding") ); + Log.log(Log.ERROR, e, e); } - catch (UnsupportedEncodingException uee) - { - throw new RuntimeException(uee); - } - - Output output = process.getErrorOutput(); - try - { - StringBuilder lineBuffer = new StringBuilder(100); - char[] input = new char[1024]; - int read = 0; - - process.streamStart(this); - - try - { - while ( !abortFlag ) - { - try - { - /* wait until: * - * - either the end of the input stream has been reached * - * - or user interrupts this thread. */ - while (true) // waiting loop - { - if (abortFlag) - { - throw new InterruptedException("Break the main loop: aborting"); - } - else if ( isr.ready() ) - { - if ((read = isr.read(input, 0, input.length)) == -1) - { - throw new InterruptedException("Break the main loop: input stream is empty"); - } - else - { - break; // break waiting loop only - } - } - else if (finishFlag) - { - throw new InterruptedException("End the main loop."); - } - - Thread.sleep(100); - } - } - catch (InterruptedException ie) - { - break; - } - - output.print( errorColor, lineBuffer.append(input, 0, read).toString() ); - lineBuffer.setLength(0); - } - } - finally - { - if (lineBuffer.length() > 0) - { - output.print( errorColor, lineBuffer.toString() ); - } - - process.streamFinish(this); - } - - } - catch (Exception e) - { - if ( process.isForeground() ) - { - Log.log(Log.ERROR, e, e); - } - - process.errorNotification(e); - } - } //}}} + } // }}} + + // {{{ exception_dumpToOwner() method + @Override + protected void exception_dumpToOwner(Exception e) + { + process.errorNotification(e); + } // }}} } Modified: plugins/Console/trunk/console/InputStreamTask.java =================================================================== --- plugins/Console/trunk/console/InputStreamTask.java 2012-12-21 23:31:35 UTC (rev 22619) +++ plugins/Console/trunk/console/InputStreamTask.java 2012-12-23 19:34:41 UTC (rev 22620) @@ -24,103 +24,58 @@ package console; //{{{ Imports -import java.io.BufferedReader; -import java.io.InputStreamReader; import java.io.OutputStream; -import java.io.PipedInputStream; import java.io.PipedOutputStream; - -import org.gjt.sp.jedit.jEdit; -import org.gjt.sp.util.Log; //}}} /** * Thread for feeding input to a running subprocess. */ -class InputStreamTask extends StreamTask +class InputStreamTask extends SimpleInputStreamTask { - private PipedOutputStream userInput; - OutputStream processOutput; - ConsoleProcessTask process; + private ConsoleProcessTask process; - //{{{ constructor + // {{{ constructor InputStreamTask(ConsoleProcessTask process, OutputStream processOutput, PipedOutputStream userInput) { + super(processOutput, userInput); + this.process = process; - this.processOutput = processOutput; - this.userInput = userInput; - } //}}} + } // }}} - //{{{ run() method - public void run() + // {{{ beforeWorking() method + /** + * Run BEFORE main working loop starts + * (under "try" section) + */ + @Override + protected void beforeWorking() throws Exception { - String _line = ""; - - try - { - process.streamStart(this); - - PipedInputStream pipeIn = new PipedInputStream(userInput); - try - { - BufferedReader inr = new BufferedReader(new InputStreamReader(pipeIn)); - - while ( !abortFlag ) - { - try - { - /* wait until: * - * - either the end of the input stream has been reached * - * - or user interrupts this thread. */ - while (true) // waiting loop - { - if (abortFlag) - { - throw new InterruptedException("Break the main loop: aborting"); - } - else if ( inr.ready() ) - { - if ((_line = inr.readLine()) == null) - { - throw new InterruptedException("Break the main loop: input stream is empty"); - } - else - { - break; // break waiting loop only - } - } - else if (finishFlag) - { - throw new InterruptedException("End the main loop."); - } - - Thread.sleep(100); - } - } - catch (InterruptedException ie) - { - break; - } - - _line += '\n'; // fixes "Press any key to continue . . ." (cjp) - _line = new String(_line.getBytes(), jEdit.getProperty("console.encoding")); - processOutput.write(_line.getBytes()); - processOutput.flush(); - } - } - finally - { - process.streamFinish(this); - - pipeIn.close(); // <-- throws IOExeption - is called last in this section - } - - } - catch (Exception e) - { - Log.log(Log.ERROR, this, e); - - process.errorNotification(e); - } - } //}}} + process.streamStart(this); + } // }}} + + // {{{ afterWorking() method + /** + * Run AFTER: + * - main working loop ends + * - "finalOutputing()" method + * + * (under "finally" section) + */ + @Override + protected void afterWorking() + { + process.streamFinish(this); + } // }}} + + // {{{ exception_dumpToOwner() method + /** + * By default do nothing AFTER "exception_dumpToLog" method + * (under "catch" section) + */ + @Override + protected void exception_dumpToOwner(Exception e) + { + process.errorNotification(e); + } // }}} } Modified: plugins/Console/trunk/console/OutputStreamTask.java =================================================================== --- plugins/Console/trunk/console/OutputStreamTask.java 2012-12-21 23:31:35 UTC (rev 22619) +++ plugins/Console/trunk/console/OutputStreamTask.java 2012-12-23 19:34:41 UTC (rev 22620) @@ -26,344 +26,46 @@ // {{{ Imports import java.awt.Color; import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.UnsupportedEncodingException; -import java.util.regex.*; -import java.util.ArrayList; -import javax.swing.text.SimpleAttributeSet; -import javax.swing.text.StyleConstants; - -import org.gjt.sp.jedit.jEdit; -import org.gjt.sp.util.Log; -import org.gjt.sp.util.StringList; - -import errorlist.DefaultErrorSource; -import errorlist.ErrorSource; - -import jcfunc.Description; // }}} -// {{{ class OutputStreamTask -class OutputStreamTask extends StreamTask +class OutputStreamTask extends ParsingOutputStreamTask { - // {{{ Private members private ConsoleProcessTask process; - - private InputStream in; - CommandOutputParser copt = null; - AnsiEscapeParser ansiescp = null; - private final int DEFAULT = CommandOutputParser.DEFAULT; - private final int WARNING = ErrorSource.WARNING; - private final int ERROR = ErrorSource.ERROR; - - private StringBuilder lineBuffer; - private int shiftUCW; - private Pattern eolPattern; - private SimpleAttributeSet commonAttrs; - private SimpleAttributeSet defaultAttrs, warningAttrs, errorAttrs; - // }}} - // {{{ constructor - OutputStreamTask(ConsoleProcessTask process, InputStream in, Color defaultColor) + OutputStreamTask(ConsoleProcessTask process, + InputStream in, + Output output, + Color defaultColor, + Console console, + String currentDirectory) { - this.process = process; - this.in = in; + super(in, output, defaultColor, console, currentDirectory); - String currentDirectory = process.getCurrentDirectory(); - Console console = process.getConsole(); - DefaultErrorSource es = console.getErrorSource(); - - copt = new CommandOutputParser(console.getView(), es, defaultColor); - copt.setDirectory(currentDirectory); - - ansiescp = new AnsiEscapeParser(defaultColor, console.getConsolePane().getBackground()); - - lineBuffer = new StringBuilder(100); - shiftUCW = 0; - - eolPattern = Pattern.compile("\n"); - - commonAttrs = new SimpleAttributeSet(); - StyleConstants.setBackground(commonAttrs, console.getConsolePane().getBackground()); - StyleConstants.setForeground(commonAttrs, defaultColor); - - defaultAttrs = setDefaultAttrs(null); + this.process = process; } // }}} - - // {{{ run() method - public void run() + + // {{{ beforeWorking() method + @Override + protected void beforeWorking() throws Exception { - InputStreamReader isr = null; - try - { - isr = new InputStreamReader(in, jEdit.getProperty("console.encoding") ); - } - catch (UnsupportedEncodingException uee) - { - throw new RuntimeException(uee); - } - - Output output = process.getOutput(); - try - { - char[] input = new char[1024]; - int read = 0; - - process.streamStart(this); - - try - { - while ( !abortFlag ) // working loop - { - try - { - /* wait until: * - * - either the end of the input stream has been reached * - * - or user interrupts this thread. */ - while (true) // waiting loop - { - if (abortFlag) - { - throw new InterruptedException("Break the main loop: aborting"); - } - else if ( isr.ready() ) - { - if ((read = isr.read(input, 0, input.length)) == -1) - { - throw new InterruptedException("Break the main loop: input stream is empty"); - } - else - { - break; // break waiting loop only - } - } - else if (finishFlag) - { - throw new InterruptedException("End the main loop."); - } - - Thread.sleep(100); - } - } - catch (InterruptedException ie) - { - break; // break working loop - } - - String line = lineBuffer.append(input, 0, read).toString(); // "111\n222\n333\n444" - - // convert all line breaks to internal "standard": "\n" - if ( ConsolePane.eolExchangeRequired() ) - { - lineBuffer.setLength(0); - line = lineBuffer.append( ConsolePane.eolExchanging(line) ).toString(); - } - - // remove all ANSI escape sequences - if ( ansiescp.touch(AnsiEscapeParser.Behaviour.REMOVE_ALL, line) ) - { - lineBuffer.setLength(0); - line = lineBuffer.append( ansiescp.removeAll(line) ).toString(); - } - - Matcher matcher = eolPattern.matcher(line); - - int bPosition = 0; - while ( !abortFlag && matcher.find() ) - { // -> "111\n" -> "222\n" -> "333\n" - printString(output, line.substring( bPosition, matcher.end() ), false); - bPosition = matcher.end(); - } - // remove already printed substrings - if (bPosition > 0) - { - lineBuffer.delete(0, bPosition); - } - - // there is some unterminated "tail": -> "444" - if (abortFlag) - { - break; // break working loop - } - else if ( lineBuffer.length() > 0 ) - { - if ( !isr.ready() ) - { - Thread.sleep(50); - - if ( !isr.ready() ) - { - printString(output, lineBuffer.toString(), true); - lineBuffer.setLength(0); - } - } - } - } - } - finally - { - if (lineBuffer.length() > 0) - { - printString(output, lineBuffer.toString(), false); - } - - copt.finishErrorParsing(); - - process.streamFinish(this); - } - - } - catch (Exception e) - { - Log.log(Log.ERROR, e, e); - - process.errorNotification(e); - } - + process.streamStart(this); } // }}} - //{{{ printString() method - /* - * output - Console/Buffer output - * str - printed string - * isUnterminated - flag: is 'str' unterminated string? - */ - private void printString(Output output, String str, boolean isUnterminated) + // {{{ afterWorking() method + @Override + protected void afterWorking() { - ArrayList<Description> seqs = null; - SimpleAttributeSet currentAttrs = null; + super.afterWorking(); - //{{{ style's relations -/* escape-Attrs commonAttrs - * | | | - * +-------+-------+ | - * | +-----+------------------+ - * | | | - * defaultAttrs warningAttrs errorAttrs - * | | | - * +------------------+------------------+ - * | - * currentAttrs - */ - //}}} - - // look for ANSI escape sequences - if ( ansiescp.touch(AnsiEscapeParser.Behaviour.PARSE, str) ) - { - seqs = ansiescp.parse(str, true); - str = ansiescp.removeAll(str); // => str's length is changed - } - - // error/warning parser - int errorStatus = copt.processLine(str); - - // choose color of full line - if (isUnterminated) - { - shiftUCW += str.length(); - } - else - { - currentAttrs = updateCurrentAttrs( errorStatus, copt.getColor() ); - - if ( shiftUCW > 0 ) - { - output.setAttrs(shiftUCW, currentAttrs); - shiftUCW = 0; - } - } - - // write line to output with/without color - try - { - if (seqs == null) // no any ANSI escape sequences - { - output.writeAttrs(currentAttrs, str); - } - else - { - boolean firstFlushed = false; - - // go over functions - for ( Description descr: seqs ) - { - // if first sequence does not situated at line's begining - // should output preceding substring - if ( !firstFlushed ) - { - if (descr.bPosition > 0) { - flushSubstring(output, str, currentAttrs, 0, descr.bPosition); - } - firstFlushed = true; - } - - switch (descr.function) - { - case SGR: - // new default style's creation: - // 1. always cancel previous escape-style - // 2. if no any escape-styles (resetting) - setup commonAttrs - defaultAttrs = setDefaultAttrs( ansiescp.processSGRparameters(descr.parameters, defaultAttrs) ); - currentAttrs = updateCurrentAttrs( errorStatus, copt.getColor() ); - - flushSubstring(output, str, currentAttrs, descr.bPosition, descr.ePosition); - break; - case NUL: - default : - flushSubstring(output, str, currentAttrs, descr.bPosition, descr.ePosition); - break; - } - } - } - } - catch (Exception err) - { - Log.log (Log.ERROR, this, "Can't Flush:", err); - } - } //}}} + process.streamFinish(this); + } // }}} - //{{{ flushSubstring() method - private void flushSubstring(Output output, String str, SimpleAttributeSet localAttrs, int bpos, int epos) + // {{{ exception_dumpToOwner() method + @Override + protected void exception_dumpToOwner(Exception e) { - if (epos - bpos > 0) - output.writeAttrs( localAttrs, str.substring(bpos, epos) ); - } //}}} - - //{{{ setDefaultAttrs() method - private SimpleAttributeSet setDefaultAttrs(SimpleAttributeSet newAttrs) - { - return newAttrs == null ? commonAttrs : newAttrs; - } //}}} - - //{{{ setNondefaultAttrs() method - private SimpleAttributeSet setNondefaultAttrs(SimpleAttributeSet nondefAttrs, Color color) - { - if (nondefAttrs == null) - { - nondefAttrs = new SimpleAttributeSet(commonAttrs); - StyleConstants.setForeground(nondefAttrs, color); - } - - return nondefAttrs; - } //}}} - - //{{{ updateCurrentAttrs() method - private SimpleAttributeSet updateCurrentAttrs(int errorStatus, Color errorColor) - { - switch (errorStatus) - { - case DEFAULT: - return defaultAttrs; - case WARNING: - warningAttrs = setNondefaultAttrs(warningAttrs, errorColor); - return warningAttrs; - case ERROR: - errorAttrs = setNondefaultAttrs(errorAttrs , errorColor); - return errorAttrs; - } - - return defaultAttrs; - } //}}} -} // }}} + process.errorNotification(e); + } // }}} +} Added: plugins/Console/trunk/console/ParsingOutputStreamTask.java =================================================================== --- plugins/Console/trunk/console/ParsingOutputStreamTask.java (rev 0) +++ plugins/Console/trunk/console/ParsingOutputStreamTask.java 2012-12-23 19:34:41 UTC (rev 22620) @@ -0,0 +1,485 @@ +/* + * ParsingOutputStreamTask.java - Outputting thread using an error parrser + * and an ANSI escaped sequencies parser + * + * :tabSize=4:indentSize=4:noTabs=false: + * :folding=explicit:collapseFolds=1: + * + * Copyright (C) 1999-2012 Slava Pestov, Alan Ezust, Marcelo Vanzin, Artem Bryantsev + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +package console; + +// {{{ Imports +import java.awt.Color; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.regex.*; +import java.util.ArrayList; + +import javax.swing.text.SimpleAttributeSet; +import javax.swing.text.StyleConstants; + +import org.gjt.sp.jedit.View; +import org.gjt.sp.util.Log; + +import errorlist.ErrorSource; +import errorlist.DefaultErrorSource; + +import jcfunc.Description; +// }}} + +/** + ParsingOutputStreamTask is an outputting thread using an error parrser and + an ANSI escaped sequencies parser. + User can set parsers either at the moment of class's creating or after class' + creation. If some parser is not defined - the class does not use that parser. + + A getted from InputStream char's sequence (size == SimpleOutputStreamTask.BUFFER_SIZE) + is divided by line's breaks (if any), because the error parser might work + with individual lines only, not the whole getted sequence - in common case + InputStream can produce really many individual lines. On the other hand each + individual line is outputted in EDT (AWT-EventQueue). So, if InputStream produces + a lot of lines - most of the time EDT processes them, and not other events + of the jEdit's interface: it will hang until all the generated lines will + be processed. However, this can be avoided if the output are not the individual + lines, but an array of them. In this case, the number of events that must + be processed EDT, sharply reduced. + For this reason after processing of each individual line ParsingOutputStreamTask + tries flush one to the cash. If the cash is full (CASH_SIZE_LIMIT) or current + style attributes are changed or the last cash's update was a long ago (CASH_TIME_DELAY) + then the whole cash is flushed to output. + */ +public class ParsingOutputStreamTask extends SimpleOutputStreamTask +{ + // {{{ private members + private CommandOutputParser errorParser = null; + private AnsiEscapeParser ansiParser = null; + + private final int DEFAULT = CommandOutputParser.DEFAULT; + private final int WARNING = ErrorSource.WARNING; + private final int ERROR = ErrorSource.ERROR; + + private int shiftUCW; + private Pattern eolPattern; + private SimpleAttributeSet commonAttrs; + private SimpleAttributeSet defaultAttrs, warningAttrs, errorAttrs; + + // {{{ cash's members + private final int CASH_SIZE_LIMIT = 100; // line's count + private final long CASH_TIME_DELAY = 100; // in msek + private SimpleAttributeSet cash_lastAttrs; + private int cash_strCount = 0; + private long cash_lastTime = 0; + private StringBuilder cash = new StringBuilder(); + // }}} + + // {{{ init() method + private void init(Color defaultColor, Color backgroundColor) + { + shiftUCW = 0; + + eolPattern = Pattern.compile("\n"); + + commonAttrs = new SimpleAttributeSet(); + StyleConstants.setBackground(commonAttrs, backgroundColor); + StyleConstants.setForeground(commonAttrs, defaultColor); + + defaultAttrs = setDefaultAttrs(null); + } // }}} + + // {{{ resetCash() method + private boolean resetCash(boolean flag) + { + if (flag || cash_strCount >= CASH_SIZE_LIMIT) + { + output.writeAttrs(cash_lastAttrs, cash.toString()); + + cash_strCount = 0; + cash.setLength(0); + + return true; + } + + return false; + } // }}} + + // {{{ outputting() method + private void outputting(SimpleAttributeSet currentAttrs, String str) + { + if ( resetCash(currentAttrs != cash_lastAttrs) ) + { + cash_lastAttrs = currentAttrs; + } + + cash.append(str); + cash_strCount++; + cash_lastTime = System.currentTimeMillis(); + } // }}} + + // {{{ printString() method + private void printString(String str, boolean isUnterminated) + { + ArrayList<Description> seqs = null; + SimpleAttributeSet currentAttrs = null; + + //{{{ style's relations +/* escape-Attrs commonAttrs + * | | | + * +-------+-------+ | + * | +-----+------------------+ + * | | | + * defaultAttrs warningAttrs errorAttrs + * | | | + * +------------------+------------------+ + * | + * currentAttrs + */ + //}}} + + // look for ANSI escape sequences + if ( ansiParser != null && ansiParser.touch(AnsiEscapeParser.Behaviour.PARSE, str) ) + { + seqs = ansiParser.parse(str, true); + str = ansiParser.removeAll(str); // => str's length is changed + } + + // error/warning parser + int errorStatus = errorParser != null ? + errorParser.processLine(str) : + DEFAULT; + + // choose color of full line + if (isUnterminated) + { + shiftUCW += str.length(); + } + else + { + currentAttrs = updateCurrentAttrs(errorStatus); + + if ( shiftUCW > 0 ) + { + output.setAttrs(shiftUCW, currentAttrs); + shiftUCW = 0; + } + } + + // write line to output with/without color + try + { + if (seqs == null) // no any ANSI escape sequences + { + outputting(currentAttrs, str); + } + else + { + boolean firstFlushed = false; + + // go over functions + for ( Description descr: seqs ) + { + // if first sequence does not situated at line's begining + // should output preceding substring + if ( !firstFlushed ) + { + if (descr.bPosition > 0) { + flushSubstring(str, currentAttrs, 0, descr.bPosition); + } + firstFlushed = true; + } + + switch (descr.function) + { + case SGR: + // new default style's creation: + // 1. always cancel previous escape-style + // 2. if no any escape-styles (resetting) - setup commonAttrs + defaultAttrs = setDefaultAttrs( ansiParser.processSGRparameters(descr.parameters, defaultAttrs) ); + currentAttrs = updateCurrentAttrs(errorStatus); + + flushSubstring(str, currentAttrs, descr.bPosition, descr.ePosition); + break; + case NUL: + default : + flushSubstring(str, currentAttrs, descr.bPosition, descr.ePosition); + break; + } + } + } + } + catch (Exception err) + { + Log.log (Log.ERROR, this, "Can't print data:", err); + } + } // }}} + + // {{{ flushSubstring() method + private void flushSubstring(String str, SimpleAttributeSet localAttrs, int bpos, int epos) + { + if (epos - bpos > 0) + { + outputting( localAttrs, str.substring(bpos, epos) ); + } + } // }}} + + // {{{ setDefaultAttrs() method + private SimpleAttributeSet setDefaultAttrs(SimpleAttributeSet newAttrs) + { + return newAttrs == null ? commonAttrs : newAttrs; + } // }}} + + // {{{ setNondefaultAttrs() method + private SimpleAttributeSet setNondefaultAttrs(SimpleAttributeSet nondefAttrs, Color color) + { + if (nondefAttrs == null) + { + nondefAttrs = new SimpleAttributeSet(commonAttrs); + StyleConstants.setForeground(nondefAttrs, color); + } + + return nondefAttrs; + } // }}} + + // {{{ updateCurrentAttrs() method + private SimpleAttributeSet updateCurrentAttrs(int errorStatus) + { + switch (errorStatus) + { + case DEFAULT: + return defaultAttrs; + case WARNING: + warningAttrs = setNondefaultAttrs(warningAttrs, errorParser.getColor() ); + return warningAttrs; + case ERROR: + errorAttrs = setNondefaultAttrs(errorAttrs , errorParser.getColor() ); + return errorAttrs; + } + + return defaultAttrs; + } // }}} + // }}} + + // {{{ constructor + /** + Create instance using given View and DefaultErrorSource. + + @param in input stream, from which we receive data + @param output instance implements Output + @param defaultColor default foreground color + @param backgroundColor default backgroundColor color + @param view working jEdit's view + @param des error source for error parser + @param currentDirectory console's current (working) directory + */ + public ParsingOutputStreamTask(InputStream in, + Output output, + Color defaultColor, + Color backgroundColor, + View view, + DefaultErrorSource des, + String currentDirectory) + { + super(in, output, defaultColor); + + setErrorParser(view, des, defaultColor, currentDirectory); + setAnsiParser(defaultColor, backgroundColor); + + init(defaultColor, backgroundColor); + } + + /** + Create instance using default Console's parameters. + + @param in input stream, from which we receive data + @param output instance implements Output + @param defaultColor default foreground color + @param console console, which manipulates the input stream + @param currentDirectory console's current (working) directory + */ + public ParsingOutputStreamTask(InputStream in, + Output output, + Color defaultColor, + Console console, + String currentDirectory) + { + this(in, + output, + defaultColor, + console.getConsolePane().getBackground(), + console.getView(), + console.getErrorSource(), + currentDirectory); + } + + /** + Create instance using external parsers. + + @param in input stream, from which we receive data + @param output instance implements Output + @param defaultColor default foreground color + @param backgroundColor default backgroundColor color + @param extErrorParser given error parser instance + @param extAnsiParser given ansi parser instance + */ + public ParsingOutputStreamTask(InputStream in, + Output output, + Color defaultColor, + Color backgroundColor, + CommandOutputParser extErrorParser, + AnsiEscapeParser extAnsiParser) + { + super(in, output, defaultColor); + + setErrorParser(extErrorParser); + setAnsiParser(extAnsiParser); + + init(defaultColor, backgroundColor); + } // }}} + + // {{{ actionInsideWaitingLoop() method + /** + * Extended outputting: working process outputs nothing a long time. + * In that case flush unterminated string. + */ + @Override + protected void actionInsideWaitingLoop(InputStreamReader isr) throws Exception + { + // there is some unterminated "tail": -> "444" (see outputData() method) + if ( !isr.ready() ) + { + printString(lineBuffer.toString(), true); + lineBuffer.setLength(0); + } + + resetCash( System.currentTimeMillis() - cash_lastTime > CASH_TIME_DELAY ); + } // }}} + + // {{{ afterWorking() method + @Override + protected void afterWorking() + { + errorParser.finishErrorParsing(); + } // }}} + + // {{{ finalOutputting() method + @Override + protected void finalOutputting() + { + if (lineBuffer.length() > 0) + { + printString(lineBuffer.toString(), false); + } + + resetCash( cash.length() > 0 ); + } // }}} + + // {{{ outputData() method + /** + * Do followed: + * - exchanging and removing symbols in whole input line + * - splitting input line by line breaks + */ + @Override + protected void outputData() throws Exception + { + String line = lineBuffer.toString(); // "111\n222\n333\n444" + + // convert all line breaks to internal "standard": "\n" + if ( ConsolePane.eolExchangeRequired() ) + { + lineBuffer.setLength(0); + line = lineBuffer.append( ConsolePane.eolExchanging(line) ).toString(); + } + + // remove all ANSI escape sequences + if ( ansiParser != null && ansiParser.touch(AnsiEscapeParser.Behaviour.REMOVE_ALL, line) ) + { + lineBuffer.setLength(0); + line = lineBuffer.append( ansiParser.removeAll(line) ).toString(); + } + + Matcher matcher = eolPattern.matcher(line); + + int bPosition = 0; + while ( !abortFlag && matcher.find() ) + { // -> "111\n" -> "222\n" -> "333\n" + printString(line.substring( bPosition, matcher.end() ), false); + bPosition = matcher.end(); + } + // remove already printed substrings + if (bPosition > 0) + { + lineBuffer.delete(0, bPosition); + } + /* + * If lineBuffer is not empty then either it's content will be flushed + * in actionInsideWaitingLoop() method or new data will be added to one. + */ + } // }}} + + // {{{ setAnsiParser() method + /** + Set given ansi parser. + Setting parser to "null" turns off this parser. + + @param extAnsiParser given ansi parser instance + */ + public void setAnsiParser(AnsiEscapeParser extAnsiParser) + { + ansiParser = extAnsiParser; + } + + /** + Create new ansi parser and set one. + @param defaultColor default foreground color + @param backgroundColor default backgroundColor color + */ + public void setAnsiParser(Color defaultColor, Color backgroundColor) + { + ansiParser = new AnsiEscapeParser(defaultColor, backgroundColor); + } // }}} + + // {{{ setErrorParser() method + /** + Set given error parser.. + Setting parser to "null" turns off this parser. + + @param extErrorParser given error parser instance + */ + public void setErrorParser(CommandOutputParser extErrorParser) + { + errorParser = extErrorParser; + } + + /** + Create new error parser and set one. + @param view working jEdit's view + @param des error source for error parser + @param defaultColor default foreground color + @param currentDirectory console's current (working) directory + */ + public void setErrorParser(View view, + DefaultErrorSource des, + Color defaultColor, + String currentDirectory) + { + errorParser = new CommandOutputParser(view, des, defaultColor); + errorParser.setDirectory(currentDirectory); + } // }}} +} + Added: plugins/Console/trunk/console/SimpleInputStreamTask.java =================================================================== --- plugins/Console/trunk/console/SimpleInputStreamTask.java (rev 0) +++ plugins/Console/trunk/console/SimpleInputStreamTask.java 2012-12-23 19:34:41 UTC (rev 22620) @@ -0,0 +1,192 @@ +/* + * SimpleInputStreamTask.java - Thread for the process input handling. + * + * :tabSize=4:indentSize=4:noTabs=false: + * :folding=explicit:collapseFolds=1: + * + * Copyright (C) 1999-2012 Slava Pestov, Alan Ezust, Marcelo Vanzin, Artem Bryantsev + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +package console; + +//{{{ Imports +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.PipedInputStream; +import java.io.PipedOutputStream; + +import org.gjt.sp.jedit.jEdit; +import org.gjt.sp.util.Log; +//}}} + +/** + * Thread for feeding input to a running subprocess. + */ +public class SimpleInputStreamTask extends StreamTask +{ + private PipedOutputStream userInput; + private OutputStream processOutput; + + protected int SLEEP_DELAY = 100; + + // {{{ constructor + public SimpleInputStreamTask(OutputStream processOutput, PipedOutputStream userInput) + { + super("Input thread"); + + this.processOutput = processOutput; + this.userInput = userInput; + } // }}} + + // {{{ actionInsideWaitingLoop() method + /** + * Run waiting loop inside. + */ + protected void actionInsideWaitingLoop(BufferedReader inr) throws Exception + { + } // }}} + + // {{{ beforeWorking() method + /** + * Run BEFORE main working loop starts + * (under "try" section) + */ + protected void beforeWorking() throws Exception + { + } // }}} + + // {{{ flushData() method + /** + * Flush string of data to working process. + * @param _line string for flushing to working process + * + * (under "try" section) + */ + protected void flushData(String _line) throws Exception + { + processOutput.write(_line.getBytes()); + processOutput.flush(); + } // }}} + + // {{{ afterWorking() method + /** + * Run AFTER: + * - main working loop ends + * - "finalOutputing()" method + * + * (under "finally" section) + */ + protected void afterWorking() + { + } // }}} + + // {{{ exception_dumpToLog() method + /** + * By default dump data about exception to jEdit.Log + * BEFORE "exception_dumpToOwner()" method + * (under "catch" section) + */ + protected void exception_dumpToLog(Exception e) + { + Log.log(Log.ERROR, e, e); + } // }}} + + // {{{ exception_dumpToOwner() method + /** + * By default do nothing AFTER "exception_dumpToLog" method + * (under "catch" section) + */ + protected void exception_dumpToOwner(Exception e) + { + } // }}} + + // {{{ run() method + public final void run() + { + String _line = ""; + + try + { + beforeWorking(); + + PipedInputStream pipeIn = new PipedInputStream(userInput); + try + { + BufferedReader inr = new BufferedReader(new InputStreamReader(pipeIn)); + + while ( !abortFlag ) + { + try + { + /* wait until: * + * - either the end of the input stream has been reached * + * - or user interrupts this thread. */ + while (true) // waiting loop + { + if (abortFlag) + { + throw new InterruptedException("Break the main loop: aborting"); + } + else if ( inr.ready() ) + { + if ((_line = inr.readLine()) == null) + { + throw new InterruptedException("Break the main loop: input stream is empty"); + } + else + { + break; // break waiting loop only + } + } + else if (finishFlag) + { + throw new InterruptedException("End the main loop."); + } + + Thread.sleep(SLEEP_DELAY); + + actionInsideWaitingLoop(inr); + } + } + catch (InterruptedException ie) + { + break; + } + + _line += '\n'; // fixes "Press any key to continue . . ." (cjp) + _line = new String(_line.getBytes(), jEdit.getProperty("console.encoding")); + + flushData(_line); + } + } + finally + { + afterWorking(); + + pipeIn.close(); // <-- throws IOExeption - is called last in this section + } + + } + catch (Exception e) + { + exception_dumpToLog(e); + + exception_dumpToOwner(e); + } + } // }}} +} Added: plugins/Console/trunk/console/SimpleOutputStreamTask.java =================================================================== --- plugins/Console/trunk/console/SimpleOutputStreamTask.java (rev 0) +++ plugins/Console/trunk/console/SimpleOutputStreamTask.java 2012-12-23 19:34:41 UTC (rev 22620) @@ -0,0 +1,266 @@ +/* + * SimpleOutputStreamTask.java - Thread for outputting data from InputStream to + * Output using Color + * + * :tabSize=4:indentSize=4:noTabs=false: + * :folding=explicit:collapseFolds=1: + * + * Copyright (C) 1999-2012 Slava Pestov, Alan Ezust, Marcelo Vanzin, Artem Bryantsev + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +package console; + +// {{{ imports +import java.awt.Color; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.UnsupportedEncodingException; + +import org.gjt.sp.jedit.jEdit; +import org.gjt.sp.util.Log; +// }}} + +// {{{ class description +/** +<p>SimpleOutputStreamTask is a base outputting class. It gets a data from + an external <i>InputStream</i> and dumps one to an external <i>Output</i> + using a <i>defaultColor</i>. + + Main working method <b>run()</b> of this class is marked as <b>final</b>. + On the other hand SimpleOutputStreamTask provides its descendants with + a few methods, which descendants can override. + Below these methods are shown inside the <b>run()</b> method's body + + run() { + try { + <b>beforeWorking()</b> + + try { + MAIN-LOOP { + try { + WAITING-LOOP { + // reading from InputStream isr + // sleep + + <b>actionInsideWaitingLoop(isr)</b> + } + } catch (InterruptedException ie) { + break // MAIN-LOOP + } + // lineBuffer's filling + + <b>outputData()</b> + } + } finally { + <b>finalOutputting()</b> + <b>afterWorking()</b> + } + } catch (Exception e) { + <b>exception_dumpToLog(e)</b> + <b>exception_dumpToOwner(e)</b> + } + } +</p> + */ + // }}} +public class SimpleOutputStreamTask extends StreamTask +{ + private InputStream in; + private Color defaultColor; + + protected Output output; + protected StringBuilder lineBuffer; + + protected int BUILDER_CAPACITY = 100; + protected int BUFFER_SIZE = 1024; + protected int SLEEP_DELAY = 100; + + // {{{ constructor + public SimpleOutputStreamTask(InputStream in, Output output, Color defaultColor) + { + super("Output thread"); + + this.in = in; + this.output = output; + this.defaultColor = defaultColor; + + lineBuffer = new StringBuilder(BUILDER_CAPACITY); + } // }}} + + // {{{ actionInsideWaitingLoop() method + /** + * Run waiting loop inside. + */ + protected void actionInsideWaitingLoop(InputStreamReader isr) throws Exception + { + } // }}} + + // {{{ afterWorking() method + /** + * Run AFTER: + * - main working loop ends + * - "finalOutputing()" method + * + * (under "finally" section) + */ + protected void afterWorking() + { + } // }}} + + // {{{ beforeWorking() method + /** + * Run BEFORE main working loop starts + * (under "try" section) + */ + protected void beforeWorking() throws Exception + { + } // }}} + + // {{{ finalOutputting() method + /** + * Dump remained data from internal buffer "lineBuffer" + * to output "output" with color "defaultColor". + * Run AFTER main working loop ends, but BEFORE "afterWorking()" method. + * + * (under "finally" section) + */ + protected void finalOutputting() + { + if (lineBuffer.length() > 0) + { + output.print( defaultColor, lineBuffer.toString() ); + } + } // }}} + + // {{{ exception_dumpToLog() method + /** + * By default dump data about exception to jEdit.Log + * BEFORE "exception_dumpToOwner()" method + * (under "catch" section) + */ + protected void exception_dumpToLog(Exception e) + { + Log.log(Log.ERROR, e, e); + } // }}} + + // {{{ exception_dumpToOwner() method + /** + * By default do nothing AFTER "exception_dumpToLog" method + * (under "catch" section) + */ + protected void exception_dumpToOwner(Exception e) + { + } // }}} + + // {{{ run() method + /** Task class method */ + @Override + public final void run() + { + InputStreamReader isr = null; + try + { + isr = new InputStreamReader(in, jEdit.getProperty("console.encoding") ); + } + catch (UnsupportedEncodingException uee) + { + throw new RuntimeException(uee); + } + + try + { + char[] input = new char[BUFFER_SIZE]; + int read = 0; + + beforeWorking(); + + try + { + while ( !abortFlag ) + { + try + { + /* wait until: * + * - either the end of the input stream has been reached * + * - or user interrupts this thread. */ + while (true) // waiting loop + { + if (abortFlag) + { + throw new InterruptedException("Break the main loop: aborting"); + } + else if ( isr.ready() ) + { + if ((read = isr.read(input, 0, input.length)) == -1) + { + throw new InterruptedException("Break the main loop: input stream is empty"); + } + else + { + break; // break waiting loop only + } + } + else if (finishFlag) + { + throw new InterruptedException("End the main loop."); + } + + Thread.sleep(SLEEP_DELAY); + + actionInsideWaitingLoop(isr); + } + } + catch (InterruptedException ie) + { + break; + } + + lineBuffer.append(input, 0, read); + + outputData(); + } + } + finally + { + finalOutputting(); + + afterWorking(); + } + + } + catch (Exception e) + { + exception_dumpToLog(e); + + exception_dumpToOwner(e); + } + } // }}} + + // {{{ outputData() method + /** + * - Dump collected data from internal buffer "lineBuffer" + * to output "output" with color "defaultColor" + * - Clean internal buffer "lineBuffer" + * + * (under "try" section) + */ + protected void outputData() throws Exception + { + output.print( defaultColor, lineBuffer.toString() ); + lineBuffer.setLength(0); + } // }}} +} Modified: plugins/Console/trunk/console/StreamTask.java =================================================================== --- plugins/Console/trunk/console/StreamTask.java 2012-12-21 23:31:35 UTC (rev 22619) +++ plugins/Console/trunk/console/StreamTask.java 2012-12-23 19:34:41 UTC (rev 22620) @@ -1,6 +1,5 @@ /* - * StreamTask.java - Base class for OutputStreamTask, InputStreamTask and - * ErrorStreamTask. + * StreamTask.java - Base class for SimpleOutputStreamTask and SimpleInputStreamTask. * * :tabSize=4:indentSize=4:noTabs=false: * :folding=explicit:collapseFolds=1: @@ -24,26 +23,41 @@ package console; -abstract class StreamTask extends Thread +/** +<p>Hierarchy of classes: + + StreamTask + | | + | +-- SimpleInputStreamTask --> InputStreamTask + | + SimpleOutputStreamTask --> ErrorStreamTask + | + ParsingOutputStreamTask --> OutputStreamTask</p> + */ +public abstract class StreamTask extends Thread { - protected boolean abortFlag; - protected boolean finishFlag; + protected boolean abortFlag = false; + protected boolean finishFlag = false; public StreamTask() { } - /** Interrupt the stream immediately. */ + public StreamTask(String name) + { + super(name); + } + + /** Interrupt this thread immediately. */ public void abort() { abortFlag = true; interrupt(); } - /** Ask the stream to be completed and wait until one finishes. */ + /** Ask this thread to be completed and wait until one finishes. */ public void finish() { finishFlag = true; } - } Modified: plugins/Console/trunk/docs/CHANGELOG.txt =================================================================== --- plugins/Console/trunk/docs/CHANGELOG.txt 2012-12-21 23:31:35 UTC (rev 22619) +++ plugins/Console/trunk/docs/CHANGELOG.txt 2012-12-23 19:34:41 UTC (rev 22620) @@ -8,6 +8,16 @@ Long-term processes (commands run from System shell) are implemented as Tasks. Those marked with '&' are background processes, those marked with '#' have a closed input channel. + + Remade data handling subsystem: + + added three new data handlers: + * SimpleOutputStreamTask - public basic class class to handle the data + from running process + * ParsingOutputStreamTask - descendant of SimpleOutputStreamTask, which + outputs using error and ANSI escape parser + * SimpleInputStreamTask - public basic class to handle the data from user + + modified OutputStreamTask, InputStreamTask, ErrorStreamTask and + ConsoleProcessTask with the new data handlers [Alan Ezust] Observe jEdit's general option "Check for changed files" and check after command executions too. This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |