/*
* $Id$
*
* Copyright (C) 2003-2015 JNode.org
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library 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 Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; If not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.jnode.util;
import java.io.IOException;
import java.io.InputStream;
/**
* The SystemInputStream is a stream wrapped for the system's actual keyboard
* input stream. It deals with the problem that a System.in needs to be set
* before we have configured the keyboard. It also supports thread localization
* of the system input, though the CommandInvoker implementations do not use
* this anymore.
*/
public final class SystemInputStream extends InputStream {
// FIXME ... remove the thread localization support. It is now just a misleading
// historical relic.
private static final InputStream EMPTY = new EmptyInputStream();
private final class ThreadLocalInputStream extends InheritableThreadLocal {
public Object get() {
Object o = super.get();
if (o == EMPTY) {
set(systemIn);
o = systemIn;
}
return o;
}
protected Object initialValue() {
return systemIn;
}
}
private static SystemInputStream instance;
private InputStream systemIn;
private ThreadLocalInputStream systemInOwnerLocal;
private final ThreadLocalInputStream localeIn = new ThreadLocalInputStream();
public final InputStream getIn() {
return getLocalIn();
}
public final void setIn(InputStream in) {
if (in != this) {
localeIn.set(in);
}
}
public final void claimSystemIn() {
// TODO must be protected by the SecurityManager
setIn(systemIn);
}
public final void releaseSystemIn() {
this.systemIn = EMPTY;
}
public static SystemInputStream getInstance() {
// TODO protect me with SecurityManager !
if (instance == null) {
instance = new SystemInputStream();
}
return instance;
}
/**
* Set the wrapped stream to the supplied parameter. This method
* only has any effect if the wrapped stream is unset.
*
* @param systemIn
*/
public void initialize(InputStream systemIn) {
// TODO protect me with SecurityManager !
// register only the first keyboard
if (this.systemIn == EMPTY && systemIn != this) {
this.systemIn = systemIn;
}
}
private SystemInputStream() {
this.systemIn = EMPTY; // by default, no keyboard
}
/**
* Calls the <code>in.mark(int)</code> method.
*
* @param readlimit The parameter passed to <code>in.mark(int)</code>
*/
public final void mark(int readlimit) {
getLocalIn().mark(readlimit);
}
/**
* Calls the <code>in.markSupported()</code> method.
*
* @return <code>true</code> if mark/reset is supported, <code>false</code>
* otherwise
*/
public final boolean markSupported() {
return getLocalIn().markSupported();
}
/**
* Calls the <code>in.reset()</code> method.
*
* @throws IOException If an error occurs
*/
public final void reset() throws IOException {
getLocalIn().reset();
}
/**
* Calls the <code>in.available()</code> method.
*
* @return The value returned from <code>in.available()</code>
* @throws IOException If an error occurs
*/
public final int available() throws IOException {
return getLocalIn().available();
}
/**
* Calls the <code>in.skip(long)</code> method
*
* @param numBytes The requested number of bytes to skip.
* @return The value returned from <code>in.skip(long)</code>
* @throws IOException If an error occurs
*/
public final long skip(long numBytes) throws IOException {
return getLocalIn().skip(numBytes);
}
/**
* Calls the <code>in.read()</code> method
*
* @return The value returned from <code>in.read()</code>
* @throws IOException If an error occurs
*/
public final int read() throws IOException {
return getLocalIn().read();
}
/**
* Calls the <code>read(byte[], int, int)</code> overloaded method.
* Note that
* this method does not redirect its call directly to a corresponding
* method in <code>in</code>. This allows subclasses to override only the
* three argument version of <code>read</code>.
*
* @param buf The buffer to read bytes into
* @return The value retured from <code>in.read(byte[], int, int)</code>
* @throws IOException If an error occurs
*/
public final int read(byte[] buf) throws IOException {
return read(buf, 0, buf.length);
}
/**
* Calls the <code>in.read(byte[], int, int)</code> method.
*
* @param buf The buffer to read bytes into
* @param offset The index into the buffer to start storing bytes
* @param len The maximum number of bytes to read.
* @return The value retured from <code>in.read(byte[], int, int)</code>
* @throws IOException If an error occurs
*/
public final int read(byte[] buf, int offset, int len) throws IOException {
return getLocalIn().read(buf, offset, len);
}
/**
* This method closes the input stream by closing the input stream that
* this object is filtering. Future attempts to access this stream may
* throw an exception.
*
* @throws IOException If an error occurs
*/
public final void close() throws IOException {
getLocalIn().close();
}
private InputStream getLocalIn() {
return (InputStream) localeIn.get();
}
}