/* **************************************************************************

  $Archive: /njcl_v2/src/com/novell/java/io/RandomAccess.java $
  $Revision: 7 $
  $Modtime: 1/18/00 9:24a $
 
  Copyright (c) 1998 Novell, Inc.  All Rights Reserved.

  THIS WORK IS  SUBJECT  TO  U.S.  AND  INTERNATIONAL  COPYRIGHT  LAWS  AND
  TREATIES.   NO  PART  OF  THIS  WORK MAY BE  USED,  PRACTICED,  PERFORMED
  COPIED, DISTRIBUTED, REVISED, MODIFIED, TRANSLATED,  ABRIDGED, CONDENSED,
  EXPANDED,  COLLECTED,  COMPILED,  LINKED,  RECAST, TRANSFORMED OR ADAPTED
  WITHOUT THE PRIOR WRITTEN CONSENT OF NOVELL, INC. ANY USE OR EXPLOITATION
  OF THIS WORK WITHOUT AUTHORIZATION COULD SUBJECT THE PERPETRATOR TO
  CRIMINAL AND CIVIL LIABILITY.

****************************************************************************/

package com.novell.java.io;

import java.io.IOException;
import java.io.EOFException;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.DataInputStream;
import java.io.UTFDataFormatException;

import com.novell.java.io.SubordinateAccessOnlyException;
import com.novell.java.io.NoSubordinateAccessException;
import com.novell.java.io.RandomAccessNotSupportedException;

import com.novell.java.io.spi.DataAccessor;

/**
 * Provides random access functionality to various data device objects
 * that implements the DataAccessable interface.
 *
 * <p>This interface extends java.io.DataInput and java.io.DataOuput,
 * thereby adding the type filtered methods of these interfaces to this
 * interface.<p/>
 *
 * <p>The various constructors open access on (or with) the DataAccessable 
 * object passed into the constructors. The DataAccessable object might 
 * have container capabilities and support subordinate access, or it may
 * support only access to itself. The constructors with a name parameter 
 * (which must be atomic) will attempt to open subordinate access through 
 * the given DataAccessable object. The constructors without a name
 * parameter attempt to open access on the given DataAcessable object.
 *
 * <p>There is also a set of constructors that support provider-based
 * custom parameters. A given implementation of a DataAccessable object 
 * might require custom parameters to open the access. For example, a file
 * system provider might require open flags, fork selectors, name space 
 * information, size, and so forth, to open the stream. The custom Object
 * parameter allows for the passing of known provider-supplied objects to
 * their DataAccessable implementations, allowing the user full control
 * over the opening of the stream if needed. The various providers must 
 * implement reasonable defaults for any custom parameters if the non-custom
 * constructors are used (open flags == READ). See Novell's 
 * DataAccessableParameters and NFileInputStream for an example of how a 
 * provider might implement this support.
 *
 * <p>NoSubordinateAccessException will be thrown if the given DataAccessable
 * object does not support subordinate access (subordinate constructors).
 * SubordinateAccessOnlyException will be thrown if the given 
 * DataAccessable object does not support access on itself.
 *
 * @see DataAccessable
 * @see NInputStream
 * @see NOutputStream
 * @see com.novell.service.file.nw.NRandomAccessFile
 * @see com.novell.service.file.nw.DataAccessableParameters
 */

public class RandomAccess implements DataInput, DataOutput
{
   private DataAccessor accessor;
   private long position;

/* **************************************************************************
   non-subordinate stream
****************************************************************************/

   /**
    * Creates an instance of RandomAccess with a DataAccessable parameter.
    * 
    * <p>Opens random access to the DataAccessable accessor parameter.</p>
    *
    * @param accessor The DataAccessable object to use.
    *
    * @exception IOException If the accessor cannot be opened.
    *
    * @exception SubordinateAccessOnlyException If the accessor does not
    *            support access to itself, for example a directory.
    *
    * @exception RandomAccessNotSupportedException If the accessor does not
    *            support random access.
    */
    
   public RandomAccess(DataAccessable accessor)
      throws IOException
   {
      this.accessor = accessor.openStream(DataAccessor.RANDOM);
      position = 0;
   }

