package jacorb.orb;

/*
 *        JacORB - a free Java ORB
 *
 *   Copyright (C) 1997-98  Gerald Brose.
 *
 *   This library 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; either
 *   version 2 of the License, or (at your option) any later version.
 *
 *   This library is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *   Library General Public License for more details.
 *
 *   You should have received a copy of the GNU Library General Public
 *   License along with this library; if not, write to the Free
 *   Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

import java.io.*;
import java.lang.*;
import java.util.*;
import org.omg.CORBA.TCKind;
import org.omg.PortableServer.*;

/**
 * @author Gerald Brose, FU Berlin 1999
 * @version	$Id: CDROutputStream.java,v 1.16 2000/04/03 16:31:13 brose Exp $	
 * 
 * A stream for CDR marshalling.
 *
 */

public class CDROutputStream
    extends org.omg.CORBA.portable.OutputStream
{
    /** GIOP Message header version 1.0 Big Endian */
    private static final byte[] giopMessageHeader = { (byte)'G', (byte)'I',
                                                      (byte)'O', (byte)'P',
                                                      1, 0, 0 } ;
    private int index = 0;
    private int pos = 0;
    protected byte[] buffer;
    private int BUF_SIZE;
    private int NET_BUF_SIZE = 1024;
    private static final int MEM_BUF_SIZE = 64;
    private boolean closed = false;
    private boolean released = false;

    private BufferManager bufMgr;

    private int resize_factor = 1;
    private int encaps_start = -1;
    private Stack encaps_stack = new java.util.Stack();
    private Stack recursiveTCStack = new Stack();

    private final static String null_ior_str = 
	"IOR:00000000000000010000000000000000";
    private final static org.omg.IOP.IOR null_ior = 
	new org.omg.IOP.IOR("", new org.omg.IOP.TaggedProfile[0]);

    private ORB orb;
	
    /**
     * Associated connection. Can be null only if stream is used
     * for data encapsulation which should not use connection's
     * properties such as CodeSet.
     */
    protected Connection connection;

  /**
   * This stream contains the separated header
   */
  protected CDROutputStream header_stream = null;

    /** 
     * OutputStreams created using the empty constructor 
     * are used for in memory marshaling, but do not use the
     * ORB's output buffer manager
     */

    public CDROutputStream()
    {
	bufMgr = null;
	connection=null;
	buffer = new byte[MEM_BUF_SIZE];
    }

    /** 
     * OutputStreams created using this constructor 
     * are used also for in memory marshaling, but do use the
     * ORB's output buffer manager
     */

    public CDROutputStream(org.omg.CORBA.ORB orb)
    {
	this.orb = (jacorb.orb.ORB)orb;
	bufMgr = ((jacorb.orb.ORB)orb).getBufferManager();
	connection=null;
	buffer = bufMgr.getBuffer(MEM_BUF_SIZE);
    }
	
    /** 
     *  Class constructor using a default buffer size
     */

    public CDROutputStream(Connection c)
    {
	connection=c;
	orb = c.getORB();
	bufMgr = c.getBufferManager();
	buffer = bufMgr.getBuffer(NET_BUF_SIZE);
    }
    
    /** 
     *  Class constructor setting the buffer size for the message
     */
    public CDROutputStream( Connection c,byte [] buf )
    {
	connection=c;
	orb = c.getORB();
	bufMgr = c.getBufferManager();
	buffer = buf;
    }

    /**
     * Returns codeset OSF registry number used for this stream.
     */

    protected int codeSet(boolean wide)
    {
	if(connection==null)
	{
	    if(wide) throw new RuntimeException("Wide codeset can't be used without associated connection");
	    return CodeSet.ISO8859_1;			
	}
	return wide ? connection.TCSW : connection.TCS;
    }

    /**
     * Writes a GIOPMessageHeader of the required type to the beginning of
     * the buffer and sets the start position and index.
     */

    public void writeGIOPMsgHeader( byte messageType )
    {
	// Write the array header without
	System.arraycopy(giopMessageHeader,0,buffer,0,giopMessageHeader.length);
	buffer[7]=messageType;

	// Skip the header + leave 4 bytes for message size
	index=12;
	pos=12;
    }

    /**
     * insertMsgSize, messageSize and close have been moved here from
     * derived classes. 
     */

    public void insertMsgSize(int size)
    {
	buffer[8] = (byte)((size >>> 24) & 0xFF);
	buffer[9] = (byte)((size >>> 16) & 0xFF);
	buffer[10] = (byte)((size >>>  8) & 0xFF);
	buffer[11] = (byte)(size & 0xFF);
    }

    public void insertMsgSize()
    {
	insertMsgSize( pos-12 );
    }

    public void close() 
    {
	if( closed )
	    throw new java.lang.Error("Stream already closed!");
	
	if (header_stream != null)
	  header_stream.close();
	else 
	  //if ((header_stream == null) && (! header_valid))
	  insertMsgSize();

	closed = true;
    }

    public void release()
    {
	if( released )
	    throw new java.lang.Error("Stream already released!");
	if( bufMgr != null ){
	    bufMgr.returnBuffer( buffer );
	    
	    if (header_stream != null)
	      header_stream.release();
	}

	released = true;
    }

    /**
     * This version of check does both array length checking and
     * data type alignement. It is a convenience method.
     */

    private final void check(int i,int align)
    {
	check(i);
	int remainder = align - (index % align);
	if (remainder != align)
	{
	    index += remainder;
	    pos+=remainder;
	}
    }
	
    /** 
     * check whether the current buffer is big enough to receive
     * i more bytes. If it isn't, get a bigger buffer.
     */

    private final void check(int i)
    {
	//if( closed )
	//   throw new java.lang.Error("Trying to write to a closed stream!");

	if( pos + i + 2 > (buffer.length))
	{
	    byte [] new_buf;
	    if( bufMgr == null )
	    {
		int size = buffer.length;
		while( pos + i + 2 > size )
		    size = size<<1;
		new_buf = new byte[size];
		System.arraycopy(buffer,0,new_buf,0,pos);
	    }
	    else
	    {
		new_buf = bufMgr.getBuffer(pos+i+2);
		System.arraycopy(buffer,0,new_buf,0,pos);
		bufMgr.returnBuffer(buffer);
	    }
	    buffer = new_buf;
	}
    }

    private final static void _write4int(byte[] buf, int _pos, int value)
    {
	buf[_pos]   = (byte)((value >>> 24) & 0xFF);
	buf[_pos+1] = (byte)((value >>> 16) & 0xFF);
	buf[_pos+2] = (byte)((value >>>  8) & 0xFF);
	buf[_pos+3] = (byte) (value & 0xFF);
    }

    /** 
     *  Start a CDR encapsulation. All subsequent writes
     *  will place data in the encapsulation until
     *  endEncapsulation is called. This will write
     *  the size of the encapsulation. 
     */

    public final void beginEncapsulation()
    {
	

	// align to the next four byte boundary 
	// as a preparation for writing the size
	// integer (which we don't know before the
	// encapsulation is closed)
		
	check(8,4);

	// leave 4 bytes for the encaps. size that
	// is to be written later

	pos += 4;
	index += 4;

	/* Because encapsulations can be nested, we need to
	   remember the beginnning of the enclosing
	   encapsulation (or -1 if we are in the outermost encapsulation)
	   Also, remember the current index because we need to 
	   restore this when closing the encapsulation */

	encaps_stack.push(new EncapsInfo(index, encaps_start ));

	// the start of this encapsulation

	encaps_start = pos;

	/* set the index for alignment to 0, i.e. align relative to the
	   beginning of the encapsulation */
	resetIndex();
	
	// byte_order flag set to FALSE

	buffer[pos++] = 0;
	index++;

    }

    /**
     * Terminate the encapsulation by writing its length
     * to its beginning. 
     */

    public final void endEncapsulation()
	throws IOException
    {
	if( encaps_start == -1 )
	    throw new IOException("too many end-of-encapsulations");

	// determine the size of this encapsulation

	int encaps_size = pos - encaps_start;

	// insert the size integer into the appropriate place

	buffer[encaps_start -4 ]  = (byte)((encaps_size >>> 24) & 0xFF);
	buffer[encaps_start -3 ] = (byte)((encaps_size >>> 16) & 0xFF);
	buffer[encaps_start -2 ] = (byte)((encaps_size >>>  8) & 0xFF);
	buffer[encaps_start -1 ] = (byte)(encaps_size & 0xFF);

	/* restore index and encaps_start information*/

	EncapsInfo ei = (EncapsInfo)encaps_stack.pop();
	encaps_start = ei.start;
	index = ei.index + encaps_size;
    }

    public org.omg.CORBA.portable.InputStream create_input_stream()
    {
	byte [] buf = (byte [])buffer.clone();
	return new CDRInputStream( orb, buf );
    }

//      public final byte[] getBuffer(boolean exact_size)
//      {
//  	if( exact_size )
//  	    return getBuffer();
//  	else
//  	    return buffer;
//      }

//      public final byte[] getBuffer()
//      {

//  	// System.out.println("getBuffer, size" + (pos-12));
//  	// Connection.dumpBA( buffer );

//  	byte [] result = new byte[pos];
//  	System.arraycopy(buffer,0,result,0,result.length);
//  	return result;
//      }

  /**
   * WARNING: The header, if separate, might not
   * be valid yet, because, the message size
   * has not been set!
   */
  public byte[] getBufferCopy(){
    byte[] result = null;

    if (header_stream != null){
      result = new byte[pos + header_stream.pos];
      
      System.arraycopy(header_stream.buffer, 0, result, 0, header_stream.pos);
      System.arraycopy(buffer, 0, result, header_stream.pos, pos);
    }
    else{
      result = new byte[pos];
      System.arraycopy(buffer,0,result,0,result.length);
    }
    return result;
  }

  /**
   * WARNING: the buffer returned here might only be the data part,
   * without the GIOP header.
   */
  public byte[] getInternalBuffer(){
    return buffer;
  }

    public void resetIndex()
    {
	index = 0;
    }

    public int size()
    {
	return pos;
    }

  

    public final void write_any (org.omg.CORBA.Any value)
    {
	write_TypeCode( value.type());
	value.write_value(this);
    }

    public  final void write_boolean (boolean value)
    {
	check(1);

	if( value )
	    buffer[pos++] = 1;
	else
	    buffer[pos++] = 0;
	index++;
    }

    public  final void write_boolean_array(boolean[] value, int offset, int length)
    {
	if( value != null )
	{
	    check(length);

	    for( int i = offset; i < offset+length; i++ )
	    {
		if( value[i] )
		    buffer[pos++] = 1;
		else
		    buffer[pos++] = 0;
	    }
	    index += length;
	}
    }

    public final void write_char (char c)
    {
	check(3); 
	write_char(c,codeSet(false));
    }
	
    /**
     * Writes char according to specified encoding. Note that check is not
     * called so call it before using this method. Each character can take
     * up to 3 bytes.
     */
    private final void write_char (char c,int codeset)
    {
	//check(3);
	switch(codeset)
	{
	case 0:
	case CodeSet.ISO8859_1:
	    short x = (short)c;
	    if( x > 255 || x < 0 )
		throw new org.omg.CORBA.MARSHAL("char ("+x+") out of range for ISO8859_1");
	    index++; buffer[pos++] = (byte)c;
	    break;
	case CodeSet.UTF8:
	    if (c <= 0x007F) 
	    {
		buffer[pos++]=(byte)c; index++;	
	    } 
	    else if (c > 0x07FF) 
	    {
		buffer[pos++]=(byte)(0xE0 | ((c >> 12) & 0x0F));
		buffer[pos++]=(byte)(0x80 | ((c >>  6) & 0x3F));
		buffer[pos++]=(byte)(0x80 | ((c >>  0) & 0x3F));
		index+=3; 
	    } 
	    else 
	    {
		buffer[pos++]=(byte)(0xC0 | ((c >>  6) & 0x1F));
		buffer[pos++]=(byte)(0x80 | ((c >>  0) & 0x3F));
		index+=2; 
	    }
	    break;
	case CodeSet.UTF16:
	    buffer[pos++] = (byte)((c >>>  8) & 0xFF);
	    buffer[pos++] = (byte)(c & 0xFF);
	    index+=2; 
	    break;
	default: throw new Error("Bad codeset");
	}
    }

    public final void write_char_array(char[] value, int offset, int length)
    {
	if( value == null ) 
	    throw new org.omg.CORBA.MARSHAL("Null References");
	check(length*3);
	for( int i = offset; i < offset+length; i++) 
	    write_char(value[i],codeSet(false));
    }
	
    public  final void write_string (String s)
    {
	if (s==null) 
	    throw new org.omg.CORBA.MARSHAL("Null References");
		
	check(7 + s.length()*3,4);	// the worst case
	int size = s.length();
	int startPos = pos;			// store position for length indicator
	pos += 4; 
	index += 4;		// reserve for length indicator
		
	// write characters in current encoding, add null terminator
	int cs=codeSet(false);
	for(int i=0;i<size;i++) 
	    write_char(s.charAt(i),cs);
	write_char((char)0,cs);
			
	_write4int(buffer,startPos,pos-startPos-4); // write length indicator
	//Connection.dumpBA(buffer);
    }

	
    public  final void write_wchar (char c)
    {
	check(3); 
	write_char(c,codeSet(true));
    }

    public  final void write_wchar_array(char[] value, int offset, int length)
    {
	if( value == null ) 
	    throw new org.omg.CORBA.MARSHAL("Null References");

	check(length*3);

	for( int i = offset; i < offset+length; i++)
	    write_char(value[i],codeSet(true));
    }
	
    public final void write_wstring (String s)
    {
      
	if (s==null) 
	    throw new org.omg.CORBA.MARSHAL("Null References");
		
	check(7 + s.length()*3,4);	// the worst case

	int size=s.length();
	int startPos=pos;			// store position for length indicator
	pos += 4; 
	index += 4;		// reserve for length indicator
		
	// write characters in current wide encoding, add null terminator
	int cs=codeSet(true);
	for(int i=0;i<size;i++) 
	    write_char(s.charAt(i),cs);
	write_char((char)0,cs);
			
	boolean sizeInChars = connection!=null && connection.IIOPVersion.minor<2 && cs==CodeSet.UTF16;
	_write4int(buffer,startPos,pos-startPos-4 >> (sizeInChars?1:0)); // write length indicator
	//Connection.dumpBA(buffer);
    }

    public  final void write_double (double value)
    {
	write_longlong(Double.doubleToLongBits(value));
    }

    public  final void write_double_array(double[] value, int offset, int length)
    {
	/* align to 8 byte boundary */
		
	check(7 + length*8, 8);
	
	if( value != null )
	{
	    for( int i = offset; i < offset+length; i++ )
	    {
		long d = Double.doubleToLongBits(value[i]);
		buffer[pos]   = (byte)((d >>> 56) & 0xFF);
		buffer[pos+1] = (byte)((d >>> 48) & 0xFF);
		buffer[pos+2] = (byte)((d >>> 40) & 0xFF);
		buffer[pos+3] = (byte)((d >>> 32) & 0xFF);
		buffer[pos+4] = (byte)((d >>> 24) & 0xFF);
		buffer[pos+5] = (byte)((d >>> 16) & 0xFF);
		buffer[pos+6] = (byte)((d >>>  8) & 0xFF);
		buffer[pos+7] = (byte) (d & 0xFF);
		pos += 8;
	    }
	    index += 8*length;
	}
    }

    public  final void write_fixed(java.math.BigDecimal value) 
    {    
	String v = value.movePointRight(value.scale()).toString();
	byte [] representation;
	int b, c;

	if( (v.length() %2) == 0)
	{
	    representation = new byte[ v.length()/2 +1];
	    representation[0] = 0x00;
     
	    for( int i = 0; i < v.length(); i++ )
	    {
		c = Character.digit(v.charAt(i), 10);
		b = representation[(1 + i)/2] << 4;
		b |= c;
		representation[(1 + i)/2] = (byte)b;
	    }
	}
	else
	{
	    representation = new byte[ (v.length()+1) /2];
	    for( int i = 0; i < v.length(); i++ )
	    {
		c = Character.digit(v.charAt(i), 10);
		b = representation[i/2] << 4;
		b |= c;
		representation[i/2] = (byte)b;
	    }
	}
	b = representation[representation.length-1] << 4;

	representation[representation.length-1] = (byte)((value.signum() < 0 )? (b | 0xD) : (b | 0xC));
	//Connection.dumpBA( representation );

	check(representation.length);
	System.arraycopy(representation,0,buffer,pos,representation.length);
	index += representation.length;
	pos += representation.length;
	
    }

    public  final void write_float (float value)
    {
	write_long(Float.floatToIntBits(value));
    }

    public  final void write_float_array(float[] value, int offset, int length)
    {
	/* align to 4 byte boundary */
		
	check(3 + length*4,4);
	
	if( value != null )
	{
	    for( int i = offset; i < offset+length; i++ )
	    {
		_write4int(buffer,pos, Float.floatToIntBits( value[i] ));
		pos += 4;
	    }
	    index += 4*length;
	}
    }

    public  final void write_long (int value)
    {
	check(7,4);

	_write4int(buffer,pos,value);

	pos += 4; index += 4;
    }

    public final void write_long_array(int[] value, int offset, int length)
    {
	/* align to 4 byte boundary */

	check(3 + length*4,4);
	
	
	int remainder = 4 - (index % 4);
	if (remainder != 4)
	{
	    index += remainder;
	    pos+=remainder;
	}
	
	if( value != null )
	{
	    for( int i = offset; i < offset+length; i++ )
	    {
		_write4int(buffer,pos,value[i]);
		pos += 4;
	    }
	    index += 4*length;
	}
    }

    public  final void write_longlong (long value)
    {
	check(15,8);

	buffer[pos]   = (byte)((value >>> 56) & 0xFF);
	buffer[pos+1] = (byte)((value >>> 48) & 0xFF);
	buffer[pos+2] = (byte)((value >>> 40) & 0xFF);
	buffer[pos+3] = (byte)((value >>> 32) & 0xFF);
	buffer[pos+4] = (byte)((value >>> 24) & 0xFF);
	buffer[pos+5] = (byte)((value >>> 16) & 0xFF);
	buffer[pos+6] = (byte)((value >>>  8) & 0xFF);
	buffer[pos+7] = (byte)(value & 0xFF);

	index += 8;
	pos += 8;
    }

    public  final void write_longlong_array(long[] value, int offset, int length)
    {
	check(7 + length*8,8);

	if( value != null )
	{
	    for( int i = offset; i < offset+length; i++ )
	    {
		buffer[pos]   = (byte)((value[i] >>> 56) & 0xFF);
		buffer[pos+1] = (byte)((value[i] >>> 48) & 0xFF);
		buffer[pos+2] = (byte)((value[i] >>> 40) & 0xFF);
		buffer[pos+3] = (byte)((value[i] >>> 32) & 0xFF);
		buffer[pos+4] = (byte)((value[i] >>> 24) & 0xFF);
		buffer[pos+5] = (byte)((value[i] >>> 16) & 0xFF);
		buffer[pos+6] = (byte)((value[i] >>>  8) & 0xFF);
		buffer[pos+7] = (byte) (value[i] & 0xFF);
		pos += 8;
	    }
	    index += 8*length;
	}
    }

    public void write_Object (org.omg.CORBA.Object value)
    {
	if( value instanceof jacorb.orb.LocalityConstrainedObject )
	    throw new org.omg.CORBA.MARSHAL("Attempt to serialize a locality-constrained object.");
	if( value == null )
	{
	    org.omg.IOP.IORHelper.write(this, null_ior );
	}
	else
	{
	    org.omg.CORBA.portable.ObjectImpl obj = (org.omg.CORBA.portable.ObjectImpl)value;
	    org.omg.IOP.IORHelper.write(this,  ((jacorb.orb.Delegate)obj._get_delegate()).getIOR()  );
	}
    }

    ////////////////////////////////////////////// NEW!
    public void write_IOR (org.omg.IOP.IOR ior)
    {
	if( ior == null )
	    {
		org.omg.IOP.IORHelper.write(this, null_ior );
	}
	else
	    {
		org.omg.IOP.IORHelper.write(this, ior);
	    }
    }
    ////////////////////////////////////////////// NEW!    

    public  final void write_octet (byte value)
    {
	check(1);
	index++;
	buffer[pos++] = value;
    }

    public  final void write_octet_array(byte[] value, int offset, int length)
    {
	if( value != null )
	{
	    check(length);
	    System.arraycopy(value,offset,buffer,pos,length);
	    index += length;
	    pos += length;
	}
    }

    public  final void write_Principal(org.omg.CORBA.Principal value)
    {
	write_octet_array( value.name(), 0, value.name().length);
    }

    public  final void write_short(short value)
    {
	check(3,2);
	
	buffer[pos]   = (byte)((value >>>  8) & 0xFF);
	buffer[pos+1] = (byte)(value & 0xFF);
	index += 2; pos+=2;
    }

    public  final void write_short_array(short[] value, int offset, int length)
    {
	/* align to 2-byte boundary */

	check(2*length + 3);
	
	int remainder = 2 - (index % 2);
	if (remainder != 2)
	{
	    index += remainder;
	    pos+=remainder;
	}
	
	if( value != null )
	{
	    for( int i = offset; i < offset+length; i++ )
	    {
		buffer[pos]   = (byte)((value[i] >>>  8) & 0xFF);
		buffer[pos+1] = (byte)( value[i] & 0xFF);
		pos += 2;
	    }
	    index += 2*length;
	}
    }

    public  final void write_TypeCode(org.omg.CORBA.TypeCode value)
    {
	/* remember the buffer position this TC gets written at. This
	   is necessary if nested TCs are recursively defined by refererring
	   to this TC.
	*/

	int start_pos = pos;
	int _kind = ((jacorb.orb.TypeCode)value)._kind();
	int _mc; // member count
	write_long( _kind  );
	try
	{
	    switch( _kind ) 
	    {
	    case 0:
	    case 1:
	    case 2:
	    case 3:
	    case 4:
	    case 5:
	    case 6:
	    case 7:
	    case 8:
	    case 9:
	    case 10:
	    case 11:
	    case 12:
	    case 13:
		break;
	    case TCKind._tk_objref: 
		beginEncapsulation();
		write_string(value.id());
		write_string(value.name());
		endEncapsulation();
		break;
	    case TCKind._tk_struct: 
	    case TCKind._tk_except:
		recursiveTCStack.push( new Integer( start_pos ) );
		beginEncapsulation();
		write_string(value.id());
		write_string(value.name());
		_mc = value.member_count();
		write_long(_mc);
		for( int i = 0; i < _mc; i++)
		{
		    write_string( value.member_name(i) );
		    write_TypeCode( value.member_type(i));
		}
		endEncapsulation();
		recursiveTCStack.pop();
		break;
	    case TCKind._tk_enum:
		{
		    beginEncapsulation();
		    write_string(value.id());
		    write_string(value.name());
		    _mc = value.member_count();
		    write_long(_mc);
		    for( int i = 0; i < _mc; i++)
		    {
			write_string( value.member_name(i) );
		    }
		    endEncapsulation();
		    break;
		}
	    case TCKind._tk_union:
		{
		    recursiveTCStack.push( new Integer( start_pos ) );
		    beginEncapsulation();
		    write_string(value.id());
		    write_string(value.name());

		    write_TypeCode( value.discriminator_type());
		    write_long( value.default_index());
		    _mc = value.member_count();
		    write_long(_mc);
		    for( int i = 0; i < _mc; i++)	
		    {
			if( i == value.default_index() )
			{
			    write_octet((byte)0);
			} 
			else
			{
			    value.member_label(i).write_value( this );
			}
			write_string( value.member_name(i));
			write_TypeCode( value.member_type(i));
		    }
		    endEncapsulation();
		    recursiveTCStack.pop();
		    break;
		}
	    case TCKind._tk_string: 
		write_long(value.length());
		break;
	    case TCKind._tk_array: 
	    case TCKind._tk_sequence: 
		beginEncapsulation();
		write_TypeCode( value.content_type());
		write_long(value.length());
		endEncapsulation();
		break;
	    case TCKind._tk_alias: 
		beginEncapsulation();
		write_string(value.id());
		write_TypeCode( value.content_type());
		endEncapsulation();
		break;
	    case 0xffffffff:
		Integer enclosing_tc_pos = (Integer)recursiveTCStack.peek();
		write_long( enclosing_tc_pos.intValue() - pos );
		break;
	    default: break;
	    }

	}
	catch (org.omg.CORBA.TypeCodePackage.BadKind bk)
	{ 
	    throw new RuntimeException("org.omg.CORBA.TypeCodePackage.BadKind");
	}
	catch (org.omg.CORBA.TypeCodePackage.Bounds bds)
	{ 
	    throw new RuntimeException("org.omg.CORBA.TypeCodePackage.Bounds");
	}
	catch (java.io.IOException ioe)
	{
	}
    }

    public  final void write_ulong (int value)
    {
	write_long(value);
    }

    public  final void write_ulong_array(int[] value, int offset, int length)
    {
	write_long_array(value, offset, length);
    }

    public  final void write_ulonglong (long value)
    {
	write_longlong(value);
    }

    public  final void write_ulonglong_array(long[] value, int offset, int length)
    {
	write_longlong_array(value, offset, length);
    }

    public  final void write_ushort (short value)
    {
	write_short(value);
    }

    public  final void write_ushort_array(short[] value, int offset, int length)
    {
	write_short_array(value, offset, length);
    }

    // old versions:

    //      public  final void write_wchar (char value)
    //      {
    //  	write_short((short)value);
    //      }

    //      public  final void write_wchar_array(char[] value, int offset, int length)
    //      {
    //  	write_char_array( value, offset, length);
    //      }

    //      public  final void write_wstring (String s)
    //      {
    //  	if (s==null) 
    //  	    throw new org.omg.CORBA.MARSHAL("Null References");
    //  	else 
    //  	{
    //  	    int size = 2*(s.getBytes().length + 1);
    //  	    check(7 + size);

    //  	    int remainder = 4 - (index % 4);
    //  	    if (remainder != 4)
    //  	    {
    //  		index += remainder;
    //  		pos+=remainder;
    //  	    }

    //  	    _write4int(buffer,pos,size);
    //  	    pos += 4;
    //  	    index += 4;

    //  	    System.arraycopy( s.getBytes(), 0, buffer, pos, (size-2) );
    //  	    pos += size;
    //  	    buffer[pos-1]=0;
    //  	    index += size;
    //  	    //Connection.dumpBA(buffer);
    //  	}
    //      }

    /** to be called from Any */

    public final void write_value( org.omg.CORBA.TypeCode tc, CDRInputStream in)
    {
	int kind = ((jacorb.orb.TypeCode)tc)._kind();
	switch (kind)
	{
	case TCKind._tk_null: 
	case TCKind._tk_void:
	    break;
	case TCKind._tk_boolean:
	    write_boolean( in.read_boolean());
	    break;
	case TCKind._tk_char:
	    write_char( in.read_char());
	    break;
	case TCKind._tk_octet:
	    write_octet( in.read_octet());
	    break;	    
	case TCKind._tk_short:
	    write_ushort( in.read_ushort());
	    break;
	case TCKind._tk_ushort:
	    write_short(in.read_short());
	    break;
	case TCKind._tk_long:
	    {
		write_long( in.read_long());
		break;
	    }
	case TCKind._tk_ulong:
	    write_ulong( in.read_ulong());
	    break;
	case TCKind._tk_float:
	    write_float( in.read_float());
	    break;
	case TCKind._tk_double:
	    write_double(in.read_double());
	    break;
	case TCKind._tk_longlong:
	    write_longlong(in.read_longlong());
	    break;
	case TCKind._tk_ulonglong:
	    write_ulonglong( in.read_ulonglong());
	    break;
	case TCKind._tk_any:
	    write_any( in.read_any());
	    break;
	case TCKind._tk_TypeCode:
	    write_TypeCode(in.read_TypeCode());
	    break;
	case TCKind._tk_Principal:
	    write_Principal( in.read_Principal());
	    break;
	case TCKind._tk_objref: 
	    write_Object( in.read_Object());
	    break;
	case TCKind._tk_string: 
	    write_string( in.read_string());
	    break;
	case TCKind._tk_array: 
	    try
	    {
		int length = tc.length();
		for( int i = 0; i < length; i++ )
		    write_value( tc.content_type(), in);
	    } catch ( org.omg.CORBA.TypeCodePackage.BadKind b ){} 
	    break;
	case TCKind._tk_sequence: 
	    try
	    {
		int len = in.read_long();
		write_long(len);
		for( int i = 0; i < len; i++ )
		    write_value( tc.content_type(), in);
	    } 
	    catch ( org.omg.CORBA.TypeCodePackage.BadKind b ){} 
	    break;
	case TCKind._tk_except:
	    write_string( in.read_string());
	    // don't break, fall through to ...
	case TCKind._tk_struct: 
	    recursiveTCStack.push(tc);
	    try
	    {
		for( int i = 0; i < tc.member_count(); i++)
		    write_value( tc.member_type(i), in);
	    } 
	    catch ( org.omg.CORBA.TypeCodePackage.BadKind b ){} 
	    catch ( org.omg.CORBA.TypeCodePackage.Bounds b ){}
	    recursiveTCStack.pop();
	    break;
	case TCKind._tk_enum:
	    write_long( in.read_long() );
	    break;
	case TCKind._tk_union:
	    try
	    {	    
		recursiveTCStack.push(tc);
		jacorb.orb.TypeCode disc = (jacorb.orb.TypeCode)tc.discriminator_type();
		int def_idx = tc.default_index();
		int member_idx = -1;

		switch( disc._kind() )
		{
		case TCKind._tk_short:
		    {
			short s = in.read_short();
			write_short(s);
			for(int i = 0 ; i < tc.member_count() ; i++)
			{
			    if(i != def_idx)
			    {
				if(s == tc.member_label(i).extract_short())
				{
				    member_idx = i;
				    break;
				}
			    }		
			}
			break;
		    }
		case TCKind._tk_ushort:
		    {
			short s = in.read_ushort();
			write_ushort(s);
			for(int i = 0 ; i < tc.member_count() ; i++)
			{
			    if(i != def_idx)
			    {
				if(s == tc.member_label(i).extract_ushort())
				{
				    member_idx = i;
				    break;
				}
			    }		
			}
			break;
		    }
		case TCKind._tk_long:
		    {
			int s = in.read_long();
			write_long(s);
			for(int i = 0 ; i < tc.member_count() ; i++)
			{
			    if(i != def_idx)
			    {
				if(s == tc.member_label(i).extract_long())
				{
				    member_idx = i;
				    break;
				}
			    }
			}
			break;
		    }
		case TCKind._tk_ulong:
		    {
			int s = in.read_ulong();
			write_ulong(s);
			for(int i = 0 ; i < tc.member_count() ; i++)
			{
			    if(i != def_idx)
			    {
				if(s == tc.member_label(i).extract_ulong())
				{
				    member_idx = i;
				    break;
				}
			    }
			}
			break;
		    }
		case TCKind._tk_longlong:
		    {
			long s = in.read_longlong();
			write_longlong(s);
			for(int i = 0 ; i < tc.member_count() ; i++)
			{
			    if(i != def_idx)
			    {
				if(s == tc.member_label(i).extract_longlong())
				{
				    member_idx = i;
				    break;
				}
			    }
			}
			break;
		    }
		case TCKind._tk_ulonglong:
		    {
			long s = in.read_ulonglong();
			write_ulonglong(s);
			for(int i = 0 ; i < tc.member_count() ; i++)
			{
			    if(i != def_idx)
			    {
				if(s == tc.member_label(i).extract_ulonglong())
				{
				    member_idx = i;
				    break;
				}
			    }
			}
			break;
		    }
		case TCKind._tk_boolean:
		    {
			boolean s = in.read_boolean();
			write_boolean(s);
			for(int i = 0 ; i < tc.member_count() ; i++)
			{
			    if(i != def_idx)
			    {
				if(s == tc.member_label(i).extract_boolean())
				{
				    member_idx = i;
				    break;
				}
			    }		
			}
			break;
		    }
		case TCKind._tk_enum:
		    {
			int s = in.read_long();
			write_long(s);
			for(int i = 0 ; i < tc.member_count() ; i++)
			{
			    if(i != def_idx)
			    {
				/* we have to use the any's input stream because enums are not 
				   inserted as longs */
				if(s == tc.member_label(i).create_input_stream().read_long())
				{
				    member_idx = i;
				    break;
				}
			    }		
			}
			break;
		    }
		case TCKind._tk_char:
		    {
			char s = in.read_char();
			write_char(s);
			for(int i = 0 ; i < tc.member_count() ; i++)
			{
			    if(i != def_idx)
			    {
				if(s == tc.member_label(i).extract_char())
				{
				    member_idx = i;
				    break;
				}
			    }		
			}
			break;
		    }		default:
			throw new RuntimeException("Unfinished implementation for unions in anys, sorry.");
		}
		if( member_idx != -1 )
		{
		    write_value( tc.member_type(member_idx), in);
		}
		else
		{
		    write_value( tc.member_type(def_idx),in);
		}
	    } 
	    catch ( org.omg.CORBA.TypeCodePackage.BadKind b ){} 
	    catch ( org.omg.CORBA.TypeCodePackage.Bounds b ){}
	    recursiveTCStack.pop();
	    break;
	case TCKind._tk_alias:
	    write_string( in.read_string());
	    write_string( in.read_string());
	    write_TypeCode( in.read_TypeCode());
	    break;
	case 0xffffffff:
	    write_value( (org.omg.CORBA.TypeCode)recursiveTCStack.peek(), in);
	    break;
	default:
	    throw new RuntimeException("Cannot handle TypeCode with kind " + kind);
	}
    }

  public void setSize(int s){
    pos = s;
  }

  public void setGIOPRequestId(int id){
    buffer[16]=(byte)((id >> 24) & 0xFF);
    buffer[17]=(byte)((id >> 16) & 0xFF);
    buffer[18]=(byte)((id >>  8) & 0xFF);
    buffer[19]=(byte)( id        & 0xFF);
  }

  public void setBuffer(byte[] b){
    if (bufMgr != null)
      bufMgr.returnBuffer(buffer);

    if ((header_stream != null) &&
      ! header_stream.released){
      header_stream.release();
      header_stream = null;
    }

    buffer = b;
    pos = buffer.length;
  }
  
  public void reset(){
    pos = 0;
    index = 0;
  }

  public void write_to(java.io.OutputStream out)
    throws java.io.IOException{
    if (header_stream != null){
      header_stream.insertMsgSize(header_stream.size() + size() - 12);
      out.write(header_stream.buffer, 0, header_stream.pos);
    }

    out.write(buffer, 0, pos);
    out.flush();
  }
  
  public boolean separateHeader(){
    return (header_stream != null);
  }

  public CDROutputStream getHeaderStream(){
    return header_stream;
  }
}



