From: <cr...@us...> - 2008-08-31 09:17:37
|
Revision: 4518 http://jnode.svn.sourceforge.net/jnode/?rev=4518&view=rev Author: crawley Date: 2008-08-31 09:17:32 +0000 (Sun, 31 Aug 2008) Log Message: ----------- Reorganising the shell io classes and creating infrastructure ready for changes to the Command / AbstractCommand APIs. Added Paths: ----------- trunk/shell/src/shell/org/jnode/shell/io/ trunk/shell/src/shell/org/jnode/shell/io/AbstractCommandIO.java trunk/shell/src/shell/org/jnode/shell/io/AbstractCommandInput.java trunk/shell/src/shell/org/jnode/shell/io/AbstractCommandOutput.java trunk/shell/src/shell/org/jnode/shell/io/CommandIO.java trunk/shell/src/shell/org/jnode/shell/io/CommandIOException.java trunk/shell/src/shell/org/jnode/shell/io/CommandInput.java trunk/shell/src/shell/org/jnode/shell/io/CommandOutput.java trunk/shell/src/shell/org/jnode/shell/io/FanoutOutputStream.java trunk/shell/src/shell/org/jnode/shell/io/NullInputStream.java trunk/shell/src/shell/org/jnode/shell/io/NullOutputStream.java trunk/shell/src/shell/org/jnode/shell/io/ReaderInputStream.java trunk/shell/src/shell/org/jnode/shell/io/StreamMarker.java Added: trunk/shell/src/shell/org/jnode/shell/io/AbstractCommandIO.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/io/AbstractCommandIO.java (rev 0) +++ trunk/shell/src/shell/org/jnode/shell/io/AbstractCommandIO.java 2008-08-31 09:17:32 UTC (rev 4518) @@ -0,0 +1,27 @@ +package org.jnode.shell.io; + +public abstract class AbstractCommandIO implements CommandIO { + + private String assignedEncoding; + + @Override + public final String getAssignedEncoding() { + return assignedEncoding; + } + + @Override + public abstract int getDirection(); + + @Override + public final String getEncoding() { + return assignedEncoding != null ? assignedEncoding : getImpliedEncoding(); + } + + protected abstract String getImpliedEncoding(); + + @Override + public abstract Object getSystemObject(); + + @Override + public abstract boolean isTTY(); +} Added: trunk/shell/src/shell/org/jnode/shell/io/AbstractCommandInput.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/io/AbstractCommandInput.java (rev 0) +++ trunk/shell/src/shell/org/jnode/shell/io/AbstractCommandInput.java 2008-08-31 09:17:32 UTC (rev 4518) @@ -0,0 +1,54 @@ +package org.jnode.shell.io; + +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.io.UnsupportedEncodingException; + +public abstract class AbstractCommandInput implements CommandInput { + + private Reader reader; + private InputStream inputStream; + + AbstractCommandInput(Reader reader, InputStream inputStream) { + this.reader = reader; + this.inputStream = inputStream; + } + + @Override + public synchronized InputStream getInputStream() { + if (inputStream == null) { + inputStream = new ReaderInputStream(reader, getEncoding()); + } + return inputStream; + } + + @Override + public Reader getReader() throws CommandIOException { + if (reader == null) { + try { + reader = new InputStreamReader(inputStream, getEncoding()); + } catch (UnsupportedEncodingException ex) { + throw new CommandIOException("Cannot get reader", ex); + } + } + return reader; + } + + @Override + public final int getDirection() { + return DIRECTION_IN; + } + + @Override + public Object getSystemObject() { + return null; + } + + @Override + public boolean isTTY() { + // TODO Auto-generated method stub + return false; + } + +} Added: trunk/shell/src/shell/org/jnode/shell/io/AbstractCommandOutput.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/io/AbstractCommandOutput.java (rev 0) +++ trunk/shell/src/shell/org/jnode/shell/io/AbstractCommandOutput.java 2008-08-31 09:17:32 UTC (rev 4518) @@ -0,0 +1,60 @@ +package org.jnode.shell.io; + +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.Reader; +import java.io.UnsupportedEncodingException; +import java.io.Writer; + +// FIXME implement our own one ... +import org.apache.tools.ant.util.ReaderInputStream; + +public abstract class AbstractCommandOutput implements CommandOutput { + + private Writer writer; + private OutputStream outputStream; + + AbstractCommandOutput(Writer writer, OutputStream outputStream) { + this.writer = writer; + this.outputStream = outputStream; + } + + @Override + public synchronized OutputStream getOutputStream() { + if (outputStream == null) { + outputStream = new WriterOutputStream(writer, getEncoding()); + } + return outputStream; + } + + @Override + public Writer getWriter() throws CommandIOException { + if (writer == null) { + try { + writer = new OutputStreamWriter(outputStream, getEncoding()); + } catch (UnsupportedEncodingException ex) { + throw new CommandIOException("Cannot get writer", ex); + } + } + return writer; + } + + @Override + public final int getDirection() { + return DIRECTION_OUT; + } + + @Override + public Object getSystemObject() { + return null; + } + + @Override + public boolean isTTY() { + // TODO Auto-generated method stub + return false; + } + +} Added: trunk/shell/src/shell/org/jnode/shell/io/CommandIO.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/io/CommandIO.java (rev 0) +++ trunk/shell/src/shell/org/jnode/shell/io/CommandIO.java 2008-08-31 09:17:32 UTC (rev 4518) @@ -0,0 +1,19 @@ +package org.jnode.shell.io; + +public interface CommandIO { + + public static final int DIRECTION_IN = 1; + public static final int DIRECTION_OUT = 2; + public static final int DIRECTION_INOUT = 3; // Not yet used + + public int getDirection(); + + public boolean isTTY(); + + public Object getSystemObject(); + + public String getEncoding(); + + public String getAssignedEncoding(); + +} Added: trunk/shell/src/shell/org/jnode/shell/io/CommandIOException.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/io/CommandIOException.java (rev 0) +++ trunk/shell/src/shell/org/jnode/shell/io/CommandIOException.java 2008-08-31 09:17:32 UTC (rev 4518) @@ -0,0 +1,15 @@ +package org.jnode.shell.io; + +public class CommandIOException extends RuntimeException { + + private static final long serialVersionUID = 1L; + + public CommandIOException(String message, Throwable cause) { + super(message, cause); + } + + public CommandIOException(String message) { + super(message); + } + +} Added: trunk/shell/src/shell/org/jnode/shell/io/CommandInput.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/io/CommandInput.java (rev 0) +++ trunk/shell/src/shell/org/jnode/shell/io/CommandInput.java 2008-08-31 09:17:32 UTC (rev 4518) @@ -0,0 +1,11 @@ +package org.jnode.shell.io; + +import java.io.InputStream; +import java.io.Reader; + +public interface CommandInput extends CommandIO { + + public InputStream getInputStream(); + + public Reader getReader() throws CommandIOException; +} Added: trunk/shell/src/shell/org/jnode/shell/io/CommandOutput.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/io/CommandOutput.java (rev 0) +++ trunk/shell/src/shell/org/jnode/shell/io/CommandOutput.java 2008-08-31 09:17:32 UTC (rev 4518) @@ -0,0 +1,14 @@ +package org.jnode.shell.io; + +import java.io.OutputStream; +import java.io.PrintStream; +import java.io.Writer; + +public interface CommandOutput extends CommandIO { + + public OutputStream getOutputStream(); + + public PrintStream getPrintStream(); + + public Writer getWriter(); +} Added: trunk/shell/src/shell/org/jnode/shell/io/FanoutOutputStream.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/io/FanoutOutputStream.java (rev 0) +++ trunk/shell/src/shell/org/jnode/shell/io/FanoutOutputStream.java 2008-08-31 09:17:32 UTC (rev 4518) @@ -0,0 +1,131 @@ +/* + * $Id: ThreadCommandInvoker.java 3374 2007-08-02 18:15:27Z lsantha $ + * + * JNode.org + * Copyright (C) 2007 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.shell.io; + +import java.io.IOException; +import java.io.OutputStream; + +/** + * The FanoutOutputStream writes its output to multiple print streams. This implementation + * makes no attempt to you from adding the same OutputStream multiple times in the fanout. + * If you do this, the close method will attempt to close some streams more than once. + * + * @author cr...@jn... + */ +public class FanoutOutputStream extends OutputStream { + + private OutputStream[] streams; + private final boolean ignoreClose; + + /** + * Construct a FanoutOutputStream for an an initial set of streams + * @param ignoreClose if <code>true</code>, a close all just does a flush. In other words + * the responsibility of closing the individual streams remains with the caller. + * @param streams The initial set of streams. + */ + public FanoutOutputStream(boolean ignoreClose, OutputStream ... streams) { + this.streams = streams; + this.ignoreClose = ignoreClose; + } + + /** + * Add another OutputStream to the fanout. + * + * @param os the stream to be added. + */ + public synchronized void addStream(OutputStream os) { + int len = streams.length; + OutputStream[] tmp = new OutputStream[len + 1]; + System.arraycopy(streams, 0, tmp, 0, len); + tmp[len] = os; + streams = tmp; + } + + /** + * Remove a OutputStream from the fanout. + * @param os the stream to be removed. + * @return returns <code>true</code> if the stream to be removed was removed. + */ + public synchronized boolean removeStream(OutputStream os) { + int len = streams.length; + for (int i = 0; i < len; i++) { + if (streams[i] == os) { + int len2 = streams.length - 1; + OutputStream[] tmp = new OutputStream[len2]; + for (int j = 0; j < len2; j++) { + if (j < i) { + tmp[j] = streams[j]; + } else { + tmp[j] = streams[j + 1]; + } + } + return true; + } + } + return false; + } + + @Override + public synchronized void close() throws IOException { + if (streams == null) { + // already closed. + } else if (ignoreClose) { + flush(); + } else { + for (OutputStream os : streams) { + os.close(); + } + streams = null; + } + } + + @Override + public void flush() throws IOException { + OutputStream[] streams = this.streams; + for (OutputStream os : streams) { + os.flush(); + } + } + + @Override + public void write(byte[] b, int off, int len) throws IOException { + OutputStream[] streams = this.streams; + for (OutputStream os : streams) { + os.write(b, off, len); + } + } + + @Override + public void write(byte[] b) throws IOException { + OutputStream[] streams = this.streams; + for (OutputStream os : streams) { + os.write(b); + } + } + + @Override + public void write(int b) throws IOException { + OutputStream[] streams = this.streams; + for (OutputStream os : streams) { + os.write(b); + } + } +} Added: trunk/shell/src/shell/org/jnode/shell/io/NullInputStream.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/io/NullInputStream.java (rev 0) +++ trunk/shell/src/shell/org/jnode/shell/io/NullInputStream.java 2008-08-31 09:17:32 UTC (rev 4518) @@ -0,0 +1,39 @@ +/* + * $Id: ThreadCommandInvoker.java 3374 2007-08-02 18:15:27Z lsantha $ + * + * JNode.org + * Copyright (C) 2007 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.shell.io; + +import java.io.IOException; +import java.io.InputStream; + +/** + * A NullInputStream instance is the logical equivalent of "/dev/null". Calling + * a read method returns -1 to indicate EOF. + * + * @author Stephen Crawley + */ +public class NullInputStream extends InputStream { + + @Override + public int read() throws IOException { + // Return the EOF indication + return -1; + } +} Added: trunk/shell/src/shell/org/jnode/shell/io/NullOutputStream.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/io/NullOutputStream.java (rev 0) +++ trunk/shell/src/shell/org/jnode/shell/io/NullOutputStream.java 2008-08-31 09:17:32 UTC (rev 4518) @@ -0,0 +1,33 @@ +/* + * $Id: ThreadCommandInvoker.java 3374 2007-08-02 18:15:27Z lsantha $ + * + * JNode.org + * Copyright (C) 2007 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.shell.io; + +import java.io.IOException; +import java.io.OutputStream; + +public class NullOutputStream extends OutputStream { + + @Override + public void write(int b) throws IOException { + // black-hole all output. + } + +} Added: trunk/shell/src/shell/org/jnode/shell/io/ReaderInputStream.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/io/ReaderInputStream.java (rev 0) +++ trunk/shell/src/shell/org/jnode/shell/io/ReaderInputStream.java 2008-08-31 09:17:32 UTC (rev 4518) @@ -0,0 +1,110 @@ +package org.jnode.shell.io; + +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.Charset; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.CoderResult; + +public class ReaderInputStream extends InputStream { + private final Reader reader; + + private CharBuffer chars = CharBuffer.allocate(1024); + private ByteBuffer bytes = ByteBuffer.allocate(2048); + + private CharsetEncoder encoder; + + public ReaderInputStream(Reader reader, String encoding) { + this.reader = reader; + this.encoder = Charset.forName(encoding).newEncoder(); + this.bytes.position(bytes.limit()); + this.chars.position(chars.limit()); + } + + @Override + public synchronized int read() throws IOException { + if (bytes.remaining() == 0) { + if (!fillBuffer(true)) { + return -1; + } + } + return bytes.get(); + } + + @Override + public synchronized int read(byte[] b, int off, int len) throws IOException { + if (off < 0 || off > b.length || len < 0 || off + len > b.length || off + len < 0) { + throw new IndexOutOfBoundsException(); + } + // This implementation is simple-minded. I'm sure we could recode it to avoid + // the 'bytes.get' copying step if we thought about it. + int count = 0; + while (count < len) { + if (bytes.remaining() == 0) { + if (!fillBuffer(count == 0)) { + return count == 0 ? -1 : count; + } + } + int copied = Math.min(bytes.remaining(), len); + bytes.get(b, off, copied); + System.err.println("Copied " + copied); + count += copied; + len -= copied; + off += copied; + } + return count; + } + + @Override + public int read(byte[] b) throws IOException { + return this.read(b, 0, b.length); + } + + /** + * This method puts bytes into the (empty) 'bytes' buffer. It returns + * <code>false</code> if no bytes were copied either because the reader + * would have blocked or because it returned <code>-1</code>. + * + * @param wait if <code>true</code> allow the reader to block. + * @return <code>true</code> if we've added some data to 'bytes'. + * @throws IOException + */ + private boolean fillBuffer(boolean wait) throws IOException { + bytes.clear(); + // The loop is necessary because the way that the encoder has to deal + // with UTF-16 surrogate pairs. If the one and only character returned + // by the reader is the first char of a surrogate pair, the encoder won't + // (can't) put anything into the 'bytes' buffer. So if 'wait' is true, + // we must go around a second time to get the second character of the + // surrogate pair. + do { + CoderResult cr; + if (chars.remaining() == 0) { + chars.clear(); + if (!reader.ready() && !wait) { + bytes.flip(); + return false; + } + if (reader.read(chars) == -1) { + System.err.println("Reached EOF"); + bytes.flip(); + cr = encoder.encode(chars, bytes, true); + if (cr.isError()) { + cr.throwException(); + } + return bytes.remaining() > 0; + } + chars.flip(); + } + cr = encoder.encode(chars, bytes, false); + if (cr.isError()) { + cr.throwException(); + } + } while (wait && bytes.position() == 0); + bytes.flip(); + return true; + } +} Added: trunk/shell/src/shell/org/jnode/shell/io/StreamMarker.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/io/StreamMarker.java (rev 0) +++ trunk/shell/src/shell/org/jnode/shell/io/StreamMarker.java 2008-08-31 09:17:32 UTC (rev 4518) @@ -0,0 +1,46 @@ +/* + * $Id: ShellManager.java 3571 2007-10-26 21:30:12Z lsantha $ + * + * JNode.org + * Copyright (C) 2003-2006 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.shell.io; + +import java.io.Closeable; + +/** + * Instances of this class are used to denote well known streams (e.g. standard + * input, etc) when building a CommandLine. They should be translated into real + * streams before the command is actually invoked. + * + * @author cr...@jn... + */ +public final class StreamMarker implements Closeable { + private final String name; + + public StreamMarker(String name) { + this.name = name; + } + + public void close() { + // Dummy operation + } + + public String toString() { + return name; + } +} This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |