| /******************************************************************************* |
| * Copyright (c) 2000, 2018 IBM Corporation and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| * Patrik Suzzi <psuzzi@gmail.com> - Bug 507661 |
| *******************************************************************************/ |
| package org.eclipse.ui.console; |
| |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.UnsupportedEncodingException; |
| |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.graphics.Color; |
| |
| /** |
| * InputStream used to read input from an {@link IOConsole}. |
| * This stream will buffer input that it receives until it has been read. |
| * An input stream is available from its {@link IOConsole}. |
| * @since 3.1 |
| * @noinstantiate This class is not intended to be instantiated by clients. |
| * @noextend This class is not intended to be subclassed by clients. |
| * |
| */ |
| public class IOConsoleInputStream extends InputStream { |
| /** |
| * Buffer to hold data from console until it is read. |
| */ |
| private byte[] input = new byte[100]; |
| |
| /** |
| * Location in the buffer that the next byte of data from the |
| * console should be stored. |
| */ |
| private int inPointer = 0; |
| |
| /** |
| * Location in the buffer that the next byte of data read from |
| * this stream should come from. |
| */ |
| private int outPointer = 0; |
| |
| /** |
| * The number of bytes of real data currently in the buffer. |
| */ |
| private int size = 0; |
| |
| /** |
| * Flag to indicate that EOF has been sent already. |
| */ |
| private boolean eofSent = false; |
| |
| /** |
| * Flag to indicate that the stream has been closed. |
| */ |
| private boolean closed = false; |
| |
| /** |
| * The console that this stream is connected to. |
| */ |
| private IOConsole console; |
| |
| /** |
| * The color used to display input in the console. |
| */ |
| private Color color; |
| |
| /** |
| * The font style used to decorate input in the console. |
| */ |
| private int fontStyle = SWT.NORMAL; |
| |
| |
| /** |
| * Constructs a new input stream on the given console. |
| * |
| * @param console I/O console |
| */ |
| IOConsoleInputStream(IOConsole console) { |
| this.console = console; |
| } |
| |
| @Override |
| public synchronized int read(byte[] b, int off, int len) throws IOException { |
| waitForData(); |
| if (available() == -1) { |
| return -1; |
| } |
| |
| int toCopy = Math.min(len, size); |
| if(input.length-outPointer > toCopy) { |
| System.arraycopy(input, outPointer, b, off, toCopy); |
| outPointer += toCopy; |
| size -= toCopy; |
| } else { |
| int bytesToEnd = input.length-outPointer; |
| System.arraycopy(input, outPointer, b, off, bytesToEnd); |
| System.arraycopy(input, 0, b, off+bytesToEnd, toCopy-bytesToEnd); |
| outPointer = toCopy-bytesToEnd; |
| size -=toCopy; |
| } |
| return toCopy; |
| } |
| |
| @Override |
| public int read(byte[] b) throws IOException { |
| return read(b, 0, b.length); |
| } |
| |
| @Override |
| public synchronized int read() throws IOException { |
| waitForData(); |
| if (available() == -1) { |
| return -1; |
| } |
| |
| byte b = input[outPointer]; |
| outPointer++; |
| if (outPointer == input.length) { |
| outPointer = 0; |
| } |
| size -= 1; |
| return b; |
| } |
| |
| /** |
| * Blocks until data is available to be read. |
| * Ensure that the monitor for this object is obtained before |
| * calling this method. |
| */ |
| private void waitForData() { |
| while (size == 0 && !closed) { |
| try { |
| wait(); |
| } catch (InterruptedException e) { |
| } |
| } |
| } |
| |
| /** |
| * Appends text to this input stream's buffer. |
| * |
| * @param text the text to append to the buffer. |
| */ |
| public synchronized void appendData(String text) { |
| String encoding = console.getEncoding(); |
| byte[] newData; |
| if (encoding!=null) { |
| try { |
| newData = text.getBytes(encoding); |
| } catch (UnsupportedEncodingException e) { |
| newData = text.getBytes(); |
| } |
| } else { |
| newData = text.getBytes(); |
| } |
| |
| while(input.length-size < newData.length) { |
| growArray(); |
| } |
| |
| if (size == 0) { //inPointer == outPointer |
| System.arraycopy(newData, 0, input, 0, newData.length); |
| inPointer = newData.length; |
| size = newData.length; |
| outPointer = 0; |
| } else if (inPointer < outPointer || input.length - inPointer > newData.length) { |
| System.arraycopy(newData, 0, input, inPointer, newData.length); |
| inPointer += newData.length; |
| size += newData.length; |
| } else { |
| System.arraycopy(newData, 0, input, inPointer, input.length-inPointer); |
| System.arraycopy(newData, input.length-inPointer, input, 0, newData.length-(input.length-inPointer)); |
| inPointer = newData.length-(input.length-inPointer); |
| size += newData.length; |
| } |
| |
| if (inPointer == input.length) { |
| inPointer = 0; |
| } |
| notifyAll(); |
| } |
| |
| /** |
| * Enlarges the buffer. |
| */ |
| private void growArray() { |
| byte[] newInput = new byte[input.length+1024]; |
| if (outPointer < inPointer) { |
| System.arraycopy(input, outPointer, newInput, 0, size); |
| } else { |
| System.arraycopy(input, outPointer, newInput, 0, input.length-outPointer); |
| System.arraycopy(input, 0, newInput, input.length-outPointer, inPointer); |
| } |
| outPointer = 0; |
| inPointer = size; |
| input = newInput; |
| newInput = null; |
| } |
| |
| /** |
| * Returns this stream's font style. |
| * |
| * @return the font style used to decorate input in the associated console |
| */ |
| public int getFontStyle() { |
| return fontStyle; |
| } |
| |
| /** |
| * Sets this stream's font style. |
| * |
| * @param newFontStyle the font style to be used to decorate input in the associated console |
| */ |
| public void setFontStyle(int newFontStyle) { |
| if (newFontStyle != fontStyle) { |
| int old = fontStyle; |
| fontStyle = newFontStyle; |
| console.firePropertyChange(this, IConsoleConstants.P_FONT_STYLE, Integer.valueOf(old), Integer.valueOf(fontStyle)); |
| } |
| } |
| |
| /** |
| * Sets the color to used to decorate input in the associated console. |
| * |
| * @param newColor the color to used to decorate input in the associated console. |
| */ |
| public void setColor(Color newColor) { |
| Color old = color; |
| if (old == null || !old.equals(newColor)) { |
| color = newColor; |
| console.firePropertyChange(this, IConsoleConstants.P_STREAM_COLOR, old, newColor); |
| } |
| } |
| |
| /** |
| * Returns the color used to decorate input in the associated console |
| * |
| * @return the color used to decorate input in the associated console |
| */ |
| public Color getColor() { |
| return color; |
| } |
| |
| @Override |
| public int available() throws IOException { |
| if (closed && eofSent) { |
| throw new IOException("Input Stream Closed"); //$NON-NLS-1$ |
| } else if (size == 0) { |
| if (!eofSent) { |
| eofSent = true; |
| return -1; |
| } |
| } |
| return size; |
| } |
| |
| @Override |
| public synchronized void close() throws IOException { |
| if(closed) { |
| // Closeable#close() has no effect if already closed |
| return; |
| } |
| closed = true; |
| notifyAll(); |
| console.streamClosed(this); |
| } |
| } |