   /**
    * Creates an instance of RandomAccess with the custom parameter.
    * 
    * <p>Opens a random access to the DataAccessable accessor parameter, 
    * passing the provider-based custom parameter to the accessor for the 
    * open.</p>
    *
    * @param accessor The DataAccessable object to use.
    *
    * @param custom    Provider-specific custom object containing 
    *                  any needed parameters to open the access.
    *
    * @exception SubordinateAccessOnlyException If the accessor does not
    *            support access to itself, for example a directory.
    *
    * @exception RandomAccessNotSupportedException If the accessor does not
    *            support random access.
    *
    * @exception IllegalArgumentException If the custom object is not of
    *            the expected type for the given provider.
    */
    
   public RandomAccess(
      DataAccessable accessor, 
      Object custom)
      throws IOException
   {
      this.accessor = accessor.openStream(DataAccessor.RANDOM, custom);
   }

/* **************************************************************************
   subordinate stream
****************************************************************************/

   /**
    * Creates a subordinate instance of RandomAccess.
    * 
    * <p>Opens a subordinate random access to the DataAccessable accessor 
    * parameter.</p>
    *
    * @param name     The atomic subordinate access name to open.
    *
    * @param accessor The DataAccessable object to use.
    *
    * @exception IOException If the access cannot be opened.
    *
    * @exception NoSubordinateAccessException If the accessor does not
    *            support opening a subordinate access, for example, a file.
    *
    * @exception RandomAccessNotSupportedException If the accessor does not
    *            support random access.
    */
    
   public RandomAccess(String name, DataAccessable accessor)
      throws IOException
   {
      this.accessor = accessor.openStream(name, DataAccessor.RANDOM);
   }

   /**
    * Creates a subordinate instance of RandomAccess with the custom parameter.
    * 
    * <p>Opens a subordinate random access to the DataAccessable accessor 
    * parameter, passing the provider-based custom parameter to the accessor 
    * for the open.</p>
    *
    * @param name     The atomic subordinate access name to open.
    *
    * @param accessor The DataAccessable object to use.
    *
    * @param custom   Provider-specific custom object containing 
    *                 any needed parameters to open the access.
    *
    * @exception IOException If the access cannot be opened.
    *
    * @exception NoSubordinateAccessException If the accessor does not
    *            support opening a subordinate access, for example, a file.
    *
    * @exception RandomAccessNotSupportedException If the accessor does not
    *            support random access.
    *
    * @exception IllegalArgumentException If the custom object is not of
    *            the expected type for the given provider.
    */
    
   public RandomAccess(
      String name,
      DataAccessable accessor, 
      Object custom)
      throws IOException
   {
      this.accessor = accessor.openStream(name, DataAccessor.RANDOM, custom);
   }

   /**
    * Creates an instance of RandomAccess with default parameters.
    *
    * <p>This constructor should not be used by the user application layer
    * but is for SPI provider developers only.
    */

   protected RandomAccess()
   {
   }

/* **************************************************************************
   Accessor methods
****************************************************************************/

   /**
    * Reads a byte of data from this accessor source. This method
    * could block if no input is yet available. 
    *
    * @return The next byte of data, or -1 if the end of the data
    *          is reached.
    *
    * @exception IOException If an I/O error occurs.
    */
   public int read() throws IOException
   {
      int value = accessor.read();
      if (value >= 0)
         position++;
      return value;
   }


   /**
    * Reads up to len bytes of data from this accessor source into an 
    * array of bytes. This method could block until at least one byte
    * of input is available. 
    *
    * @param b   The buffer into which the data is read.
    * @param off The start offset of the data to read.
    * @param len The maximum number of bytes read.
    *
    * @return The total number of bytes read into the buffer, or 
    *         -1 if there is no more data because the end of the
    *         source has been reached.
    *
    * @exception IOException If an I/O error occurs.
    */
   public int read(byte b[], int off, int len) throws IOException
   {
      int value = accessor.read(b, off, len);
      if (value >= 0)
         position += value;
      return value;
   }


   /**
    * Reads up to b.length bytes of data from this accessor source 
    * into an array of bytes. This method could block until at least
    * one byte of input is available. 
    *
    * @param  b The buffer into which the data is read.
    *
    * @return The total number of bytes read into the buffer, or
    *         -1 if there is no more data because the end of this
    *         source has been reached.
    *
    * @exception  IOException If an I/O error occurs.
    */
   public int read(byte b[]) throws IOException
   {
      return this.read(b, 0, b.length);
   }


