/******************************************************************************* * Copyright (c) 2000, 2017 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 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; } /* * (non-Javadoc) * @see java.io.InputStream#read(byte[], int, int) */ @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; } /* * (non-Javadoc) * @see java.io.InputStream#read(byte[]) */ @Override public int read(byte[] b) throws IOException { return read(b, 0, b.length); } /* * (non-Javadoc) * @see java.io.InputStream#read() */ @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; } /* (non-Javadoc) * @see java.io.InputStream#available() */ @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; } throw new IOException("Input Stream Closed"); //$NON-NLS-1$ } return size; } /* (non-Javadoc) * @see java.io.InputStream#close() */ @Override public synchronized void close() throws IOException { if(closed) { // Closeable#close() has no effect if already closed return; } closed = true; notifyAll(); console.streamClosed(this); } }