diff options
Diffstat (limited to 'libjava/classpath/java/io')
79 files changed, 23049 insertions, 0 deletions
diff --git a/libjava/classpath/java/io/BufferedInputStream.java b/libjava/classpath/java/io/BufferedInputStream.java new file mode 100644 index 00000000000..6fb024614d2 --- /dev/null +++ b/libjava/classpath/java/io/BufferedInputStream.java @@ -0,0 +1,379 @@ +/* BufferedInputStream.java -- An input stream that implements buffering + Copyright (C) 1998, 1999, 2001, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 + * "The Java Language Specification", ISBN 0-201-63451-1 + * plus online API docs for JDK 1.2 beta from http://www.javasoft.com. + * Status: Believed complete and correct. + */ + +/** + * This subclass of <code>FilterInputStream</code> buffers input from an + * underlying implementation to provide a possibly more efficient read + * mechanism. It maintains the buffer and buffer state in instance + * variables that are available to subclasses. The default buffer size + * of 2048 bytes can be overridden by the creator of the stream. + * <p> + * This class also implements mark/reset functionality. It is capable + * of remembering any number of input bytes, to the limits of + * system memory or the size of <code>Integer.MAX_VALUE</code> + * <p> + * Please note that this class does not properly handle character + * encodings. Consider using the <code>BufferedReader</code> class which + * does. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + * @author Jeroen Frijters (jeroen@frijters.net) + */ +public class BufferedInputStream extends FilterInputStream +{ + + /** + * This is the default buffer size + */ + private static final int DEFAULT_BUFFER_SIZE = 2048; + + /** + * The buffer used for storing data from the underlying stream. + */ + protected byte[] buf; + + /** + * The number of valid bytes currently in the buffer. It is also the index + * of the buffer position one byte past the end of the valid data. + */ + protected int count; + + /** + * The index of the next character that will by read from the buffer. + * When <code>pos == count</code>, the buffer is empty. + */ + protected int pos; + + /** + * The value of <code>pos</code> when the <code>mark()</code> method was + * called. + * This is set to -1 if there is no mark set. + */ + protected int markpos = -1; + + /** + * This is the maximum number of bytes than can be read after a + * call to <code>mark()</code> before the mark can be discarded. + * After this may bytes are read, the <code>reset()</code> method + * may not be called successfully. + */ + protected int marklimit; + + /** + * This is the initial buffer size. When the buffer is grown because + * of marking requirements, it will be grown by bufferSize increments. + * The underlying stream will be read in chunks of bufferSize. + */ + private final int bufferSize; + + /** + * This method initializes a new <code>BufferedInputStream</code> that will + * read from the specified subordinate stream with a default buffer size + * of 2048 bytes + * + * @param in The subordinate stream to read from + */ + public BufferedInputStream(InputStream in) + { + this(in, DEFAULT_BUFFER_SIZE); + } + + /** + * This method initializes a new <code>BufferedInputStream</code> that will + * read from the specified subordinate stream with a buffer size that + * is specified by the caller. + * + * @param in The subordinate stream to read from + * @param size The buffer size to use + * + * @exception IllegalArgumentException when size is smaller then 1 + */ + public BufferedInputStream(InputStream in, int size) + { + super(in); + if (size <= 0) + throw new IllegalArgumentException(); + buf = new byte[size]; + // initialize pos & count to bufferSize, to prevent refill from + // allocating a new buffer (if the caller starts out by calling mark()). + pos = count = bufferSize = size; + } + + /** + * This method returns the number of bytes that can be read from this + * stream before a read can block. A return of 0 indicates that blocking + * might (or might not) occur on the very next read attempt. + * <p> + * The number of available bytes will be the number of read ahead bytes + * stored in the internal buffer plus the number of available bytes in + * the underlying stream. + * + * @return The number of bytes that can be read before blocking could occur + * + * @exception IOException If an error occurs + */ + public synchronized int available() throws IOException + { + return count - pos + super.available(); + } + + /** + * This method closes the underlying input stream and frees any + * resources associated with it. Sets <code>buf</code> to <code>null</code>. + * + * @exception IOException If an error occurs. + */ + public void close() throws IOException + { + // Free up the array memory. + buf = null; + pos = count = 0; + markpos = -1; + super.close(); + } + + /** + * This method marks a position in the input to which the stream can be + * "reset" by calling the <code>reset()</code> method. The parameter + * <code>readlimit</code> is the number of bytes that can be read from the + * stream after setting the mark before the mark becomes invalid. For + * example, if <code>mark()</code> is called with a read limit of 10, then + * when 11 bytes of data are read from the stream before the + * <code>reset()</code> method is called, then the mark is invalid and the + * stream object instance is not required to remember the mark. + * <p> + * Note that the number of bytes that can be remembered by this method + * can be greater than the size of the internal read buffer. It is also + * not dependent on the subordinate stream supporting mark/reset + * functionality. + * + * @param readlimit The number of bytes that can be read before the mark + * becomes invalid + */ + public synchronized void mark(int readlimit) + { + marklimit = readlimit; + markpos = pos; + } + + /** + * This method returns <code>true</code> to indicate that this class + * supports mark/reset functionality. + * + * @return <code>true</code> to indicate that mark/reset functionality is + * supported + * + */ + public boolean markSupported() + { + return true; + } + + /** + * This method reads an unsigned byte from the input stream and returns it + * as an int in the range of 0-255. This method also will return -1 if + * the end of the stream has been reached. + * <p> + * This method will block until the byte can be read. + * + * @return The byte read or -1 if end of stream + * + * @exception IOException If an error occurs + */ + public synchronized int read() throws IOException + { + if (pos >= count && !refill()) + return -1; // EOF + + return buf[pos++] & 0xFF; + } + + /** + * This method reads bytes from a stream and stores them into a caller + * supplied buffer. It starts storing the data at index <code>off</code> + * into the buffer and attempts to read <code>len</code> bytes. This method + * can return before reading the number of bytes requested, but it will try + * to read the requested number of bytes by repeatedly calling the underlying + * stream as long as available() for this stream continues to return a + * non-zero value (or until the requested number of bytes have been read). + * The actual number of bytes read is returned as an int. A -1 is returned + * to indicate the end of the stream. + * <p> + * This method will block until some data can be read. + * + * @param b The array into which the bytes read should be stored + * @param off The offset into the array to start storing bytes + * @param len The requested number of bytes to read + * + * @return The actual number of bytes read, or -1 if end of stream. + * + * @exception IOException If an error occurs. + * @exception IndexOutOfBoundsException when <code>off</code> or + * <code>len</code> are negative, or when <code>off + len</code> + * is larger then the size of <code>b</code>, + */ + public synchronized int read(byte[] b, int off, int len) throws IOException + { + if (off < 0 || len < 0 || b.length - off < len) + throw new IndexOutOfBoundsException(); + + if (len == 0) + return 0; + + if (pos >= count && !refill()) + return -1; // No bytes were read before EOF. + + int totalBytesRead = Math.min(count - pos, len); + System.arraycopy(buf, pos, b, off, totalBytesRead); + pos += totalBytesRead; + off += totalBytesRead; + len -= totalBytesRead; + + while (len > 0 && super.available() > 0 && refill()) + { + int remain = Math.min(count - pos, len); + System.arraycopy(buf, pos, b, off, remain); + pos += remain; + off += remain; + len -= remain; + totalBytesRead += remain; + } + + return totalBytesRead; + } + + /** + * This method resets a stream to the point where the <code>mark()</code> + * method was called. Any bytes that were read after the mark point was + * set will be re-read during subsequent reads. + * <p> + * This method will throw an IOException if the number of bytes read from + * the stream since the call to <code>mark()</code> exceeds the mark limit + * passed when establishing the mark. + * + * @exception IOException If <code>mark()</code> was never called or more + * then <code>marklimit</code> bytes were read since the last + * call to <code>mark()</code> + */ + public synchronized void reset() throws IOException + { + if (markpos == -1) + throw new IOException(buf == null ? "Stream closed." : "Invalid mark."); + + pos = markpos; + } + + /** + * This method skips the specified number of bytes in the stream. It + * returns the actual number of bytes skipped, which may be less than the + * requested amount. + * + * @param n The requested number of bytes to skip + * + * @return The actual number of bytes skipped. + * + * @exception IOException If an error occurs + */ + public synchronized long skip(long n) throws IOException + { + if (buf == null) + throw new IOException("Stream closed."); + + final long origN = n; + + while (n > 0L) + { + if (pos >= count && !refill()) + break; + + int numread = (int) Math.min((long) (count - pos), n); + pos += numread; + n -= numread; + } + + return origN - n; + } + + /** + * Called to refill the buffer (when count is equal to pos). + * + * @return <code>true</code> when at least one additional byte was read + * into <code>buf</code>, <code>false</code> otherwise (at EOF). + */ + private boolean refill() throws IOException + { + if (buf == null) + throw new IOException("Stream closed."); + + if (markpos == -1 || count - markpos >= marklimit) + { + markpos = -1; + pos = count = 0; + } + else + { + byte[] newbuf = buf; + if (markpos < bufferSize) + { + newbuf = new byte[count - markpos + bufferSize]; + } + System.arraycopy(buf, markpos, newbuf, 0, count - markpos); + buf = newbuf; + count -= markpos; + pos -= markpos; + markpos = 0; + } + + int numread = super.read(buf, count, bufferSize); + + if (numread <= 0) // EOF + return false; + + count += numread; + return true; + } +} diff --git a/libjava/classpath/java/io/BufferedOutputStream.java b/libjava/classpath/java/io/BufferedOutputStream.java new file mode 100644 index 00000000000..ce7ebc7e938 --- /dev/null +++ b/libjava/classpath/java/io/BufferedOutputStream.java @@ -0,0 +1,192 @@ +/* BufferedOutputStream.java -- Buffer output into large blocks before writing + Copyright (C) 1998, 2000, 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This class accumulates bytes written in a buffer instead of immediately + * writing the data to the underlying output sink. The bytes are instead + * as one large block when the buffer is filled, or when the stream is + * closed or explicitly flushed. This mode operation can provide a more + * efficient mechanism for writing versus doing numerous small unbuffered + * writes. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class BufferedOutputStream extends FilterOutputStream +{ + /** + * This is the default buffer size + */ + private static final int DEFAULT_BUFFER_SIZE = 512; + + /** + * This is the internal byte array used for buffering output before + * writing it. + */ + protected byte[] buf; + + /** + * This is the number of bytes that are currently in the buffer and + * are waiting to be written to the underlying stream. It always points to + * the index into the buffer where the next byte of data will be stored + */ + protected int count; + + /** + * This method initializes a new <code>BufferedOutputStream</code> instance + * that will write to the specified subordinate <code>OutputStream</code> + * and which will use a default buffer size of 512 bytes. + * + * @param out The underlying <code>OutputStream</code> to write data to + */ + public BufferedOutputStream(OutputStream out) + { + this(out, DEFAULT_BUFFER_SIZE); + } + + /** + * This method initializes a new <code>BufferedOutputStream</code> instance + * that will write to the specified subordinate <code>OutputStream</code> + * and which will use the specified buffer size + * + * @param out The underlying <code>OutputStream</code> to write data to + * @param size The size of the internal buffer + */ + public BufferedOutputStream(OutputStream out, int size) + { + super(out); + + buf = new byte[size]; + } + + /** + * This method causes any currently buffered bytes to be immediately + * written to the underlying output stream. + * + * @exception IOException If an error occurs + */ + public synchronized void flush() throws IOException + { + if (count == 0) + return; + + out.write(buf, 0, count); + count = 0; + out.flush(); + } + + /** + * This method flushes any remaining buffered bytes then closes the + * underlying output stream. Any further attempts to write to this stream + * may throw an exception + * + public synchronized void close() throws IOException + { + flush(); + out.close(); + } + */ + + /** + * This method runs when the object is garbage collected. It is + * responsible for ensuring that all buffered bytes are written and + * for closing the underlying stream. + * + * @exception IOException If an error occurs (ignored by the Java runtime) + * + protected void finalize() throws IOException + { + close(); + } + */ + + /** + * This method writes a single byte of data. This will be written to the + * buffer instead of the underlying data source. However, if the buffer + * is filled as a result of this write request, it will be flushed to the + * underlying output stream. + * + * @param b The byte of data to be written, passed as an int + * + * @exception IOException If an error occurs + */ + public synchronized void write(int b) throws IOException + { + if (count == buf.length) + flush(); + + buf[count] = (byte)(b & 0xFF); + ++count; + } + + /** + * This method writes <code>len</code> bytes from the byte array + * <code>buf</code> starting at position <code>offset</code> in the buffer. + * These bytes will be written to the internal buffer. However, if this + * write operation fills the buffer, the buffer will be flushed to the + * underlying output stream. + * + * @param buf The array of bytes to write. + * @param offset The index into the byte array to start writing from. + * @param len The number of bytes to write. + * + * @exception IOException If an error occurs + */ + public synchronized void write(byte[] buf, int offset, int len) + throws IOException + { + // Buffer can hold everything. Note that the case where LEN < 0 + // is automatically handled by the downstream write. + if (len < (this.buf.length - count)) + { + System.arraycopy(buf, offset, this.buf, count, len); + count += len; + } + else + { + // The write was too big. So flush the buffer and write the new + // bytes directly to the underlying stream, per the JDK 1.2 + // docs. + flush(); + out.write (buf, offset, len); + } + } + +} // class BufferedOutputStream + diff --git a/libjava/classpath/java/io/BufferedReader.java b/libjava/classpath/java/io/BufferedReader.java new file mode 100644 index 00000000000..4849949c989 --- /dev/null +++ b/libjava/classpath/java/io/BufferedReader.java @@ -0,0 +1,581 @@ +/* BufferedReader.java + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/* Written using "Java Class Libraries", 2nd edition, plus online + * API docs for JDK 1.2 beta from http://www.javasoft.com. + * Status: Believed complete and correct. + */ + +/** + * This subclass of <code>FilterReader</code> buffers input from an + * underlying implementation to provide a possibly more efficient read + * mechanism. It maintains the buffer and buffer state in instance + * variables that are available to subclasses. The default buffer size + * of 8192 chars can be overridden by the creator of the stream. + * <p> + * This class also implements mark/reset functionality. It is capable + * of remembering any number of input chars, to the limits of + * system memory or the size of <code>Integer.MAX_VALUE</code> + * + * @author Per Bothner (bothner@cygnus.com) + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class BufferedReader extends Reader +{ + Reader in; + char[] buffer; + /* Index of current read position. Must be >= 0 and <= limit. */ + /* There is a special case where pos may be equal to limit+1; this + * is used as an indicator that a readLine was done with a '\r' was + * the very last char in the buffer. Since we don't want to read-ahead + * and potentially block, we set pos this way to indicate the situation + * and deal with it later. Doing it this way rather than having a + * separate boolean field to indicate the condition has the advantage + * that it is self-clearing on things like mark/reset. + */ + int pos; + /* Limit of valid data in buffer. Must be >= pos and <= buffer.length. */ + /* This can be < pos in the one special case described above. */ + int limit; + + /* The value -1 means there is no mark, or the mark has been invalidated. + Otherwise, markPos is the index in the buffer of the marked position. + Must be >= 0 and <= pos. + Note we do not explicitly store the read-limit. + The implicit read-limit is (buffer.length - markPos), which is + guaranteed to be >= the read-limit requested in the call to mark. */ + int markPos = -1; + + // The JCL book specifies the default buffer size as 8K characters. + // This is package-private because it is used by LineNumberReader. + static final int DEFAULT_BUFFER_SIZE = 8192; + + /** + * The line buffer for <code>readLine</code>. + */ + private StringBuffer sbuf = null; + + /** + * Create a new <code>BufferedReader</code> that will read from the + * specified subordinate stream with a default buffer size of 8192 chars. + * + * @param in The subordinate stream to read from + */ + public BufferedReader(Reader in) + { + this(in, DEFAULT_BUFFER_SIZE); + } + + /** + * Create a new <code>BufferedReader</code> that will read from the + * specified subordinate stream with a buffer size that is specified by the + * caller. + * + * @param in The subordinate stream to read from + * @param size The buffer size to use + * + * @exception IllegalArgumentException if size <= 0 + */ + public BufferedReader(Reader in, int size) + { + super(in.lock); + if (size <= 0) + throw new IllegalArgumentException("Illegal buffer size: " + size); + this.in = in; + buffer = new char[size]; + } + + /** + * This method closes the underlying stream and frees any associated + * resources. + * + * @exception IOException If an error occurs + */ + public void close() throws IOException + { + synchronized (lock) + { + if (in != null) + in.close(); + in = null; + buffer = null; + } + } + + /** + * Returns <code>true</code> to indicate that this class supports mark/reset + * functionality. + * + * @return <code>true</code> + */ + public boolean markSupported() + { + return true; + } + + /** + * Mark a position in the input to which the stream can be + * "reset" by calling the <code>reset()</code> method. The parameter + * <code>readLimit</code> is the number of chars that can be read from the + * stream after setting the mark before the mark becomes invalid. For + * example, if <code>mark()</code> is called with a read limit of 10, then + * when 11 chars of data are read from the stream before the + * <code>reset()</code> method is called, then the mark is invalid and the + * stream object instance is not required to remember the mark. + * <p> + * Note that the number of chars that can be remembered by this method + * can be greater than the size of the internal read buffer. It is also + * not dependent on the subordinate stream supporting mark/reset + * functionality. + * + * @param readLimit The number of chars that can be read before the mark + * becomes invalid + * + * @exception IOException If an error occurs + * @exception IllegalArgumentException if readLimit is negative. + */ + public void mark(int readLimit) throws IOException + { + if (readLimit < 0) + throw new IllegalArgumentException("Read-ahead limit is negative"); + + synchronized (lock) + { + checkStatus(); + // In this method we need to be aware of the special case where + // pos + 1 == limit. This indicates that a '\r' was the last char + // in the buffer during a readLine. We'll want to maintain that + // condition after we shift things around and if a larger buffer is + // needed to track readLimit, we'll have to make it one element + // larger to ensure we don't invalidate the mark too early, if the + // char following the '\r' is NOT a '\n'. This is ok because, per + // the spec, we are not required to invalidate when passing readLimit. + // + // Note that if 'pos > limit', then doing 'limit -= pos' will cause + // limit to be negative. This is the only way limit will be < 0. + + if (pos + readLimit > limit) + { + char[] old_buffer = buffer; + int extraBuffSpace = 0; + if (pos > limit) + extraBuffSpace = 1; + if (readLimit + extraBuffSpace > limit) + buffer = new char[readLimit + extraBuffSpace]; + limit -= pos; + if (limit >= 0) + { + System.arraycopy(old_buffer, pos, buffer, 0, limit); + pos = 0; + } + } + + if (limit < 0) + { + // Maintain the relationship of 'pos > limit'. + pos = 1; + limit = markPos = 0; + } + else + markPos = pos; + // Now pos + readLimit <= buffer.length. thus if we need to read + // beyond buffer.length, then we are allowed to invalidate markPos. + } + } + + /** + * Reset the stream to the point where the <code>mark()</code> method + * was called. Any chars that were read after the mark point was set will + * be re-read during subsequent reads. + * <p> + * This method will throw an IOException if the number of chars read from + * the stream since the call to <code>mark()</code> exceeds the mark limit + * passed when establishing the mark. + * + * @exception IOException If an error occurs; + */ + public void reset() throws IOException + { + synchronized (lock) + { + checkStatus(); + if (markPos < 0) + throw new IOException("mark never set or invalidated"); + + // Need to handle the extremely unlikely case where a readLine was + // done with a '\r' as the last char in the buffer; which was then + // immediately followed by a mark and a reset with NO intervening + // read of any sort. In that case, setting pos to markPos would + // lose that info and a subsequent read would thus not skip a '\n' + // (if one exists). The value of limit in this rare case is zero. + // We can assume that if limit is zero for other reasons, then + // pos is already set to zero and doesn't need to be readjusted. + if (limit > 0) + pos = markPos; + } + } + + /** + * This method determines whether or not a stream is ready to be read. If + * this method returns <code>false</code> then this stream could (but is + * not guaranteed to) block on the next read attempt. + * + * @return <code>true</code> if this stream is ready to be read, + * <code>false</code> otherwise + * + * @exception IOException If an error occurs + */ + public boolean ready() throws IOException + { + synchronized (lock) + { + checkStatus(); + return pos < limit || in.ready(); + } + } + + /** + * This method read chars from a stream and stores them into a caller + * supplied buffer. It starts storing the data at index + * <code>offset</code> into + * the buffer and attempts to read <code>len</code> chars. This method can + * return before reading the number of chars requested. The actual number + * of chars read is returned as an int. A -1 is returned to indicate the + * end of the stream. + * <p> + * This method will block until some data can be read. + * + * @param buf The array into which the chars read should be stored + * @param offset The offset into the array to start storing chars + * @param count The requested number of chars to read + * + * @return The actual number of chars read, or -1 if end of stream. + * + * @exception IOException If an error occurs. + * @exception IndexOutOfBoundsException If offset and count are not + * valid regarding buf. + */ + public int read(char[] buf, int offset, int count) throws IOException + { + if (offset < 0 || offset + count > buf.length || count < 0) + throw new IndexOutOfBoundsException(); + + synchronized (lock) + { + checkStatus(); + // Once again, we need to handle the special case of a readLine + // that has a '\r' at the end of the buffer. In this case, we'll + // need to skip a '\n' if it is the next char to be read. + // This special case is indicated by 'pos > limit'. + boolean retAtEndOfBuffer = false; + + int avail = limit - pos; + if (count > avail) + { + if (avail > 0) + count = avail; + else // pos >= limit + { + if (limit == buffer.length) + markPos = -1; // read too far - invalidate the mark. + if (pos > limit) + { + // Set a boolean and make pos == limit to simplify things. + retAtEndOfBuffer = true; + --pos; + } + if (markPos < 0) + { + // Optimization: can read directly into buf. + if (count >= buffer.length && !retAtEndOfBuffer) + return in.read(buf, offset, count); + pos = limit = 0; + } + avail = in.read(buffer, limit, buffer.length - limit); + if (retAtEndOfBuffer && avail > 0 && buffer[limit] == '\n') + { + --avail; + limit++; + } + if (avail < count) + { + if (avail <= 0) + return avail; + count = avail; + } + limit += avail; + } + } + System.arraycopy(buffer, pos, buf, offset, count); + pos += count; + return count; + } + } + + /* Read more data into the buffer. Update pos and limit appropriately. + Assumes pos==limit initially. May invalidate the mark if read too much. + Return number of chars read (never 0), or -1 on eof. */ + private int fill() throws IOException + { + checkStatus(); + // Handle the special case of a readLine that has a '\r' at the end of + // the buffer. In this case, we'll need to skip a '\n' if it is the + // next char to be read. This special case is indicated by 'pos > limit'. + boolean retAtEndOfBuffer = false; + if (pos > limit) + { + retAtEndOfBuffer = true; + --pos; + } + + if (markPos >= 0 && limit == buffer.length) + markPos = -1; + if (markPos < 0) + pos = limit = 0; + int count = in.read(buffer, limit, buffer.length - limit); + if (count > 0) + limit += count; + + if (retAtEndOfBuffer && buffer[pos] == '\n') + { + --count; + // If the mark was set to the location of the \n, then we + // must change it to fully pretend that the \n does not + // exist. + if (markPos == pos) + ++markPos; + ++pos; + } + + return count; + } + + public int read() throws IOException + { + synchronized (lock) + { + checkStatus(); + if (pos >= limit && fill () <= 0) + return -1; + return buffer[pos++]; + } + } + + /* Return the end of the line starting at this.pos and ending at limit. + * The index returns is *before* any line terminators, or limit + * if no line terminators were found. + */ + private int lineEnd(int limit) + { + int i = pos; + for (; i < limit; i++) + { + char ch = buffer[i]; + if (ch == '\n' || ch == '\r') + break; + } + return i; + } + + /** + * This method reads a single line of text from the input stream, returning + * it as a <code>String</code>. A line is terminated by "\n", a "\r", or + * an "\r\n" sequence. The system dependent line separator is not used. + * The line termination characters are not returned in the resulting + * <code>String</code>. + * + * @return The line of text read, or <code>null</code> if end of stream. + * + * @exception IOException If an error occurs + */ + public String readLine() throws IOException + { + checkStatus(); + // Handle the special case where a previous readLine (with no intervening + // reads/skips) had a '\r' at the end of the buffer. + // In this case, we'll need to skip a '\n' if it's the next char to be read. + // This special case is indicated by 'pos > limit'. + if (pos > limit) + { + int ch = read(); + if (ch < 0) + return null; + if (ch != '\n') + --pos; + } + int i = lineEnd(limit); + if (i < limit) + { + String str = String.valueOf(buffer, pos, i - pos); + pos = i + 1; + // If the last char in the buffer is a '\r', we must remember + // to check if the next char to be read after the buffer is refilled + // is a '\n'. If so, skip it. To indicate this condition, we set pos + // to be limit + 1, which normally is never possible. + if (buffer[i] == '\r') + if (pos == limit || buffer[pos] == '\n') + pos++; + return str; + } + if (sbuf == null) + sbuf = new StringBuffer(200); + else + sbuf.setLength(0); + sbuf.append(buffer, pos, i - pos); + pos = i; + // We only want to return null when no characters were read before + // EOF. So we must keep track of this separately. Otherwise we + // would treat an empty `sbuf' as an EOF condition, which is wrong + // when there is just a newline. + boolean eof = false; + for (;;) + { + // readLine should block. So we must not return until a -1 is reached. + if (pos >= limit) + { + // here count == 0 isn't sufficient to give a failure. + int count = fill(); + if (count < 0) + { + eof = true; + break; + } + continue; + } + int ch = buffer[pos++]; + if (ch == '\n' || ch == '\r') + { + // Check here if a '\r' was the last char in the buffer; if so, + // mark it as in the comment above to indicate future reads + // should skip a newline that is the next char read after + // refilling the buffer. + if (ch == '\r') + if (pos == limit || buffer[pos] == '\n') + pos++; + break; + } + i = lineEnd(limit); + sbuf.append(buffer, pos - 1, i - (pos - 1)); + pos = i; + } + return (sbuf.length() == 0 && eof) ? null : sbuf.toString(); + } + + /** + * This method skips the specified number of chars in the stream. It + * returns the actual number of chars skipped, which may be less than the + * requested amount. + * <p> + * This method first discards chars in the buffer, then calls the + * <code>skip</code> method on the underlying stream to skip the + * remaining chars. + * + * @param count The requested number of chars to skip + * + * @return The actual number of chars skipped. + * + * @exception IOException If an error occurs. + * @exception IllegalArgumentException If count is negative. + */ + public long skip(long count) throws IOException + { + synchronized (lock) + { + checkStatus(); + if (count < 0) + throw new IllegalArgumentException("skip value is negative"); + if (count == 0) + return 0; + // Yet again, we need to handle the special case of a readLine + // that has a '\r' at the end of the buffer. In this case, we need + // to ignore a '\n' if it is the next char to be read. + // This special case is indicated by 'pos > limit' (i.e. avail < 0). + // To simplify things, if we're dealing with the special case for + // readLine, just read the next char (since the fill method will + // skip the '\n' for us). By doing this, we'll have to back up pos. + // That's easier than trying to keep track of whether we've skipped + // one element or not. + if (pos > limit) + { + if (read() < 0) + return 0; + else + --pos; + } + + int avail = limit - pos; + + if (count < avail) + { + pos += count; + return count; + } + + pos = limit; + long todo = count - avail; + if (todo > buffer.length) + { + markPos = -1; + todo -= in.skip(todo); + } + else + { + while (todo > 0) + { + avail = fill(); + if (avail <= 0) + break; + if (avail > todo) + avail = (int) todo; + pos += avail; + todo -= avail; + } + } + return count - todo; + } + } + + private void checkStatus() throws IOException + { + if (in == null) + throw new IOException("Stream closed"); + } +} diff --git a/libjava/classpath/java/io/BufferedWriter.java b/libjava/classpath/java/io/BufferedWriter.java new file mode 100644 index 00000000000..185a5344062 --- /dev/null +++ b/libjava/classpath/java/io/BufferedWriter.java @@ -0,0 +1,262 @@ +/* BufferedWriter.java -- Buffer output into large blocks before writing + Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 + * "The Java Language Specification", ISBN 0-201-63451-1 + * Status: Complete to version 1.1. + */ + +/** + * This class accumulates chars written in a buffer instead of immediately + * writing the data to the underlying output sink. The chars are instead + * as one large block when the buffer is filled, or when the stream is + * closed or explicitly flushed. This mode operation can provide a more + * efficient mechanism for writing versus doing numerous small unbuffered + * writes. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@cygnus.com) + * @date September 25, 1998 + */ +public class BufferedWriter extends Writer +{ + /** + * This is the default buffer size + */ + private static final int DEFAULT_BUFFER_SIZE = 8192; + + /** + * This is the underlying <code>Writer</code> to which this object + * sends its output. + */ + private Writer out; + + /** + * This is the internal char array used for buffering output before + * writing it. + */ + char[] buffer; + + /** + * This is the number of chars that are currently in the buffer and + * are waiting to be written to the underlying stream. It always points to + * the index into the buffer where the next char of data will be stored + */ + int count; + + /** + * This method initializes a new <code>BufferedWriter</code> instance + * that will write to the specified subordinate <code>Writer</code> + * and which will use a default buffer size of 8192 chars. + * + * @param out The underlying <code>Writer</code> to write data to + */ + public BufferedWriter (Writer out) + { + this (out, DEFAULT_BUFFER_SIZE); + } + + /** + * This method initializes a new <code>BufferedWriter</code> instance + * that will write to the specified subordinate <code>Writer</code> + * and which will use the specified buffer size + * + * @param out The underlying <code>Writer</code> to write data to + * @param size The size of the internal buffer + */ + public BufferedWriter (Writer out, int size) + { + super(out.lock); + this.out = out; + this.buffer = new char[size]; + this.count = 0; + } + + /** + * This method flushes any remaining buffered chars then closes the + * underlying output stream. Any further attempts to write to this stream + * may throw an exception + * + * @exception IOException If an error occurs. + */ + public void close () throws IOException + { + synchronized (lock) + { + // It is safe to call localFlush even if the stream is already + // closed. + localFlush (); + out.close(); + buffer = null; + } + } + + /** + * This method causes any currently buffered chars to be immediately + * written to the underlying output stream. + * + * @exception IOException If an error occurs + */ + public void flush () throws IOException + { + synchronized (lock) + { + if (buffer == null) + throw new IOException ("Stream closed"); + localFlush (); + out.flush(); + } + } + + /** + * This method writes out a system depedent line separator sequence. The + * actual value written is detemined from the <xmp>line.separator</xmp> + * system property. + * + * @exception IOException If an error occurs + */ + public void newLine () throws IOException + { + write (System.getProperty("line.separator")); + } + + /** + * This method writes a single char of data. This will be written to the + * buffer instead of the underlying data source. However, if the buffer + * is filled as a result of this write request, it will be flushed to the + * underlying output stream. + * + * @param oneChar The char of data to be written, passed as an int + * + * @exception IOException If an error occurs + */ + public void write (int oneChar) throws IOException + { + synchronized (lock) + { + if (buffer == null) + throw new IOException ("Stream closed"); + buffer[count++] = (char) oneChar; + if (count == buffer.length) + localFlush (); + } + } + + /** + * This method writes <code>len</code> chars from the char array + * <code>buf</code> starting at position <code>offset</code> in the buffer. + * These chars will be written to the internal buffer. However, if this + * write operation fills the buffer, the buffer will be flushed to the + * underlying output stream. + * + * @param buf The array of chars to write. + * @param offset The index into the char array to start writing from. + * @param len The number of chars to write. + * + * @exception IOException If an error occurs + */ + public void write (char[] buf, int offset, int len) throws IOException + { + synchronized (lock) + { + if (buffer == null) + throw new IOException ("Stream closed"); + + // Bypass buffering if there is too much incoming data. + if (count + len > buffer.length) + { + localFlush (); + out.write(buf, offset, len); + } + else + { + System.arraycopy(buf, offset, buffer, count, len); + count += len; + if (count == buffer.length) + localFlush (); + } + } + } + + /** + * This method writes <code>len</code> chars from the <code>String</code> + * <code>str</code> starting at position <code>offset</code> in the string. + * These chars will be written to the internal buffer. However, if this + * write operation fills the buffer, the buffer will be flushed to the + * underlying output stream. + * + * @param str The <code>String</code> to write. + * @param offset The index into the string to start writing from. + * @param len The number of chars to write. + * + * @exception IOException If an error occurs + */ + public void write (String str, int offset, int len) throws IOException + { + synchronized (lock) + { + if (buffer == null) + throw new IOException ("Stream closed"); + + if (count + len > buffer.length) + { + localFlush (); + out.write(str, offset, len); + } + else + { + str.getChars(offset, offset + len, buffer, count); + count += len; + if (count == buffer.length) + localFlush (); + } + } + } + + // This should only be called with the lock held. + private void localFlush () throws IOException + { + if (count > 0) + { + out.write(buffer, 0, count); + count = 0; + } + } +} diff --git a/libjava/classpath/java/io/ByteArrayInputStream.java b/libjava/classpath/java/io/ByteArrayInputStream.java new file mode 100644 index 00000000000..2bbde95b724 --- /dev/null +++ b/libjava/classpath/java/io/ByteArrayInputStream.java @@ -0,0 +1,251 @@ +/* ByteArrayInputStream.java -- Read an array as a stream + Copyright (C) 1998, 1999, 2001, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This class permits an array of bytes to be read as an input stream. + * + * @author Warren Levy (warrenl@cygnus.com) + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class ByteArrayInputStream extends InputStream +{ + /** + * The array that contains the data supplied during read operations + */ + protected byte[] buf; + + /** + * The array index of the next byte to be read from the buffer + * <code>buf</code> + */ + protected int pos; + + /** + * The currently marked position in the stream. This defaults to 0, so a + * reset operation on the stream resets it to read from array index 0 in + * the buffer - even if the stream was initially created with an offset + * greater than 0 + */ + protected int mark; + + /** + * This indicates the maximum number of bytes that can be read from this + * stream. It is the array index of the position after the last valid + * byte in the buffer <code>buf</code> + */ + protected int count; + + /** + * Create a new ByteArrayInputStream that will read bytes from the passed + * in byte array. This stream will read from the beginning to the end + * of the array. It is identical to calling an overloaded constructor + * as <code>ByteArrayInputStream(buf, 0, buf.length)</code>. + * <p> + * Note that this array is not copied. If its contents are changed + * while this stream is being read, those changes will be reflected in the + * bytes supplied to the reader. Please use caution in changing the + * contents of the buffer while this stream is open. + * + * @param buffer The byte array buffer this stream will read from. + */ + public ByteArrayInputStream(byte[] buffer) + { + this(buffer, 0, buffer.length); + } + + /** + * Create a new ByteArrayInputStream that will read bytes from the + * passed in byte array. This stream will read from position + * <code>offset</code> in the array for a length of + * <code>length</code> bytes past <code>offset</code>. If the + * stream is reset to a position before <code>offset</code> then + * more than <code>length</code> bytes can be read from the stream. + * The <code>length</code> value should be viewed as the array index + * one greater than the last position in the buffer to read. + * <p> + * Note that this array is not copied. If its contents are changed + * while this stream is being read, those changes will be reflected in the + * bytes supplied to the reader. Please use caution in changing the + * contents of the buffer while this stream is open. + * + * @param buffer The byte array buffer this stream will read from. + * @param offset The index into the buffer to start reading bytes from + * @param length The number of bytes to read from the buffer + */ + public ByteArrayInputStream(byte[] buffer, int offset, int length) + { + if (offset < 0 || length < 0 || offset > buffer.length) + throw new IllegalArgumentException(); + + buf = buffer; + + count = offset + length; + if (count > buf.length) + count = buf.length; + + pos = offset; + mark = pos; + } + + /** + * This method returns the number of bytes available to be read from this + * stream. The value returned will be equal to <code>count - pos</code>. + * + * @return The number of bytes that can be read from this stream + * before blocking, which is all of them + */ + public synchronized int available() + { + return count - pos; + } + + /** + * This method sets the mark position in this stream to the current + * position. Note that the <code>readlimit</code> parameter in this + * method does nothing as this stream is always capable of + * remembering all the bytes int it. + * <p> + * Note that in this class the mark position is set by default to + * position 0 in the stream. This is in constrast to some other + * stream types where there is no default mark position. + * + * @param readLimit The number of bytes this stream must remember. + * This parameter is ignored. + */ + public synchronized void mark(int readLimit) + { + // readLimit is ignored per Java Class Lib. book, p.220. + mark = pos; + } + + /** + * This method overrides the <code>markSupported</code> method in + * <code>InputStream</code> in order to return <code>true</code> - + * indicating that this stream class supports mark/reset + * functionality. + * + * @return <code>true</code> to indicate that this class supports + * mark/reset. + */ + public boolean markSupported() + { + return true; + } + + /** + * This method reads one byte from the stream. The <code>pos</code> + * counter is advanced to the next byte to be read. The byte read is + * returned as an int in the range of 0-255. If the stream position + * is already at the end of the buffer, no byte is read and a -1 is + * returned in order to indicate the end of the stream. + * + * @return The byte read, or -1 if end of stream + */ + public synchronized int read() + { + if (pos < count) + return ((int) buf[pos++]) & 0xFF; + return -1; + } + + /** + * This method reads bytes from the stream and stores them into a + * caller supplied buffer. It starts storing the data at index + * <code>offset</code> into the buffer and attempts to read + * <code>len</code> bytes. This method can return before reading + * the number of bytes requested if the end of the stream is + * encountered first. The actual number of bytes read is returned. + * If no bytes can be read because the stream is already at the end + * of stream position, a -1 is returned. + * <p> + * This method does not block. + * + * @param buffer The array into which the bytes read should be stored. + * @param offset The offset into the array to start storing bytes + * @param length The requested number of bytes to read + * + * @return The actual number of bytes read, or -1 if end of stream. + */ + public synchronized int read(byte[] buffer, int offset, int length) + { + if (pos >= count) + return -1; + + int numBytes = Math.min(count - pos, length); + System.arraycopy(buf, pos, buffer, offset, numBytes); + pos += numBytes; + return numBytes; + } + + /** + * This method sets the read position in the stream to the mark + * point by setting the <code>pos</code> variable equal to the + * <code>mark</code> variable. Since a mark can be set anywhere in + * the array, the mark/reset methods int this class can be used to + * provide random search capabilities for this type of stream. + */ + public synchronized void reset() + { + pos = mark; + } + + /** + * This method attempts to skip the requested number of bytes in the + * input stream. It does this by advancing the <code>pos</code> + * value by the specified number of bytes. It this would exceed the + * length of the buffer, then only enough bytes are skipped to + * position the stream at the end of the buffer. The actual number + * of bytes skipped is returned. + * + * @param num The requested number of bytes to skip + * + * @return The actual number of bytes skipped. + */ + public synchronized long skip(long num) + { + // Even though the var numBytes is a long, in reality it can never + // be larger than an int since the result of subtracting 2 positive + // ints will always fit in an int. Since we have to return a long + // anyway, numBytes might as well just be a long. + long numBytes = Math.min((long) (count - pos), num < 0 ? 0L : num); + pos += numBytes; + return numBytes; + } +} diff --git a/libjava/classpath/java/io/ByteArrayOutputStream.java b/libjava/classpath/java/io/ByteArrayOutputStream.java new file mode 100644 index 00000000000..e996ebbc70f --- /dev/null +++ b/libjava/classpath/java/io/ByteArrayOutputStream.java @@ -0,0 +1,283 @@ +/* BufferedReader.java + Copyright (C) 1998, 1999, 2000, 2001, 2003, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 + * "The Java Language Specification", ISBN 0-201-63451-1 + * Status: Complete to version 1.1. + */ + +/** + * This class allows data to be written to a byte array buffer and + * and then retrieved by an application. The internal byte array + * buffer is dynamically resized to hold all the data written. Please + * be aware that writing large amounts to data to this stream will + * cause large amounts of memory to be allocated. + * <p> + * The size of the internal buffer defaults to 32 and it is resized + * by doubling the size of the buffer. This default size can be + * overridden by using the + * <code>gnu.java.io.ByteArrayOutputStream.initialBufferSize</code> + * property. + * <p> + * There is a constructor that specified the initial buffer size and + * that is the preferred way to set that value because it it portable + * across all Java class library implementations. + * <p> + * Note that this class also has methods that convert the byte array + * buffer to a <code>String</code> using either the system default or an + * application specified character encoding. Thus it can handle + * multibyte character encodings. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@cygnus.com) + * @date September 24, 1998 + */ +public class ByteArrayOutputStream extends OutputStream +{ + /** + * This method initializes a new <code>ByteArrayOutputStream</code> + * with the default buffer size of 32 bytes. If a different initial + * buffer size is desired, see the constructor + * <code>ByteArrayOutputStream(int size)</code>. For applications + * where the source code is not available, the default buffer size + * can be set using the system property + * <code>gnu.java.io.ByteArrayOutputStream.initialBufferSize</code> + */ + public ByteArrayOutputStream () + { + this (initial_buffer_size); + } + + /** + * This method initializes a new <code>ByteArrayOutputStream</code> with + * a specified initial buffer size. + * + * @param size The initial buffer size in bytes + */ + public ByteArrayOutputStream (int size) + { + buf = new byte[size]; + count = 0; + } + + /** + * This method discards all of the bytes that have been written to + * the internal buffer so far by setting the <code>count</code> + * variable to 0. The internal buffer remains at its currently + * allocated size. + */ + public synchronized void reset () + { + count = 0; + } + + /** + * This method returns the number of bytes that have been written to + * the buffer so far. This is the same as the value of the protected + * <code>count</code> variable. If the <code>reset</code> method is + * called, then this value is reset as well. Note that this method does + * not return the length of the internal buffer, but only the number + * of bytes that have been written to it. + * + * @return The number of bytes in the internal buffer + * + * @see #reset() + */ + public int size () + { + return count; + } + + /** + * This method returns a byte array containing the bytes that have been + * written to this stream so far. This array is a copy of the valid + * bytes in the internal buffer and its length is equal to the number of + * valid bytes, not necessarily to the the length of the current + * internal buffer. Note that since this method allocates a new array, + * it should be used with caution when the internal buffer is very large. + */ + public synchronized byte[] toByteArray () + { + byte[] ret = new byte[count]; + System.arraycopy(buf, 0, ret, 0, count); + return ret; + } + + /** + * Returns the bytes in the internal array as a <code>String</code>. The + * bytes in the buffer are converted to characters using the system default + * encoding. There is an overloaded <code>toString()</code> method that + * allows an application specified character encoding to be used. + * + * @return A <code>String</code> containing the data written to this + * stream so far + */ + public String toString () + { + return new String (buf, 0, count); + } + + /** + * Returns the bytes in the internal array as a <code>String</code>. The + * bytes in the buffer are converted to characters using the specified + * encoding. + * + * @param enc The name of the character encoding to use + * + * @return A <code>String</code> containing the data written to this + * stream so far + * + * @exception UnsupportedEncodingException If the named encoding is + * not available + */ + public String toString (String enc) throws UnsupportedEncodingException + { + return new String (buf, 0, count, enc); + } + + /** + * This method returns the bytes in the internal array as a + * <code>String</code>. It uses each byte in the array as the low + * order eight bits of the Unicode character value and the passed in + * parameter as the high eight bits. + * <p> + * This method does not convert bytes to characters in the proper way and + * so is deprecated in favor of the other overloaded <code>toString</code> + * methods which use a true character encoding. + * + * @param hibyte The high eight bits to use for each character in + * the <code>String</code> + * + * @return A <code>String</code> containing the data written to this + * stream so far + * + * @deprecated + */ + public String toString (int hibyte) + { + return new String (buf, 0, count, hibyte); + } + + // Resize buffer to accommodate new bytes. + private void resize (int add) + { + if (count + add > buf.length) + { + int newlen = buf.length * 2; + if (count + add > newlen) + newlen = count + add; + byte[] newbuf = new byte[newlen]; + System.arraycopy(buf, 0, newbuf, 0, count); + buf = newbuf; + } + } + + /** + * This method writes the writes the specified byte into the internal + * buffer. + * + * @param oneByte The byte to be read passed as an int + */ + public synchronized void write (int oneByte) + { + resize (1); + buf[count++] = (byte) oneByte; + } + + /** + * This method writes <code>len</code> bytes from the passed in array + * <code>buf</code> starting at index <code>offset</code> into the + * internal buffer. + * + * @param buffer The byte array to write data from + * @param offset The index into the buffer to start writing data from + * @param add The number of bytes to write + */ + public synchronized void write (byte[] buffer, int offset, int add) + { + // If ADD < 0 then arraycopy will throw the appropriate error for + // us. + if (add >= 0) + resize (add); + System.arraycopy(buffer, offset, buf, count, add); + count += add; + } + + /** + * This method writes all the bytes that have been written to this stream + * from the internal buffer to the specified <code>OutputStream</code>. + * + * @param out The <code>OutputStream</code> to write to + * + * @exception IOException If an error occurs + */ + public synchronized void writeTo (OutputStream out) throws IOException + { + out.write(buf, 0, count); + } + + /** + * The internal buffer where the data written is stored + */ + protected byte[] buf; + + /** + * The number of bytes that have been written to the buffer + */ + protected int count; + + /** + * The default initial buffer size. Specified by the JCL. + */ + private static final int DEFAULT_INITIAL_BUFFER_SIZE = 32; + + // The default buffer size which can be overridden by the user. + private static final int initial_buffer_size; + + static + { + int r + = Integer.getInteger ("gnu.java.io.ByteArrayOutputStream.initialBufferSize", + DEFAULT_INITIAL_BUFFER_SIZE).intValue (); + if (r <= 0) + r = DEFAULT_INITIAL_BUFFER_SIZE; + initial_buffer_size = r; + } +} diff --git a/libjava/classpath/java/io/CharArrayReader.java b/libjava/classpath/java/io/CharArrayReader.java new file mode 100644 index 00000000000..c14fa077592 --- /dev/null +++ b/libjava/classpath/java/io/CharArrayReader.java @@ -0,0 +1,305 @@ +/* CharArrayReader.java -- Read an array of characters as a stream + Copyright (C) 1998, 2001, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This class permits an array of chars to be read as an input stream. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + */ +public class CharArrayReader extends Reader +{ + /** + * The array that contains the data supplied during read operations + */ + protected char[] buf; + + /** + * The array index of the next char to be read from the buffer + * <code>buf</code> + */ + protected int pos; + + /** + * The currently marked position in the stream. This defaults to 0, so a + * reset operation on the stream resets it to read from array index 0 in + * the buffer - even if the stream was initially created with an offset + * greater than 0 + */ + protected int markedPos; + + /** + * This indicates the maximum number of chars that can be read from this + * stream. It is the array index of the position after the last valid + * char in the buffer <code>buf</code> + */ + protected int count; + + /** + * Create a new CharArrayReader that will read chars from the passed + * in char array. This stream will read from the beginning to the end + * of the array. It is identical to calling an overloaded constructor + * as <code>CharArrayReader(buf, 0, buf.length)</code>. + * <p> + * Note that this array is not copied. If its contents are changed + * while this stream is being read, those changes will be reflected in the + * chars supplied to the reader. Please use caution in changing the + * contents of the buffer while this stream is open. + * + * @param buffer The char array buffer this stream will read from. + */ + public CharArrayReader(char[] buffer) + { + this(buffer, 0, buffer.length); + } + + /** + * Create a new CharArrayReader that will read chars from the passed + * in char array. This stream will read from position + * <code>offset</code> in the array for a length of + * <code>length</code> chars past <code>offset</code>. If the + * stream is reset to a position before <code>offset</code> then + * more than <code>length</code> chars can be read from the stream. + * The <code>length</code> value should be viewed as the array index + * one greater than the last position in the buffer to read. + * <p> + * Note that this array is not copied. If its contents are changed + * while this stream is being read, those changes will be reflected in the + * chars supplied to the reader. Please use caution in changing the + * contents of the buffer while this stream is open. + * + * @param buffer The char array buffer this stream will read from. + * @param offset The index into the buffer to start reading chars from + * @param length The number of chars to read from the buffer + */ + public CharArrayReader(char[] buffer, int offset, int length) + { + super(); + if (offset < 0 || length < 0 || offset > buffer.length) + throw new IllegalArgumentException(); + + buf = buffer; + + count = offset + length; + if (count > buf.length) + count = buf.length; + + pos = offset; + markedPos = pos; + } + + /** + * This method closes the stream. + */ + public void close() + { + synchronized (lock) + { + buf = null; + } + } + + /** + * This method sets the mark position in this stream to the current + * position. Note that the <code>readlimit</code> parameter in this + * method does nothing as this stream is always capable of + * remembering all the chars int it. + * <p> + * Note that in this class the mark position is set by default to + * position 0 in the stream. This is in constrast to some other + * stream types where there is no default mark position. + * + * @param readAheadLimit The number of chars this stream must + * remember. This parameter is ignored. + * + * @exception IOException If an error occurs + */ + public void mark(int readAheadLimit) throws IOException + { + synchronized (lock) + { + if (buf == null) + throw new IOException("Stream closed"); + // readAheadLimit is ignored per Java Class Lib. book, p. 318. + markedPos = pos; + } + } + + /** + * This method overrides the <code>markSupported</code> method in + * <code>Reader</code> in order to return <code>true</code> - + * indicating that this stream class supports mark/reset + * functionality. + * + * @return <code>true</code> to indicate that this class supports + * mark/reset. + */ + public boolean markSupported() + { + return true; + } + + /** + * This method reads one char from the stream. The <code>pos</code> + * counter is advanced to the next char to be read. The char read + * is returned as an int in the range of 0-65535. If the stream + * position is already at the end of the buffer, no char is read and + * a -1 is returned in order to indicate the end of the stream. + * + * @return The char read, or -1 if end of stream + */ + public int read() throws IOException + { + synchronized (lock) + { + if (buf == null) + throw new IOException("Stream closed"); + + if (pos < 0) + throw new ArrayIndexOutOfBoundsException(pos); + + if (pos < count) + return ((int) buf[pos++]) & 0xFFFF; + return -1; + } + } + + /** + * This method reads chars from the stream and stores them into a + * caller supplied buffer. It starts storing the data at index + * <code>offset</code> into the buffer and attempts to read + * <code>len</code> chars. This method can return before reading + * the number of chars requested if the end of the stream is + * encountered first. The actual number of chars read is returned. + * If no chars can be read because the stream is already at the end + * of stream position, a -1 is returned. + * <p> + * This method does not block. + * + * @param b The array into which the chars read should be stored. + * @param off The offset into the array to start storing chars + * @param len The requested number of chars to read + * + * @return The actual number of chars read, or -1 if end of stream. + */ + public int read(char[] b, int off, int len) throws IOException + { + synchronized (lock) + { + if (buf == null) + throw new IOException("Stream closed"); + + /* Don't need to check pos value, arraycopy will check it. */ + if (off < 0 || len < 0 || off + len > b.length) + throw new IndexOutOfBoundsException(); + + if (pos >= count) + return -1; + + int numChars = Math.min(count - pos, len); + System.arraycopy(buf, pos, b, off, numChars); + pos += numChars; + return numChars; + } + } + + /** + * Return true if more characters are available to be read. + * + * @return <code>true</code> to indicate that this stream is ready + * to be read. + * + * @specnote The JDK 1.3 API docs are wrong here. This method will + * return false if there are no more characters available. + */ + public boolean ready() throws IOException + { + if (buf == null) + throw new IOException("Stream closed"); + + return (pos < count); + } + + /** + * This method sets the read position in the stream to the mark + * point by setting the <code>pos</code> variable equal to the + * <code>mark</code> variable. Since a mark can be set anywhere in + * the array, the mark/reset methods int this class can be used to + * provide random search capabilities for this type of stream. + */ + public void reset() throws IOException + { + synchronized (lock) + { + if (buf == null) + throw new IOException("Stream closed"); + + pos = markedPos; + } + } + + /** + * This method attempts to skip the requested number of chars in the + * input stream. It does this by advancing the <code>pos</code> value by the + * specified number of chars. It this would exceed the length of the + * buffer, then only enough chars are skipped to position the stream at + * the end of the buffer. The actual number of chars skipped is returned. + * + * @param n The requested number of chars to skip + * + * @return The actual number of chars skipped. + */ + public long skip(long n) throws IOException + { + synchronized (lock) + { + if (buf == null) + throw new IOException("Stream closed"); + + // Even though the var numChars is a long, in reality it can never + // be larger than an int since the result of subtracting 2 positive + // ints will always fit in an int. Since we have to return a long + // anyway, numChars might as well just be a long. + long numChars = Math.min((long) (count - pos), n < 0 ? 0L : n); + pos += numChars; + return numChars; + } + } +} diff --git a/libjava/classpath/java/io/CharArrayWriter.java b/libjava/classpath/java/io/CharArrayWriter.java new file mode 100644 index 00000000000..f9b338fe0cc --- /dev/null +++ b/libjava/classpath/java/io/CharArrayWriter.java @@ -0,0 +1,274 @@ +/* CharArrayWriter.java -- Write chars to a buffer + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This class allows data to be written to a char array buffer and + * and then retrieved by an application. The internal char array + * buffer is dynamically resized to hold all the data written. Please + * be aware that writing large amounts to data to this stream will + * cause large amounts of memory to be allocated. + * <p> + * The size of the internal buffer defaults to 32 and it is resized + * in increments of 1024 chars. This behavior can be over-ridden by using the + * following two properties: + * <p> + * <ul> + * <li><xmp>gnu.java.io.CharArrayWriter.initialBufferSize</xmp></li> + * <li><xmp>gnu.java.io.CharArrayWriter.bufferIncrementSize</xmp></li> + * </ul> + * <p> + * There is a constructor that specified the initial buffer size and + * that is the preferred way to set that value because it it portable + * across all Java class library implementations. + * <p> + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@cygnus.com) + */ +public class CharArrayWriter extends Writer +{ + /** + * The default initial buffer size + */ + private static final int DEFAULT_INITIAL_BUFFER_SIZE = 32; + + /** + * This method initializes a new <code>CharArrayWriter</code> with + * the default buffer size of 32 chars. If a different initial + * buffer size is desired, see the constructor + * <code>CharArrayWriter(int size)</code>. + */ + public CharArrayWriter () + { + this (DEFAULT_INITIAL_BUFFER_SIZE); + } + + /** + * This method initializes a new <code>CharArrayWriter</code> with + * a specified initial buffer size. + * + * @param size The initial buffer size in chars + */ + public CharArrayWriter (int size) + { + super (); + buf = new char[size]; + } + + /** + * Closes the stream. This method is guaranteed not to free the contents + * of the internal buffer, which can still be retrieved. + */ + public void close () + { + } + + /** + * This method flushes all buffered chars to the stream. + */ + public void flush () + { + } + + /** + * This method discards all of the chars that have been written to the + * internal buffer so far by setting the <code>count</code> variable to + * 0. The internal buffer remains at its currently allocated size. + */ + public void reset () + { + synchronized (lock) + { + count = 0; + } + } + + /** + * This method returns the number of chars that have been written to + * the buffer so far. This is the same as the value of the protected + * <code>count</code> variable. If the <code>reset</code> method is + * called, then this value is reset as well. Note that this method does + * not return the length of the internal buffer, but only the number + * of chars that have been written to it. + * + * @return The number of chars in the internal buffer + * + * @see #reset() + */ + public int size () + { + return count; + } + + /** + * This method returns a char array containing the chars that have been + * written to this stream so far. This array is a copy of the valid + * chars in the internal buffer and its length is equal to the number of + * valid chars, not necessarily to the the length of the current + * internal buffer. Note that since this method allocates a new array, + * it should be used with caution when the internal buffer is very large. + */ + public char[] toCharArray () + { + synchronized (lock) + { + char[] nc = new char[count]; + System.arraycopy(buf, 0, nc, 0, count); + return nc; + } + } + + /** + * Returns the chars in the internal array as a <code>String</code>. The + * chars in the buffer are converted to characters using the system default + * encoding. There is an overloaded <code>toString()</code> method that + * allows an application specified character encoding to be used. + * + * @return A <code>String</code> containing the data written to this + * stream so far + */ + public String toString () + { + synchronized (lock) + { + return new String (buf, 0, count); + } + } + + /** + * This method writes the writes the specified char into the internal + * buffer. + * + * @param oneChar The char to be read passed as an int + */ + public void write (int oneChar) + { + synchronized (lock) + { + resize (1); + buf[count++] = (char) oneChar; + } + } + + /** + * This method writes <code>len</code> chars from the passed in array + * <code>buf</code> starting at index <code>offset</code> into that buffer + * + * @param buffer The char array to write data from + * @param offset The index into the buffer to start writing data from + * @param len The number of chars to write + */ + public void write (char[] buffer, int offset, int len) + { + synchronized (lock) + { + if (len >= 0) + resize (len); + System.arraycopy(buffer, offset, buf, count, len); + count += len; + } + } + + /** + * This method writes <code>len</code> chars from the passed in + * <code>String</code> <code>buf</code> starting at index + * <code>offset</code> into the internal buffer. + * + * @param str The <code>String</code> to write data from + * @param offset The index into the string to start writing data from + * @param len The number of chars to write + */ + public void write (String str, int offset, int len) + { + synchronized (lock) + { + if (len >= 0) + resize (len); + str.getChars(offset, offset + len, buf, count); + count += len; + } + } + + /** + * This method writes all the chars that have been written to this stream + * from the internal buffer to the specified <code>Writer</code>. + * + * @param out The <code>Writer</code> to write to + * + * @exception IOException If an error occurs + */ + public void writeTo (Writer out) throws IOException + { + synchronized (lock) + { + out.write(buf, 0, count); + } + } + + /** + * This private method makes the buffer bigger when we run out of room + * by allocating a larger buffer and copying the valid chars from the + * old array into it. This is obviously slow and should be avoided by + * application programmers by setting their initial buffer size big + * enough to hold everything if possible. + */ + private void resize (int len) + { + if (count + len >= buf.length) + { + int newlen = buf.length * 2; + if (count + len > newlen) + newlen = count + len; + char[] newbuf = new char[newlen]; + System.arraycopy(buf, 0, newbuf, 0, count); + buf = newbuf; + } + } + + /** + * The internal buffer where the data written is stored + */ + protected char[] buf; + + /** + * The number of chars that have been written to the buffer + */ + protected int count; +} diff --git a/libjava/classpath/java/io/CharConversionException.java b/libjava/classpath/java/io/CharConversionException.java new file mode 100644 index 00000000000..a7a608429ef --- /dev/null +++ b/libjava/classpath/java/io/CharConversionException.java @@ -0,0 +1,73 @@ +/* CharConversionException.java -- Character conversion exceptions + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This exception is thrown to indicate that a problem occurred with + * an attempted character conversion. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@cygnus.com) + * @since 1.1 + * @status updated to 1.4 + */ +public class CharConversionException extends IOException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -8680016352018427031L; + + /** + * Create an exception without a descriptive error message. + */ + public CharConversionException() + { + } + + /** + * Create an exception with a descriptive error message. + * + * @param message the descriptive error message + */ + public CharConversionException(String message) + { + super(message); + } +} // class CharConversionException diff --git a/libjava/classpath/java/io/Closeable.java b/libjava/classpath/java/io/Closeable.java new file mode 100644 index 00000000000..b8523d79e63 --- /dev/null +++ b/libjava/classpath/java/io/Closeable.java @@ -0,0 +1,63 @@ +/* Closeable.java -- Closeable object + Copyright (C) 2004, 2005 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.io; + +/** + * A <code>Closeable</code> class represents a stream of + * data, which can be closed when it is no longer needed. + * Closing a stream allows the resources it uses to be + * freed for an alternate use. + * + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public interface Closeable +{ + + /** + * Closes the stream represented by this class, thus freeing + * system resources. In that case that the stream is already + * in the closed state, this method has no effect. + * + * @throws IOException if an I/O error occurs in closing. + */ + void close() + throws IOException; + +} diff --git a/libjava/classpath/java/io/DataInput.java b/libjava/classpath/java/io/DataInput.java new file mode 100644 index 00000000000..45cb0c13025 --- /dev/null +++ b/libjava/classpath/java/io/DataInput.java @@ -0,0 +1,456 @@ +/* DataInput.java -- Interface for reading data from a stream + Copyright (C) 1998, 1999, 2001, 2003, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 + * "The Java Language Specification", ISBN 0-201-63451-1 + * plus online API docs for JDK 1.2 beta from http://www.javasoft.com. + * Status: Believed complete and correct. */ + +/** + * This interface is implemented by classes that can data from streams + * into Java primitive types. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + */ +public interface DataInput +{ + + /** + * This method reads a Java boolean value from an input stream. It does + * so by reading a single byte of data. If that byte is zero, then the + * value returned is <code>false</code>. If the byte is non-zero, then + * the value returned is <code>true</code>. + * <p> + * This method can read a <code>boolean</code> written by an object + * implementing the <code>writeBoolean()</code> method in the + * <code>DataOutput</code> interface. + * + * @return The <code>boolean</code> value read + * + * @exception EOFException If end of file is reached before + * reading the boolean + * @exception IOException If any other error occurs + * + * @see DataOutput#writeBoolean + */ + boolean readBoolean() throws EOFException, IOException; + + /** + * This method reads a Java byte value from an input stream. The value + * is in the range of -128 to 127. + * <p> + * This method can read a <code>byte</code> written by an object + * implementing the + * <code>writeByte()</code> method in the <code>DataOutput</code> interface. + * <p> + * @return The <code>byte</code> value read + * + * @exception EOFException If end of file is reached before reading the byte + * @exception IOException If any other error occurs + * + * @see DataOutput#writeByte + */ + byte readByte() throws EOFException, IOException; + + /** + * This method reads 8 unsigned bits into a Java <code>int</code> value from + * the stream. The value returned is in the range of 0 to 255. + * <p> + * This method can read an unsigned byte written by an object + * implementing the + * <code>writeByte()</code> method in the <code>DataOutput</code> + * interface. + * + * @return The unsigned bytes value read as a Java <code>int</code>. + * + * @exception EOFException If end of file is reached before reading the value + * @exception IOException If any other error occurs + * + * @see DataOutput#writeByte + */ + int readUnsignedByte() throws EOFException, IOException; + + /** + * This method reads a Java <code>char</code> value from an input stream. + * It operates by reading two bytes from the stream and converting them to + * a single 16-bit Java <code>char</code>. The two bytes are stored most + * significant byte first (i.e., "big endian") regardless of the native + * host byte ordering. + * <p> + * As an example, if <code>byte1</code> and <code>byte2</code> represent the + * first and second byte read from the stream respectively, they will be + * transformed to a <code>char</code> in the following manner: + * <p> + * <code>(char)((byte1 << 8) + byte2)</code> + * <p> + * This method can read a <code>char</code> written by an object implementing + * the + * <code>writeChar()</code> method in the <code>DataOutput</code> interface. + * + * @return The <code>char</code> value read + * + * @exception EOFException If end of file is reached before reading the char + * @exception IOException If any other error occurs + * + * @see DataOutput#writeChar + */ + char readChar() throws EOFException, IOException; + + /** + * This method reads a signed 16-bit value into a Java in from the stream. + * It operates by reading two bytes from the stream and converting them to + * a single 16-bit Java <code>short</code>. The two bytes are stored most + * significant byte first (i.e., "big endian") regardless of the native + * host byte ordering. + * <p> + * As an example, if <code>byte1</code> and <code>byte2</code> represent the + * first and second byte read from the stream respectively, they will be + * transformed to a <code>short</code> in the following manner: + * <p> + * <code>(short)(((byte1 & 0xFF) << 8) + (byte2 & 0xFF))</code> + * <p> + * The value returned is in the range of -32768 to 32767. + * <p> + * This method can read a <code>short</code> written by an object + * implementing + * the <code>writeShort()</code> method in the <code>DataOutput</code> + * interface. + * + * @return The <code>short</code> value read + * + * @exception EOFException If end of file is reached before reading the value + * @exception IOException If any other error occurs + * + * @see DataOutput#writeShort + */ + short readShort() throws EOFException, IOException; + + /** + * This method reads 16 unsigned bits into a Java int value from the stream. + * It operates by reading two bytes from the stream and converting them to + * a single Java <code>int</code>. The two bytes are stored most + * significant byte first (i.e., "big endian") regardless of the native + * host byte ordering. + * <p> + * As an example, if <code>byte1</code> and <code>byte2</code> represent the + * first and second byte read from the stream respectively, they will be + * transformed to an <code>int</code> in the following manner: + * <p> + * <code>(int)(((byte1 0xFF) << 8) + (byte2 & 0xFF))</code> + * <p> + * The value returned is in the range of 0 to 65535. + * <p> + * This method can read an unsigned short written by an object implementing + * the <code>writeShort()</code> method in the + * <code>DataOutput</code> + * interface. + * + * @return The unsigned short value read as a Java <code>int</code>. + * + * @exception EOFException If end of file is reached before reading + * the value + * @exception IOException If any other error occurs + * + * @see DataOutput#writeShort + */ + int readUnsignedShort() throws EOFException, IOException; + + /** + * This method reads a Java <code>int</code> value from an input stream + * It operates by reading four bytes from the stream and converting them to + * a single Java <code>int</code>. The bytes are stored most + * significant byte first (i.e., "big endian") regardless of the native + * host byte ordering. + * <p> + * As an example, if <code>byte1</code> through <code>byte4</code> represent + * the first four bytes read from the stream, they will be + * transformed to an <code>int</code> in the following manner: + * <p> + * <code>(int)(((byte1 & 0xFF) << 24) + ((byte2 & 0xFF) << 16) + + * ((byte3 & 0xFF)<< 8) + (byte4 & 0xFF)))</code> + * <p> + * The value returned is in the range of -2147483648 to 2147483647. + * <p> + * This method can read an <code>int</code> written by an object + * implementing the <code>writeInt()</code> method in the + * <code>DataOutput</code> interface. + * + * @return The <code>int</code> value read + * + * @exception EOFException If end of file is reached before reading the int + * @exception IOException If any other error occurs + * + * @see DataOutput#writeInt + */ + int readInt() throws EOFException, IOException; + + /** + * This method reads a Java <code>long</code> value from an input stream + * It operates by reading eight bytes from the stream and converting them to + * a single Java <code>long</code>. The bytes are stored most + * significant byte first (i.e., "big endian") regardless of the native + * host byte ordering. + * <p> + * As an example, if <code>byte1</code> through <code>byte8</code> represent + * the first eight bytes read from the stream, they will be + * transformed to an <code>long</code> in the following manner: + * <p> + * <code>(long)(((byte1 & 0xFF) << 56) + ((byte2 & 0xFF) << 48) + + * ((byte3 & 0xFF) << 40) + ((byte4 & 0xFF) << 32) + + * ((byte5 & 0xFF) << 24) + ((byte6 & 0xFF) << 16) + + * ((byte7 & 0xFF) << 8) + (byte8 & 0xFF))) + * </code> + * <p> + * The value returned is in the range of -9223372036854775808 to + * 9223372036854775807. + * <p> + * This method can read an <code>long</code> written by an object + * implementing the <code>writeLong()</code> method in the + * <code>DataOutput</code> interface. + * + * @return The <code>long</code> value read + * + * @exception EOFException If end of file is reached before reading the long + * @exception IOException If any other error occurs + * + * @see DataOutput#writeLong + */ + long readLong() throws EOFException, IOException; + + /** + * This method reads a Java float value from an input stream. It operates + * by first reading an <code>int</code> value from the stream by calling the + * <code>readInt()</code> method in this interface, then converts that + * <code>int</code> to a <code>float</code> using the + * <code>intBitsToFloat</code> method in the class + * <code>java.lang.Float</code>. + * <p> + * This method can read a <code>float</code> written by an object + * implementing + * the <code>writeFloat()</code> method in the <code>DataOutput</code> + * interface. + * + * @return The <code>float</code> value read + * + * @exception EOFException If end of file is reached before reading the + * float + * @exception IOException If any other error occurs + * + * @see DataOutput#writeFloat + * @see java.lang.Float#intBitsToFloat + */ + float readFloat() throws EOFException, IOException; + + /** + * This method reads a Java double value from an input stream. It operates + * by first reading a <code>long</code> value from the stream by calling the + * <code>readLong()</code> method in this interface, then converts that + * <code>long</code> to a <code>double</code> using the + * <code>longBitsToDouble</code> method in the class + * <code>java.lang.Double</code>. + * <p> + * This method can read a <code>double</code> written by an object + * implementing the <code>writeDouble()</code> method in the + * <code>DataOutput</code> interface. + * + * @return The <code>double</code> value read + * + * @exception EOFException If end of file is reached before reading the + * double + * @exception IOException If any other error occurs + * + * @see DataOutput#writeDouble + * @see java.lang.Double#longBitsToDouble + */ + double readDouble() throws EOFException, IOException; + + /** + * This method reads the next line of text data from an input stream. + * It operates by reading bytes and converting those bytes to + * <code>char</code> + * values by treating the byte read as the low eight bits of the + * <code>char</code> and using 0 as the high eight bits. Because of this, + * it does not support the full 16-bit Unicode character set. + * <P> + * The reading of bytes ends when either the end of file or a line terminator + * is encountered. The bytes read are then returned as a + * <code>String</code>. + * A line terminator is a byte sequence consisting of either + * <code>\r</code>, <code>\n</code> or <code>\r\n</code>. These termination + * charaters are discarded and are not returned as part of the string. + * A line is also terminated by an end of file condition. + * <p> + * + * @return The line read as a <code>String</code> + * + * @exception IOException If an error occurs + */ + String readLine() throws IOException; + + /** + * This method reads a <code>String</code> from an input stream that is + * encoded in a modified UTF-8 format. This format has a leading two byte + * sequence that contains the remaining number of bytes to read. + * This two byte + * sequence is read using the <code>readUnsignedShort()</code> method of this + * interface. + * + * After the number of remaining bytes have been determined, these bytes + * are read an transformed into <code>char</code> values. These + * <code>char</code> values are encoded in the stream using either a one, + * two, or three byte format. + * The particular format in use can be determined by examining the first + * byte read. + * <p> + * If the first byte has a high order bit of 0, then + * that character consists on only one byte. This character value consists + * of seven bits that are at positions 0 through 6 of the byte. As an + * example, if <code>byte1</code> is the byte read from the stream, it would + * be converted to a <code>char</code> like so: + * <p> + * <code>(char)byte1</code> + * <p> + * If the first byte has 110 as its high order bits, then the + * character consists of two bytes. The bits that make up the character + * value are in positions 0 through 4 of the first byte and bit positions + * 0 through 5 of the second byte. (The second byte should have + * 10 as its high order bits). These values are in most significant + * byte first (i.e., "big endian") order. + * <p> + * As an example, if <code>byte1</code> and <code>byte2</code> are the first + * two bytes read respectively, and the high order bits of them match the + * patterns which indicate a two byte character encoding, then they would be + * converted to a Java <code>char</code> like so: + * <p> + * <code>(char)(((byte1 & 0x1F) << 6) + (byte2 & 0x3F))</code> + * <p> + * If the first byte has a 1110 as its high order bits, then the + * character consists of three bytes. The bits that make up the character + * value are in positions 0 through 3 of the first byte and bit positions + * 0 through 5 of the other two bytes. (The second and third bytes should + * have 10 as their high order bits). These values are in most + * significant byte first (i.e., "big endian") order. + * <p> + * As an example, if <code>byte1</code>, <code>byte2</code>, and + * <code>byte3</code> are the three bytes read, and the high order bits of + * them match the patterns which indicate a three byte character encoding, + * then they would be converted to a Java <code>char</code> like so: + * + * <code> + * (char)(((byte1 & 0x0F) << 12) + ((byte2 & 0x3F) + (byte3 & 0x3F)) + * </code> + * + * Note that all characters are encoded in the method that requires the + * fewest number of bytes with the exception of the character with the + * value of <code>\<llll>u0000</code> which is encoded as two bytes. + * This is a modification of the UTF standard used to prevent C language + * style <code>NUL</code> values from appearing in the byte stream. + * <p> + * This method can read data that was written by an object implementing the + * <code>writeUTF()</code> method in <code>DataOutput</code>. + * + * @return The <code>String</code> read + * + * @exception EOFException If end of file is reached before reading the + * String + * @exception UTFDataFormatException If the data is not in UTF-8 format + * @exception IOException If any other error occurs + * + * @see DataOutput#writeUTF + */ + String readUTF() throws EOFException, UTFDataFormatException, IOException; + + /** + * This method reads raw bytes into the passed array until the array is + * full. Note that this method blocks until the data is available and + * throws an exception if there is not enough data left in the stream to + * fill the buffer. Note also that zero length buffers are permitted. + * In this case, the method will return immediately without reading any + * bytes from the stream. + * + * @param buf The buffer into which to read the data + * + * @exception EOFException If end of file is reached before filling the + * buffer + * @exception IOException If any other error occurs + */ + void readFully(byte[] buf) throws EOFException, IOException; + + /** + * This method reads raw bytes into the passed array <code>buf</code> + * starting + * <code>offset</code> bytes into the buffer. The number of bytes read + * will be + * exactly <code>len</code>. Note that this method blocks until the data is + * available and throws an exception if there is not enough data left in + * the stream to read <code>len</code> bytes. Note also that zero length + * buffers are permitted. In this case, the method will return immediately + * without reading any bytes from the stream. + * + * @param buf The buffer into which to read the data + * @param offset The offset into the buffer to start storing data + * @param len The number of bytes to read into the buffer + * + * @exception EOFException If end of file is reached before filling the + * buffer + * @exception IOException If any other error occurs + */ + void readFully(byte[] buf, int offset, int len) + throws EOFException, IOException; + + /** + * This method skips and discards the specified number of bytes in an + * input stream. Note that this method may skip less than the requested + * number of bytes. The actual number of bytes skipped is returned. + * No bytes are skipped if a negative number is passed to this method. + * + * @param numBytes The number of bytes to skip + * + * @return The number of bytes actually skipped, which will always be + * <code>numBytes</code> + * + * @exception EOFException If end of file is reached before all bytes can be + * skipped + * @exception IOException If any other error occurs + */ + int skipBytes(int numBytes) throws EOFException, IOException; + +} // interface DataInput diff --git a/libjava/classpath/java/io/DataInputStream.java b/libjava/classpath/java/io/DataInputStream.java new file mode 100644 index 00000000000..d2604b51ffa --- /dev/null +++ b/libjava/classpath/java/io/DataInputStream.java @@ -0,0 +1,739 @@ +/* DataInputStream.java -- FilteredInputStream that implements DataInput + Copyright (C) 1998, 1999, 2000, 2001, 2003, 2005 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.io; + +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 + * "The Java Language Specification", ISBN 0-201-63451-1 + * plus online API docs for JDK 1.2 beta from http://www.javasoft.com. + * Status: Believed complete and correct. + */ + +/** + * This subclass of <code>FilteredInputStream</code> implements the + * <code>DataInput</code> interface that provides method for reading primitive + * Java data types from a stream. + * + * @see DataInput + * + * @author Warren Levy (warrenl@cygnus.com) + * @author Aaron M. Renn (arenn@urbanophile.com) + * @date October 20, 1998. + */ +public class DataInputStream extends FilterInputStream implements DataInput +{ + // Byte buffer, used to make primitive read calls more efficient. + byte[] buf = new byte [8]; + + /** + * This constructor initializes a new <code>DataInputStream</code> + * to read from the specified subordinate stream. + * + * @param in The subordinate <code>InputStream</code> to read from + */ + public DataInputStream (InputStream in) + { + super (in); + } + + /** + * This method reads bytes from the underlying stream into the specified + * byte array buffer. It will attempt to fill the buffer completely, but + * may return a short count if there is insufficient data remaining to be + * read to fill the buffer. + * + * @param b The buffer into which bytes will be read. + * + * @return The actual number of bytes read, or -1 if end of stream reached + * before reading any bytes. + * + * @exception IOException If an error occurs. + */ + public final int read (byte[] b) throws IOException + { + return in.read (b, 0, b.length); + } + + /** + * This method reads bytes from the underlying stream into the specified + * byte array buffer. It will attempt to read <code>len</code> bytes and + * will start storing them at position <code>off</code> into the buffer. + * This method can return a short count if there is insufficient data + * remaining to be read to complete the desired read length. + * + * @param b The buffer into which bytes will be read. + * @param off The offset into the buffer to start storing bytes. + * @param len The requested number of bytes to read. + * + * @return The actual number of bytes read, or -1 if end of stream reached + * before reading any bytes. + * + * @exception IOException If an error occurs. + */ + public final int read (byte[] b, int off, int len) throws IOException + { + return in.read (b, off, len); + } + + /** + * This method reads a Java boolean value from an input stream. It does + * so by reading a single byte of data. If that byte is zero, then the + * value returned is <code>false</code>. If the byte is non-zero, then + * the value returned is <code>true</code>. + * <p> + * This method can read a <code>boolean</code> written by an object + * implementing the <code>writeBoolean()</code> method in the + * <code>DataOutput</code> interface. + * + * @return The <code>boolean</code> value read + * + * @exception EOFException If end of file is reached before reading + * the boolean + * @exception IOException If any other error occurs + * + * @see DataOutput#writeBoolean + */ + public final boolean readBoolean () throws IOException + { + return convertToBoolean (in.read ()); + } + + /** + * This method reads a Java byte value from an input stream. The value + * is in the range of -128 to 127. + * <p> + * This method can read a <code>byte</code> written by an object + * implementing the <code>writeByte()</code> method in the + * <code>DataOutput</code> interface. + * + * @return The <code>byte</code> value read + * + * @exception EOFException If end of file is reached before reading the byte + * @exception IOException If any other error occurs + * + * @see DataOutput#writeByte + */ + public final byte readByte () throws IOException + { + return convertToByte (in.read ()); + } + + /** + * This method reads a Java <code>char</code> value from an input stream. + * It operates by reading two bytes from the stream and converting them to + * a single 16-bit Java <code>char</code>. The two bytes are stored most + * significant byte first (i.e., "big endian") regardless of the native + * host byte ordering. + * <p> + * As an example, if <code>byte1</code> and <code>byte2</code> + * represent the first and second byte read from the stream + * respectively, they will be transformed to a <code>char</code> in + * the following manner: + * <p> + * <code>(char)(((byte1 & 0xFF) << 8) | (byte2 & 0xFF)</code> + * <p> + * This method can read a <code>char</code> written by an object + * implementing the <code>writeChar()</code> method in the + * <code>DataOutput</code> interface. + * + * @return The <code>char</code> value read + * + * @exception EOFException If end of file is reached before reading the char + * @exception IOException If any other error occurs + * + * @see DataOutput#writeChar + */ + public final char readChar () throws IOException + { + readFully (buf, 0, 2); + return convertToChar (buf); + } + + /** + * This method reads a Java double value from an input stream. It operates + * by first reading a <code>long</code> value from the stream by calling the + * <code>readLong()</code> method in this interface, then converts + * that <code>long</code> to a <code>double</code> using the + * <code>longBitsToDouble</code> method in the class + * <code>java.lang.Double</code> + * <p> + * This method can read a <code>double</code> written by an object + * implementing the <code>writeDouble()</code> method in the + * <code>DataOutput</code> interface. + * + * @return The <code>double</code> value read + * + * @exception EOFException If end of file is reached before reading + * the double + * @exception IOException If any other error occurs + * + * @see DataOutput#writeDouble + * @see java.lang.Double#longBitsToDouble + */ + public final double readDouble () throws IOException + { + return Double.longBitsToDouble (readLong ()); + } + + /** + * This method reads a Java float value from an input stream. It + * operates by first reading an <code>int</code> value from the + * stream by calling the <code>readInt()</code> method in this + * interface, then converts that <code>int</code> to a + * <code>float</code> using the <code>intBitsToFloat</code> method + * in the class <code>java.lang.Float</code> + * <p> + * This method can read a <code>float</code> written by an object + * implementing the <code>writeFloat()</code> method in the + * <code>DataOutput</code> interface. + * + * @return The <code>float</code> value read + * + * @exception EOFException If end of file is reached before reading the float + * @exception IOException If any other error occurs + * + * @see DataOutput#writeFloat + * @see java.lang.Float#intBitsToFloat + */ + public final float readFloat () throws IOException + { + return Float.intBitsToFloat (readInt ()); + } + + /** + * This method reads raw bytes into the passed array until the array is + * full. Note that this method blocks until the data is available and + * throws an exception if there is not enough data left in the stream to + * fill the buffer. Note also that zero length buffers are permitted. + * In this case, the method will return immediately without reading any + * bytes from the stream. + * + * @param b The buffer into which to read the data + * + * @exception EOFException If end of file is reached before filling the + * buffer + * @exception IOException If any other error occurs + */ + public final void readFully (byte[] b) throws IOException + { + readFully (b, 0, b.length); + } + + /** + * This method reads raw bytes into the passed array <code>buf</code> + * starting + * <code>offset</code> bytes into the buffer. The number of bytes read + * will be + * exactly <code>len</code>. Note that this method blocks until the data is + * available and throws an exception if there is not enough data left in + * the stream to read <code>len</code> bytes. Note also that zero length + * buffers are permitted. In this case, the method will return immediately + * without reading any bytes from the stream. + * + * @param buf The buffer into which to read the data + * @param offset The offset into the buffer to start storing data + * @param len The number of bytes to read into the buffer + * + * @exception EOFException If end of file is reached before filling the + * buffer + * @exception IOException If any other error occurs + */ + public final void readFully (byte[] buf, int offset, int len) throws IOException + { + if (len < 0) + throw new IndexOutOfBoundsException("Negative length: " + len); + + while (len > 0) + { + // in.read will block until some data is available. + int numread = in.read (buf, offset, len); + if (numread < 0) + throw new EOFException (); + len -= numread; + offset += numread; + } + } + + /** + * This method reads a Java <code>int</code> value from an input stream + * It operates by reading four bytes from the stream and converting them to + * a single Java <code>int</code>. The bytes are stored most + * significant byte first (i.e., "big endian") regardless of the native + * host byte ordering. + * <p> + * As an example, if <code>byte1</code> through <code>byte4</code> represent + * the first four bytes read from the stream, they will be + * transformed to an <code>int</code> in the following manner: + * <p> + * <code>(int)(((byte1 & 0xFF) << 24) + ((byte2 & 0xFF) << 16) + + * ((byte3 & 0xFF)<< 8) + (byte4 & 0xFF)))</code> + * <p> + * The value returned is in the range of -2147483648 to 2147483647. + * <p> + * This method can read an <code>int</code> written by an object + * implementing the <code>writeInt()</code> method in the + * <code>DataOutput</code> interface. + * + * @return The <code>int</code> value read + * + * @exception EOFException If end of file is reached before reading the int + * @exception IOException If any other error occurs + * + * @see DataOutput#writeInt + */ + public final int readInt () throws IOException + { + readFully (buf, 0, 4); + return convertToInt (buf); + } + + /** + * This method reads the next line of text data from an input + * stream. It operates by reading bytes and converting those bytes + * to <code>char</code> values by treating the byte read as the low + * eight bits of the <code>char</code> and using 0 as the high eight + * bits. Because of this, it does not support the full 16-bit + * Unicode character set. + * <p> + * The reading of bytes ends when either the end of file or a line + * terminator is encountered. The bytes read are then returned as a + * <code>String</code> A line terminator is a byte sequence + * consisting of either <code>\r</code>, <code>\n</code> or + * <code>\r\n</code>. These termination charaters are discarded and + * are not returned as part of the string. + * <p> + * This method can read data that was written by an object implementing the + * <code>writeLine()</code> method in <code>DataOutput</code>. + * + * @return The line read as a <code>String</code> + * + * @exception IOException If an error occurs + * + * @see DataOutput + * + * @deprecated + */ + public final String readLine() throws IOException + { + StringBuffer strb = new StringBuffer(); + + while (true) + { + int c = in.read(); + if (c == -1) // got an EOF + return strb.length() > 0 ? strb.toString() : null; + if (c == '\r') + { + int next_c = in.read(); + if (next_c != '\n' && next_c != -1) + { + if (!(in instanceof PushbackInputStream)) + in = new PushbackInputStream(in); + ((PushbackInputStream) in).unread(next_c); + } + break; + } + if (c == '\n') + break; + strb.append((char) c); + } + + return strb.length() > 0 ? strb.toString() : ""; + } + + /** + * This method reads a Java <code>long</code> value from an input stream + * It operates by reading eight bytes from the stream and converting them to + * a single Java <code>long</code>. The bytes are stored most + * significant byte first (i.e., "big endian") regardless of the native + * host byte ordering. + * <p> + * As an example, if <code>byte1</code> through <code>byte8</code> represent + * the first eight bytes read from the stream, they will be + * transformed to an <code>long</code> in the following manner: + * <p> + * <code>(long)(((byte1 & 0xFF) << 56) + ((byte2 & 0xFF) << 48) + + * ((byte3 & 0xFF) << 40) + ((byte4 & 0xFF) << 32) + + * ((byte5 & 0xFF) << 24) + ((byte6 & 0xFF) << 16) + + * ((byte7 & 0xFF) << 8) + (byte8 & 0xFF))) + * </code> + * <p> + * The value returned is in the range of -9223372036854775808 to + * 9223372036854775807. + * <p> + * This method can read an <code>long</code> written by an object + * implementing the <code>writeLong()</code> method in the + * <code>DataOutput</code> interface. + * + * @return The <code>long</code> value read + * + * @exception EOFException If end of file is reached before reading the long + * @exception IOException If any other error occurs + * + * @see DataOutput#writeLong + */ + public final long readLong () throws IOException + { + readFully (buf, 0, 8); + return convertToLong (buf); + } + + /** + * This method reads a signed 16-bit value into a Java in from the + * stream. It operates by reading two bytes from the stream and + * converting them to a single 16-bit Java <code>short</code>. The + * two bytes are stored most significant byte first (i.e., "big + * endian") regardless of the native host byte ordering. + * <p> + * As an example, if <code>byte1</code> and <code>byte2</code> + * represent the first and second byte read from the stream + * respectively, they will be transformed to a <code>short</code>. in + * the following manner: + * <p> + * <code>(short)(((byte1 & 0xFF) << 8) | (byte2 & 0xFF))</code> + * <p> + * The value returned is in the range of -32768 to 32767. + * <p> + * This method can read a <code>short</code> written by an object + * implementing the <code>writeShort()</code> method in the + * <code>DataOutput</code> interface. + * + * @return The <code>short</code> value read + * + * @exception EOFException If end of file is reached before reading the value + * @exception IOException If any other error occurs + * + * @see DataOutput#writeShort + */ + public final short readShort () throws IOException + { + readFully (buf, 0, 2); + return convertToShort (buf); + } + + /** + * This method reads 8 unsigned bits into a Java <code>int</code> + * value from the stream. The value returned is in the range of 0 to + * 255. + * <p> + * This method can read an unsigned byte written by an object + * implementing the <code>writeUnsignedByte()</code> method in the + * <code>DataOutput</code> interface. + * + * @return The unsigned bytes value read as a Java <code>int</code>. + * + * @exception EOFException If end of file is reached before reading the value + * @exception IOException If any other error occurs + * + * @see DataOutput#writeByte + */ + public final int readUnsignedByte () throws IOException + { + return convertToUnsignedByte (in.read ()); + } + + /** + * This method reads 16 unsigned bits into a Java int value from the stream. + * It operates by reading two bytes from the stream and converting them to + * a single Java <code>int</code> The two bytes are stored most + * significant byte first (i.e., "big endian") regardless of the native + * host byte ordering. + * <p> + * As an example, if <code>byte1</code> and <code>byte2</code> + * represent the first and second byte read from the stream + * respectively, they will be transformed to an <code>int</code> in + * the following manner: + * <p> + * <code>(int)(((byte1 & 0xFF) << 8) + (byte2 & 0xFF))</code> + * <p> + * The value returned is in the range of 0 to 65535. + * <p> + * This method can read an unsigned short written by an object + * implementing the <code>writeUnsignedShort()</code> method in the + * <code>DataOutput</code> interface. + * + * @return The unsigned short value read as a Java <code>int</code> + * + * @exception EOFException If end of file is reached before reading the value + * @exception IOException If any other error occurs + * + * @see DataOutput#writeShort + */ + public final int readUnsignedShort () throws IOException + { + readFully (buf, 0, 2); + return convertToUnsignedShort (buf); + } + + /** + * This method reads a <code>String</code> from an input stream that + * is encoded in a modified UTF-8 format. This format has a leading + * two byte sequence that contains the remaining number of bytes to + * read. This two byte sequence is read using the + * <code>readUnsignedShort()</code> method of this interface. + * <p> + * After the number of remaining bytes have been determined, these + * bytes are read an transformed into <code>char</code> values. + * These <code>char</code> values are encoded in the stream using + * either a one, two, or three byte format. The particular format + * in use can be determined by examining the first byte read. + * <p> + * If the first byte has a high order bit of 0, then that character + * consists on only one byte. This character value consists of + * seven bits that are at positions 0 through 6 of the byte. As an + * example, if <code>byte1</code> is the byte read from the stream, + * it would be converted to a <code>char</code> like so: + * <p> + * <code>(char)byte1</code> + * <p> + * If the first byte has 110 as its high order bits, then the + * character consists of two bytes. The bits that make up the character + * value are in positions 0 through 4 of the first byte and bit positions + * 0 through 5 of the second byte. (The second byte should have + * 10 as its high order bits). These values are in most significant + * byte first (i.e., "big endian") order. + * <p> + * As an example, if <code>byte1</code> and <code>byte2</code> are + * the first two bytes read respectively, and the high order bits of + * them match the patterns which indicate a two byte character + * encoding, then they would be converted to a Java + * <code>char</code> like so: + * <p> + * <code>(char)(((byte1 & 0x1F) << 6) | (byte2 & 0x3F))</code> + * <p> + * If the first byte has a 1110 as its high order bits, then the + * character consists of three bytes. The bits that make up the character + * value are in positions 0 through 3 of the first byte and bit positions + * 0 through 5 of the other two bytes. (The second and third bytes should + * have 10 as their high order bits). These values are in most + * significant byte first (i.e., "big endian") order. + * <p> + * As an example, if <code>byte1</code> <code>byte2</code> and + * <code>byte3</code> are the three bytes read, and the high order + * bits of them match the patterns which indicate a three byte + * character encoding, then they would be converted to a Java + * <code>char</code> like so: + * <p> + * <code>(char)(((byte1 & 0x0F) << 12) | ((byte2 & 0x3F) << 6) | + * (byte3 & 0x3F))</code> + * <p> + * Note that all characters are encoded in the method that requires + * the fewest number of bytes with the exception of the character + * with the value of <code>\u0000</code> which is encoded as two + * bytes. This is a modification of the UTF standard used to + * prevent C language style <code>NUL</code> values from appearing + * in the byte stream. + * <p> + * This method can read data that was written by an object implementing the + * <code>writeUTF()</code> method in <code>DataOutput</code> + * + * @return The <code>String</code> read + * + * @exception EOFException If end of file is reached before reading + * the String + * @exception UTFDataFormatException If the data is not in UTF-8 format + * @exception IOException If any other error occurs + * + * @see DataOutput#writeUTF + */ + public final String readUTF () throws IOException + { + return readUTF (this); + } + + /** + * This method reads a String encoded in UTF-8 format from the + * specified <code>DataInput</code> source. + * + * @param in The <code>DataInput</code> source to read from + * + * @return The String read from the source + * + * @exception IOException If an error occurs + * + * @see DataInput#readUTF + */ + public static final String readUTF(DataInput in) throws IOException + { + final int UTFlen = in.readUnsignedShort (); + byte[] buf = new byte [UTFlen]; + + // This blocks until the entire string is available rather than + // doing partial processing on the bytes that are available and then + // blocking. An advantage of the latter is that Exceptions + // could be thrown earlier. The former is a bit cleaner. + in.readFully (buf, 0, UTFlen); + + return convertFromUTF (buf); + } + + /** + * This method attempts to skip and discard the specified number of bytes + * in the input stream. It may actually skip fewer bytes than requested. + * This method will not skip any bytes if passed a negative number of bytes + * to skip. + * + * @param n The requested number of bytes to skip. + * + * @return The requested number of bytes to skip. + * + * @exception IOException If an error occurs. + * @specnote The JDK docs claim that this returns the number of bytes + * actually skipped. The JCL claims that this method can throw an + * EOFException. Neither of these appear to be true in the JDK 1.3's + * implementation. This tries to implement the actual JDK behaviour. + */ + public final int skipBytes (int n) throws IOException + { + if (n <= 0) + return 0; + try + { + return (int) in.skip (n); + } + catch (EOFException x) + { + // do nothing. + } + return n; + } + + static boolean convertToBoolean (int b) throws EOFException + { + if (b < 0) + throw new EOFException (); + + return (b != 0); + } + + static byte convertToByte (int i) throws EOFException + { + if (i < 0) + throw new EOFException (); + + return (byte) i; + } + + static int convertToUnsignedByte (int i) throws EOFException + { + if (i < 0) + throw new EOFException (); + + return (i & 0xFF); + } + + static char convertToChar (byte[] buf) + { + return (char) ((buf [0] << 8) + | (buf [1] & 0xff)); + } + + static short convertToShort (byte[] buf) + { + return (short) ((buf [0] << 8) + | (buf [1] & 0xff)); + } + + static int convertToUnsignedShort (byte[] buf) + { + return (((buf [0] & 0xff) << 8) + | (buf [1] & 0xff)); + } + + static int convertToInt (byte[] buf) + { + return (((buf [0] & 0xff) << 24) + | ((buf [1] & 0xff) << 16) + | ((buf [2] & 0xff) << 8) + | (buf [3] & 0xff)); + } + + static long convertToLong (byte[] buf) + { + return (((long)(buf [0] & 0xff) << 56) | + ((long)(buf [1] & 0xff) << 48) | + ((long)(buf [2] & 0xff) << 40) | + ((long)(buf [3] & 0xff) << 32) | + ((long)(buf [4] & 0xff) << 24) | + ((long)(buf [5] & 0xff) << 16) | + ((long)(buf [6] & 0xff) << 8) | + ((long)(buf [7] & 0xff))); + } + + // FIXME: This method should be re-thought. I suspect we have multiple + // UTF-8 decoders floating around. We should use the standard charset + // converters, maybe and adding a direct call into one of the new + // NIO converters for a super-fast UTF8 decode. + static String convertFromUTF (byte[] buf) + throws EOFException, UTFDataFormatException + { + // Give StringBuffer an initial estimated size to avoid + // enlarge buffer frequently + StringBuffer strbuf = new StringBuffer (buf.length / 2 + 2); + + for (int i = 0; i < buf.length; ) + { + if ((buf [i] & 0x80) == 0) // bit pattern 0xxxxxxx + strbuf.append ((char) (buf [i++] & 0xFF)); + else if ((buf [i] & 0xE0) == 0xC0) // bit pattern 110xxxxx + { + if (i + 1 >= buf.length + || (buf [i + 1] & 0xC0) != 0x80) + throw new UTFDataFormatException (); + + strbuf.append((char) (((buf [i++] & 0x1F) << 6) + | (buf [i++] & 0x3F))); + } + else if ((buf [i] & 0xF0) == 0xE0) // bit pattern 1110xxxx + { + if (i + 2 >= buf.length + || (buf [i + 1] & 0xC0) != 0x80 + || (buf [i + 2] & 0xC0) != 0x80) + throw new UTFDataFormatException (); + + strbuf.append ((char) (((buf [i++] & 0x0F) << 12) + | ((buf [i++] & 0x3F) << 6) + | (buf [i++] & 0x3F))); + } + else // must be ((buf [i] & 0xF0) == 0xF0 || (buf [i] & 0xC0) == 0x80) + throw new UTFDataFormatException (); // bit patterns 1111xxxx or + // 10xxxxxx + } + + return strbuf.toString (); + } +} diff --git a/libjava/classpath/java/io/DataOutput.java b/libjava/classpath/java/io/DataOutput.java new file mode 100644 index 00000000000..0d436233218 --- /dev/null +++ b/libjava/classpath/java/io/DataOutput.java @@ -0,0 +1,326 @@ +/* DataOutput.java -- Interface for writing data from a stream + Copyright (C) 1998, 1999, 2001, 2003, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 + * "The Java Language Specification", ISBN 0-201-63451-1 + * Status: Complete to version 1.1. + */ + +/** + * This interface is implemented by classes that can wrte data to streams + * from Java primitive types. This data can subsequently be read back + * by classes implementing the <code>DataInput</code> interface. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@cygnus.com) + * + * @see DataInput + */ +public interface DataOutput +{ + /** + * This method writes a Java boolean value to an output stream. If + * <code>value</code> is <code>true</code>, a byte with the value of + * 1 will be written, otherwise a byte with the value of 0 will be + * written. + * + * The value written can be read using the <code>readBoolean</code> + * method in <code>DataInput</code>. + * + * @param value The boolean value to write + * + * @exception IOException If an error occurs + * + * @see DataInput#readBoolean + */ + void writeBoolean(boolean value) throws IOException; + + /** + * This method writes a Java byte value to an output stream. The + * byte to be written will be in the lowest 8 bits of the + * <code>int</code> value passed. + * + * The value written can be read using the <code>readByte</code> or + * <code>readUnsignedByte</code> methods in <code>DataInput</code>. + * + * @param value The int value to write + * + * @exception IOException If an error occurs + * + * @see DataInput#readByte + * @see DataInput#readUnsignedByte + */ + void writeByte(int value) throws IOException; + + /** + * This method writes a Java char value to an output stream. The + * char to be written will be in the lowest 16 bits of the <code>int</code> + * value passed. These bytes will be written "big endian". That is, + * with the high byte written first in the following manner: + * <p> + * <code>byte0 = (byte)((value & 0xFF00) >> 8);<br> + * byte1 = (byte)(value & 0x00FF);</code> + * <p> + * + * The value written can be read using the <code>readChar</code> + * method in <code>DataInput</code>. + * + * @param value The char value to write + * + * @exception IOException If an error occurs + * + * @see DataInput#readChar + */ + void writeChar(int value) throws IOException; + + /** + * This method writes a Java short value to an output stream. The + * char to be written will be in the lowest 16 bits of the <code>int</code> + * value passed. These bytes will be written "big endian". That is, + * with the high byte written first in the following manner: + * <p> + * <code>byte0 = (byte)((value & 0xFF00) >> 8);<br> + * byte1 = (byte)(value & 0x00FF);</code> + * <p> + * + * The value written can be read using the <code>readShort</code> and + * <code>readUnsignedShort</code> methods in <code>DataInput</code>. + * + * @param value The int value to write as a 16-bit value + * + * @exception IOException If an error occurs + * + * @see DataInput#readShort + * @see DataInput#readUnsignedShort + */ + void writeShort(int value) throws IOException; + + /** + * This method writes a Java int value to an output stream. The 4 bytes + * of the passed value will be written "big endian". That is, with + * the high byte written first in the following manner: + * <p> + * <code>byte0 = (byte)((value & 0xFF000000) >> 24);<br> + * byte1 = (byte)((value & 0x00FF0000) >> 16);<br> + * byte2 = (byte)((value & 0x0000FF00) >> 8);<br> + * byte3 = (byte)(value & 0x000000FF);</code> + * <p> + * + * The value written can be read using the <code>readInt</code> + * method in <code>DataInput</code>. + * + * @param value The int value to write + * + * @exception IOException If an error occurs + * + * @see DataInput#readInt + */ + void writeInt(int value) throws IOException; + + /** + * This method writes a Java long value to an output stream. The 8 bytes + * of the passed value will be written "big endian". That is, with + * the high byte written first in the following manner: + * <p> + * <code>byte0 = (byte)((value & 0xFF00000000000000L) >> 56);<br> + * byte1 = (byte)((value & 0x00FF000000000000L) >> 48);<br> + * byte2 = (byte)((value & 0x0000FF0000000000L) >> 40);<br> + * byte3 = (byte)((value & 0x000000FF00000000L) >> 32);<br> + * byte4 = (byte)((value & 0x00000000FF000000L) >> 24);<br> + * byte5 = (byte)((value & 0x0000000000FF0000L) >> 16);<br> + * byte6 = (byte)((value & 0x000000000000FF00L) >> 8);<br> + * byte7 = (byte)(value & 0x00000000000000FFL);</code> + * <p> + * + * The value written can be read using the <code>readLong</code> + * method in <code>DataInput</code>. + * + * @param value The long value to write + * + * @exception IOException If an error occurs + * + * @see DataInput#readLong + */ + void writeLong(long value) throws IOException; + + /** + * This method writes a Java <code>float</code> value to the stream. This + * value is written by first calling the method + * <code>Float.floatToIntBits</code> + * to retrieve an <code>int</code> representing the floating point number, + * then writing this <code>int</code> value to the stream exactly the same + * as the <code>writeInt()</code> method does. + * + * The value written can be read using the <code>readFloat</code> + * method in <code>DataInput</code>. + * + * @param value The float value to write + * + * @exception IOException If an error occurs + * + * @see #writeInt + * @see DataInput#readFloat + * @see Float#floatToIntBits + */ + void writeFloat(float value) throws IOException; + + /** + * This method writes a Java <code>double</code> value to the stream. This + * value is written by first calling the method + * <code>Double.doubleToLongBits</code> + * to retrieve an <code>long</code> representing the floating point number, + * then writing this <code>long</code> value to the stream exactly the same + * as the <code>writeLong()</code> method does. + * + * The value written can be read using the <code>readDouble</code> + * method in <code>DataInput</code>. + * + * @param value The double value to write + * + * @exception IOException If any other error occurs + * + * @see #writeLong + * @see DataInput#readDouble + * @see Double#doubleToLongBits + */ + void writeDouble(double value) throws IOException; + + /** + * This method writes all the bytes in a <code>String</code> out to the + * stream. One byte is written for each character in the + * <code>String</code>. + * The high eight bits of each character are discarded, thus this + * method is inappropriate for completely representing Unicode characters. + * + * @param value The <code>String</code> to write + * + * @exception IOException If an error occurs + */ + void writeBytes(String value) throws IOException; + + /** + * This method writes all the characters of a <code>String</code> to an + * output stream as an array of <code>char</code>'s. Each character + * is written using the method specified in the <code>writeChar</code> + * method. + * + * @param value The String to write + * + * @exception IOException If an error occurs + * + * @see #writeChar(int) + */ + void writeChars(String value) throws IOException; + + /** + * This method writes a Java <code>String</code> to the stream in a modified + * UTF-8 format. First, two bytes are written to the stream indicating the + * number of bytes to follow. This is written in the form of a Java + * <code>short</code> value in the same manner used by the + * <code>writeShort</code> method. Note that this is the number of + * bytes in the + * encoded <code>String</code> not the <code>String</code> length. Next + * come the encoded characters. Each character in the <code>String</code> + * is encoded as either one, two or three bytes. For characters in the + * range of <code>\u0001</code> to <code>\u007F</code>, one byte is used. + * The character + * value goes into bits 0-7 and bit eight is 0. For characters in the range + * of <code>\u0080</code> to <code>\u007FF</code>, two bytes are used. Bits + * 6-10 of the character value are encoded bits 0-4 of the first byte, with + * the high bytes having a value of "110". Bits 0-5 of the character value + * are stored in bits 0-5 of the second byte, with the high bits set to + * "10". This type of encoding is also done for the null character + * <code>\u0000</code>. This eliminates any C style NUL character values + * in the output. All remaining characters are stored as three bytes. + * Bits 12-15 of the character value are stored in bits 0-3 of the first + * byte. The high bits of the first bytes are set to "1110". Bits 6-11 + * of the character value are stored in bits 0-5 of the second byte. The + * high bits of the second byte are set to "10". And bits 0-5 of the + * character value are stored in bits 0-5 of byte three, with the high bits + * of that byte set to "10". + * + * The value written can be read using the <code>readUTF</code> + * method in <code>DataInput</code>. + * + * @param value The <code>String</code> to write + * + * @exception IOException If an error occurs + * + * @see DataInput#readUTF + */ + void writeUTF(String value) throws IOException; + + /** + * This method writes an 8-bit value (passed into the method as a Java + * <code>int</code>) to an output stream. The low 8 bits of the + * passed value are written. + * + * @param value The <code>byte</code> to write to the output stream + * + * @exception IOException If an error occurs + */ + void write(int value) throws IOException; + + /** + * This method writes the raw byte array passed in to the output stream. + * + * @param buf The byte array to write + * + * @exception IOException If an error occurs + */ + void write(byte[] buf) throws IOException; + + /** + * This method writes raw bytes from the passed array <code>buf</code> + * starting + * <code>offset</code> bytes into the buffer. The number of bytes + * written will be exactly <code>len</code>. + * + * @param buf The buffer from which to write the data + * @param offset The offset into the buffer to start writing data from + * @param len The number of bytes to write from the buffer to the output + * stream + * + * @exception IOException If any other error occurs + */ + void write(byte[] buf, int offset, int len) throws IOException; + +} // interface DataOutput + diff --git a/libjava/classpath/java/io/DataOutputStream.java b/libjava/classpath/java/io/DataOutputStream.java new file mode 100644 index 00000000000..39f7ed1ff24 --- /dev/null +++ b/libjava/classpath/java/io/DataOutputStream.java @@ -0,0 +1,455 @@ +/* DataOutputStream.java -- Writes primitive Java datatypes to streams + Copyright (C) 1998, 2001, 2003, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 + * "The Java Language Specification", ISBN 0-201-63451-1 + * Status: Complete to version 1.1. + */ + +/** + * This class provides a mechanism for writing primitive Java datatypes + * to an <code>OutputStream</code> in a portable way. Data written to + * a stream using this class can be read back in using the + * <code>DataInputStream</code> class on any platform. + * + * @see DataInputStream + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@cygnus.com) + */ +public class DataOutputStream extends FilterOutputStream implements DataOutput +{ + /** + * This is the total number of bytes that have been written to the + * stream by this object instance. + */ + protected int written; + + /** + * This method initializes an instance of <code>DataOutputStream</code> to + * write its data to the specified underlying <code>OutputStream</code> + * + * @param out The subordinate <code>OutputStream</code> to which this + * object will write + */ + public DataOutputStream (OutputStream out) + { + super (out); + written = 0; + } + + /** + * This method flushes any unwritten bytes to the underlying stream. + * + * @exception IOException If an error occurs. + */ + public void flush () throws IOException + { + out.flush(); + } + + /** + * This method returns the total number of bytes that have been written to + * the underlying output stream so far. This is the value of the + * <code>written</code> instance variable + * + * @return The number of bytes written to the stream. + */ + public final int size () + { + return written; + } + + /** + * This method writes the specified byte (passed as an <code>int</code>) + * to the underlying output stream. + * + * @param value The <code>byte</code> to write, passed as an <code>int</code>. + * + * @exception IOException If an error occurs. + */ + public synchronized void write (int value) throws IOException + { + out.write (value); + ++written; + } + + /** + * This method writes <code>len</code> bytes from the specified byte array + * <code>buf</code> starting at position <code>offset</code> into the + * buffer to the underlying output stream. + * + * @param buf The byte array to write from. + * @param offset The index into the byte array to start writing from. + * @param len The number of bytes to write. + * + * @exception IOException If an error occurs. + */ + public synchronized void write (byte[] buf, int offset, int len) + throws IOException + { + out.write(buf, offset, len); + written += len; + } + + /** + * This method writes a Java boolean value to an output stream. If + * <code>value</code> is <code>true</code>, a byte with the value of + * 1 will be written, otherwise a byte with the value of 0 will be + * written. + * + * The value written can be read using the <code>readBoolean</code> + * method in <code>DataInput</code>. + * + * @param value The <code>boolean</code> value to write to the stream + * + * @exception IOException If an error occurs + * + * @see DataInput#readBoolean + */ + public final void writeBoolean (boolean value) throws IOException + { + write (value ? 1 : 0); + } + + /** + * This method writes a Java byte value to an output stream. The + * byte to be written will be in the lowest 8 bits of the + * <code>int</code> value passed. + * + * The value written can be read using the <code>readByte</code> or + * <code>readUnsignedByte</code> methods in <code>DataInput</code>. + * + * @param value The <code>byte</code> to write to the stream, passed as + * the low eight bits of an <code>int</code>. + * + * @exception IOException If an error occurs + * + * @see DataInput#readByte + * @see DataInput#readUnsignedByte + */ + public final void writeByte (int value) throws IOException + { + write (value & 0xff); + } + + /** + * This method writes a Java short value to an output stream. The + * char to be written will be in the lowest 16 bits of the <code>int</code> + * value passed. These bytes will be written "big endian". That is, + * with the high byte written first in the following manner: + * <p> + * <code>byte0 = (byte)((value & 0xFF00) >> 8);<br> + * byte1 = (byte)(value & 0x00FF);</code> + * <p> + * + * The value written can be read using the <code>readShort</code> and + * <code>readUnsignedShort</code> methods in <code>DataInput</code>. + * + * @param value The <code>short</code> value to write to the stream, + * passed as an <code>int</code>. + * + * @exception IOException If an error occurs + * + * @see DataInput#readShort + * @see DataInput#readUnsignedShort + */ + public final synchronized void writeShort (int value) throws IOException + { + write ((byte) (0xff & (value >> 8))); + write ((byte) (0xff & value)); + } + + /** + * This method writes a Java char value to an output stream. The + * char to be written will be in the lowest 16 bits of the <code>int</code> + * value passed. These bytes will be written "big endian". That is, + * with the high byte written first in the following manner: + * <p> + * <code>byte0 = (byte)((value & 0xFF00) >> 8);<br> + * byte1 = (byte)(value & 0x00FF);</code> + * <p> + * + * The value written can be read using the <code>readChar</code> + * method in <code>DataInput</code>. + * + * @param value The <code>char</code> value to write, + * passed as an <code>int</code>. + * + * @exception IOException If an error occurs + * + * @see DataInput#readChar + */ + public final synchronized void writeChar (int value) throws IOException + { + write ((byte) (0xff & (value >> 8))); + write ((byte) (0xff & value)); + } + + /** + * This method writes a Java int value to an output stream. The 4 bytes + * of the passed value will be written "big endian". That is, with + * the high byte written first in the following manner: + * <p> + * <code>byte0 = (byte)((value & 0xFF000000) >> 24);<br> + * byte1 = (byte)((value & 0x00FF0000) >> 16);<br> + * byte2 = (byte)((value & 0x0000FF00) >> 8);<br> + * byte3 = (byte)(value & 0x000000FF);</code> + * <p> + * + * The value written can be read using the <code>readInt</code> + * method in <code>DataInput</code>. + * + * @param value The <code>int</code> value to write to the stream + * + * @exception IOException If an error occurs + * + * @see DataInput#readInt + */ + public final synchronized void writeInt (int value) throws IOException + { + write ((byte) (0xff & (value >> 24))); + write ((byte) (0xff & (value >> 16))); + write ((byte) (0xff & (value >> 8))); + write ((byte) (0xff & value)); + } + + /** + * This method writes a Java long value to an output stream. The 8 bytes + * of the passed value will be written "big endian". That is, with + * the high byte written first in the following manner: + * <p> + * <code>byte0 = (byte)((value & 0xFF00000000000000L) >> 56);<br> + * byte1 = (byte)((value & 0x00FF000000000000L) >> 48);<br> + * byte2 = (byte)((value & 0x0000FF0000000000L) >> 40);<br> + * byte3 = (byte)((value & 0x000000FF00000000L) >> 32);<br> + * byte4 = (byte)((value & 0x00000000FF000000L) >> 24);<br> + * byte5 = (byte)((value & 0x0000000000FF0000L) >> 16);<br> + * byte6 = (byte)((value & 0x000000000000FF00L) >> 8);<br> + * byte7 = (byte)(value & 0x00000000000000FFL);</code> + * <p> + * + * The value written can be read using the <code>readLong</code> + * method in <code>DataInput</code>. + * + * @param value The <code>long</code> value to write to the stream + * + * @exception IOException If an error occurs + * + * @see DataInput#readLong + */ + public final synchronized void writeLong (long value) throws IOException + { + write ((byte) (0xff & (value >> 56))); + write ((byte) (0xff & (value>> 48))); + write ((byte) (0xff & (value>> 40))); + write ((byte) (0xff & (value>> 32))); + write ((byte) (0xff & (value>> 24))); + write ((byte) (0xff & (value>> 16))); + write ((byte) (0xff & (value>> 8))); + write ((byte) (0xff & value)); + } + + /** + * This method writes a Java <code>float</code> value to the stream. This + * value is written by first calling the method + * <code>Float.floatToIntBits</code> + * to retrieve an <code>int</code> representing the floating point number, + * then writing this <code>int</code> value to the stream exactly the same + * as the <code>writeInt()</code> method does. + * + * The value written can be read using the <code>readFloat</code> + * method in <code>DataInput</code>. + * + * @param value The <code>float</code> value to write to the stream + * + * @exception IOException If an error occurs + * + * @see writeInt + * @see DataInput#readFloat + * @see Float#floatToIntBits + */ + public final void writeFloat (float value) throws IOException + { + writeInt (Float.floatToIntBits (value)); + } + + /** + * This method writes a Java <code>double</code> value to the stream. This + * value is written by first calling the method + * <code>Double.doubleToLongBits</code> + * to retrieve an <code>long</code> representing the floating point number, + * then writing this <code>long</code> value to the stream exactly the same + * as the <code>writeLong()</code> method does. + * + * The value written can be read using the <code>readDouble</code> + * method in <code>DataInput</code>. + * + * @param value The <code>double</code> value to write to the stream + * + * @exception IOException If an error occurs + * + * @see writeLong + * @see DataInput#readDouble + * @see Double#doubleToLongBits + */ + public final void writeDouble (double value) throws IOException + { + writeLong (Double.doubleToLongBits (value)); + } + + /** + * This method writes all the bytes in a <code>String</code> out to the + * stream. One byte is written for each character in the + * <code>String</code>. + * The high eight bits of each character are discarded, thus this + * method is inappropriate for completely representing Unicode characters. + * + * @param value The <code>String</code> to write to the stream + * + * @exception IOException If an error occurs + */ + public final void writeBytes (String value) throws IOException + { + int len = value.length(); + for (int i = 0; i < len; ++i) + writeByte (value.charAt(i)); + } + + /** + * This method writes all the characters of a <code>String</code> to an + * output stream as an array of <code>char</code>'s. Each character + * is written using the method specified in the <code>writeChar</code> + * method. + * + * @param value The <code>String</code> to write to the stream + * + * @exception IOException If an error occurs + * + * @see writeChar + */ + public final void writeChars (String value) throws IOException + { + int len = value.length(); + for (int i = 0; i < len; ++i) + writeChar (value.charAt(i)); + } + + /** + * This method writes a Java <code>String</code> to the stream in a modified + * UTF-8 format. First, two bytes are written to the stream indicating the + * number of bytes to follow. Note that this is the number of bytes in the + * encoded <code>String</code> not the <code>String</code> length. Next + * come the encoded characters. Each character in the <code>String</code> + * is encoded as either one, two or three bytes. For characters in the + * range of <code>\u0001</code> to <\u007F>, one byte is used. The character + * value goes into bits 0-7 and bit eight is 0. For characters in the range + * of <code>\u0080</code> to <code>\u007FF</code>, two bytes are used. Bits + * 6-10 of the character value are encoded bits 0-4 of the first byte, with + * the high bytes having a value of "110". Bits 0-5 of the character value + * are stored in bits 0-5 of the second byte, with the high bits set to + * "10". This type of encoding is also done for the null character + * <code>\u0000</code>. This eliminates any C style NUL character values + * in the output. All remaining characters are stored as three bytes. + * Bits 12-15 of the character value are stored in bits 0-3 of the first + * byte. The high bits of the first bytes are set to "1110". Bits 6-11 + * of the character value are stored in bits 0-5 of the second byte. The + * high bits of the second byte are set to "10". And bits 0-5 of the + * character value are stored in bits 0-5 of byte three, with the high bits + * of that byte set to "10". + * + * The value written can be read using the <code>readUTF</code> + * method in <code>DataInput</code>. + * + * @param value The <code>String</code> to write to the output in UTF format + * + * @exception IOException If an error occurs + * + * @see DataInput#readUTF + */ + public final synchronized void writeUTF(String value) throws IOException + { + int len = value.length(); + int sum = 0; + + for (int i = 0; i < len && sum <= 65535; ++i) + { + char c = value.charAt(i); + if (c >= '\u0001' && c <= '\u007f') + sum += 1; + else if (c == '\u0000' || (c >= '\u0080' && c <= '\u07ff')) + sum += 2; + else + sum += 3; + } + + if (sum > 65535) + throw new UTFDataFormatException (); + + int pos = 0; + byte[] buf = new byte[sum]; + + for (int i = 0; i < len; ++i) + { + char c = value.charAt(i); + if (c >= '\u0001' && c <= '\u007f') + buf[pos++] = (byte) c; + else if (c == '\u0000' || (c >= '\u0080' && c <= '\u07ff')) + { + buf[pos++] = (byte) (0xc0 | (0x1f & (c >> 6))); + buf[pos++] = (byte) (0x80 | (0x3f & c)); + } + else + { + // JSL says the first byte should be or'd with 0xc0, but + // that is a typo. Unicode says 0xe0, and that is what is + // consistent with DataInputStream. + buf[pos++] = (byte) (0xe0 | (0x0f & (c >> 12))); + buf[pos++] = (byte) (0x80 | (0x3f & (c >> 6))); + buf[pos++] = (byte) (0x80 | (0x3f & c)); + } + } + + writeShort (sum); + write(buf, 0, sum); + } + +} // class DataOutputStream + diff --git a/libjava/classpath/java/io/DeleteFileHelper.java b/libjava/classpath/java/io/DeleteFileHelper.java new file mode 100644 index 00000000000..d73628c4973 --- /dev/null +++ b/libjava/classpath/java/io/DeleteFileHelper.java @@ -0,0 +1,109 @@ +/* DeleteFileHelper.java -- Helper class to delete files on VM exit + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.io; + +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.ArrayList; +import java.util.Iterator; + +/** + * @author Guilhem Lavaux (guilhem@kaffe.org) + * @author Jeroen Frijters (jeroen@sumatra.nl) + * @author Michael Koch (konqueror@gmx.de) + */ +final class DeleteFileHelper extends Thread +{ + private static ArrayList filesToDelete; + + static synchronized void add(File file) + { + if (filesToDelete == null) + { + filesToDelete = new ArrayList(); + + AccessController.doPrivileged(new PrivilegedAction() + { + public Object run() + { + try + { + Runtime.getRuntime().addShutdownHook(new DeleteFileHelper()); + } + catch (IllegalStateException e) + { + // Shutdown is already in progress, so we can't + // register ours. + } + + return null; + } + }); + } + + filesToDelete.add(file); + } + + private static synchronized void deleteFiles() + { + Iterator it = filesToDelete.iterator(); + + while (it.hasNext()) + { + try + { + File file = (File) it.next(); + file.delete(); + } + catch (Exception e) + { + // Do nothing here. + } + } + } + + // Package-private to avoid a trampoline constructor. + DeleteFileHelper() + { + } + + public void run() + { + deleteFiles(); + } +} diff --git a/libjava/classpath/java/io/EOFException.java b/libjava/classpath/java/io/EOFException.java new file mode 100644 index 00000000000..cfedb7d9eb0 --- /dev/null +++ b/libjava/classpath/java/io/EOFException.java @@ -0,0 +1,76 @@ +/* EOFException.java -- unexpected end of file exception + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This exception is thrown when the end of the file or stream was + * encountered unexpectedly. This is not the normal way that an EOF + * condition is reported; such as a special value like -1 being returned. + * However, certain types of streams expecting certain data in a certain + * format might reach EOF before reading their expected data pattern and + * thus throw this exception. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@cygnus.com) + * @status updated to 1.4 + */ +public class EOFException extends IOException +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = 6433858223774886977L; + + /** + * Create an exception without a descriptive error message. + */ + public EOFException() + { + } + + /** + * Create an exception with a descriptive error message. + * + * @param message the descriptive error message + */ + public EOFException(String message) + { + super(message); + } +} // class EOFException diff --git a/libjava/classpath/java/io/Externalizable.java b/libjava/classpath/java/io/Externalizable.java new file mode 100644 index 00000000000..113c19ff60f --- /dev/null +++ b/libjava/classpath/java/io/Externalizable.java @@ -0,0 +1,107 @@ +/* Externalizable.java -- Interface for saving and restoring object data + Copyright (C) 1998 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This interface provides a way that classes can completely control how + * the data of their object instances are written and read to and from + * streams. It has two methods which are used to write the data to a stream + * and to read the data from a stream. The read method must read the data + * in exactly the way it was written by the write method. + * <p> + * Note that classes which implement this interface must take into account + * that all superclass data must also be written to the stream as well. + * The class implementing this interface must figure out how to make that + * happen. + * <p> + * This interface can be used to provide object persistence. When an + * object is to be stored externally, the <code>writeExternal</code> method is + * called to save state. When the object is restored, an instance is + * created using the default no-argument constructor and the + * <code>readExternal</code> method is used to restore the state. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public interface Externalizable extends Serializable +{ + /** + * This method restores an object's state by reading in the instance data + * for the object from the passed in stream. Note that this stream is not + * a subclass of <code>InputStream</code>, but rather is a class that + * implements + * the <code>ObjectInput</code> interface. That interface provides a + * mechanism for + * reading in Java data types from a stream. + * <p> + * Note that this method must be compatible with <code>writeExternal</code>. + * It must read back the exact same types that were written by that + * method in the exact order they were written. + * <p> + * If this method needs to read back an object instance, then the class + * for that object must be found and loaded. If that operation fails, + * then this method throws a <code>ClassNotFoundException</code> + * + * @param in An <code>ObjectInput</code> instance for reading in the object + * state + * + * @exception ClassNotFoundException If the class of an object being + * restored cannot be found + * @exception IOException If any other error occurs + */ + void readExternal(ObjectInput in) + throws ClassNotFoundException, IOException; + + /** + * This method is responsible for writing the instance data of an object + * to the passed in stream. Note that this stream is not a subclass of + * <code>OutputStream</code>, but rather is a class that implements the + * <code>ObjectOutput</code> interface. That interface provides a + * number of methods + * for writing Java data values to a stream. + * <p> + * Not that the implementation of this method must be coordinated with + * the implementation of <code>readExternal</code>. + * + * @param out An <code>ObjectOutput</code> instance for writing the + * object state + * + * @exception IOException If an error occurs + */ + void writeExternal(ObjectOutput out) throws IOException; +} diff --git a/libjava/classpath/java/io/File.java b/libjava/classpath/java/io/File.java new file mode 100644 index 00000000000..3b747e6bd03 --- /dev/null +++ b/libjava/classpath/java/io/File.java @@ -0,0 +1,1357 @@ +/* File.java -- Class representing a file on disk + Copyright (C) 1998, 1999, 2000, 2001, 2003, 2004, 2005 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +import gnu.classpath.SystemProperties; + +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; + +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 + * "The Java Language Specification", ISBN 0-201-63451-1 + * Status: Complete to version 1.3. + */ + +/** + * This class represents a file or directory on a local disk. It provides + * facilities for dealing with a variety of systems that use various + * types of path separators ("/" versus "\", for example). It also + * contains method useful for creating and deleting files and directories. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@cygnus.com) + */ +public class File implements Serializable, Comparable +{ + private static final long serialVersionUID = 301077366599181567L; + + /** + * This is the path separator string for the current host. This field + * contains the value of the <code>file.separator</code> system property. + * An example separator string would be "/" on the GNU system. + */ + public static final String separator = SystemProperties.getProperty("file.separator"); + private static final String dupSeparator = separator + separator; + + /** + * This is the first character of the file separator string. On many + * hosts (for example, on the GNU system), this represents the entire + * separator string. The complete separator string is obtained from the + * <code>file.separator</code>system property. + */ + public static final char separatorChar = separator.charAt(0); + + /** + * This is the string that is used to separate the host name from the + * path name in paths than include the host name. It is the value of + * the <code>path.separator</code> system property. + */ + public static final String pathSeparator + = SystemProperties.getProperty("path.separator"); + + /** + * This is the first character of the string used to separate the host name + * from the path name in paths that include a host. The separator string + * is taken from the <code>path.separator</code> system property. + */ + public static final char pathSeparatorChar = pathSeparator.charAt(0); + + /** + * This is the path to the file set when the object is created. It + * may be an absolute or relative path name. + */ + private String path; + + /** + * This method tests whether or not the current thread is allowed to + * to read the file pointed to by this object. This will be true if and + * and only if 1) the file exists and 2) the <code>SecurityManager</code> + * (if any) allows access to the file via it's <code>checkRead</code> + * method 3) the file is readable. + * + * @return <code>true</code> if reading is allowed, + * <code>false</code> otherwise + * + * @exception SecurityException If the <code>SecurityManager</code> + * does not allow access to the file + */ + public boolean canRead() + { + // Test for existence. This also does the SecurityManager check + if (!exists()) + return false; + + return VMFile.canRead(path); + } + + /** + * This method test whether or not the current thread is allowed to + * write to this object. This will be true if and only if 1) The + * <code>SecurityManager</code> (if any) allows write access to the + * file and 2) The file exists and 3) The file is writable. To determine + * whether or not a non-existent file can be created, check the parent + * directory for write access. + * + * @return <code>true</code> if writing is allowed, <code>false</code> + * otherwise + * + * @exception SecurityException If the <code>SecurityManager</code> + * does not allow access to the file + */ + public boolean canWrite() + { + // First do a SecurityCheck before doing anything else. + checkWrite(); + + // Test for existence. This is required by the spec + if (! VMFile.exists(path)) + return false; + + if (VMFile.isDirectory(path)) + return VMFile.canWriteDirectory(this); + else + return VMFile.canWrite(path); + } + + /** + * This method creates a new file of zero length with the same name as + * the path of this <code>File</code> object if an only if that file + * does not already exist. + * <p> + * A <code>SecurityManager.checkWrite</code> check is done prior + * to performing this action. + * + * @return <code>true</code> if the file was created, <code>false</code> if + * the file alread existed. + * + * @exception IOException If an I/O error occurs + * @exception SecurityException If the <code>SecurityManager</code> will + * not allow this operation to be performed. + * + * @since 1.2 + */ + public boolean createNewFile() throws IOException + { + checkWrite(); + return VMFile.create(path); + } + /** + * This method deletes the file represented by this object. If this file + * is a directory, it must be empty in order for the delete to succeed. + * + * @return <code>true</code> if the file was deleted, <code>false</code> + * otherwise + * + * @exception SecurityException If deleting of the file is not allowed + */ + public synchronized boolean delete() + { + SecurityManager s = System.getSecurityManager(); + + if (s != null) + s.checkDelete(path); + + return VMFile.delete(path); + } + + /** + * This method tests two <code>File</code> objects for equality by + * comparing the path of the specified <code>File</code> against the path + * of this object. The two objects are equal if an only if 1) The + * argument is not null 2) The argument is a <code>File</code> object and + * 3) The path of the <code>File</code>argument is equal to the path + * of this object. + * <p> + * The paths of the files are determined by calling the + * <code>getPath()</code> + * method on each object. + * + * @return <code>true</code> if the two objects are equal, + * <code>false</code> otherwise. + */ + public boolean equals(Object obj) + { + if (! (obj instanceof File)) + return false; + + File other = (File) obj; + + if (VMFile.IS_CASE_SENSITIVE) + return path.equals(other.path); + else + return path.equalsIgnoreCase(other.path); + } + + /** + * This method tests whether or not the file represented by the object + * actually exists on the filesystem. + * + * @return <code>true</code> if the file exists, <code>false</code>otherwise. + * + * @exception SecurityException If reading of the file is not permitted + */ + public boolean exists() + { + checkRead(); + return VMFile.exists(path); + } + + /** + * This method initializes a new <code>File</code> object to represent + * a file with the specified path. + * + * @param name The path name of the file + */ + public File(String name) + { + path = normalizePath (name); + } + + // Remove duplicate and redundant separator characters. + private String normalizePath(String p) + { + // On Windows, convert any '/' to '\'. This appears to be the same logic + // that Sun's Win32 Java performs. + if (separatorChar == '\\') + { + p = p.replace ('/', '\\'); + // We have to special case the "\c:" prefix. + if (p.length() > 2 && p.charAt(0) == '\\' && + ((p.charAt(1) >= 'a' && p.charAt(1) <= 'z') || + (p.charAt(1) >= 'A' && p.charAt(1) <= 'Z')) && + p.charAt(2) == ':') + p = p.substring(1); + } + + int dupIndex = p.indexOf(dupSeparator); + int plen = p.length(); + + // Special case: permit Windows UNC path prefix. + if (dupSeparator.equals("\\\\") && dupIndex == 0) + dupIndex = p.indexOf(dupSeparator, 1); + + if (dupIndex == -1) + { + // Ignore trailing separator (though on Windows "a:\", for + // example, is a valid and minimal path). + if (plen > 1 && p.charAt (plen - 1) == separatorChar) + { + if (! (separatorChar == '\\' && plen == 3 && p.charAt (1) == ':')) + return p.substring (0, plen - 1); + } + else + return p; + } + + StringBuffer newpath = new StringBuffer(plen); + int last = 0; + while (dupIndex != -1) + { + newpath.append(p.substring(last, dupIndex)); + // Ignore the duplicate path characters. + while (p.charAt(dupIndex) == separatorChar) + { + dupIndex++; + if (dupIndex == plen) + return newpath.toString(); + } + newpath.append(separatorChar); + last = dupIndex; + dupIndex = p.indexOf(dupSeparator, last); + } + + // Again, ignore possible trailing separator (except special cases + // like "a:\" on Windows). + int end; + if (plen > 1 && p.charAt (plen - 1) == separatorChar) + { + if (separatorChar == '\\' && plen == 3 && p.charAt (1) == ':') + end = plen; + else + end = plen - 1; + } + else + end = plen; + newpath.append(p.substring(last, end)); + + return newpath.toString(); + } + + /** + * This method initializes a new <code>File</code> object to represent + * a file in the specified named directory. The path name to the file + * will be the directory name plus the separator string plus the file + * name. If the directory path name ends in the separator string, another + * separator string will still be appended. + * + * @param dirPath The path to the directory the file resides in + * @param name The name of the file + */ + public File(String dirPath, String name) + { + if (name == null) + throw new NullPointerException(); + if (dirPath != null) + { + if (dirPath.length() > 0) + { + // Try to be smart about the number of separator characters. + if (dirPath.charAt(dirPath.length() - 1) == separatorChar + || name.length() == 0) + path = normalizePath(dirPath + name); + else + path = normalizePath(dirPath + separatorChar + name); + } + else + { + // If dirPath is empty, use a system dependant + // default prefix. + // Note that the leading separators in name have + // to be chopped off, to prevent them forming + // a UNC prefix on Windows. + if (separatorChar == '\\' /* TODO use ON_WINDOWS */) + { + int skip = 0; + while(name.length() > skip + && (name.charAt(skip) == separatorChar + || name.charAt(skip) == '/')) + { + skip++; + } + name = name.substring(skip); + } + path = normalizePath(separatorChar + name); + } + } + else + path = normalizePath(name); + } + + /** + * This method initializes a new <code>File</code> object to represent + * a file in the specified directory. If the <code>directory</code> + * argument is <code>null</code>, the file is assumed to be in the + * current directory as specified by the <code>user.dir</code> system + * property + * + * @param directory The directory this file resides in + * @param name The name of the file + */ + public File(File directory, String name) + { + this (directory == null ? null : directory.path, name); + } + + /** + * This method initializes a new <code>File</code> object to represent + * a file corresponding to the specified <code>file:</code> protocol URI. + * + * @param uri The uri. + */ + public File(URI uri) + { + if (uri == null) + throw new NullPointerException("uri is null"); + + if (!uri.getScheme().equals("file")) + throw new IllegalArgumentException("invalid uri protocol"); + + path = normalizePath(uri.getPath()); + } + + /** + * This method returns the path of this file as an absolute path name. + * If the path name is already absolute, then it is returned. Otherwise + * the value returned is the current directory plus the separatory + * string plus the path of the file. The current directory is determined + * from the <code>user.dir</code> system property. + * + * @return The absolute path of this file + */ + public String getAbsolutePath() + { + if (isAbsolute()) + return path; + else if (separatorChar == '\\' + && path.length() > 0 && path.charAt (0) == '\\') + { + // On Windows, even if the path starts with a '\\' it is not + // really absolute until we prefix the drive specifier from + // the current working directory to it. + return System.getProperty ("user.dir").substring (0, 2) + path; + } + else if (separatorChar == '\\' + && path.length() > 1 && path.charAt (1) == ':' + && ((path.charAt (0) >= 'a' && path.charAt (0) <= 'z') + || (path.charAt (0) >= 'A' && path.charAt (0) <= 'Z'))) + { + // On Windows, a process has a current working directory for + // each drive and a path like "G:foo\bar" would mean the + // absolute path "G:\wombat\foo\bar" if "\wombat" is the + // working directory on the G drive. + String drvDir = null; + try + { + drvDir = new File (path.substring (0, 2)).getCanonicalPath(); + } + catch (IOException e) + { + drvDir = path.substring (0, 2) + "\\"; + } + + // Note: this would return "C:\\." for the path "C:.", if "\" + // is the working folder on the C drive, but this is + // consistent with what Sun's JRE 1.4.1.01 actually returns! + if (path.length() > 2) + return drvDir + '\\' + path.substring (2, path.length()); + else + return drvDir; + } + else + return System.getProperty ("user.dir") + separatorChar + path; + } + + /** + * This method returns a <code>File</code> object representing the + * absolute path of this object. + * + * @return A <code>File</code> with the absolute path of the object. + * + * @since 1.2 + */ + public File getAbsoluteFile() + { + return new File(getAbsolutePath()); + } + + /** + * This method returns a canonical representation of the pathname of + * this file. The actual form of the canonical representation is + * different. On the GNU system, the canonical form differs from the + * absolute form in that all relative file references to "." and ".." + * are resolved and removed. + * <p> + * Note that this method, unlike the other methods which return path + * names, can throw an IOException. This is because native method + * might be required in order to resolve the canonical path + * + * @exception IOException If an error occurs + */ + public String getCanonicalPath() throws IOException + { + // On Windows, getAbsolutePath might end up calling us, so we + // have to special case that call to avoid infinite recursion. + if (separatorChar == '\\' && path.length() == 2 && + ((path.charAt(0) >= 'a' && path.charAt(0) <= 'z') || + (path.charAt(0) >= 'A' && path.charAt(0) <= 'Z')) && + path.charAt(1) == ':') + { + return VMFile.toCanonicalForm(path); + } + // Call getAbsolutePath first to make sure that we do the + // current directory handling, because the native code + // may have a different idea of the current directory. + return VMFile.toCanonicalForm(getAbsolutePath()); + } + + /** + * This method returns a <code>File</code> object representing the + * canonical path of this object. + * + * @return A <code>File</code> instance representing the canonical path of + * this object. + * + * @exception IOException If an error occurs. + * + * @since 1.2 + */ + public File getCanonicalFile() throws IOException + { + return new File(getCanonicalPath()); + } + + /** + * This method returns the name of the file. This is everything in the + * complete path of the file after the last instance of the separator + * string. + * + * @return The file name + */ + public String getName() + { + return VMFile.getName(path); + } + + /** + * This method returns a <code>String</code> the represents this file's + * parent. <code>null</code> is returned if the file has no parent. The + * parent is determined via a simple operation which removes the + * + * @return The parent directory of this file + */ + public String getParent() + { + String prefix = null; + int nameSeqIndex = 0; + + // The "prefix", if present, is the leading "/" on UNIX and + // either the drive specifier (e.g. "C:") or the leading "\\" + // of a UNC network path on Windows. + if (separatorChar == '/' && path.charAt (0) == '/') + { + prefix = "/"; + nameSeqIndex = 1; + } + else if (separatorChar == '\\' && path.length() > 1) + { + if ((path.charAt (0) == '\\' && path.charAt (1) == '\\') + || (((path.charAt (0) >= 'a' && path.charAt (0) <= 'z') + || (path.charAt (0) >= 'A' && path.charAt (0) <= 'Z')) + && path.charAt (1) == ':')) + { + prefix = path.substring (0, 2); + nameSeqIndex = 2; + } + } + + // According to the JDK docs, the returned parent path is the + // portion of the name sequence before the last separator + // character, if found, prefixed by the prefix, otherwise null. + if (nameSeqIndex < path.length()) + { + String nameSeq = path.substring (nameSeqIndex, path.length()); + int last = nameSeq.lastIndexOf (separatorChar); + if (last == -1) + return prefix; + else if (last == (nameSeq.length() - 1)) + // Note: The path would not have a trailing separator + // except for cases like "C:\" on Windows (see + // normalizePath( )), where Sun's JRE 1.4 returns null. + return null; + else if (last == 0) + last++; + + if (prefix != null) + return prefix + nameSeq.substring (0, last); + else + return nameSeq.substring (0, last); + } + else + // Sun's JRE 1.4 returns null if the prefix is the only + // component of the path - so "/" gives null on UNIX and + // "C:", "\\", etc. return null on Windows. + return null; + } + + /** + * This method returns a <code>File</code> object representing the parent + * file of this one. + * + * @return a <code>File</code> for the parent of this object. + * <code>null</code> + * will be returned if this object does not have a parent. + * + * @since 1.2 + */ + public File getParentFile() + { + String parent = getParent(); + return parent != null ? new File(parent) : null; + } + + /** + * Returns the path name that represents this file. May be a relative + * or an absolute path name + * + * @return The pathname of this file + */ + public String getPath() + { + return path; + } + + /** + * This method returns a hash code representing this file. It is the + * hash code of the path of this file (as returned by <code>getPath()</code>) + * exclusived or-ed with the value 1234321. + * + * @return The hash code for this object + */ + public int hashCode() + { + if (VMFile.IS_CASE_SENSITIVE) + return path.hashCode() ^ 1234321; + else + return path.toLowerCase().hashCode() ^ 1234321; + } + + /** + * This method returns true if this object represents an absolute file + * path and false if it does not. The definition of an absolute path varies + * by system. As an example, on GNU systems, a path is absolute if it starts + * with a "/". + * + * @return <code>true</code> if this object represents an absolute + * file name, <code>false</code> otherwise. + */ + public boolean isAbsolute() + { + if (separatorChar == '\\') + return path.startsWith(dupSeparator) || + (path.length() > 2 && + ((path.charAt(0) >= 'a' && path.charAt(0) <= 'z') || + (path.charAt(0) >= 'A' && path.charAt(0) <= 'Z')) && + path.charAt(1) == ':' && + path.charAt(2) == '\\'); + else + return path.startsWith(separator); + } + + /** + * This method tests whether or not the file represented by this object + * is a directory. In order for this method to return <code>true</code>, + * the file represented by this object must exist and be a directory. + * + * @return <code>true</code> if this file is a directory, <code>false</code> + * otherwise + * + * @exception SecurityException If reading of the file is not permitted + */ + public boolean isDirectory() + { + checkRead(); + return VMFile.isDirectory(path); + } + + /** + * This method tests whether or not the file represented by this object + * is a "plain" file. A file is a plain file if and only if it 1) Exists, + * 2) Is not a directory or other type of special file. + * + * @return <code>true</code> if this is a plain file, <code>false</code> + * otherwise + * + * @exception SecurityException If reading of the file is not permitted + */ + public boolean isFile() + { + checkRead(); + return VMFile.isFile(path); + } + + /** + * This method tests whether or not this file represents a "hidden" file. + * On GNU systems, a file is hidden if its name begins with a "." + * character. Files with these names are traditionally not shown with + * directory listing tools. + * + * @return <code>true</code> if the file is hidden, <code>false</code> + * otherwise. + * + * @since 1.2 + */ + public boolean isHidden() + { + return VMFile.isHidden(path); + } + + /** + * This method returns the last modification time of this file. The + * time value returned is an abstract value that should not be interpreted + * as a specified time value. It is only useful for comparing to other + * such time values returned on the same system. In that case, the larger + * value indicates a more recent modification time. + * <p> + * If the file does not exist, then a value of 0 is returned. + * + * @return The last modification time of the file + * + * @exception SecurityException If reading of the file is not permitted + */ + public long lastModified() + { + checkRead(); + return VMFile.lastModified(path); + } + + /** + * This method returns the length of the file represented by this object, + * or 0 if the specified file does not exist. + * + * @return The length of the file + * + * @exception SecurityException If reading of the file is not permitted + */ + public long length() + { + checkRead(); + return VMFile.length(path); + } + + /** + * This method returns a array of <code>String</code>'s representing the + * list of files is then directory represented by this object. If this + * object represents a non-directory file or a non-existent file, then + * <code>null</code> is returned. The list of files will not contain + * any names such as "." or ".." which indicate the current or parent + * directory. Also, the names are not guaranteed to be sorted. + * <p> + * In this form of the <code>list()</code> method, a filter is specified + * that allows the caller to control which files are returned in the + * list. The <code>FilenameFilter</code> specified is called for each + * file returned to determine whether or not that file should be included + * in the list. + * <p> + * A <code>SecurityManager</code> check is made prior to reading the + * directory. If read access to the directory is denied, an exception + * will be thrown. + * + * @param filter An object which will identify files to exclude from + * the directory listing. + * + * @return An array of files in the directory, or <code>null</code> + * if this object does not represent a valid directory. + * + * @exception SecurityException If read access is not allowed to the + * directory by the <code>SecurityManager</code> + */ + public String[] list(FilenameFilter filter) + { + checkRead(); + + if (!exists() || !isDirectory()) + return null; + + // Get the list of files + String files[] = VMFile.list(path); + + // Check if an error occured in listInternal(). + if (files == null) + return null; + + if (filter == null) + return files; + + // Apply the filter + int count = 0; + for (int i = 0; i < files.length; i++) + { + if (filter.accept(this, files[i])) + ++count; + else + files[i] = null; + } + + String[] retfiles = new String[count]; + count = 0; + for (int i = 0; i < files.length; i++) + if (files[i] != null) + retfiles[count++] = files[i]; + + return retfiles; + } + + /** + * This method returns a array of <code>String</code>'s representing the + * list of files is then directory represented by this object. If this + * object represents a non-directory file or a non-existent file, then + * <code>null</code> is returned. The list of files will not contain + * any names such as "." or ".." which indicate the current or parent + * directory. Also, the names are not guaranteed to be sorted. + * <p> + * A <code>SecurityManager</code> check is made prior to reading the + * directory. If read access to the directory is denied, an exception + * will be thrown. + * + * @return An array of files in the directory, or <code>null</code> if + * this object does not represent a valid directory. + * + * @exception SecurityException If read access is not allowed to the + * directory by the <code>SecurityManager</code> + */ + public String[] list() + { + return list(null); + } + + /** + * This method returns an array of <code>File</code> objects representing + * all the files in the directory represented by this object. If this + * object does not represent a directory, <code>null</code> is returned. + * Each of the returned <code>File</code> object is constructed with this + * object as its parent. + * <p> + * A <code>SecurityManager</code> check is made prior to reading the + * directory. If read access to the directory is denied, an exception + * will be thrown. + * + * @return An array of <code>File</code> objects for this directory. + * + * @exception SecurityException If the <code>SecurityManager</code> denies + * access to this directory. + * + * @since 1.2 + */ + public File[] listFiles() + { + return listFiles((FilenameFilter) null); + } + + /** + * This method returns an array of <code>File</code> objects representing + * all the files in the directory represented by this object. If this + * object does not represent a directory, <code>null</code> is returned. + * Each of the returned <code>File</code> object is constructed with this + * object as its parent. + * <p> + * In this form of the <code>listFiles()</code> method, a filter is specified + * that allows the caller to control which files are returned in the + * list. The <code>FilenameFilter</code> specified is called for each + * file returned to determine whether or not that file should be included + * in the list. + * <p> + * A <code>SecurityManager</code> check is made prior to reading the + * directory. If read access to the directory is denied, an exception + * will be thrown. + * + * @return An array of <code>File</code> objects for this directory. + * + * @exception SecurityException If the <code>SecurityManager</code> denies + * access to this directory. + * + * @since 1.2 + */ + public File[] listFiles(FilenameFilter filter) + { + String[] filelist = list(filter); + + if (filelist == null) + return null; + + File[] fobjlist = new File [filelist.length]; + + for (int i = 0; i < filelist.length; i++) + fobjlist [i] = new File(this, filelist [i]); + + return fobjlist; + } + + /** + * This method returns an array of <code>File</code> objects representing + * all the files in the directory represented by this object. If this + * object does not represent a directory, <code>null</code> is returned. + * Each of the returned <code>File</code> object is constructed with this + * object as its parent. + * <p> + * In this form of the <code>listFiles()</code> method, a filter is specified + * that allows the caller to control which files are returned in the + * list. The <code>FileFilter</code> specified is called for each + * file returned to determine whether or not that file should be included + * in the list. + * <p> + * A <code>SecurityManager</code> check is made prior to reading the + * directory. If read access to the directory is denied, an exception + * will be thrown. + * + * @return An array of <code>File</code> objects for this directory. + * + * @exception SecurityException If the <code>SecurityManager</code> denies + * access to this directory. + * + * @since 1.2 + */ + public File[] listFiles(FileFilter filter) + { + File[] fobjlist = listFiles((FilenameFilter) null); + + if (fobjlist == null) + return null; + + if (filter == null) + return fobjlist; + + int count = 0; + for (int i = 0; i < fobjlist.length; i++) + if (filter.accept(fobjlist[i]) == true) + ++count; + + File[] final_list = new File[count]; + count = 0; + for (int i = 0; i < fobjlist.length; i++) + if (filter.accept(fobjlist[i]) == true) + { + final_list[count] = fobjlist[i]; + ++count; + } + + return final_list; + } + + /** + * This method returns a <code>String</code> that is the path name of the + * file as returned by <code>getPath</code>. + * + * @return A <code>String</code> representation of this file + */ + public String toString() + { + return path; + } + + /** + * @return A <code>URI</code> for this object. + */ + public URI toURI() + { + String abspath = getAbsolutePath(); + + if (isDirectory()) + abspath = abspath + separatorChar; + + if (separatorChar == '\\') + abspath = separatorChar + abspath; + + try + { + return new URI("file", null, null, -1, + abspath.replace(separatorChar, '/'), + null, null); + } + catch (URISyntaxException use) + { + // Can't happen. + throw (InternalError) new InternalError("Unconvertible file: " + + this).initCause(use); + } + } + + /** + * This method returns a <code>URL</code> with the <code>file:</code> + * protocol that represents this file. The exact form of this URL is + * system dependent. + * + * @return A <code>URL</code> for this object. + * + * @exception MalformedURLException If the URL cannot be created + * successfully. + */ + public URL toURL() throws MalformedURLException + { + // On Win32, Sun's JDK returns URLs of the form "file:/c:/foo/bar.txt", + // while on UNIX, it returns URLs of the form "file:/foo/bar.txt". + if (separatorChar == '\\') + return new URL ("file:/" + getAbsolutePath().replace ('\\', '/') + + (isDirectory() ? "/" : "")); + else + return new URL ("file:" + getAbsolutePath() + + (isDirectory() ? "/" : "")); + } + + + /** + * This method creates a directory for the path represented by this object. + * + * @return <code>true</code> if the directory was created, + * <code>false</code> otherwise + * + * @exception SecurityException If write access is not allowed to this file + */ + public boolean mkdir() + { + checkWrite(); + return VMFile.mkdir(path); + } + + /** + * This method creates a directory for the path represented by this file. + * It will also create any intervening parent directories if necessary. + * + * @return <code>true</code> if the directory was created, + * <code>false</code> otherwise + * + * @exception SecurityException If write access is not allowed to this file + */ + public boolean mkdirs() + { + String parent = getParent(); + if (parent == null) + { + return mkdir(); + } + + File f = new File(parent); + if (!f.exists()) + { + boolean rc = f.mkdirs(); + if (rc == false) + return false; + } + + return mkdir(); + } + + /** + * This method creates a temporary file in the specified directory. If + * the directory name is null, then this method uses the system temporary + * directory. The files created are guaranteed not to currently exist and + * the same file name will never be used twice in the same virtual + * machine instance. + * The system temporary directory is determined by examinging the + * <code>java.io.tmpdir</code> system property. + * <p> + * The <code>prefix</code> parameter is a sequence of at least three + * characters that are used as the start of the generated filename. The + * <code>suffix</code> parameter is a sequence of characters that is used + * to terminate the file name. This parameter may be <code>null</code> + * and if it is, the suffix defaults to ".tmp". + * <p> + * If a <code>SecurityManager</code> exists, then its <code>checkWrite</code> + * method is used to verify that this operation is permitted. + * + * @param prefix The character prefix to use in generating the path name. + * @param suffix The character suffix to use in generating the path name. + * @param directory The directory to create the file in, or + * <code>null</code> for the default temporary directory + * + * @exception IllegalArgumentException If the patterns is not valid + * @exception SecurityException If there is no permission to perform + * this operation + * @exception IOException If an error occurs + * + * @since 1.2 + */ + public static File createTempFile(String prefix, String suffix, + File directory) + throws IOException + { + // Grab the system temp directory if necessary + if (directory == null) + { + String dirname = System.getProperty("java.io.tmpdir"); + if (dirname == null) + throw new IOException("Cannot determine system temporary directory"); + + directory = new File(dirname); + if (! VMFile.exists(directory.path)) + throw new IOException("System temporary directory " + + directory.getName() + " does not exist."); + if (! VMFile.isDirectory(directory.path)) + throw new IOException("System temporary directory " + + directory.getName() + + " is not really a directory."); + } + + // Check if prefix is at least 3 characters long + if (prefix.length() < 3) + throw new IllegalArgumentException("Prefix too short: " + prefix); + + // Set default value of suffix + if (suffix == null) + suffix = ".tmp"; + + // Now identify a file name and make sure it doesn't exist. + File file; + if (!VMFile.IS_DOS_8_3) + { + do + { + String filename = prefix + System.currentTimeMillis() + suffix; + file = new File(directory, filename); + } + while (VMFile.exists(file.path)); + } + else + { + // make sure prefix is not longer than 7 characters + if (prefix.length() >= 8) + throw new IllegalArgumentException("Prefix too long: " + prefix + "(valid length 3..7)"); + + long mask = 0x000000ffffFFFFL >> (prefix.length() * 4); + do + { + int n = (int) (System.currentTimeMillis() & mask); + String filename = prefix + java.lang.Integer.toHexString(n) + suffix; + file = new File(directory, filename); + } + while (VMFile.exists(file.path)); + } + + // Verify that we are allowed to create this file + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkWrite(file.getAbsolutePath()); + + // Now create the file and return our file object + // XXX - FIXME race condition. + VMFile.create(file.getAbsolutePath()); + return file; + } + + /** + * This method sets the file represented by this object to be read only. + * A read only file or directory cannot be modified. Please note that + * GNU systems allow read only files to be deleted if the directory it + * is contained in is writable. + * + * @return <code>true</code> if the operation succeeded, <code>false</code> + * otherwise. + * + * @exception SecurityException If the <code>SecurityManager</code> does + * not allow this operation. + * + * @since 1.2 + */ + public boolean setReadOnly() + { + // Do a security check before trying to do anything else. + checkWrite(); + + // Test for existence. + if (! VMFile.exists(path)) + return false; + + return VMFile.setReadOnly(path); + } + + /** + * This method returns an array of filesystem roots. Some operating systems + * have volume oriented filesystem. This method provides a mechanism for + * determining which volumes exist. GNU systems use a single hierarchical + * filesystem, so will have only one "/" filesystem root. + * + * @return An array of <code>File</code> objects for each filesystem root + * available. + * + * @since 1.2 + */ + public static File[] listRoots() + { + return VMFile.listRoots(); + } + + /** + * This method creates a temporary file in the system temporary directory. + * The files created are guaranteed not to currently exist and the same file + * name will never be used twice in the same virtual machine instance. The + * system temporary directory is determined by examinging the + * <code>java.io.tmpdir</code> system property. + * <p> + * The <code>prefix</code> parameter is a sequence of at least three + * characters that are used as the start of the generated filename. The + * <code>suffix</code> parameter is a sequence of characters that is used + * to terminate the file name. This parameter may be <code>null</code> + * and if it is, the suffix defaults to ".tmp". + * <p> + * If a <code>SecurityManager</code> exists, then its <code>checkWrite</code> + * method is used to verify that this operation is permitted. + * <p> + * This method is identical to calling + * <code>createTempFile(prefix, suffix, null)</code>. + * + * @param prefix The character prefix to use in generating the path name. + * @param suffix The character suffix to use in generating the path name. + * + * @exception IllegalArgumentException If the prefix or suffix are not valid. + * @exception SecurityException If there is no permission to perform + * this operation + * @exception IOException If an error occurs + */ + public static File createTempFile(String prefix, String suffix) + throws IOException + { + return createTempFile(prefix, suffix, null); + } + + /** + * This method compares the specified <code>File</code> to this one + * to test for equality. It does this by comparing the canonical path names + * of the files. + * <p> + * The canonical paths of the files are determined by calling the + * <code>getCanonicalPath</code> method on each object. + * <p> + * This method returns a 0 if the specified <code>Object</code> is equal + * to this one, a negative value if it is less than this one + * a positive value if it is greater than this one. + * + * @return An integer as described above + * + * @since 1.2 + */ + public int compareTo(File other) + { + if (VMFile.IS_CASE_SENSITIVE) + return path.compareTo (other.path); + else + return path.compareToIgnoreCase (other.path); + } + + /** + * This method compares the specified <code>Object</code> to this one + * to test for equality. It does this by comparing the canonical path names + * of the files. This method is identical to <code>compareTo(File)</code> + * except that if the <code>Object</code> passed to it is not a + * <code>File</code>, it throws a <code>ClassCastException</code> + * <p> + * The canonical paths of the files are determined by calling the + * <code>getCanonicalPath</code> method on each object. + * <p> + * This method returns a 0 if the specified <code>Object</code> is equal + * to this one, a negative value if it is less than this one + * a positive value if it is greater than this one. + * + * @return An integer as described above + * + * @exception ClassCastException If the passed <code>Object</code> is + * not a <code>File</code> + * + * @since 1.2 + */ + public int compareTo(Object obj) + { + return compareTo((File) obj); + } + + /** + * This method renames the file represented by this object to the path + * of the file represented by the argument <code>File</code>. + * + * @param dest The <code>File</code> object representing the target name + * + * @return <code>true</code> if the rename succeeds, <code>false</code> + * otherwise. + * + * @exception SecurityException If write access is not allowed to the + * file by the <code>SecurityMananger</code>. + */ + public synchronized boolean renameTo(File dest) + { + checkWrite(); + dest.checkWrite(); + // Call our native rename method + return VMFile.renameTo(path, dest.path); + } + + /** + * This method sets the modification time on the file to the specified + * value. This is specified as the number of seconds since midnight + * on January 1, 1970 GMT. + * + * @param time The desired modification time. + * + * @return <code>true</code> if the operation succeeded, <code>false</code> + * otherwise. + * + * @exception IllegalArgumentException If the specified time is negative. + * @exception SecurityException If the <code>SecurityManager</code> will + * not allow this operation. + * + * @since 1.2 + */ + public boolean setLastModified(long time) + { + if (time < 0) + throw new IllegalArgumentException("Negative modification time: " + time); + + checkWrite(); + return VMFile.setLastModified(path, time); + } + + private void checkWrite() + { + // Check the SecurityManager + SecurityManager s = System.getSecurityManager(); + + if (s != null) + s.checkWrite(path); + } + + private void checkRead() + { + // Check the SecurityManager + SecurityManager s = System.getSecurityManager(); + + if (s != null) + s.checkRead(path); + } + + /** + * Calling this method requests that the file represented by this object + * be deleted when the virtual machine exits. Note that this request cannot + * be cancelled. Also, it will only be carried out if the virtual machine + * exits normally. + * + * @exception SecurityException If deleting of the file is not allowed + * + * @since 1.2 + */ + public void deleteOnExit() + { + // Check the SecurityManager + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkDelete(path); + + DeleteFileHelper.add(this); + } + + private void writeObject(ObjectOutputStream oos) throws IOException + { + oos.defaultWriteObject(); + oos.writeChar(separatorChar); + } + + private void readObject(ObjectInputStream ois) + throws ClassNotFoundException, IOException + { + ois.defaultReadObject(); + + // If the file was from an OS with a different dir separator, + // fixup the path to use the separator on this OS. + char oldSeparatorChar = ois.readChar(); + + if (oldSeparatorChar != separatorChar) + path = path.replace(oldSeparatorChar, separatorChar); + } + +} // class File + diff --git a/libjava/classpath/java/io/FileDescriptor.java b/libjava/classpath/java/io/FileDescriptor.java new file mode 100644 index 00000000000..d300c9cb617 --- /dev/null +++ b/libjava/classpath/java/io/FileDescriptor.java @@ -0,0 +1,139 @@ +/* FileDescriptor.java -- Opaque file handle class + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +import gnu.java.nio.channels.FileChannelImpl; + +import java.nio.channels.ByteChannel; +import java.nio.channels.FileChannel; + +/** + * This class represents an opaque file handle as a Java class. It should + * be used only to pass to other methods that expect an object of this + * type. No system specific information can be obtained from this object. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@cygnus.com) + * @date September 24, 1998 + */ +public final class FileDescriptor +{ + /** + * A <code>FileDescriptor</code> representing the system standard input + * stream. This will usually be accessed through the + * <code>System.in</code>variable. + */ + public static final FileDescriptor in + = new FileDescriptor (FileChannelImpl.in); + + /** + * A <code>FileDescriptor</code> representing the system standard output + * stream. This will usually be accessed through the + * <code>System.out</code>variable. + */ + public static final FileDescriptor out + = new FileDescriptor (FileChannelImpl.out); + + /** + * A <code>FileDescriptor</code> representing the system standard error + * stream. This will usually be accessed through the + * <code>System.err</code>variable. + */ + public static final FileDescriptor err + = new FileDescriptor (FileChannelImpl.err); + + final ByteChannel channel; + + /** + * This method is used to initialize an invalid FileDescriptor object. + */ + public FileDescriptor() + { + channel = null; + } + + /** + * This method is used to initialize a FileDescriptor object. + */ + FileDescriptor(ByteChannel channel) + { + this.channel = channel; + } + + + /** + * This method forces all data that has not yet been physically written to + * the underlying storage medium associated with this + * <code>FileDescriptor</code> + * to be written out. This method will not return until all data has + * been fully written to the underlying device. If the device does not + * support this functionality or if an error occurs, then an exception + * will be thrown. + */ + public void sync () throws SyncFailedException + { + if (channel instanceof FileChannel) + { + try + { + ((FileChannel) channel).force(true); + } + catch (IOException ex) + { + if (ex instanceof SyncFailedException) + throw (SyncFailedException) ex; + else + throw new SyncFailedException(ex.toString()); + } + } + } + + /** + * This methods tests whether or not this object represents a valid open + * native file handle. + * + * @return <code>true</code> if this object represents a valid + * native file handle, <code>false</code> otherwise + */ + public boolean valid () + { + return channel != null && channel.isOpen(); + } +} diff --git a/libjava/classpath/java/io/FileFilter.java b/libjava/classpath/java/io/FileFilter.java new file mode 100644 index 00000000000..e57ac9fd060 --- /dev/null +++ b/libjava/classpath/java/io/FileFilter.java @@ -0,0 +1,65 @@ +/* FileFilter.java -- Filter a list of pathnames + Copyright (C) 1998,2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This interface has one method which is used for filtering pathnames + * returned in a pathname listing. It is currently used by the + * <code>File.listFiles(FileFilter)</code> method. + * <p> + * The method in this interface determines if a particular pathname should + * or should not be included in the pathname listing. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * + * @see File#listFiles(java.io.FileFilter) + */ +public interface FileFilter +{ + /** + * This method determines whether or not a given pathname should be included + * in a pathname listing. + * + * @param pathname The pathname to test + * + * @return <code>true</code> if the path should be included in the list, + * <code>false</code> otherwise. + */ + boolean accept(File pathname); +} diff --git a/libjava/classpath/java/io/FileInputStream.java b/libjava/classpath/java/io/FileInputStream.java new file mode 100644 index 00000000000..8ca38b02fc4 --- /dev/null +++ b/libjava/classpath/java/io/FileInputStream.java @@ -0,0 +1,309 @@ +/* FileInputStream.java -- An input stream that reads from disk files. + Copyright (C) 1998, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +import gnu.java.nio.channels.FileChannelImpl; + +import java.nio.channels.FileChannel; + +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 + * "The Java Language Specification", ISBN 0-201-63451-1 + * plus online API docs for JDK 1.2 beta from http://www.javasoft.com. + * Status: Believed complete and correct. + */ + +/** + * This class is a stream that reads its bytes from a file. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + */ +public class FileInputStream extends InputStream +{ + /** + * This is the native file handle for the file this stream is reading from + */ + private FileDescriptor fd; + + private FileChannelImpl ch; + + /** + * This method initializes a <code>FileInputStream</code> to read from the + * specified named file. A security check is first made to determine + * whether or not access to this file is allowed. This is done by + * calling the <code>checkRead()</code> method of the + * <code>SecurityManager</code> + * (if one exists) with the name of this file. An exception is thrown + * if reading is not allowed. If the file does not exist, an exception + * is also thrown. + * + * @param name The name of the file this stream should read from + * + * @exception SecurityException If read access to the file is not allowed + * @exception FileNotFoundException If the file does not exist + * or if it is a directory + */ + public FileInputStream(String name) throws FileNotFoundException + { + this(new File(name)); + } + + /** + * This method initializes a <code>FileInputStream</code> to read from the + * specified <code>File</code> object. A security check is first + * made to determine + * whether or not access to this file is allowed. This is done by + * calling the <code>checkRead()</code> method of the + * <code>SecurityManager</code> + * (if one exists) with the name of this file. An exception is thrown + * if reading is not allowed. If the file does not exist, an exception + * is also thrown. + * + * @param file The <code>File</code> object this stream should read from + * + * @exception SecurityException If read access to the file is not allowed + * @exception FileNotFoundException If the file does not exist + * or if it is a directory. + */ + public FileInputStream(File file) throws FileNotFoundException + { + SecurityManager s = System.getSecurityManager(); + if (s != null) + s.checkRead(file.getPath()); + + ch = FileChannelImpl.create(file, FileChannelImpl.READ); + } + + /** + * This method initializes a <code>FileInputStream</code> to read from the + * specified <code>FileDescriptor</code> object. A security + * check is first made to + * determine whether or not access to this file is allowed. This is done by + * calling the <code>checkRead()</code> method of the + * <code>SecurityManager</code> + * (if one exists) with the specified <code>FileDescriptor</code> + * An exception is + * thrown if reading is not allowed. + * + * @param fdObj The <code>FileDescriptor</code> object this stream + * should read from + * + * @exception SecurityException If read access to the file is not allowed + */ + public FileInputStream(FileDescriptor fdObj) + { + SecurityManager s = System.getSecurityManager(); + if (s != null) + s.checkRead(fdObj); + + fd = fdObj; + ch = (FileChannelImpl) fdObj.channel; + } + + FileInputStream(FileChannelImpl ch) + { + this.ch = ch; + } + + /** + * This method returns the number of bytes that can be read from this + * stream before a read can block. A return of 0 indicates that blocking + * might (or might not) occur on the very next read attempt. + * <p> + * This method returns the number of unread bytes remaining in the file if + * the descriptor being read from is an actual file. If this method is + * reading from a ''special'' file such a the standard input, this method + * will return the appropriate value for the stream being read. + * <p> + * Be aware that reads on plain files that do not reside locally might + * possibly block even if this method says they should not. For example, + * a remote server might crash, preventing an NFS mounted file from being + * read. + * + * @return The number of bytes that can be read before blocking could occur + * + * @exception IOException If an error occurs + */ + public int available() throws IOException + { + return ch.available(); + } + + /** + * This method closes the stream. Any futher attempts to read from the + * stream will likely generate an IOException since the underlying file + * will be closed. + * + * @exception IOException If an error occurs. + */ + public void close() throws IOException + { + ch.close(); + } + + protected void finalize() throws IOException + { + // We don't actually need this, but we include it because it is + // mentioned in the JCL. + } + + /** + * This method returns a <code>FileDescriptor</code> object representing the + * underlying native file handle of the file this stream is reading + * from + * + * @return A <code>FileDescriptor</code> for this stream + * + * @exception IOException If an error occurs + */ + public final FileDescriptor getFD() throws IOException + { + synchronized (this) + { + if (fd == null) + fd = new FileDescriptor (ch); + return fd; + } + } + + /** + * This method reads an unsigned byte from the input stream and returns it + * as an int in the range of 0-255. This method also will return -1 if + * the end of the stream has been reached. + * <p> + * This method will block until the byte can be read. + * + * @return The byte read or -1 if end of stream + * + * @exception IOException If an error occurs + */ + public int read() throws IOException + { + return ch.read(); + } + + /** + * This method reads bytes from a stream and stores them into a caller + * supplied buffer. This method attempts to completely fill the buffer, + * but can return before doing so. The actual number of bytes read is + * returned as an int. A -1 is returned to indicate the end of the stream. + * <p> + * This method will block until some data can be read. + * <p> + * This method operates by calling an overloaded read method like so: + * <code>read(buf, 0, buf.length)</code> + * + * @param buf The buffer into which the bytes read will be stored. + * + * @return The number of bytes read or -1 if end of stream. + * + * @exception IOException If an error occurs. + */ + public int read(byte[] buf) throws IOException + { + return read(buf, 0, buf.length); + } + + /** + * This method read bytes from a stream and stores them into a caller + * supplied buffer. It starts storing the data at index + * <code>offset</code> into + * the buffer and attempts to read <code>len</code> bytes. This method can + * return before reading the number of bytes requested. The actual number + * of bytes read is returned as an int. A -1 is returned to indicate the + * end of the stream. + * <p> + * This method will block until some data can be read. + * + * @param buf The array into which the bytes read should be stored + * @param offset The offset into the array to start storing bytes + * @param len The requested number of bytes to read + * + * @return The actual number of bytes read, or -1 if end of stream. + * + * @exception IOException If an error occurs. + */ + public int read(byte[] buf, int offset, int len) throws IOException + { + if (offset < 0 + || len < 0 + || offset + len > buf.length) + throw new ArrayIndexOutOfBoundsException(); + + return ch.read(buf, offset, len); + } + + /** + * This method skips the specified number of bytes in the stream. It + * returns the actual number of bytes skipped, which may be less than the + * requested amount. + * <p> + * @param numBytes The requested number of bytes to skip + * + * @return The actual number of bytes skipped. + * + * @exception IOException If an error occurs + */ + public synchronized long skip (long numBytes) throws IOException + { + if (numBytes < 0) + throw new IllegalArgumentException ("Can't skip negative bytes: " + + numBytes); + + if (numBytes == 0) + return 0; + + long oldPos = ch.position (); + ch.position(oldPos + numBytes); + return ch.position() - oldPos; + } + + /** + * This method creates a java.nio.channels.FileChannel. + * Nio does not allow one to create a file channel directly. + * A file channel must be created by first creating an instance of + * Input/Output/RandomAccessFile and invoking the getChannel() method on it. + */ + public synchronized FileChannel getChannel () + { + return ch; + } + +} // class FileInputStream + diff --git a/libjava/classpath/java/io/FileNotFoundException.java b/libjava/classpath/java/io/FileNotFoundException.java new file mode 100644 index 00000000000..3c11e296072 --- /dev/null +++ b/libjava/classpath/java/io/FileNotFoundException.java @@ -0,0 +1,73 @@ +/* FileNotFoundException.java -- the requested file could not be found + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This exception is thrown when an attempt is made to access a file that + * does not exist, or is inaccessible for some other reason (such as writing + * a read-only file). + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@cygnus.com) + * @status updated to 1.4 + */ +public class FileNotFoundException extends IOException +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -897856973823710492L; + + /** + * Create an exception without a descriptive error message. + */ + public FileNotFoundException() + { + } + + /** + * Create an exception with a descriptive error message. + * + * @param message the descriptive error message + */ + public FileNotFoundException(String message) + { + super(message); + } +} // class FileNotFoundException diff --git a/libjava/classpath/java/io/FileOutputStream.java b/libjava/classpath/java/io/FileOutputStream.java new file mode 100644 index 00000000000..10ea6b536cb --- /dev/null +++ b/libjava/classpath/java/io/FileOutputStream.java @@ -0,0 +1,296 @@ +/* FileOutputStream.java -- Writes to a file on disk. + Copyright (C) 1998, 2001, 2003, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +import gnu.java.nio.channels.FileChannelImpl; + +import java.nio.channels.FileChannel; + +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 + * "The Java Language Specification", ISBN 0-201-63451-1 + * Status: Complete to version 1.1. + */ + +/** + * This classes allows a stream of data to be written to a disk file or + * any open <code>FileDescriptor</code>. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@cygnus.com) + */ +public class FileOutputStream extends OutputStream +{ + private FileDescriptor fd; + + private FileChannelImpl ch; + + /** + * This method initializes a <code>FileOutputStream</code> object to write + * to the named file. The file is created if it does not exist, and + * the bytes written are written starting at the beginning of the file if + * the <code>append</code> argument is <code>false</code> or at the end + * of the file if the <code>append</code> argument is true. + * <p> + * Before opening a file, a security check is performed by calling the + * <code>checkWrite</code> method of the <code>SecurityManager</code> (if + * one exists) with the name of the file to be opened. An exception is + * thrown if writing is not allowed. + * + * @param path The name of the file this stream should write to + * @param append <code>true</code> to append bytes to the end of the file, + * or <code>false</code> to write bytes to the beginning + * + * @exception SecurityException If write access to the file is not allowed + * @exception FileNotFoundException If a non-security error occurs + */ + public FileOutputStream (String path, boolean append) + throws SecurityException, FileNotFoundException + { + this (new File(path), append); + } + + /** + * This method initializes a <code>FileOutputStream</code> object to write + * to the named file. The file is created if it does not exist, and + * the bytes written are written starting at the beginning of the file. + * <p> + * Before opening a file, a security check is performed by calling the + * <code>checkWrite</code> method of the <code>SecurityManager</code> (if + * one exists) with the name of the file to be opened. An exception is + * thrown if writing is not allowed. + * + * @param path The name of the file this stream should write to + * + * @exception SecurityException If write access to the file is not allowed + * @exception FileNotFoundException If a non-security error occurs + */ + public FileOutputStream (String path) + throws SecurityException, FileNotFoundException + { + this (path, false); + } + + /** + * This method initializes a <code>FileOutputStream</code> object to write + * to the specified <code>File</code> object. The file is created if it + * does not exist, and the bytes written are written starting at the + * beginning of the file. + * <p> + * Before opening a file, a security check is performed by calling the + * <code>checkWrite</code> method of the <code>SecurityManager</code> (if + * one exists) with the name of the file to be opened. An exception is + * thrown if writing is not allowed. + * + * @param file The <code>File</code> object this stream should write to + * + * @exception SecurityException If write access to the file is not allowed + * @exception FileNotFoundException If a non-security error occurs + */ + public FileOutputStream (File file) + throws SecurityException, FileNotFoundException + { + this (file, false); + } + + /** + * This method initializes a <code>FileOutputStream</code> object to write + * to the specified <code>File</code> object. The file is created if it + * does not exist, and the bytes written are written starting at the + * beginning of the file if the <code>append</code> parameter is + * <code>false</code>. Otherwise bytes are written at the end of the + * file. + * <p> + * Before opening a file, a security check is performed by calling the + * <code>checkWrite</code> method of the <code>SecurityManager</code> (if + * one exists) with the name of the file to be opened. An exception is + * thrown if writing is not allowed. + * + * @param file The <code>File</code> object this stream should write to + * @param append <code>true</code> to append bytes to the end of the file, + * or <code>false</code> to write bytes to the beginning + * + * @exception SecurityException If write access to the file is not allowed + * @exception FileNotFoundException If a non-security error occurs + */ + public FileOutputStream (File file, boolean append) + throws FileNotFoundException + { + SecurityManager s = System.getSecurityManager(); + if (s != null) + s.checkWrite(file.getPath()); + + ch = FileChannelImpl.create(file, (append + ? FileChannelImpl.WRITE + | FileChannelImpl.APPEND + : FileChannelImpl.WRITE)); + } + + /** + * This method initializes a <code>FileOutputStream</code> object to write + * to the file represented by the specified <code>FileDescriptor</code> + * object. This method does not create any underlying disk file or + * reposition the file pointer of the given descriptor. It assumes that + * this descriptor is ready for writing as is. + * <p> + * Before opening a file, a security check is performed by calling the + * <code>checkWrite</code> method of the <code>SecurityManager</code> (if + * one exists) with the specified <code>FileDescriptor</code> as an argument. + * An exception is thrown if writing is not allowed. + * + * @param fdObj The <code>FileDescriptor</code> this stream should write to + * + * @exception SecurityException If write access to the file is not allowed + */ + public FileOutputStream (FileDescriptor fdObj) + throws SecurityException + { + // Hmm, no other exception but this one to throw, but if the descriptor + // isn't valid, we surely don't have "permission" to write to it. + if (!fdObj.valid()) + throw new SecurityException("Invalid FileDescriptor"); + + SecurityManager s = System.getSecurityManager(); + if (s != null) + s.checkWrite(fdObj); + + fd = fdObj; + ch = (FileChannelImpl) fdObj.channel; + } + + FileOutputStream(FileChannelImpl ch) + { + this.ch = ch; + } + + protected void finalize () throws IOException + { + // We don't actually need this, but we include it because it is + // mentioned in the JCL. + } + + /** + * This method returns a <code>FileDescriptor</code> object representing + * the file that is currently being written to + * + * @return A <code>FileDescriptor</code> object for this stream + * + * @exception IOException If an error occurs + */ + public final FileDescriptor getFD () throws IOException + { + synchronized (this) + { + if (fd == null) + fd = new FileDescriptor (ch); + return fd; + } + } + + /** + * This method writes a single byte of data to the file. + * + * @param b The byte of data to write, passed as an <code>int</code> + * + * @exception IOException If an error occurs + */ + public void write (int b) throws IOException + { + ch.write (b); + } + + /** + * This method writes all the bytes in the specified array to the + * file. + * + * @param buf The array of bytes to write to the file + * + * @exception IOException If an error occurs + */ + public void write (byte[] buf) + throws IOException + { + write (buf, 0, buf.length); + } + + /** + * This method writes <code>len</code> bytes from the byte array + * <code>buf</code> to the file starting at index <code>offset</code>. + * + * @param buf The array of bytes to write to the file + * @param offset The offset into the array to start writing bytes from + * @param len The number of bytes to write to the file + * + * @exception IOException If an error occurs + */ + public void write (byte[] buf, int offset, int len) + throws IOException + { + if (offset < 0 + || len < 0 + || offset + len > buf.length) + throw new ArrayIndexOutOfBoundsException (); + + ch.write (buf, offset, len); + } + + /** + * This method closes the underlying file. Any further attempts to + * write to this stream will likely generate an exception since the + * file is closed. + * + * @exception IOException If an error occurs + */ + public void close () throws IOException + { + ch.close(); + } + + /** + * This method creates a java.nio.channels.FileChannel. + * Nio does not allow one to create a file channel directly. + * A file channel must be created by first creating an instance of + * Input/Output/RandomAccessFile and invoking the getChannel() method on it. + */ + public synchronized FileChannel getChannel() + { + return ch; + } + +} // class FileOutputStream + diff --git a/libjava/classpath/java/io/FilePermission.java b/libjava/classpath/java/io/FilePermission.java new file mode 100644 index 00000000000..356787bfa72 --- /dev/null +++ b/libjava/classpath/java/io/FilePermission.java @@ -0,0 +1,292 @@ +/* FilePermission.java -- + Copyright (C) 1998, 2000, 2003, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +import java.security.Permission; + +public final class FilePermission extends Permission implements Serializable +{ + private static final long serialVersionUID = 7930732926638008763L; + + private static final String CURRENT_DIRECTORY = + System.getProperty("user.dir"); + + private static final String ALL_FILES = "<<ALL FILES>>"; + + private boolean readPerm = false; + private boolean writePerm = false; + private boolean executePerm = false; + private boolean deletePerm = false; + private final String actionsString; + + // Checks and caches the actions + private void checkPerms() throws IllegalArgumentException + { + String action; + int i = actionsString.indexOf(','); + int startI = 0; + while (i != -1) + { + action = actionsString.substring(startI, i).trim().toLowerCase(); + if (action.equals("read")) + readPerm = true; + else if (action.equals("write")) + writePerm = true; + else if (action.equals("execute")) + executePerm = true; + else if (action.equals("delete")) + deletePerm = true; + else + throw new IllegalArgumentException("Unknown action: " + action); + + startI = i + 1; + i = actionsString.indexOf(',', startI); + } + + action = actionsString.substring(startI).trim().toLowerCase(); + if (action.equals("read")) + readPerm = true; + else if (action.equals("write")) + writePerm = true; + else if (action.equals("execute")) + executePerm = true; + else if (action.equals("delete")) + deletePerm = true; + else + throw new IllegalArgumentException("Unknown action: " + action); + } + + /** + * Create a new FilePermission. + * + * @param pathExpression an expression specifying the paths this + * permission represents. + * @param actionsString a comma-separated list of the actions this + * permission represents. The actions must be "read", "write", + * "execute" and/or "delete". + */ + public FilePermission(String pathExpression, String actionsString) + { + // FIXME: what to do when the file string is malformed? + super(pathExpression); + if (pathExpression == null) + throw new NullPointerException("pathExpression"); + if (actionsString == null) + throw new IllegalArgumentException("actionsString"); + this.actionsString = actionsString; + checkPerms(); + } + + /** + * Get the actions this FilePermission supports. + * @return the String representing the actions this FilePermission supports. + */ + public String getActions() + { + return actionsString; + } + + /** + * Get the hash code for this Object.<P> + * FilePermission's hash code is calculated as the exclusive or of the + * target + * String's hash code and the action String's hash code. + * @specnote Sun did not specify how to calculate the hash code; + * I made this up. + * @return the hash code for this Object. + */ + public int hashCode() + { + return getName().hashCode() ^ actionsString.hashCode(); + } + + /** + * Check two FilePermissions for semantic equality. + * Two FilePermissions are exactly equivalent if they have identical path + * expressions and have exactly the same access permissions. + * @param o the Object to compare to. + * @return whether the Objects are semantically equivalent. + */ + public boolean equals(Object o) + { + if (! (o instanceof FilePermission)) + return false; + FilePermission p = (FilePermission) o; + + String f1 = getName(); + String f2 = p.getName(); + + // Compare names, taking into account if they refer to a directory + // and one has a separator and the other does not. + if (f1.length() > 0 && f1.charAt(f1.length() - 1) == File.separatorChar) + { + if (f2.length() > 0 + && f2.charAt(f2.length() - 1) == File.separatorChar) + { + if (! f2.equals(f1)) + return false; + } + else + { + if (! f2.equals(f1.substring(0, f1.length() - 1))) + return false; + } + } + else + { + if (f2.length() > 0 + && f2.charAt(f2.length() - 1) == File.separatorChar) + { + if (! f1.equals(f2.substring(0, f2.length() - 1))) + return false; + } + else + { + if (! f1.equals(f2)) + return false; + } + } + return (readPerm == p.readPerm + && writePerm == p.writePerm + && executePerm == p.executePerm + && deletePerm == p.deletePerm); + } + + /** + * Check to see if this permission implies another. + * Permission A implies permission B if these things are all true: + * <OL> + * <LI>A and B are both FilePermissions.</LI> + * <LI>All possible files in B are included in A + * (possibly more are in A).</LI> + * <LI>All actions B supports, A also supports.</LI> + * </OL> + * @param p the Permission to compare against. + * @return whether this Permission implies p + */ + public boolean implies(Permission p) + { + if (! (p instanceof FilePermission)) + return false; + + String f1 = getName(); + + if (f1.equals(ALL_FILES)) + return true; + + FilePermission fp = (FilePermission) p; + String f2 = fp.getName(); + + if (f1.charAt(0) != File.separatorChar) + f1 = CURRENT_DIRECTORY + f1; + if (f2.charAt(0) != File.separatorChar) + f2 = CURRENT_DIRECTORY + f2; + + String sub1; + + switch (f1.charAt(f1.length() - 1)) + { + case '*': + sub1 = f1.substring(0, f1.length() - 1); // chop off "*" + if (f2.length() <= sub1.length()) + { + // If it's smaller, there is no way it could be part of + // this directory. If it's the same (or length - 1), it + // could be the same directory but specifies access to + // the directory rather than the files in it. + return false; + } + else if (f2.charAt(sub1.length() - 1) == File.separatorChar) + { + // Make sure the part before the "/" is the same. + if (! f2.substring(0, sub1.length()).equals(sub1)) + return false; + // Make sure there are no subdirectories specified + // underneath this one. + if (f2.substring(sub1.length() + 1).indexOf(File.separatorChar) + != -1) + return false; + } + else + { + // Obviously not equal: f2 is either not a directory or + // is not the same directory (its name continues further + // than we want). + return false; + } + break; + case '-': + // Chop off "/-". + sub1 = f1.substring(0, f1.length() - 2); + if (f2.length() < sub1.length()) + { + // If it's smaller, there is no way it could be part of + // this directory. + return false; + } + else if (f2.length() > sub1.length() + && f2.charAt(sub1.length()) != File.separatorChar) + return false; + else if (! f2.substring(0, sub1.length()).equals(sub1)) + return false; + break; + + default: + if (f2.charAt(f2.length() - 1) == File.separatorChar) + { + if (! f1.equals(f2.substring(0, f2.length() - 1))) + return false; + } + else if (!f1.equals(f2)) + return false; + break; + } + + if (readPerm && ! fp.readPerm) + return false; + if (writePerm && ! fp.writePerm) + return false; + if (executePerm && ! fp.executePerm) + return false; + if (deletePerm && ! fp.deletePerm) + return false; + + return true; + } +} diff --git a/libjava/classpath/java/io/FileReader.java b/libjava/classpath/java/io/FileReader.java new file mode 100644 index 00000000000..4a1dd5ff4ce --- /dev/null +++ b/libjava/classpath/java/io/FileReader.java @@ -0,0 +1,92 @@ +/* FileReader.java -- Convenience class for reading characters from a file + Copyright (C) 1998, 2000, 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This class provides a convenient way to set up a <code>Reader</code> + * to read from a file. It opens the specified file for reading and creates + * the <code>InputStreamReader</code> to read from the + * resulting <code>FileInputStream</code>. This class can only be used + * to read from files using the default character encoding. Use + * <code>InputStreamReader</code> directly to use a non-default encoding. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class FileReader extends InputStreamReader +{ + /** + * This method initializes a <code>FileReader</code> instance to read from + * the specified <code>File</code> object. + * + * @param file The <code>File</code> object representing the file to read from + * + * @exception FileNotFoundException If the file is not found or some other + * error occurs + */ + public FileReader(File file) throws FileNotFoundException + { + super(new FileInputStream(file)); + } + + /** + * This method initializes a <code>FileReader</code> instance to read from + * this specified <code>FileDescriptor</code> object. + * + * @param fd The <code>FileDescriptor</code> to read from. + */ + public FileReader(FileDescriptor fd) + { + super(new FileInputStream(fd)); + } + + /** + * This method initializes a <code>FileReader</code> instance to read from + * the specified named file. + * + * @param name The name of the file to read from + * + * @exception FileNotFoundException If the file is not found or some other + * error occurs + */ + public FileReader(String name) throws FileNotFoundException + { + super(new FileInputStream(name)); + } +} // class FileReader + diff --git a/libjava/classpath/java/io/FileWriter.java b/libjava/classpath/java/io/FileWriter.java new file mode 100644 index 00000000000..b34db83231e --- /dev/null +++ b/libjava/classpath/java/io/FileWriter.java @@ -0,0 +1,137 @@ +/* FileWriter.java -- Convenience class for writing to files. + Copyright (C) 1998, 1999, 2001, 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 + * "The Java Language Specification", ISBN 0-201-63451-1 + * Status: Complete to version 1.1. + */ + +/** + * This is a convenience class for writing to files. It creates an + * <code>FileOutputStream</code> and initializes an + * <code>OutputStreamWriter</code> to write to it. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@cygnus.com) + */ +public class FileWriter extends OutputStreamWriter +{ + /** + * This method initializes a new <code>FileWriter</code> object to write + * to the specified <code>File</code> object. + * + * @param file The <code>File</code> object to write to. + * + * @throws SecurityException If writing to this file is forbidden by the + * <code>SecurityManager</code>. + * @throws IOException If any other error occurs + */ + public FileWriter(File file) throws SecurityException, IOException + { + super(new FileOutputStream(file)); + } + + /** + * This method initializes a new <code>FileWriter</code> object to write + * to the specified <code>File</code> object. + * + * @param file The <code>File</code> object to write to. + * @param append <code>true</code> to start adding data at the end of the + * file, <code>false</code> otherwise. + * + * @throws SecurityException If writing to this file is forbidden by the + * <code>SecurityManager</code>. + * @throws IOException If any other error occurs + */ + public FileWriter(File file, boolean append) throws IOException + { + super(new FileOutputStream(file, append)); + } + + /** + * This method initializes a new <code>FileWriter</code> object to write + * to the specified <code>FileDescriptor</code> object. + * + * @param fd The <code>FileDescriptor</code> object to write to + * + * @throws SecurityException If writing to this file is forbidden by the + * <code>SecurityManager</code>. + */ + public FileWriter(FileDescriptor fd) throws SecurityException + { + super(new FileOutputStream(fd)); + } + + /** + * This method intializes a new <code>FileWriter</code> object to + * write to the + * specified named file. + * + * @param name The name of the file to write to + * + * @throws SecurityException If writing to this file is forbidden by the + * <code>SecurityManager</code>. + * @throws IOException If any other error occurs + */ + public FileWriter(String name) throws IOException + { + super(new FileOutputStream(name)); + } + + /** + * This method intializes a new <code>FileWriter</code> object to + * write to the + * specified named file. This form of the constructor allows the caller + * to determin whether data should be written starting at the beginning or + * the end of the file. + * + * @param name The name of the file to write to + * @param append <code>true</code> to start adding data at the end of the + * file, <code>false</code> otherwise. + * + * @throws SecurityException If writing to this file is forbidden by the + * <code>SecurityManager</code>. + * @throws IOException If any other error occurs + */ + public FileWriter(String name, boolean append) throws IOException + { + super(new FileOutputStream(name, append)); + } +} diff --git a/libjava/classpath/java/io/FilenameFilter.java b/libjava/classpath/java/io/FilenameFilter.java new file mode 100644 index 00000000000..57b4d3b182c --- /dev/null +++ b/libjava/classpath/java/io/FilenameFilter.java @@ -0,0 +1,76 @@ +/* FilenameFilter.java -- Filter a list of filenames + Copyright (C) 1998, 1999, 2001, 2003, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 + * "The Java Language Specification", ISBN 0-201-63451-1 + * Status: Complete to 1.1. + */ + +/** + * This interface has one method which is used for filtering filenames + * returned in a directory listing. It is currently used by the + * <code>File.list(FilenameFilter)</code> method and by the filename + * dialog in AWT. + * <p> + * The method in this interface determines if a particular file should + * or should not be included in the file listing. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@cygnus.com) + * + * @see File#listFiles(java.io.FilenameFilter) + * @see java.awt.FileDialog#setFilenameFilter(java.io.FilenameFilter) + */ +public interface FilenameFilter +{ + /** + * This method determines whether or not a given file should be included + * in a directory listing. + * + * @param dir The <code>File</code> instance for the directory being read + * @param name The name of the file to test + * + * @return <code>true</code> if the file should be included in the list, + * <code>false</code> otherwise. + */ + boolean accept(File dir, String name); + +} // interface FilenameFilter + diff --git a/libjava/classpath/java/io/FilterInputStream.java b/libjava/classpath/java/io/FilterInputStream.java new file mode 100644 index 00000000000..d3cb9e4f71f --- /dev/null +++ b/libjava/classpath/java/io/FilterInputStream.java @@ -0,0 +1,203 @@ +/* FilterInputStream.java -- Base class for classes that filter input + Copyright (C) 1998, 1999, 2001, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 + * "The Java Language Specification", ISBN 0-201-63451-1 + * plus online API docs for JDK 1.2 beta from http://www.javasoft.com. + * Status: Believed complete and correct. + */ + +/** + * This is the common superclass of all standard classes that filter + * input. It acts as a layer on top of an underlying <code>InputStream</code> + * and simply redirects calls made to it to the subordinate InputStream + * instead. Subclasses of this class perform additional filtering + * functions in addition to simply redirecting the call. + * <p> + * This class is not abstract. However, since it only redirects calls + * to a subordinate <code>InputStream</code> without adding any functionality + * on top of it, this class should not be used directly. Instead, various + * subclasses of this class should be used. This is enforced with a + * protected constructor. Do not try to hack around it. + * <p> + * When creating a subclass of <code>FilterInputStream</code>, override the + * appropriate methods to implement the desired filtering. However, note + * that the <code>read(byte[])</code> method does not need to be overridden + * as this class redirects calls to that method to + * <code>read(byte[], int, int)</code> instead of to the subordinate + * <code>InputStream read(byte[])</code> method. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + */ +public class FilterInputStream extends InputStream +{ + /** + * This is the subordinate <code>InputStream</code> to which method calls + * are redirected + */ + protected InputStream in; + + /** + * Create a <code>FilterInputStream</code> with the specified subordinate + * <code>InputStream</code>. + * + * @param in The subordinate <code>InputStream</code> + */ + protected FilterInputStream(InputStream in) + { + this.in = in; + } + + /** + * Calls the <code>in.mark(int)</code> method. + * + * @param readlimit The parameter passed to <code>in.mark(int)</code> + */ + public void mark(int readlimit) + { + in.mark(readlimit); + } + + /** + * Calls the <code>in.markSupported()</code> method. + * + * @return <code>true</code> if mark/reset is supported, <code>false</code> + * otherwise + */ + public boolean markSupported() + { + return in.markSupported(); + } + + /** + * Calls the <code>in.reset()</code> method. + * + * @exception IOException If an error occurs + */ + public void reset() throws IOException + { + in.reset(); + } + + /** + * Calls the <code>in.available()</code> method. + * + * @return The value returned from <code>in.available()</code> + * + * @exception IOException If an error occurs + */ + public int available() throws IOException + { + return in.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> + * + * @exception IOException If an error occurs + */ + public long skip(long numBytes) throws IOException + { + return in.skip(numBytes); + } + + /** + * Calls the <code>in.read()</code> method + * + * @return The value returned from <code>in.read()</code> + * + * @exception IOException If an error occurs + */ + public int read() throws IOException + { + return in.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> + * + * @exception IOException If an error occurs + */ + public 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> + * + * @exception IOException If an error occurs + */ + public int read(byte[] buf, int offset, int len) throws IOException + { + return in.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. + * + * @exception IOException If an error occurs + */ + public void close() throws IOException + { + in.close(); + } +} diff --git a/libjava/classpath/java/io/FilterOutputStream.java b/libjava/classpath/java/io/FilterOutputStream.java new file mode 100644 index 00000000000..4c2dfc04a65 --- /dev/null +++ b/libjava/classpath/java/io/FilterOutputStream.java @@ -0,0 +1,150 @@ +/* FilterOutputStream.java -- Parent class for output streams that filter + Copyright (C) 1998, 1999, 2001, 2003, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 + * "The Java Language Specification", ISBN 0-201-63451-1 + * Status: Complete to version 1.1. + */ + +/** + * This class is the common superclass of output stream classes that + * filter the output they write. These classes typically transform the + * data in some way prior to writing it out to another underlying + * <code>OutputStream</code>. This class simply overrides all the + * methods in <code>OutputStream</code> to redirect them to the + * underlying stream. Subclasses provide actual filtering. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@cygnus.com) + */ +public class FilterOutputStream extends OutputStream +{ + /** + * This is the subordinate <code>OutputStream</code> that this class + * redirects its method calls to. + */ + protected OutputStream out; + + /** + * This method initializes an instance of <code>FilterOutputStream</code> + * to write to the specified subordinate <code>OutputStream</code>. + * + * @param out The <code>OutputStream</code> to write to + */ + public FilterOutputStream(OutputStream out) + { + this.out = out; + } + + /** + * This method closes the underlying <code>OutputStream</code>. Any + * further attempts to write to this stream may throw an exception. + * + * @exception IOException If an error occurs + */ + public void close() throws IOException + { + flush(); + out.close(); + } + + /** + * This method attempt to flush all buffered output to be written to the + * underlying output sink. + * + * @exception IOException If an error occurs + */ + public void flush() throws IOException + { + out.flush(); + } + + /** + * This method writes a single byte of output to the underlying + * <code>OutputStream</code>. + * + * @param b The byte to write, passed as an int. + * + * @exception IOException If an error occurs + */ + public void write(int b) throws IOException + { + out.write(b); + } + + /** + * This method writes all the bytes in the specified array to the underlying + * <code>OutputStream</code>. It does this by calling the three parameter + * version of this method - <code>write(byte[], int, int)</code> in this + * class instead of writing to the underlying <code>OutputStream</code> + * directly. This allows most subclasses to avoid overriding this method. + * + * @param buf The byte array to write bytes from + * + * @exception IOException If an error occurs + */ + public void write(byte[] buf) throws IOException + { + // Don't do checking here, per Java Lang Spec. + write(buf, 0, buf.length); + } + + /** + * This method calls the <code>write(int)</code> method <code>len</code> + * times for all bytes from the array <code>buf</code> starting at index + * <code>offset</code>. Subclasses should overwrite this method to get a + * more efficient implementation. + * + * @param buf The byte array to write bytes from + * @param offset The index into the array to start writing bytes from + * @param len The number of bytes to write + * + * @exception IOException If an error occurs + */ + public void write(byte[] buf, int offset, int len) throws IOException + { + // Don't do checking here, per Java Lang Spec. + for (int i=0; i < len; i++) + write(buf[offset + i]); + + } + +} // class FilterOutputStream + diff --git a/libjava/classpath/java/io/FilterReader.java b/libjava/classpath/java/io/FilterReader.java new file mode 100644 index 00000000000..2bd040a7f72 --- /dev/null +++ b/libjava/classpath/java/io/FilterReader.java @@ -0,0 +1,185 @@ +/* FilterReader.java -- Base class for char stream classes that filter input + Copyright (C) 1998, 1999, 2001, 2003, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 + * "The Java Language Specification", ISBN 0-201-63451-1 + * plus online API docs for JDK 1.2 beta from http://www.javasoft.com. + * Status: Believed complete and correct. + */ + +/** + * This is the common superclass of all standard classes that filter + * input. It acts as a layer on top of an underlying <code>Reader</code> + * and simply redirects calls made to it to the subordinate Reader + * instead. Subclasses of this class perform additional filtering + * functions in addition to simply redirecting the call. + * <p> + * When creating a subclass of <code>FilterReader</code>, override the + * appropriate methods to implement the desired filtering. However, note + * that the <code>read(char[])</code> method does not need to be overridden + * as this class redirects calls to that method to + * <code>read(yte[], int, int)</code> instead of to the subordinate + * <code>Reader} read(yte[])</code> method. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + */ +public abstract class FilterReader extends Reader +{ + /** + * This is the subordinate <code>Reader</code> to which method calls + * are redirected + */ + protected Reader in; + + /** + * Create a <code>FilterReader</code> with the specified subordinate + * <code>Reader</code>. + * The <code>lock</code> of the new <code>FilterReader</code> will be set + * to <code>in.lock</code>. + * + * @param in The subordinate <code>Reader</code> + */ + protected FilterReader(Reader in) + { + super(in.lock); + this.in = in; + } + + /** + * Calls the <code>in.mark(int)</code> method. + * + * @param readlimit The parameter passed to <code>in.mark(int)</code> + * + * @exception IOException If an error occurs + */ + public void mark(int readlimit) throws IOException + { + in.mark(readlimit); + } + + /** + * Calls the <code>in.markSupported()</code> method. + * + * @return <code>true</code> if mark/reset is supported, + * <code>false</code> otherwise + */ + public boolean markSupported() + { + return(in.markSupported()); + } + + /** + * Calls the <code>in.reset()</code> method. + * + * @exception IOException If an error occurs + */ + public void reset() throws IOException + { + in.reset(); + } + + /** + * Calls the <code>in.read()</code> method. + * + * @return The value returned from <code>in.available()</code> + * + * @exception IOException If an error occurs + */ + public boolean ready() throws IOException + { + return(in.ready()); + } + + /** + * Calls the <code>in.skip(long)</code> method + * + * @param numBytes The requested number of chars to skip. + * + * @return The value returned from <code>in.skip(long)</code> + * + * @exception IOException If an error occurs + */ + public long skip(long num_chars) throws IOException + { + return(in.skip(num_chars)); + } + + /** + * Calls the <code>in.read()</code> method + * + * @return The value returned from <code>in.read()</code> + * + * @exception IOException If an error occurs + */ + public int read() throws IOException + { + return(in.read()); + } + + /** + * Calls the <code>in.read(char[], int, int)</code> method. + * + * @param buf The buffer to read chars into + * @param offset The index into the buffer to start storing chars + * @param len The maximum number of chars to read. + * + * @return The value retured from <code>in.read(char[], int, int)</code> + * + * @exception IOException If an error occurs + */ + public int read(char[] buf, int offset, int len) throws IOException + { + return(in.read(buf, offset, len)); + } + + /** + * This method closes the stream by calling the <code>close()</code> method + * of the underlying stream. + * + * @exception IOException If an error occurs + */ + public void close() throws IOException + { + in.close(); + } + +} // class FilterReader + diff --git a/libjava/classpath/java/io/FilterWriter.java b/libjava/classpath/java/io/FilterWriter.java new file mode 100644 index 00000000000..9b9ce33f9f6 --- /dev/null +++ b/libjava/classpath/java/io/FilterWriter.java @@ -0,0 +1,147 @@ +/* FilterWriter.java -- Parent class for output streams that filter + Copyright (C) 1998, 1999, 2001, 2003, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 + * "The Java Language Specification", ISBN 0-201-63451-1 + * Status: Complete to version 1.1. + */ + +/** + * This class is the common superclass of output character stream classes + * that filter the output they write. These classes typically transform the + * data in some way prior to writing it out to another underlying + * <code>Writer</code>. This class simply overrides all the + * methods in <code>Writer</code> to redirect them to the + * underlying stream. Subclasses provide actual filtering. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@cygnus.com) + */ +public abstract class FilterWriter extends Writer +{ + /** + * This is the subordinate <code>Writer</code> that this class + * redirects its method calls to. + */ + protected Writer out; + + /** + * This method initializes an instance of <code>FilterWriter</code> + * to write to the specified subordinate <code>Writer</code>. + * The given <code>Writer</code> will be used as <code>lock</code> for + * the newly created <code>FilterWriter</code>. + * + * @param out The <code>Writer</code> to write to + */ + protected FilterWriter(Writer out) + { + super(out.lock); + this.out = out; + } + + /** + * This method closes the underlying <code>Writer</code>. Any + * further attempts to write to this stream may throw an exception. + * + * @exception IOException If an error occurs + */ + public void close() throws IOException + { + out.close(); + } + + /** + * This method attempt to flush all buffered output to be written to the + * underlying output sink. + * + * @exception IOException If an error occurs + */ + public void flush() throws IOException + { + out.flush(); + } + + /** + * This method writes a single char of output to the underlying + * <code>Writer</code>. + * + * @param b The char to write, passed as an int. + * + * @exception IOException If an error occurs + */ + public void write(int b) throws IOException + { + out.write(b); + } + + /** + * This method writes <code>len</code> chars from the array <code>buf</code> + * starting at index <code>offset</code> to the underlying + * <code>Writer</code>. + * + * @param buf The char array to write chars from + * @param offset The index into the array to start writing chars from + * @param len The number of chars to write + * + * @exception IOException If an error occurs + */ + public void write(char[] buf, int offset, int len) throws IOException + { + out.write(buf, offset, len); + } + + /** + * This method writes <code>len</code> chars from the <code>String</code> + * starting at position <code>offset</code>. + * + * @param str The <code>String</code> that is to be written + * @param offset The character offset into the <code>String</code> + * to start writing from + * @param len The number of chars to write + * + * @exception IOException If an error occurs + */ + public void write(String str, int offset, int len) throws IOException + { + out.write(str, offset, len); + } + +} // class FilterWriter + diff --git a/libjava/classpath/java/io/Flushable.java b/libjava/classpath/java/io/Flushable.java new file mode 100644 index 00000000000..e9718d60a9b --- /dev/null +++ b/libjava/classpath/java/io/Flushable.java @@ -0,0 +1,62 @@ +/* Flushable.java -- Flushable object + Copyright (C) 2004, 2005 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.io; + +/** + * A <code>Flushable</code> class represents a stream of + * data, for which internally buffered data can be `flushed'. + * Flushing such a stream causes the buffered data to be + * written to the stream. + * + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public interface Flushable +{ + + /** + * Flushes the stream represented by this class, + * so that any buffered data is written to the stream. + * + * @throws IOException if an I/O error occurs in flushing. + */ + void flush() + throws IOException; + +} diff --git a/libjava/classpath/java/io/IOException.java b/libjava/classpath/java/io/IOException.java new file mode 100644 index 00000000000..cf3ad194633 --- /dev/null +++ b/libjava/classpath/java/io/IOException.java @@ -0,0 +1,74 @@ +/* IOException.java -- Generic input/output exception + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This exception is thrown to indicate an I/O problem of some sort + * occurred. Since this is a fairly generic exception, often a subclass + * of IOException will actually be thrown in order to provide a more + * detailed indication of what happened. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@cygnus.com) + * @status updated to 1.4 + */ +public class IOException extends Exception +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = 7818375828146090155L; + + /** + * Create an exception without a descriptive error message. + */ + public IOException() + { + } + + /** + * Create an exception with a descriptive error message. + * + * @param message the descriptive error message + */ + public IOException(String message) + { + super(message); + } +} // class IOException diff --git a/libjava/classpath/java/io/InputStream.java b/libjava/classpath/java/io/InputStream.java new file mode 100644 index 00000000000..86d1cd74914 --- /dev/null +++ b/libjava/classpath/java/io/InputStream.java @@ -0,0 +1,272 @@ +/* InputStream.java -- Base class for input + Copyright (C) 1998, 1999, 2001, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This abstract class forms the base of the hierarchy of classes that read + * input as a stream of bytes. It provides a common set of methods for + * reading bytes from streams. Subclasses implement and extend these + * methods to read bytes from a particular input source such as a file + * or network connection. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + */ +public abstract class InputStream +{ + /** + * Default, no-arg, public constructor + */ + public InputStream() + { + } + + /** + * This method returns the number of bytes that can be read from this + * stream before a read can block. A return of 0 indicates that blocking + * might (or might not) occur on the very next read attempt. + * <p> + * This method always returns 0 in this class + * + * @return The number of bytes that can be read before blocking could occur + * + * @exception IOException If an error occurs + */ + public int available() throws IOException + { + return 0; + } + + /** + * This method closes the stream. Any futher attempts to read from the + * stream may generate an <code>IOException</code> + * <p> + * This method does nothing in this class, but subclasses may override + * this method in order to provide additional functionality. + * + * @exception IOException If an error occurs, which can only happen + * in a subclass + */ + public void close() throws IOException + { + // Do nothing + } + + /** + * This method marks a position in the input to which the stream can + * be "reset" by calling the <code>reset()</code> method. The + * parameter @code{readlimit} is the number of bytes that can be read + * from the stream after setting the mark before the mark becomes + * invalid. For example, if <code>mark()</code> is called with a + * read limit of 10, then when 11 bytes of data are read from the + * stream before the <code>reset()</code> method is called, then the + * mark is invalid and the stream object instance is not required to + * remember the mark. + * <p> + * This method does nothing in this class, but subclasses may override it + * to provide mark/reset functionality. + * + * @param readLimit The number of bytes that can be read before the + * mark becomes invalid + */ + public void mark(int readLimit) + { + // Do nothing + } + + /** + * This method returns a boolean that indicates whether the mark/reset + * methods are supported in this class. Those methods can be used to + * remember a specific point in the stream and reset the stream to that + * point. + * <p> + * This method always returns <code>false</code> in this class, but + * subclasses can override this method to return <code>true</code> + * if they support mark/reset functionality. + * + * @return <code>true</code> if mark/reset functionality is + * supported, <code>false</code> otherwise + */ + public boolean markSupported() + { + return false; + } + + /** + * This method reads an unsigned byte from the input stream and returns it + * as an int in the range of 0-255. This method also will return -1 if + * the end of the stream has been reached. + * <p> + * This method will block until the byte can be read. + * + * @return The byte read or -1 if end of stream + * + * @exception IOException If an error occurs + */ + public abstract int read() throws IOException; + + /** + * This method reads bytes from a stream and stores them into a caller + * supplied buffer. This method attempts to completely fill the buffer, + * but can return before doing so. The actual number of bytes read is + * returned as an int. A -1 is returned to indicate the end of the stream. + * <p> + * This method will block until some data can be read. + * <p> + * This method operates by calling an overloaded read method like so: + * <code>read(b, 0, b.length)</code> + * + * @param b The buffer into which the bytes read will be stored. + * + * @return The number of bytes read or -1 if end of stream. + * + * @exception IOException If an error occurs. + */ + public int read(byte[] b) throws IOException + { + return read(b, 0, b.length); + } + + /** + * This method read bytes from a stream and stores them into a + * caller supplied buffer. It starts storing the data at index + * <code>off</code> into the buffer and attempts to read + * <code>len</code> bytes. This method can return before reading the + * number of bytes requested. The actual number of bytes read is + * returned as an int. A -1 is returned to indicate the end of the + * stream. + * <p> + * This method will block until some data can be read. + * <p> + * This method operates by calling the single byte <code>read()</code> method + * in a loop until the desired number of bytes are read. The read loop + * stops short if the end of the stream is encountered or if an IOException + * is encountered on any read operation except the first. If the first + * attempt to read a bytes fails, the IOException is allowed to propagate + * upward. And subsequent IOException is caught and treated identically + * to an end of stream condition. Subclasses can (and should if possible) + * override this method to provide a more efficient implementation. + * + * @param b The array into which the bytes read should be stored + * @param off The offset into the array to start storing bytes + * @param len The requested number of bytes to read + * + * @return The actual number of bytes read, or -1 if end of stream. + * + * @exception IOException If an error occurs. + */ + public int read(byte[] b, int off, int len) throws IOException + { + if (off < 0 || len < 0 || off + len > b.length) + throw new IndexOutOfBoundsException(); + if (b.length == 0) + return 0; + + int i, ch; + + for (i = 0; i < len; ++i) + try + { + if ((ch = read()) < 0) + return i == 0 ? -1 : i; // EOF + b[off + i] = (byte) ch; + } + catch (IOException ex) + { + // Only reading the first byte should cause an IOException. + if (i == 0) + throw ex; + return i; + } + + return i; + } + + /** + * This method resets a stream to the point where the + * <code>mark()</code> method was called. Any bytes that were read + * after the mark point was set will be re-read during subsequent + * reads. + * <p> + * This method always throws an IOException in this class, but subclasses + * can override this method if they provide mark/reset functionality. + * + * @exception IOException Always thrown for this class + */ + public void reset() throws IOException + { + throw new IOException("mark/reset not supported"); + } + + /** + * This method skips the specified number of bytes in the stream. It + * returns the actual number of bytes skipped, which may be less than the + * requested amount. + * <p> + * This method reads and discards bytes into a byte array until the + * specified number of bytes were skipped or until either the end of stream + * is reached or a read attempt returns a short count. Subclasses can + * override this metho to provide a more efficient implementation where + * one exists. + * + * @param n The requested number of bytes to skip + * + * @return The actual number of bytes skipped. + * + * @exception IOException If an error occurs + */ + public long skip(long n) throws IOException + { + // Throw away n bytes by reading them into a temp byte[]. + // Limit the temp array to 2Kb so we don't grab too much memory. + final int buflen = n > 2048 ? 2048 : (int) n; + byte[] tmpbuf = new byte[buflen]; + final long origN = n; + + while (n > 0L) + { + int numread = read(tmpbuf, 0, n > buflen ? buflen : (int) n); + if (numread <= 0) + break; + n -= numread; + } + + return origN - n; + } +} diff --git a/libjava/classpath/java/io/InputStreamReader.java b/libjava/classpath/java/io/InputStreamReader.java new file mode 100644 index 00000000000..315af83e1a4 --- /dev/null +++ b/libjava/classpath/java/io/InputStreamReader.java @@ -0,0 +1,438 @@ +/* InputStreamReader.java -- Reader than transforms bytes to chars + Copyright (C) 1998, 1999, 2001, 2003, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +import java.nio.charset.UnsupportedCharsetException; +import java.nio.charset.CharacterCodingException; +import java.nio.charset.IllegalCharsetNameException; +import java.nio.charset.CoderResult; +import java.nio.charset.CodingErrorAction; +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.CharBuffer; +import java.nio.ByteBuffer; +import gnu.java.nio.charset.EncodingHelper; + +/** + * This class reads characters from a byte input stream. The characters + * read are converted from bytes in the underlying stream by a + * decoding layer. The decoding layer transforms bytes to chars according + * to an encoding standard. There are many available encodings to choose + * from. The desired encoding can either be specified by name, or if no + * encoding is selected, the system default encoding will be used. The + * system default encoding name is determined from the system property + * <code>file.encoding</code>. The only encodings that are guaranteed to + * be availalbe are "8859_1" (the Latin-1 character set) and "UTF8". + * Unforunately, Java does not provide a mechanism for listing the + * ecodings that are supported in a given implementation. + * <p> + * Here is a list of standard encoding names that may be available: + * <p> + * <ul> + * <li>8859_1 (ISO-8859-1/Latin-1)</li> + * <li>8859_2 (ISO-8859-2/Latin-2)</li> + * <li>8859_3 (ISO-8859-3/Latin-3)</li> + * <li>8859_4 (ISO-8859-4/Latin-4)</li> + * <li>8859_5 (ISO-8859-5/Latin-5)</li> + * <li>8859_6 (ISO-8859-6/Latin-6)</li> + * <li>8859_7 (ISO-8859-7/Latin-7)</li> + * <li>8859_8 (ISO-8859-8/Latin-8)</li> + * <li>8859_9 (ISO-8859-9/Latin-9)</li> + * <li>ASCII (7-bit ASCII)</li> + * <li>UTF8 (UCS Transformation Format-8)</li> + * <li>More later</li> + * </ul> + * <p> + * It is recommended that applications do not use + * <code>InputStreamReader</code>'s + * directly. Rather, for efficiency purposes, an object of this class + * should be wrapped by a <code>BufferedReader</code>. + * <p> + * Due to a deficiency the Java class library design, there is no standard + * way for an application to install its own byte-character encoding. + * + * @see BufferedReader + * @see InputStream + * + * @author Robert Schuster + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Per Bothner (bothner@cygnus.com) + * @date April 22, 1998. + */ +public class InputStreamReader extends Reader +{ + /** + * The input stream. + */ + private InputStream in; + + /** + * The charset decoder. + */ + private CharsetDecoder decoder; + + /** + * End of stream reached. + */ + private boolean isDone = false; + + /** + * Need this. + */ + private float maxBytesPerChar; + + /** + * Buffer holding surplus loaded bytes (if any) + */ + private ByteBuffer byteBuffer; + + /** + * java.io canonical name of the encoding. + */ + private String encoding; + + /** + * We might decode to a 2-char UTF-16 surrogate, which won't fit in the + * output buffer. In this case we need to save the surrogate char. + */ + private char savedSurrogate; + private boolean hasSavedSurrogate = false; + + /** + * This method initializes a new instance of <code>InputStreamReader</code> + * to read from the specified stream using the default encoding. + * + * @param in The <code>InputStream</code> to read from + */ + public InputStreamReader(InputStream in) + { + if (in == null) + throw new NullPointerException(); + this.in = in; + try + { + encoding = System.getProperty("file.encoding"); + // Don't use NIO if avoidable + if(EncodingHelper.isISOLatin1(encoding)) + { + encoding = "ISO8859_1"; + maxBytesPerChar = 1f; + decoder = null; + return; + } + Charset cs = EncodingHelper.getCharset(encoding); + decoder = cs.newDecoder(); + encoding = EncodingHelper.getOldCanonical(cs.name()); + try { + maxBytesPerChar = cs.newEncoder().maxBytesPerChar(); + } catch(UnsupportedOperationException _){ + maxBytesPerChar = 1f; + } + decoder.onMalformedInput(CodingErrorAction.REPLACE); + decoder.onUnmappableCharacter(CodingErrorAction.REPLACE); + decoder.reset(); + } catch(RuntimeException e) { + encoding = "ISO8859_1"; + maxBytesPerChar = 1f; + decoder = null; + } catch(UnsupportedEncodingException e) { + encoding = "ISO8859_1"; + maxBytesPerChar = 1f; + decoder = null; + } + } + + /** + * This method initializes a new instance of <code>InputStreamReader</code> + * to read from the specified stream using a caller supplied character + * encoding scheme. Note that due to a deficiency in the Java language + * design, there is no way to determine which encodings are supported. + * + * @param in The <code>InputStream</code> to read from + * @param encoding_name The name of the encoding scheme to use + * + * @exception UnsupportedEncodingException If the encoding scheme + * requested is not available. + */ + public InputStreamReader(InputStream in, String encoding_name) + throws UnsupportedEncodingException + { + if (in == null + || encoding_name == null) + throw new NullPointerException(); + + this.in = in; + // Don't use NIO if avoidable + if(EncodingHelper.isISOLatin1(encoding_name)) + { + encoding = "ISO8859_1"; + maxBytesPerChar = 1f; + decoder = null; + return; + } + try { + Charset cs = EncodingHelper.getCharset(encoding_name); + try { + maxBytesPerChar = cs.newEncoder().maxBytesPerChar(); + } catch(UnsupportedOperationException _){ + maxBytesPerChar = 1f; + } + + decoder = cs.newDecoder(); + decoder.onMalformedInput(CodingErrorAction.REPLACE); + decoder.onUnmappableCharacter(CodingErrorAction.REPLACE); + decoder.reset(); + + // The encoding should be the old name, if such exists. + encoding = EncodingHelper.getOldCanonical(cs.name()); + } catch(RuntimeException e) { + encoding = "ISO8859_1"; + maxBytesPerChar = 1f; + decoder = null; + } + } + + /** + * Creates an InputStreamReader that uses a decoder of the given + * charset to decode the bytes in the InputStream into + * characters. + */ + public InputStreamReader(InputStream in, Charset charset) { + this.in = in; + decoder = charset.newDecoder(); + + decoder.onMalformedInput(CodingErrorAction.REPLACE); + decoder.onUnmappableCharacter(CodingErrorAction.REPLACE); + decoder.reset(); + encoding = EncodingHelper.getOldCanonical(charset.name()); + } + + /** + * Creates an InputStreamReader that uses the given charset decoder + * to decode the bytes in the InputStream into characters. + */ + public InputStreamReader(InputStream in, CharsetDecoder decoder) { + this.in = in; + this.decoder = decoder; + + try { + maxBytesPerChar = decoder.charset().newEncoder().maxBytesPerChar(); + } catch(UnsupportedOperationException _){ + maxBytesPerChar = 1f; + } + + decoder.onMalformedInput(CodingErrorAction.REPLACE); + decoder.onUnmappableCharacter(CodingErrorAction.REPLACE); + decoder.reset(); + encoding = EncodingHelper.getOldCanonical(decoder.charset().name()); + } + + /** + * This method closes this stream, as well as the underlying + * <code>InputStream</code>. + * + * @exception IOException If an error occurs + */ + public void close() throws IOException + { + synchronized (lock) + { + // Makes sure all intermediate data is released by the decoder. + if (decoder != null) + decoder.reset(); + if (in != null) + in.close(); + in = null; + isDone = true; + decoder = null; + } + } + + /** + * This method returns the name of the encoding that is currently in use + * by this object. If the stream has been closed, this method is allowed + * to return <code>null</code>. + * + * @return The current encoding name + */ + public String getEncoding() + { + return in != null ? encoding : null; + } + + /** + * This method checks to see if the stream is ready to be read. It + * will return <code>true</code> if is, or <code>false</code> if it is not. + * If the stream is not ready to be read, it could (although is not required + * to) block on the next read attempt. + * + * @return <code>true</code> if the stream is ready to be read, + * <code>false</code> otherwise + * + * @exception IOException If an error occurs + */ + public boolean ready() throws IOException + { + if (in == null) + throw new IOException("Reader has been closed"); + + return in.available() != 0; + } + + /** + * This method reads up to <code>length</code> characters from the stream into + * the specified array starting at index <code>offset</code> into the + * array. + * + * @param buf The character array to recieve the data read + * @param offset The offset into the array to start storing characters + * @param length The requested number of characters to read. + * + * @return The actual number of characters read, or -1 if end of stream. + * + * @exception IOException If an error occurs + */ + public int read(char[] buf, int offset, int length) throws IOException + { + if (in == null) + throw new IOException("Reader has been closed"); + if (isDone) + return -1; + if(decoder != null){ + int totalBytes = (int)((double)length * maxBytesPerChar); + byte[] bytes = new byte[totalBytes]; + + int remaining = 0; + if(byteBuffer != null) + { + remaining = byteBuffer.remaining(); + byteBuffer.get(bytes, 0, remaining); + } + int read; + if(totalBytes - remaining > 0) + { + read = in.read(bytes, remaining, totalBytes - remaining); + if(read == -1){ + read = remaining; + isDone = true; + } else + read += remaining; + } else + read = remaining; + byteBuffer = ByteBuffer.wrap(bytes, 0, read); + CharBuffer cb = CharBuffer.wrap(buf, offset, length); + int startPos = cb.position(); + + if(hasSavedSurrogate){ + hasSavedSurrogate = false; + cb.put(savedSurrogate); + read++; + } + + CoderResult cr = decoder.decode(byteBuffer, cb, isDone); + decoder.reset(); + // 1 char remains which is the first half of a surrogate pair. + if(cr.isOverflow() && cb.hasRemaining()){ + CharBuffer overflowbuf = CharBuffer.allocate(2); + cr = decoder.decode(byteBuffer, overflowbuf, isDone); + overflowbuf.flip(); + if(overflowbuf.hasRemaining()) + { + cb.put(overflowbuf.get()); + savedSurrogate = overflowbuf.get(); + hasSavedSurrogate = true; + isDone = false; + } + } + + if(byteBuffer.hasRemaining()) { + byteBuffer.compact(); + byteBuffer.flip(); + isDone = false; + } else + byteBuffer = null; + + read = cb.position() - startPos; + return (read <= 0) ? -1 : read; + } else { + byte[] bytes = new byte[length]; + int read = in.read(bytes); + for(int i=0;i<read;i++) + buf[offset+i] = (char)(bytes[i]&0xFF); + return read; + } + } + + /** + * Reads an char from the input stream and returns it + * as an int in the range of 0-65535. This method also will return -1 if + * the end of the stream has been reached. + * <p> + * This method will block until the char can be read. + * + * @return The char read or -1 if end of stream + * + * @exception IOException If an error occurs + */ + public int read() throws IOException + { + char[] buf = new char[1]; + int count = read(buf, 0, 1); + return count > 0 ? buf[0] : -1; + } + + /** + * Skips the specified number of chars in the stream. It + * returns the actual number of chars skipped, which may be less than the + * requested amount. + * + * @param count The requested number of chars to skip + * + * @return The actual number of chars skipped. + * + * @exception IOException If an error occurs + */ + public long skip(long count) throws IOException + { + if (in == null) + throw new IOException("Reader has been closed"); + + return super.skip(count); + } +} diff --git a/libjava/classpath/java/io/InterruptedIOException.java b/libjava/classpath/java/io/InterruptedIOException.java new file mode 100644 index 00000000000..96ec83649f8 --- /dev/null +++ b/libjava/classpath/java/io/InterruptedIOException.java @@ -0,0 +1,94 @@ +/* InterruptedIOException.java -- an I/O operation was interrupted + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This exception is thrown when a in process I/O operation is interrupted + * for some reason. The field bytesTransferred will contain the number of + * bytes that were read/written prior to the interruption. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@cygnus.com) + * @see Thread#interrupt() + * @status updated to 1.4 + */ +public class InterruptedIOException extends IOException +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = 4020568460727500567L; + + /** + * The number of bytes read/written prior to the interruption. + * + * @serial count of bytes successfully transferred + */ + public int bytesTransferred; + + /** + * Create an extends without a descriptive error message. + */ + public InterruptedIOException() + { + } + + /** + * Create an exception with a descriptive error message. + * + * @param message the descriptive error message + */ + public InterruptedIOException(String message) + { + super(message); + } + + /** + * Create an exception with a descriptive error message and count of + * bytes transferred. + * + * @param message the descriptive error message + * @param bytesTransferred number of bytes tranferred before interruption + */ + InterruptedIOException(String message, int bytesTransferred) + { + super(message); + this.bytesTransferred = bytesTransferred; + } +} // class InterruptedIOException diff --git a/libjava/classpath/java/io/InvalidClassException.java b/libjava/classpath/java/io/InvalidClassException.java new file mode 100644 index 00000000000..c71b0c67fc7 --- /dev/null +++ b/libjava/classpath/java/io/InvalidClassException.java @@ -0,0 +1,111 @@ +/* InvalidClassException.java -- deserializing a class failed + Copyright (C) 1998, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This exception is thrown when there is some sort of problem with a + * class during a serialization operation. This could be:<br><ul> + * <li>the serial version of the class doesn't match</li> + * <li>the class contains unknown datatypes</li> + * <li>the class does not have an accessible no-arg constructor</li> + * </ul>. + * + * <p>The field <code>classname</code> will contain the name of the + * class that caused the problem if known. The getMessage() method + * for this exception will always include the name of that class + * if known. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @since 1.1 + * @status updated to 1.4 + */ +public class InvalidClassException extends ObjectStreamException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -4333316296251054416L; + + /** + * The name of the class which encountered the error. + * + * @serial the classname causing the error + */ + public String classname; + + /** + * Create an exception with a descriptive error message, but a null + * classname. + * + * @param message the descriptive error message + */ + public InvalidClassException(String message) + { + super(message); + } + + /** + * Create an exception with a descriptive error message, and the name of + * the class that caused the problem. + * + * @param classname the name of the faulty class + * @param message the descriptive error message + */ + public InvalidClassException(String classname, String message) + { + super(message); + this.classname = classname; + } + + /** + * Returns the descriptive error message for this exception. It will + * include the class name that caused the problem if known, in the format: + * <code>[classname][; ][super.getMessage()]</code>. + * + * @return A descriptive error message, may be null + */ + public String getMessage() + { + String msg = super.getMessage(); + if (msg == null) + return classname; + return (classname == null ? "" : classname + "; ") + msg; + } +} + diff --git a/libjava/classpath/java/io/InvalidObjectException.java b/libjava/classpath/java/io/InvalidObjectException.java new file mode 100644 index 00000000000..deee876db27 --- /dev/null +++ b/libjava/classpath/java/io/InvalidObjectException.java @@ -0,0 +1,66 @@ +/* InvalidObjectException.java -- deserialization failed verification + Copyright (C) 1998, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This exception is thrown when an object fails a validation test + * during serialization. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @since 1.1 + * @status updated to 1.4 + */ +public class InvalidObjectException extends ObjectStreamException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 3233174318281839583L; + + /** + * Create an exception with a descriptive error message String. This should + * be the cause of the verification failure. + * + * @param message the descriptive error message + */ + public InvalidObjectException(String message) + { + super(message); + } +} // class InvalidObjectException diff --git a/libjava/classpath/java/io/LineNumberInputStream.java b/libjava/classpath/java/io/LineNumberInputStream.java new file mode 100644 index 00000000000..da43097f28c --- /dev/null +++ b/libjava/classpath/java/io/LineNumberInputStream.java @@ -0,0 +1,315 @@ +/* LineNumberInputStream.java -- An input stream which counts line numbers + Copyright (C) 1998, 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This class functions like a standard <code>InputStream</code> + * except that it counts line numbers, and canonicalizes newline + * characters. As data is read, whenever the byte sequences "\r", + * "\n", or "\r\n" are encountered, the running line count is + * incremeted by one. Additionally, the whatever line termination + * sequence was encountered will be converted to a "\n" byte. Note + * that this class numbers lines from 0. When the first line + * terminator is encountered, the line number is incremented to 1, and + * so on. + * <p> + * This class counts only line termination characters. If the last line + * read from the stream does not end in a line termination sequence, it + * will not be counted as a line. + * <p> + * Note that since this class operates as a filter on an underlying + * stream, it has the same mark/reset functionality as the underlying + * stream. The <code>mark()</code> and <code>reset()</code> methods + * in this class handle line numbers correctly. Calling + * <code>reset()</code> resets the line number to the point at which + * <code>mark()</code> was called if the subordinate stream supports + * that functionality. + * <p> + * @deprecated This class is deprecated in favor if + * <code>LineNumberReader</code> because it operates on ASCII bytes + * instead of an encoded character stream. This class is for backward + * compatibility only and should not be used in new applications. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + */ +public class LineNumberInputStream extends FilterInputStream +{ + /** The current line number. */ + private int lineNumber = 0; + + /** The line number when the stream was marked. */ + private int markLineNumber = 0; + + /** Flag to indicate a '\r' was just read so that an immediately + * subsequent '\n' can be ignored. */ + private boolean justReadReturnChar = false; + + /** + * Create a new <code>LineNumberInputStream</code> that reads from the + * specified subordinate <code>InputStream</code> + * + * @param in The subordinate <code>InputStream</code> to read from + */ + public LineNumberInputStream(InputStream in) + { + super(in); + } + + /** + * This method returns the number of bytes that can be read from the + * stream before the stream can block. This method is tricky + * because the subordinate <code>InputStream</code> might return + * only "\r\n" characters, which are replaced by a single "\n" + * character by the <code>read()</code> method of this class. So + * this method can only guarantee that <code>in.available() / + * 2</code> bytes can actually be read before blocking. In + * practice, considerably more bytes might be read before blocking + * <p> + * Note that the stream may not block if additional bytes beyond the count + * returned by this method are read. + * + * @return The number of bytes that can be read before blocking could occur + * + * @exception IOException If an error occurs + */ + public int available() throws IOException + { + // We can only guarantee half the characters that might be available + // without blocking because "\r\n" is treated as a single character. + return in.available() / 2; + } + + /** + * This method returns the current line number + * + * @return The current line number + */ + public int getLineNumber() + { + return lineNumber; + } + + /** + * This method marks a position in the input to which the stream can + * be "reset" byte calling the <code>reset()</code> method. The + * parameter <code>readlimit</code> is the number of bytes that can + * be read from the stream after setting the mark before the mark + * becomes invalid. For example, if <code>mark()</code> is called + * with a read limit of 10, then when 11 bytes of data are read from + * the stream before the <code>reset()</code> method is called, then + * the mark is invalid and the stream object instance is not + * required to remember the mark. + * <p> + * In this class, this method will remember the current line number + * as well as the current position in the stream. When the + * <code>reset()</code> method is called, the line number will be + * restored to the saved line number in addition to the stream + * position. + * <p> + * This method only works if the subordinate stream supports mark/reset + * functionality. + * + * @param readlimit The number of bytes that can be read before the + * mark becomes invalid + */ + public void mark(int readlimit) + { + in.mark(readlimit); + markLineNumber = lineNumber; + } + + /** + * This method reads an unsigned byte from the input stream and returns it + * as an int in the range of 0-255. This method will return -1 if the + * end of the stream has been reached. + * <p> + * Note that if a line termination sequence is encountered (ie, "\r", + * "\n", or "\r\n") then that line termination sequence is converted to + * a single "\n" value which is returned from this method. This means + * that it is possible this method reads two bytes from the subordinate + * stream instead of just one. + * <p> + * Note that this method will block until a byte of data is available + * to be read. + * + * @return The byte read or -1 if end of stream + * + * @exception IOException If an error occurs + */ + public int read() throws IOException + { + // Treat "\r\n" as a single character. A '\r' may have been read by + // a previous call to read so we keep an internal flag to avoid having + // to read ahead. + + int ch = in.read(); + + if (ch == '\n') + if (justReadReturnChar) + { + ch = in.read(); + justReadReturnChar = false; + } + else + lineNumber++; + else if (ch == '\r') + { + ch = '\n'; + justReadReturnChar = true; + lineNumber++; + } + else + justReadReturnChar = false; + + return ch; + } + + /** + * This method reads bytes from a stream and stores them into a caller + * supplied buffer. It starts storing data at index <code>offset</code> into + * the buffer and attemps to read <code>len</code> bytes. This method can + * return before reading the number of bytes requested. The actual number + * of bytes read is returned as an int. A -1 is returned to indicated the + * end of the stream. + * <p> + * This method will block until some data can be read. + * <p> + * Note that if a line termination sequence is encountered (ie, "\r", + * "\n", or "\r\n") then that line termination sequence is converted to + * a single "\n" value which is stored in the buffer. Only a single + * byte is counted towards the number of bytes read in this case. + * + * @param b The array into which the bytes read should be stored + * @param off The offset into the array to start storing bytes + * @param len The requested number of bytes to read + * + * @return The actual number of bytes read, or -1 if end of stream + * + * @exception IOException If an error occurs. + */ + public int read(byte[] b, int off, int len) throws IOException + { + if (off < 0 || len < 0 || off + len > b.length) + throw new ArrayIndexOutOfBoundsException(); + + // This case always succeeds. + if (len == 0) + return 0; + + // The simplest, though not necessarily the most time efficient thing + // to do is simply call read(void) len times. Since this is a deprecated + // class, that should be ok. + final int origOff = off; + while (len-- > 0) + { + int ch = read(); + if (ch < 0) + break; + + b[off++] = (byte) ch; + } + + // This is safe since we already know that some bytes were + // actually requested. + return off == origOff ? -1 : off - origOff; + } + + /** + * This method resets a stream to the point where the + * <code>mark()</code> method was called. Any bytes that were read + * after the mark point was set will be re-read during subsequent + * reads. + * <p> + * In this class, this method will also restore the line number that was + * current when the <code>mark()</code> method was called. + * <p> + * This method only works if the subordinate stream supports mark/reset + * functionality. + * + * @exception IOException If an error occurs + */ + public void reset() throws IOException + { + in.reset(); + lineNumber = markLineNumber; + justReadReturnChar = false; + } + + /** + * This method sets the current line number to the specified value. + * + * @param lineNumber The new line number + */ + public void setLineNumber(int lineNumber) + { + this.lineNumber = lineNumber; + } + + /** + * This method skips up to the requested number of bytes in the + * input stream. The actual number of bytes skipped is returned. If the + * desired number of bytes to skip is negative, no bytes are skipped. + * + * @param n requested number of bytes to skip. + * + * @return The actual number of bytes skipped. + * + * @exception IOException If an error occurs. + */ + public long skip(long n) throws IOException + { + if (n <= 0) + return 0L; + + final long origN = n; + + do + { + int ch = read(); + if (ch < 0) + break; + if (ch == '\n' || ch == '\r') + lineNumber++; + } + while (--n > 0); + + return origN - n; + } +} diff --git a/libjava/classpath/java/io/LineNumberReader.java b/libjava/classpath/java/io/LineNumberReader.java new file mode 100644 index 00000000000..ea418a5e4d6 --- /dev/null +++ b/libjava/classpath/java/io/LineNumberReader.java @@ -0,0 +1,417 @@ +/* LineNumberReader.java -- A character input stream which counts line numbers + Copyright (C) 1998, 1999, 2001, 2003, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.io; + +/** + * This class functions like a standard <code>Reader</code> except that it + * counts line numbers, and canonicalizes newline characters. As data + * is read, whenever the char sequences "\r", "\n", or "\r\n" are encountered, + * the running line count is incremeted by one. Additionally, the whatever + * line termination sequence was encountered will be converted to a "\n" + * char. Note that this class numbers lines from 0. When the first + * line terminator is encountered, the line number is incremented to 1, and + * so on. Also note that actual "\r" and "\n" characters are looked for. + * The system dependent line separator sequence is ignored. + * <p> + * This class counts only line termination characters. If the last line + * read from the stream does not end in a line termination sequence, it + * will not be counted as a line. + * + * @author Per Bothner (bothner@cygnus.com) + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Guilhem Lavaux (guilhem@kaffe.org) + * @date December 28, 2003. + */ +/* Written using "Java Class Libraries", 2nd edition, plus online + * API docs for JDK 1.2 beta from http://www.javasoft.com. + * Status: Believed complete and correct. + * + * This implementation has the feature that if '\r' is read, it + * does not look for a '\n', but immediately returns '\n'. + * On the next read(), if a '\n' is read, it is skipped. + * This has the advantage that we do not read (and hang) unnecessarily. + * + * This implementation is also minimal in the number of fields it uses. + */ +public class LineNumberReader extends BufferedReader +{ + /** The current line number. */ + private int lineNumber; + /** Whether we already found a new line in the former call. */ + private boolean matchedNewLine; + /** The saved line number when calling mark() */ + private int savedLineNumber; + + /** + * Create a new <code>LineNumberReader</code> that reads from the + * specified subordinate <code>Reader</code>. A default 8K char sized + * buffer will be used for reads. + * + * @param in The subordinate <code>Reader</code> to read from + */ + public LineNumberReader(Reader in) + { + super(in, DEFAULT_BUFFER_SIZE); + } + + /** + * This method initializes a new <code>LineNumberReader</code> to read + * from the specified subordinate <code>Reader</code> using the specified + * read buffer size. + * + * @param in The subordinate <code>Reader</code> to read from + * @param size The buffer size to use for reading + */ + public LineNumberReader(Reader in, int size) + { + super(in, size); + } + + /** + * This method returns the current line number + * + * @return The current line number + */ + public int getLineNumber() + { + return lineNumber; + } + + /** + * This method sets the current line number to the specified value. + * + * @param line_number The new line number + */ + public void setLineNumber(int lineNumber) + { + this.lineNumber = lineNumber; + } + + /** + * This method marks a position in the input to which the stream can be + * "reset" char calling the <code>reset()</code> method. The parameter + * <code>readlimit</code> is the number of chars that can be read from the + * stream after setting the mark before the mark becomes invalid. For + * example, if <code>mark()</code> is called with a read limit of 10, + * then when + * 11 chars of data are read from the stream before the <code>reset()</code> + * method is called, then the mark is invalid and the stream object + * instance is not required to remember the mark. + * <p> + * In this class, this method will remember the current line number as well + * as the current position in the stream. When the <code>reset()</code> + * method + * is called, the line number will be restored to the saved line number in + * addition to the stream position. + * + * @param readlimit The number of chars that can be read before the + * mark becomes invalid + * + * @exception IOException If an error occurs + */ + public void mark(int readLimit) throws IOException + { + if (readLimit < 0) + throw new IllegalArgumentException("Read-ahead limit is negative"); + + synchronized (lock) + { + // This is basically the same as BufferedReader.mark. + // However, if the previous character was a '\r', we need to + // save that 'r', in case the next character is a '\n'. + if (pos + readLimit > limit) + { + int saveCR = matchedNewLine ? 1 : 0; + char[] old_buffer = buffer; + if (readLimit > limit) + buffer = new char[saveCR + readLimit]; + int copy_start = pos - saveCR; + savedLineNumber = lineNumber; + limit -= copy_start; + System.arraycopy(old_buffer, copy_start, buffer, 0, limit); + pos = saveCR; + } + markPos = pos; + } + } + + /** + * This method resets a stream to the point where the <code>mark()</code> + * method + * was called. Any chars that were read after the mark point was set will + * be re-read during subsequent reads. + * <p> + * In this class, this method will also restore the line number that was + * current when the <code>mark()</code> method was called. + * + * @exception IOException If an error occurs + */ + public void reset() throws IOException + { + synchronized (lock) + { + if (markPos < 0) + throw new IOException("mark never set or invalidated"); + lineNumber = savedLineNumber; + pos = markPos; + matchedNewLine = (markPos > 0 && buffer[markPos-1] == '\r'); + } + } + + /** + * This private method fills the input buffer whatever pos is. + * Consequently pos should be checked before calling this method. + * + * @return the number of bytes actually read from the input stream or + * -1 if end of stream. + * @exception IOException If an error occurs. + */ + private int fill() throws IOException + { + if (markPos >= 0 && limit == buffer.length) + markPos = -1; + if (markPos < 0) + pos = limit = 0; + int count = in.read(buffer, limit, buffer.length - limit); + if (count <= 0) + return -1; + limit += count; + + return count; + } + + /** + * This method reads an unsigned char from the input stream and returns it + * as an int in the range of 0-65535. This method will return -1 if the + * end of the stream has been reached. + * <p> + * Note that if a line termination sequence is encountered (ie, "\r", + * "\n", or "\r\n") then that line termination sequence is converted to + * a single "\n" value which is returned from this method. This means + * that it is possible this method reads two chars from the subordinate + * stream instead of just one. + * <p> + * Note that this method will block until a char of data is available + * to be read. + * + * @return The char read or -1 if end of stream + * + * @exception IOException If an error occurs + */ + public int read() throws IOException + { + synchronized (lock) + { + skipRedundantLF(); + if (pos >= limit && fill() < 0) + return -1; + char ch = buffer[pos++]; + + if ((matchedNewLine = (ch == '\r')) || ch == '\n') + { + lineNumber++; + return '\n'; + } + matchedNewLine = false; + return (int) ch; + } + } + + /** + * This method reads chars from a stream and stores them into a caller + * supplied buffer. It starts storing data at index <code>offset</code> into + * the buffer and attemps to read <code>len</code> chars. This method can + * return before reading the number of chars requested. The actual number + * of chars read is returned as an int. A -1 is returned to indicated the + * end of the stream. + * <p> + * This method will block until some data can be read. + * <p> + * Note that if a line termination sequence is encountered (ie, "\r", + * "\n", or "\r\n") then that line termination sequence is converted to + * a single "\n" value which is stored in the buffer. Only a single + * char is counted towards the number of chars read in this case. + * + * @param buf The array into which the chars read should be stored + * @param offset The offset into the array to start storing chars + * @param len The requested number of chars to read + * + * @return The actual number of chars read, or -1 if end of stream + * + * @exception IOException If an error occurs. + * @exception NullPointerException If buf is null (in any case). + * @exception IndexOutOfBoundsException If buffer parameters (offset and + * count) lies outside of the buffer capacity. + */ + public int read(char[] buf, int offset, int count) throws IOException + { + if (buf == null) + throw new NullPointerException(); + + if (offset + count > buf.length || offset < 0) + throw new IndexOutOfBoundsException(); + + if (count <= 0) + { + if (count < 0) + throw new IndexOutOfBoundsException(); + return 0; + } + + synchronized (lock) + { + if (pos >= limit && fill() < 0) + return -1; + + int start_offset = offset; + boolean matched = matchedNewLine; + + while (count-- > 0 && pos < limit) + { + char ch = buffer[pos++]; + if (ch == '\r') + { + lineNumber++; + matched = true; + } + else if (ch == '\n' && !matched) + lineNumber++; + else + matched = false; + + buf[offset++] = ch; + } + + matchedNewLine = matched; + return offset - start_offset; + } + } + + private void skipRedundantLF() throws IOException + { + if (pos > 0 && matchedNewLine) + { + if (pos < limit) + { // fast case + if (buffer[pos] == '\n') + pos++; + } + else + { // check whether the next buffer begins with '\n'. + // in that case kill the '\n'. + if (fill() <= 0) + return; + if (buffer[pos] == '\n') + pos++; + } + matchedNewLine = true; + } + } + + /** + * This method reads a line of text from the input stream and returns + * it as a <code>String</code>. A line is considered to be terminated + * by a "\r", "\n", or "\r\n" sequence, not by the system dependent line + * separator. + * + * @return The line read as a <code>String</code> or <code>null</code> + * if end of stream. + * + * @exception IOException If an error occurs + */ + public String readLine() throws IOException + { + // BufferedReader.readLine already does this. Shouldn't need to keep + // track of newlines (since the read method deals with this for us). + // But if the buffer is large, we may not call the read method at all + // and super.readLine can't increment lineNumber itself. + // Though it may seem kludgy, the safest thing to do is to save off + // lineNumber and increment it explicitly when we're done (iff we + // ended with a '\n' or '\r' as opposed to EOF). + // + // Also, we need to undo the special casing done by BufferedReader.readLine + // when a '\r' is the last char in the buffer. That situation is marked + // by 'pos > limit'. + int tmpLineNumber = lineNumber; + skipRedundantLF(); + String str = super.readLine(); + if (pos > limit) + --pos; + + // The only case where you mustn't increment the line number is you are + // at the EOS. + if (str != null) + lineNumber = tmpLineNumber + 1; + + return str; + } + + /** + * This method skips over characters in the stream. This method will + * skip the specified number of characters if possible, but is not required + * to skip them all. The actual number of characters skipped is returned. + * This method returns 0 if the specified number of chars is less than 1. + * + * @param count The specified number of chars to skip. + * + * @return The actual number of chars skipped. + * + * @exception IOException If an error occurs + */ + public long skip (long count) throws IOException + { + if (count < 0) + throw new IllegalArgumentException("skip() value is negative"); + if (count == 0) + return 0; + + int skipped; + char[] buf = new char[1]; + + for (skipped = 0; skipped < count; skipped++) + { + int ch = read(buf, 0, 1); + + if (ch < 0) + break; + } + + return skipped; + } +} + diff --git a/libjava/classpath/java/io/NotActiveException.java b/libjava/classpath/java/io/NotActiveException.java new file mode 100644 index 00000000000..949ba8eca52 --- /dev/null +++ b/libjava/classpath/java/io/NotActiveException.java @@ -0,0 +1,72 @@ +/* NotActiveException.java -- thrown when serialization is not active + Copyright (C) 1998, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This exception is thrown when a problem occurs due to the fact that + * serialization is not active. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @since 1.1 + * @status updated to 1.4 + */ +public class NotActiveException extends ObjectStreamException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -3893467273049808895L; + + /** + * Create an exception without a descriptive error message. + */ + public NotActiveException() + { + } + + /** + * Create an exception with a descriptive error message. + * + * @param message the descriptive error message + */ + public NotActiveException(String message) + { + super(message); + } +} // class NotActiveException diff --git a/libjava/classpath/java/io/NotSerializableException.java b/libjava/classpath/java/io/NotSerializableException.java new file mode 100644 index 00000000000..d49c939e31d --- /dev/null +++ b/libjava/classpath/java/io/NotSerializableException.java @@ -0,0 +1,74 @@ +/* NotSerializableException.java -- a Serializable class that isn't + Copyright (C) 1998, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This exception is thrown when a class implements Serializable because + * of a superclass, but should not be serialized. The descriptive message + * will consist of the name of the class in question. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @since 1.1 + * @status updated to 1.4 + */ +public class NotSerializableException extends ObjectStreamException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 2906642554793891381L; + + /** + * Create an exception without a descriptive error message. + */ + public NotSerializableException() + { + } + + /** + * Create an exception with a descriptive error message, which should + * be the name of the class. + * + * @param message the descriptive error message + */ + public NotSerializableException(String message) + { + super(message); + } +} // class NotSerializableException diff --git a/libjava/classpath/java/io/ObjectInput.java b/libjava/classpath/java/io/ObjectInput.java new file mode 100644 index 00000000000..175b60f9dc0 --- /dev/null +++ b/libjava/classpath/java/io/ObjectInput.java @@ -0,0 +1,140 @@ +/* ObjectInput.java -- Read object data from a stream + Copyright (C) 1998,2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This interface extends the <code>DataInput</code> interface to provide a + * facility to read objects as well as primitive types from a stream. It + * also has methods that allow input to be done in a manner similar to + * <code>InputStream</code> + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * + * @see DataInput + */ +public interface ObjectInput extends DataInput +{ + /** + * This method returns the number of bytes that can be read without + * blocking. + * + * @return The number of bytes available before blocking + * + * @exception IOException If an error occurs + */ + int available() throws IOException; + + /** + * This method reading a byte of data from a stream. It returns that byte + * as an <code>int</code>. This method blocks if no data is available + * to be read. + * + * @return The byte of data read + * + * @exception IOException If an error occurs + */ + int read() throws IOException; + + /** + * This method reads raw bytes and stores them them a byte array buffer. + * Note that this method will block if no data is available. However, + * it will not necessarily block until it fills the entire buffer. That is, + * a "short count" is possible. + * + * @param buf The byte array to receive the data read + * + * @return The actual number of bytes read or -1 if end of stream + * + * @exception IOException If an error occurs + */ + int read(byte[] buf) throws IOException; + + /** + * This method reads raw bytes and stores them in a byte array buffer + * <code>buf</code> starting at position <code>offset</code> into the + * buffer. A + * maximum of <code>len</code> bytes will be read. Note that this method + * blocks if no data is available, but will not necessarily block until + * it can read <code>len</code> bytes of data. That is, a "short count" is + * possible. + * + * @param buf The byte array to receive the data read + * @param offset The offset into <code>buf</code> to start storing data + * @param len The maximum number of bytes to read + * + * @return The actual number of bytes read or -1 if end of stream + * + * @exception IOException If an error occurs + */ + int read(byte[] buf, int offset, int len) throws IOException; + + /** + * Reads an object instance and returns it. If the class for the object + * being read cannot be found, then a <code>ClassNotFoundException</code> + * will be thrown. + * + * @return The object instance that was read + * + * @exception ClassNotFoundException If a class for the object cannot be + * found + * @exception IOException If any other error occurs + */ + Object readObject() + throws ClassNotFoundException, IOException; + + /** + * This method causes the specified number of bytes to be read and + * discarded. It is possible that fewer than the requested number of bytes + * will actually be skipped. + * + * @param numBytes The number of bytes to skip + * + * @return The actual number of bytes skipped + * + * @exception IOException If an error occurs + */ + long skip(long numBytes) throws IOException; + + /** + * This method closes the input source + * + * @exception IOException If an error occurs + */ + void close() throws IOException; +} diff --git a/libjava/classpath/java/io/ObjectInputStream.java b/libjava/classpath/java/io/ObjectInputStream.java new file mode 100644 index 00000000000..05776a7fcdd --- /dev/null +++ b/libjava/classpath/java/io/ObjectInputStream.java @@ -0,0 +1,1956 @@ +/* ObjectInputStream.java -- Class used to read serialized objects + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2005 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +import gnu.classpath.Configuration; +import gnu.java.io.ObjectIdentityWrapper; + +import java.lang.reflect.Array; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.Proxy; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.Arrays; +import java.util.Hashtable; +import java.util.Vector; + +public class ObjectInputStream extends InputStream + implements ObjectInput, ObjectStreamConstants +{ + /** + * Creates a new <code>ObjectInputStream</code> that will do all of + * its reading from <code>in</code>. This method also checks + * the stream by reading the header information (stream magic number + * and stream version). + * + * @exception IOException Reading stream header from underlying + * stream cannot be completed. + * + * @exception StreamCorruptedException An invalid stream magic + * number or stream version was read from the stream. + * + * @see #readStreamHeader() + */ + public ObjectInputStream(InputStream in) + throws IOException, StreamCorruptedException + { + if (DEBUG) + { + String val = System.getProperty("gcj.dumpobjects"); + if (dump == false && val != null && !val.equals("")) + { + dump = true; + System.out.println ("Serialization debugging enabled"); + } + else if (dump == true && (val == null || val.equals(""))) + { + dump = false; + System.out.println ("Serialization debugging disabled"); + } + } + + this.resolveEnabled = false; + this.isDeserializing = false; + this.blockDataPosition = 0; + this.blockDataBytes = 0; + this.blockData = new byte[BUFFER_SIZE]; + this.blockDataInput = new DataInputStream(this); + this.realInputStream = new DataInputStream(in); + this.nextOID = baseWireHandle; + this.objectLookupTable = new Hashtable(); + this.validators = new Vector(); + this.classLookupTable = new Hashtable(); + setBlockDataMode(true); + readStreamHeader(); + } + + + /** + * Returns the next deserialized object read from the underlying stream. + * + * This method can be overriden by a class by implementing + * <code>private void readObject (ObjectInputStream)</code>. + * + * If an exception is thrown from this method, the stream is left in + * an undefined state. + * + * @exception ClassNotFoundException The class that an object being + * read in belongs to cannot be found. + * + * @exception IOException Exception from underlying + * <code>InputStream</code>. + */ + public final Object readObject() throws ClassNotFoundException, IOException + { + if (this.useSubclassMethod) + return readObjectOverride(); + + boolean was_deserializing; + + Object ret_val; + was_deserializing = this.isDeserializing; + + boolean is_consumed = false; + boolean old_mode = setBlockDataMode(false); + + this.isDeserializing = true; + + byte marker = this.realInputStream.readByte(); + + depth += 2; + + if(dump) dumpElement("MARKER: 0x" + Integer.toHexString(marker) + " "); + + try + { + switch (marker) + { + case TC_ENDBLOCKDATA: + { + ret_val = null; + is_consumed = true; + break; + } + + case TC_BLOCKDATA: + case TC_BLOCKDATALONG: + { + if (marker == TC_BLOCKDATALONG) + { if(dump) dumpElementln("BLOCKDATALONG"); } + else + { if(dump) dumpElementln("BLOCKDATA"); } + readNextBlock(marker); + throw new StreamCorruptedException("Unexpected blockData"); + } + + case TC_NULL: + { + if(dump) dumpElementln("NULL"); + ret_val = null; + break; + } + + case TC_REFERENCE: + { + if(dump) dumpElement("REFERENCE "); + Integer oid = new Integer(this.realInputStream.readInt()); + if(dump) dumpElementln(Integer.toHexString(oid.intValue())); + ret_val = ((ObjectIdentityWrapper) + this.objectLookupTable.get(oid)).object; + break; + } + + case TC_CLASS: + { + if(dump) dumpElementln("CLASS"); + ObjectStreamClass osc = (ObjectStreamClass)readObject(); + Class clazz = osc.forClass(); + assignNewHandle(clazz); + ret_val = clazz; + break; + } + + case TC_PROXYCLASSDESC: + { + if(dump) dumpElementln("PROXYCLASS"); + int n_intf = this.realInputStream.readInt(); + String[] intfs = new String[n_intf]; + for (int i = 0; i < n_intf; i++) + { + intfs[i] = this.realInputStream.readUTF(); + System.out.println(intfs[i]); + } + + boolean oldmode = setBlockDataMode(true); + Class cl = resolveProxyClass(intfs); + setBlockDataMode(oldmode); + + ObjectStreamClass osc = lookupClass(cl); + assignNewHandle(osc); + + if (!is_consumed) + { + byte b = this.realInputStream.readByte(); + if (b != TC_ENDBLOCKDATA) + throw new IOException("Data annotated to class was not consumed." + b); + } + else + is_consumed = false; + ObjectStreamClass superosc = (ObjectStreamClass)readObject(); + osc.setSuperclass(superosc); + ret_val = osc; + break; + } + + case TC_CLASSDESC: + { + ObjectStreamClass osc = readClassDescriptor(); + + if (!is_consumed) + { + byte b = this.realInputStream.readByte(); + if (b != TC_ENDBLOCKDATA) + throw new IOException("Data annotated to class was not consumed." + b); + } + else + is_consumed = false; + + osc.setSuperclass ((ObjectStreamClass)readObject()); + ret_val = osc; + break; + } + + case TC_STRING: + case TC_LONGSTRING: + { + if(dump) dumpElement("STRING="); + String s = this.realInputStream.readUTF(); + if(dump) dumpElementln(s); + ret_val = processResolution(null, s, assignNewHandle(s)); + break; + } + + case TC_ARRAY: + { + if(dump) dumpElementln("ARRAY"); + ObjectStreamClass osc = (ObjectStreamClass)readObject(); + Class componentType = osc.forClass().getComponentType(); + if(dump) dumpElement("ARRAY LENGTH="); + int length = this.realInputStream.readInt(); + if(dump) dumpElementln (length + "; COMPONENT TYPE=" + componentType); + Object array = Array.newInstance(componentType, length); + int handle = assignNewHandle(array); + readArrayElements(array, componentType); + if(dump) + for (int i = 0, len = Array.getLength(array); i < len; i++) + dumpElementln(" ELEMENT[" + i + "]=" + Array.get(array, i)); + ret_val = processResolution(null, array, handle); + break; + } + + case TC_OBJECT: + { + if(dump) dumpElementln("OBJECT"); + ObjectStreamClass osc = (ObjectStreamClass)readObject(); + Class clazz = osc.forClass(); + + if (!osc.realClassIsSerializable) + throw new NotSerializableException + (clazz + " is not Serializable, and thus cannot be deserialized."); + + if (osc.realClassIsExternalizable) + { + Externalizable obj = osc.newInstance(); + + int handle = assignNewHandle(obj); + + boolean read_from_blocks = ((osc.getFlags() & SC_BLOCK_DATA) != 0); + + boolean oldmode = this.readDataFromBlock; + if (read_from_blocks) + setBlockDataMode(true); + + obj.readExternal(this); + + if (read_from_blocks) + { + setBlockDataMode(oldmode); + if (!oldmode) + if (this.realInputStream.readByte() != TC_ENDBLOCKDATA) + throw new IOException("No end of block data seen for class with readExternal (ObjectInputStream) method."); + } + + ret_val = processResolution(osc, obj, handle); + break; + } // end if (osc.realClassIsExternalizable) + + Object obj = newObject(clazz, osc.firstNonSerializableParentConstructor); + + int handle = assignNewHandle(obj); + Object prevObject = this.currentObject; + ObjectStreamClass prevObjectStreamClass = this.currentObjectStreamClass; + + this.currentObject = obj; + ObjectStreamClass[] hierarchy = + inputGetObjectStreamClasses(clazz); + + for (int i = 0; i < hierarchy.length; i++) + { + this.currentObjectStreamClass = hierarchy[i]; + + if(dump) dumpElementln("Reading fields of " + this.currentObjectStreamClass.getName ()); + + // XXX: should initialize fields in classes in the hierarchy + // that aren't in the stream + // should skip over classes in the stream that aren't in the + // real classes hierarchy + + Method readObjectMethod = this.currentObjectStreamClass.readObjectMethod; + if (readObjectMethod != null) + { + fieldsAlreadyRead = false; + boolean oldmode = setBlockDataMode(true); + callReadMethod(readObjectMethod, this.currentObjectStreamClass.forClass(), obj); + setBlockDataMode(oldmode); + } + else + { + readFields(obj, currentObjectStreamClass); + } + + if (this.currentObjectStreamClass.hasWriteMethod()) + { + if(dump) dumpElement("ENDBLOCKDATA? "); + try + { + // FIXME: XXX: This try block is to + // catch EOF which is thrown for some + // objects. That indicates a bug in + // the logic. + + if (this.realInputStream.readByte() != TC_ENDBLOCKDATA) + throw new IOException + ("No end of block data seen for class with readObject (ObjectInputStream) method."); + if(dump) dumpElementln("yes"); + } +// catch (EOFException e) +// { +// if(dump) dumpElementln("no, got EOFException"); +// } + catch (IOException e) + { + if(dump) dumpElementln("no, got IOException"); + } + } + } + + this.currentObject = prevObject; + this.currentObjectStreamClass = prevObjectStreamClass; + ret_val = processResolution(osc, obj, handle); + + break; + } + + case TC_RESET: + if(dump) dumpElementln("RESET"); + clearHandles(); + ret_val = readObject(); + break; + + case TC_EXCEPTION: + { + if(dump) dumpElement("EXCEPTION="); + Exception e = (Exception)readObject(); + if(dump) dumpElementln(e.toString()); + clearHandles(); + throw new WriteAbortedException("Exception thrown during writing of stream", e); + } + + default: + throw new IOException("Unknown marker on stream: " + marker); + } + } + finally + { + setBlockDataMode(old_mode); + + this.isDeserializing = was_deserializing; + + depth -= 2; + + if (! was_deserializing) + { + if (validators.size() > 0) + invokeValidators(); + } + } + + return ret_val; + } + + /** + * This method makes a partial check of types for the fields + * contained given in arguments. It checks primitive types of + * fields1 against non primitive types of fields2. This method + * assumes the two lists has already been sorted according to + * the Java specification. + * + * @param name Name of the class owning the given fields. + * @param fields1 First list to check. + * @param fields2 Second list to check. + * @throws InvalidClassException if a field in fields1, which has a primitive type, is a present + * in the non primitive part in fields2. + */ + private void checkTypeConsistency(String name, ObjectStreamField[] fields1, ObjectStreamField[] fields2) + throws InvalidClassException + { + int nonPrimitive = 0; + + for (nonPrimitive = 0; + nonPrimitive < fields1.length + && fields1[nonPrimitive].isPrimitive(); nonPrimitive++) + { + } + + if (nonPrimitive == fields1.length) + return; + + int i = 0; + ObjectStreamField f1; + ObjectStreamField f2; + + while (i < fields2.length + && nonPrimitive < fields1.length) + { + f1 = fields1[nonPrimitive]; + f2 = fields2[i]; + + if (!f2.isPrimitive()) + break; + + int compVal = f1.getName().compareTo (f2.getName()); + + if (compVal < 0) + { + nonPrimitive++; + } + else if (compVal > 0) + { + i++; + } + else + { + throw new InvalidClassException + ("invalid field type for " + f2.getName() + + " in class " + name); + } + } + } + + /** + * This method reads a class descriptor from the real input stream + * and use these data to create a new instance of ObjectStreamClass. + * Fields are sorted and ordered for the real read which occurs for + * each instance of the described class. Be aware that if you call that + * method you must ensure that the stream is synchronized, in the other + * case it may be completely desynchronized. + * + * @return A new instance of ObjectStreamClass containing the freshly + * created descriptor. + * @throws ClassNotFoundException if the required class to build the + * descriptor has not been found in the system. + * @throws IOException An input/output error occured. + * @throws InvalidClassException If there was a compatibility problem + * between the class present in the system and the serialized class. + */ + protected ObjectStreamClass readClassDescriptor() + throws ClassNotFoundException, IOException + { + if(dump) dumpElement("CLASSDESC NAME="); + String name = this.realInputStream.readUTF(); + if(dump) dumpElement(name + "; UID="); + long uid = this.realInputStream.readLong (); + if(dump) dumpElement(Long.toHexString(uid) + "; FLAGS="); + byte flags = this.realInputStream.readByte (); + if(dump) dumpElement(Integer.toHexString(flags) + "; FIELD COUNT="); + short field_count = this.realInputStream.readShort(); + if(dump) dumpElementln(Short.toString(field_count)); + ObjectStreamField[] fields = new ObjectStreamField[field_count]; + ObjectStreamClass osc = new ObjectStreamClass(name, uid, + flags, fields); + assignNewHandle(osc); + + if (callersClassLoader == null) + callersClassLoader = currentLoader(); + + for (int i = 0; i < field_count; i++) + { + if(dump) dumpElement(" TYPE CODE="); + char type_code = (char)this.realInputStream.readByte(); + if(dump) dumpElement(type_code + "; FIELD NAME="); + String field_name = this.realInputStream.readUTF(); + if(dump) dumpElementln(field_name); + String class_name; + + // If the type code is an array or an object we must + // decode a String here. In the other case we convert + // the type code and pass it to ObjectStreamField. + // Type codes are decoded by gnu.java.lang.reflect.TypeSignature. + if (type_code == 'L' || type_code == '[') + class_name = (String)readObject(); + else + class_name = String.valueOf(type_code); + + fields[i] = + new ObjectStreamField(field_name, class_name, callersClassLoader); + } + + /* Now that fields have been read we may resolve the class + * (and read annotation if needed). */ + Class clazz; + try + { + clazz = resolveClass(osc); + } + catch (ClassNotFoundException cnfe) + { + // Maybe it was an primitive class? + if (name.equals("void")) + clazz = Void.TYPE; + else if (name.equals("boolean")) + clazz = Boolean.TYPE; + else if (name.equals("byte")) + clazz = Byte.TYPE; + else if (name.equals("short")) + clazz = Short.TYPE; + else if (name.equals("char")) + clazz = Character.TYPE; + else if (name.equals("int")) + clazz = Integer.TYPE; + else if (name.equals("long")) + clazz = Long.TYPE; + else if (name.equals("float")) + clazz = Float.TYPE; + else if (name.equals("double")) + clazz = Double.TYPE; + else + throw cnfe; + } + + boolean oldmode = setBlockDataMode(true); + osc.setClass(clazz, lookupClass(clazz.getSuperclass())); + classLookupTable.put(clazz, osc); + setBlockDataMode(oldmode); + + // find the first non-serializable, non-abstract + // class in clazz's inheritance hierarchy + Class first_nonserial = clazz.getSuperclass(); + // Maybe it is a primitive class, those don't have a super class, + // or Object itself. Otherwise we can keep getting the superclass + // till we hit the Object class, or some other non-serializable class. + + if (first_nonserial == null) + first_nonserial = clazz; + else + while (Serializable.class.isAssignableFrom(first_nonserial) + || Modifier.isAbstract(first_nonserial.getModifiers())) + first_nonserial = first_nonserial.getSuperclass(); + + final Class local_constructor_class = first_nonserial; + + osc.firstNonSerializableParentConstructor = + (Constructor)AccessController.doPrivileged(new PrivilegedAction() + { + public Object run() + { + try + { + Constructor c = local_constructor_class. + getDeclaredConstructor(new Class[0]); + if (Modifier.isPrivate(c.getModifiers())) + return null; + return c; + } + catch (NoSuchMethodException e) + { + // error will be reported later, in newObject() + return null; + } + } + }); + + osc.realClassIsSerializable = Serializable.class.isAssignableFrom(clazz); + osc.realClassIsExternalizable = Externalizable.class.isAssignableFrom(clazz); + + ObjectStreamField[] stream_fields = osc.fields; + ObjectStreamField[] real_fields = ObjectStreamClass.lookupForClassObject(clazz).fields; + ObjectStreamField[] fieldmapping = new ObjectStreamField[2 * Math.max(stream_fields.length, real_fields.length)]; + + int stream_idx = 0; + int real_idx = 0; + int map_idx = 0; + + /* + * Check that there is no type inconsistencies between the lists. + * A special checking must be done for the two groups: primitive types and + * not primitive types. + */ + checkTypeConsistency(name, real_fields, stream_fields); + checkTypeConsistency(name, stream_fields, real_fields); + + + while (stream_idx < stream_fields.length + || real_idx < real_fields.length) + { + ObjectStreamField stream_field = null; + ObjectStreamField real_field = null; + + if (stream_idx == stream_fields.length) + { + real_field = real_fields[real_idx++]; + } + else if (real_idx == real_fields.length) + { + stream_field = stream_fields[stream_idx++]; + } + else + { + int comp_val = + real_fields[real_idx].compareTo (stream_fields[stream_idx]); + + if (comp_val < 0) + { + real_field = real_fields[real_idx++]; + } + else if (comp_val > 0) + { + stream_field = stream_fields[stream_idx++]; + } + else + { + stream_field = stream_fields[stream_idx++]; + real_field = real_fields[real_idx++]; + if (stream_field.getType() != real_field.getType()) + throw new InvalidClassException + ("invalid field type for " + real_field.getName() + + " in class " + name); + } + } + + /* If some of stream_fields does not correspond to any of real_fields, + * or the opposite, then fieldmapping will go short. + */ + if (map_idx == fieldmapping.length) + { + ObjectStreamField[] newfieldmapping = + new ObjectStreamField[fieldmapping.length + 2]; + System.arraycopy(fieldmapping, 0, + newfieldmapping, 0, fieldmapping.length); + fieldmapping = newfieldmapping; + } + fieldmapping[map_idx++] = stream_field; + fieldmapping[map_idx++] = real_field; + } + osc.fieldMapping = fieldmapping; + + return osc; + } + + /** + * Reads the current objects non-transient, non-static fields from + * the current class from the underlying output stream. + * + * This method is intended to be called from within a object's + * <code>private void readObject (ObjectInputStream)</code> + * method. + * + * @exception ClassNotFoundException The class that an object being + * read in belongs to cannot be found. + * + * @exception NotActiveException This method was called from a + * context other than from the current object's and current class's + * <code>private void readObject (ObjectInputStream)</code> + * method. + * + * @exception IOException Exception from underlying + * <code>OutputStream</code>. + */ + public void defaultReadObject() + throws ClassNotFoundException, IOException, NotActiveException + { + if (this.currentObject == null || this.currentObjectStreamClass == null) + throw new NotActiveException("defaultReadObject called by non-active" + + " class and/or object"); + + if (fieldsAlreadyRead) + throw new NotActiveException("defaultReadObject called but fields " + + "already read from stream (by " + + "defaultReadObject or readFields)"); + + boolean oldmode = setBlockDataMode(false); + readFields(this.currentObject, this.currentObjectStreamClass); + setBlockDataMode(oldmode); + + fieldsAlreadyRead = true; + } + + + /** + * Registers a <code>ObjectInputValidation</code> to be carried out + * on the object graph currently being deserialized before it is + * returned to the original caller of <code>readObject ()</code>. + * The order of validation for multiple + * <code>ObjectInputValidation</code>s can be controled using + * <code>priority</code>. Validators with higher priorities are + * called first. + * + * @see java.io.ObjectInputValidation + * + * @exception InvalidObjectException <code>validator</code> is + * <code>null</code> + * + * @exception NotActiveException an attempt was made to add a + * validator outside of the <code>readObject</code> method of the + * object currently being deserialized + */ + public void registerValidation(ObjectInputValidation validator, + int priority) + throws InvalidObjectException, NotActiveException + { + if (this.currentObject == null || this.currentObjectStreamClass == null) + throw new NotActiveException("registerValidation called by non-active " + + "class and/or object"); + + if (validator == null) + throw new InvalidObjectException("attempt to add a null " + + "ObjectInputValidation object"); + + this.validators.addElement(new ValidatorAndPriority (validator, + priority)); + } + + + /** + * Called when a class is being deserialized. This is a hook to + * allow subclasses to read in information written by the + * <code>annotateClass (Class)</code> method of an + * <code>ObjectOutputStream</code>. + * + * This implementation looks up the active call stack for a + * <code>ClassLoader</code>; if a <code>ClassLoader</code> is found, + * it is used to load the class associated with <code>osc</code>, + * otherwise, the default system <code>ClassLoader</code> is used. + * + * @exception IOException Exception from underlying + * <code>OutputStream</code>. + * + * @see java.io.ObjectOutputStream#annotateClass (java.lang.Class) + */ + protected Class resolveClass(ObjectStreamClass osc) + throws ClassNotFoundException, IOException + { + if (callersClassLoader == null) + { + callersClassLoader = currentLoader (); + if (DEBUG && dump) + { + dumpElementln ("CallersClassLoader = " + callersClassLoader); + } + } + + return Class.forName(osc.getName(), true, callersClassLoader); + } + + /** + * Returns the most recent user defined ClassLoader on the execution stack + * or null if none is found. + */ + private ClassLoader currentLoader() + { + return VMObjectInputStream.currentClassLoader(); + } + + /** + * Lookup a class stored in the local hashtable. If it is not + * use the global lookup function in ObjectStreamClass to build + * the ObjectStreamClass. This method is requested according to + * the behaviour detected in the JDK by Kaffe's team. + * + * @param clazz Class to lookup in the hash table or for which + * we must build a descriptor. + * @return A valid instance of ObjectStreamClass corresponding + * to the specified class. + */ + private ObjectStreamClass lookupClass(Class clazz) + { + if (clazz == null) + return null; + + ObjectStreamClass oclazz; + oclazz = (ObjectStreamClass)classLookupTable.get(clazz); + if (oclazz == null) + return ObjectStreamClass.lookup(clazz); + else + return oclazz; + } + + /** + * Reconstruct class hierarchy the same way + * {@link java.io.ObjectStreamClass.getObjectStreamClasses(java.lang.Class)} does + * but using lookupClass instead of ObjectStreamClass.lookup. This + * dup is necessary localize the lookup table. Hopefully some future + * rewritings will be able to prevent this. + * + * @param clazz This is the class for which we want the hierarchy. + * + * @return An array of valid {@link java.io.ObjectStreamClass} instances which + * represent the class hierarchy for clazz. + */ + private ObjectStreamClass[] inputGetObjectStreamClasses(Class clazz) + { + ObjectStreamClass osc = lookupClass(clazz); + + if (osc == null) + return new ObjectStreamClass[0]; + else + { + Vector oscs = new Vector(); + + while (osc != null) + { + oscs.addElement(osc); + osc = osc.getSuper(); + } + + int count = oscs.size(); + ObjectStreamClass[] sorted_oscs = new ObjectStreamClass[count]; + + for (int i = count - 1; i >= 0; i--) + sorted_oscs[count - i - 1] = (ObjectStreamClass) oscs.elementAt(i); + + return sorted_oscs; + } + } + + /** + * Allows subclasses to resolve objects that are read from the + * stream with other objects to be returned in their place. This + * method is called the first time each object is encountered. + * + * This method must be enabled before it will be called in the + * serialization process. + * + * @exception IOException Exception from underlying + * <code>OutputStream</code>. + * + * @see #enableResolveObject(boolean) + */ + protected Object resolveObject(Object obj) throws IOException + { + return obj; + } + + + protected Class resolveProxyClass(String[] intfs) + throws IOException, ClassNotFoundException + { + ClassLoader cl = currentLoader(); + + Class[] clss = new Class[intfs.length]; + if(cl == null) + { + for (int i = 0; i < intfs.length; i++) + clss[i] = Class.forName(intfs[i]); + cl = ClassLoader.getSystemClassLoader(); + } + else + for (int i = 0; i < intfs.length; i++) + clss[i] = cl.loadClass(intfs[i]); + try + { + return Proxy.getProxyClass(cl, clss); + } + catch (IllegalArgumentException e) + { + throw new ClassNotFoundException(null, e); + } + } + + /** + * If <code>enable</code> is <code>true</code> and this object is + * trusted, then <code>resolveObject (Object)</code> will be called + * in subsequent calls to <code>readObject (Object)</code>. + * Otherwise, <code>resolveObject (Object)</code> will not be called. + * + * @exception SecurityException This class is not trusted. + */ + protected boolean enableResolveObject (boolean enable) + throws SecurityException + { + if (enable) + { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkPermission(new SerializablePermission("enableSubstitution")); + } + + boolean old_val = this.resolveEnabled; + this.resolveEnabled = enable; + return old_val; + } + + /** + * Reads stream magic and stream version information from the + * underlying stream. + * + * @exception IOException Exception from underlying stream. + * + * @exception StreamCorruptedException An invalid stream magic + * number or stream version was read from the stream. + */ + protected void readStreamHeader() + throws IOException, StreamCorruptedException + { + if(dump) dumpElement("STREAM MAGIC "); + if (this.realInputStream.readShort() != STREAM_MAGIC) + throw new StreamCorruptedException("Invalid stream magic number"); + + if(dump) dumpElementln("STREAM VERSION "); + if (this.realInputStream.readShort() != STREAM_VERSION) + throw new StreamCorruptedException("Invalid stream version number"); + } + + public int read() throws IOException + { + if (this.readDataFromBlock) + { + if (this.blockDataPosition >= this.blockDataBytes) + readNextBlock(); + return (this.blockData[this.blockDataPosition++] & 0xff); + } + else + return this.realInputStream.read(); + } + + public int read(byte[] data, int offset, int length) throws IOException + { + if (this.readDataFromBlock) + { + if (this.blockDataPosition + length > this.blockDataBytes) + { + int remain = this.blockDataBytes - this.blockDataPosition; + if (remain != 0) + { + System.arraycopy(this.blockData, this.blockDataPosition, + data, offset, remain); + offset += remain; + length -= remain; + } + readNextBlock (); + } + + System.arraycopy(this.blockData, this.blockDataPosition, + data, offset, length); + this.blockDataPosition += length; + + return length; + } + else + return this.realInputStream.read(data, offset, length); + } + + public int available() throws IOException + { + if (this.readDataFromBlock) + { + if (this.blockDataPosition >= this.blockDataBytes) + readNextBlock (); + + return this.blockDataBytes - this.blockDataPosition; + } + else + return this.realInputStream.available(); + } + + public void close() throws IOException + { + this.realInputStream.close(); + } + + public boolean readBoolean() throws IOException + { + boolean switchmode = true; + boolean oldmode = this.readDataFromBlock; + if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 1) + switchmode = false; + if (switchmode) + oldmode = setBlockDataMode (true); + boolean value = this.dataInputStream.readBoolean (); + if (switchmode) + setBlockDataMode (oldmode); + return value; + } + + public byte readByte() throws IOException + { + boolean switchmode = true; + boolean oldmode = this.readDataFromBlock; + if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 1) + switchmode = false; + if (switchmode) + oldmode = setBlockDataMode(true); + byte value = this.dataInputStream.readByte(); + if (switchmode) + setBlockDataMode(oldmode); + return value; + } + + public int readUnsignedByte() throws IOException + { + boolean switchmode = true; + boolean oldmode = this.readDataFromBlock; + if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 1) + switchmode = false; + if (switchmode) + oldmode = setBlockDataMode(true); + int value = this.dataInputStream.readUnsignedByte(); + if (switchmode) + setBlockDataMode(oldmode); + return value; + } + + public short readShort() throws IOException + { + boolean switchmode = true; + boolean oldmode = this.readDataFromBlock; + if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 2) + switchmode = false; + if (switchmode) + oldmode = setBlockDataMode(true); + short value = this.dataInputStream.readShort(); + if (switchmode) + setBlockDataMode(oldmode); + return value; + } + + public int readUnsignedShort() throws IOException + { + boolean switchmode = true; + boolean oldmode = this.readDataFromBlock; + if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 2) + switchmode = false; + if (switchmode) + oldmode = setBlockDataMode(true); + int value = this.dataInputStream.readUnsignedShort(); + if (switchmode) + setBlockDataMode(oldmode); + return value; + } + + public char readChar() throws IOException + { + boolean switchmode = true; + boolean oldmode = this.readDataFromBlock; + if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 2) + switchmode = false; + if (switchmode) + oldmode = setBlockDataMode(true); + char value = this.dataInputStream.readChar(); + if (switchmode) + setBlockDataMode(oldmode); + return value; + } + + public int readInt() throws IOException + { + boolean switchmode = true; + boolean oldmode = this.readDataFromBlock; + if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 4) + switchmode = false; + if (switchmode) + oldmode = setBlockDataMode(true); + int value = this.dataInputStream.readInt(); + if (switchmode) + setBlockDataMode(oldmode); + return value; + } + + public long readLong() throws IOException + { + boolean switchmode = true; + boolean oldmode = this.readDataFromBlock; + if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 8) + switchmode = false; + if (switchmode) + oldmode = setBlockDataMode(true); + long value = this.dataInputStream.readLong(); + if (switchmode) + setBlockDataMode(oldmode); + return value; + } + + public float readFloat() throws IOException + { + boolean switchmode = true; + boolean oldmode = this.readDataFromBlock; + if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 4) + switchmode = false; + if (switchmode) + oldmode = setBlockDataMode(true); + float value = this.dataInputStream.readFloat(); + if (switchmode) + setBlockDataMode(oldmode); + return value; + } + + public double readDouble() throws IOException + { + boolean switchmode = true; + boolean oldmode = this.readDataFromBlock; + if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 8) + switchmode = false; + if (switchmode) + oldmode = setBlockDataMode(true); + double value = this.dataInputStream.readDouble(); + if (switchmode) + setBlockDataMode(oldmode); + return value; + } + + public void readFully(byte data[]) throws IOException + { + this.dataInputStream.readFully(data); + } + + public void readFully(byte data[], int offset, int size) + throws IOException + { + this.dataInputStream.readFully(data, offset, size); + } + + public int skipBytes(int len) throws IOException + { + return this.dataInputStream.skipBytes(len); + } + + /** + * @deprecated + * @see java.io.DataInputStream#readLine () + */ + public String readLine() throws IOException + { + return this.dataInputStream.readLine(); + } + + public String readUTF() throws IOException + { + return this.dataInputStream.readUTF(); + } + + /** + * This class allows a class to specify exactly which fields should + * be read, and what values should be read for these fields. + * + * XXX: finish up comments + */ + public abstract static class GetField + { + public abstract ObjectStreamClass getObjectStreamClass(); + + public abstract boolean defaulted(String name) + throws IOException, IllegalArgumentException; + + public abstract boolean get(String name, boolean defvalue) + throws IOException, IllegalArgumentException; + + public abstract char get(String name, char defvalue) + throws IOException, IllegalArgumentException; + + public abstract byte get(String name, byte defvalue) + throws IOException, IllegalArgumentException; + + public abstract short get(String name, short defvalue) + throws IOException, IllegalArgumentException; + + public abstract int get(String name, int defvalue) + throws IOException, IllegalArgumentException; + + public abstract long get(String name, long defvalue) + throws IOException, IllegalArgumentException; + + public abstract float get(String name, float defvalue) + throws IOException, IllegalArgumentException; + + public abstract double get(String name, double defvalue) + throws IOException, IllegalArgumentException; + + public abstract Object get(String name, Object defvalue) + throws IOException, IllegalArgumentException; + } + + /** + * This method should be called by a method called 'readObject' in the + * deserializing class (if present). It cannot (and should not)be called + * outside of it. Its goal is to read all fields in the real input stream + * and keep them accessible through the {@link #GetField} class. Calling + * this method will not alter the deserializing object. + * + * @return A valid freshly created 'GetField' instance to get access to + * the deserialized stream. + * @throws IOException An input/output exception occured. + * @throws ClassNotFoundException + * @throws NotActiveException + */ + public GetField readFields() + throws IOException, ClassNotFoundException, NotActiveException + { + if (this.currentObject == null || this.currentObjectStreamClass == null) + throw new NotActiveException("readFields called by non-active class and/or object"); + + if (prereadFields != null) + return prereadFields; + + if (fieldsAlreadyRead) + throw new NotActiveException("readFields called but fields already read from" + + " stream (by defaultReadObject or readFields)"); + + final ObjectStreamClass clazz = this.currentObjectStreamClass; + final byte[] prim_field_data = new byte[clazz.primFieldSize]; + final Object[] objs = new Object[clazz.objectFieldCount]; + + // Apparently Block data is not used with GetField as per + // empirical evidence against JDK 1.2. Also see Mauve test + // java.io.ObjectInputOutput.Test.GetPutField. + boolean oldmode = setBlockDataMode(false); + readFully(prim_field_data); + for (int i = 0; i < objs.length; ++ i) + objs[i] = readObject(); + setBlockDataMode(oldmode); + + prereadFields = new GetField() + { + public ObjectStreamClass getObjectStreamClass() + { + return clazz; + } + + public boolean defaulted(String name) + throws IOException, IllegalArgumentException + { + ObjectStreamField f = clazz.getField(name); + + /* First if we have a serialized field use the descriptor */ + if (f != null) + { + /* It is in serialPersistentFields but setClass tells us + * it should not be set. This value is defaulted. + */ + if (f.isPersistent() && !f.isToSet()) + return true; + + return false; + } + + /* This is not a serialized field. There should be + * a default value only if the field really exists. + */ + try + { + return (clazz.forClass().getDeclaredField (name) != null); + } + catch (NoSuchFieldException e) + { + throw new IllegalArgumentException(e.getMessage()); + } + } + + public boolean get(String name, boolean defvalue) + throws IOException, IllegalArgumentException + { + ObjectStreamField field = getField(name, Boolean.TYPE); + + if (field == null) + return defvalue; + + return prim_field_data[field.getOffset()] == 0 ? false : true; + } + + public char get(String name, char defvalue) + throws IOException, IllegalArgumentException + { + ObjectStreamField field = getField(name, Character.TYPE); + + if (field == null) + return defvalue; + + int off = field.getOffset(); + + return (char)(((prim_field_data[off++] & 0xFF) << 8) + | (prim_field_data[off] & 0xFF)); + } + + public byte get(String name, byte defvalue) + throws IOException, IllegalArgumentException + { + ObjectStreamField field = getField(name, Byte.TYPE); + + if (field == null) + return defvalue; + + return prim_field_data[field.getOffset()]; + } + + public short get(String name, short defvalue) + throws IOException, IllegalArgumentException + { + ObjectStreamField field = getField(name, Short.TYPE); + + if (field == null) + return defvalue; + + int off = field.getOffset(); + + return (short)(((prim_field_data[off++] & 0xFF) << 8) + | (prim_field_data[off] & 0xFF)); + } + + public int get(String name, int defvalue) + throws IOException, IllegalArgumentException + { + ObjectStreamField field = getField(name, Integer.TYPE); + + if (field == null) + return defvalue; + + int off = field.getOffset(); + + return ((prim_field_data[off++] & 0xFF) << 24) + | ((prim_field_data[off++] & 0xFF) << 16) + | ((prim_field_data[off++] & 0xFF) << 8) + | (prim_field_data[off] & 0xFF); + } + + public long get(String name, long defvalue) + throws IOException, IllegalArgumentException + { + ObjectStreamField field = getField(name, Long.TYPE); + + if (field == null) + return defvalue; + + int off = field.getOffset(); + + return (long)(((prim_field_data[off++] & 0xFFL) << 56) + | ((prim_field_data[off++] & 0xFFL) << 48) + | ((prim_field_data[off++] & 0xFFL) << 40) + | ((prim_field_data[off++] & 0xFFL) << 32) + | ((prim_field_data[off++] & 0xFF) << 24) + | ((prim_field_data[off++] & 0xFF) << 16) + | ((prim_field_data[off++] & 0xFF) << 8) + | (prim_field_data[off] & 0xFF)); + } + + public float get(String name, float defvalue) + throws IOException, IllegalArgumentException + { + ObjectStreamField field = getField(name, Float.TYPE); + + if (field == null) + return defvalue; + + int off = field.getOffset(); + + return Float.intBitsToFloat(((prim_field_data[off++] & 0xFF) << 24) + | ((prim_field_data[off++] & 0xFF) << 16) + | ((prim_field_data[off++] & 0xFF) << 8) + | (prim_field_data[off] & 0xFF)); + } + + public double get(String name, double defvalue) + throws IOException, IllegalArgumentException + { + ObjectStreamField field = getField(name, Double.TYPE); + + if (field == null) + return defvalue; + + int off = field.getOffset(); + + return Double.longBitsToDouble + ( (long) (((prim_field_data[off++] & 0xFFL) << 56) + | ((prim_field_data[off++] & 0xFFL) << 48) + | ((prim_field_data[off++] & 0xFFL) << 40) + | ((prim_field_data[off++] & 0xFFL) << 32) + | ((prim_field_data[off++] & 0xFF) << 24) + | ((prim_field_data[off++] & 0xFF) << 16) + | ((prim_field_data[off++] & 0xFF) << 8) + | (prim_field_data[off] & 0xFF))); + } + + public Object get(String name, Object defvalue) + throws IOException, IllegalArgumentException + { + ObjectStreamField field = + getField(name, defvalue == null ? null : defvalue.getClass ()); + + if (field == null) + return defvalue; + + return objs[field.getOffset()]; + } + + private ObjectStreamField getField(String name, Class type) + throws IllegalArgumentException + { + ObjectStreamField field = clazz.getField(name); + boolean illegal = false; + + try + { + try + { + Class field_type = field.getType(); + + if (type == field_type || + (type == null && !field_type.isPrimitive())) + { + /* See defaulted */ + return field; + } + + illegal = true; + throw new IllegalArgumentException + ("Field requested is of type " + + field_type.getName() + + ", but requested type was " + + (type == null ? "Object" : type.getName())); + } + catch (NullPointerException _) + { + /* Here we catch NullPointerException, because it may + only come from the call 'field.getType()'. If field + is null, we have to return null and classpath ethic + say we must try to avoid 'if (xxx == null)'. + */ + } + catch (IllegalArgumentException e) + { + throw e; + } + + return null; + } + finally + { + /* If this is an unassigned field we should return + * the default value. + */ + if (!illegal && field != null && !field.isToSet() && field.isPersistent()) + return null; + + /* We do not want to modify transient fields. They should + * be left to 0. + */ + try + { + Field f = clazz.forClass().getDeclaredField(name); + if (Modifier.isTransient(f.getModifiers())) + throw new IllegalArgumentException + ("no such field (non transient) " + name); + if (field == null && f.getType() != type) + throw new IllegalArgumentException + ("Invalid requested type for field " + name); + } + catch (NoSuchFieldException e) + { + if (field == null) + throw new IllegalArgumentException(e.getMessage()); + } + + } + } + }; + + fieldsAlreadyRead = true; + return prereadFields; + } + + /** + * Protected constructor that allows subclasses to override + * deserialization. This constructor should be called by subclasses + * that wish to override <code>readObject (Object)</code>. This + * method does a security check <i>NOTE: currently not + * implemented</i>, then sets a flag that informs + * <code>readObject (Object)</code> to call the subclasses + * <code>readObjectOverride (Object)</code> method. + * + * @see #readObjectOverride() + */ + protected ObjectInputStream() + throws IOException, SecurityException + { + SecurityManager sec_man = System.getSecurityManager(); + if (sec_man != null) + sec_man.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION); + this.useSubclassMethod = true; + } + + /** + * This method allows subclasses to override the default + * de serialization mechanism provided by + * <code>ObjectInputStream</code>. To make this method be used for + * writing objects, subclasses must invoke the 0-argument + * constructor on this class from their constructor. + * + * @see #ObjectInputStream() + */ + protected Object readObjectOverride() + throws ClassNotFoundException, IOException, OptionalDataException + { + throw new IOException("Subclass of ObjectInputStream must implement readObjectOverride"); + } + + /** + * Assigns the next available handle to <code>obj</code>. + * + * @param obj The object for which we want a new handle. + * @return A valid handle for the specified object. + */ + private int assignNewHandle(Object obj) + { + this.objectLookupTable.put(new Integer(this.nextOID), + new ObjectIdentityWrapper(obj)); + return this.nextOID++; + } + + private Object processResolution(ObjectStreamClass osc, Object obj, int handle) + throws IOException + { + if (osc != null && obj instanceof Serializable) + { + try + { + Method m = osc.readResolveMethod; + if(m != null) + { + obj = m.invoke(obj, new Object[] {}); + } + } + catch (IllegalAccessException ignore) + { + } + catch (InvocationTargetException ignore) + { + } + } + + if (this.resolveEnabled) + obj = resolveObject(obj); + + this.objectLookupTable.put(new Integer(handle), + new ObjectIdentityWrapper(obj)); + + return obj; + } + + private void clearHandles() + { + this.objectLookupTable.clear(); + this.nextOID = baseWireHandle; + } + + private void readNextBlock() throws IOException + { + readNextBlock(this.realInputStream.readByte()); + } + + private void readNextBlock(byte marker) throws IOException + { + if (marker == TC_BLOCKDATA) + { + if(dump) dumpElement("BLOCK DATA SIZE="); + this.blockDataBytes = this.realInputStream.readUnsignedByte(); + if(dump) dumpElementln (Integer.toString(this.blockDataBytes)); + } + else if (marker == TC_BLOCKDATALONG) + { + if(dump) dumpElement("BLOCK DATA LONG SIZE="); + this.blockDataBytes = this.realInputStream.readInt(); + if(dump) dumpElementln (Integer.toString(this.blockDataBytes)); + } + else + { + throw new EOFException("Attempt to read primitive data, but no data block is active."); + } + + if (this.blockData.length < this.blockDataBytes) + this.blockData = new byte[this.blockDataBytes]; + + this.realInputStream.readFully (this.blockData, 0, this.blockDataBytes); + this.blockDataPosition = 0; + } + + private void readArrayElements (Object array, Class clazz) + throws ClassNotFoundException, IOException + { + if (clazz.isPrimitive()) + { + if (clazz == Boolean.TYPE) + { + boolean[] cast_array = (boolean[])array; + for (int i=0; i < cast_array.length; i++) + cast_array[i] = this.realInputStream.readBoolean(); + return; + } + if (clazz == Byte.TYPE) + { + byte[] cast_array = (byte[])array; + for (int i=0; i < cast_array.length; i++) + cast_array[i] = this.realInputStream.readByte(); + return; + } + if (clazz == Character.TYPE) + { + char[] cast_array = (char[])array; + for (int i=0; i < cast_array.length; i++) + cast_array[i] = this.realInputStream.readChar(); + return; + } + if (clazz == Double.TYPE) + { + double[] cast_array = (double[])array; + for (int i=0; i < cast_array.length; i++) + cast_array[i] = this.realInputStream.readDouble(); + return; + } + if (clazz == Float.TYPE) + { + float[] cast_array = (float[])array; + for (int i=0; i < cast_array.length; i++) + cast_array[i] = this.realInputStream.readFloat(); + return; + } + if (clazz == Integer.TYPE) + { + int[] cast_array = (int[])array; + for (int i=0; i < cast_array.length; i++) + cast_array[i] = this.realInputStream.readInt(); + return; + } + if (clazz == Long.TYPE) + { + long[] cast_array = (long[])array; + for (int i=0; i < cast_array.length; i++) + cast_array[i] = this.realInputStream.readLong(); + return; + } + if (clazz == Short.TYPE) + { + short[] cast_array = (short[])array; + for (int i=0; i < cast_array.length; i++) + cast_array[i] = this.realInputStream.readShort(); + return; + } + } + else + { + Object[] cast_array = (Object[])array; + for (int i=0; i < cast_array.length; i++) + cast_array[i] = readObject(); + } + } + + private void readFields (Object obj, ObjectStreamClass stream_osc) + throws ClassNotFoundException, IOException + { + ObjectStreamField[] fields = stream_osc.fieldMapping; + + for (int i = 0; i < fields.length; i += 2) + { + ObjectStreamField stream_field = fields[i]; + ObjectStreamField real_field = fields[i + 1]; + boolean read_value = (stream_field != null && stream_field.getOffset() >= 0 && stream_field.isToSet()); + boolean set_value = (real_field != null && real_field.isToSet()); + String field_name; + char type; + + if (stream_field != null) + { + field_name = stream_field.getName(); + type = stream_field.getTypeCode(); + } + else + { + field_name = real_field.getName(); + type = real_field.getTypeCode(); + } + + switch(type) + { + case 'Z': + { + boolean value = + read_value ? this.realInputStream.readBoolean() : false; + if (dump && read_value && set_value) + dumpElementln(" " + field_name + ": " + value); + if (set_value) + real_field.setBooleanField(obj, value); + break; + } + case 'B': + { + byte value = + read_value ? this.realInputStream.readByte() : 0; + if (dump && read_value && set_value) + dumpElementln(" " + field_name + ": " + value); + if (set_value) + real_field.setByteField(obj, value); + break; + } + case 'C': + { + char value = + read_value ? this.realInputStream.readChar(): 0; + if (dump && read_value && set_value) + dumpElementln(" " + field_name + ": " + value); + if (set_value) + real_field.setCharField(obj, value); + break; + } + case 'D': + { + double value = + read_value ? this.realInputStream.readDouble() : 0; + if (dump && read_value && set_value) + dumpElementln(" " + field_name + ": " + value); + if (set_value) + real_field.setDoubleField(obj, value); + break; + } + case 'F': + { + float value = + read_value ? this.realInputStream.readFloat() : 0; + if (dump && read_value && set_value) + dumpElementln(" " + field_name + ": " + value); + if (set_value) + real_field.setFloatField(obj, value); + break; + } + case 'I': + { + int value = + read_value ? this.realInputStream.readInt() : 0; + if (dump && read_value && set_value) + dumpElementln(" " + field_name + ": " + value); + if (set_value) + real_field.setIntField(obj, value); + break; + } + case 'J': + { + long value = + read_value ? this.realInputStream.readLong() : 0; + if (dump && read_value && set_value) + dumpElementln(" " + field_name + ": " + value); + if (set_value) + real_field.setLongField(obj, value); + break; + } + case 'S': + { + short value = + read_value ? this.realInputStream.readShort() : 0; + if (dump && read_value && set_value) + dumpElementln(" " + field_name + ": " + value); + if (set_value) + real_field.setShortField(obj, value); + break; + } + case 'L': + case '[': + { + Object value = + read_value ? readObject() : null; + if (set_value) + real_field.setObjectField(obj, value); + break; + } + default: + throw new InternalError("Invalid type code: " + type); + } + } + } + + // Toggles writing primitive data to block-data buffer. + private boolean setBlockDataMode (boolean on) + { + boolean oldmode = this.readDataFromBlock; + this.readDataFromBlock = on; + + if (on) + this.dataInputStream = this.blockDataInput; + else + this.dataInputStream = this.realInputStream; + return oldmode; + } + + // returns a new instance of REAL_CLASS that has been constructed + // only to the level of CONSTRUCTOR_CLASS (a super class of REAL_CLASS) + private Object newObject (Class real_class, Constructor constructor) + throws ClassNotFoundException, IOException + { + if (constructor == null) + throw new InvalidClassException("Missing accessible no-arg base class constructor for " + real_class.getName()); + try + { + return VMObjectInputStream.allocateObject(real_class, constructor.getDeclaringClass(), constructor); + } + catch (InstantiationException e) + { + throw new ClassNotFoundException + ("Instance of " + real_class + " could not be created"); + } + } + + // runs all registered ObjectInputValidations in prioritized order + // on OBJ + private void invokeValidators() throws InvalidObjectException + { + Object[] validators = new Object[this.validators.size()]; + this.validators.copyInto (validators); + Arrays.sort (validators); + + try + { + for (int i=0; i < validators.length; i++) + ((ObjectInputValidation)validators[i]).validateObject(); + } + finally + { + this.validators.removeAllElements(); + } + } + + private void callReadMethod (Method readObject, Class klass, Object obj) + throws ClassNotFoundException, IOException + { + try + { + readObject.invoke(obj, new Object[] { this }); + } + catch (InvocationTargetException x) + { + /* Rethrow if possible. */ + Throwable exception = x.getTargetException(); + if (exception instanceof RuntimeException) + throw (RuntimeException) exception; + if (exception instanceof IOException) + throw (IOException) exception; + if (exception instanceof ClassNotFoundException) + throw (ClassNotFoundException) exception; + + throw new IOException("Exception thrown from readObject() on " + + klass + ": " + exception.getClass().getName()); + } + catch (Exception x) + { + throw new IOException("Failure invoking readObject() on " + + klass + ": " + x.getClass().getName()); + } + + // Invalidate fields which has been read through readFields. + prereadFields = null; + } + + private static final int BUFFER_SIZE = 1024; + + private DataInputStream realInputStream; + private DataInputStream dataInputStream; + private DataInputStream blockDataInput; + private int blockDataPosition; + private int blockDataBytes; + private byte[] blockData; + private boolean useSubclassMethod; + private int nextOID; + private boolean resolveEnabled; + private Hashtable objectLookupTable; + private Object currentObject; + private ObjectStreamClass currentObjectStreamClass; + private boolean readDataFromBlock; + private boolean isDeserializing; + private boolean fieldsAlreadyRead; + private Vector validators; + private Hashtable classLookupTable; + private GetField prereadFields; + + private ClassLoader callersClassLoader; + private static boolean dump; + + // The nesting depth for debugging output + private int depth = 0; + + private static final boolean DEBUG = false; + + private void dumpElement (String msg) + { + System.out.print(msg); + } + + private void dumpElementln (String msg) + { + System.out.println(msg); + for (int i = 0; i < depth; i++) + System.out.print (" "); + System.out.print (Thread.currentThread() + ": "); + } + + static + { + if (Configuration.INIT_LOAD_LIBRARY) + { + System.loadLibrary ("javaio"); + } + } + + // used to keep a prioritized list of object validators + private static final class ValidatorAndPriority implements Comparable + { + int priority; + ObjectInputValidation validator; + + ValidatorAndPriority (ObjectInputValidation validator, int priority) + { + this.priority = priority; + this.validator = validator; + } + + public int compareTo (Object o) + { + ValidatorAndPriority vap = (ValidatorAndPriority)o; + return this.priority - vap.priority; + } + } +} + diff --git a/libjava/classpath/java/io/ObjectInputValidation.java b/libjava/classpath/java/io/ObjectInputValidation.java new file mode 100644 index 00000000000..4fdb8414f63 --- /dev/null +++ b/libjava/classpath/java/io/ObjectInputValidation.java @@ -0,0 +1,67 @@ +/* ObjectInputValidation.java -- Validate an object + Copyright (C) 1998, 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This class allows an object to validate that it is valid after + * deserialization has run completely for it and all dependent objects. + * This allows an object to determine if it is invalid even if all + * state data was correctly deserialized from the stream. It can also + * be used to perform re-initialization type activities on an object + * after it has been completely deserialized. + * + * Since this method functions as a type of callback, it must be + * registered through <code>ObjectInputStream.registerValidation</code> + * in order to be invoked. This is typically done in the + * <code>readObject</code> method. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * + * @see ObjectInputStream#registerValidation + */ +public interface ObjectInputValidation +{ + /** + * This method is called to validate an object after serialization + * is complete. If the object is invalid an exception is thrown. + * + * @exception InvalidObjectException If the object is invalid + */ + void validateObject() throws InvalidObjectException; +} diff --git a/libjava/classpath/java/io/ObjectOutput.java b/libjava/classpath/java/io/ObjectOutput.java new file mode 100644 index 00000000000..d35a09c3acb --- /dev/null +++ b/libjava/classpath/java/io/ObjectOutput.java @@ -0,0 +1,111 @@ +/* ObjectOutput.java -- Interface for writing objects to a stream + Copyright (C) 1998, 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This interface extends <code>DataOutput</code> to provide the additional + * facility of writing object instances to a stream. It also adds some + * additional methods to make the interface more + * <code>OutputStream</code> like. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * + * @see DataOutput + */ +public interface ObjectOutput extends DataOutput +{ + /** + * This method writes the specified byte to the output stream. + * + * @param b The byte to write. + * + * @exception IOException If an error occurs. + */ + void write(int b) throws IOException; + + /** + * This method writes all the bytes in the specified byte array to the + * output stream. + * + * @param buf The array of bytes to write. + * + * @exception IOException If an error occurs. + */ + void write(byte[] buf) throws IOException; + + /** + * This method writes <code>len</code> bytes from the specified array + * starting at index <code>offset</code> into that array. + * + * @param buf The byte array to write from. + * @param offset The index into the byte array to start writing from. + * @param len The number of bytes to write. + * + * @exception IOException If an error occurs. + */ + void write(byte[] buf, int offset, int len) + throws IOException; + + /** + * This method writes a object instance to a stream. The format of the + * data written is determined by the actual implementation of this method + * + * @param obj The object to write + * + * @exception IOException If an error occurs + */ + void writeObject(Object obj) throws IOException; + + /** + * This method causes any buffered data to be flushed out to the underlying + * stream + * + * @exception IOException If an error occurs + */ + void flush() throws IOException; + + /** + * This method closes the underlying stream. + * + * @exception IOException If an error occurs + */ + void close() throws IOException; + +} // interface ObjectOutput + diff --git a/libjava/classpath/java/io/ObjectOutputStream.java b/libjava/classpath/java/io/ObjectOutputStream.java new file mode 100644 index 00000000000..5e754c5ec7a --- /dev/null +++ b/libjava/classpath/java/io/ObjectOutputStream.java @@ -0,0 +1,1578 @@ +/* ObjectOutputStream.java -- Class used to write serialized objects + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +import gnu.classpath.Configuration; +import gnu.java.io.ObjectIdentityWrapper; +import gnu.java.lang.reflect.TypeSignature; +import gnu.java.security.action.SetAccessibleAction; + +import java.lang.reflect.Array; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.security.AccessController; +import java.util.Hashtable; + +/** + * An <code>ObjectOutputStream</code> can be used to write objects + * as well as primitive data in a platform-independent manner to an + * <code>OutputStream</code>. + * + * The data produced by an <code>ObjectOutputStream</code> can be read + * and reconstituted by an <code>ObjectInputStream</code>. + * + * <code>writeObject (Object)</code> is used to write Objects, the + * <code>write<type></code> methods are used to write primitive + * data (as in <code>DataOutputStream</code>). Strings can be written + * as objects or as primitive data. + * + * Not all objects can be written out using an + * <code>ObjectOutputStream</code>. Only those objects that are an + * instance of <code>java.io.Serializable</code> can be written. + * + * Using default serialization, information about the class of an + * object is written, all of the non-transient, non-static fields of + * the object are written, if any of these fields are objects, they are + * written out in the same manner. + * + * An object is only written out the first time it is encountered. If + * the object is encountered later, a reference to it is written to + * the underlying stream. Thus writing circular object graphs + * does not present a problem, nor are relationships between objects + * in a graph lost. + * + * Example usage: + * <pre> + * Hashtable map = new Hashtable (); + * map.put ("one", new Integer (1)); + * map.put ("two", new Integer (2)); + * + * ObjectOutputStream oos = + * new ObjectOutputStream (new FileOutputStream ("numbers")); + * oos.writeObject (map); + * oos.close (); + * + * ObjectInputStream ois = + * new ObjectInputStream (new FileInputStream ("numbers")); + * Hashtable newmap = (Hashtable)ois.readObject (); + * + * System.out.println (newmap); + * </pre> + * + * The default serialization can be overriden in two ways. + * + * By defining a method <code>private void + * writeObject (ObjectOutputStream)</code>, a class can dictate exactly + * how information about itself is written. + * <code>defaultWriteObject ()</code> may be called from this method to + * carry out default serialization. This method is not + * responsible for dealing with fields of super-classes or subclasses. + * + * By implementing <code>java.io.Externalizable</code>. This gives + * the class complete control over the way it is written to the + * stream. If this approach is used the burden of writing superclass + * and subclass data is transfered to the class implementing + * <code>java.io.Externalizable</code>. + * + * @see java.io.DataOutputStream + * @see java.io.Externalizable + * @see java.io.ObjectInputStream + * @see java.io.Serializable + */ +public class ObjectOutputStream extends OutputStream + implements ObjectOutput, ObjectStreamConstants +{ + /** + * Creates a new <code>ObjectOutputStream</code> that will do all of + * its writing onto <code>out</code>. This method also initializes + * the stream by writing the header information (stream magic number + * and stream version). + * + * @exception IOException Writing stream header to underlying + * stream cannot be completed. + * + * @see #writeStreamHeader() + */ + public ObjectOutputStream (OutputStream out) throws IOException + { + realOutput = new DataOutputStream(out); + blockData = new byte[ BUFFER_SIZE ]; + blockDataCount = 0; + blockDataOutput = new DataOutputStream(this); + setBlockDataMode(true); + replacementEnabled = false; + isSerializing = false; + nextOID = baseWireHandle; + OIDLookupTable = new Hashtable(); + protocolVersion = defaultProtocolVersion; + useSubclassMethod = false; + writeStreamHeader(); + + if (DEBUG) + { + String val = System.getProperty("gcj.dumpobjects"); + if (val != null && !val.equals("")) + dump = true; + } + } + + /** + * Writes a representation of <code>obj</code> to the underlying + * output stream by writing out information about its class, then + * writing out each of the objects non-transient, non-static + * fields. If any of these fields are other objects, + * they are written out in the same manner. + * + * This method can be overriden by a class by implementing + * <code>private void writeObject (ObjectOutputStream)</code>. + * + * If an exception is thrown from this method, the stream is left in + * an undefined state. + * + * @exception NotSerializableException An attempt was made to + * serialize an <code>Object</code> that is not serializable. + * + * @exception InvalidClassException Somebody tried to serialize + * an object which is wrongly formatted. + * + * @exception IOException Exception from underlying + * <code>OutputStream</code>. + */ + public final void writeObject(Object obj) throws IOException + { + if (useSubclassMethod) + { + if (dump) + dumpElementln ("WRITE OVERRIDE: " + obj); + + writeObjectOverride(obj); + return; + } + + if (dump) + dumpElementln ("WRITE: " + obj); + + depth += 2; + + boolean was_serializing = isSerializing; + boolean old_mode = setBlockDataMode(false); + try + { + isSerializing = true; + boolean replaceDone = false; + Object replacedObject = null; + + while (true) + { + if (obj == null) + { + realOutput.writeByte(TC_NULL); + break; + } + + Integer handle = findHandle(obj); + if (handle != null) + { + realOutput.writeByte(TC_REFERENCE); + realOutput.writeInt(handle.intValue()); + break; + } + + if (obj instanceof Class) + { + Class cl = (Class)obj; + ObjectStreamClass osc = ObjectStreamClass.lookupForClassObject(cl); + realOutput.writeByte(TC_CLASS); + if (!osc.isProxyClass) + { + writeObject (osc); + } + else + { + realOutput.writeByte(TC_PROXYCLASSDESC); + Class[] intfs = cl.getInterfaces(); + realOutput.writeInt(intfs.length); + for (int i = 0; i < intfs.length; i++) + realOutput.writeUTF(intfs[i].getName()); + + boolean oldmode = setBlockDataMode(true); + annotateProxyClass(cl); + setBlockDataMode(oldmode); + realOutput.writeByte(TC_ENDBLOCKDATA); + + writeObject(osc.getSuper()); + } + assignNewHandle(obj); + break; + } + + if (obj instanceof ObjectStreamClass) + { + writeClassDescriptor((ObjectStreamClass) obj); + break; + } + + Class clazz = obj.getClass(); + ObjectStreamClass osc = ObjectStreamClass.lookupForClassObject(clazz); + if (osc == null) + throw new NotSerializableException(clazz.getName()); + + if ((replacementEnabled || obj instanceof Serializable) + && ! replaceDone) + { + replacedObject = obj; + + if (obj instanceof Serializable) + { + try + { + Method m = osc.writeReplaceMethod; + if (m != null) + obj = m.invoke(obj, new Object[0]); + } + catch (IllegalAccessException ignore) + { + } + catch (InvocationTargetException ignore) + { + } + } + + if (replacementEnabled) + obj = replaceObject(obj); + + replaceDone = true; + continue; + } + + if (obj instanceof String) + { + realOutput.writeByte(TC_STRING); + assignNewHandle(obj); + realOutput.writeUTF((String)obj); + break; + } + + if (clazz.isArray ()) + { + realOutput.writeByte(TC_ARRAY); + writeObject(osc); + assignNewHandle(obj); + writeArraySizeAndElements(obj, clazz.getComponentType()); + break; + } + + realOutput.writeByte(TC_OBJECT); + writeObject(osc); + + if (replaceDone) + assignNewHandle(replacedObject); + else + assignNewHandle(obj); + + if (obj instanceof Externalizable) + { + if (protocolVersion == PROTOCOL_VERSION_2) + setBlockDataMode(true); + + ((Externalizable)obj).writeExternal(this); + + if (protocolVersion == PROTOCOL_VERSION_2) + { + setBlockDataMode(false); + realOutput.writeByte(TC_ENDBLOCKDATA); + } + + break; + } + + if (obj instanceof Serializable) + { + Object prevObject = this.currentObject; + ObjectStreamClass prevObjectStreamClass = this.currentObjectStreamClass; + currentObject = obj; + ObjectStreamClass[] hierarchy = + ObjectStreamClass.getObjectStreamClasses(clazz); + + for (int i = 0; i < hierarchy.length; i++) + { + currentObjectStreamClass = hierarchy[i]; + + fieldsAlreadyWritten = false; + if (currentObjectStreamClass.hasWriteMethod()) + { + if (dump) + dumpElementln ("WRITE METHOD CALLED FOR: " + obj); + setBlockDataMode(true); + callWriteMethod(obj, currentObjectStreamClass); + setBlockDataMode(false); + realOutput.writeByte(TC_ENDBLOCKDATA); + if (dump) + dumpElementln ("WRITE ENDBLOCKDATA FOR: " + obj); + } + else + { + if (dump) + dumpElementln ("WRITE FIELDS CALLED FOR: " + obj); + writeFields(obj, currentObjectStreamClass); + } + } + + this.currentObject = prevObject; + this.currentObjectStreamClass = prevObjectStreamClass; + currentPutField = null; + break; + } + + throw new NotSerializableException(clazz.getName ()); + } // end pseudo-loop + } + catch (ObjectStreamException ose) + { + // Rethrow these are fatal. + throw ose; + } + catch (IOException e) + { + realOutput.writeByte(TC_EXCEPTION); + reset(true); + + setBlockDataMode(false); + try + { + if (DEBUG) + { + e.printStackTrace(System.out); + } + writeObject(e); + } + catch (IOException ioe) + { + StreamCorruptedException ex = + new StreamCorruptedException + (ioe + " thrown while exception was being written to stream."); + if (DEBUG) + { + ex.printStackTrace(System.out); + } + throw ex; + } + + reset (true); + + } + finally + { + isSerializing = was_serializing; + setBlockDataMode(old_mode); + depth -= 2; + + if (dump) + dumpElementln ("END: " + obj); + } + } + + protected void writeClassDescriptor(ObjectStreamClass osc) throws IOException + { + realOutput.writeByte(TC_CLASSDESC); + realOutput.writeUTF(osc.getName()); + realOutput.writeLong(osc.getSerialVersionUID()); + assignNewHandle(osc); + + int flags = osc.getFlags(); + + if (protocolVersion == PROTOCOL_VERSION_2 + && osc.isExternalizable()) + flags |= SC_BLOCK_DATA; + + realOutput.writeByte(flags); + + ObjectStreamField[] fields = osc.fields; + realOutput.writeShort(fields.length); + + ObjectStreamField field; + for (int i = 0; i < fields.length; i++) + { + field = fields[i]; + realOutput.writeByte(field.getTypeCode ()); + realOutput.writeUTF(field.getName ()); + + if (! field.isPrimitive()) + writeObject(field.getTypeString()); + } + + boolean oldmode = setBlockDataMode(true); + annotateClass(osc.forClass()); + setBlockDataMode(oldmode); + realOutput.writeByte(TC_ENDBLOCKDATA); + + if (osc.isSerializable() || osc.isExternalizable()) + writeObject(osc.getSuper()); + else + writeObject(null); + } + + /** + * Writes the current objects non-transient, non-static fields from + * the current class to the underlying output stream. + * + * This method is intended to be called from within a object's + * <code>private void writeObject (ObjectOutputStream)</code> + * method. + * + * @exception NotActiveException This method was called from a + * context other than from the current object's and current class's + * <code>private void writeObject (ObjectOutputStream)</code> + * method. + * + * @exception IOException Exception from underlying + * <code>OutputStream</code>. + */ + public void defaultWriteObject() + throws IOException, NotActiveException + { + markFieldsWritten(); + writeFields(currentObject, currentObjectStreamClass); + } + + + private void markFieldsWritten() throws IOException + { + if (currentObject == null || currentObjectStreamClass == null) + throw new NotActiveException + ("defaultWriteObject called by non-active class and/or object"); + + if (fieldsAlreadyWritten) + throw new IOException + ("Only one of writeFields and defaultWriteObject may be called, and it may only be called once"); + + fieldsAlreadyWritten = true; + } + + /** + * Resets stream to state equivalent to the state just after it was + * constructed. + * + * Causes all objects previously written to the stream to be + * forgotten. A notification of this reset is also written to the + * underlying stream. + * + * @exception IOException Exception from underlying + * <code>OutputStream</code> or reset called while serialization is + * in progress. + */ + public void reset() throws IOException + { + reset(false); + } + + + private void reset(boolean internal) throws IOException + { + if (!internal) + { + if (isSerializing) + throw new IOException("Reset called while serialization in progress"); + + realOutput.writeByte(TC_RESET); + } + + clearHandles(); + } + + + /** + * Informs this <code>ObjectOutputStream</code> to write data + * according to the specified protocol. There are currently two + * different protocols, specified by <code>PROTOCOL_VERSION_1</code> + * and <code>PROTOCOL_VERSION_2</code>. This implementation writes + * data using <code>PROTOCOL_VERSION_2</code> by default, as is done + * by the JDK 1.2. + * + * A non-portable method, <code>setDefaultProtocolVersion (int + * version)</code> is provided to change the default protocol + * version. + * + * For an explination of the differences beween the two protocols + * see XXX: the Java ObjectSerialization Specification. + * + * @exception IOException if <code>version</code> is not a valid + * protocol + * + * @see #setDefaultProtocolVersion(int) + */ + public void useProtocolVersion(int version) throws IOException + { + if (version != PROTOCOL_VERSION_1 && version != PROTOCOL_VERSION_2) + throw new IOException("Invalid protocol version requested."); + + protocolVersion = version; + } + + + /** + * <em>GNU $classpath specific</em> + * + * Changes the default stream protocol used by all + * <code>ObjectOutputStream</code>s. There are currently two + * different protocols, specified by <code>PROTOCOL_VERSION_1</code> + * and <code>PROTOCOL_VERSION_2</code>. The default default is + * <code>PROTOCOL_VERSION_1</code>. + * + * @exception IOException if <code>version</code> is not a valid + * protocol + * + * @see #useProtocolVersion(int) + */ + public static void setDefaultProtocolVersion(int version) + throws IOException + { + if (version != PROTOCOL_VERSION_1 && version != PROTOCOL_VERSION_2) + throw new IOException("Invalid protocol version requested."); + + defaultProtocolVersion = version; + } + + + /** + * An empty hook that allows subclasses to write extra information + * about classes to the stream. This method is called the first + * time each class is seen, and after all of the standard + * information about the class has been written. + * + * @exception IOException Exception from underlying + * <code>OutputStream</code>. + * + * @see ObjectInputStream#resolveClass(java.io.ObjectStreamClass) + */ + protected void annotateClass(Class cl) throws IOException + { + } + + protected void annotateProxyClass(Class cl) throws IOException + { + } + + /** + * Allows subclasses to replace objects that are written to the + * stream with other objects to be written in their place. This + * method is called the first time each object is encountered + * (modulo reseting of the stream). + * + * This method must be enabled before it will be called in the + * serialization process. + * + * @exception IOException Exception from underlying + * <code>OutputStream</code>. + * + * @see #enableReplaceObject(boolean) + */ + protected Object replaceObject(Object obj) throws IOException + { + return obj; + } + + + /** + * If <code>enable</code> is <code>true</code> and this object is + * trusted, then <code>replaceObject (Object)</code> will be called + * in subsequent calls to <code>writeObject (Object)</code>. + * Otherwise, <code>replaceObject (Object)</code> will not be called. + * + * @exception SecurityException This class is not trusted. + */ + protected boolean enableReplaceObject(boolean enable) + throws SecurityException + { + if (enable) + { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkPermission(new SerializablePermission("enableSubstitution")); + } + + boolean old_val = replacementEnabled; + replacementEnabled = enable; + return old_val; + } + + + /** + * Writes stream magic and stream version information to the + * underlying stream. + * + * @exception IOException Exception from underlying + * <code>OutputStream</code>. + */ + protected void writeStreamHeader() throws IOException + { + realOutput.writeShort(STREAM_MAGIC); + realOutput.writeShort(STREAM_VERSION); + } + + /** + * Protected constructor that allows subclasses to override + * serialization. This constructor should be called by subclasses + * that wish to override <code>writeObject (Object)</code>. This + * method does a security check <i>NOTE: currently not + * implemented</i>, then sets a flag that informs + * <code>writeObject (Object)</code> to call the subclasses + * <code>writeObjectOverride (Object)</code> method. + * + * @see #writeObjectOverride(Object) + */ + protected ObjectOutputStream() throws IOException, SecurityException + { + SecurityManager sec_man = System.getSecurityManager (); + if (sec_man != null) + sec_man.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION); + useSubclassMethod = true; + } + + + /** + * This method allows subclasses to override the default + * serialization mechanism provided by + * <code>ObjectOutputStream</code>. To make this method be used for + * writing objects, subclasses must invoke the 0-argument + * constructor on this class from there constructor. + * + * @see #ObjectOutputStream() + * + * @exception NotActiveException Subclass has arranged for this + * method to be called, but did not implement this method. + */ + protected void writeObjectOverride(Object obj) throws NotActiveException, + IOException + { + throw new NotActiveException + ("Subclass of ObjectOutputStream must implement writeObjectOverride"); + } + + + /** + * @see DataOutputStream#write(int) + */ + public void write (int data) throws IOException + { + if (writeDataAsBlocks) + { + if (blockDataCount == BUFFER_SIZE) + drain(); + + blockData[ blockDataCount++ ] = (byte)data; + } + else + realOutput.write(data); + } + + + /** + * @see DataOutputStream#write(byte[]) + */ + public void write(byte[] b) throws IOException + { + write(b, 0, b.length); + } + + + /** + * @see DataOutputStream#write(byte[],int,int) + */ + public void write(byte[] b, int off, int len) throws IOException + { + if (writeDataAsBlocks) + { + if (len < 0) + throw new IndexOutOfBoundsException(); + + if (blockDataCount + len < BUFFER_SIZE) + { + System.arraycopy(b, off, blockData, blockDataCount, len); + blockDataCount += len; + } + else + { + drain(); + writeBlockDataHeader(len); + realOutput.write(b, off, len); + } + } + else + realOutput.write(b, off, len); + } + + + /** + * @see DataOutputStream#flush() + */ + public void flush () throws IOException + { + drain(); + realOutput.flush(); + } + + + /** + * Causes the block-data buffer to be written to the underlying + * stream, but does not flush underlying stream. + * + * @exception IOException Exception from underlying + * <code>OutputStream</code>. + */ + protected void drain() throws IOException + { + if (blockDataCount == 0) + return; + + if (writeDataAsBlocks) + writeBlockDataHeader(blockDataCount); + realOutput.write(blockData, 0, blockDataCount); + blockDataCount = 0; + } + + + /** + * @see java.io.DataOutputStream#close () + */ + public void close() throws IOException + { + flush(); + realOutput.close(); + } + + + /** + * @see java.io.DataOutputStream#writeBoolean (boolean) + */ + public void writeBoolean(boolean data) throws IOException + { + blockDataOutput.writeBoolean(data); + } + + + /** + * @see java.io.DataOutputStream#writeByte (int) + */ + public void writeByte(int data) throws IOException + { + blockDataOutput.writeByte(data); + } + + + /** + * @see java.io.DataOutputStream#writeShort (int) + */ + public void writeShort (int data) throws IOException + { + blockDataOutput.writeShort(data); + } + + + /** + * @see java.io.DataOutputStream#writeChar (int) + */ + public void writeChar(int data) throws IOException + { + blockDataOutput.writeChar(data); + } + + + /** + * @see java.io.DataOutputStream#writeInt (int) + */ + public void writeInt(int data) throws IOException + { + blockDataOutput.writeInt(data); + } + + + /** + * @see java.io.DataOutputStream#writeLong (long) + */ + public void writeLong(long data) throws IOException + { + blockDataOutput.writeLong(data); + } + + + /** + * @see java.io.DataOutputStream#writeFloat (float) + */ + public void writeFloat(float data) throws IOException + { + blockDataOutput.writeFloat(data); + } + + + /** + * @see java.io.DataOutputStream#writeDouble (double) + */ + public void writeDouble(double data) throws IOException + { + blockDataOutput.writeDouble(data); + } + + + /** + * @see java.io.DataOutputStream#writeBytes (java.lang.String) + */ + public void writeBytes(String data) throws IOException + { + blockDataOutput.writeBytes(data); + } + + + /** + * @see java.io.DataOutputStream#writeChars (java.lang.String) + */ + public void writeChars(String data) throws IOException + { + dataOutput.writeChars(data); + } + + + /** + * @see java.io.DataOutputStream#writeUTF (java.lang.String) + */ + public void writeUTF(String data) throws IOException + { + dataOutput.writeUTF(data); + } + + + /** + * This class allows a class to specify exactly which fields should + * be written, and what values should be written for these fields. + * + * XXX: finish up comments + */ + public abstract static class PutField + { + public abstract void put (String name, boolean value); + public abstract void put (String name, byte value); + public abstract void put (String name, char value); + public abstract void put (String name, double value); + public abstract void put (String name, float value); + public abstract void put (String name, int value); + public abstract void put (String name, long value); + public abstract void put (String name, short value); + public abstract void put (String name, Object value); + + /** + * @deprecated + */ + public abstract void write (ObjectOutput out) throws IOException; + } + + public PutField putFields() throws IOException + { + if (currentPutField != null) + return currentPutField; + + currentPutField = new PutField() + { + private byte[] prim_field_data + = new byte[currentObjectStreamClass.primFieldSize]; + private Object[] objs + = new Object[currentObjectStreamClass.objectFieldCount]; + + private ObjectStreamField getField (String name) + { + ObjectStreamField field + = currentObjectStreamClass.getField(name); + + if (field == null) + throw new IllegalArgumentException("no such serializable field " + name); + + return field; + } + + public void put(String name, boolean value) + { + ObjectStreamField field = getField(name); + + checkType(field, 'Z'); + prim_field_data[field.getOffset ()] = (byte)(value ? 1 : 0); + } + + public void put(String name, byte value) + { + ObjectStreamField field = getField(name); + + checkType(field, 'B'); + prim_field_data[field.getOffset()] = value; + } + + public void put(String name, char value) + { + ObjectStreamField field = getField(name); + + checkType(field, 'C'); + int off = field.getOffset(); + prim_field_data[off++] = (byte)(value >>> 8); + prim_field_data[off] = (byte)value; + } + + public void put(String name, double value) + { + ObjectStreamField field = getField (name); + + checkType(field, 'D'); + int off = field.getOffset(); + long l_value = Double.doubleToLongBits (value); + prim_field_data[off++] = (byte)(l_value >>> 52); + prim_field_data[off++] = (byte)(l_value >>> 48); + prim_field_data[off++] = (byte)(l_value >>> 40); + prim_field_data[off++] = (byte)(l_value >>> 32); + prim_field_data[off++] = (byte)(l_value >>> 24); + prim_field_data[off++] = (byte)(l_value >>> 16); + prim_field_data[off++] = (byte)(l_value >>> 8); + prim_field_data[off] = (byte)l_value; + } + + public void put(String name, float value) + { + ObjectStreamField field = getField(name); + + checkType(field, 'F'); + int off = field.getOffset(); + int i_value = Float.floatToIntBits(value); + prim_field_data[off++] = (byte)(i_value >>> 24); + prim_field_data[off++] = (byte)(i_value >>> 16); + prim_field_data[off++] = (byte)(i_value >>> 8); + prim_field_data[off] = (byte)i_value; + } + + public void put(String name, int value) + { + ObjectStreamField field = getField(name); + checkType(field, 'I'); + int off = field.getOffset(); + prim_field_data[off++] = (byte)(value >>> 24); + prim_field_data[off++] = (byte)(value >>> 16); + prim_field_data[off++] = (byte)(value >>> 8); + prim_field_data[off] = (byte)value; + } + + public void put(String name, long value) + { + ObjectStreamField field = getField(name); + checkType(field, 'J'); + int off = field.getOffset(); + prim_field_data[off++] = (byte)(value >>> 52); + prim_field_data[off++] = (byte)(value >>> 48); + prim_field_data[off++] = (byte)(value >>> 40); + prim_field_data[off++] = (byte)(value >>> 32); + prim_field_data[off++] = (byte)(value >>> 24); + prim_field_data[off++] = (byte)(value >>> 16); + prim_field_data[off++] = (byte)(value >>> 8); + prim_field_data[off] = (byte)value; + } + + public void put(String name, short value) + { + ObjectStreamField field = getField(name); + checkType(field, 'S'); + int off = field.getOffset(); + prim_field_data[off++] = (byte)(value >>> 8); + prim_field_data[off] = (byte)value; + } + + public void put(String name, Object value) + { + ObjectStreamField field = getField(name); + + if (value != null && + ! field.getType().isAssignableFrom(value.getClass ())) + throw new IllegalArgumentException("Class " + value.getClass() + + " cannot be cast to " + field.getType()); + objs[field.getOffset()] = value; + } + + public void write(ObjectOutput out) throws IOException + { + // Apparently Block data is not used with PutField as per + // empirical evidence against JDK 1.2. Also see Mauve test + // java.io.ObjectInputOutput.Test.GetPutField. + boolean oldmode = setBlockDataMode(false); + out.write(prim_field_data); + for (int i = 0; i < objs.length; ++ i) + out.writeObject(objs[i]); + setBlockDataMode(oldmode); + } + + private void checkType(ObjectStreamField field, char type) + throws IllegalArgumentException + { + if (TypeSignature.getEncodingOfClass(field.getType()).charAt(0) + != type) + throw new IllegalArgumentException(); + } + }; + // end PutFieldImpl + + return currentPutField; + } + + + public void writeFields() throws IOException + { + if (currentPutField == null) + throw new NotActiveException("writeFields can only be called after putFields has been called"); + + markFieldsWritten(); + currentPutField.write(this); + } + + + // write out the block-data buffer, picking the correct header + // depending on the size of the buffer + private void writeBlockDataHeader(int size) throws IOException + { + if (size < 256) + { + realOutput.writeByte(TC_BLOCKDATA); + realOutput.write(size); + } + else + { + realOutput.writeByte(TC_BLOCKDATALONG); + realOutput.writeInt(size); + } + } + + + // lookup the handle for OBJ, return null if OBJ doesn't have a + // handle yet + private Integer findHandle(Object obj) + { + return (Integer)OIDLookupTable.get(new ObjectIdentityWrapper(obj)); + } + + + // assigns the next availible handle to OBJ + private int assignNewHandle(Object obj) + { + OIDLookupTable.put(new ObjectIdentityWrapper(obj), + new Integer(nextOID)); + return nextOID++; + } + + + // resets mapping from objects to handles + private void clearHandles() + { + nextOID = baseWireHandle; + OIDLookupTable.clear(); + } + + + // write out array size followed by each element of the array + private void writeArraySizeAndElements(Object array, Class clazz) + throws IOException + { + int length = Array.getLength(array); + + if (clazz.isPrimitive()) + { + if (clazz == Boolean.TYPE) + { + boolean[] cast_array = (boolean[])array; + realOutput.writeInt (length); + for (int i = 0; i < length; i++) + realOutput.writeBoolean(cast_array[i]); + return; + } + if (clazz == Byte.TYPE) + { + byte[] cast_array = (byte[])array; + realOutput.writeInt(length); + realOutput.write(cast_array, 0, length); + return; + } + if (clazz == Character.TYPE) + { + char[] cast_array = (char[])array; + realOutput.writeInt(length); + for (int i = 0; i < length; i++) + realOutput.writeChar(cast_array[i]); + return; + } + if (clazz == Double.TYPE) + { + double[] cast_array = (double[])array; + realOutput.writeInt(length); + for (int i = 0; i < length; i++) + realOutput.writeDouble(cast_array[i]); + return; + } + if (clazz == Float.TYPE) + { + float[] cast_array = (float[])array; + realOutput.writeInt(length); + for (int i = 0; i < length; i++) + realOutput.writeFloat(cast_array[i]); + return; + } + if (clazz == Integer.TYPE) + { + int[] cast_array = (int[])array; + realOutput.writeInt(length); + for (int i = 0; i < length; i++) + realOutput.writeInt(cast_array[i]); + return; + } + if (clazz == Long.TYPE) + { + long[] cast_array = (long[])array; + realOutput.writeInt (length); + for (int i = 0; i < length; i++) + realOutput.writeLong(cast_array[i]); + return; + } + if (clazz == Short.TYPE) + { + short[] cast_array = (short[])array; + realOutput.writeInt (length); + for (int i = 0; i < length; i++) + realOutput.writeShort(cast_array[i]); + return; + } + } + else + { + Object[] cast_array = (Object[])array; + realOutput.writeInt(length); + for (int i = 0; i < length; i++) + writeObject(cast_array[i]); + } + } + + + // writes out FIELDS of OBJECT for the specified ObjectStreamClass. + // FIELDS are already in canonical order. + private void writeFields(Object obj, ObjectStreamClass osc) + throws IOException + { + ObjectStreamField[] fields = osc.fields; + boolean oldmode = setBlockDataMode(false); + String field_name; + Class type; + + for (int i = 0; i < fields.length; i++) + { + field_name = fields[i].getName(); + type = fields[i].getType(); + + if (dump) + dumpElementln ("WRITE FIELD: " + field_name + " type=" + type); + + if (type == Boolean.TYPE) + realOutput.writeBoolean(getBooleanField(obj, osc.forClass(), field_name)); + else if (type == Byte.TYPE) + realOutput.writeByte(getByteField(obj, osc.forClass(), field_name)); + else if (type == Character.TYPE) + realOutput.writeChar(getCharField(obj, osc.forClass(), field_name)); + else if (type == Double.TYPE) + realOutput.writeDouble(getDoubleField(obj, osc.forClass(), field_name)); + else if (type == Float.TYPE) + realOutput.writeFloat(getFloatField(obj, osc.forClass(), field_name)); + else if (type == Integer.TYPE) + realOutput.writeInt(getIntField(obj, osc.forClass(), field_name)); + else if (type == Long.TYPE) + realOutput.writeLong(getLongField(obj, osc.forClass(), field_name)); + else if (type == Short.TYPE) + realOutput.writeShort(getShortField(obj, osc.forClass(), field_name)); + else + writeObject(getObjectField(obj, osc.forClass(), field_name, + fields[i].getTypeString ())); + } + setBlockDataMode(oldmode); + } + + + // Toggles writing primitive data to block-data buffer. + // Package-private to avoid a trampoline constructor. + boolean setBlockDataMode(boolean on) throws IOException + { + if (on == writeDataAsBlocks) + return on; + + drain(); + boolean oldmode = writeDataAsBlocks; + writeDataAsBlocks = on; + + if (on) + dataOutput = blockDataOutput; + else + dataOutput = realOutput; + + return oldmode; + } + + + private void callWriteMethod(Object obj, ObjectStreamClass osc) + throws IOException + { + currentPutField = null; + try + { + Object args[] = {this}; + osc.writeObjectMethod.invoke(obj, args); + } + catch (InvocationTargetException x) + { + /* Rethrow if possible. */ + Throwable exception = x.getTargetException(); + if (exception instanceof RuntimeException) + throw (RuntimeException) exception; + if (exception instanceof IOException) + throw (IOException) exception; + + IOException ioe + = new IOException("Exception thrown from writeObject() on " + + osc.forClass().getName() + ": " + + exception.getClass().getName()); + ioe.initCause(exception); + throw ioe; + } + catch (Exception x) + { + IOException ioe + = new IOException("Failure invoking writeObject() on " + + osc.forClass().getName() + ": " + + x.getClass().getName()); + ioe.initCause(x); + throw ioe; + } + } + + private boolean getBooleanField(Object obj, Class klass, String field_name) + throws IOException + { + try + { + Field f = getField(klass, field_name); + boolean b = f.getBoolean(obj); + return b; + } + catch (IllegalArgumentException _) + { + throw new InvalidClassException + ("invalid requested type for field " + field_name + " in class " + klass.getName()); + } + catch (IOException e) + { + throw e; + } + catch (Exception _) + { + throw new IOException("Unexpected exception " + _); + } + } + + private byte getByteField (Object obj, Class klass, String field_name) + throws IOException + { + try + { + Field f = getField (klass, field_name); + byte b = f.getByte (obj); + return b; + } + catch (IllegalArgumentException _) + { + throw new InvalidClassException + ("invalid requested type for field " + field_name + " in class " + klass.getName()); + } + catch (IOException e) + { + throw e; + } + catch (Exception _) + { + throw new IOException("Unexpected exception " + _); + } + } + + private char getCharField (Object obj, Class klass, String field_name) + throws IOException + { + try + { + Field f = getField (klass, field_name); + char b = f.getChar (obj); + return b; + } + catch (IllegalArgumentException _) + { + throw new InvalidClassException + ("invalid requested type for field " + field_name + " in class " + klass.getName()); + } + catch (IOException e) + { + throw e; + } + catch (Exception _) + { + throw new IOException("Unexpected exception " + _); + } + } + + private double getDoubleField (Object obj, Class klass, String field_name) + throws IOException + { + try + { + Field f = getField (klass, field_name); + double b = f.getDouble (obj); + return b; + } + catch (IllegalArgumentException _) + { + throw new InvalidClassException + ("invalid requested type for field " + field_name + " in class " + klass.getName()); + } + catch (IOException e) + { + throw e; + } + catch (Exception _) + { + throw new IOException("Unexpected exception " + _); + } + } + + private float getFloatField (Object obj, Class klass, String field_name) + throws IOException + { + try + { + Field f = getField (klass, field_name); + float b = f.getFloat (obj); + return b; + } + catch (IllegalArgumentException _) + { + throw new InvalidClassException + ("invalid requested type for field " + field_name + " in class " + klass.getName()); + } + catch (IOException e) + { + throw e; + } + catch (Exception _) + { + throw new IOException("Unexpected exception " + _); + } + } + + private int getIntField (Object obj, Class klass, String field_name) + throws IOException + { + try + { + Field f = getField (klass, field_name); + int b = f.getInt (obj); + return b; + } + catch (IllegalArgumentException _) + { + throw new InvalidClassException + ("invalid requested type for field " + field_name + " in class " + klass.getName()); + } + catch (IOException e) + { + throw e; + } + catch (Exception _) + { + throw new IOException("Unexpected exception " + _); + } + } + + private long getLongField (Object obj, Class klass, String field_name) + throws IOException + { + try + { + Field f = getField (klass, field_name); + long b = f.getLong (obj); + return b; + } + catch (IllegalArgumentException _) + { + throw new InvalidClassException + ("invalid requested type for field " + field_name + " in class " + klass.getName()); + } + catch (IOException e) + { + throw e; + } + catch (Exception _) + { + throw new IOException("Unexpected exception " + _); + } + } + + private short getShortField (Object obj, Class klass, String field_name) + throws IOException + { + try + { + Field f = getField (klass, field_name); + short b = f.getShort (obj); + return b; + } + catch (IllegalArgumentException _) + { + throw new InvalidClassException + ("invalid requested type for field " + field_name + " in class " + klass.getName()); + } + catch (IOException e) + { + throw e; + } + catch (Exception _) + { + throw new IOException("Unexpected exception " + _); + } + } + + private Object getObjectField (Object obj, Class klass, String field_name, + String type_code) throws IOException + { + try + { + Field f = getField (klass, field_name); + ObjectStreamField of = new ObjectStreamField(f.getName(), f.getType()); + + /* if of is primitive something went wrong + * in the check for primitive classes in writeFields. + */ + if (of.isPrimitive()) + throw new InvalidClassException + ("invalid type code for " + field_name + " in class " + klass.getName() + " : object stream field is primitive"); + + if (!of.getTypeString().equals(type_code)) + throw new InvalidClassException + ("invalid type code for " + field_name + " in class " + klass.getName() + " : object stream field " + of + " has type string " + of.getTypeString() + " instead of " + type_code); + + Object o = f.get (obj); + // FIXME: We should check the type_code here + return o; + } + catch (IOException e) + { + throw e; + } + catch (Exception e) + { + throw new IOException (); + } + } + + private Field getField (Class klass, String name) + throws java.io.InvalidClassException + { + try + { + final Field f = klass.getDeclaredField(name); + setAccessible.setMember(f); + AccessController.doPrivileged(setAccessible); + return f; + } + catch (java.lang.NoSuchFieldException e) + { + throw new InvalidClassException + ("no field called " + name + " in class " + klass.getName()); + } + } + + private void dumpElementln (String msg) + { + for (int i = 0; i < depth; i++) + System.out.print (" "); + System.out.print (Thread.currentThread() + ": "); + System.out.println(msg); + } + + // this value comes from 1.2 spec, but is used in 1.1 as well + private static final int BUFFER_SIZE = 1024; + + private static int defaultProtocolVersion = PROTOCOL_VERSION_2; + + private DataOutputStream dataOutput; + private boolean writeDataAsBlocks; + private DataOutputStream realOutput; + private DataOutputStream blockDataOutput; + private byte[] blockData; + private int blockDataCount; + private Object currentObject; + // Package-private to avoid a trampoline. + ObjectStreamClass currentObjectStreamClass; + private PutField currentPutField; + private boolean fieldsAlreadyWritten; + private boolean replacementEnabled; + private boolean isSerializing; + private int nextOID; + private Hashtable OIDLookupTable; + private int protocolVersion; + private boolean useSubclassMethod; + private SetAccessibleAction setAccessible = new SetAccessibleAction(); + + // The nesting depth for debugging output + private int depth = 0; + + // Set if we're generating debugging dumps + private boolean dump = false; + + private static final boolean DEBUG = false; + + static + { + if (Configuration.INIT_LOAD_LIBRARY) + { + System.loadLibrary("javaio"); + } + } +} diff --git a/libjava/classpath/java/io/ObjectStreamClass.java b/libjava/classpath/java/io/ObjectStreamClass.java new file mode 100644 index 00000000000..b7bd1271324 --- /dev/null +++ b/libjava/classpath/java/io/ObjectStreamClass.java @@ -0,0 +1,976 @@ +/* ObjectStreamClass.java -- Class used to write class information + about serialized objects. + Copyright (C) 1998, 1999, 2000, 2001, 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +import gnu.java.io.NullOutputStream; +import gnu.java.lang.reflect.TypeSignature; +import gnu.java.security.action.SetAccessibleAction; +import gnu.java.security.provider.Gnu; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Member; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.Proxy; +import java.security.AccessController; +import java.security.DigestOutputStream; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.PrivilegedAction; +import java.security.Security; +import java.util.Arrays; +import java.util.Comparator; +import java.util.Hashtable; +import java.util.Vector; + +public class ObjectStreamClass implements Serializable +{ + /** + * Returns the <code>ObjectStreamClass</code> for <code>cl</code>. + * If <code>cl</code> is null, or is not <code>Serializable</code>, + * null is returned. <code>ObjectStreamClass</code>'s are memorized; + * later calls to this method with the same class will return the + * same <code>ObjectStreamClass</code> object and no recalculation + * will be done. + * + * @see java.io.Serializable + */ + public static ObjectStreamClass lookup(Class cl) + { + if (cl == null) + return null; + if (! (Serializable.class).isAssignableFrom(cl)) + return null; + + return lookupForClassObject(cl); + } + + /** + * This lookup for internal use by ObjectOutputStream. Suppose + * we have a java.lang.Class object C for class A, though A is not + * serializable, but it's okay to serialize C. + */ + static ObjectStreamClass lookupForClassObject(Class cl) + { + if (cl == null) + return null; + + ObjectStreamClass osc = (ObjectStreamClass) classLookupTable.get(cl); + + if (osc != null) + return osc; + else + { + osc = new ObjectStreamClass(cl); + classLookupTable.put(cl, osc); + return osc; + } + } + + /** + * Returns the name of the class that this + * <code>ObjectStreamClass</code> represents. + * + * @return the name of the class. + */ + public String getName() + { + return name; + } + + /** + * Returns the class that this <code>ObjectStreamClass</code> + * represents. Null could be returned if this + * <code>ObjectStreamClass</code> was read from an + * <code>ObjectInputStream</code> and the class it represents cannot + * be found or loaded. + * + * @see java.io.ObjectInputStream + */ + public Class forClass() + { + return clazz; + } + + /** + * Returns the serial version stream-unique identifier for the class + * represented by this <code>ObjectStreamClass</code>. This SUID is + * either defined by the class as <code>static final long + * serialVersionUID</code> or is calculated as specified in + * Javasoft's "Object Serialization Specification" XXX: add reference + * + * @return the serial version UID. + */ + public long getSerialVersionUID() + { + return uid; + } + + /** + * Returns the serializable (non-static and non-transient) Fields + * of the class represented by this ObjectStreamClass. The Fields + * are sorted by name. + * + * @return the fields. + */ + public ObjectStreamField[] getFields() + { + ObjectStreamField[] copy = new ObjectStreamField[ fields.length ]; + System.arraycopy(fields, 0, copy, 0, fields.length); + return copy; + } + + // XXX doc + // Can't do binary search since fields is sorted by name and + // primitiveness. + public ObjectStreamField getField (String name) + { + for (int i = 0; i < fields.length; i++) + if (fields[i].getName().equals(name)) + return fields[i]; + return null; + } + + /** + * Returns a textual representation of this + * <code>ObjectStreamClass</code> object including the name of the + * class it represents as well as that class's serial version + * stream-unique identifier. + * + * @see #getSerialVersionUID() + * @see #getName() + */ + public String toString() + { + return "java.io.ObjectStreamClass< " + name + ", " + uid + " >"; + } + + // Returns true iff the class that this ObjectStreamClass represents + // has the following method: + // + // private void writeObject (ObjectOutputStream) + // + // This method is used by the class to override default + // serialization behavior. + boolean hasWriteMethod() + { + return (flags & ObjectStreamConstants.SC_WRITE_METHOD) != 0; + } + + // Returns true iff the class that this ObjectStreamClass represents + // implements Serializable but does *not* implement Externalizable. + boolean isSerializable() + { + return (flags & ObjectStreamConstants.SC_SERIALIZABLE) != 0; + } + + + // Returns true iff the class that this ObjectStreamClass represents + // implements Externalizable. + boolean isExternalizable() + { + return (flags & ObjectStreamConstants.SC_EXTERNALIZABLE) != 0; + } + + + // Returns the <code>ObjectStreamClass</code> that represents the + // class that is the superclass of the class this + // <code>ObjectStreamClass</code> represents. If the superclass is + // not Serializable, null is returned. + ObjectStreamClass getSuper() + { + return superClass; + } + + + // returns an array of ObjectStreamClasses that represent the super + // classes of CLAZZ and CLAZZ itself in order from most super to + // CLAZZ. ObjectStreamClass[0] is the highest superclass of CLAZZ + // that is serializable. + static ObjectStreamClass[] getObjectStreamClasses(Class clazz) + { + ObjectStreamClass osc = ObjectStreamClass.lookup(clazz); + + if (osc == null) + return new ObjectStreamClass[0]; + else + { + Vector oscs = new Vector(); + + while (osc != null) + { + oscs.addElement (osc); + osc = osc.getSuper(); + } + + int count = oscs.size(); + ObjectStreamClass[] sorted_oscs = new ObjectStreamClass[ count ]; + + for (int i = count - 1; i >= 0; i--) + sorted_oscs[ count - i - 1 ] = (ObjectStreamClass) oscs.elementAt(i); + + return sorted_oscs; + } + } + + + // Returns an integer that consists of bit-flags that indicate + // properties of the class represented by this ObjectStreamClass. + // The bit-flags that could be present are those defined in + // ObjectStreamConstants that begin with `SC_' + int getFlags() + { + return flags; + } + + + ObjectStreamClass(String name, long uid, byte flags, + ObjectStreamField[] fields) + { + this.name = name; + this.uid = uid; + this.flags = flags; + this.fields = fields; + } + + /** + * This method builds the internal description corresponding to a Java Class. + * As the constructor only assign a name to the current ObjectStreamClass instance, + * that method sets the serial UID, chose the fields which will be serialized, + * and compute the position of the fields in the serialized stream. + * + * @param cl The Java class which is used as a reference for building the descriptor. + * @param superClass The descriptor of the super class for this class descriptor. + * @throws InvalidClassException if an incompatibility between computed UID and + * already set UID is found. + */ + void setClass(Class cl, ObjectStreamClass superClass) throws InvalidClassException + { + this.clazz = cl; + + cacheMethods(); + + long class_uid = getClassUID(cl); + if (uid == 0) + uid = class_uid; + else + { + // Check that the actual UID of the resolved class matches the UID from + // the stream. + if (uid != class_uid) + { + String msg = cl + + ": Local class not compatible: stream serialVersionUID=" + + uid + ", local serialVersionUID=" + class_uid; + throw new InvalidClassException (msg); + } + } + + isProxyClass = clazz != null && Proxy.isProxyClass(clazz); + this.superClass = superClass; + calculateOffsets(); + + try + { + ObjectStreamField[] exportedFields = getSerialPersistentFields (clazz); + + if (exportedFields == null) + return; + + ObjectStreamField[] newFieldList = new ObjectStreamField[exportedFields.length + fields.length]; + int i, j, k; + + /* We now check the import fields against the exported fields. + * There should not be contradiction (e.g. int x and String x) + * but extra virtual fields can be added to the class. + */ + + Arrays.sort(exportedFields); + + i = 0; j = 0; k = 0; + while (i < fields.length && j < exportedFields.length) + { + int comp = fields[i].compareTo(exportedFields[j]); + + if (comp < 0) + { + newFieldList[k] = fields[i]; + fields[i].setPersistent(false); + fields[i].setToSet(false); + i++; + } + else if (comp > 0) + { + /* field not found in imported fields. We add it + * in the list of supported fields. + */ + newFieldList[k] = exportedFields[j]; + newFieldList[k].setPersistent(true); + newFieldList[k].setToSet(false); + try + { + newFieldList[k].lookupField(clazz); + newFieldList[k].checkFieldType(); + } + catch (NoSuchFieldException _) + { + } + j++; + } + else + { + try + { + exportedFields[j].lookupField(clazz); + exportedFields[j].checkFieldType(); + } + catch (NoSuchFieldException _) + { + } + + if (!fields[i].getType().equals(exportedFields[j].getType())) + throw new InvalidClassException + ("serialPersistentFields must be compatible with" + + " imported fields (about " + fields[i].getName() + ")"); + newFieldList[k] = fields[i]; + fields[i].setPersistent(true); + i++; + j++; + } + k++; + } + + if (i < fields.length) + for (;i<fields.length;i++,k++) + { + fields[i].setPersistent(false); + fields[i].setToSet(false); + newFieldList[k] = fields[i]; + } + else + if (j < exportedFields.length) + for (;j<exportedFields.length;j++,k++) + { + exportedFields[j].setPersistent(true); + exportedFields[j].setToSet(false); + newFieldList[k] = exportedFields[j]; + } + + fields = new ObjectStreamField[k]; + System.arraycopy(newFieldList, 0, fields, 0, k); + } + catch (NoSuchFieldException ignore) + { + return; + } + catch (IllegalAccessException ignore) + { + return; + } + } + + void setSuperclass (ObjectStreamClass osc) + { + superClass = osc; + } + + void calculateOffsets() + { + int i; + ObjectStreamField field; + primFieldSize = 0; + int fcount = fields.length; + for (i = 0; i < fcount; ++ i) + { + field = fields[i]; + + if (! field.isPrimitive()) + break; + + field.setOffset(primFieldSize); + switch (field.getTypeCode()) + { + case 'B': + case 'Z': + ++ primFieldSize; + break; + case 'C': + case 'S': + primFieldSize += 2; + break; + case 'I': + case 'F': + primFieldSize += 4; + break; + case 'D': + case 'J': + primFieldSize += 8; + break; + } + } + + for (objectFieldCount = 0; i < fcount; ++ i) + fields[i].setOffset(objectFieldCount++); + } + + private Method findMethod(Method[] methods, String name, Class[] params, + Class returnType, boolean mustBePrivate) + { +outer: + for (int i = 0; i < methods.length; i++) + { + final Method m = methods[i]; + int mods = m.getModifiers(); + if (Modifier.isStatic(mods) + || (mustBePrivate && !Modifier.isPrivate(mods))) + { + continue; + } + + if (m.getName().equals(name) + && m.getReturnType() == returnType) + { + Class[] mp = m.getParameterTypes(); + if (mp.length == params.length) + { + for (int j = 0; j < mp.length; j++) + { + if (mp[j] != params[j]) + { + continue outer; + } + } + AccessController.doPrivileged(new SetAccessibleAction(m)); + return m; + } + } + } + return null; + } + + private static boolean inSamePackage(Class c1, Class c2) + { + String name1 = c1.getName(); + String name2 = c2.getName(); + + int id1 = name1.lastIndexOf('.'); + int id2 = name2.lastIndexOf('.'); + + // Handle the default package + if (id1 == -1 || id2 == -1) + return id1 == id2; + + String package1 = name1.substring(0, id1); + String package2 = name2.substring(0, id2); + + return package1.equals(package2); + } + + final static Class[] noArgs = new Class[0]; + + private static Method findAccessibleMethod(String name, Class from) + { + for (Class c = from; c != null; c = c.getSuperclass()) + { + try + { + Method res = c.getDeclaredMethod(name, noArgs); + int mods = res.getModifiers(); + + if (c != from + && (Modifier.isPrivate(mods) + || ! Modifier.isPublic(mods) && ! inSamePackage(c, from))) + continue; + + AccessController.doPrivileged(new SetAccessibleAction(res)); + return res; + } + catch (NoSuchMethodException e) + { + } + } + + return null; + } + + private void cacheMethods() + { + Method[] methods = forClass().getDeclaredMethods(); + + readObjectMethod = findMethod(methods, "readObject", + new Class[] { ObjectInputStream.class }, + Void.TYPE, true); + writeObjectMethod = findMethod(methods, "writeObject", + new Class[] { ObjectOutputStream.class }, + Void.TYPE, true); + + // readResolve and writeReplace can be in parent classes, as long as they + // are accessible from this class. + readResolveMethod = findAccessibleMethod("readResolve", forClass()); + writeReplaceMethod = findAccessibleMethod("writeReplace", forClass()); + } + + private ObjectStreamClass(Class cl) + { + uid = 0; + flags = 0; + isProxyClass = Proxy.isProxyClass(cl); + + clazz = cl; + cacheMethods(); + name = cl.getName(); + setFlags(cl); + setFields(cl); + // to those class nonserializable, its uid field is 0 + if ( (Serializable.class).isAssignableFrom(cl) && !isProxyClass) + uid = getClassUID(cl); + superClass = lookup(cl.getSuperclass()); + } + + + // Sets bits in flags according to features of CL. + private void setFlags(Class cl) + { + if ((java.io.Externalizable.class).isAssignableFrom(cl)) + flags |= ObjectStreamConstants.SC_EXTERNALIZABLE; + else if ((java.io.Serializable.class).isAssignableFrom(cl)) + // only set this bit if CL is NOT Externalizable + flags |= ObjectStreamConstants.SC_SERIALIZABLE; + + if (writeObjectMethod != null) + flags |= ObjectStreamConstants.SC_WRITE_METHOD; + } + + + // Sets fields to be a sorted array of the serializable fields of + // clazz. + private void setFields(Class cl) + { + SetAccessibleAction setAccessible = new SetAccessibleAction(); + + if (!isSerializable() || isExternalizable()) + { + fields = NO_FIELDS; + return; + } + + try + { + final Field f = + cl.getDeclaredField("serialPersistentFields"); + setAccessible.setMember(f); + AccessController.doPrivileged(setAccessible); + int modifiers = f.getModifiers(); + + if (Modifier.isStatic(modifiers) + && Modifier.isFinal(modifiers) + && Modifier.isPrivate(modifiers)) + { + fields = getSerialPersistentFields(cl); + if (fields != null) + { + Arrays.sort (fields); + // Retrieve field reference. + for (int i=0; i < fields.length; i++) + { + try + { + fields[i].lookupField(cl); + } + catch (NoSuchFieldException _) + { + fields[i].setToSet(false); + } + } + + calculateOffsets(); + return; + } + } + } + catch (NoSuchFieldException ignore) + { + } + catch (IllegalAccessException ignore) + { + } + + int num_good_fields = 0; + Field[] all_fields = cl.getDeclaredFields(); + + int modifiers; + // set non-serializable fields to null in all_fields + for (int i = 0; i < all_fields.length; i++) + { + modifiers = all_fields[i].getModifiers(); + if (Modifier.isTransient(modifiers) + || Modifier.isStatic(modifiers)) + all_fields[i] = null; + else + num_good_fields++; + } + + // make a copy of serializable (non-null) fields + fields = new ObjectStreamField[ num_good_fields ]; + for (int from = 0, to = 0; from < all_fields.length; from++) + if (all_fields[from] != null) + { + final Field f = all_fields[from]; + setAccessible.setMember(f); + AccessController.doPrivileged(setAccessible); + fields[to] = new ObjectStreamField(all_fields[from]); + to++; + } + + Arrays.sort(fields); + // Make sure we don't have any duplicate field names + // (Sun JDK 1.4.1. throws an Internal Error as well) + for (int i = 1; i < fields.length; i++) + { + if(fields[i - 1].getName().equals(fields[i].getName())) + throw new InternalError("Duplicate field " + + fields[i].getName() + " in class " + cl.getName()); + } + calculateOffsets(); + } + + // Returns the serial version UID defined by class, or if that + // isn't present, calculates value of serial version UID. + private long getClassUID(Class cl) + { + try + { + // Use getDeclaredField rather than getField, since serialVersionUID + // may not be public AND we only want the serialVersionUID of this + // class, not a superclass or interface. + final Field suid = cl.getDeclaredField("serialVersionUID"); + SetAccessibleAction setAccessible = new SetAccessibleAction(suid); + AccessController.doPrivileged(setAccessible); + int modifiers = suid.getModifiers(); + + if (Modifier.isStatic(modifiers) + && Modifier.isFinal(modifiers) + && suid.getType() == Long.TYPE) + return suid.getLong(null); + } + catch (NoSuchFieldException ignore) + { + } + catch (IllegalAccessException ignore) + { + } + + // cl didn't define serialVersionUID, so we have to compute it + try + { + MessageDigest md; + try + { + md = MessageDigest.getInstance("SHA"); + } + catch (NoSuchAlgorithmException e) + { + // If a provider already provides SHA, use it; otherwise, use this. + Gnu gnuProvider = new Gnu(); + Security.addProvider(gnuProvider); + md = MessageDigest.getInstance("SHA"); + } + + DigestOutputStream digest_out = + new DigestOutputStream(nullOutputStream, md); + DataOutputStream data_out = new DataOutputStream(digest_out); + + data_out.writeUTF(cl.getName()); + + int modifiers = cl.getModifiers(); + // just look at interesting bits + modifiers = modifiers & (Modifier.ABSTRACT | Modifier.FINAL + | Modifier.INTERFACE | Modifier.PUBLIC); + data_out.writeInt(modifiers); + + // Pretend that an array has no interfaces, because when array + // serialization was defined (JDK 1.1), arrays didn't have it. + if (! cl.isArray()) + { + Class[] interfaces = cl.getInterfaces(); + Arrays.sort(interfaces, interfaceComparator); + for (int i = 0; i < interfaces.length; i++) + data_out.writeUTF(interfaces[i].getName()); + } + + Field field; + Field[] fields = cl.getDeclaredFields(); + Arrays.sort(fields, memberComparator); + for (int i = 0; i < fields.length; i++) + { + field = fields[i]; + modifiers = field.getModifiers(); + if (Modifier.isPrivate(modifiers) + && (Modifier.isStatic(modifiers) + || Modifier.isTransient(modifiers))) + continue; + + data_out.writeUTF(field.getName()); + data_out.writeInt(modifiers); + data_out.writeUTF(TypeSignature.getEncodingOfClass (field.getType())); + } + + // write class initializer method if present + if (VMObjectStreamClass.hasClassInitializer(cl)) + { + data_out.writeUTF("<clinit>"); + data_out.writeInt(Modifier.STATIC); + data_out.writeUTF("()V"); + } + + Constructor constructor; + Constructor[] constructors = cl.getDeclaredConstructors(); + Arrays.sort (constructors, memberComparator); + for (int i = 0; i < constructors.length; i++) + { + constructor = constructors[i]; + modifiers = constructor.getModifiers(); + if (Modifier.isPrivate(modifiers)) + continue; + + data_out.writeUTF("<init>"); + data_out.writeInt(modifiers); + + // the replacement of '/' with '.' was needed to make computed + // SUID's agree with those computed by JDK + data_out.writeUTF + (TypeSignature.getEncodingOfConstructor(constructor).replace('/','.')); + } + + Method method; + Method[] methods = cl.getDeclaredMethods(); + Arrays.sort(methods, memberComparator); + for (int i = 0; i < methods.length; i++) + { + method = methods[i]; + modifiers = method.getModifiers(); + if (Modifier.isPrivate(modifiers)) + continue; + + data_out.writeUTF(method.getName()); + data_out.writeInt(modifiers); + + // the replacement of '/' with '.' was needed to make computed + // SUID's agree with those computed by JDK + data_out.writeUTF + (TypeSignature.getEncodingOfMethod(method).replace('/', '.')); + } + + data_out.close(); + byte[] sha = md.digest(); + long result = 0; + int len = sha.length < 8 ? sha.length : 8; + for (int i = 0; i < len; i++) + result += (long) (sha[i] & 0xFF) << (8 * i); + + return result; + } + catch (NoSuchAlgorithmException e) + { + throw new RuntimeException + ("The SHA algorithm was not found to use in computing the Serial Version UID for class " + + cl.getName(), e); + } + catch (IOException ioe) + { + throw new RuntimeException(ioe); + } + } + + /** + * Returns the value of CLAZZ's private static final field named + * `serialPersistentFields'. It performs some sanity checks before + * returning the real array. Besides, the returned array is a clean + * copy of the original. So it can be modified. + * + * @param clazz Class to retrieve 'serialPersistentFields' from. + * @return The content of 'serialPersistentFields'. + */ + private ObjectStreamField[] getSerialPersistentFields(Class clazz) + throws NoSuchFieldException, IllegalAccessException + { + ObjectStreamField[] fieldsArray = null; + ObjectStreamField[] o; + + // Use getDeclaredField rather than getField for the same reason + // as above in getDefinedSUID. + Field f = clazz.getDeclaredField("serialPersistentFields"); + f.setAccessible(true); + + int modifiers = f.getModifiers(); + if (!(Modifier.isStatic(modifiers) && + Modifier.isFinal(modifiers) && + Modifier.isPrivate(modifiers))) + return null; + + o = (ObjectStreamField[]) f.get(null); + + if (o == null) + return null; + + fieldsArray = new ObjectStreamField[ o.length ]; + System.arraycopy(o, 0, fieldsArray, 0, o.length); + + return fieldsArray; + } + + /** + * Returns a new instance of the Class this ObjectStreamClass corresponds + * to. + * Note that this should only be used for Externalizable classes. + * + * @return A new instance. + */ + Externalizable newInstance() throws InvalidClassException + { + synchronized(this) + { + if (constructor == null) + { + try + { + final Constructor c = clazz.getConstructor(new Class[0]); + + AccessController.doPrivileged(new PrivilegedAction() + { + public Object run() + { + c.setAccessible(true); + return null; + } + }); + + constructor = c; + } + catch(NoSuchMethodException x) + { + throw new InvalidClassException(clazz.getName(), + "No public zero-argument constructor"); + } + } + } + + try + { + return (Externalizable)constructor.newInstance(null); + } + catch(Exception x) + { + throw (InvalidClassException) + new InvalidClassException(clazz.getName(), + "Unable to instantiate").initCause(x); + } + } + + public static final ObjectStreamField[] NO_FIELDS = {}; + + private static Hashtable classLookupTable = new Hashtable(); + private static final NullOutputStream nullOutputStream = new NullOutputStream(); + private static final Comparator interfaceComparator = new InterfaceComparator(); + private static final Comparator memberComparator = new MemberComparator(); + private static final + Class[] writeMethodArgTypes = { java.io.ObjectOutputStream.class }; + + private ObjectStreamClass superClass; + private Class clazz; + private String name; + private long uid; + private byte flags; + + // this field is package protected so that ObjectInputStream and + // ObjectOutputStream can access it directly + ObjectStreamField[] fields; + + // these are accessed by ObjectIn/OutputStream + int primFieldSize = -1; // -1 if not yet calculated + int objectFieldCount; + + Method readObjectMethod; + Method readResolveMethod; + Method writeReplaceMethod; + Method writeObjectMethod; + boolean realClassIsSerializable; + boolean realClassIsExternalizable; + ObjectStreamField[] fieldMapping; + Constructor firstNonSerializableParentConstructor; + private Constructor constructor; // default constructor for Externalizable + + boolean isProxyClass = false; + + // This is probably not necessary because this class is special cased already + // but it will avoid showing up as a discrepancy when comparing SUIDs. + private static final long serialVersionUID = -6120832682080437368L; + + + // interfaces are compared only by name + private static final class InterfaceComparator implements Comparator + { + public int compare(Object o1, Object o2) + { + return ((Class) o1).getName().compareTo(((Class) o2).getName()); + } + } + + + // Members (Methods and Constructors) are compared first by name, + // conflicts are resolved by comparing type signatures + private static final class MemberComparator implements Comparator + { + public int compare(Object o1, Object o2) + { + Member m1 = (Member) o1; + Member m2 = (Member) o2; + + int comp = m1.getName().compareTo(m2.getName()); + + if (comp == 0) + return TypeSignature.getEncodingOfMember(m1). + compareTo(TypeSignature.getEncodingOfMember(m2)); + else + return comp; + } + } +} diff --git a/libjava/classpath/java/io/ObjectStreamConstants.java b/libjava/classpath/java/io/ObjectStreamConstants.java new file mode 100644 index 00000000000..f1a4af724e7 --- /dev/null +++ b/libjava/classpath/java/io/ObjectStreamConstants.java @@ -0,0 +1,89 @@ +/* ObjectStreamConstants.java -- Interface containing constant values + used in reading and writing serialized objects + Copyright (C) 1998, 1999, 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This interface contains constants that are used in object + * serialization. This interface is used by <code>ObjectOutputStream</code>, + * <code>ObjectInputStream</code>, and <code>ObjectStreamClass</code>. + * The values for these constants are specified by the Java library + * specification. + */ +public interface ObjectStreamConstants +{ + // FIXME: Javadoc comment these values. + int PROTOCOL_VERSION_1 = 1; + int PROTOCOL_VERSION_2 = 2; + + short STREAM_MAGIC = (short)0xaced; + short STREAM_VERSION = 5; + + byte TC_NULL = (byte)112; //0x70 + byte TC_REFERENCE = (byte)113; //0x71 + byte TC_CLASSDESC = (byte)114; //0x72 + byte TC_OBJECT = (byte)115; //0x73 + byte TC_STRING = (byte)116; //0x74 + byte TC_ARRAY = (byte)117; //0x75 + byte TC_CLASS = (byte)118; //0x76 + byte TC_BLOCKDATA = (byte)119; //0x77 + byte TC_ENDBLOCKDATA = (byte)120; //0x78 + byte TC_RESET = (byte)121; //0x79 + byte TC_BLOCKDATALONG = (byte)122; //0x7A + byte TC_EXCEPTION = (byte)123; //0x7B + byte TC_LONGSTRING = (byte)124; //0x7C + byte TC_PROXYCLASSDESC = (byte)125; //0x7D + + byte TC_BASE = TC_NULL; + byte TC_MAX = TC_PROXYCLASSDESC; + + int baseWireHandle = 0x7e0000; + + byte SC_WRITE_METHOD = 0x01; + byte SC_SERIALIZABLE = 0x02; + byte SC_EXTERNALIZABLE = 0x04; + byte SC_BLOCK_DATA = 0x08; + + SerializablePermission SUBSTITUTION_PERMISSION + = new SerializablePermission("enableSubstitution"); + + SerializablePermission SUBCLASS_IMPLEMENTATION_PERMISSION + = new SerializablePermission("enableSubclassImplementation"); +} + diff --git a/libjava/classpath/java/io/ObjectStreamException.java b/libjava/classpath/java/io/ObjectStreamException.java new file mode 100644 index 00000000000..61d4dd09e52 --- /dev/null +++ b/libjava/classpath/java/io/ObjectStreamException.java @@ -0,0 +1,74 @@ +/* ObjectStreamException.java -- Superclass of all serialization exceptions + Copyright (C) 1998, 2000, 2001, 2002, 2003, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This exception is thrown when a problem occurs during serialization. + * There are more specific subclasses that give more fine grained + * indications of the precise failure. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + * @since 1.1 + * @status updated to 1.4 + */ +public abstract class ObjectStreamException extends IOException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 7260898174833392607L; + + /** + * Create an exception without a descriptive error message. + */ + protected ObjectStreamException() + { + } + + /** + * Create an exception with a descriptive error message. + * + * @param message the descriptive error message + */ + protected ObjectStreamException(String message) + { + super(message); + } +} // class ObjectStreamException diff --git a/libjava/classpath/java/io/ObjectStreamField.java b/libjava/classpath/java/io/ObjectStreamField.java new file mode 100644 index 00000000000..611457b3cfb --- /dev/null +++ b/libjava/classpath/java/io/ObjectStreamField.java @@ -0,0 +1,412 @@ +/* ObjectStreamField.java -- Class used to store name and class of fields + Copyright (C) 1998, 1999, 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +import gnu.java.lang.reflect.TypeSignature; + +import java.lang.reflect.Field; +import java.security.AccessController; +import java.security.PrivilegedAction; + +/** + * This class intends to describe the field of a class for the serialization + * subsystem. Serializable fields in a serializable class can be explicitly + * exported using an array of ObjectStreamFields. + */ +public class ObjectStreamField implements Comparable +{ + private String name; + private Class type; + private String typename; + private int offset = -1; // XXX make sure this is correct + private boolean unshared; + private boolean persistent = false; + private boolean toset = true; + private Field field; + + ObjectStreamField (Field field) + { + this (field.getName(), field.getType()); + this.field = field; + } + + /** + * This constructor creates an ObjectStreamField instance + * which represents a field named <code>name</code> and is + * of the type <code>type</code>. + * + * @param name Name of the field to export. + * @param type Type of the field in the concerned class. + */ + public ObjectStreamField (String name, Class type) + { + this (name, type, false); + } + + /** + * This constructor creates an ObjectStreamField instance + * which represents a field named <code>name</code> and is + * of the type <code>type</code>. + * + * @param name Name of the field to export. + * @param type Type of the field in the concerned class. + * @param unshared true if field will be unshared, false otherwise. + */ + public ObjectStreamField (String name, Class type, boolean unshared) + { + if (name == null) + throw new NullPointerException(); + + this.name = name; + this.type = type; + this.typename = TypeSignature.getEncodingOfClass(type); + this.unshared = unshared; + } + + /** + * There are many cases you can not get java.lang.Class from typename + * if your context class loader cannot load it, then use typename to + * construct the field. + * + * @param name Name of the field to export. + * @param typename The coded name of the type for this field. + */ + ObjectStreamField (String name, String typename) + { + this.name = name; + this.typename = typename; + try + { + type = TypeSignature.getClassForEncoding(typename); + } + catch(ClassNotFoundException e) + { + } + } + + /** + * There are many cases you can not get java.lang.Class from typename + * if your context class loader cann not load it, then use typename to + * construct the field. + * + * @param name Name of the field to export. + * @param typename The coded name of the type for this field. + * @param loader The class loader to use to resolve class names. + */ + ObjectStreamField (String name, String typename, ClassLoader loader) + { + this.name = name; + this.typename = typename; + try + { + type = TypeSignature.getClassForEncoding(typename, true, loader); + } + catch(ClassNotFoundException e) + { + } + } + + /** + * This method returns the name of the field represented by the + * ObjectStreamField instance. + * + * @return A string containing the name of the field. + */ + public String getName () + { + return name; + } + + /** + * This method returns the class representing the type of the + * field which is represented by this instance of ObjectStreamField. + * + * @return A class representing the type of the field. + */ + public Class getType () + { + return type; + } + + /** + * This method returns the char encoded type of the field which + * is represented by this instance of ObjectStreamField. + * + * @return A char representing the type of the field. + */ + public char getTypeCode () + { + return typename.charAt (0); + } + + /** + * This method returns a more explicit type name than + * {@link #getTypeCode()} in the case the type is a real + * class (and not a primitive). + * + * @return The name of the type (class name) if it is not a + * primitive, in the other case null is returned. + */ + public String getTypeString () + { + // use intern() + if (isPrimitive()) + return null; + return typename.intern(); + } + + /** + * This method returns the current offset of the field in + * the serialization stream relatively to the other fields. + * The offset is expressed in bytes. + * + * @return The offset of the field in bytes. + * @see #setOffset(int) + */ + public int getOffset () + { + return offset; + } + + /** + * This method sets the current offset of the field. + * + * @param off The offset of the field in bytes. + * @see getOffset() + */ + protected void setOffset (int off) + { + offset = off; + } + + /** + * This method returns whether the field represented by this object is + * unshared or not. + * + * @return Tells if this field is unshared or not. + */ + public boolean isUnshared () + { + return unshared; + } + + /** + * This method returns true if the type of the field + * represented by this instance is a primitive. + * + * @return true if the type is a primitive, false + * in the other case. + */ + public boolean isPrimitive () + { + return typename.length() == 1; + } + + /** + * Compares this object to the given object. + * + * @param obj the object to compare to. + * + * @return -1, 0 or 1. + */ + public int compareTo (Object obj) + { + ObjectStreamField f = (ObjectStreamField) obj; + boolean this_is_primitive = isPrimitive (); + boolean f_is_primitive = f.isPrimitive (); + + if (this_is_primitive && !f_is_primitive) + return -1; + + if (!this_is_primitive && f_is_primitive) + return 1; + + return getName ().compareTo (f.getName ()); + } + + /** + * This method is specific to classpath's implementation and so has the default + * access. It changes the state of this field to "persistent". It means that + * the field should not be changed when the stream is read (if it is not + * explicitly specified using serialPersistentFields). + * + * @param persistent True if the field is persistent, false in the + * other cases. + * @see #isPersistent() + */ + void setPersistent(boolean persistent) + { + this.persistent = persistent; + } + + /** + * This method returns true if the field is marked as persistent. + * + * @return True if persistent, false in the other cases. + * @see #setPersistent(boolean) + */ + boolean isPersistent() + { + return persistent; + } + + /** + * This method is specific to classpath's implementation and so + * has the default access. It changes the state of this field as + * to be set by ObjectInputStream. + * + * @param toset True if this field should be set, false in the other + * cases. + * @see #isToSet() + */ + void setToSet(boolean toset) + { + this.toset = toset; + } + + /** + * This method returns true if the field is marked as to be + * set. + * + * @return True if it is to be set, false in the other cases. + * @see #setToSet(boolean) + */ + boolean isToSet() + { + return toset; + } + + /** + * This method searches for its field reference in the specified class + * object. It requests privileges. If an error occurs the internal field + * reference is not modified. + * + * @throws NoSuchFieldException if the field name does not exist in this class. + * @throws SecurityException if there was an error requesting the privileges. + */ + void lookupField(Class clazz) throws NoSuchFieldException, SecurityException + { + final Field f = clazz.getDeclaredField(name); + + AccessController.doPrivileged(new PrivilegedAction() + { + public Object run() + { + f.setAccessible(true); + return null; + } + }); + + this.field = f; + } + + /** + * This method check whether the field described by this + * instance of ObjectStreamField is compatible with the + * actual implementation of this field. + * + * @throws NullPointerException if this field does not exist + * in the real class. + * @throws InvalidClassException if the types are incompatible. + */ + void checkFieldType() throws InvalidClassException + { + Class ftype = field.getType(); + + if (!ftype.isAssignableFrom(type)) + throw new InvalidClassException + ("invalid field type for " + name + + " in class " + field.getDeclaringClass()); + } + + /** + * Returns a string representing this object. + * + * @return the string. + */ + public String toString () + { + return "ObjectStreamField< " + type + " " + name + " >"; + } + + final void setBooleanField(Object obj, boolean val) + { + VMObjectStreamClass.setBooleanNative(field, obj, val); + } + + final void setByteField(Object obj, byte val) + { + VMObjectStreamClass.setByteNative(field, obj, val); + } + + final void setCharField(Object obj, char val) + { + VMObjectStreamClass.setCharNative(field, obj, val); + } + + final void setShortField(Object obj, short val) + { + VMObjectStreamClass.setShortNative(field, obj, val); + } + + final void setIntField(Object obj, int val) + { + VMObjectStreamClass.setIntNative(field, obj, val); + } + + final void setLongField(Object obj, long val) + { + VMObjectStreamClass.setLongNative(field, obj, val); + } + + final void setFloatField(Object obj, float val) + { + VMObjectStreamClass.setFloatNative(field, obj, val); + } + + final void setDoubleField(Object obj, double val) + { + VMObjectStreamClass.setDoubleNative(field, obj, val); + } + + final void setObjectField(Object obj, Object val) + { + VMObjectStreamClass.setObjectNative(field, obj, val); + } +} diff --git a/libjava/classpath/java/io/OptionalDataException.java b/libjava/classpath/java/io/OptionalDataException.java new file mode 100644 index 00000000000..8d8b1bda0e3 --- /dev/null +++ b/libjava/classpath/java/io/OptionalDataException.java @@ -0,0 +1,91 @@ +/* OptionalDataException.java -- indicates unexpected data in serialized stream + Copyright (C) 1998, 2000, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This exception is thrown when unexpected data appears in the input + * stream from which a serialized object is being read. There are two + * cases:<br><ul> + * <li>The next stream element is primitive data. <code>eof</code> will + * be false, and <code>count</code> is the number of bytes of primitive + * data available.</li> + * <li>The data consumable by readObject or readExternal has been exhausted. + * <code>eof</code> is true, and <code>count</code> is 0.</li> + * </ul> + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + * @since 1.1 + * @status updated to 1.4 + */ +public class OptionalDataException extends ObjectStreamException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -8011121865681257820L; + + /** + * Whether or not the end of the stream has been reached. + * + * @serial the end of the buffer was reached + */ + public boolean eof; + + /** + * The number of valid bytes that can be read. + * + * @serial the bytes of the buffer remaining + */ + public int length; + + /** + * Create a new OptionalDataException with an eof parameter indicating + * whether or not the end of stream is reached and the number of valid + * bytes that may be read. + * + * @param eof 'true' if end of stream reached, 'false' otherwise + * @param count The number of valid bytes to be read + */ + OptionalDataException(boolean eof, int count) + { + this.eof = eof; + this.length = count; + } +} // class OptionalDataException diff --git a/libjava/classpath/java/io/OutputStream.java b/libjava/classpath/java/io/OutputStream.java new file mode 100644 index 00000000000..8608daaa161 --- /dev/null +++ b/libjava/classpath/java/io/OutputStream.java @@ -0,0 +1,140 @@ +/* OutputStream.java -- Base class for byte output streams + Copyright (C) 1998, 1999, 2001, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This abstract class forms the base of the hierarchy of classes that + * write output as a stream of bytes. It provides a common set of methods + * for writing bytes to stream. Subclasses implement and/or extend these + * methods to write bytes in a particular manner or to a particular + * destination such as a file on disk or network connection. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@cygnus.com) + */ +public abstract class OutputStream +{ + /** + * This is the default no-argument constructor for this class. This method + * does nothing in this class. + */ + public OutputStream () + { + } + + /** + * This method writes a single byte to the output stream. The byte written + * is the low eight bits of the <code>int</code> passed and a argument. + * <p> + * Subclasses must provide an implementation of this abstract method + * + * @param b The byte to be written to the output stream, passed as + * the low eight bits of an <code>int</code> + * + * @exception IOException If an error occurs + */ + public abstract void write (int b) throws IOException; + + /** + * This method all the writes bytes from the passed array to the + * output stream. This method is equivalent to <code>write(b, 0, + * buf.length)</code> which is exactly how it is implemented in this + * class. + * + * @param b The array of bytes to write + * + * @exception IOException If an error occurs + */ + public void write (byte[] b) throws IOException, NullPointerException + { + write (b, 0, b.length); + } + + /** + * This method writes <code>len</code> bytes from the specified array + * <code>b</code> starting at index <code>off</code> into the array. + * <p> + * This method in this class calls the single byte <code>write()</code> + * method in a loop until all bytes have been written. Subclasses should + * override this method if possible in order to provide a more efficent + * implementation. + * + * @param b The array of bytes to write from + * @param off The index into the array to start writing from + * @param len The number of bytes to write + * + * @exception IOException If an error occurs + */ + public void write (byte[] b, int off, int len) + throws IOException, NullPointerException, IndexOutOfBoundsException + { + if (off < 0 || len < 0 || off + len > b.length) + throw new ArrayIndexOutOfBoundsException (); + for (int i = 0; i < len; ++i) + write (b[off + i]); + } + + /** + * This method forces any data that may have been buffered to be written + * to the underlying output device. Please note that the host environment + * might perform its own buffering unbeknowst to Java. In that case, a + * write made (for example, to a disk drive) might be cached in OS + * buffers instead of actually being written to disk. + * <p> + * This method in this class does nothing. + * + * @exception IOException If an error occurs + */ + public void flush () throws IOException + { + } + + /** + * This method closes the stream. Any internal or native resources + * associated with this stream are freed. Any subsequent attempt to + * access the stream might throw an exception. + * <p> + * This method in this class does nothing. + * + * @exception IOException If an error occurs + */ + public void close () throws IOException + { + } +} diff --git a/libjava/classpath/java/io/OutputStreamWriter.java b/libjava/classpath/java/io/OutputStreamWriter.java new file mode 100644 index 00000000000..ee229796cce --- /dev/null +++ b/libjava/classpath/java/io/OutputStreamWriter.java @@ -0,0 +1,356 @@ +/* OutputStreamWriter.java -- Writer that converts chars to bytes + Copyright (C) 1998, 1999, 2000, 2001, 2003, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +import gnu.java.nio.charset.EncodingHelper; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.MalformedInputException; +import java.nio.charset.UnsupportedCharsetException; +import java.nio.charset.CharacterCodingException; +import java.nio.charset.IllegalCharsetNameException; +import java.nio.charset.CoderResult; +import java.nio.charset.CodingErrorAction; +import java.nio.charset.Charset; +import java.nio.charset.CharsetEncoder; + +/** + * This class writes characters to an output stream that is byte oriented + * It converts the chars that are written to bytes using an encoding layer, + * which is specific to a particular encoding standard. The desired + * encoding can either be specified by name, or if no encoding is specified, + * the system default encoding will be used. The system default encoding + * name is determined from the system property <code>file.encoding</code>. + * The only encodings that are guaranteed to be available are "8859_1" + * (the Latin-1 character set) and "UTF8". Unfortunately, Java does not + * provide a mechanism for listing the encodings that are supported in + * a given implementation. + * <p> + * Here is a list of standard encoding names that may be available: + * <p> + * <ul> + * <li>8859_1 (ISO-8859-1/Latin-1) + * <li>8859_2 (ISO-8859-2/Latin-2) + * <li>8859_3 (ISO-8859-3/Latin-3) + * <li>8859_4 (ISO-8859-4/Latin-4) + * <li>8859_5 (ISO-8859-5/Latin-5) + * <li>8859_6 (ISO-8859-6/Latin-6) + * <li>8859_7 (ISO-8859-7/Latin-7) + * <li>8859_8 (ISO-8859-8/Latin-8) + * <li>8859_9 (ISO-8859-9/Latin-9) + * <li>ASCII (7-bit ASCII) + * <li>UTF8 (UCS Transformation Format-8) + * <li>More Later + * </ul> + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Per Bothner (bothner@cygnus.com) + * @date April 17, 1998. + */ +public class OutputStreamWriter extends Writer +{ + /** + * The output stream. + */ + private OutputStream out; + + /** + * The charset encoder. + */ + private CharsetEncoder encoder; + + /** + * java.io canonical name of the encoding. + */ + private String encodingName; + + /** + * Buffer output before character conversion as it has costly overhead. + */ + private CharBuffer outputBuffer; + private final static int BUFFER_SIZE = 1024; + + /** + * This method initializes a new instance of <code>OutputStreamWriter</code> + * to write to the specified stream using a caller supplied character + * encoding scheme. Note that due to a deficiency in the Java language + * design, there is no way to determine which encodings are supported. + * + * @param out The <code>OutputStream</code> to write to + * @param encoding_scheme The name of the encoding scheme to use for + * character to byte translation + * + * @exception UnsupportedEncodingException If the named encoding is + * not available. + */ + public OutputStreamWriter (OutputStream out, String encoding_scheme) + throws UnsupportedEncodingException + { + this.out = out; + try + { + // Don't use NIO if avoidable + if(EncodingHelper.isISOLatin1(encoding_scheme)) + { + encodingName = "ISO8859_1"; + encoder = null; + return; + } + + /* + * Workraround for encodings with a byte-order-mark. + * We only want to write it once per stream. + */ + try { + if(encoding_scheme.equalsIgnoreCase("UnicodeBig") || + encoding_scheme.equalsIgnoreCase("UTF-16") || + encoding_scheme.equalsIgnoreCase("UTF16")) + { + encoding_scheme = "UTF-16BE"; + out.write((byte)0xFE); + out.write((byte)0xFF); + } else if(encoding_scheme.equalsIgnoreCase("UnicodeLittle")){ + encoding_scheme = "UTF-16LE"; + out.write((byte)0xFF); + out.write((byte)0xFE); + } + } catch(IOException ioe){ + } + + outputBuffer = CharBuffer.allocate(BUFFER_SIZE); + + Charset cs = EncodingHelper.getCharset(encoding_scheme); + if(cs == null) + throw new UnsupportedEncodingException("Encoding "+encoding_scheme+ + " unknown"); + encoder = cs.newEncoder(); + encodingName = EncodingHelper.getOldCanonical(cs.name()); + + encoder.onMalformedInput(CodingErrorAction.REPLACE); + encoder.onUnmappableCharacter(CodingErrorAction.REPLACE); + } catch(RuntimeException e) { + // Default to ISO Latin-1, will happen if this is called, for instance, + // before the NIO provider is loadable. + encoder = null; + encodingName = "ISO8859_1"; + } + } + + /** + * This method initializes a new instance of <code>OutputStreamWriter</code> + * to write to the specified stream using the default encoding. + * + * @param out The <code>OutputStream</code> to write to + */ + public OutputStreamWriter (OutputStream out) + { + this.out = out; + outputBuffer = null; + try + { + String encoding = System.getProperty("file.encoding"); + Charset cs = Charset.forName(encoding); + encoder = cs.newEncoder(); + encodingName = EncodingHelper.getOldCanonical(cs.name()); + } catch(RuntimeException e) { + encoder = null; + encodingName = "ISO8859_1"; + } + if(encoder != null) + { + encoder.onMalformedInput(CodingErrorAction.REPLACE); + encoder.onUnmappableCharacter(CodingErrorAction.REPLACE); + outputBuffer = CharBuffer.allocate(BUFFER_SIZE); + } + } + + /** + * This method closes this stream, and the underlying + * <code>OutputStream</code> + * + * @exception IOException If an error occurs + */ + public void close () throws IOException + { + if(out == null) + return; + flush(); + out.close (); + out = null; + } + + /** + * This method returns the name of the character encoding scheme currently + * in use by this stream. If the stream has been closed, then this method + * may return <code>null</code>. + * + * @return The encoding scheme name + */ + public String getEncoding () + { + return out != null ? encodingName : null; + } + + /** + * This method flushes any buffered bytes to the underlying output sink. + * + * @exception IOException If an error occurs + */ + public void flush () throws IOException + { + if(out != null){ + if(outputBuffer != null){ + char[] buf = new char[outputBuffer.position()]; + if(buf.length > 0){ + outputBuffer.flip(); + outputBuffer.get(buf); + writeConvert(buf, 0, buf.length); + outputBuffer.clear(); + } + } + out.flush (); + } + } + + /** + * This method writes <code>count</code> characters from the specified + * array to the output stream starting at position <code>offset</code> + * into the array. + * + * @param buf The array of character to write from + * @param offset The offset into the array to start writing chars from + * @param count The number of chars to write. + * + * @exception IOException If an error occurs + */ + public void write (char[] buf, int offset, int count) throws IOException + { + if(out == null) + throw new IOException("Stream is closed."); + if(buf == null) + throw new IOException("Buffer is null."); + + if(outputBuffer != null) + { + if(count >= outputBuffer.remaining()) + { + int r = outputBuffer.remaining(); + outputBuffer.put(buf, offset, r); + writeConvert(outputBuffer.array(), 0, BUFFER_SIZE); + outputBuffer.clear(); + offset += r; + count -= r; + // if the remaining bytes is larger than the whole buffer, + // just don't buffer. + if(count >= outputBuffer.remaining()){ + writeConvert(buf, offset, count); + return; + } + } + outputBuffer.put(buf, offset, count); + } else writeConvert(buf, offset, count); + } + + /** + * Converts and writes characters. + */ + private void writeConvert (char[] buf, int offset, int count) + throws IOException + { + if(encoder == null) + { + byte[] b = new byte[count]; + for(int i=0;i<count;i++) + b[i] = (byte)((buf[offset+i] <= 0xFF)?buf[offset+i]:'?'); + out.write(b); + } else { + try { + ByteBuffer output = encoder.encode(CharBuffer.wrap(buf,offset,count)); + encoder.reset(); + if(output.hasArray()) + out.write(output.array()); + else + { + byte[] outbytes = new byte[output.remaining()]; + output.get(outbytes); + out.write(outbytes); + } + } catch(IllegalStateException e) { + throw new IOException("Internal error."); + } catch(MalformedInputException e) { + throw new IOException("Invalid character sequence."); + } catch(CharacterCodingException e) { + throw new IOException("Unmappable character."); + } + } + } + + /** + * This method writes <code>count</code> bytes from the specified + * <code>String</code> starting at position <code>offset</code> into the + * <code>String</code>. + * + * @param str The <code>String</code> to write chars from + * @param offset The position in the <code>String</code> to start + * writing chars from + * @param count The number of chars to write + * + * @exception IOException If an error occurs + */ + public void write (String str, int offset, int count) throws IOException + { + if(str == null) + throw new IOException("String is null."); + + write(str.toCharArray(), offset, count); + } + + /** + * This method writes a single character to the output stream. + * + * @param ch The char to write, passed as an int. + * + * @exception IOException If an error occurs + */ + public void write (int ch) throws IOException + { + write(new char[]{ (char)ch }, 0, 1); + } +} // class OutputStreamWriter + diff --git a/libjava/classpath/java/io/PipedInputStream.java b/libjava/classpath/java/io/PipedInputStream.java new file mode 100644 index 00000000000..beb310b4f0c --- /dev/null +++ b/libjava/classpath/java/io/PipedInputStream.java @@ -0,0 +1,374 @@ +/* PipedInputStream.java -- Read portion of piped streams. + Copyright (C) 1998, 1999, 2000, 2001, 2003, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.io; + +// NOTE: This implementation is very similar to that of PipedReader. If you +// fix a bug in here, chances are you should make a similar change to the +// PipedReader code. + +/** + * An input stream that reads its bytes from an output stream + * to which it is connected. + * <p> + * Data is read and written to an internal buffer. It is highly recommended + * that the <code>PipedInputStream</code> and connected + * <code>PipedOutputStream</code> + * be part of different threads. If they are not, the read and write + * operations could deadlock their thread. + * + * @specnote The JDK implementation appears to have some undocumented + * functionality where it keeps track of what thread is writing + * to pipe and throws an IOException if that thread susequently + * dies. This behaviour seems dubious and unreliable - we don't + * implement it. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class PipedInputStream extends InputStream +{ + /** PipedOutputStream to which this is connected. Null only if this + * InputStream hasn't been connected yet. */ + PipedOutputStream source; + + /** Set to true if close() has been called on this InputStream. */ + boolean closed; + + + /** + * The size of the internal buffer used for input/output. + */ + /* The "Constant Field Values" Javadoc of the Sun J2SE 1.4 + * specifies 1024. + */ + protected static final int PIPE_SIZE = 1024; + + + /** + * This is the internal circular buffer used for storing bytes written + * to the pipe and from which bytes are read by this stream + */ + protected byte[] buffer = new byte[PIPE_SIZE]; + + /** + * The index into buffer where the next byte from the connected + * <code>PipedOutputStream</code> will be written. If this variable is + * equal to <code>out</code>, then the buffer is full. If set to < 0, + * the buffer is empty. + */ + protected int in = -1; + + /** + * This index into the buffer where bytes will be read from. + */ + protected int out = 0; + + /** Buffer used to implement single-argument read/receive */ + private byte[] read_buf = new byte[1]; + + /** + * Creates a new <code>PipedInputStream</code> that is not connected to a + * <code>PipedOutputStream</code>. It must be connected before bytes can + * be read from this stream. + */ + public PipedInputStream() + { + } + + /** + * This constructor creates a new <code>PipedInputStream</code> and connects + * it to the passed in <code>PipedOutputStream</code>. The stream is then + * ready for reading. + * + * @param source The <code>PipedOutputStream</code> to connect this + * stream to + * + * @exception IOException If <code>source</code> is already connected. + */ + public PipedInputStream(PipedOutputStream source) throws IOException + { + connect(source); + } + + /** + * This method connects this stream to the passed in + * <code>PipedOutputStream</code>. + * This stream is then ready for reading. If this stream is already + * connected or has been previously closed, then an exception is thrown + * + * @param src The <code>PipedOutputStream</code> to connect this stream to + * + * @exception IOException If this PipedInputStream or <code>source</code> + * has been connected already. + */ + public void connect(PipedOutputStream source) throws IOException + { + // The JDK (1.3) does not appear to check for a previously closed + // connection here. + + if (this.source != null || source.sink != null) + throw new IOException ("Already connected"); + + source.sink = this; + this.source = source; + } + + /** + * This method receives a byte of input from the source PipedOutputStream. + * If the internal circular buffer is full, this method blocks. + * + * @param val The byte to write to this stream + * + * @exception IOException if error occurs + * @specnote Weird. This method must be some sort of accident. + */ + protected synchronized void receive(int val) throws IOException + { + read_buf[0] = (byte) (val & 0xff); + receive (read_buf, 0, 1); + } + + /** + * This method is used by the connected <code>PipedOutputStream</code> to + * write bytes into the buffer. + * + * @param buf The array containing bytes to write to this stream + * @param offset The offset into the array to start writing from + * @param len The number of bytes to write. + * + * @exception IOException If an error occurs + * @specnote This code should be in PipedOutputStream.write, but we + * put it here in order to support that bizarre recieve(int) + * method. + */ + synchronized void receive(byte[] buf, int offset, int len) + throws IOException + { + if (closed) + throw new IOException ("Pipe closed"); + + int bufpos = offset; + int copylen; + + while (len > 0) + { + try + { + while (in == out) + { + // The pipe is full. Wake up any readers and wait for them. + notifyAll(); + wait(); + // The pipe could have been closed while we were waiting. + if (closed) + throw new IOException ("Pipe closed"); + } + } + catch (InterruptedException ix) + { + throw new InterruptedIOException (); + } + + if (in < 0) // The pipe is empty. + in = 0; + + // Figure out how many bytes from buf can be copied without + // overrunning out or going past the length of the buffer. + if (in < out) + copylen = Math.min (len, out - in); + else + copylen = Math.min (len, buffer.length - in); + + // Copy bytes until the pipe is filled, wrapping if necessary. + System.arraycopy(buf, bufpos, buffer, in, copylen); + len -= copylen; + bufpos += copylen; + in += copylen; + if (in == buffer.length) + in = 0; + } + // Notify readers that new data is in the pipe. + notifyAll(); + } + + /** + * This method reads one byte from the stream. + * -1 is returned to indicated that no bytes can be read + * because the end of the stream was reached. If the stream is already + * closed, a -1 will again be returned to indicate the end of the stream. + * + * <p>This method will block if no byte is available to be read.</p> + * + * @return the value of the read byte value, or -1 of the end of the stream + * was reached + * + * @throws IOException if an error occured + */ + public int read() throws IOException + { + // Method operates by calling the multibyte overloaded read method + // Note that read_buf is an internal instance variable. I allocate it + // there to avoid constant reallocation overhead for applications that + // call this method in a loop at the cost of some unneeded overhead + // if this method is never called. + + int r = read(read_buf, 0, 1); + return r != -1 ? (read_buf[0] & 0xff) : -1; + } + + /** + * This method reads bytes from the stream into a caller supplied buffer. + * It starts storing bytes at position <code>offset</code> into the + * buffer and + * reads a maximum of <code>len</code> bytes. Note that this method + * can actually + * read fewer than <code>len</code> bytes. The actual number of bytes + * read is + * returned. A -1 is returned to indicated that no bytes can be read + * because the end of the stream was reached - ie close() was called on the + * connected PipedOutputStream. + * <p> + * This method will block if no bytes are available to be read. + * + * @param buf The buffer into which bytes will be stored + * @param offset The index into the buffer at which to start writing. + * @param len The maximum number of bytes to read. + * + * @exception IOException If <code>close()</code> was called on this Piped + * InputStream. + */ + public synchronized int read(byte[] buf, int offset, int len) + throws IOException + { + if (source == null) + throw new IOException ("Not connected"); + if (closed) + throw new IOException ("Pipe closed"); + + // If the buffer is empty, wait until there is something in the pipe + // to read. + try + { + while (in < 0) + { + if (source.closed) + return -1; + wait(); + } + } + catch (InterruptedException ix) + { + throw new InterruptedIOException(); + } + + int total = 0; + int copylen; + + while (true) + { + // Figure out how many bytes from the pipe can be copied without + // overrunning in or going past the length of buf. + if (out < in) + copylen = Math.min (len, in - out); + else + copylen = Math.min (len, buffer.length - out); + + System.arraycopy (buffer, out, buf, offset, copylen); + offset += copylen; + len -= copylen; + out += copylen; + total += copylen; + + if (out == buffer.length) + out = 0; + + if (out == in) + { + // Pipe is now empty. + in = -1; + out = 0; + } + + // If output buffer is filled or the pipe is empty, we're done. + if (len == 0 || in == -1) + { + // Notify any waiting outputstream that there is now space + // to write. + notifyAll(); + return total; + } + } + } + + /** + * This method returns the number of bytes that can be read from this stream + * before blocking could occur. This is the number of bytes that are + * currently unread in the internal circular buffer. Note that once this + * many additional bytes are read, the stream may block on a subsequent + * read, but it not guaranteed to block. + * + * @return The number of bytes that can be read before blocking might occur + * + * @exception IOException If an error occurs + */ + public synchronized int available() throws IOException + { + // The JDK 1.3 implementation does not appear to check for the closed or + // unconnected stream conditions here. + + if (in < 0) + return 0; + else if (out < in) + return in - out; + else + return (buffer.length - out) + in; + } + + /** + * This methods closes the stream so that no more data can be read + * from it. + * + * @exception IOException If an error occurs + */ + public synchronized void close() throws IOException + { + closed = true; + // Wake any thread which may be in receive() waiting to write data. + notifyAll(); + } +} + diff --git a/libjava/classpath/java/io/PipedOutputStream.java b/libjava/classpath/java/io/PipedOutputStream.java new file mode 100644 index 00000000000..81881050d1b --- /dev/null +++ b/libjava/classpath/java/io/PipedOutputStream.java @@ -0,0 +1,181 @@ +/* PipedOutputStream.java -- Write portion of piped streams. + Copyright (C) 1998, 2000, 2001, 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +// NOTE: This implementation is very similar to that of PipedWriter. If you +// fix a bug in here, chances are you should make a similar change to the +// PipedWriter code. + +/** + * This class writes its bytes to a <code>PipedInputStream</code> to + * which it is connected. + * <p> + * It is highly recommended that a <code>PipedOutputStream</code> and its + * connected <code>PipedInputStream</code> be in different threads. If + * they are in the same thread, read and write operations could deadlock + * the thread. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class PipedOutputStream extends OutputStream +{ + /** Target PipedInputStream to which this is connected. Null only if this + * OutputStream hasn't been connected yet. */ + PipedInputStream sink; + + /** Set to true if close() has been called on this OutputStream. */ + boolean closed; + + /** + * Create an unconnected PipedOutputStream. It must be connected + * to a <code>PipedInputStream</code> using the <code>connect</code> + * method prior to writing any data or an exception will be thrown. + */ + public PipedOutputStream() + { + } + + /** + * Create a new <code>PipedOutputStream</code> instance + * to write to the specified <code>PipedInputStream</code>. This stream + * is then ready for writing. + * + * @param sink The <code>PipedInputStream</code> to connect this stream to. + * + * @exception IOException If <code>sink</code> has already been connected + * to a different PipedOutputStream. + */ + public PipedOutputStream(PipedInputStream sink) throws IOException + { + sink.connect(this); + } + + /** + * Connects this object to the specified <code>PipedInputStream</code> + * object. This stream will then be ready for writing. + * + * @param sink The <code>PipedInputStream</code> to connect this stream to + * + * @exception IOException If the stream has not been connected or has + * been closed. + */ + public void connect(PipedInputStream sink) throws IOException + { + if (this.sink != null || sink.source != null) + throw new IOException ("Already connected"); + sink.connect(this); + } + + /** + * Write a single byte of date to the stream. Note that this method will + * block if the <code>PipedInputStream</code> to which this object is + * connected has a full buffer. + * + * @param b The byte of data to be written, passed as an <code>int</code>. + * + * @exception IOException If the stream has not been connected or has + * been closed. + */ + public void write(int b) throws IOException + { + if (sink == null) + throw new IOException ("Not connected"); + if (closed) + throw new IOException ("Pipe closed"); + + sink.receive (b); + } + + /** + * This method writes <code>len</code> bytes of data from the byte array + * <code>buf</code> starting at index <code>offset</code> in the array + * to the stream. Note that this method will block if the + * <code>PipedInputStream</code> to which this object is connected has + * a buffer that cannot hold all of the bytes to be written. + * + * @param buffer The array containing bytes to write to the stream. + * @param offset The index into the array to start writing bytes from. + * @param len The number of bytes to write. + * + * @exception IOException If the stream has not been connected or has + * been closed. + */ + public void write(byte[] buffer, int offset, int len) throws IOException + { + if (sink == null) + throw new IOException ("Not connected"); + if (closed) + throw new IOException ("Pipe closed"); + + sink.receive(buffer, offset, len); + } + + /** + * This method does nothing. + * + * @exception IOException If the stream is closed. + * @specnote You'd think that this method would block until the sink + * had read all available data. Thats not the case - this method + * appears to be a no-op? + */ + public void flush() throws IOException + { + } + + /** + * This method closes this stream so that no more data can be written + * to it. Any further attempts to write to this stream may throw an + * exception + * + * @exception IOException If an error occurs + */ + public void close() throws IOException + { + // A close call on an unconnected PipedOutputStream has no effect. + if (sink != null) + { + closed = true; + // Notify any waiting readers that the stream is now closed. + synchronized (sink) + { + sink.notifyAll(); + } + } + } +} diff --git a/libjava/classpath/java/io/PipedReader.java b/libjava/classpath/java/io/PipedReader.java new file mode 100644 index 00000000000..90fc10f672d --- /dev/null +++ b/libjava/classpath/java/io/PipedReader.java @@ -0,0 +1,361 @@ +/* PipedReader.java -- Read portion of piped character streams. + Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.io; + +// NOTE: This implementation is very similar to that of PipedInputStream. +// If you fix a bug in here, chances are you should make a similar change to +// the PipedInputStream code. + +/** + * An input stream that reads characters from a piped writer to which it is + * connected. + * <p> + * Data is read and written to an internal buffer. It is highly recommended + * that the <code>PipedReader</code> and connected <code>PipedWriter</code> + * be part of different threads. If they are not, there is a possibility + * that the read and write operations could deadlock their thread. + * + * @specnote The JDK implementation appears to have some undocumented + * functionality where it keeps track of what thread is writing + * to pipe and throws an IOException if that thread susequently + * dies. This behaviour seems dubious and unreliable - we don't + * implement it. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class PipedReader extends Reader +{ + /** PipedWriter to which this is connected. Null only if this + * Reader hasn't been connected yet. */ + PipedWriter source; + + /** Set to true if close() has been called on this Reader. */ + boolean closed; + + /** + * The size of the internal buffer used for input/output. + */ + static final int PIPE_SIZE = 2048; + + /** + * This is the internal circular buffer used for storing chars written + * to the pipe and from which chars are read by this stream + */ + char[] buffer = new char[PIPE_SIZE]; + + /** + * The index into buffer where the next char from the connected + * <code>PipedWriter</code> will be written. If this variable is + * equal to <code>out</code>, then the buffer is full. If set to < 0, + * the buffer is empty. + */ + int in = -1; + + /** + * This index into the buffer where chars will be read from. + */ + int out = 0; + + /** Buffer used to implement single-argument read/receive */ + char[] read_buf = new char[1]; + + /** + * Creates a new <code>PipedReader</code> that is not connected to a + * <code>PipedWriter</code>. It must be connected before chars can + * be read from this stream. + */ + public PipedReader() + { + } + + /** + * This constructor creates a new <code>PipedReader</code> and connects + * it to the passed in <code>PipedWriter</code>. The stream is then + * ready for reading. + * + * @param source The <code>PipedWriter</code> to connect this stream to + * + * @exception IOException If <code>source</code> is already connected. + */ + public PipedReader(PipedWriter source) throws IOException + { + connect(source); + } + + /** + * This method connects this stream to the passed in + * <code>PipedWriter</code>. + * This stream is then ready for reading. If this stream is already + * connected or has been previously closed, then an exception is thrown + * + * @param source The <code>PipedWriter</code> to connect this stream to + * + * @exception IOException If this PipedReader or <code>source</code> + * has been connected already. + */ + public void connect(PipedWriter source) throws IOException + { + // The JDK (1.3) does not appear to check for a previously closed + // connection here. + + if (this.source != null || source.sink != null) + throw new IOException ("Already connected"); + + source.sink = this; + this.source = source; + } + + /** + * This method is used by the connected <code>PipedWriter</code> to + * write chars into the buffer. + * + * @param buf The array containing chars to write to this stream + * @param offset The offset into the array to start writing from + * @param len The number of chars to write. + * + * @exception IOException If an error occurs + * @specnote This code should be in PipedWriter.write, but we + * put it here in order to support that bizarre recieve(int) + * method. + */ + void receive(char[] buf, int offset, int len) + throws IOException + { + synchronized (lock) + { + if (closed) + throw new IOException ("Pipe closed"); + + int bufpos = offset; + int copylen; + + while (len > 0) + { + try + { + while (in == out) + { + // The pipe is full. Wake up any readers and wait for them. + lock.notifyAll(); + lock.wait(); + // The pipe could have been closed while we were waiting. + if (closed) + throw new IOException ("Pipe closed"); + } + } + catch (InterruptedException ix) + { + throw new InterruptedIOException (); + } + + if (in < 0) // The pipe is empty. + in = 0; + + // Figure out how many chars from buf can be copied without + // overrunning out or going past the length of the buffer. + if (in < out) + copylen = Math.min (len, out - in); + else + copylen = Math.min (len, buffer.length - in); + + // Copy chars until the pipe is filled, wrapping if necessary. + System.arraycopy(buf, bufpos, buffer, in, copylen); + len -= copylen; + bufpos += copylen; + in += copylen; + if (in == buffer.length) + in = 0; + } + // Notify readers that new data is in the pipe. + lock.notifyAll(); + } + } + + /** + * This method reads chars from the stream into a caller supplied buffer. + * It starts storing chars at position <code>offset</code> into the + * buffer and + * reads a maximum of <code>len</code> chars. Note that this method + * can actually + * read fewer than <code>len</code> chars. The actual number of chars + * read is + * returned. A -1 is returned to indicated that no chars can be read + * because the end of the stream was reached. If the stream is already + * closed, a -1 will again be returned to indicate the end of the stream. + * <p> + * This method will block if no char is available to be read. + */ + public int read() throws IOException + { + // Method operates by calling the multichar overloaded read method + // Note that read_buf is an internal instance variable. I allocate it + // there to avoid constant reallocation overhead for applications that + // call this method in a loop at the cost of some unneeded overhead + // if this method is never called. + + int r = read(read_buf, 0, 1); + return r != -1 ? read_buf[0] : -1; + } + + /** + * This method reads characters from the stream into a caller supplied + * buffer. It starts storing chars at position <code>offset</code> into + * the buffer and reads a maximum of <code>len</code> chars. Note that + * this method can actually read fewer than <code>len</code> chars. + * The actual number of chars read is + * returned. A -1 is returned to indicated that no chars can be read + * because the end of the stream was reached - ie close() was called on the + * connected PipedWriter. + * <p> + * This method will block if no chars are available to be read. + * + * @param buf The buffer into which chars will be stored + * @param offset The index into the buffer at which to start writing. + * @param len The maximum number of chars to read. + * + * @exception IOException If <code>close()</code> was called on this Piped + * Reader. + */ + public int read(char[] buf, int offset, int len) + throws IOException + { + synchronized (lock) + { + if (source == null) + throw new IOException ("Not connected"); + if (closed) + throw new IOException ("Pipe closed"); + + // If the buffer is empty, wait until there is something in the pipe + // to read. + try + { + while (in < 0) + { + if (source.closed) + return -1; + lock.wait(); + } + } + catch (InterruptedException ix) + { + throw new InterruptedIOException(); + } + + int total = 0; + int copylen; + + while (true) + { + // Figure out how many chars from the pipe can be copied without + // overrunning in or going past the length of buf. + if (out < in) + copylen = Math.min (len, in - out); + else + copylen = Math.min (len, buffer.length - out); + + System.arraycopy (buffer, out, buf, offset, copylen); + offset += copylen; + len -= copylen; + out += copylen; + total += copylen; + + if (out == buffer.length) + out = 0; + + if (out == in) + { + // Pipe is now empty. + in = -1; + out = 0; + } + + // If output buffer is filled or the pipe is empty, we're done. + if (len == 0 || in == -1) + { + // Notify any waiting Writer that there is now space + // to write. + lock.notifyAll(); + return total; + } + } + } + } + + public boolean ready() throws IOException + { + // The JDK 1.3 implementation does not appear to check for the closed or + // unconnected stream conditions here. However, checking for a + // closed stream is explicitly required by the JDK 1.2 and 1.3 + // documentation (for Reader.close()), so we do it. + + synchronized (lock) + { + if (closed) + throw new IOException("Pipe closed"); + + if (in < 0) + return false; + + int count; + if (out < in) + count = in - out; + else + count = (buffer.length - out) - in; + + return (count > 0); + } + } + + /** + * This methods closes the stream so that no more data can be read + * from it. + * + * @exception IOException If an error occurs + */ + public void close() throws IOException + { + synchronized (lock) + { + closed = true; + // Wake any thread which may be in receive() waiting to write data. + lock.notifyAll(); + } + } +} + diff --git a/libjava/classpath/java/io/PipedWriter.java b/libjava/classpath/java/io/PipedWriter.java new file mode 100644 index 00000000000..92786e5de7d --- /dev/null +++ b/libjava/classpath/java/io/PipedWriter.java @@ -0,0 +1,182 @@ +/* PipedWriter.java -- Write portion of piped character streams. + Copyright (C) 1998, 2000, 2001, 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +// NOTE: This implementation is very similar to that of PipedOutputStream. +// If you fix a bug in here, chances are you should make a similar change to +// the PipedOutputStream code. + +/** + * This class writes its chars to a <code>PipedReader</code> to + * which it is connected. + * <p> + * It is highly recommended that a <code>PipedWriter</code> and its + * connected <code>PipedReader</code> be in different threads. If + * they are in the same thread, read and write operations could deadlock + * the thread. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class PipedWriter extends Writer +{ + /** Target PipedReader to which this is connected. Null only if this + * Writer hasn't been connected yet. */ + PipedReader sink; + + /** Set to true if close() has been called on this Writer. */ + boolean closed; + + /** Buffer used to implement single-argument write */ + char[] read_buf = new char[1]; + + /** + * Create an unconnected PipedWriter. It must be connected + * to a <code>PipedReader</code> using the <code>connect</code> + * method prior to writing any data or an exception will be thrown. + */ + public PipedWriter() + { + } + + /** + * Create a new <code>PipedWriter</code> instance + * to write to the specified <code>PipedReader</code>. This stream + * is then ready for writing. + * + * @param sink The <code>PipedReader</code> to connect this stream to. + * + * @exception IOException If <code>sink</code> has already been connected + * to a different PipedWriter. + */ + public PipedWriter(PipedReader sink) throws IOException + { + sink.connect(this); + } + + /** + * Connects this object to the specified <code>PipedReader</code> + * object. This stream will then be ready for writing. + * + * @param sink The <code>PipedReader</code> to connect this stream to + * + * @exception IOException If the stream has not been connected or has + * been closed. + */ + public void connect(PipedReader sink) throws IOException + { + if (this.sink != null || sink.source != null) + throw new IOException ("Already connected"); + sink.connect(this); + } + + /** + * Write a single char of date to the stream. Note that this method will + * block if the <code>PipedReader</code> to which this object is + * connected has a full buffer. + * + * @param b The char of data to be written, passed as an <code>int</code>. + * + * @exception IOException If the stream has not been connected or has + * been closed. + */ + public void write(int b) throws IOException + { + read_buf[0] = (char) (b & 0xffff); + sink.receive (read_buf, 0, 1); + } + + /** + * This method writes <code>len</code> chars of data from the char array + * <code>buf</code> starting at index <code>offset</code> in the array + * to the stream. Note that this method will block if the + * <code>PipedReader</code> to which this object is connected has + * a buffer that cannot hold all of the chars to be written. + * + * @param buffer The array containing chars to write to the stream. + * @param offset The index into the array to start writing chars from. + * @param len The number of chars to write. + * + * @exception IOException If the stream has not been connected or has + * been closed. + */ + public void write(char[] buffer, int offset, int len) throws IOException + { + if (sink == null) + throw new IOException ("Not connected"); + if (closed) + throw new IOException ("Pipe closed"); + + sink.receive(buffer, offset, len); + } + + /** + * This method does nothing. + * + * @exception IOException If the stream is closed. + * @specnote You'd think that this method would block until the sink + * had read all available data. Thats not the case - this method + * appears to be a no-op? + */ + public void flush() throws IOException + { + if (closed) + throw new IOException ("Pipe closed"); + } + + /** + * This method closes this stream so that no more data can be written + * to it. Any further attempts to write to this stream may throw an + * exception + * + * @exception IOException If an error occurs + */ + public void close() throws IOException + { + // A close call on an unconnected PipedWriter has no effect. + if (sink != null) + { + closed = true; + // Notify any waiting readers that the stream is now closed. + synchronized (sink) + { + sink.notifyAll(); + } + } + } +} diff --git a/libjava/classpath/java/io/PrintStream.java b/libjava/classpath/java/io/PrintStream.java new file mode 100644 index 00000000000..8e50559b310 --- /dev/null +++ b/libjava/classpath/java/io/PrintStream.java @@ -0,0 +1,553 @@ +/* PrintStream.java -- OutputStream for printing output + Copyright (C) 1998, 1999, 2001, 2003, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 + * "The Java Language Specification", ISBN 0-201-63451-1 + * Status: Believed complete and correct to 1.3 + */ + +/** + * This class prints Java primitive values and object to a stream as + * text. None of the methods in this class throw an exception. However, + * errors can be detected by calling the <code>checkError()</code> method. + * Additionally, this stream can be designated as "autoflush" when + * created so that any writes are automatically flushed to the underlying + * output sink when the current line is terminated. + * <p> + * This class converts char's into byte's using the system default encoding. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@cygnus.com) + */ +public class PrintStream extends FilterOutputStream +{ + /* Notice the implementation is quite similar to OutputStreamWriter. + * This leads to some minor duplication, because neither inherits + * from the other, and we want to maximize performance. */ + + // Line separator string. + private static final char[] line_separator + = System.getProperty("line.separator").toCharArray(); + + /** + * Encoding name + */ + private String encoding; + + /** + * This boolean indicates whether or not an error has ever occurred + * on this stream. + */ + private boolean error_occurred = false; + + /** + * This is <code>true</code> if auto-flush is enabled, + * <code>false</code> otherwise + */ + private boolean auto_flush; + + /** + * This method intializes a new <code>PrintStream</code> object to write + * to the specified output sink. + * + * @param out The <code>OutputStream</code> to write to. + */ + public PrintStream (OutputStream out) + { + this (out, false); + } + + /** + * This method intializes a new <code>PrintStream</code> object to write + * to the specified output sink. This constructor also allows "auto-flush" + * functionality to be specified where the stream will be flushed after + * every <code>print</code> or <code>println</code> call, when the + * <code>write</code> methods with array arguments are called, or when a + * single new-line character is written. + * <p> + * + * @param out The <code>OutputStream</code> to write to. + * @param auto_flush <code>true</code> to flush the stream after every + * line, <code>false</code> otherwise + */ + public PrintStream (OutputStream out, boolean auto_flush) + { + super (out); + + try { + this.encoding = System.getProperty("file.encoding"); + } catch (SecurityException e){ + this.encoding = "ISO8859_1"; + } catch (IllegalArgumentException e){ + this.encoding = "ISO8859_1"; + } catch (NullPointerException e){ + this.encoding = "ISO8859_1"; + } + this.auto_flush = auto_flush; + } + + /** + * This method intializes a new <code>PrintStream</code> object to write + * to the specified output sink. This constructor also allows "auto-flush" + * functionality to be specified where the stream will be flushed after + * every <code>print</code> or <code>println</code> call, when the + * <code>write</code> methods with array arguments are called, or when a + * single new-line character is written. + * <p> + * + * @param out The <code>OutputStream</code> to write to. + * @param auto_flush <code>true</code> to flush the stream after every + * line, <code>false</code> otherwise + * @param encoding The name of the character encoding to use for this + * object. + */ + public PrintStream (OutputStream out, boolean auto_flush, String encoding) + throws UnsupportedEncodingException + { + super (out); + + new String(new byte[]{0}, encoding); // check if encoding is supported + this.encoding = encoding; + this.auto_flush = auto_flush; + } + + /** + * This method checks to see if an error has occurred on this stream. Note + * that once an error has occurred, this method will continue to report + * <code>true</code> forever for this stream. Before checking for an + * error condition, this method flushes the stream. + * + * @return <code>true</code> if an error has occurred, + * <code>false</code> otherwise + */ + public boolean checkError () + { + flush (); + return error_occurred; + } + + /** + * This method can be called by subclasses to indicate that an error + * has occurred and should be reported by <code>checkError</code>. + */ + protected void setError () + { + error_occurred = true; + } + + /** + * This method closes this stream and all underlying streams. + */ + public void close () + { + try + { + flush(); + out.close(); + } + catch (InterruptedIOException iioe) + { + Thread.currentThread().interrupt(); + } + catch (IOException e) + { + setError (); + } + } + + /** + * This method flushes any buffered bytes to the underlying stream and + * then flushes that stream as well. + */ + public void flush () + { + try + { + out.flush(); + } + catch (InterruptedIOException iioe) + { + Thread.currentThread().interrupt(); + } + catch (IOException e) + { + setError (); + } + } + + private synchronized void print (String str, boolean println) + { + try + { + writeChars(str, 0, str.length()); + if (println) + writeChars(line_separator, 0, line_separator.length); + if (auto_flush) + flush(); + } + catch (InterruptedIOException iioe) + { + Thread.currentThread().interrupt(); + } + catch (IOException e) + { + setError (); + } + } + + private synchronized void print (char[] chars, int pos, int len, + boolean println) + { + try + { + writeChars(chars, pos, len); + if (println) + writeChars(line_separator, 0, line_separator.length); + if (auto_flush) + flush(); + } + catch (InterruptedIOException iioe) + { + Thread.currentThread().interrupt(); + } + catch (IOException e) + { + setError (); + } + } + + private void writeChars(char[] buf, int offset, int count) + throws IOException + { + byte[] bytes = (new String(buf, offset, count)).getBytes(encoding); + out.write(bytes, 0, bytes.length); + } + + private void writeChars(String str, int offset, int count) + throws IOException + { + byte[] bytes = str.substring(offset, offset+count).getBytes(encoding); + out.write(bytes, 0, bytes.length); + } + + /** + * This methods prints a boolean value to the stream. <code>true</code> + * values are printed as "true" and <code>false</code> values are printed + * as "false". + * + * @param bool The <code>boolean</code> value to print + */ + public void print (boolean bool) + { + print(String.valueOf(bool), false); + } + + /** + * This method prints an integer to the stream. The value printed is + * determined using the <code>String.valueOf()</code> method. + * + * @param inum The <code>int</code> value to be printed + */ + public void print (int inum) + { + print(String.valueOf(inum), false); + } + + /** + * This method prints a long to the stream. The value printed is + * determined using the <code>String.valueOf()</code> method. + * + * @param lnum The <code>long</code> value to be printed + */ + public void print (long lnum) + { + print(String.valueOf(lnum), false); + } + + /** + * This method prints a float to the stream. The value printed is + * determined using the <code>String.valueOf()</code> method. + * + * @param fnum The <code>float</code> value to be printed + */ + public void print (float fnum) + { + print(String.valueOf(fnum), false); + } + + /** + * This method prints a double to the stream. The value printed is + * determined using the <code>String.valueOf()</code> method. + * + * @param dnum The <code>double</code> value to be printed + */ + public void print (double dnum) + { + print(String.valueOf(dnum), false); + } + + /** + * This method prints an <code>Object</code> to the stream. The actual + * value printed is determined by calling the <code>String.valueOf()</code> + * method. + * + * @param obj The <code>Object</code> to print. + */ + public void print (Object obj) + { + print(obj == null ? "null" : obj.toString(), false); + } + + /** + * This method prints a <code>String</code> to the stream. The actual + * value printed depends on the system default encoding. + * + * @param str The <code>String</code> to print. + */ + public void print (String str) + { + print(str == null ? "null" : str, false); + } + + /** + * This method prints a char to the stream. The actual value printed is + * determined by the character encoding in use. + * + * @param ch The <code>char</code> value to be printed + */ + public synchronized void print (char ch) + { + print(new char[]{ch}, 0, 1, false); + } + + /** + * This method prints an array of characters to the stream. The actual + * value printed depends on the system default encoding. + * + * @param charArray The array of characters to print. + */ + public void print (char[] charArray) + { + print(charArray, 0, charArray.length, false); + } + + /** + * This method prints a line separator sequence to the stream. The value + * printed is determined by the system property <xmp>line.separator</xmp> + * and is not necessarily the Unix '\n' newline character. + */ + public void println () + { + print(line_separator, 0, line_separator.length, false); + } + + /** + * This methods prints a boolean value to the stream. <code>true</code> + * values are printed as "true" and <code>false</code> values are printed + * as "false". + * <p> + * This method prints a line termination sequence after printing the value. + * + * @param bool The <code>boolean</code> value to print + */ + public void println (boolean bool) + { + print(String.valueOf(bool), true); + } + + /** + * This method prints an integer to the stream. The value printed is + * determined using the <code>String.valueOf()</code> method. + * <p> + * This method prints a line termination sequence after printing the value. + * + * @param inum The <code>int</code> value to be printed + */ + public void println (int inum) + { + print(String.valueOf(inum), true); + } + + /** + * This method prints a long to the stream. The value printed is + * determined using the <code>String.valueOf()</code> method. + * <p> + * This method prints a line termination sequence after printing the value. + * + * @param lnum The <code>long</code> value to be printed + */ + public void println (long lnum) + { + print(String.valueOf(lnum), true); + } + + /** + * This method prints a float to the stream. The value printed is + * determined using the <code>String.valueOf()</code> method. + * <p> + * This method prints a line termination sequence after printing the value. + * + * @param fnum The <code>float</code> value to be printed + */ + public void println (float fnum) + { + print(String.valueOf(fnum), true); + } + + /** + * This method prints a double to the stream. The value printed is + * determined using the <code>String.valueOf()</code> method. + * <p> + * This method prints a line termination sequence after printing the value. + * + * @param dnum The <code>double</code> value to be printed + */ + public void println (double dnum) + { + print(String.valueOf(dnum), true); + } + + /** + * This method prints an <code>Object</code> to the stream. The actual + * value printed is determined by calling the <code>String.valueOf()</code> + * method. + * <p> + * This method prints a line termination sequence after printing the value. + * + * @param obj The <code>Object</code> to print. + */ + public void println (Object obj) + { + print(obj == null ? "null" : obj.toString(), true); + } + + /** + * This method prints a <code>String</code> to the stream. The actual + * value printed depends on the system default encoding. + * <p> + * This method prints a line termination sequence after printing the value. + * + * @param str The <code>String</code> to print. + */ + public void println (String str) + { + print (str == null ? "null" : str, true); + } + + /** + * This method prints a char to the stream. The actual value printed is + * determined by the character encoding in use. + * <p> + * This method prints a line termination sequence after printing the value. + * + * @param ch The <code>char</code> value to be printed + */ + public synchronized void println (char ch) + { + print(new char[]{ch}, 0, 1, true); + } + + /** + * This method prints an array of characters to the stream. The actual + * value printed depends on the system default encoding. + * <p> + * This method prints a line termination sequence after printing the value. + * + * @param charArray The array of characters to print. + */ + public void println (char[] charArray) + { + print(charArray, 0, charArray.length, true); + } + + /** + * This method writes a byte of data to the stream. If auto-flush is + * enabled, printing a newline character will cause the stream to be + * flushed after the character is written. + * + * @param oneByte The byte to be written + */ + public void write (int oneByte) + { + try + { + out.write (oneByte & 0xff); + + if (auto_flush && (oneByte == '\n')) + flush (); + } + catch (InterruptedIOException iioe) + { + Thread.currentThread ().interrupt (); + } + catch (IOException e) + { + setError (); + } + } + + /** + * This method writes <code>len</code> bytes from the specified array + * starting at index <code>offset</code> into the array. + * + * @param buffer The array of bytes to write + * @param offset The index into the array to start writing from + * @param len The number of bytes to write + */ + public void write (byte[] buffer, int offset, int len) + { + try + { + out.write (buffer, offset, len); + + if (auto_flush) + flush (); + } + catch (InterruptedIOException iioe) + { + Thread.currentThread ().interrupt (); + } + catch (IOException e) + { + setError (); + } + } +} // class PrintStream + diff --git a/libjava/classpath/java/io/PrintWriter.java b/libjava/classpath/java/io/PrintWriter.java new file mode 100644 index 00000000000..5fd0b162f31 --- /dev/null +++ b/libjava/classpath/java/io/PrintWriter.java @@ -0,0 +1,571 @@ +/* PrintWriter.java -- prints primitive values and objects to a stream as text + Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.io; + +/* Written using "Java Class Libraries", 2nd edition, plus online + * API docs for JDK 1.2 beta from http://www.javasoft.com. + * Status: Believed complete and correct. + * However, should use native methods for conversion. + */ + +/** + * This class prints Java primitive values and objects to a stream as + * text. None of the methods in this class throw an exception. However, + * errors can be detected by calling the <code>checkError()</code> method. + * Additionally, this stream can be designated as "autoflush" when + * created so that any writes are automatically flushed to the underlying + * output sink whenever one of the <code>println</code> methods is + * called. (Note that this differs from the <code>PrintStream</code> + * class which also auto-flushes when it encounters a newline character + * in the chars written). + * + * @author Per Bothner (bothner@cygnus.com) + * @author Aaron M. Renn (arenn@urbanophile.com) + * @date April 17, 1998. + */ +public class PrintWriter extends Writer +{ + /** + * <code>true</code> if auto-flush is enabled, <code>false</code> otherwise + */ + private boolean autoflush; + + /** + * This boolean indicates whether or not an error has ever occurred + * on this stream. + */ + private boolean error; + + /** + * This is the underlying <code>Writer</code> we are sending output + * to + */ + protected Writer out; + + /** + * This method intializes a new <code>PrintWriter</code> object to write + * to the specified output sink. The form of the constructor does not + * enable auto-flush functionality. + * + * @param wr The <code>Writer</code> to write to. + */ + public PrintWriter(Writer wr) + { + super(wr.lock); + this.out = wr; + } + + /** + * This method intializes a new <code>PrintWriter</code> object to write + * to the specified output sink. This constructor also allows "auto-flush" + * functionality to be specified where the stream will be flushed after + * every line is terminated or newline character is written. + * + * @param wr The <code>Writer</code> to write to. + * @param autoflush <code>true</code> to flush the stream after every + * line, <code>false</code> otherwise + */ + public PrintWriter(Writer wr, boolean autoflush) + { + super(wr.lock); + this.out = wr; + this.autoflush = autoflush; + } + + /** + * This method initializes a new <code>PrintWriter</code> object to write + * to the specified <code>OutputStream</code>. Characters will be converted + * to chars using the system default encoding. Auto-flush functionality + * will not be enabled. + * + * @param out The <code>OutputStream</code> to write to + */ + public PrintWriter(OutputStream out) + { + super(); + this.out = new OutputStreamWriter(out); + this.lock = this.out; + } + + /** + * This method initializes a new <code>PrintWriter</code> object to write + * to the specified <code>OutputStream</code>. Characters will be converted + * to chars using the system default encoding. This form of the + * constructor allows auto-flush functionality to be enabled if desired + * + * @param out The <code>OutputStream</code> to write to + * @param autoflush <code>true</code> to flush the stream after every + * <code>println</code> call, <code>false</code> otherwise. + */ + public PrintWriter(OutputStream out, boolean autoflush) + { + this(out); + this.autoflush = autoflush; + } + + /** + * This method can be called by subclasses to indicate that an error + * has occurred and should be reported by <code>checkError</code>. + */ + protected void setError() + { + error = true; + } + + /** + * This method checks to see if an error has occurred on this stream. Note + * that once an error has occurred, this method will continue to report + * <code>true</code> forever for this stream. Before checking for an + * error condition, this method flushes the stream. + * + * @return <code>true</code> if an error has occurred, + * <code>false</code> otherwise + */ + public boolean checkError() + { + flush(); + return error; + } + + /** + * This method flushes any buffered chars to the underlying stream and + * then flushes that stream as well. + */ + public void flush() + { + try + { + out.flush(); + } + catch (IOException ex) + { + error = true; + } + } + + /** + * This method closes this stream and all underlying streams. + */ + public void close() + { + try + { + out.close(); + } + catch (IOException ex) + { + error = true; + } + } + + /** + * This method prints a <code>String</code> to the stream. The actual + * value printed depends on the system default encoding. + * + * @param str The <code>String</code> to print. + */ + public void print(String str) + { + write(str == null ? "null" : str); + } + + /** + * This method prints a char to the stream. The actual value printed is + * determined by the character encoding in use. + * + * @param ch The <code>char</code> value to be printed + */ + public void print(char ch) + { + write((int) ch); + } + + /** + * This method prints an array of characters to the stream. The actual + * value printed depends on the system default encoding. + * + * @param charArray The array of characters to print. + */ + public void print(char[] charArray) + { + write(charArray, 0, charArray.length); + } + + /** + * This methods prints a boolean value to the stream. <code>true</code> + * values are printed as "true" and <code>false</code> values are printed + * as "false". + * + * @param bool The <code>boolean</code> value to print + */ + public void print(boolean bool) + { + // We purposely call write() and not print() here. This preserves + // compatibility with JDK 1.2. + write (bool ? "true" : "false"); + } + + /** + * This method prints an integer to the stream. The value printed is + * determined using the <code>String.valueOf()</code> method. + * + * @param inum The <code>int</code> value to be printed + */ + public void print(int inum) + { + // We purposely call write() and not print() here. This preserves + // compatibility with JDK 1.2. + write(Integer.toString(inum)); + } + + /** + * This method prints a long to the stream. The value printed is + * determined using the <code>String.valueOf()</code> method. + * + * @param lnum The <code>long</code> value to be printed + */ + public void print(long lnum) + { + // We purposely call write() and not print() here. This preserves + // compatibility with JDK 1.2. + write(Long.toString(lnum)); + } + + /** + * This method prints a float to the stream. The value printed is + * determined using the <code>String.valueOf()</code> method. + * + * @param fnum The <code>float</code> value to be printed + */ + public void print(float fnum) + { + // We purposely call write() and not print() here. This preserves + // compatibility with JDK 1.2. + write(Float.toString(fnum)); + } + + /** + * This method prints a double to the stream. The value printed is + * determined using the <code>String.valueOf()</code> method. + * + * @param dnum The <code>double</code> value to be printed + */ + public void print(double dnum) + { + // We purposely call write() and not print() here. This preserves + // compatibility with JDK 1.2. + write(Double.toString(dnum)); + } + + /** + * This method prints an <code>Object</code> to the stream. The actual + * value printed is determined by calling the <code>String.valueOf()</code> + * method. + * + * @param obj The <code>Object</code> to print. + */ + public void print(Object obj) + { + // We purposely call write() and not print() here. This preserves + // compatibility with JDK 1.2. + write(obj == null ? "null" : obj.toString()); + } + + /** + * This is the system dependent line separator + */ + private static final char[] line_separator + = System.getProperty("line.separator").toCharArray(); + + /** + * This method prints a line separator sequence to the stream. The value + * printed is determined by the system property <xmp>line.separator</xmp> + * and is not necessarily the Unix '\n' newline character. + */ + public void println() + { + synchronized (lock) + { + try + { + write(line_separator, 0, line_separator.length); + if (autoflush) + out.flush(); + } + catch (IOException ex) + { + error = true; + } + } + } + + /** + * This methods prints a boolean value to the stream. <code>true</code> + * values are printed as "true" and <code>false</code> values are printed + * as "false". + * + * This method prints a line termination sequence after printing the value. + * + * @param bool The <code>boolean</code> value to print + */ + public void println(boolean bool) + { + synchronized (lock) + { + print(bool); + println(); + } + } + + /** + * This method prints an integer to the stream. The value printed is + * determined using the <code>String.valueOf()</code> method. + * + * This method prints a line termination sequence after printing the value. + * + * @param inum The <code>int</code> value to be printed + */ + public void println(int inum) + { + synchronized (lock) + { + print(inum); + println(); + } + } + + /** + * This method prints a long to the stream. The value printed is + * determined using the <code>String.valueOf()</code> method. + * + * This method prints a line termination sequence after printing the value. + * + * @param lnum The <code>long</code> value to be printed + */ + public void println(long lnum) + { + synchronized (lock) + { + print(lnum); + println(); + } + } + + /** + * This method prints a float to the stream. The value printed is + * determined using the <code>String.valueOf()</code> method. + * + * This method prints a line termination sequence after printing the value. + * + * @param fnum The <code>float</code> value to be printed + */ + public void println(float fnum) + { + synchronized (lock) + { + print(fnum); + println(); + } + } + + /** + * This method prints a double to the stream. The value printed is + * determined using the <code>String.valueOf()</code> method. + * + * This method prints a line termination sequence after printing the value. + * + * @param dnum The <code>double</code> value to be printed + */ + public void println(double dnum) + { + synchronized (lock) + { + print(dnum); + println(); + } + } + + /** + * This method prints an <code>Object</code> to the stream. The actual + * value printed is determined by calling the <code>String.valueOf()</code> + * method. + * + * This method prints a line termination sequence after printing the value. + * + * @param obj The <code>Object</code> to print. + */ + public void println(Object obj) + { + synchronized (lock) + { + print(obj); + println(); + } + } + + /** + * This method prints a <code>String</code> to the stream. The actual + * value printed depends on the system default encoding. + * + * This method prints a line termination sequence after printing the value. + * + * @param str The <code>String</code> to print. + */ + public void println(String str) + { + synchronized (lock) + { + print(str); + println(); + } + } + + /** + * This method prints a char to the stream. The actual value printed is + * determined by the character encoding in use. + * + * This method prints a line termination sequence after printing the value. + * + * @param ch The <code>char</code> value to be printed + */ + public void println(char ch) + { + synchronized (lock) + { + print(ch); + println(); + } + } + + /** + * This method prints an array of characters to the stream. The actual + * value printed depends on the system default encoding. + * + * This method prints a line termination sequence after printing the value. + * + * @param charArray The array of characters to print. + */ + public void println(char[] charArray) + { + synchronized (lock) + { + print(charArray); + println(); + } + } + + /** + * This method writes a single char to the stream. + * + * @param ch The char to be written, passed as a int + */ + public void write(int ch) + { + try + { + out.write(ch); + } + catch (IOException ex) + { + error = true; + } + } + + /** + * This method writes <code>count</code> chars from the specified array + * starting at index <code>offset</code> into the array. + * + * @param charArray The array of chars to write + * @param offset The index into the array to start writing from + * @param count The number of chars to write + */ + public void write(char[] charArray, int offset, int count) + { + try + { + out.write(charArray, offset, count); + } + catch (IOException ex) + { + error = true; + } + } + + /** + * This method writes <code>count</code> chars from the specified + * <code>String</code> to the output starting at character position + * <code>offset</code> into the <code>String</code> + * + * @param str The <code>String</code> to write chars from + * @param offset The offset into the <code>String</code> to start writing from + * @param count The number of chars to write. + */ + public void write(String str, int offset, int count) + { + try + { + out.write(str, offset, count); + } + catch (IOException ex) + { + error = true; + } + } + + /** + * This method write all the chars in the specified array to the output. + * + * @param charArray The array of characters to write + */ + public void write(char[] charArray) + { + write(charArray, 0, charArray.length); + } + + /** + * This method writes the contents of the specified <code>String</code> + * to the underlying stream. + * + * @param str The <code>String</code> to write + */ + public void write(String str) + { + write(str, 0, str.length()); + } +} + diff --git a/libjava/classpath/java/io/PushbackInputStream.java b/libjava/classpath/java/io/PushbackInputStream.java new file mode 100644 index 00000000000..71cf244274e --- /dev/null +++ b/libjava/classpath/java/io/PushbackInputStream.java @@ -0,0 +1,328 @@ +/* PushbackInputStream.java -- An input stream that can unread bytes + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.io; + +/** + * This subclass of <code>FilterInputStream</code> provides the ability to + * unread data from a stream. It maintains an internal buffer of unread + * data that is supplied to the next read operation. This is conceptually + * similar to mark/reset functionality, except that in this case the + * position to reset the stream to does not need to be known in advance. + * <p> + * The default pushback buffer size one byte, but this can be overridden + * by the creator of the stream. + * <p> + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + */ +public class PushbackInputStream extends FilterInputStream +{ + /** + * This is the default buffer size + */ + private static final int DEFAULT_BUFFER_SIZE = 1; + + /** + * This is the buffer that is used to store the pushed back data + */ + protected byte[] buf; + + /** + * This is the position in the buffer from which the next byte will be + * read. Bytes are stored in reverse order in the buffer, starting from + * <code>buf[buf.length - 1]</code> to <code>buf[0]</code>. Thus when + * <code>pos</code> is 0 the buffer is full and <code>buf.length</code> when + * it is empty + */ + protected int pos; + + /** + * This method initializes a <code>PushbackInputStream</code> to + * read from the specified subordinate <code>InputStream</code> + * with a default pushback buffer size of 1. + * + * @param in The subordinate stream to read from + */ + public PushbackInputStream(InputStream in) + { + this(in, DEFAULT_BUFFER_SIZE); + } + + /** + * This method initializes a <code>PushbackInputStream</code> to + * read from the specified subordinate <code>InputStream</code> with + * the specified buffer size + * + * @param in The subordinate <code>InputStream</code> to read from + * @param size The pushback buffer size to use + */ + public PushbackInputStream(InputStream in, int size) + { + super(in); + if (size < 0) + throw new IllegalArgumentException(); + buf = new byte[size]; + pos = buf.length; + } + + /** + * This method returns the number of bytes that can be read from this + * stream before a read can block. A return of 0 indicates that blocking + * might (or might not) occur on the very next read attempt. + * <p> + * This method will return the number of bytes available from the + * pushback buffer plus the number of bytes available from the + * underlying stream. + * + * @return The number of bytes that can be read before blocking could occur + * + * @exception IOException If an error occurs + */ + public int available() throws IOException + { + return (buf.length - pos) + super.available(); + } + + /** + * This method closes the stream and releases any associated resources. + * + * @exception IOException If an error occurs. + */ + public synchronized void close() throws IOException + { + buf = null; + super.close(); + } + + /** + * This method returns <code>false</code> to indicate that it does + * not support mark/reset functionality. + * + * @return This method returns <code>false</code> to indicate that + * this class does not support mark/reset functionality + */ + public boolean markSupported() + { + return false; + } + + /** + * This method always throws an IOException in this class because + * mark/reset functionality is not supported. + * + * @exception IOException Always thrown for this class + */ + public void reset() throws IOException + { + throw new IOException("Mark not supported in this class"); + } + + /** + * This method reads an unsigned byte from the input stream and returns it + * as an int in the range of 0-255. This method also will return -1 if + * the end of the stream has been reached. The byte returned will be read + * from the pushback buffer, unless the buffer is empty, in which case + * the byte will be read from the underlying stream. + * <p> + * This method will block until the byte can be read. + * + * @return The byte read or -1 if end of stream + * + * @exception IOException If an error occurs + */ + public synchronized int read() throws IOException + { + if (pos < buf.length) + return ((int) buf[pos++]) & 0xFF; + + return super.read(); + } + + /** + * This method read bytes from a stream and stores them into a + * caller supplied buffer. It starts storing the data at index + * <code>offset</code> into the buffer and attempts to read + * <code>len</code> bytes. This method can return before reading the + * number of bytes requested. The actual number of bytes read is + * returned as an int. A -1 is returned to indicate the end of the + * stream. + * <p> + * This method will block until some data can be read. + * <p> + * This method first reads bytes from the pushback buffer in order to + * satisfy the read request. If the pushback buffer cannot provide all + * of the bytes requested, the remaining bytes are read from the + * underlying stream. + * + * @param b The array into which the bytes read should be stored + * @param off The offset into the array to start storing bytes + * @param len The requested number of bytes to read + * + * @return The actual number of bytes read, or -1 if end of stream. + * + * @exception IOException If an error occurs. + */ + public synchronized int read(byte[] b, int off, int len) throws IOException + { + int numBytes = Math.min(buf.length - pos, len); + + if (numBytes > 0) + { + System.arraycopy (buf, pos, b, off, numBytes); + pos += numBytes; + len -= numBytes; + off += numBytes; + } + + if (len > 0) + { + len = super.read(b, off, len); + if (len == -1) //EOF + return numBytes > 0 ? numBytes : -1; + numBytes += len; + } + return numBytes; + } + + /** + * This method pushes a single byte of data into the pushback buffer. + * The byte pushed back is the one that will be returned as the first byte + * of the next read. + * <p> + * If the pushback buffer is full, this method throws an exception. + * <p> + * The argument to this method is an <code>int</code>. Only the low + * eight bits of this value are pushed back. + * + * @param b The byte to be pushed back, passed as an int + * + * @exception IOException If the pushback buffer is full. + */ + public synchronized void unread(int b) throws IOException + { + if (pos <= 0) + throw new IOException("Insufficient space in pushback buffer"); + + buf[--pos] = (byte) b; + } + + /** + * This method pushes all of the bytes in the passed byte array into + * the pushback bfer. These bytes are pushed in reverse order so that + * the next byte read from the stream after this operation will be + * <code>b[0]</code> followed by <code>b[1]</code>, etc. + * <p> + * If the pushback buffer cannot hold all of the requested bytes, an + * exception is thrown. + * + * @param b The byte array to be pushed back + * + * @exception IOException If the pushback buffer is full + */ + public synchronized void unread(byte[] b) throws IOException + { + unread(b, 0, b.length); + } + + /** + * This method pushed back bytes from the passed in array into the + * pushback buffer. The bytes from <code>b[offset]</code> to + * <code>b[offset + len]</code> are pushed in reverse order so that + * the next byte read from the stream after this operation will be + * <code>b[offset]</code> followed by <code>b[offset + 1]</code>, + * etc. + * <p> + * If the pushback buffer cannot hold all of the requested bytes, an + * exception is thrown. + * + * @param b The byte array to be pushed back + * @param off The index into the array where the bytes to be push start + * @param len The number of bytes to be pushed. + * + * @exception IOException If the pushback buffer is full + */ + public synchronized void unread(byte[] b, int off, int len) + throws IOException + { + if (pos < len) + throw new IOException("Insufficient space in pushback buffer"); + + // Note the order that these bytes are being added is the opposite + // of what would be done if they were added to the buffer one at a time. + // See the Java Class Libraries book p. 1390. + System.arraycopy(b, off, buf, pos - len, len); + + // Don't put this into the arraycopy above, an exception might be thrown + // and in that case we don't want to modify pos. + pos -= len; + } + + /** + * This method skips the specified number of bytes in the stream. It + * returns the actual number of bytes skipped, which may be less than the + * requested amount. + * <p> + * This method first discards bytes from the buffer, then calls the + * <code>skip</code> method on the underlying <code>InputStream</code> to + * skip additional bytes if necessary. + * + * @param n The requested number of bytes to skip + * + * @return The actual number of bytes skipped. + * + * @exception IOException If an error occurs + * + * @since 1.2 + */ + public synchronized long skip(long n) throws IOException + { + final long origN = n; + + if (n > 0L) + { + int numread = (int) Math.min((long) (buf.length - pos), n); + pos += numread; + n -= numread; + if (n > 0) + n -= super.skip(n); + } + + return origN - n; + } +} diff --git a/libjava/classpath/java/io/PushbackReader.java b/libjava/classpath/java/io/PushbackReader.java new file mode 100644 index 00000000000..04bccc70fc5 --- /dev/null +++ b/libjava/classpath/java/io/PushbackReader.java @@ -0,0 +1,384 @@ +/* PushbackReader.java -- An character stream that can unread chars + Copyright (C) 1998, 2000, 2001, 2003, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This subclass of <code>FilterReader</code> provides the ability to + * unread data from a stream. It maintains an internal buffer of unread + * data that is supplied to the next read operation. This is conceptually + * similar to mark/reset functionality, except that in this case the + * position to reset the stream to does not need to be known in advance. + * <p> + * The default pushback buffer size one char, but this can be overridden + * by the creator of the stream. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + */ +public class PushbackReader extends FilterReader +{ + /** + * This is the default buffer size + */ + private static final int DEFAULT_BUFFER_SIZE = 1; + + /** + * This is the buffer that is used to store the pushed back data + */ + private char[] buf; + + /** + * This is the position in the buffer from which the next char will be + * read. Bytes are stored in reverse order in the buffer, starting from + * <code>buf[buf.length - 1]</code> to <code>buf[0]</code>. Thus when + * <code>pos</code> is 0 the buffer is full and <code>buf.length</code> when + * it is empty + */ + private int pos; + + /** + * This method initializes a <code>PushbackReader</code> to read from the + * specified subordinate <code>Reader</code> with a default pushback buffer + * size of 1. + * + * @param in The subordinate stream to read from + */ + public PushbackReader(Reader in) + { + this(in, DEFAULT_BUFFER_SIZE); + } + + /** + * This method initializes a <code>PushbackReader</code> to read from the + * specified subordinate <code>Reader</code> with the specified buffer + * size + * + * @param in The subordinate <code>Reader</code> to read from + * @param bufsize The pushback buffer size to use + */ + public PushbackReader(Reader in, int bufsize) + { + super(in); + + if (bufsize < 0) + throw new IllegalArgumentException("buffer size must be positive"); + + buf = new char[bufsize]; + pos = bufsize; + } + + /** + * This method closes the stream and frees any associated resources. + * + * @exception IOException If an error occurs. + */ + public void close() throws IOException + { + synchronized (lock) + { + buf = null; + super.close(); + } + } + + /** + * This method throws an exception when called since this class does + * not support mark/reset. + * + * @param read_limit Not used. + * + * @exception IOException Always thrown to indicate mark/reset not supported. + */ + public void mark(int read_limit) throws IOException + { + throw new IOException("mark not supported in this class"); + } + + /** + * This method returns <code>false</code> to indicate that it does not support + * mark/reset functionality. + * + * @return This method returns <code>false</code> to indicate that this + * class does not support mark/reset functionality + * + */ + public boolean markSupported() + { + return(false); + } + + /** + * This method always throws an IOException in this class because + * mark/reset functionality is not supported. + * + * @exception IOException Always thrown for this class + */ + public void reset() throws IOException + { + throw new IOException("reset not supported in this class"); + } + + /** + * This method determines whether or not this stream is ready to be read. + * If it returns <code>false</code> to indicate that the stream is not + * ready, any attempt to read from the stream could (but is not + * guaranteed to) block. + * <p> + * This stream is ready to read if there are either chars waiting to be + * read in the pushback buffer or if the underlying stream is ready to + * be read. + * + * @return <code>true</code> if this stream is ready to be read, + * <code>false</code> otherwise + * + * @exception IOException If an error occurs + */ + public boolean ready() throws IOException + { + synchronized (lock) + { + if (buf == null) + throw new IOException ("stream closed"); + + if (((buf.length - pos) > 0) || super.ready()) + return(true); + else + return(false); + } + } + + // Don't delete this method just because the spec says it shouldn't be there! + // See the CVS log for details. + /** + * This method skips the specified number of chars in the stream. It + * returns the actual number of chars skipped, which may be less than the + * requested amount. + * <p> + * This method first discards chars from the buffer, then calls the + * <code>skip</code> method on the underlying <code>Reader</code> to + * skip additional chars if necessary. + * + * @param num_chars The requested number of chars to skip + * + * @return The actual number of chars skipped. + * + * @exception IOException If an error occurs + */ + public long skip(long num_chars) throws IOException + { + synchronized (lock) + { + if (num_chars <= 0) + return(0); + + if ((buf.length - pos) >= num_chars) + { + pos += num_chars; + return(num_chars); + } + + int chars_discarded = buf.length - pos; + pos = buf.length; + + long chars_skipped = in.skip(num_chars - chars_discarded); + + return(chars_discarded + chars_skipped); + } + } + + /** + * This method reads an unsigned char from the input stream and returns it + * as an int in the range of 0-65535. This method also will return -1 if + * the end of the stream has been reached. The char returned will be read + * from the pushback buffer, unless the buffer is empty, in which case + * the char will be read from the underlying stream. + * <p> + * This method will block until the char can be read. + * + * @return The char read or -1 if end of stream + * + * @exception IOException If an error occurs + */ + public int read() throws IOException + { + synchronized (lock) + { + if (buf == null) + throw new IOException("stream closed"); + + if (pos == buf.length) + return(super.read()); + + ++pos; + return((buf[pos - 1] & 0xFFFF)); + } + } + + /** + * This method read chars from a stream and stores them into a caller + * supplied buffer. It starts storing the data at index <code>offset</code> + * into + * the buffer and attempts to read <code>len</code> chars. This method can + * return before reading the number of chars requested. The actual number + * of chars read is returned as an int. A -1 is returned to indicate the + * end of the stream. + * <p> + * This method will block until some data can be read. + * <p> + * This method first reads chars from the pushback buffer in order to + * satisfy the read request. If the pushback buffer cannot provide all + * of the chars requested, the remaining chars are read from the + * underlying stream. + * + * @param buffer The array into which the chars read should be stored + * @param offset The offset into the array to start storing chars + * @param length The requested number of chars to read + * + * @return The actual number of chars read, or -1 if end of stream. + * + * @exception IOException If an error occurs. + */ + public synchronized int read(char[] buffer, int offset, int length) + throws IOException + { + synchronized (lock) + { + if (buf == null) + throw new IOException("stream closed"); + + if (offset < 0 || length < 0 || offset + length > buffer.length) + throw new ArrayIndexOutOfBoundsException(); + + int numBytes = Math.min(buf.length - pos, length); + if (numBytes > 0) + { + System.arraycopy (buf, pos, buffer, offset, numBytes); + pos += numBytes; + return numBytes; + } + + return super.read(buffer, offset, length); + } + } + + /** + * This method pushes a single char of data into the pushback buffer. + * The char pushed back is the one that will be returned as the first char + * of the next read. + * <p> + * If the pushback buffer is full, this method throws an exception. + * <p> + * The argument to this method is an <code>int</code>. Only the low eight + * bits of this value are pushed back. + * + * @param b The char to be pushed back, passed as an int + * + * @exception IOException If the pushback buffer is full. + */ + public void unread(int b) throws IOException + { + synchronized (lock) + { + if (buf == null) + throw new IOException("stream closed"); + if (pos == 0) + throw new IOException("Pushback buffer is full"); + + --pos; + buf[pos] = (char)(b & 0xFFFF); + } + } + + /** + * This method pushes all of the chars in the passed char array into + * the pushback buffer. These chars are pushed in reverse order so that + * the next char read from the stream after this operation will be + * <code>buf[0]</code> followed by <code>buf[1]</code>, etc. + * <p> + * If the pushback buffer cannot hold all of the requested chars, an + * exception is thrown. + * + * @param buf The char array to be pushed back + * + * @exception IOException If the pushback buffer is full + */ + public synchronized void unread(char[] buf) throws IOException + { + unread(buf, 0, buf.length); + } + + /** + * This method pushed back chars from the passed in array into the pushback + * buffer. The chars from <code>buf[offset]</code> to + * <code>buf[offset + len]</code> + * are pushed in reverse order so that the next char read from the stream + * after this operation will be <code>buf[offset]</code> followed by + * <code>buf[offset + 1]</code>, etc. + * <p> + * If the pushback buffer cannot hold all of the requested chars, an + * exception is thrown. + * + * @param buffer The char array to be pushed back + * @param offset The index into the array where the chars to be push start + * @param length The number of chars to be pushed. + * + * @exception IOException If the pushback buffer is full + */ + public synchronized void unread(char[] buffer, int offset, int length) + throws IOException + { + synchronized (lock) + { + if (buf == null) + throw new IOException("stream closed"); + if (pos < length) + throw new IOException("Pushback buffer is full"); + + // Note the order that these chars are being added is the opposite + // of what would be done if they were added to the buffer one at a time. + // See the Java Class Libraries book p. 1397. + System.arraycopy(buffer, offset, buf, pos - length, length); + + // Don't put this into the arraycopy above, an exception might be thrown + // and in that case we don't want to modify pos. + pos -= length; + } + } +} + diff --git a/libjava/classpath/java/io/RandomAccessFile.java b/libjava/classpath/java/io/RandomAccessFile.java new file mode 100644 index 00000000000..23be5a31947 --- /dev/null +++ b/libjava/classpath/java/io/RandomAccessFile.java @@ -0,0 +1,991 @@ +/* RandomAccessFile.java -- Class supporting random file I/O + Copyright (C) 1998, 1999, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +import gnu.java.nio.channels.FileChannelImpl; + +import java.nio.channels.FileChannel; + +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 + * "The Java Language Specification", ISBN 0-201-63451-1 + * Status: Believe complete and correct to 1.1. + */ + +/** + * This class allows reading and writing of files at random locations. + * Most Java I/O classes are either pure sequential input or output. This + * class fulfills the need to be able to read the bytes of a file in an + * arbitrary order. In addition, this class implements the + * <code>DataInput</code> and <code>DataOutput</code> interfaces to allow + * the reading and writing of Java primitives. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@cygnus.com) + */ +public class RandomAccessFile implements DataOutput, DataInput +{ + + // The underlying file. + private FileChannelImpl ch; + private FileDescriptor fd; + // The corresponding input and output streams. + private DataOutputStream out; + private DataInputStream in; + + + /** + * This method initializes a new instance of <code>RandomAccessFile</code> + * to read from the specified <code>File</code> object with the specified + * access mode. The access mode is either "r" for read only access or "rw" + * for read-write access. + * <p> + * Note that a <code>SecurityManager</code> check is made prior to + * opening the file to determine whether or not this file is allowed to + * be read or written. + * + * @param file The <code>File</code> object to read and/or write. + * @param mode "r" for read only or "rw" for read-write access to the file + * + * @exception IllegalArgumentException If <code>mode</code> has an + * illegal value + * @exception SecurityException If the requested access to the file + * is not allowed + * @exception FileNotFoundException If the file is a directory, or + * any other error occurs + */ + public RandomAccessFile (File file, String mode) + throws FileNotFoundException + { + int fdmode; + if (mode.equals("r")) + fdmode = FileChannelImpl.READ; + else if (mode.equals("rw")) + fdmode = FileChannelImpl.READ | FileChannelImpl.WRITE; + else if (mode.equals("rws")) + { + fdmode = (FileChannelImpl.READ | FileChannelImpl.WRITE + | FileChannelImpl.SYNC); + } + else if (mode.equals("rwd")) + { + fdmode = (FileChannelImpl.READ | FileChannelImpl.WRITE + | FileChannelImpl.DSYNC); + } + else + throw new IllegalArgumentException ("invalid mode: " + mode); + + final String fileName = file.getPath(); + + // The obligatory SecurityManager stuff + SecurityManager s = System.getSecurityManager(); + if (s != null) + { + s.checkRead(fileName); + + if ((fdmode & FileChannelImpl.WRITE) != 0) + s.checkWrite(fileName); + } + + ch = FileChannelImpl.create(file, fdmode); + fd = new FileDescriptor(ch); + out = new DataOutputStream (new FileOutputStream (fd)); + in = new DataInputStream (new FileInputStream (fd)); + } + + /** + * This method initializes a new instance of <code>RandomAccessFile</code> + * to read from the specified file name with the specified access mode. + * The access mode is either "r" for read only access, "rw" for read + * write access, "rws" for synchronized read/write access of both + * content and metadata, or "rwd" for read/write access + * where only content is required to be synchronous. + * <p> + * Note that a <code>SecurityManager</code> check is made prior to + * opening the file to determine whether or not this file is allowed to + * be read or written. + * + * @param fileName The name of the file to read and/or write + * @param mode "r", "rw", "rws", or "rwd" + * + * @exception IllegalArgumentException If <code>mode</code> has an + * illegal value + * @exception SecurityException If the requested access to the file + * is not allowed + * @exception FileNotFoundException If the file is a directory or + * any other error occurs + */ + public RandomAccessFile (String fileName, String mode) + throws FileNotFoundException + { + this (new File(fileName), mode); + } + + /** + * This method closes the file and frees up all file related system + * resources. Since most operating systems put a limit on how many files + * may be opened at any given time, it is a good idea to close all files + * when no longer needed to avoid hitting this limit + */ + public void close () throws IOException + { + ch.close(); + } + + /** + * This method returns a <code>FileDescriptor</code> object that + * represents the native file handle for this file. + * + * @return The <code>FileDescriptor</code> object for this file + * + * @exception IOException If an error occurs + */ + public final FileDescriptor getFD () throws IOException + { + synchronized (this) + { + if (fd == null) + fd = new FileDescriptor (ch); + return fd; + } + } + + /** + * This method returns the current offset in the file at which the next + * read or write will occur + * + * @return The current file position + * + * @exception IOException If an error occurs + */ + public long getFilePointer () throws IOException + { + return ch.position(); + } + + /** + * This method sets the length of the file to the specified length. + * If the currently length of the file is longer than the specified + * length, then the file is truncated to the specified length (the + * file position is set to the end of file in this case). If the + * current length of the file is shorter than the specified length, + * the file is extended with bytes of an undefined value (the file + * position is unchanged in this case). + * <p> + * The file must be open for write access for this operation to succeed. + * + * @param newLen The new length of the file + * + * @exception IOException If an error occurs + */ + public void setLength (long newLen) throws IOException + { + // FIXME: Extending a file should probably be done by one method call. + + // FileChannel.truncate() can only shrink a file. + // To expand it we need to seek forward and write at least one byte. + if (newLen < length()) + ch.truncate (newLen); + else if (newLen > length()) + { + long pos = getFilePointer(); + seek(newLen - 1); + write(0); + seek(pos); + } + } + + /** + * This method returns the length of the file in bytes + * + * @return The length of the file + * + * @exception IOException If an error occurs + */ + public long length () throws IOException + { + return ch.size(); + } + + /** + * This method reads a single byte of data from the file and returns it + * as an integer. + * + * @return The byte read as an int, or -1 if the end of the file was reached. + * + * @exception IOException If an error occurs + */ + public int read () throws IOException + { + return in.read(); + } + + /** + * This method reads bytes from the file into the specified array. The + * bytes are stored starting at the beginning of the array and up to + * <code>buf.length</code> bytes can be read. + * + * @param buffer The buffer to read bytes from the file into + * + * @return The actual number of bytes read or -1 if end of file + * + * @exception IOException If an error occurs + */ + public int read (byte[] buffer) throws IOException + { + return in.read (buffer); + } + + /** + * This methods reads up to <code>len</code> bytes from the file into the + * specified array starting at position <code>offset</code> into the array. + * + * @param buffer The array to read the bytes into + * @param offset The index into the array to start storing bytes + * @param len The requested number of bytes to read + * + * @return The actual number of bytes read, or -1 if end of file + * + * @exception IOException If an error occurs + */ + public int read (byte[] buffer, int offset, int len) throws IOException + { + return in.read (buffer, offset, len); + } + + /** + * This method reads a Java boolean value from an input stream. It does + * so by reading a single byte of data. If that byte is zero, then the + * value returned is <code>false</code> If the byte is non-zero, then + * the value returned is <code>true</code> + * <p> + * This method can read a <code>boolean</code> written by an object + * implementing the + * <code>writeBoolean()</code> method in the <code>DataOutput</code> + * interface. + * + * @return The <code>boolean</code> value read + * + * @exception EOFException If end of file is reached before reading the + * boolean + * @exception IOException If any other error occurs + */ + public final boolean readBoolean () throws IOException + { + return in.readBoolean (); + } + + /** + * This method reads a Java byte value from an input stream. The value + * is in the range of -128 to 127. + * <p> + * This method can read a <code>byte</code> written by an object + * implementing the + * <code>writeByte()</code> method in the <code>DataOutput</code> interface. + * + * @return The <code>byte</code> value read + * + * @exception EOFException If end of file is reached before reading the byte + * @exception IOException If any other error occurs + * + * @see DataOutput + */ + public final byte readByte () throws IOException + { + return in.readByte (); + } + + /** + * This method reads a Java <code>char</code> value from an input stream. + * It operates by reading two bytes from the stream and converting them to + * a single 16-bit Java <code>char</code> The two bytes are stored most + * significant byte first (i.e., "big endian") regardless of the native + * host byte ordering. + * <p> + * As an example, if <code>byte1</code> and <code>byte2</code> represent + * the first + * and second byte read from the stream respectively, they will be + * transformed to a <code>char</code> in the following manner: + * <p> + * <code>(char)(((byte1 & 0xFF) << 8) | (byte2 & 0xFF)</code> + * <p> + * This method can read a <code>char</code> written by an object + * implementing the + * <code>writeChar()</code> method in the <code>DataOutput</code> interface. + * + * @return The <code>char</code> value read + * + * @exception EOFException If end of file is reached before reading the char + * @exception IOException If any other error occurs + * + * @see DataOutput + */ + public final char readChar () throws IOException + { + return in.readChar(); + } + + /** + * This method reads a Java double value from an input stream. It operates + * by first reading a <code>logn</code> value from the stream by calling the + * <code>readLong()</code> method in this interface, then + * converts that <code>long</code> + * to a <code>double</code> using the <code>longBitsToDouble</code> + * method in the class <code>java.lang.Double</code> + * <p> + * This method can read a <code>double</code> written by an object + * implementing the + * <code>writeDouble()</code> method in the <code>DataOutput</code> + * interface. + * + * @return The <code>double</code> value read + * + * @exception EOFException If end of file is reached before reading + * the double + * @exception IOException If any other error occurs + * + * @see java.lang.Double + * @see DataOutput + */ + public final double readDouble () throws IOException + { + return in.readDouble (); + } + + /** + * This method reads a Java float value from an input stream. It operates + * by first reading an <code>int</code> value from the stream by calling the + * <code>readInt()</code> method in this interface, then converts + * that <code>int</code> + * to a <code>float</code> using the <code>intBitsToFloat</code> method in + * the class <code>java.lang.Float</code> + * <p> + * This method can read a <code>float</code> written by an object + * implementing the + * <code>writeFloat()</code> method in the <code>DataOutput</code> interface. + * + * @return The <code>float</code> value read + * + * @exception EOFException If end of file is reached before reading the float + * @exception IOException If any other error occurs + * + * @see java.lang.Float + * @see DataOutput + */ + public final float readFloat () throws IOException + { + return in.readFloat(); + } + + /** + * This method reads raw bytes into the passed array until the array is + * full. Note that this method blocks until the data is available and + * throws an exception if there is not enough data left in the stream to + * fill the buffer + * + * @param buffer The buffer into which to read the data + * + * @exception EOFException If end of file is reached before filling the + * buffer + * @exception IOException If any other error occurs + */ + public final void readFully (byte[] buffer) throws IOException + { + in.readFully(buffer); + } + + /** + * This method reads raw bytes into the passed array <code>buf</code> + * starting + * <code>offset</code> bytes into the buffer. The number of bytes read + * will be + * exactly <code>len</code> Note that this method blocks until the data is + * available and throws an exception if there is not enough data left in + * the stream to read <code>len</code> bytes. + * + * @param buffer The buffer into which to read the data + * @param offset The offset into the buffer to start storing data + * @param count The number of bytes to read into the buffer + * + * @exception EOFException If end of file is reached before filling + * the buffer + * @exception IOException If any other error occurs + */ + public final void readFully (byte[] buffer, int offset, int count) + throws IOException + { + in.readFully (buffer, offset, count); + } + + /** + * This method reads a Java <code>int</code> value from an input stream + * It operates by reading four bytes from the stream and converting them to + * a single Java <code>int</code> The bytes are stored most + * significant byte first (i.e., "big endian") regardless of the native + * host byte ordering. + * <p> + * As an example, if <code>byte1</code> through <code>byte4</code> + * represent the first + * four bytes read from the stream, they will be + * transformed to an <code>int</code> in the following manner: + * <p> + * <code>(int)(((byte1 & 0xFF) << 24) + ((byte2 & 0xFF) << 16) + + * ((byte3 & 0xFF) << 8) + (byte4 & 0xFF)))</code> + * <p> + * The value returned is in the range of 0 to 65535. + * <p> + * This method can read an <code>int</code> written by an object + * implementing the + * <code>writeInt()</code> method in the <code>DataOutput</code> interface. + * + * @return The <code>int</code> value read + * + * @exception EOFException If end of file is reached before reading the int + * @exception IOException If any other error occurs + * + * @see DataOutput + */ + public final int readInt () throws IOException + { + return in.readInt(); + } + + /** + * This method reads the next line of text data from an input stream. + * It operates by reading bytes and converting those bytes to + * <code>char</code> + * values by treating the byte read as the low eight bits of the + * <code>char</code> + * and using <code>0</code> as the high eight bits. Because of this, it does + * not support the full 16-bit Unicode character set. + * <p> + * The reading of bytes ends when either the end of file or a line terminator + * is encountered. The bytes read are then returned as a <code>String</code> + * A line terminator is a byte sequence consisting of either + * <code>\r</code> <code>\n</code> or <code>\r\n</code> These + * termination charaters are + * discarded and are not returned as part of the string. + * <p> + * This method can read data that was written by an object implementing the + * <code>writeLine()</code> method in <code>DataOutput</code> + * + * @return The line read as a <code>String</code> + * + * @exception IOException If an error occurs + * + * @see DataOutput + */ + public final String readLine () throws IOException + { + return in.readLine (); + } + + /** + * This method reads a Java long value from an input stream + * It operates by reading eight bytes from the stream and converting them to + * a single Java <code>long</code> The bytes are stored most + * significant byte first (i.e., "big endian") regardless of the native + * host byte ordering. + * <p> + * As an example, if <code>byte1</code> through <code>byte8</code> + * represent the first + * eight bytes read from the stream, they will be + * transformed to an <code>long</code> in the following manner: + * <p> + * <code> + * (long)((((long)byte1 & 0xFF) << 56) + (((long)byte2 & 0xFF) << 48) + + * (((long)byte3 & 0xFF) << 40) + (((long)byte4 & 0xFF) << 32) + + * (((long)byte5 & 0xFF) << 24) + (((long)byte6 & 0xFF) << 16) + + * (((long)byte7 & 0xFF) << 8) + ((long)byte9 & 0xFF)))</code> + * <p> + * The value returned is in the range of 0 to 65535. + * <p> + * This method can read an <code>long</code> written by an object + * implementing the + * <code>writeLong()</code> method in the <code>DataOutput</code> interface. + * + * @return The <code>long</code> value read + * + * @exception EOFException If end of file is reached before reading the long + * @exception IOException If any other error occurs + * + * @see DataOutput + */ + public final long readLong () throws IOException + { + return in.readLong(); + } + + /** + * This method reads a signed 16-bit value into a Java in from the stream. + * It operates by reading two bytes from the stream and converting them to + * a single 16-bit Java <code>short</code> The two bytes are stored most + * significant byte first (i.e., "big endian") regardless of the native + * host byte ordering. + * <p> + * As an example, if <code>byte1</code> and <code>byte2</code> + * represent the first + * and second byte read from the stream respectively, they will be + * transformed to a <code>short</code> in the following manner: + * <p> + * <code>(short)(((byte1 & 0xFF) << 8) | (byte2 & 0xFF)</code> + * <p> + * The value returned is in the range of -32768 to 32767. + * <p> + * This method can read a <code>short</code> written by an object + * implementing the + * <code>writeShort()</code> method in the <code>DataOutput</code> interface. + * + * @return The <code>short</code> value read + * + * @exception EOFException If end of file is reached before reading the value + * @exception IOException If any other error occurs + * + * @see DataOutput + */ + public final short readShort () throws IOException + { + return in.readShort(); + } + + /** + * This method reads 8 unsigned bits into a Java <code>int</code> value + * from the + * stream. The value returned is in the range of 0 to 255. + * <p> + * This method can read an unsigned byte written by an object implementing + * the <code>writeUnsignedByte()</code> method in the + * <code>DataOutput</code> interface. + * + * @return The unsigned bytes value read as a Java <code>int</code> + * + * @exception EOFException If end of file is reached before reading the value + * @exception IOException If any other error occurs + * + * @see DataOutput + */ + public final int readUnsignedByte () throws IOException + { + return in.readUnsignedByte(); + } + + /** + * This method reads 16 unsigned bits into a Java int value from the stream. + * It operates by reading two bytes from the stream and converting them to + * a single Java <code>int</code> The two bytes are stored most + * significant byte first (i.e., "big endian") regardless of the native + * host byte ordering. + * <p> + * As an example, if <code>byte1</code> and <code>byte2</code> + * represent the first + * and second byte read from the stream respectively, they will be + * transformed to an <code>int</code> in the following manner: + * <p> + * <code>(int)(((byte1 & 0xFF) << 8) + (byte2 & 0xFF))</code> + * <p> + * The value returned is in the range of 0 to 65535. + * <p> + * This method can read an unsigned short written by an object implementing + * the <code>writeUnsignedShort()</code> method in the + * <code>DataOutput</code> interface. + * + * @return The unsigned short value read as a Java <code>int</code> + * + * @exception EOFException If end of file is reached before reading the value + * @exception IOException If any other error occurs + */ + public final int readUnsignedShort () throws IOException + { + return in.readUnsignedShort(); + } + + /** + * This method reads a <code>String</code> from an input stream that + * is encoded in + * a modified UTF-8 format. This format has a leading two byte sequence + * that contains the remaining number of bytes to read. This two byte + * sequence is read using the <code>readUnsignedShort()</code> method of this + * interface. + * <p> + * After the number of remaining bytes have been determined, these bytes + * are read an transformed into <code>char</code> values. + * These <code>char</code> values + * are encoded in the stream using either a one, two, or three byte format. + * The particular format in use can be determined by examining the first + * byte read. + * <p> + * If the first byte has a high order bit of 0 then + * that character consists on only one byte. This character value consists + * of seven bits that are at positions 0 through 6 of the byte. As an + * example, if <code>byte1</code> is the byte read from the stream, it would + * be converted to a <code>char</code> like so: + * <p> + * <code>(char)byte1</code> + * <p> + * If the first byte has <code>110</code> as its high order bits, then the + * character consists of two bytes. The bits that make up the character + * value are in positions 0 through 4 of the first byte and bit positions + * 0 through 5 of the second byte. (The second byte should have + * 10 as its high order bits). These values are in most significant + * byte first (i.e., "big endian") order. + * <p> + * As an example, if <code>byte1</code> and <code>byte2</code> + * are the first two bytes + * read respectively, and the high order bits of them match the patterns + * which indicate a two byte character encoding, then they would be + * converted to a Java <code>char</code> like so: + * <p> + * <code>(char)(((byte1 & 0x1F) << 6) | (byte2 & 0x3F))</code> + * <p> + * If the first byte has a <code>1110</code> as its high order bits, then the + * character consists of three bytes. The bits that make up the character + * value are in positions 0 through 3 of the first byte and bit positions + * 0 through 5 of the other two bytes. (The second and third bytes should + * have <code>10</code> as their high order bits). These values are in most + * significant byte first (i.e., "big endian") order. + * <p> + * As an example, if <code>byte1</code> <code>byte2</code> + * and <code>byte3</code> are the + * three bytes read, and the high order bits of them match the patterns + * which indicate a three byte character encoding, then they would be + * converted to a Java <code>char</code> like so: + * <p> + * <code>(char)(((byte1 & 0x0F) << 12) | ((byte2 & 0x3F) << 6) | + * (byte3 & 0x3F))</code> + * <p> + * Note that all characters are encoded in the method that requires the + * fewest number of bytes with the exception of the character with the + * value of <code>\u0000</code> which is encoded as two bytes. This is + * a modification of the UTF standard used to prevent C language style + * <code>NUL</code> values from appearing in the byte stream. + * <p> + * This method can read data that was written by an object implementing the + * <code>writeUTF()</code> method in <code>DataOutput</code> + * + * @return The <code>String</code> read + * + * @exception EOFException If end of file is reached before reading the + * String + * @exception UTFDataFormatException If the data is not in UTF-8 format + * @exception IOException If any other error occurs + * + * @see DataOutput + */ + public final String readUTF () throws IOException + { + return in.readUTF(); + } + + /** + * This method sets the current file position to the specified offset + * from the beginning of the file. Note that some operating systems will + * allow the file pointer to be set past the current end of the file. + * + * @param pos The offset from the beginning of the file at which to set + * the file pointer + * + * @exception IOException If an error occurs + */ + public void seek (long pos) throws IOException + { + ch.position(pos); + } + + /** + * This method attempts to skip and discard the specified number of bytes + * in the input stream. It may actually skip fewer bytes than requested. + * The actual number of bytes skipped is returned. This method will not + * skip any bytes if passed a negative number of bytes to skip. + * + * @param numBytes The requested number of bytes to skip. + * + * @return The number of bytes actually skipped. + * + * @exception IOException If an error occurs. + */ + public int skipBytes (int numBytes) throws IOException + { + if (numBytes < 0) + throw new IllegalArgumentException ("Can't skip negative bytes: " + + numBytes); + + if (numBytes == 0) + return 0; + + long oldPos = ch.position(); + long newPos = oldPos + numBytes; + long size = ch.size(); + if (newPos > size) + newPos = size; + ch.position(newPos); + return (int) (ch.position() - oldPos); + } + + /** + * This method writes a single byte of data to the file. The file must + * be open for read-write in order for this operation to succeed. + * + * @param oneByte The byte of data to write, passed as an int. + * + * @exception IOException If an error occurs + */ + public void write (int oneByte) throws IOException + { + out.write(oneByte); + } + + /** + * This method writes all the bytes in the specified array to the file. + * The file must be open read-write in order for this operation to succeed. + * + * @param buffer The array of bytes to write to the file + */ + public void write (byte[] buffer) throws IOException + { + out.write(buffer); + } + + /** + * This method writes <code>len</code> bytes to the file from the specified + * array starting at index <code>offset</code> into the array. + * + * @param buffer The array of bytes to write to the file + * @param offset The index into the array to start writing file + * @param len The number of bytes to write + * + * @exception IOException If an error occurs + */ + public void write (byte[] buffer, int offset, int len) throws IOException + { + out.write (buffer, offset, len); + } + + /** + * This method writes a Java <code>boolean</code> to the underlying output + * stream. For a value of <code>true</code>, 1 is written to the stream. + * For a value of <code>false</code>, 0 is written. + * + * @param val The <code>boolean</code> value to write to the stream + * + * @exception IOException If an error occurs + */ + public final void writeBoolean (boolean val) throws IOException + { + out.writeBoolean(val); + } + + /** + * This method writes a Java <code>byte</code> value to the underlying + * output stream. + * + * @param val The <code>byte</code> to write to the stream, passed + * as an <code>int</code>. + * + * @exception IOException If an error occurs + */ + public final void writeByte (int val) throws IOException + { + out.writeByte(val); + } + + /** + * This method writes a Java <code>short</code> to the stream, high byte + * first. This method requires two bytes to encode the value. + * + * @param val The <code>short</code> value to write to the stream, + * passed as an <code>int</code>. + * + * @exception IOException If an error occurs + */ + public final void writeShort (int val) throws IOException + { + out.writeShort(val); + } + + /** + * This method writes a single <code>char</code> value to the stream, + * high byte first. + * + * @param val The <code>char</code> value to write, passed as + * an <code>int</code>. + * + * @exception IOException If an error occurs + */ + public final void writeChar (int val) throws IOException + { + out.writeChar(val); + } + + /** + * This method writes a Java <code>int</code> to the stream, high bytes + * first. This method requires four bytes to encode the value. + * + * @param val The <code>int</code> value to write to the stream. + * + * @exception IOException If an error occurs + */ + public final void writeInt (int val) throws IOException + { + out.writeInt(val); + } + + /** + * This method writes a Java <code>long</code> to the stream, high bytes + * first. This method requires eight bytes to encode the value. + * + * @param val The <code>long</code> value to write to the stream. + * + * @exception IOException If an error occurs + */ + public final void writeLong (long val) throws IOException + { + out.writeLong(val); + } + + /** + * This method writes a Java <code>float</code> value to the stream. This + * value is written by first calling the method + * <code>Float.floatToIntBits</code> + * to retrieve an <code>int</code> representing the floating point number, + * then writing this <code>int</code> value to the stream exactly the same + * as the <code>writeInt()</code> method does. + * + * @param val The floating point number to write to the stream. + * + * @exception IOException If an error occurs + * + * @see #writeInt(int) + */ + public final void writeFloat (float val) throws IOException + { + out.writeFloat(val); + } + + /** + * This method writes a Java <code>double</code> value to the stream. This + * value is written by first calling the method + * <code>Double.doubleToLongBits</code> + * to retrieve an <code>long</code> representing the floating point number, + * then writing this <code>long</code> value to the stream exactly the same + * as the <code>writeLong()</code> method does. + * + * @param val The double precision floating point number to write to the + * stream. + * + * @exception IOException If an error occurs + * + * @see #writeLong(long) + */ + public final void writeDouble (double val) throws IOException + { + out.writeDouble(val); + } + + /** + * This method writes all the bytes in a <code>String</code> out to the + * stream. One byte is written for each character in the <code>String</code>. + * The high eight bits of each character are discarded. + * + * @param val The <code>String</code> to write to the stream + * + * @exception IOException If an error occurs + */ + public final void writeBytes (String val) throws IOException + { + out.writeBytes(val); + } + + /** + * This method writes all the characters in a <code>String</code> to the + * stream. There will be two bytes for each character value. The high + * byte of the character will be written first. + * + * @param val The <code>String</code> to write to the stream. + * + * @exception IOException If an error occurs + */ + public final void writeChars (String val) throws IOException + { + out.writeChars(val); + } + + /** + * This method writes a Java <code>String</code> to the stream in a modified + * UTF-8 format. First, two bytes are written to the stream indicating the + * number of bytes to follow. Note that this is the number of bytes in the + * encoded <code>String</code> not the <code>String</code> length. Next + * come the encoded characters. Each character in the <code>String</code> + * is encoded as either one, two or three bytes. For characters in the + * range of <code>\u0001</code> to <code>\u007F</code>, + * one byte is used. The character + * value goes into bits 0-7 and bit eight is 0. For characters in the range + * of <code>\u0080</code> to <code>\u007FF</code>, two + * bytes are used. Bits + * 6-10 of the character value are encoded bits 0-4 of the first byte, with + * the high bytes having a value of "110". Bits 0-5 of the character value + * are stored in bits 0-5 of the second byte, with the high bits set to + * "10". This type of encoding is also done for the null character + * <code>\u0000</code>. This eliminates any C style NUL character values + * in the output. All remaining characters are stored as three bytes. + * Bits 12-15 of the character value are stored in bits 0-3 of the first + * byte. The high bits of the first bytes are set to "1110". Bits 6-11 + * of the character value are stored in bits 0-5 of the second byte. The + * high bits of the second byte are set to "10". And bits 0-5 of the + * character value are stored in bits 0-5 of byte three, with the high bits + * of that byte set to "10". + * + * @param val The <code>String</code> to write to the output in UTF format + * + * @exception IOException If an error occurs + */ + public final void writeUTF (String val) throws IOException + { + out.writeUTF(val); + } + + /** + * This method creates a java.nio.channels.FileChannel. + * Nio does not allow one to create a file channel directly. + * A file channel must be created by first creating an instance of + * Input/Output/RandomAccessFile and invoking the getChannel() method on it. + */ + public final synchronized FileChannel getChannel () + { + return ch; + } +} diff --git a/libjava/classpath/java/io/Reader.java b/libjava/classpath/java/io/Reader.java new file mode 100644 index 00000000000..7970d9a2434 --- /dev/null +++ b/libjava/classpath/java/io/Reader.java @@ -0,0 +1,271 @@ +/* Reader.java -- base class of classes that read input as a stream of chars + Copyright (C) 1998, 1999, 2000, 2003 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.io; + +/* Written using "Java Class Libraries", 2nd edition, plus online + * API docs for JDK 1.2 beta from http://www.javasoft.com. + * Status: Believed complete and correct. + */ + +/** + * This abstract class forms the base of the hierarchy of classes that read + * input as a stream of characters. It provides a common set of methods for + * reading characters from streams. Subclasses implement and extend these + * methods to read characters from a particular input source such as a file + * or network connection. + * + * @author Per Bothner (bothner@cygnus.com) + * @date April 21, 1998. + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public abstract class Reader +{ + /** + * This is the <code>Object</code> used for synchronizing critical code + * sections. Subclasses should use this variable instead of a + * synchronized method or an explicit synchronization on <code>this</code> + */ + protected Object lock; + + /** + * Unitializes a <code>Reader</code> that will use the object + * itself for synchronization of critical code sections. + */ + protected Reader() + { + this.lock = this; + } + + /** + * Initializes a <code>Reader</code> that will use the specified + * <code>Object</code> for synchronization of critical code sections. + * + * @param lock The <code>Object</code> to use for synchronization + */ + protected Reader(Object lock) + { + this.lock = lock; + } + + /** + * Read chars from a stream and stores them into a caller + * supplied buffer. It starts storing the data at index <code>offset</code> + * into the buffer and attempts to read <code>len</code> chars. This method + * can return before reading the number of chars requested. The actual + * number of chars read is returned as an int. A -1 is returned to indicate + * the end of the stream. + * <p> + * This method will block until some data can be read. + * <p> + * This method operates by calling the single char <code>read()</code> method + * in a loop until the desired number of chars are read. The read loop + * stops short if the end of the stream is encountered or if an IOException + * is encountered on any read operation except the first. If the first + * attempt to read a chars fails, the IOException is allowed to propagate + * upward. And subsequent IOException is caught and treated identically + * to an end of stream condition. Subclasses can (and should if possible) + * override this method to provide a more efficient implementation. + * + * @param buf The array into which the chars read should be stored + * @param offset The offset into the array to start storing chars + * @param count The requested number of chars to read + * + * @return The actual number of chars read, or -1 if end of stream. + * + * @exception IOException If an error occurs. + */ + public abstract int read(char buf[], int offset, int count) + throws IOException; + + /** + * Reads chars from a stream and stores them into a caller + * supplied buffer. This method attempts to completely fill the buffer, + * but can return before doing so. The actual number of chars read is + * returned as an int. A -1 is returned to indicate the end of the stream. + * <p> + * This method will block until some data can be read. + * <p> + * This method operates by calling an overloaded read method like so: + * <code>read(buf, 0, buf.length)</code> + * + * @param buf The buffer into which the chars read will be stored. + * + * @return The number of chars read or -1 if end of stream. + * + * @exception IOException If an error occurs. + */ + public int read(char buf[]) throws IOException + { + return read(buf, 0, buf.length); + } + + /** + * Reads an char from the input stream and returns it + * as an int in the range of 0-65535. This method also will return -1 if + * the end of the stream has been reached. + * <p> + * This method will block until the char can be read. + * + * @return The char read or -1 if end of stream + * + * @exception IOException If an error occurs + */ + public int read() throws IOException + { + char[] buf = new char[1]; + int count = read(buf, 0, 1); + return count > 0 ? buf[0] : -1; + } + + /** + * Closes the stream. Any futher attempts to read from the + * stream may generate an <code>IOException</code>. + * + * @exception IOException If an error occurs + */ + public abstract void close() throws IOException; + + /** + * Returns a boolean that indicates whether the mark/reset + * methods are supported in this class. Those methods can be used to + * remember a specific point in the stream and reset the stream to that + * point. + * <p> + * This method always returns <code>false</code> in this class, but + * subclasses can override this method to return <code>true</code> if they + * support mark/reset functionality. + * + * @return <code>true</code> if mark/reset functionality is supported, + * <code>false</code> otherwise + * + */ + public boolean markSupported() + { + return false; + } + + /** + * Marks a position in the input to which the stream can be + * "reset" by calling the <code>reset()</code> method. The parameter + * <code>readlimit</code> is the number of chars that can be read from the + * stream after setting the mark before the mark becomes invalid. For + * example, if <code>mark()</code> is called with a read limit of 10, then + * when 11 chars of data are read from the stream before the + * <code>reset()</code> method is called, then the mark is invalid and the + * stream object instance is not required to remember the mark. + * + * @param readLimit The number of chars that can be read before the mark + * becomes invalid + * + * @exception IOException If an error occurs such as mark not being + * supported for this class + */ + public void mark(int readLimit) throws IOException + { + throw new IOException("mark not supported"); + } + + /** + * Resets a stream to the point where the <code>mark()</code> + * method was called. Any chars that were read after the mark point was + * set will be re-read during subsequent reads. + * <p> + * This method always throws an IOException in this class, but subclasses + * can override this method if they provide mark/reset functionality. + * + * @exception IOException Always thrown for this class + */ + public void reset() throws IOException + { + throw new IOException("reset not supported"); + } + + /** + * Determines whether or not this stream is ready to be + * read. If it returns <code>false</code> the stream may block if a + * read is attempted, but it is not guaranteed to do so. + * <p> + * This method always returns <code>false</code> in this class + * + * @return <code>true</code> if the stream is ready to be read, + * <code>false</code> otherwise. + * + * @exception IOException If an error occurs + */ + public boolean ready() throws IOException + { + return false; + } + + /** + * Skips the specified number of chars in the stream. It + * returns the actual number of chars skipped, which may be less than the + * requested amount. + * <p> + * This method reads and discards chars into a 256 char array until the + * specified number of chars were skipped or until either the end of stream + * is reached or a read attempt returns a short count. Subclasses can + * override this method to provide a more efficient implementation where + * one exists. + * + * @param count The requested number of chars to skip + * + * @return The actual number of chars skipped. + * + * @exception IOException If an error occurs + */ + public long skip(long count) throws IOException + { + if (count <= 0) + return 0; + int bsize = count > 1024 ? 1024 : (int) count; + char[] buffer = new char[bsize]; + long todo = count; + synchronized (lock) + { + while (todo > 0) + { + int skipped = read(buffer, 0, bsize > todo ? (int) todo : bsize); + if (skipped <= 0) + break; + todo -= skipped; + } + } + return count - todo; + } +} diff --git a/libjava/classpath/java/io/SequenceInputStream.java b/libjava/classpath/java/io/SequenceInputStream.java new file mode 100644 index 00000000000..7fefe243263 --- /dev/null +++ b/libjava/classpath/java/io/SequenceInputStream.java @@ -0,0 +1,221 @@ +/* SequenceInputStream.java -- Reads multiple input streams in sequence + Copyright (C) 1998, 1999, 2001, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +import java.util.Enumeration; + +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 + * "The Java Language Specification", ISBN 0-201-63451-1 + * plus online API docs for JDK 1.2 beta from http://www.javasoft.com. + * Status: Believed complete and correct. + */ + +/** + * This class merges a sequence of multiple <code>InputStream</code>'s in + * order to form a single logical stream that can be read by applications + * that expect only one stream. + * <p> + * The streams passed to the constructor method are read in order until + * they return -1 to indicate they are at end of stream. When a stream + * reports end of stream, it is closed, then the next stream is read. + * When the last stream is closed, the next attempt to read from this + * stream will return a -1 to indicate it is at end of stream. + * <p> + * If this stream is closed prior to all subordinate streams being read + * to completion, all subordinate streams are closed. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + */ +public class SequenceInputStream extends InputStream +{ + /** The handle for the current input stream. */ + private InputStream in; + + /** Secondary input stream; not used if constructed w/ enumeration. */ + private InputStream in2; + + /** The enumeration handle; not used if constructed w/ 2 explicit input streams. */ + private Enumeration e; + + /** + * This method creates a new <code>SequenceInputStream</code> that obtains + * its list of subordinate <code>InputStream</code>s from the specified + * <code>Enumeration</code> + * + * @param e An <code>Enumeration</code> that will return a list of + * <code>InputStream</code>s to read in sequence + */ + public SequenceInputStream(Enumeration e) + { + this.e = e; + in = (InputStream) e.nextElement(); + in2 = null; + } + + /** + * This method creates a new <code>SequenceInputStream</code> that will read + * the two specified subordinate <code>InputStream</code>s in sequence. + * + * @param s1 The first <code>InputStream</code> to read + * @param s2 The second <code>InputStream</code> to read + */ + public SequenceInputStream(InputStream s1, InputStream s2) + { + in = s1; + in2 = s2; + } + + /** + * This method returns the number of bytes than can be read from the + * currently being read subordinate stream before that stream could + * block. Note that it is possible more bytes than this can actually + * be read without the stream blocking. If a 0 is returned, then the + * stream could block on the very next read. + * + * @return The number of bytes that can be read before blocking could occur + * + * @exception IOException If an error occurs + */ + public int available() throws IOException + { + if (in == null) + return 0; + + return in.available(); + } + + /** + * Closes this stream. This will cause any remaining unclosed subordinate + * <code>InputStream</code>'s to be closed as well. Subsequent attempts to + * read from this stream may cause an exception. + * + * @exception IOException If an error occurs + */ + public void close() throws IOException + { + while (in != null) + { + in.close(); + in = getNextStream (); + } + } + + /** + * This method reads an unsigned byte from the input stream and returns it + * as an int in the range of 0-255. This method also will return -1 if + * the end of the stream has been reached. This will only happen when + * all of the subordinate streams have been read. + * <p> + * This method will block until the byte can be read. + * + * @return The byte read, or -1 if end of stream + * + * @exception IOException If an error occurs + */ + public int read() throws IOException + { + int ch = -1; + + while (in != null && (ch = in.read()) < 0) + { + in.close(); + in = getNextStream(); + } + + return ch; + } + + /** + * This method reads bytes from a stream and stores them into a caller + * supplied buffer. It starts storing the data at index <code>offset</code> + * into the buffer and attempts to read <code>len</code> bytes. This method + * can return before reading the number of bytes requested. The actual number + * of bytes read is returned as an int. A -1 is returend to indicate the + * end of the stream. This will only happen when all of the subordinate + * streams have been read. + * <p> + * This method will block until at least one byte can be read. + * + * @param b The array into which bytes read should be stored + * @param off The offset into the array to start storing bytes + * @param len The requested number of bytes to read + * + * @return The actual number of bytes read, or -1 if end of stream + * + * @exception IOException If an error occurs + */ + public int read(byte[] b, int off, int len) throws IOException + { + int ch = -1; + + // The validity of the parameters will be checked by in.read so + // don't bother doing it here. + while (in != null && (ch = in.read(b, off, len)) < 0) + { + in.close(); + in = getNextStream(); + } + + return ch; + } + + /** + * This private method is used to get the next <code>InputStream</code> to + * read from. Returns null when no more streams are available. + */ + private InputStream getNextStream() + { + InputStream nextIn = null; + + if (e != null) + { + if (e.hasMoreElements()) + nextIn = (InputStream) e.nextElement(); + } + else + if (in2 != null) + { + nextIn = in2; + in2 = null; + } + + return nextIn; + } +} diff --git a/libjava/classpath/java/io/Serializable.java b/libjava/classpath/java/io/Serializable.java new file mode 100644 index 00000000000..a6d99f6d522 --- /dev/null +++ b/libjava/classpath/java/io/Serializable.java @@ -0,0 +1,54 @@ +/* Serializable.java -- Interface to indicate a class may be serialized + Copyright (C) 1998, 1999, 2001, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 + * Status: Believed complete + */ + +/** + * This interface has no methods. It simply serves to indicate that + * the implementing class may be serialized. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + */ +public interface Serializable +{ +} // interface Serializable diff --git a/libjava/classpath/java/io/SerializablePermission.java b/libjava/classpath/java/io/SerializablePermission.java new file mode 100644 index 00000000000..b5c07e4ec2c --- /dev/null +++ b/libjava/classpath/java/io/SerializablePermission.java @@ -0,0 +1,113 @@ +/* SerializablePermission.java -- Basic permissions related to serialization. + Copyright (C) 1998, 2000, 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +import java.security.BasicPermission; + +/** + * This class models permissions related to serialization. As a subclass + * of <code>BasicPermission</code>, this class has permissions that have + * a name only. There is no associated action list. + * <p> + * There are currently two allowable permission names for this class: + * <ul> + * <li><code>enableSubclassImplementation</code> - Allows a subclass to + * override the default serialization behavior of objects.</li> + * <li><code>enableSubstitution</code> - Allows substitution of one object + * for another during serialization or deserialization.</li> + * </ul> + * + * @see java.security.BasicPermission + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public final class SerializablePermission extends BasicPermission +{ + static final long serialVersionUID = 8537212141160296410L; + + /* + * Class Variables + */ + + private static final String[] legal_names = { "enableSubclassImplementation", + "enableSubstitution" }; + /* + * Constructors + */ + + /** + * This method initializes a new instance of + * <code>SerializablePermission</code> + * that has the specified name. + * + * @param name The name of the permission. + * + * @exception IllegalArgumentException If the name is not valid for + * this class. + */ + public SerializablePermission(String name) + { + this(name, null); + } + + /** + * This method initializes a new instance of + * <code>SerializablePermission</code> + * that has the specified name and action list. Note that the action list + * is unused in this class. + * + * @param name The name of the permission. + * @param actions The action list (unused). + * + * @exception IllegalArgumentException If the name is not valid for + * this class. + */ + public SerializablePermission(String name, String actions) + { + super(name, actions); + + for (int i = 0; i < legal_names.length; i++) + if (legal_names[i].equals(name)) + return; + + throw new IllegalArgumentException("Bad permission name: " + name); + } + +} // class SerializablePermission + diff --git a/libjava/classpath/java/io/StreamCorruptedException.java b/libjava/classpath/java/io/StreamCorruptedException.java new file mode 100644 index 00000000000..d24d12150c5 --- /dev/null +++ b/libjava/classpath/java/io/StreamCorruptedException.java @@ -0,0 +1,73 @@ +/* StreamCorruptedException.java -- Error in stream during serialization + Copyright (C) 1998, 2000, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This exception is thrown when there is an error in the data that is + * read from a stream during de-serialization. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + * @since 1.1 + * @status updated to 1.4 + */ +public class StreamCorruptedException extends ObjectStreamException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 8983558202217591746L; + + /** + * Create an exception without a descriptive error message. + */ + public StreamCorruptedException() + { + } + + /** + * Create an exception with a descriptive error message. + * + * @param message the descriptive error message + */ + public StreamCorruptedException(String message) + { + super(message); + } +} // class StreamCorruptedException diff --git a/libjava/classpath/java/io/StreamTokenizer.java b/libjava/classpath/java/io/StreamTokenizer.java new file mode 100644 index 00000000000..bd7773b1990 --- /dev/null +++ b/libjava/classpath/java/io/StreamTokenizer.java @@ -0,0 +1,708 @@ +/* StreamTokenizer.java -- parses streams of characters into tokens + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.io; + +/** + * This class parses streams of characters into tokens. There are a + * million-zillion flags that can be set to control the parsing, as + * described under the various method headings. + * + * @author Warren Levy (warrenl@cygnus.com) + * @date October 25, 1998. + */ +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 + * "The Java Language Specification", ISBN 0-201-63451-1 + * plus online API docs for JDK 1.2 beta from http://www.javasoft.com. + * Status: Believed complete and correct. + */ + +public class StreamTokenizer +{ + /** A constant indicating that the end of the stream has been read. */ + public static final int TT_EOF = -1; + + /** A constant indicating that the end of the line has been read. */ + public static final int TT_EOL = '\n'; + + /** A constant indicating that a number token has been read. */ + public static final int TT_NUMBER = -2; + + /** A constant indicating that a word token has been read. */ + public static final int TT_WORD = -3; + + /** A constant indicating that no tokens have been read yet. */ + private static final int TT_NONE = -4; + + /** + * Contains the type of the token read resulting from a call to nextToken + * The rules are as follows: + * <ul> + * <li>For a token consisting of a single ordinary character, this is the + * value of that character.</li> + * <li>For a quoted string, this is the value of the quote character</li> + * <li>For a word, this is TT_WORD</li> + * <li>For a number, this is TT_NUMBER</li> + * <li>For the end of the line, this is TT_EOL</li> + * <li>For the end of the stream, this is TT_EOF</li> + * </ul> + */ + public int ttype = TT_NONE; + + /** The String associated with word and string tokens. */ + public String sval; + + /** The numeric value associated with number tokens. */ + public double nval; + + /* Indicates whether end-of-line is recognized as a token. */ + private boolean eolSignificant = false; + + /* Indicates whether word tokens are automatically made lower case. */ + private boolean lowerCase = false; + + /* Indicates whether C++ style comments are recognized and skipped. */ + private boolean slashSlash = false; + + /* Indicates whether C style comments are recognized and skipped. */ + private boolean slashStar = false; + + /* Attribute tables of each byte from 0x00 to 0xFF. */ + private boolean[] whitespace = new boolean[256]; + private boolean[] alphabetic = new boolean[256]; + private boolean[] numeric = new boolean[256]; + private boolean[] quote = new boolean[256]; + private boolean[] comment = new boolean[256]; + + /* The Reader associated with this class. */ + private PushbackReader in; + + /* Indicates if a token has been pushed back. */ + private boolean pushedBack = false; + + /* Contains the current line number of the reader. */ + private int lineNumber = 1; + + /** + * This method reads bytes from an <code>InputStream</code> and tokenizes + * them. For details on how this method operates by default, see + * <code>StreamTokenizer(Reader)</code>. + * + * @param is The <code>InputStream</code> to read from + * + * @deprecated Since JDK 1.1. + */ + public StreamTokenizer(InputStream is) + { + this(new InputStreamReader(is)); + } + + /** + * This method initializes a new <code>StreamTokenizer</code> to read + * characters from a <code>Reader</code> and parse them. The char values + * have their hight bits masked so that the value is treated a character + * in the range of 0x0000 to 0x00FF. + * <p> + * This constructor sets up the parsing table to parse the stream in the + * following manner: + * <ul> + * <li>The values 'A' through 'Z', 'a' through 'z' and 0xA0 through 0xFF + * are initialized as alphabetic</li> + * <li>The values 0x00 through 0x20 are initialized as whitespace</li> + * <li>The values '\'' and '"' are initialized as quote characters</li> + * <li>'/' is a comment character</li> + * <li>Numbers will be parsed</li> + * <li>EOL is not treated as significant</li> + * <li>C and C++ (//) comments are not recognized</li> + * </ul> + * + * @param r The <code>Reader</code> to read chars from + */ + public StreamTokenizer(Reader r) + { + in = new PushbackReader(r); + + whitespaceChars(0x00, 0x20); + wordChars('A', 'Z'); + wordChars('a', 'z'); + wordChars(0xA0, 0xFF); + commentChar('/'); + quoteChar('\''); + quoteChar('"'); + parseNumbers(); + } + + /** + * This method sets the comment attribute on the specified + * character. Other attributes for the character are cleared. + * + * @param ch The character to set the comment attribute for, passed as an int + */ + public void commentChar(int ch) + { + if (ch >= 0 && ch <= 255) + { + comment[ch] = true; + whitespace[ch] = false; + alphabetic[ch] = false; + numeric[ch] = false; + quote[ch] = false; + } + } + + /** + * This method sets a flag that indicates whether or not the end of line + * sequence terminates and is a token. The defaults to <code>false</code> + * + * @param flag <code>true</code> if EOF is significant, <code>false</code> + * otherwise + */ + public void eolIsSignificant(boolean flag) + { + eolSignificant = flag; + } + + /** + * This method returns the current line number. Note that if the + * <code>pushBack()</code> method is called, it has no effect on the + * line number returned by this method. + * + * @return The current line number + */ + public int lineno() + { + return lineNumber; + } + + /** + * This method sets a flag that indicates whether or not alphabetic + * tokens that are returned should be converted to lower case. + * + * @param flag <code>true</code> to convert to lower case, + * <code>false</code> otherwise + */ + public void lowerCaseMode(boolean flag) + { + lowerCase = flag; + } + + private boolean isWhitespace(int ch) + { + return (ch >= 0 && ch <= 255 && whitespace[ch]); + } + + private boolean isAlphabetic(int ch) + { + return ((ch > 255) || (ch >= 0 && alphabetic[ch])); + } + + private boolean isNumeric(int ch) + { + return (ch >= 0 && ch <= 255 && numeric[ch]); + } + + private boolean isQuote(int ch) + { + return (ch >= 0 && ch <= 255 && quote[ch]); + } + + private boolean isComment(int ch) + { + return (ch >= 0 && ch <= 255 && comment[ch]); + } + + /** + * This method reads the next token from the stream. It sets the + * <code>ttype</code> variable to the appropriate token type and + * returns it. It also can set <code>sval</code> or <code>nval</code> + * as described below. The parsing strategy is as follows: + * <ul> + * <li>Skip any whitespace characters.</li> + * <li>If a numeric character is encountered, attempt to parse a numeric + * value. Leading '-' characters indicate a numeric only if followed by + * another non-'-' numeric. The value of the numeric token is terminated + * by either the first non-numeric encountered, or the second occurrence of + * '-' or '.'. The token type returned is TT_NUMBER and <code>nval</code> + * is set to the value parsed.</li> + * <li>If an alphabetic character is parsed, all subsequent characters + * are read until the first non-alphabetic or non-numeric character is + * encountered. The token type returned is TT_WORD and the value parsed + * is stored in <code>sval</code>. If lower case mode is set, the token + * stored in <code>sval</code> is converted to lower case. The end of line + * sequence terminates a word only if EOL signficance has been turned on. + * The start of a comment also terminates a word. Any character with a + * non-alphabetic and non-numeric attribute (such as white space, a quote, + * or a commet) are treated as non-alphabetic and terminate the word.</li> + * <li>If a comment character is parsed, then all remaining characters on + * the current line are skipped and another token is parsed. Any EOL or + * EOF's encountered are not discarded, but rather terminate the comment.</li> + * <li>If a quote character is parsed, then all characters up to the + * second occurrence of the same quote character are parsed into a + * <code>String</code>. This <code>String</code> is stored as + * <code>sval</code>, but is not converted to lower case, even if lower case + * mode is enabled. The token type returned is the value of the quote + * character encountered. Any escape sequences + * (\b (backspace), \t (HTAB), \n (linefeed), \f (form feed), \r + * (carriage return), \" (double quote), \' (single quote), \\ + * (backslash), \XXX (octal esacpe)) are converted to the appropriate + * char values. Invalid esacape sequences are left in untranslated. + * Unicode characters like ('\ u0000') are not recognized. </li> + * <li>If the C++ comment sequence "//" is encountered, and the parser + * is configured to handle that sequence, then the remainder of the line + * is skipped and another token is read exactly as if a character with + * the comment attribute was encountered.</li> + * <li>If the C comment sequence "/*" is encountered, and the parser + * is configured to handle that sequence, then all characters up to and + * including the comment terminator sequence are discarded and another + * token is parsed.</li> + * <li>If all cases above are not met, then the character is an ordinary + * character that is parsed as a token by itself. The char encountered + * is returned as the token type.</li> + * </ul> + * + * @return The token type + * @exception IOException If an I/O error occurs + */ + public int nextToken() throws IOException + { + if (pushedBack) + { + pushedBack = false; + if (ttype != TT_NONE) + return ttype; + } + + sval = null; + int ch; + + // Skip whitespace. Deal with EOL along the way. + while (isWhitespace(ch = in.read())) + if (ch == '\n' || ch == '\r') + { + lineNumber++; + + // Throw away \n if in combination with \r. + if (ch == '\r' && (ch = in.read()) != '\n') + { + if (ch != TT_EOF) + in.unread(ch); + } + if (eolSignificant) + return (ttype = TT_EOL); + } + + if (ch == '/') + if ((ch = in.read()) == '/' && slashSlash) + { + while ((ch = in.read()) != '\n' && ch != '\r' && ch != TT_EOF) + ; + if (ch != TT_EOF) + in.unread(ch); + return nextToken(); // Recursive, but not too deep in normal cases + } + else if (ch == '*' && slashStar) + { + while (true) + { + ch = in.read(); + if (ch == '*') + { + if ((ch = in.read()) == '/') + break; + else if (ch != TT_EOF) + in.unread(ch); + } + else if (ch == '\n' || ch == '\r') + { + lineNumber++; + if (ch == '\r' && (ch = in.read()) != '\n') + { + if (ch != TT_EOF) + in.unread(ch); + } + } + else if (ch == TT_EOF) + { + break; + } + } + return nextToken(); // Recursive, but not too deep in normal cases + } + else + { + if (ch != TT_EOF) + in.unread(ch); + ch = '/'; + } + + if (ch == TT_EOF) + ttype = TT_EOF; + else if (isNumeric(ch)) + { + boolean isNegative = false; + if (ch == '-') + { + // Read ahead to see if this is an ordinary '-' rather than numeric. + ch = in.read(); + if (isNumeric(ch) && ch != '-') + { + isNegative = true; + } + else + { + if (ch != TT_EOF) + in.unread(ch); + return (ttype = '-'); + } + } + + StringBuffer tokbuf = new StringBuffer(); + tokbuf.append((char) ch); + + int decCount = 0; + while (isNumeric(ch = in.read()) && ch != '-') + if (ch == '.' && decCount++ > 0) + break; + else + tokbuf.append((char) ch); + + if (ch != TT_EOF) + in.unread(ch); + ttype = TT_NUMBER; + try + { + nval = Double.valueOf(tokbuf.toString()).doubleValue(); + } + catch (NumberFormatException _) + { + nval = 0.0; + } + if (isNegative) + nval = -nval; + } + else if (isAlphabetic(ch)) + { + StringBuffer tokbuf = new StringBuffer(); + tokbuf.append((char) ch); + while (isAlphabetic(ch = in.read()) || isNumeric(ch)) + tokbuf.append((char) ch); + if (ch != TT_EOF) + in.unread(ch); + ttype = TT_WORD; + sval = tokbuf.toString(); + if (lowerCase) + sval = sval.toLowerCase(); + } + else if (isComment(ch)) + { + while ((ch = in.read()) != '\n' && ch != '\r' && ch != TT_EOF) + ; + if (ch != TT_EOF) + in.unread(ch); + return nextToken(); // Recursive, but not too deep in normal cases. + } + else if (isQuote(ch)) + { + ttype = ch; + StringBuffer tokbuf = new StringBuffer(); + while ((ch = in.read()) != ttype && ch != '\n' && ch != '\r' && + ch != TT_EOF) + { + if (ch == '\\') + switch (ch = in.read()) + { + case 'a': ch = 0x7; + break; + case 'b': ch = '\b'; + break; + case 'f': ch = 0xC; + break; + case 'n': ch = '\n'; + break; + case 'r': ch = '\r'; + break; + case 't': ch = '\t'; + break; + case 'v': ch = 0xB; + break; + case '\n': ch = '\n'; + break; + case '\r': ch = '\r'; + break; + case '\"': + case '\'': + case '\\': + break; + default: + int ch1, nextch; + if ((nextch = ch1 = ch) >= '0' && ch <= '7') + { + ch -= '0'; + if ((nextch = in.read()) >= '0' && nextch <= '7') + { + ch = ch * 8 + nextch - '0'; + if ((nextch = in.read()) >= '0' && nextch <= '7' && + ch1 >= '0' && ch1 <= '3') + { + ch = ch * 8 + nextch - '0'; + nextch = in.read(); + } + } + } + + if (nextch != TT_EOF) + in.unread(nextch); + } + + tokbuf.append((char) ch); + } + + // Throw away matching quote char. + if (ch != ttype && ch != TT_EOF) + in.unread(ch); + + sval = tokbuf.toString(); + } + else + { + ttype = ch; + } + + return ttype; + } + + private void resetChar(int ch) + { + whitespace[ch] = alphabetic[ch] = numeric[ch] = quote[ch] = comment[ch] = + false; + } + + /** + * This method makes the specified character an ordinary character. This + * means that none of the attributes (whitespace, alphabetic, numeric, + * quote, or comment) will be set on this character. This character will + * parse as its own token. + * + * @param ch The character to make ordinary, passed as an int + */ + public void ordinaryChar(int ch) + { + if (ch >= 0 && ch <= 255) + resetChar(ch); + } + + /** + * This method makes all the characters in the specified range, range + * terminators included, ordinary. This means the none of the attributes + * (whitespace, alphabetic, numeric, quote, or comment) will be set on + * any of the characters in the range. This makes each character in this + * range parse as its own token. + * + * @param low The low end of the range of values to set the whitespace + * attribute for + * @param hi The high end of the range of values to set the whitespace + * attribute for + */ + public void ordinaryChars(int low, int hi) + { + if (low < 0) + low = 0; + if (hi > 255) + hi = 255; + for (int i = low; i <= hi; i++) + resetChar(i); + } + + /** + * This method sets the numeric attribute on the characters '0' - '9' and + * the characters '.' and '-'. + */ + public void parseNumbers() + { + for (int i = 0; i <= 9; i++) + numeric['0' + i] = true; + + numeric['.'] = true; + numeric['-'] = true; + } + + /** + * Puts the current token back into the StreamTokenizer so + * <code>nextToken</code> will return the same value on the next call. + * May cause the lineno method to return an incorrect value + * if lineno is called before the next call to nextToken. + */ + public void pushBack() + { + pushedBack = true; + } + + /** + * This method sets the quote attribute on the specified character. + * Other attributes for the character are cleared. + * + * @param ch The character to set the quote attribute for, passed as an int. + */ + public void quoteChar(int ch) + { + if (ch >= 0 && ch <= 255) + { + quote[ch] = true; + comment[ch] = false; + whitespace[ch] = false; + alphabetic[ch] = false; + numeric[ch] = false; + } + } + + /** + * This method removes all attributes (whitespace, alphabetic, numeric, + * quote, and comment) from all characters. It is equivalent to calling + * <code>ordinaryChars(0x00, 0xFF)</code>. + * + * @see #ordinaryChars(int, int) + */ + public void resetSyntax() + { + ordinaryChars(0x00, 0xFF); + } + + /** + * This method sets a flag that indicates whether or not "C++" language style + * comments ("//" comments through EOL ) are handled by the parser. + * If this is <code>true</code> commented out sequences are skipped and + * ignored by the parser. This defaults to <code>false</code>. + * + * @param flag <code>true</code> to recognized and handle "C++" style + * comments, <code>false</code> otherwise + */ + public void slashSlashComments(boolean flag) + { + slashSlash = flag; + } + + /** + * This method sets a flag that indicates whether or not "C" language style + * comments (with nesting not allowed) are handled by the parser. + * If this is <code>true</code> commented out sequences are skipped and + * ignored by the parser. This defaults to <code>false</code>. + * + * @param flag <code>true</code> to recognized and handle "C" style comments, + * <code>false</code> otherwise + */ + public void slashStarComments(boolean flag) + { + slashStar = flag; + } + + /** + * This method returns the current token value as a <code>String</code> in + * the form "Token[x], line n", where 'n' is the current line numbers and + * 'x' is determined as follows. + * <p> + * <ul> + * <li>If no token has been read, then 'x' is "NOTHING" and 'n' is 0</li> + * <li>If <code>ttype</code> is TT_EOF, then 'x' is "EOF"</li> + * <li>If <code>ttype</code> is TT_EOL, then 'x' is "EOL"</li> + * <li>If <code>ttype</code> is TT_WORD, then 'x' is <code>sval</code></li> + * <li>If <code>ttype</code> is TT_NUMBER, then 'x' is "n=strnval" where + * 'strnval' is <code>String.valueOf(nval)</code>.</li> + * <li>If <code>ttype</code> is a quote character, then 'x' is + * <code>sval</code></li> + * <li>For all other cases, 'x' is <code>ttype</code></li> + * </ul> + */ + public String toString() + { + String tempstr; + if (ttype == TT_EOF) + tempstr = "EOF"; + else if (ttype == TT_EOL) + tempstr = "EOL"; + else if (ttype == TT_WORD) + tempstr = sval; + else if (ttype == TT_NUMBER) + tempstr = "n=" + nval; + else if (ttype == TT_NONE) + tempstr = "NOTHING"; + else // must be an ordinary char. + tempstr = "\'" + (char) ttype + "\'"; + + return "Token[" + tempstr + "], line " + lineno(); + } + + /** + * This method sets the whitespace attribute for all characters in the + * specified range, range terminators included. + * + * @param low The low end of the range of values to set the whitespace + * attribute for + * @param hi The high end of the range of values to set the whitespace + * attribute for + */ + public void whitespaceChars(int low, int hi) + { + if (low < 0) + low = 0; + if (hi > 255) + hi = 255; + for (int i = low; i <= hi; i++) + { + resetChar(i); + whitespace[i] = true; + } + } + + /** + * This method sets the alphabetic attribute for all characters in the + * specified range, range terminators included. + * + * @param low The low end of the range of values to set the alphabetic + * attribute for + * @param hi The high end of the range of values to set the alphabetic + * attribute for + */ + public void wordChars(int low, int hi) + { + if (low < 0) + low = 0; + if (hi > 255) + hi = 255; + for (int i = low; i <= hi; i++) + alphabetic[i] = true; + } +} diff --git a/libjava/classpath/java/io/StringBufferInputStream.java b/libjava/classpath/java/io/StringBufferInputStream.java new file mode 100644 index 00000000000..090881985b8 --- /dev/null +++ b/libjava/classpath/java/io/StringBufferInputStream.java @@ -0,0 +1,187 @@ +/* StringBufferInputStream.java -- Read an String as a stream + Copyright (C) 1998, 1999, 2001, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 + * "The Java Language Specification", ISBN 0-201-63451-1 + * plus online API docs for JDK 1.2 beta from http://www.javasoft.com. + * Status: Believed complete and correct. Deprecated in JDK 1.1. + */ + +/** + * This class permits a <code>String</code> to be read as an input stream. + * The low eight bits of each character in the <code>String</code> are the + * bytes that are returned. The high eight bits of each character are + * discarded. + * <p> + * The mark/reset functionality in this class behaves differently than + * normal. The <code>mark()</code> method is always ignored and the + * <code>reset()</code> method always resets in stream to start reading from + * position 0 in the String. Note that since this method does not override + * <code>markSupported()</code> in <code>InputStream</code>, calling that + * method will return <code>false</code>. + * <p> + * Note that this class is deprecated because it does not properly handle + * 16-bit Java characters. It is provided for backwards compatibility only + * and should not be used for new development. The <code>StringReader</code> + * class should be used instead. + * + * @deprecated + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + */ +public class StringBufferInputStream extends InputStream +{ + /** The String which is the input to this stream. */ + protected String buffer; + + /** Position of the next byte in buffer to be read. */ + protected int pos = 0; + + /** The length of the String buffer. */ + protected int count; + + /** + * Create a new <code>StringBufferInputStream</code> that will read bytes + * from the passed in <code>String</code>. This stream will read from the + * beginning to the end of the <code>String</code>. + * + * @param s The <code>String</code> this stream will read from. + */ + public StringBufferInputStream(String s) + { + buffer = s; + count = s.length(); + } + + /** + * This method returns the number of bytes available to be read from this + * stream. The value returned will be equal to <code>count - pos</code>. + * + * @return The number of bytes that can be read from this stream before + * blocking, which is all of them + */ + public int available() + { + return count - pos; + } + + /** + * This method reads one byte from the stream. The <code>pos</code> counter + * is advanced to the next byte to be read. The byte read is returned as + * an int in the range of 0-255. If the stream position is already at the + * end of the buffer, no byte is read and a -1 is returned in order to + * indicate the end of the stream. + * + * @return The byte read, or -1 if end of stream + */ + public int read() + { + if (pos >= count) + return -1; // EOF + + return ((int) buffer.charAt(pos++)) & 0xFF; + } + +/** + * This method reads bytes from the stream and stores them into a caller + * supplied buffer. It starts storing the data at index <code>offset</code> + * into the buffer and attempts to read <code>len</code> bytes. This method + * can return before reading the number of bytes requested if the end of the + * stream is encountered first. The actual number of bytes read is + * returned. If no bytes can be read because the stream is already at + * the end of stream position, a -1 is returned. + * <p> + * This method does not block. + * + * @param b The array into which the bytes read should be stored. + * @param off The offset into the array to start storing bytes + * @param len The requested number of bytes to read + * + * @return The actual number of bytes read, or -1 if end of stream. + */ + public int read(byte[] b, int off, int len) + { + if (off < 0 || len < 0 || off + len > b.length) + throw new ArrayIndexOutOfBoundsException(); + + if (pos >= count) + return -1; // EOF + + int numRead = Math.min(len, count - pos); + if (numRead < 0) + return 0; + + buffer.getBytes(pos, pos + numRead, b, off); + pos += numRead; + return numRead; + } + + /** + * This method sets the read position in the stream to the beginning + * setting the <code>pos</code> variable equal to 0. Note that this differs + * from the common implementation of the <code>reset()</code> method. + */ + public void reset() + { + pos = 0; + } + + /** + * This method attempts to skip the requested number of bytes in the + * input stream. It does this by advancing the <code>pos</code> value by the + * specified number of bytes. It this would exceed the length of the + * buffer, then only enough bytes are skipped to position the stream at + * the end of the buffer. The actual number of bytes skipped is returned. + * + * @param n The requested number of bytes to skip + * + * @return The actual number of bytes skipped. + */ + public long skip(long n) + { + if (n < 0) + return 0L; + + long actualSkip = Math.min(n, count - pos); + pos += actualSkip; + return actualSkip; + } +} diff --git a/libjava/classpath/java/io/StringReader.java b/libjava/classpath/java/io/StringReader.java new file mode 100644 index 00000000000..7e4e7d84f62 --- /dev/null +++ b/libjava/classpath/java/io/StringReader.java @@ -0,0 +1,209 @@ +/* StringReader.java -- permits a String to be read as a character input stream + Copyright (C) 1998, 1999, 2000, 2003 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.io; + +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 + * "The Java Language Specification", ISBN 0-201-63451-1 + * plus online API docs for JDK 1.2 beta from http://www.javasoft.com. + * Status: Believed complete and correct + */ + +/** + * This class permits a <code>String</code> to be read as a character + * input stream. + * <p> + * The mark/reset functionality in this class behaves differently than + * normal. If no mark has been set, then calling the <code>reset()</code> + * method rewinds the read pointer to the beginning of the <code>String</code>. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + * @date October 19, 1998. + */ +public class StringReader extends Reader +{ + /* A String provided by the creator of the stream. */ + private String buf; + + /* Position of the next char in buf to be read. */ + private int pos; + + /* The currently marked position in the stream. */ + private int markedPos; + + /* The index in buf one greater than the last valid character. */ + private int count; + + /** + * Create a new <code>StringReader</code> that will read chars from the + * passed in <code>String</code>. This stream will read from the beginning + * to the end of the <code>String</code>. + * + * @param buffer The <code>String</code> this stream will read from. + */ + public StringReader(String buffer) + { + super(); + buf = buffer; + + count = buffer.length(); + markedPos = pos = 0; + } + + public void close() + { + synchronized (lock) + { + buf = null; + } + } + + public void mark(int readAheadLimit) throws IOException + { + synchronized (lock) + { + if (buf == null) + throw new IOException("Stream closed"); + + // readAheadLimit is ignored per Java Class Lib. book, p. 1692. + markedPos = pos; + } + } + + public boolean markSupported() + { + return true; + } + + public int read() throws IOException + { + synchronized (lock) + { + if (buf == null) + throw new IOException("Stream closed"); + + if (pos < count) + return ((int) buf.charAt(pos++)) & 0xFFFF; + return -1; + } + } + + public int read(char[] b, int off, int len) throws IOException + { + synchronized (lock) + { + if (buf == null) + throw new IOException("Stream closed"); + + /* Don't need to check pos value, arraycopy will check it. */ + if (off < 0 || len < 0 || off + len > b.length) + throw new ArrayIndexOutOfBoundsException(); + + if (pos >= count) + return -1; + + int lastChar = Math.min(count, pos + len); + buf.getChars(pos, lastChar, b, off); + int numChars = lastChar - pos; + pos = lastChar; + return numChars; + } + } + + /** + * This method determines if the stream is ready to be read. This class + * is always ready to read and so always returns <code>true</code>, unless + * close() has previously been called in which case an IOException is + * thrown. + * + * @return <code>true</code> to indicate that this object is ready to be read. + * @exception IOException If the stream is closed. + */ + public boolean ready() throws IOException + { + if (buf == null) + throw new IOException("Stream closed"); + + return true; + } + + /** + * Sets the read position in the stream to the previously + * marked position or to 0 (i.e., the beginning of the stream) if the mark + * has not already been set. + */ + public void reset() throws IOException + { + synchronized (lock) + { + if (buf == null) + throw new IOException("Stream closed"); + + pos = markedPos; + } + } + + /** + * This method attempts to skip the requested number of chars in the + * input stream. It does this by advancing the <code>pos</code> value by + * the specified number of chars. It this would exceed the length of the + * buffer, then only enough chars are skipped to position the stream at + * the end of the buffer. The actual number of chars skipped is returned. + * + * @param n The requested number of chars to skip + * + * @return The actual number of chars skipped. + */ + public long skip(long n) throws IOException + { + synchronized (lock) + { + if (buf == null) + throw new IOException("Stream closed"); + + // Even though the var numChars is a long, in reality it can never + // be larger than an int since the result of subtracting 2 positive + // ints will always fit in an int. Since we have to return a long + // anyway, numChars might as well just be a long. + long numChars = Math.min((long) (count - pos), n < 0 ? 0L : n); + pos += numChars; + return numChars; + } + } +} + diff --git a/libjava/classpath/java/io/StringWriter.java b/libjava/classpath/java/io/StringWriter.java new file mode 100644 index 00000000000..a1e9aeb6bd2 --- /dev/null +++ b/libjava/classpath/java/io/StringWriter.java @@ -0,0 +1,191 @@ +/* StringWriter.java -- Writes bytes to a StringBuffer + Copyright (C) 1998, 1999, 2000, 2001, 2003, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +// Wow is this a dumb class. CharArrayWriter can do all this and +// more. I would redirect all calls to one in fact, but the javadocs say +// use a StringBuffer so I will comply. + +/** + * This class writes chars to an internal <code>StringBuffer</code> that + * can then be used to retrieve a <code>String</code>. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@cygnus.com) + */ +public class StringWriter extends Writer +{ + /** + * This is the default size of the buffer if the user doesn't specify it. + * @specnote The JCL Volume 1 says that 16 is the default size. + */ + private static final int DEFAULT_BUFFER_SIZE = 16; + + /** + * This method closes the stream. The contents of the internal buffer + * can still be retrieved, but future writes are not guaranteed to work. + * + * @exception IOException If an error orrurs. + */ + public void close () throws IOException + { + // JCL says this does nothing. This seems to violate the Writer + // contract, in that other methods should still throw an + // IOException after a close. Still, we just follow JCL. + } + + /** + * This method flushes any buffered characters to the underlying output. + * It does nothing in this class. + */ + public void flush () + { + } + + /** + * This method returns the <code>StringBuffer</code> object that this + * object is writing to. Note that this is the actual internal buffer, so + * any operations performed on it will affect this stream object. + * + * @return The <code>StringBuffer</code> object being written to + */ + public StringBuffer getBuffer () + { + return buffer; + } + + /** + * This method initializes a new <code>StringWriter</code> to write to a + * <code>StringBuffer</code> initially sized to a default size of 16 + * chars. + */ + public StringWriter () + { + this (DEFAULT_BUFFER_SIZE); + } + + /** + * This method initializes a new <code>StringWriter</code> to write to a + * <code>StringBuffer</code> with the specified initial size. + * + * @param size The initial size to make the <code>StringBuffer</code> + */ + public StringWriter (int size) + { + super (); + buffer = new StringBuffer (size); + lock = buffer; + } + + /** + * This method returns the contents of the internal <code>StringBuffer</code> + * as a <code>String</code>. + * + * @return A <code>String</code> representing the chars written to + * this stream. + */ + public String toString () + { + return buffer.toString(); + } + + /** + * This method writes a single character to the output, storing it in + * the internal buffer. + * + * @param oneChar The <code>char</code> to write, passed as an int. + */ + public void write (int oneChar) + { + buffer.append((char) (oneChar & 0xFFFF)); + } + + /** + * This method writes <code>len</code> chars from the specified + * array starting at index <code>offset</code> in that array to this + * stream by appending the chars to the end of the internal buffer. + * + * @param chars The array of chars to write + * @param offset The index into the array to start writing from + * @param len The number of chars to write + */ + public void write (char[] chars, int offset, int len) + { + buffer.append(chars, offset, len); + } + + /** + * This method writes the characters in the specified <code>String</code> + * to the stream by appending them to the end of the internal buffer. + * + * @param str The <code>String</code> to write to the stream. + */ + public void write (String str) + { + buffer.append(str); + } + + /** + * This method writes out <code>len</code> characters of the specified + * <code>String</code> to the stream starting at character position + * <code>offset</code> into the stream. This is done by appending the + * characters to the internal buffer. + * + * @param str The <code>String</code> to write characters from + * @param offset The character position to start writing from + * @param len The number of characters to write. + */ + public void write (String str, int offset, int len) + { +// char[] tmpbuf = new char[len]; +// str.getChars(offset, offset+len, tmpbuf, 0); +// buf.append(tmpbuf, 0, tmpbuf.length); + // This implementation assumes that String.substring is more + // efficient than using String.getChars and copying the data + // twice. For libgcj, this is true. For Classpath, it is not. + // FIXME. + buffer.append(str.substring(offset, offset + len)); + } + + /** + * This is the <code>StringBuffer</code> that we use to store bytes that + * are written. + */ + private StringBuffer buffer; +} diff --git a/libjava/classpath/java/io/SyncFailedException.java b/libjava/classpath/java/io/SyncFailedException.java new file mode 100644 index 00000000000..c514c44f23f --- /dev/null +++ b/libjava/classpath/java/io/SyncFailedException.java @@ -0,0 +1,66 @@ +/* SyncFailedException.java -- a file sync failed + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * Thrown when a file synchronization fails. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@cygnus.com) + * @see FileDescriptor#sync() + * @since 1.1 + * @status updated to 1.4 + */ +public class SyncFailedException extends IOException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -2353342684412443330L; + + /** + * Create an exception with a descriptive error message. + * + * @param message the descriptive error message + */ + public SyncFailedException(String message) + { + super(message); + } +} // class SyncFailedException diff --git a/libjava/classpath/java/io/UTFDataFormatException.java b/libjava/classpath/java/io/UTFDataFormatException.java new file mode 100644 index 00000000000..6bb76aebdfe --- /dev/null +++ b/libjava/classpath/java/io/UTFDataFormatException.java @@ -0,0 +1,74 @@ +/* UTFDataFormatException.java -- thrown on bad format in UTF data + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * When reading a UTF string from an input stream, this exception is thrown + * to indicate that the data read is invalid. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@cygnus.com) + * @see DataInput + * @see DataInputStream#readUTF(DataInput) + * @status updated to 1.4 + */ +public class UTFDataFormatException extends IOException +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = 420743449228280612L; + + /** + * Create a new UTFDataFormatException without a descriptive error message. + */ + public UTFDataFormatException() + { + } + + /** + * Create a new UTFDataFormatException with a descriptive error message. + * + * @param message the descriptive error message + */ + public UTFDataFormatException(String message) + { + super(message); + } +} // class UTFDataFormatException diff --git a/libjava/classpath/java/io/UnsupportedEncodingException.java b/libjava/classpath/java/io/UnsupportedEncodingException.java new file mode 100644 index 00000000000..cf0ab64cb89 --- /dev/null +++ b/libjava/classpath/java/io/UnsupportedEncodingException.java @@ -0,0 +1,73 @@ +/* UnsupportedEncodingException.java -- the requested encoding isn't supported + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This exception is thrown when the requested character encoding is + * not supported. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Per Bothner (bothner@cygnus.com) + * @since 1.1 + * @status updated to 1.4 + */ +public class UnsupportedEncodingException extends IOException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -4274276298326136670L; + + /** + * Create an exception without a descriptive error message. + */ + public UnsupportedEncodingException() + { + } + + /** + * Create an exception with a descriptive error message. + * + * @param message the descriptive error message + */ + public UnsupportedEncodingException(String message) + { + super(message); + } +} // class UnsupportedEncodingException diff --git a/libjava/classpath/java/io/WriteAbortedException.java b/libjava/classpath/java/io/WriteAbortedException.java new file mode 100644 index 00000000000..f051dc975c8 --- /dev/null +++ b/libjava/classpath/java/io/WriteAbortedException.java @@ -0,0 +1,109 @@ +/* WriteAbortedException.java -- wraps an exception thrown while writing + Copyright (C) 1998, 2000, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This exception is thrown when another ObjectStreamException occurs during + * a serialization read or write. The stream is reset, and deserialized + * objects are discarded. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.1 + * @status updated to 1.4 + */ +public class WriteAbortedException extends ObjectStreamException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -3326426625597282442L; + + /** + * The cause of this exception. This pre-dates the exception chaining + * of Throwable; and although you can change this field, you are wiser + * to leave it alone. + * + * @serial the exception cause + */ + public Exception detail; + + /** + * Create a new WriteAbortedException with a specified message and + * cause. + * + * @param msg the message + * @param detail the cause + */ + public WriteAbortedException(String msg, Exception detail) + { + super(msg); + initCause(detail); + this.detail = detail; + } + + /** + * This method returns a message indicating what went wrong, in this + * format: + * <code>super.getMessage() + (detail == null ? "" : "; " + detail)</code>. + * + * @return the chained message + */ + public String getMessage() + { + if (detail == this || detail == null) + return super.getMessage(); + return super.getMessage() + "; " + detail; + } + + /** + * Returns the cause of this exception. Note that this may not be the + * original cause, thanks to the <code>detail</code> field being public + * and non-final (yuck). However, to avoid violating the contract of + * Throwable.getCause(), this returns null if <code>detail == this</code>, + * as no exception can be its own cause. + * + * @return the cause + * @since 1.4 + */ + public Throwable getCause() + { + return detail == this ? null : detail; + } +} // class WriteAbortedException diff --git a/libjava/classpath/java/io/Writer.java b/libjava/classpath/java/io/Writer.java new file mode 100644 index 00000000000..f153e31cfed --- /dev/null +++ b/libjava/classpath/java/io/Writer.java @@ -0,0 +1,192 @@ +/* Writer.java -- Base class for character output streams + Copyright (C) 1998, 1999, 2001, 2003, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/* Written using "Java Class Libraries", 2nd edition, plus online + * API docs for JDK 1.2 beta from http://www.javasoft.com. + * Status: Believed complete and correct. + */ + +/** + * This abstract class forms the base of the hierarchy of classes that + * write output as a stream of chars. It provides a common set of methods + * for writing chars to stream. Subclasses implement and/or extend these + * methods to write chars in a particular manner or to a particular + * destination such as a file on disk or network connection. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Per Bothner (bothner@cygnus.com) + */ +public abstract class Writer +{ + /** + * This is the object used to synchronize criticial code sections for + * thread safety. Subclasses should use this field instead of using + * synchronized methods or explicity synchronizations on <code>this</code> + */ + protected Object lock; + + /** + * This is the default no-argument constructor for this class. This method + * will set up the class to synchronize criticial sections on itself. + */ + protected Writer() + { + lock = this; + } + + /** + * This method initializes a <code>Writer</code> that will synchronize + * on the specified <code>Object</code>. + * + * @param lock The <code>Object</code> to use for synchronizing critical + * sections. Must not be null. + */ + protected Writer(Object lock) + { + if (lock == null) + throw new NullPointerException(); + + this.lock = lock; + } + + /** + * This method forces any data that may have been buffered to be written + * to the underlying output device. Please note that the host environment + * might perform its own buffering unbeknowst to Java. In that case, a + * write made (for example, to a disk drive) might be cached in OS + * buffers instead of actually being written to disk. + * + * @exception IOException If an error occurs + */ + public abstract void flush() throws IOException; + + /** + * This method closes the stream. Any internal or native resources + * associated + * with this stream are freed. Any subsequent attempt to access the stream + * might throw an exception. + * <p> + * This method in this class does nothing. + * + * @exception IOException If an error occurs + */ + public abstract void close() throws IOException; + + /** + * This method writes a single char to the output stream. + * + * @param b The char to be written to the output stream, passed as an int + * + * @exception IOException If an error occurs + */ + public void write(int b) throws IOException + { + char[] buf = new char[1]; + + buf[0] = (char)b; + write(buf, 0, buf.length); + } + + /** + * This method all the writes char from the passed array to the output + * stream. This method is equivalent to + * <code>write(buf, 0, buf.length)</code> which + * is exactly how it is implemented in this class. + * + * @param buf The array of char to write + * + * @exception IOException If an error occurs + */ + public void write(char[] buf) throws IOException + { + write(buf, 0, buf.length); + } + + /** + * This method writes <code>len</code> char from the specified array + * <code>buf</code> starting at index <code>offset</code> into the array. + * <p> + * Subclasses must provide an implementation of this abstract method. + * + * @param buf The array of char to write from + * @param offset The index into the array to start writing from + * @param len The number of char to write + * + * @exception IOException If an error occurs + */ + public abstract void write(char[] buf, int offset, int len) + throws IOException; + + /** + * This method writes all the characters in a <code>String</code> to the + * output. + * + * @param str The <code>String</code> whose chars are to be written. + * + * @exception IOException If an error occurs + */ + public void write(String str) throws IOException + { + write(str, 0, str.length()); + } + + /** + * This method writes <code>len</code> chars from the <code>String</code> + * starting at position <code>offset</code>. + * + * @param str The <code>String</code> that is to be written + * @param offset The character offset into the <code>String</code> to start + * writing from + * @param len The number of chars to write + * + * @exception IOException If an error occurs + */ + public void write(String str, int offset, int len) throws IOException + { + // FIXME - for libgcj re-write using native code to not require + // copied buffer. + char[] buf = new char[len]; + + str.getChars(offset, offset + len, buf, 0); + write(buf, 0, len); + } + +} // class Writer + diff --git a/libjava/classpath/java/io/class-dependencies.conf b/libjava/classpath/java/io/class-dependencies.conf new file mode 100644 index 00000000000..633bb174941 --- /dev/null +++ b/libjava/classpath/java/io/class-dependencies.conf @@ -0,0 +1,100 @@ +# This property file contains dependencies of classes, methods, and +# field on other methods or classes. +# +# Syntax: +# +# <used>: <needed 1> [... <needed N>] +# +# means that when <used> is included, <needed 1> (... <needed N>) must +# be included as well. +# +# <needed X> and <used> are of the form +# +# <class.methodOrField(signature)> +# +# or just +# +# <class> +# +# Within dependencies, variables can be used. A variable is defined as +# follows: +# +# {variable}: value1 value2 ... value<n> +# +# variables can be used on the right side of dependencies as follows: +# +# <used>: com.bla.blu.{variable}.Class.m()V +# +# The use of the variable will expand to <n> dependencies of the form +# +# <used>: com.bla.blu.value1.Class.m()V +# <used>: com.bla.blu.value2.Class.m()V +# ... +# <used>: com.bla.blu.value<n>.Class.m()V +# +# Variables can be redefined when building a system to select the +# required support for features like encodings, protocols, etc. +# +# Hints: +# +# - For methods and fields, the signature is mandatory. For +# specification, please see the Java Virtual Machine Specification by +# SUN. Unlike in the spec, field signatures (types) are in brackets. +# +# - Package names must be separated by '/' (and not '.'). E.g., +# java/lang/Class (this is necessary, because the '.' is used to +# separate method or field names from classes) +# +# - In case <needed> refers to a class, only the class itself will be +# included in the resulting binary, NOT necessarily all its methods +# and fields. If you want to refer to all methods and fields, you can +# write class.* as an abbreviation. +# +# - Abbreviations for packages are also possible: my/package/* means all +# methods and fields of all classes in my/package. +# +# - A line with a trailing '\' continues in the next line. + +java/io/File: \ + java/lang/ClassNotFoundException.<init>(Ljava/lang/String;)V \ + java/lang/InternalError.<init>(Ljava/lang/String;)V \ + java/io/IOException.<init>(Ljava/lang/String;)V \ + java/lang/IllegalArgumentException.<init>(Ljava/lang/String;)V + +java/io/FileDescriptor: \ + java/lang/ClassNotFoundException.<init>(Ljava/lang/String;)V \ + java/lang/InternalError.<init>(Ljava/lang/String;)V \ + java/lang/IllegalArgumentException.<init>(Ljava/lang/String;)V \ + java/io/IOException.<init>(Ljava/lang/String;)V + +java/io/FileInputStream: \ + java/lang/ClassNotFoundException.<init>(Ljava/lang/String;)V \ + java/lang/InternalError.<init>(Ljava/lang/String;)V \ + java/io/IOException.<init>(Ljava/lang/String;)V \ + java/io/FileNotFoundException.<init>(Ljava/lang/String;)V + +java/io/FileOutputStream: \ + java/lang/ClassNotFoundException.<init>(Ljava/lang/String;)V \ + java/lang/InternalError.<init>(Ljava/lang/String;)V \ + java/io/FileNotFoundException.<init>(Ljava/lang/String;)V \ + java/io/IOException.<init>(Ljava/lang/String;)V + +java/io/ObjectInputStream: \ + java/lang/ClassNotFoundException.<init>(Ljava/lang/String;)V \ + java/lang/InternalError.<init>(Ljava/lang/String;)V \ + java/lang/SecurityManager.currentClassLoader()Ljava/lang/ClassLoader; \ + java/lang/IllegalArgumentException.<init>(Ljava/lang/String;)V + +java/io/ObjectOutputStream: \ + java/lang/ClassNotFoundException.<init>(Ljava/lang/String;)V \ + java/lang/InternalError.<init>(Ljava/lang/String;)V \ + java/lang/SecurityManager.currentClassLoader()Ljava/lang/ClassLoader; \ + java/lang/IllegalArgumentException.<init>(Ljava/lang/String;)V + +java/io/RandomAccessFile: \ + java/lang/ClassNotFoundException.<init>(Ljava/lang/String;)V \ + java/lang/InternalError.<init>(Ljava/lang/String;)V \ + java/io/FileNotFoundException.<init>(Ljava/lang/String;)V \ + java/io/IOException.<init>(Ljava/lang/String;)V + +# end of file diff --git a/libjava/classpath/java/io/package.html b/libjava/classpath/java/io/package.html new file mode 100644 index 00000000000..02e1c5bc365 --- /dev/null +++ b/libjava/classpath/java/io/package.html @@ -0,0 +1,46 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<!-- package.html - describes classes in java.io package. + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. --> + +<html> +<head><title>GNU Classpath - java.io</title></head> + +<body> +<p>Classes for manipulating character and byte streams and files.</p> + +</body> +</html> |