blob: 1b8596c6dc4047c3525d9ae073b3bb8bb84e6586 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2004, 2016 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM - Initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.internal.core;
import java.io.IOException;
import java.io.OutputStream;
import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.IConsoleParser;
/**
* Intercepts an output to console and forwards it to console parsers for processing
*/
public class ConsoleOutputSniffer {
/**
* Private class to sniff the output stream for this sniffer.
*/
private class ConsoleOutputStream extends OutputStream {
// Stream's private buffer for the stream's read contents.
private StringBuilder currentLine = new StringBuilder();
private OutputStream outputStream = null;
public ConsoleOutputStream(OutputStream outputStream) {
this.outputStream = outputStream;
}
@Override
public void write(int b) throws IOException {
currentLine.append((char) b);
checkLine(false);
// Continue writing the bytes to the console's output.
if (outputStream != null) {
outputStream.write(b);
}
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
if (b == null) {
throw new NullPointerException();
} else if (off != 0 || (len < 0) || (len > b.length)) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return;
}
currentLine.append(new String(b, 0, len));
checkLine(false);
// Continue writing the bytes to the console's output.
if (outputStream != null)
outputStream.write(b, off, len);
}
@Override
public void close() throws IOException {
checkLine(true);
closeConsoleOutputStream();
}
@Override
public void flush() throws IOException {
if (outputStream != null) {
outputStream.flush();
}
}
/**
* Checks to see if the already read input constitutes
* a complete line (e.g. does the sniffing). If so, then
* send it to processLine.
*
* @param flush
*/
private void checkLine(boolean flush) {
if (currentLine.length() == 0) {
return;
}
String buffer = currentLine.toString();
int i = 0;
while ((i = buffer.indexOf('\n')) != -1) {
int eol = i;
if (i > 0 && buffer.charAt(i-1) == '\r') {
// also get rid of trailing \r in case of Windows line delimiter "\r\n"
eol = i - 1;
}
String line = buffer.substring(0, eol);
processLine(line);
buffer = buffer.substring(i + 1); // skip the \n and advance
}
currentLine.setLength(0);
if (flush) {
if (buffer.length() > 0) {
processLine(buffer);
}
} else {
currentLine.append(buffer);
}
}
} // end ConsoleOutputStream class
private int nOpens = 0;
private OutputStream consoleOutputStream;
private OutputStream consoleErrorStream;
private IConsoleParser[] parsers;
public ConsoleOutputSniffer(IConsoleParser[] parsers) {
this.parsers = parsers;
}
public ConsoleOutputSniffer(OutputStream outputStream, OutputStream errorStream, IConsoleParser[] parsers) {
this(parsers);
this.consoleOutputStream = outputStream;
this.consoleErrorStream = errorStream;
}
/**
* Returns an output stream that will be sniffed.
* This stream should be hooked up so the command
* output stream goes into here.
*/
public OutputStream getOutputStream() {
incNOpens();
return new ConsoleOutputStream(consoleOutputStream);
}
/**
* Returns an error stream that will be sniffed.
* This stream should be hooked up so the command
* error stream goes into here.
*/
public OutputStream getErrorStream() {
incNOpens();
return new ConsoleOutputStream(consoleErrorStream);
}
private synchronized void incNOpens() {
nOpens++;
}
/*
*/
public synchronized void closeConsoleOutputStream() throws IOException {
if (nOpens > 0 && --nOpens == 0) {
for (int i = 0; i < parsers.length; ++i) {
try {
parsers[i].shutdown();
} catch (Throwable e) {
// Report exception if any but let all the parsers a chance to shutdown.
CCorePlugin.log(e);
}
}
}
}
/*
* Processes the line by passing the line to the parsers.
*
* @param line
*/
private synchronized void processLine(String line) {
for (IConsoleParser parser : parsers) {
try {
// Report exception if any but let all the parsers a chance to process the line.
parser.processLine(line);
} catch (Throwable e) {
CCorePlugin.log(e);
}
}
}
}