Variable-length UDT including nondefault input and output methods

/* Variable Length UDT example type: Record3
** Example of required and explicit method implementations.
**
**  The C language structure equivalent of this JUDT is:
**
** typedef struct
** {
**    mi_double_precision d;
**    mi_chara[4];
**    mi_integerb;
**    mi_realc;
**    mi_datee;
**    mi_smallintf;
**    mi_booleang[MAXBOOLS];
** } NewFixUDT;
**
**  Where the last boolean array can contain up to MAX values
**  but only valid values will be written to disk.
*/
// Put this in our test package,
//  could be anywhere but needs to match SQL definitons for UDRs.
package informix.testclasses.jlm.udt;
// get the usual suspect classes
import java.sql.*;
// get informix specific interfaces, etal.
import com.informix.jdbc.*;
// These are only needed for the non-default Input/Output
// functions, remove if you use defaults.
import informix.jvp.dbapplet.impl.IfmxTextInStream;
import informix.jvp.dbapplet.impl.IfmxTextOutStream;
/**************** Now here’s our UDT *************/
public class Record3 implements SQLData
{
   // to turn debug print lines on and off
   private static boolean classDebug = true;

   // define storage for Java members of UDT
   private double d_double;
   private String a_char;
   private int b_int;
   private float c_float;
   private java.sql.Date e_date;
   private short f_smint;
   // could use a Vector for booleans, but would then need Boolean
   // objects ...so I’ve left it as an exercise for the reader...
   private static final int MAXBOOLS = 20;
   private boolean g_boolvals[] = new boolean[MAXBOOLS];
   private int numbools = 0;
   // dummy constructor just so we can log instantiation
   public Record3()
   {
      super();
      if( classDebug )
         System.out.println( "Record3() " + super.toString() + " created" );
   }
   // dummy finalizer just so we can log our own destruction
   protected void finalize()
   {
      super.finalize();
      if( classDebug )
         System.out.println( "Record3() " + super.toString() + " deleted" );
   }
/*********** REQUIRED SQLData implementation: ***********/
   // needed for SQLData interface
   private String type;
   public String getSQLTypeName()
   {
       return type;
   }
   // Called to convert an SQL buffer TYPE to JAVA class.
   //  note: we need to use SQLInput as the argument type or this
   // method signature won’t resolve correctly.
   public void readSQL (SQLInput in, String typeName) throws
   SQLException
   {
      if( classDebug )
         System.out.println( "Record3.readSQL() entered" );
         // save the type name 
      type = typeName;
      // cast the _real_ type of Stream for IFMX extensions.
      IfmxUDTSQLInput stream = (IfmxUDTSQLInput) in;
      // trap exceptions; don’t really know how many bytes
      //  are in the input.
      try
      {
         d_double = stream.readDouble();
         a_char = stream.readString(4);
         b_int = stream.readInt();
         c_float = stream.readFloat();
         e_date = stream.readDate();
         f_smint = stream.readShort();
      // Read booleans until we get an exception:
      // converting a non-existant boolean will throw cookies.
      // but we can use available() to make sure there is more
      // to read...
         for( int count = 0; (stream.available() > 0) && (count
            < MAXBOOLS); ++count )
         {
            g_boolvals[count] = stream.readBoolean();
            ++numbools;
         }
      }
      catch (SQLException e)
      {
      // if we got something besides end of input rethrow, 
      //  otherwise just assume we’re done.
         if( e.getErrorCode() != IfxErrMsg.S_BADSQLDATA )
         {
            if( classDebug )
               System.out.println("Record3.readSQL() exception = " + 
                                  e.toString());
            throw e;
         }
      }
   }

// Called to convert JAVA class to SQL buffer TYPE.
// note: we need to use SQLOutput as the argument type or this
// method signature won’t resolve correctly.
        
   public void writeSQL( SQLOutput out ) throws SQLException
   {
      if( classDebug )
         System.out.println( "Record3.writeSQL() entered" );
   // cast up to _real_ type of Stream to use IFMX extensions.
      IfmxUDTSQLOutput stream = (IfmxUDTSQLOutput) out;
      stream.writeDouble(d_double);
      stream.writeString(a_char, 4);
      stream.writeInt(b_int);
      stream.writeFloat(c_float);
      stream.writeDate(e_date);
      stream.writeShort(f_smint);
      for( int i = 0; i < numbools; i++ )
         stream.writeBoolean(g_boolvals[i]);
   }
/*********** END SQLData implementation ***********/
/**** NON-DEFAULT implementation of Input and Output functions ****/
/* Remove all this if you only use the Defaults */
The following example illustrates the implementation of user-defined input and output functions that override the default I/O methods. If you use the default methods, you do not need to implement overriding methods like those that follow:
// Called as Input function to convert SQL lvarchar to JAVA class
public static Record3 fromString( String str )
{
   if( classDebug )
      System.out.println( "Record3.fromString(String) entered" );
   // Make a stream of the right kind.
   IfmxTextInStream stream = new IfmxTextInStream(str);
   // Make a new Java object of the right type.
   Record3 record = new Record3();
   // Just call readSQL ourselves.
   //  For a real implementation you would probably copy all the
   //   readXXX()’s and intersperse delimiting chars as needed...
   try
   {
      readSQL( stream, "Record3" );
   }
   catch (Exception e)
   {
      System.err.println(e.getMessage());
   }
   return record;
}

// Called as Output function; convert JAVA class to SQL lvarchar.
// note: could use toString() directly,
// except that the UDR method must be "static", and
// it needs to take a Record3 as an argument....

public static String makeString(Record3 x)
{
   if( classDebug )
      System.out.println( "Record3.makeString() entered" );
   return x.toString();
}
        
// Might as well implement the standard toString() as long as
//  we’re doing non-defaults. If a different method name is
//  used here, Object.toString() will be called when the class
//  gets printed out in debug lines....

public String toString()
{
// Need to use a StringBuffer because we can’t pass a
// reference to a String to be initialized.
// We could optimize by guessing at size of buffer, too.
// StringBuffer str = new StringBuffer();
// IfmxTextOutStream stream = new IfmxTextOutStream(str);
// Just call writeSQL.
// For a real implementation you would probably copy all the
// writeXXX()’s and intersperse delimiting chars as needed...
   try
   {
      writeSQL( stream );
   }
   catch (Exception e)
   {
      System.err.println(e.getMessage());
   // not sure if we need to clear out result string?
      str.setLength(0);
   }
   return str.toString();
}