wasCSharpSQLite – Rev 1
?pathlinks?
/*
* Channel.java
*
* Copyright (c) 1997 Sun Microsystems, Inc.
*
* See the file "license.terms" for information on usage and
* redistribution of this file, and for a DISCLAIMER OF ALL
* WARRANTIES.
*
* Included in SQLite3 port to C# for use in testharness only; 2008 Noah B Hart
*
* RCS @(#) $Id: Channel.java,v 1.25 2003/03/08 03:42:43 mdejong Exp $
*/
using System;
using System.Text;
using System.IO;
namespace tcl.lang
{
/// <summary> The Channel class provides functionality that will
/// be needed for any type of Tcl channel. It performs
/// generic reads, writes, without specifying how a
/// given channel is actually created. Each new channel
/// type will need to extend the abstract Channel class
/// and override any methods it needs to provide a
/// specific implementation for.
/// </summary>
public abstract class Channel
{
private void InitBlock()
{
buffering = TclIO.BUFF_FULL;
inputTranslation = TclIO.TRANS_AUTO;
outputTranslation = TclIO.TRANS_PLATFORM;
}
/// <summary> This method should be overridden in the subclass to provide
/// a channel specific InputStream object.
/// </summary>
protected internal abstract Stream InputStream
{
get;
}
/// <summary> This method should be overridden in the subclass to provide
/// a channel specific OutputStream object.
/// </summary>
protected internal abstract Stream OutputStream
{
get;
}
/// <summary> Gets the chanName that is the key for the chanTable hashtable.</summary>
/// <returns> channelId
/// </returns>
/// <summary> Sets the chanName that is the key for the chanTable hashtable.</summary>
/// <param name="chan">the unique channelId
/// </param>
public string ChanName
{
get
{
return chanName;
}
set
{
chanName = value;
}
}
/// <summary> Return a string that describes the channel type.
///
/// This is the equivilent of the Tcl_ChannelTypE.typeName field.
/// </summary>
public abstract string ChanType
{
get;
}
/// <summary> Return number of references to this Channel.</summary>
public int RefCount
{
get
{
return refCount;
}
}
public bool ReadOnly
{
get
{
return ( ( mode & TclIO.RDONLY ) != 0 );
}
}
public bool WriteOnly
{
get
{
return ( ( mode & TclIO.WRONLY ) != 0 );
}
}
public bool ReadWrite
{
get
{
return ( ( mode & TclIO.RDWR ) != 0 );
}
}
/// <summary> Query blocking mode.</summary>
/// <summary> Set blocking mode.
///
/// </summary>
/// <param name="blocking">new blocking mode
/// </param>
public bool Blocking
{
get
{
return blocking;
}
set
{
blocking = value;
if ( input != null )
input.Blocking = blocking;
if ( output != null )
output.Blocking = blocking;
}
}
/// <summary> Query buffering mode.</summary>
/// <summary> Set buffering mode
///
/// </summary>
/// <param name="buffering">One of TclIO.BUFF_FULL, TclIO.BUFF_LINE,
/// or TclIO.BUFF_NONE
/// </param>
public int Buffering
{
get
{
return buffering;
}
set
{
if ( value < TclIO.BUFF_FULL || value > TclIO.BUFF_NONE )
throw new TclRuntimeError( "invalid buffering mode in Channel.setBuffering()" );
buffering = value;
if ( input != null )
input.Buffering = buffering;
if ( output != null )
output.Buffering = buffering;
}
}
/// <summary> Query buffer size</summary>
/// <summary> Tcl_SetChannelBufferSize -> setBufferSize
///
/// </summary>
/// <param name="size">new buffer size
/// </param>
public int BufferSize
{
get
{
return bufferSize;
}
set
{
// If the buffer size is smaller than 10 bytes or larger than 1 Meg
// do not accept the requested size and leave the current buffer size.
if ( ( value < 10 ) || ( value > ( 1024 * 1024 ) ) )
{
return;
}
bufferSize = value;
if ( input != null )
input.BufferSize = bufferSize;
if ( output != null )
output.BufferSize = bufferSize;
}
}
public int NumBufferedInputBytes
{
get
{
if ( input != null )
return input.NumBufferedBytes;
else
return 0;
}
}
public int NumBufferedOutputBytes
{
get
{
if ( output != null )
return output.NumBufferedBytes;
else
return 0;
}
}
/// <summary> Returns true if a background flush is waiting to happen.</summary>
public bool BgFlushScheduled
{
get
{
// FIXME: Need to query output here
return false;
}
}
/// <summary> Query encoding
///
/// </summary>
/// <returns> Name of Channel's Java encoding (null if no encoding)
/// </returns>
/// <summary> Set new Java encoding</summary>
internal System.Text.Encoding Encoding
{
get
{
return encoding;
}
set
{
encoding = value;
if ( (System.Object)encoding == null )
bytesPerChar = 1;
else
bytesPerChar = EncodingCmd.getBytesPerChar( encoding );
if ( input != null )
input.Encoding = encoding;
if ( output != null )
output.Encoding = encoding;
// FIXME: Pass bytesPerChar to input and output
}
}
/// <summary> Query input translation
/// Set new input translation</summary>
public int InputTranslation
{
get
{
return inputTranslation;
}
set
{
inputTranslation = value;
if ( input != null )
input.Translation = inputTranslation;
}
}
/// <summary> Query output translation
/// Set new output translation</summary>
public int OutputTranslation
{
get
{
return outputTranslation;
}
set
{
outputTranslation = value;
if ( output != null )
output.Translation = outputTranslation;
}
}
/// <summary> Query input eof character</summary>
/// <summary> Set new input eof character</summary>
internal char InputEofChar
{
get
{
return inputEofChar;
}
set
{
// Store as a byte, not a unicode character
inputEofChar = (char)( value & 0xFF );
if ( input != null )
input.EofChar = inputEofChar;
}
}
/// <summary> Query output eof character</summary>
/// <summary> Set new output eof character</summary>
internal char OutputEofChar
{
get
{
return outputEofChar;
}
set
{
// Store as a byte, not a unicode character
outputEofChar = (char)( value & 0xFF );
if ( output != null )
output.EofChar = outputEofChar;
}
}
/// <summary> The read, write, append and create flags are set here. The
/// variables used to set the flags are found in the class TclIO.
/// </summary>
protected internal int mode;
/// <summary> This is a unique name that sub-classes need to set. It is used
/// as the key in the hashtable of registered channels (in interp).
/// </summary>
private string chanName;
/// <summary> How many interpreters hold references to this IO channel?</summary>
protected internal int refCount = 0;
/// <summary> Tcl input and output objecs. These are like a mix between
/// a Java Stream and a Reader.
/// </summary>
protected internal TclInputStream input = null;
protected internal TclOutputStream output = null;
/// <summary> Set to false when channel is in non-blocking mode.</summary>
protected internal bool blocking = true;
/// <summary> Buffering (full,line, or none)</summary>
protected internal int buffering;
/// <summary> Buffer size, in bytes, allocated for channel to store input or output</summary>
protected internal int bufferSize = 4096;
/// <summary> Name of Java encoding for this Channel.
/// A null value means use no encoding (binary).
/// </summary>
// FIXME: Check to see if this field is updated after a call
// to "encoding system $enc" for new Channel objects!
protected internal System.Text.Encoding encoding;
protected internal int bytesPerChar;
/// <summary> Translation mode for end-of-line character</summary>
protected internal int inputTranslation;
protected internal int outputTranslation;
/// <summary> If nonzero, use this as a signal of EOF on input.</summary>
protected internal char inputEofChar = (char)( 0 );
/// <summary> If nonzero, append this to a writeable channel on close.</summary>
protected internal char outputEofChar = (char)( 0 );
internal Channel()
{
InitBlock();
Encoding = EncodingCmd.systemJavaEncoding;
}
/// <summary> Tcl_ReadChars -> read
///
/// Read data from the Channel into the given TclObject.
///
/// </summary>
/// <param name="interp"> is used for TclExceptions.
/// </param>
/// <param name="tobj"> the object data will be added to.
/// </param>
/// <param name="readType"> specifies if the read should read the entire
/// buffer (TclIO.READ_ALL), the next line
/// (TclIO.READ_LINE), of a specified number
/// of bytes (TclIO.READ_N_BYTES).
/// </param>
/// <param name="numBytes"> the number of bytes/chars to read. Used only
/// when the readType is TclIO.READ_N_BYTES.
/// </param>
/// <returns> the number of bytes read.
/// Returns -1 on EOF or on error.
/// </returns>
/// <exception cref=""> TclException is thrown if read occurs on WRONLY channel.
/// </exception>
/// <exception cref=""> IOException is thrown when an IO error occurs that was not
/// correctly tested for. Most cases should be caught.
/// </exception>
internal int read( Interp interp, TclObject tobj, int readType, int numBytes )
{
TclObject dataObj;
checkRead( interp );
initInput();
switch ( readType )
{
case TclIO.READ_ALL:
{
return input.doReadChars( tobj, -1 );
}
case TclIO.READ_LINE:
{
return input.getsObj( tobj );
}
case TclIO.READ_N_BYTES:
{
return input.doReadChars( tobj, numBytes );
}
default:
{
throw new TclRuntimeError( "Channel.read: Invalid read mode." );
}
}
}
/// <summary> Tcl_WriteObj -> write
///
/// Write data to the Channel
///
/// </summary>
/// <param name="interp">is used for TclExceptions.
/// </param>
/// <param name="outData">the TclObject that holds the data to write.
/// </param>
public virtual void write( Interp interp, TclObject outData )
{
checkWrite( interp );
initOutput();
// FIXME: Is it possible for a write to happen with a null output?
if ( output != null )
{
output.writeObj( outData );
}
}
/// <summary> Tcl_WriteChars -> write
///
/// Write string data to the Channel.
///
/// </summary>
/// <param name="interp">is used for TclExceptions.
/// </param>
/// <param name="outStr">the String object to write.
/// </param>
public void write( Interp interp, string outStr )
{
write( interp, TclString.newInstance( outStr ) );
}
/// <summary> Close the Channel. The channel is only closed, it is
/// the responsibility of the "closer" to remove the channel from
/// the channel table.
/// </summary>
internal virtual void close()
{
IOException ex = null;
if ( input != null )
{
try
{
input.close();
}
catch ( IOException e )
{
ex = e;
}
input = null;
}
if ( output != null )
{
try
{
output.close();
}
catch ( IOException e )
{
ex = e;
}
output = null;
}
if ( ex != null )
throw ex;
}
/// <summary> Flush the Channel.
///
/// </summary>
/// <exception cref=""> TclException is thrown when attempting to flush a
/// read only channel.
/// </exception>
/// <exception cref=""> IOEcception is thrown for all other flush errors.
/// </exception>
public void flush( Interp interp )
{
checkWrite( interp );
if ( output != null )
{
output.flush();
}
}
/// <summary> Move the current file pointer. If seek is not supported on the
/// given channel then -1 will be returned. A subclass should
/// override this method if it supports the seek operation.
///
/// </summary>
/// <param name="interp">currrent interpreter.
/// </param>
/// <param name="offset">The number of bytes to move the file pointer.
/// </param>
/// <param name="mode">where to begin incrementing the file pointer; beginning,
/// current, end.
/// </param>
public virtual void seek( Interp interp, long offset, int mode )
{
throw new TclPosixException( interp, TclPosixException.EINVAL, true, "error during seek on \"" + ChanName + "\"" );
}
/// <summary> Return the current file pointer. If tell is not supported on the
/// given channel then -1 will be returned. A subclass should override
/// this method if it supports the tell operation.
/// </summary>
public virtual long tell()
{
return (long)( -1 );
}
/// <summary> Setup the TclInputStream on the first call to read</summary>
protected internal void initInput()
{
if ( input != null )
return;
input = new TclInputStream( InputStream );
input.Encoding = encoding;
input.Translation = inputTranslation;
input.EofChar = inputEofChar;
input.Buffering = buffering;
input.BufferSize = bufferSize;
input.Blocking = blocking;
}
/// <summary> Setup the TclOutputStream on the first call to write</summary>
protected internal void initOutput()
{
if ( output != null )
return;
output = new TclOutputStream( OutputStream );
output.Encoding = encoding;
output.Translation = outputTranslation;
output.EofChar = outputEofChar;
output.Buffering = buffering;
output.BufferSize = bufferSize;
output.Blocking = blocking;
}
/// <summary> Returns true if the last read reached the EOF.</summary>
public bool eof()
{
if ( input != null )
return input.eof();
else
return false;
}
// Helper methods to check read/write permission and raise a
// TclException if reading is not allowed.
protected internal void checkRead( Interp interp )
{
if ( !ReadOnly && !ReadWrite )
{
throw new TclException( interp, "channel \"" + ChanName + "\" wasn't opened for reading" );
}
}
protected internal void checkWrite( Interp interp )
{
if ( !WriteOnly && !ReadWrite )
{
throw new TclException( interp, "channel \"" + ChanName + "\" wasn't opened for writing" );
}
}
/// <summary> Tcl_InputBlocked -> isBlocked
///
/// Returns true if input is blocked on this channel, false otherwise.
///
/// </summary>
public bool isBlocked( Interp interp )
{
checkRead( interp );
if ( input != null )
return input.Blocked;
else
return false;
}
/// <summary> Channel is in CRLF eol input translation mode and the last
/// byte seen was a CR.
/// </summary>
public bool inputSawCR()
{
if ( input != null )
return input.sawCR();
return false;
}
}
}