   /**
    * Reads b.length bytes from this source into the byte array. 
    * This method reads repeatedly from the source until all the 
    * bytes are read. This method could block until all the bytes
    * are read, the end of the stream is detected, or an exception
    * is thrown. 
    *
    * @param b The buffer into which the data is read.
    *
    * @exception  EOFException If this source reaches the end before
    *                           reading all the bytes.
    * @exception  IOException  If an I/O error occurs.
    */
   public void readFully(byte b[]) throws IOException
   {
      readFully(b, 0, b.length);
   }

   /**
    * Reads exactly len bytes from this source into the byte array. 
    * This method reads repeatedly from the source until all the 
    * bytes are read. This method could block until all the bytes are 
    * read, the end of the stream is detected, or an exception is thrown. 
    *
    * @param b   The buffer into which the data is read.
    * @param off The start offset of the data.
    * @param len The number of bytes to read.
    *
    * @exception  EOFException This source reaches the end before reading
    *                          all the bytes.
    * @exception  IOException  If an I/O error occurs.
    */
   public void readFully(byte b[], int off, int len) throws IOException
   {
	   int n = 0;
	   while (n < len) 
	   {
	      int count = this.read(b, off + n, len - n);
	      if (count < 0)
   	      throw new EOFException();
	      n += count;
	   }
   }

   /**
    * Writes the specified byte to this source. 
    *
    * @param  b The byte to be written.
    *
    * @exception IOException If an I/O error occurs.
    */
   public void write(int b) throws IOException
   {
      accessor.write(b);
      position++;
   }

   /**
    * Writes b.length bytes to this source from the specified byte
    * array starting at offset off. 
    *
    * @param b The bytes of data to write.
    *
    * @exception IOException If an I/O error occurs.
    */
   public void write(byte b[]) throws IOException
   {
      this.write(b, 0, b.length);
   }

    /**
    * Writes len bytes to this source from the specified byte array 
    * starting at offset off. 
    *
    * @param b   The bytes of data to write.
    * @param off The start offset in the data.
    * @param len The number of bytes to write.
    *
    * @exception IOException If an I/O error occurs.
    */
   public void write(byte b[], int off, int len) throws IOException
   {
      accessor.write(b, off, len);
      position += len;
   }

   /**
    * Returns the current offset in bytes from the beginning of this
    * source at which the next read or write will occur. 
    *
    * @return The current offset in this source.
    *
    * @exception IOException If an I/O error occurs.
    */
   public long getPosition() throws IOException
   {
      return position;      
   }

   /**
    * Sets the offset from the beginning of this source at which the 
    * next read or write will occur. 
    *
    * @param pos The absolute offset position.
    *
    * @exception IOException If an I/O error occurs.
    */
   public void setPosition(long pos) throws IOException
   {
      accessor.seek(pos);
      position = pos;
   }


   /**
    * Closes this random access source and releases any system 
    * resources associated with it. 
    *
    * @exception IOException If an I/O error occurs.
    */
   public void close() throws IOException
   {
      accessor.close();
   }

   /**
    * Returns the length of this file.
    *
    * @return The length of this file.
    * @exception IOException If an I/O error occurs.
    */
   public long length() throws IOException
   {
      return accessor.length();
   }

   /**
    * Sets the file length.
    *
    * <p>If the file is smaller than the desired length, the file is
    * extended to the desired length. Extra space is filled using a byte
    * value of 0.
    *
    * <p>If the file is larger than the desired length, the file is
    * truncated at the desired length. If the current position before
    * truncating the file is non-existent after truncation, the current
    * position should be reset to a valid file position.</p>
    *
    * @param length The desired absolute length of the file.
    *
    * @exception IOException If an I/O error occurs.
    */
   public void setLength(long length) throws IOException
   {
      if (length >= 0)
         accessor.setLength(length);
   }


/* **************************************************************************
   DataInput Implementation
****************************************************************************/

   /**
    * Reads a boolean value from this file. A single byte is read from
    * the file. A value of 0 represents FALSE, and any other value
    * represents TRUE. This method blocks until the byte is read, the
    * end of the stream is detected, or an exception is thrown. 
    *
    * @return The boolean value read.
    *
    * @exception  EOFException If this file has reached the end.
    * @exception  IOException  If an I/O error occurs.
    */
   public final boolean readBoolean() throws IOException 
   {
   	int ch = this.read();
   	if (ch < 0)
   	    throw new EOFException();
   	return (ch != 0);
    }

