Home | Previous Page | Next Page   Using Opaque User-Detfined Types > Overriding the Default I/O Methods >

An Example That Overrides the Default I/O Methods

The following example illustrates a Java UDT class with nondefault definitions. JavaType is the new Java UDT, and JavaBuffer is the buffer type for the SQL data being converted, as I/O Function Sets and Related Types shows. For a complete set of required and optional code, see Usage Example.

public class JavaType implements SQLData
{
// Java data Object declarations for this Class....
// non-default Data Input function
   public static JavaType JavaTypeInput( JavaBuffer in )
   {
      JavaType x = new JavaType(); // make a new object
      // convert JavaBuffer fields to Java data objects in 
      // this Class
      return( x );// return the new object
   }
   // non-default Data Output function
   public static JavaBuffer JavaTypeOutput( JavaType out )
   {
      JavaBuffer x = new JavaBuffer();
      // Do whatever it takes to translate object to output 
      // buffer format
   return x;     // return the initialized buffer
   }
   // required SQLData implementation
   private String type;
   public String getSQLTypeName()
   {
      return type;
   }
   public void readSQL ( SQLInput instream, String typeName )
      throws SQLException
   {
      type = typeName;
      // cast up to Informix specific stream type
      IfmxUDTSQLInput in = (IfmxUDTSQLInput) instream;
      // read stream fields into Java data objects in this Class
      return;
   }

   public void writeSQL( SQLOutput outstream ) throws SQLException
   {
   // cast up to Informix specific stream type
      IfmxUDTSQLOutput out = (IfmxUDTSQLOutput) outstream;
   // write object to output stream
      return;
   }
}

For an example of the SQL definitions required to use the explicit methods in the preceding code, see SQL Definitions for a Variable-Length UDT Example.

Usage Example

All Java UDT classes must implement the readSQL() and writeSQL() methods for the SQLData interface. The readSQL() method initializes a Java object using data from the database server in a C-language format. The writeSQL() method converts a Java object back to the representation of the database server. The readSQL() and writeSQL() methods receive a Stream argument that encapsulates the conversion methods for each built-in type that the database server uses, for example, int, float, decimal.

In the case of a fixed-length UDT, the readSQL() and writeSQL() methods know the order and number of fields they are to process. In the case of a variable-length UDT, the programmer must rely on the stream.available() method and/or the SQLException to find the end of the data as this example shows.

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();
}
SQL Definitions for a Variable-Length UDT Example

The SQL definitions for this example are:

-- VarLen UDT and support functions ----------------------------
create opaque type Record3 (internallength = variable,
   alignment = 8, maxlen = 2048, cannothash );
grant usage on type Record3 to public;
-- register JUDT implementation....
--  note package name needs to match class file package
execute procedure setUDTExtName("Record3",
   "informix.testclasses.jlm.udt.Record3");
-- Definitions for NON_DEFAULT Input/Output functions.
-- this overrides the defaults setup above
-- LVARCHAR INPUT
drop cast (Record3 as lvarchar);
create implicit cast (Record3 as lvarchar with record3_output);
create function record3_input (l lvarchar) returns Record3 
   external name
'informix.testclasses.jlm.udt.Record3.fromString(java.lang.String)'
   language java not varient;
grant execute on function record3_input to public;
-- CHAR INPUT
drop cast (Record3 as char(100));
create implicit cast (Record3 as char(100) with record3_rout);
create function record3_rin (c char(100)) returns Record3 
   external name
'informix.testclasses.jlm.udt.Record3.fromString(java.lang.String)'
   language java not varient;
grant execute on function record3_rin to public;
       
-- LVARCHAR OUTPUT
drop cast (lvarchar as Record3);
create explicit cast (lvarchar as Record3 with record3_input);
create function record3_output (c Record3) returns lvarchar 
   external name
'informix.testclasses.jlm.udt.Record3.makeString(informix.testclasses.jlm.udt.Record3)'
   language java not varient;
grant execute on function record3_output to public;
-- CHAR OUTPUT
drop cast (char(100) as Record3);
create explicit cast (char(100) as Record3 with record3_rin);
create function record3_rout (c Record3) returns varchar(100) external name
'informix.testclasses.jlm.udt.Record3.makeString(informix.testclasses.jlm.udt.Record3)'
language java not varient;
grant execute on function record3_rout to public;

-- END definitions for NON_DEFAULT Input/Output functions.
-- end VarLen UDT and support functions --------------------------
-- Example Usage ---
create table rec3tab (record_col Record3);
insert into rec3tab values ('665.999 JAVA 398 197.236 1952-04-10 47 f t t');
insert into rec3tab values ('667.000 Jive 983 791.632 2002-04-11 42 f f f f f');
select * from rec3tab;
Home | [ Top of Page | Previous Page | Next Page | Contents | Index ]