/*************************************************************************
/* DataInputStream.java -- Class for reading Java data from a stream
/*
/* Copyright (c) 1998 by Aaron M. Renn (arenn@urbanophile.com)
/*
/* This program is free software; you can redistribute it and/or modify
/* it under the terms of the GNU Library General Public License as published
/* by the Free Software Foundation, version 2. (see COPYING.LIB)
/*
/* This program 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 Library General Public License
/* along with this program; if not, write to the Free Software Foundation
/* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA
/*************************************************************************/
package java.io;
/**
* This subclass of @code{FilteredInputStream} implements the
* @code{DataInput} interface that provides method for reading primitive
* Java data types from a stream.
*
* @see DataInput
*
* @version 0.0
*
* @author Aaron M. Renn (arenn@urbanophile.com)
*/
public class DataInputStream extends FilterInputStream implements DataInput
{
/*************************************************************************/
/*
* Class Methods
*/
/**
* This method reads a String encoded in UTF-8 format from the
* specified DataInput
source.
*
* @param in The DataInput
source to read from
*
* @return The String read from the source
*
* @exception IOException If an error occurs
*/
public static final String
readUTF(DataInput in) throws IOException
{
return(in.readUTF());
}
/*************************************************************************/
/*
* Constructors
*/
/**
* This method initializes a new @code{DataInputStream} to read from
* the specified subordinate stream.
*
* @param in The subordinate @code{InputStream} to read from
*/
public
DataInputStream(InputStream in)
{
super(in);
this.in = new PushbackInputStream(in);
}
/*************************************************************************/
/*
* Instance Methods
*/
/**
* 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}. If the byte is non-zero, then
* the value returned is @code{true}.
*
* This method can read a @code{boolean} written by an object implementing the
* @code{writeBoolean()} method in the @code{DataOutput} interface.
*
* @return The @code{boolean} 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 EOFException, IOException
{
int byte_read = in.read();
if (byte_read == -1)
throw new EOFException("Unexpected end of stream");
if (byte_read == 0)
return(false);
else
return(true);
}
/*************************************************************************/
/**
* This method reads a Java byte value from an input stream. The value
* is in the range of -128 to 127.
*
* This method can read a @code{byte} written by an object implementing the
* @code{writeByte()} method in the @code{DataOutput} interface.
*
* @return The @code{byte} 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 EOFException, IOException
{
int byte_read = in.read();
if (byte_read == -1)
throw new EOFException("Unexpected end of stream");
return((byte)byte_read);
}
/*************************************************************************/
/**
* This method reads 8 unsigned bits into a Java @code{int} value from the
* stream. The value returned is in the range of 0 to 255.
*
* This method can read an unsigned byte written by an object implementing the
* @code{writeUnsignedByte()} method in the @code{DataOutput} interface.
*
* @return The unsigned bytes value read as a Java @code{int}.
*
* @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 EOFException, IOException
{
int byte_read = in.read();
if (byte_read == -1)
throw new EOFException("Unexpected end of stream");
return(byte_read);
}
/*************************************************************************/
/**
* This method reads a Java @code{char} 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}. The two bytes are stored most
* significant byte first (i.e., "big endian") regardless of the native
* host byte ordering.
*
* As an example, if @code{byte1} and code{byte2} represent the first
* and second byte read from the stream respectively, they will be
* transformed to a @code{char} in the following manner:
*
* @code{(char)(((byte1 & 0xFF) << 8) | (byte2 & 0xFF)}
*
* This method can read a @code{char} written by an object implementing the
* @code{writeChar()} method in the @code{DataOutput} interface.
*
* @return The @code{char} 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 EOFException, IOException
{
byte[] buf = new byte[2];
readFully(buf);
char retval = (char)(((buf[0] & 0xFF) << 8) | (buf[1] & 0xFF));
return(retval);
}
/*************************************************************************/
/**
* 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}. The two bytes are stored most
* significant byte first (i.e., "big endian") regardless of the native
* host byte ordering.
*
* As an example, if @code{byte1} and code{byte2} represent the first
* and second byte read from the stream respectively, they will be
* transformed to a @code{short} in the following manner:
*
* @code{(short)(((byte1 & 0xFF) << 8) | (byte2 & 0xFF)}
*
* The value returned is in the range of -32768 to 32767.
*
* This method can read a @code{short} written by an object implementing the
* @code{writeShort()} method in the @code{DataOutput} interface.
*
* @return The @code{short} 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 EOFException, IOException
{
byte[] buf = new byte[2];
readFully(buf);
short retval = (short)(((buf[0] & 0xFF) << 8) | (buf[1] & 0xFF));
return(retval);
}
/*************************************************************************/
/**
* 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}. The two bytes are stored most
* significant byte first (i.e., "big endian") regardless of the native
* host byte ordering.
*
* As an example, if @code{byte1} and code{byte2} represent the first
* and second byte read from the stream respectively, they will be
* transformed to an @code{int} in the following manner:
*
* @code{(int)(((byte1 & 0xFF) << 8) + (byte2 & 0xFF))}
*
* The value returned is in the range of 0 to 65535.
*
* This method can read an unsigned short written by an object implementing
* the @code{writeUnsignedShort()} method in the @code{DataOutput} interface.
*
* @return The unsigned short value read as a Java @code{int}.
*
* @exception EOFException If end of file is reached before reading the value
* @exception IOException If any other error occurs
*/
public final int
readUnsignedShort() throws EOFException, IOException
{
byte[] buf = new byte[2];
readFully(buf);
int retval = ((buf[0] & 0xFF) << 8) | (buf[1] & 0xFF);
return(retval);
}
/*************************************************************************/
/**
* This method reads a Java @code{int} value from an input stream
* It operates by reading four bytes from the stream and converting them to
* a single Java @code{int}. The bytes are stored most
* significant byte first (i.e., "big endian") regardless of the native
* host byte ordering.
*
* As an example, if @code{byte1} through @code{byte4} represent the first
* four bytes read from the stream, they will be
* transformed to an @code{int} in the following manner:
*
* @code{(int)(((byte1 & 0xFF) << 24) + ((byte2 & 0xFF) << 16) +
* ((byte3 & 0xFF) << 8) + (byte4 & 0xFF)))}
*
* The value returned is in the range of 0 to 65535.
*
* This method can read an @code{int} written by an object implementing the
* @code{writeInt()} method in the @code{DataOutput} interface.
*
* @return The @code{int} 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 EOFException, IOException
{
byte[] buf = new byte[4];
readFully(buf);
int retval = ((buf[0] & 0xFF) << 24) | ((buf[1] & 0xFF) << 16) |
((buf[2] & 0xFF) << 8) | (buf[3] & 0xFF);
return(retval);
}
/*************************************************************************/
/**
* 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}. The bytes are stored most
* significant byte first (i.e., "big endian") regardless of the native
* host byte ordering.
*
* As an example, if @code{byte1} through @code{byte8} represent the first
* eight bytes read from the stream, they will be
* transformed to an @code{long} in the following manner:
*
* @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)))}
*
* The value returned is in the range of 0 to 65535.
*
* This method can read an @code{long} written by an object implementing the
* @code{writeLong()} method in the @code{DataOutput} interface.
*
* @return The @code{long} 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 EOFException, IOException
{
byte[] buf = new byte[8];
readFully(buf);
long retval = (((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);
return(retval);
}
/*************************************************************************/
/**
* This method reads a Java float value from an input stream. It operates
* by first reading an @code{int} value from the stream by calling the
* @code{readInt()} method in this interface, then converts that @code{int}
* to a @code{float} using the @code{intBitsToFloat} method in
* the class @{java.lang.Float}.
*
* This method can read a @code{float} written by an object implementing the
* @code{writeFloat()} method in the @code{DataOutput} interface.
*
* @return The @code{float} 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 EOFException, IOException
{
int val = readInt();
return(Float.intBitsToFloat(val));
}
/*************************************************************************/
/**
* This method reads a Java double value from an input stream. It operates
* by first reading a @code{logn} value from the stream by calling the
* @code{readLong()} method in this interface, then converts that @code{long}
* to a @code{double} using the @code{longBitsToDouble} method in
* the class @{java.lang.Double}.
*
* This method can read a @code{double} written by an object implementing the
* @code{writeDouble()} method in the @code{DataOutput} interface.
*
* @return The @code{double} 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 EOFException, IOException
{
long val = readLong();
return(Double.longBitsToDouble(val));
}
/*************************************************************************/
/**
* This method reads the next line of text data from an input stream.
* It operates by reading bytes and converting those bytes to @{char}
* values by treating the byte read as the low eight bits of the @{char}
* and using @code{0} as the high eight bits. Because of this, it does
* not support the full 16-bit Unicode character set.
*
* 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}.
* A line terminator is a byte sequence consisting of either
* @samp{\r}, @samp{\n} or @samp{\r\n}. These termination charaters are
* discarded and are not returned as part of the string.
*
* This method can read data that was written by an object implementing the
* @code{writeLine()} method in @code{DataOutput}.
*
* @return The line read as a @code{String}
*
* @exception IOException If an error occurs
*
* @see DataOutput
*
* @deprecated
*/
public final String
readLine() throws IOException
{
StringBuffer sb = new StringBuffer("");
for (;;)
{
int byte_read = in.read();
if (byte_read == -1)
return(sb.toString());
char c = (char)byte_read;
if (c == '\r')
{
byte_read = in.read();
if (((char)byte_read) != '\n')
((PushbackInputStream)in).unread(byte_read);
return(sb.toString());
}
if (c == '\n')
return(sb.toString());
sb.append(c);
}
}
/*************************************************************************/
/**
* This method reads a @code{String} 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()} method of this
* interface.
*
* After the number of remaining bytes have been determined, these bytes
* are read an transformed into @{char} values. These @code{char} 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.
*
* If the first byte has a high order bit of @samp{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} is the byte read from the stream, it would
* be converted to a @code{char} like so:
*
* @code{(char)byte1}
*
* If the first byte has @code{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
* @samp{10} as its high order bits). These values are in most significant
* byte first (i.e., "big endian") order.
*
* As an example, if @code{byte1} and @code{byte2} 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} like so:
*
* @code{(char)(((byte1 & 0x1F) << 6) | (byte2 & 0x3F))}
*
* If the first byte has a @code{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 @code{10} as their high order bits). These values are in most
* significant byte first (i.e., "big endian") order.
*
* As an example, if @code{byte1}, @code{byte2}, and @code{byte3} 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} like so:
*
* @code{(char)(((byte1 & 0x0F) << 12) | ((byte2 & 0x3F) << 6) | (byte3 & 0x3F))}
*
* 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 @samp{\u0000} which is encoded as two bytes. This is a
* modification of the UTF standard used to prevent C language style
* @samp{NUL} values from appearing in the byte stream.
*
* This method can read data that was written by an object implementing the
* @code{writeUTF()} method in @code{DataOutput}.
*
* @returns The @code{String} 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 EOFException, UTFDataFormatException, IOException
{
StringBuffer sb = new StringBuffer("");
int num_bytes = readUnsignedShort();
for (int i = 0; i < num_bytes; i++)
{
int byte_read = in.read();
if (byte_read == -1)
throw new EOFException("Unexpected end of stream");
// Three byte encoding case
if ((byte_read & 0xE0) == 0xE0) // 224
{
int val = (byte_read & 0x0F) << 12;
byte_read = in.read();
if (byte_read == -1)
throw new EOFException("Unexpected end of stream");
if ((byte_read & 0x80) != 0x80)
throw new UTFDataFormatException("Bad byte in input: " + byte_read);
val |= (byte_read & 0x3F) << 6;
byte_read = in.read();
if (byte_read == -1)
throw new EOFException("Unexpected end of stream");
if ((byte_read & 0x80) != 0x80)
throw new UTFDataFormatException("Bad byte in input: " + byte_read);
val |= (byte_read & 0x3F);
sb.append((char)val);
i += 2;
}
// Two byte encoding case
else if ((byte_read & 0xC0) == 0xC0) // 192
{
int val = (byte_read & 0x1F) << 6;
byte_read = in.read();
if (byte_read == -1)
throw new EOFException("Unexpected end of stream");
if ((byte_read & 0x80) != 0x80)
throw new UTFDataFormatException("Bad byte in input: " + byte_read);
val |= (byte_read & 0x3F);
sb.append((char)val);
++i;
}
// One byte encoding case
else if (byte_read < 128)
{
sb.append((char)byte_read);
}
else
{
throw new UTFDataFormatException("Bad byte in input: " + byte_read);
}
}
return(sb.toString());
}
/*************************************************************************/
/**
* 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 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
*/
public final void
readFully(byte[] buf) throws EOFException, IOException
{
readFully(buf, 0, buf.length);
}
/*************************************************************************/
/**
* This method reads raw bytes into the passed array @code{buf} starting
* @code{offset} bytes into the buffer. The number of bytes read will be
* exactly @code{len}. 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} bytes.
*
* @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 EOFException, IOException
{
int total_read = 0;
while (total_read < len)
{
int bytes_read = in.read(buf, offset + total_read, len - total_read);
if (bytes_read == -1)
throw new EOFException("Unexpected end of stream");
total_read += bytes_read;
}
}
/*************************************************************************/
/**
* 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 num_bytes The requested number of bytes to skip.
*
* @return The number of bytes actually skipped.
*
* @exception IOException If an error occurs.
*/
public final int
skipBytes(int n) throws EOFException, IOException
{
long total_skipped = in.skip(n);
return((int)n);
}
} // interface DataInput