   /**
    * Reads a signed 8-bit value from this file. If the byte read is 
    * <code>b</code>, where <code>0&nbsp;&lt;=&nbsp;b&nbsp;&lt;=&nbsp;255</code>, 
    * then the result is
    * <ul><code>
    *     (byte)(b)
    *</code></ul><p>
    * 
    * This method blocks until the byte is read, the end of the stream 
    * is detected, or an exception is thrown. 
    *
    * @return The next byte of this file as a signed 8-bit byte.
    *
    * @exception EOFException If this file has reached the end.
    * @exception IOException  If an I/O error occurs.
    */
   public final byte readByte() throws IOException 
   {
   	int ch = this.read();
   	if (ch < 0)
   	    throw new EOFException();
   	return (byte)(ch);
    }

   /**
    * Reads and returns an unsigned 8-bit byte from this file.
    * 
    * This method blocks until the byte is read, the end of the stream 
    * is detected, or an exception is thrown. 
    *
    * @return The next byte of this file, interpreted as an unsigned
    *         8-bit number.
    *
    * @exception EOFException If this file has reached the end.
    * @exception IOException  If an I/O error occurs.
    */
   public final int readUnsignedByte() throws IOException 
   {
   	int ch = this.read();
   	if (ch < 0)
   	    throw new EOFException();
   	return ch;
    }

   /**
    * Reads a signed 16-bit number in 2 bytes from this file. If the
    * two bytes read, in order, are b1 and b2, where each of the two
    * values is between 0 and 255, inclusive, then the result is equal to
    * <ul><code>
    *     (short)((b1 &lt;&lt; 8) | b2)
    * </code></ul><p>
    * 
    * This method blocks until the two bytes are read, the end of the 
    * stream is detected, or an exception is thrown. 
    *
    * @return The next two bytes of this file, interpreted as a signed
    *         16-bit number.
    *
    * @exception EOFException If this file reaches the end before reading
    *            the two bytes.
    * @exception IOException If an I/O error occurs.
    */
   public final short readShort() throws IOException 
   {
   	int ch1 = this.read();
   	int ch2 = this.read();
   	if ((ch1 | ch2) < 0)
   	     throw new EOFException();
   	return (short)((ch1 << 8) + (ch2 << 0));
    }

   /**
    * Reads an unsigned 16-bit number in two bytes from this file. If the
    * bytes read, in order, are b1 and b2, where 
    * <code>0&nbsp;&lt;=&nbsp;b1, b2&nbsp;&lt;=&nbsp;255</code>, 
    * then the result is equal to
    * <ul><code>
    *     (b1 &lt;&lt; 8) | b2
    * </code></ul><p>
    * 
    * This method blocks until the two bytes are read, the end of the 
    * stream is detected, or an exception is thrown. 
    *
    * @return The next two bytes of this file, interpreted as an unsigned
    *         16-bit integer.
    *
    * @exception EOFException If this file reaches the end before reading
    *               two bytes.
    * @exception IOException If an I/O error occurs.
    */
   public final int readUnsignedShort() throws IOException 
   {
   	int ch1 = this.read();
   	int ch2 = this.read();
   	if ((ch1 | ch2) < 0)
   	     throw new EOFException();
   	return (ch1 << 8) + (ch2 << 0);
    }

   /**
    * Reads a Unicode character in two bytes from this file. If the bytes
    * read, in order, are <code>b1</code> and <code>b2</code>, where 
    * <code>0&nbsp;&lt;=&nbsp;b1,&nbsp;b2&nbsp;&lt;=&nbsp;255</code>, 
    * then the result is equal to
    * <ul><code>
    *     (char)((b1 &lt;&lt; 8) | b2)
    * </code></ul><p>
    * 
    * This method blocks until the two bytes are read, the end of the 
    * stream is detected, or an exception is thrown. 
    *
    * @return The next two bytes of this file as a Unicode character.
    *                           
    * @exception EOFException If this file reaches the end before reading
    *                         two bytes.
    * @exception IOException  If an I/O error occurs.
    */
   public final char readChar() throws IOException 
   {
   	int ch1 = this.read();
   	int ch2 = this.read();
   	if ((ch1 | ch2) < 0)
   	     throw new EOFException();
   	return (char)((ch1 << 8) + (ch2 << 0));
    }

