| /******************************************************************************* |
| * 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); |
| } |
| } |
| } |
| |
| } |