   /**
    * Reads a signed 32-bit integer in 4 bytes from this file. If the
    * bytes read, in order, are <code>b1</code>, <code>b2</code>,
    * <code>b3</code>, and <code>b4</code>, where 
    * <code>0&nbsp;&lt;=&nbsp;b1, b2, b3, b4&nbsp;&lt;=&nbsp;255</code>, 
    * then the result is equal to
    * <ul><code>
    *     (b1 &lt;&lt; 24) | (b2 &lt;&lt; 16) + (b3 &lt;&lt; 8) + b4
    * </code></ul><p>
    * 
    * This method blocks until the four bytes are read, the end of the 
    * stream is detected, or an exception is thrown. 
    *
    * @return The next four bytes of this file, interpreted as an int.
    *        
    * @exception EOFException If this file reaches the end before reading
    *                         four bytes.
    * @exception IOException  If an I/O error occurs.
    */
   public final int readInt() throws IOException 
   {
   	int ch1 = this.read();
   	int ch2 = this.read();
   	int ch3 = this.read();
   	int ch4 = this.read();
   	if ((ch1 | ch2 | ch3 | ch4) < 0)
   	     throw new EOFException();
   	return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4 << 0));
    }

   /**
    * Reads a signed 64-bit integer in 8 bytes from this file. If the
    * bytes read, in order, are <code>b1</code>, <code>b2</code>,
    * <code>b3</code>, <code>b4</code>, <code>b5</code>, <code>b6</code>, 
    * <code>b7</code>, and <code>b8,</code> where
    * <ul><code>
    *     0 &lt;= b1, b2, b3, b4, b5, b6, b7, b8 &lt;=255,
    * </code></ul><p>
    * 
    * then the result is equal to
    * <p><blockquote><pre>
    *     ((long)b1 &lt;&lt; 56) + ((long)b2 &lt;&lt; 48)
    *     + ((long)b3 &lt;&lt; 40) + ((long)b4 &lt;&lt; 32)
    *     + ((long)b5 &lt;&lt; 24) + ((long)b6 &lt;&lt; 16)
    *     + ((long)b7 &lt;&lt; 8) + b8
    * </pre></blockquote><p>
    * 
    * This method blocks until the eight bytes are read, the end of the 
    * stream is detected, or an exception is thrown. 
    *
    * @return The next eight bytes of this file, interpreted as a long.
    *
    * @exception EOFException If this file reaches the end before reading
    *                         eight bytes.
    * @exception IOException  If an I/O error occurs.
    */
   public final long readLong() throws IOException 
   {
   	return ((long)(readInt()) << 32) + (readInt() & 0xFFFFFFFFL);
   }

   /**
    * Reads a float from this file. This method reads an int value as if
    * by the readInt() method and then converts that int to a float
    * using the java.lang.Float.intBitsToFloat(int) method. 
    * 
    * <p>This method blocks until the four bytes are read, the end of the 
    * stream is detected, or an exception is thrown. 
    *
    * @return The next four bytes of this file, interpreted as a float.
    *
    * @exception EOFException If this file reaches the end before reading
    *                         four bytes.
    *
    * @exception IOException If an I/O error occurs.
    *
    * @see #readInt()
    */
   public final float readFloat() throws IOException 
   {
   	return Float.intBitsToFloat(readInt());
   }

   /**
    * Reads a double from this file. This method reads a long value as
    * if by the readLong() method and then converts that long to a
    * double using the java.lang.Double.longBitsToDouble(long) method.
    * 
    * <p>This method blocks until the eight bytes are read, the end of the 
    * stream is detected, or an exception is thrown. 
    *
    * @return The next eight bytes of this file, interpreted as a double.
    *
    * @exception EOFException If this source reaches the end before reading
    *                         eight bytes.
    * @exception IOException If an I/O error occurs.
    *
    * @see #readLong()
    */
   public final double readDouble() throws IOException 
   {
   	return Double.longBitsToDouble(readLong());
   }

   /**
    * Reads the next line of text from this file. This method 
    * successively reads bytes from the file until it reaches the end of 
    * a line of text. 
    * <p>
    * A line of text is terminated by a carriage-return character 
    * (<code>'&#92;r'</code>), a newline character (<code>'&#92;n'</code>), a 
    * carriage-return character immediately followed by a newline 
    * character, or the end of the input stream. The line-terminating 
    * character(s), if any, are included as part of the string returned. 
    * <p>
    * This method blocks until a newline character is read, a carriage 
    * return and the byte following it are read (to see if it is a 
    * newline), the end of the stream is detected, or an exception is thrown.
    *
    * @return                    the next line of text from this file.
    * @exception  IOException    if an I/O error occurs.
    */
   public final String readLine() throws IOException 
   {
      StringBuffer input = new StringBuffer();
   	int c;

   	while (((c = read()) != -1) && (c != '\n')) 
   	    input.append((char)c);
   	if ((c == -1) && (input.length() == 0)) 
   	    return null;
   	return input.toString();
    }

   /**
    * Reads in a string from this file. The string has been encoded 
    * using a modified UTF-8 format. 
    * <p>
    * The first two bytes are read as if by 
    * <code>readUnsignedShort</code>. This value gives the number of 
    * following bytes that are in the encoded string, not
    * the length of the resulting string. The following bytes are then 
    * interpreted as bytes encoding characters in the UTF-8 format 
    * and are converted into characters. 
    * <p>
    * This method blocks until all the bytes are read, the end of the 
    * stream is detected, or an exception is thrown. 
    *
    * @return                    a Unicode string.
    * @exception  EOFException   if this file reaches the end before
    *                            reading all the bytes.
    * @exception  IOException    if an I/O error occurs.
    * @exception  UTFDataFormatException  if the bytes do not represent 
    *                                     valid UTF-8 encoding of a Unicode 
    *                                     string.
    * @see com.novell.java.io.RandomAccess#readUnsignedShort()
    */
   public final String readUTF() throws IOException 
   {
   	return DataInputStream.readUTF(this);
   }

   /**
    * Skips exactly n bytes of input. 
    * 
    * <p>This method blocks until all the bytes are skipped, the end of 
    * the stream is detected, or an exception is thrown. 
    *
    * @param  n The number of bytes to be skipped.
    *
    * @return The number of bytes skipped, which is always n.
    *
    * @exception EOFException If this file reaches the end before skipping
    *                         all the bytes.
    * @exception IOException  If an I/O error occurs.
    */
   public int skipBytes(int n) throws IOException 
   {
      accessor.seek(getPosition() + n);
      position += n;
      return n;
   }

/* **************************************************************************
   DataOutput Implementation
****************************************************************************/

   /**
    * Writes a boolean to the file as a 1-byte value. The value TRUE is
    * written out as the value(byte)1; the value FALSE> is written out 
    * as the value (byte)0.
    *
    * @param v A boolean value to be written.
    *
    * @exception IOException If an I/O error occurs.
    */
   public final void writeBoolean(boolean v) throws IOException 
   {
   	write(v ? 1 : 0);
   }

   /**
    * Writes a byte to the file as a 1-byte value. 
    *
    * @param v A byte value to be written.
    *
    * @exception IOException If an I/O error occurs.
    */
   public final void writeByte(int v) throws IOException 
   {
      write(v);
   }

   /**
    * Writes a short to the file as two bytes, high byte first.
    *
    * @param v A short to be written.
    *
    * @exception IOException If an I/O error occurs.
    */
   public final void writeShort(int v) throws IOException 
   {
   	write((v >>> 8) & 0xFF);
   	write((v >>> 0) & 0xFF);
   }

   /**
    * Writes a char to the file as a 2-byte value, high byte first.
    *
    * @param v A char value to be written.
    *
    * @exception IOException If an I/O error occurs.
    */
   public final void writeChar(int v) throws IOException 
   {
   	write((v >>> 8) & 0xFF);
   	write((v >>> 0) & 0xFF);
   }

   /**
    * Writes the specified int to the file as four bytes, high byte first.
    *
    * @param v An int to be written.
    *
    * @exception IOException If an I/O error occurs.
    */
   public final void writeInt(int v) throws IOException 
   {
   	write((v >>> 24) & 0xFF);
   	write((v >>> 16) & 0xFF);
   	write((v >>>  8) & 0xFF);
   	write((v >>>  0) & 0xFF);
   }

   /**
    * Writes the specified long to the file as eight bytes, high byte first.
    *
    * @param v A long to be written.
    *
    * @exception IOException If an I/O error occurs.
    */
   public final void writeLong(long v) throws IOException 
   {
   	write((int)(v >>> 56) & 0xFF);
   	write((int)(v >>> 48) & 0xFF);
   	write((int)(v >>> 40) & 0xFF);
   	write((int)(v >>> 32) & 0xFF);
   	write((int)(v >>> 24) & 0xFF);
   	write((int)(v >>> 16) & 0xFF);
   	write((int)(v >>>  8) & 0xFF);
   	write((int)(v >>>  0) & 0xFF);
   }

   /**
    * Converts the specified float argument to an int using the 
    * java.lang.Float.floatToIntBits(float) method, and then writes
    * that int value to the file as a 4-byte quantity, high byte first. 
    *
    * @param v A float value to be written.
    *
    * @exception IOException If an I/O error occurs.
    */
   public final void writeFloat(float v) throws IOException 
   {
   	writeInt(Float.floatToIntBits(v));
   }

   /**
    * Converts the specified double argument to a long using the 
    * java.lang.Double.doubleToLongBits(double) method, and then writes
    * that long value to the file as an 8-byte quantity, high byte first. 
    *
    * @param v A double value to be written.
    *
    * @exception IOException If an I/O error occurs.
    */
   public final void writeDouble(double v) throws IOException 
   {
   	writeLong(Double.doubleToLongBits(v));
   }

   /**
    * Writes the specified string to the file as a sequence of bytes.
    * Each character in the string is written out, in sequence, by
    * discarding its high eight bits. 
    *
    * @param s A string of bytes to be written.
    *
    * @exception IOException If an I/O error occurs.
    */
   public final void writeBytes(String s) throws IOException 
   {
   	int len = s.length();
   	for (int i = 0 ; i < len ; i++) 
   	    write((byte)s.charAt(i));
   }

   /**
    * Writes the specified string to the file as a sequence of characters.
    * Each character is written to the data output stream as if by the 
    * java.io.RandomAccessFile.writeChar(int) method. 
    *
    * @param s A String value to be written.
    *
    * @exception IOException If an I/O error occurs.
    */
   public final void writeChars(String s) throws IOException 
   {
   	int len = s.length();
   	for (int i = 0 ; i < len ; i++) 
   	{
   	    int v = s.charAt(i);
   	    write((v >>> 8) & 0xFF);
   	    write((v >>> 0) & 0xFF);
   	}
   }

   /**
    * Writes the specified string to the file using UTF-8 encoding
    * in a machine-independent manner. 
    * 
    * <p>First, two bytes are written to the file as if by the 
    * writeShort() method giving the number of bytes to follow. 
    * This value is the number of bytes actually written out, 
    * not the length of the string. Following the length, each character 
    * of the string is output, in sequence, using the UTF-8 encoding 
    * for each character. 
    *
    * @param str A string to be written.
    *
    * @exception IOException If an I/O error occurs.
    */
   public final void writeUTF(String str) throws IOException 
   {
   	int strlen = str.length();
   	int utflen = 0;

   	for (int i = 0 ; i < strlen ; i++) 
   	{
         int c = str.charAt(i);
   	   if ((c >= 0x0001) && (c <= 0x007F)) 
            utflen++;
   	   else 
   	      if (c > 0x07FF) 
   		      utflen += 3;
   	      else 
         		utflen += 2;
   	}

      if (utflen > 65535)
   	   throw new UTFDataFormatException();		  

   	write((utflen >>> 8) & 0xFF);
   	write((utflen >>> 0) & 0xFF);
   	for (int i = 0 ; i < strlen ; i++) 
   	{
   	   int c = str.charAt(i);
   	   if ((c >= 0x0001) && (c <= 0x007F)) 
   	   {
   		   write(c);
   	   } else if (c > 0x07FF) 
   	   {
      		write(0xE0 | ((c >> 12) & 0x0F));
      		write(0x80 | ((c >>  6) & 0x3F));
      		write(0x80 | ((c >>  0) & 0x3F));
      		//written += 2;
   	   } else 
   	   {
      		write(0xC0 | ((c >>  6) & 0x1F));
      		write(0x80 | ((c >>  0) & 0x3F));
   	   }
   	}
   }

   /**
    * Sets the DataAccessor to be used by the methods in this class.
    *
    * <p>It is used by the sub-classes of NWInputStream to set the
    * DataAccessor to be used by the methods in this class. The
    * setDataAccessor() method should be used only by SPI developers.
    *
    * @param da The DataAccessor to be set.
    */

   protected void setDataAccessor(DataAccessor da)
   {
      accessor = da;
   }
}
