import java.util.*;
import java.sql.*;   


/***
 * A ResultSet capable of being serialized.  This is useful for using
 * port 80 with a servlet instead of 1433, since it may be blocked by 
 * firewalls, but we know 80 is open.
 * Not all of the ResultSet is properly implemented.  I only implemented the immediate
 * methods that I needed, mostly String and Int methods.
 * I had to add a few extra methods ResultSet interface.
 * The most important are:
 * setCursor() - to move to an arbitrary record, and 
 * previous() - move back a record.
 *
 * this class reads a ResultSet and stores the values in vectors that can 
 * be serialized over the network.
 *
 * The Column Names are stored in a vector, ColumnNamesVector.
 *
 * @author mitch fincher
 * @see SerializableResultSetMetaData, ResultSet 
**/

////////////////////////////////////////////////////////////////
public class SerializableResultSet implements ResultSet, java.io.Serializable
////////////////////////////////////////////////////////////////
{
  Vector vector = new Vector(); // a vector containing the row vectors
  Vector ColumnNamesVector = null;
  Vector ColumnTypesVector = null;
  int NumberOfColumns = -1;
  int CursorLocation = -1;//index into Vector vector, 0 based.
  int NumberOfRows = 0;
  Vector CurrentVector = null;
  String ColumnTypeNames[];

/***
 * 
 * @param result a ResultSet from a jdbc query usually done in a servlet.
**/
////////////////////////////////////////////////////////////////
public  SerializableResultSet(ResultSet result) 
////////////////////////////////////////////////////////////////
{
  ResultSetMetaData rsmd = null;
  try {
    if(result != null)
      {
	rsmd = result.getMetaData();
	if(rsmd == null)
	  {
	    throw (new Exception("SerializableResultSet: ResultSetMetaData is null"));
	  }
	NumberOfColumns  = rsmd.getColumnCount();
      }
    else // incoming result set is null
      {
	NumberOfColumns = 0;
      }
    ColumnTypeNames = new String[NumberOfColumns+1];
    ColumnNamesVector = new Vector(NumberOfColumns);
    ColumnTypesVector = new Vector(NumberOfColumns);
    // get all the column info and create row vector
    for(int i = 1; i<=NumberOfColumns; i++)
      {
	ColumnNamesVector.addElement(rsmd.getColumnName(i));
	ColumnTypesVector.addElement(new Integer(rsmd.getColumnType(i)));
	ColumnTypeNames[i] = rsmd.getColumnTypeName(i);
      }
    // loop thru rows
    Vector rowVector=null;
    while( (result != null) && result.next())
      {
	rowVector = new Vector(NumberOfColumns);// create the row vector
	vector.addElement(rowVector); // add to master vector
	for(int i = 1; i<=NumberOfColumns; i++)
	  {
	    rowVector.addElement(result.getObject(i));
	  }
	NumberOfRows++;
      }
    if(result != null)result.close();
  }
  catch(Exception ex)
    { System.out.println("SerializableResultSet.constructor():" + ex);}
}

/***
 * This constructor is used only by executeStoredProcedure to fake 
 * an integer return.
 * @param ColumnName
 * @param Value the integer value for this column
**/
////////////////////////////////////////////////////////////////
public  SerializableResultSet(String ColumnName, int Value)
////////////////////////////////////////////////////////////////
{
  try {
    NumberOfColumns  = 1;
    ColumnTypeNames = new String[NumberOfColumns+1];
    // get all the column info and create row vector

    ColumnNamesVector.addElement(ColumnName);
    ColumnTypesVector.addElement(new Integer(4)); //mdf needs fixin'
    ColumnTypeNames[1] = "integer";

    Vector rowVector=null;
    rowVector = new Vector(NumberOfColumns);// create the row vector
    vector.addElement(rowVector); // add to master vector
    rowVector.addElement(new Integer(Value));
  }
  catch(Exception ex)
    { System.out.println("SerializableResultSet.constructor(String ColumnName, int Value):" + ex);}
}

/***
 * This routine is much slower than getInt(int).
 * @param ColumnName
 * @return the int value in ColumnNumber
**/
////////////////////////////////////////////////////////////////
public int getInt(String ColumnName)
////////////////////////////////////////////////////////////////
{
  int index = ColumnNamesVector.indexOf((Object) ColumnName);
  if(index < 0) // its not there, go fish.
    {
      System.out.println("SerializableResultSet.getInt(String): " + ColumnName + " not found");System.out.flush();
      return(-1);
    }

  return(getInt(++index)); // convert index to '1' based instead of zero.
}

/***
 * @param ColumnNumber
 * @return the int value in ColumnNumber
**/
////////////////////////////////////////////////////////////////
public int getInt(int ColumnNumber)
////////////////////////////////////////////////////////////////
{

  if( (ColumnNumber < 1) || ( ColumnNumber > NumberOfColumns) )
    {
      System.out.println("SerializableResultSet.getInt: invalid column number:" + ColumnNumber);System.out.flush();
      return(-1);
    }

  ColumnNumber--; // convert from SQL to zero based vectors

  Object obj = CurrentVector.elementAt(ColumnNumber);
  if( ! (obj instanceof Integer))
    {
      System.err.println("SerializableResultSet.getInt(int) type mismatch for column:" + ColumnNumber + "  I expected an Integer.");
      return(-1);
    }
  return(((Integer)(obj)).intValue());
}

/***
 * 
 * @param ColumnName
 * @return the String value in ColumnNumber
**/
////////////////////////////////////////////////////////////////
public String getString(String ColumnName)
////////////////////////////////////////////////////////////////
{
  int index = ColumnNamesVector.indexOf((Object) ColumnName);
  if(index < 0) // its not there, go fish.
    {
      System.out.println("SerializableResultSet.getInt(String): " + ColumnName + " not found");System.out.flush();
      return("");
    }

  return(getString(++index)); // convert index to '1' based instead of zero.
}

/***
 * 
 * @param ColumnNumber
 * @return the String value in ColumnNumber
**/
////////////////////////////////////////////////////////////////
public String getString(int ColumnNumber)
////////////////////////////////////////////////////////////////
{

  if( (ColumnNumber < 1) || ( ColumnNumber > NumberOfColumns) )
    {
      System.out.println("SerializableResultSet.getInt: invalid column number:" + ColumnNumber);System.out.flush();
      return("");
    }
  ColumnNumber--; // convert from SQL to zero based vectors

  Object obj = CurrentVector.elementAt(ColumnNumber);
  if( ! (obj instanceof String))
    {
      System.err.println("SerializableResultSet.getString(int) type mismatch for column:" + (ColumnNumber+1) + "  I expected a String.");
      return("");
    }

  return((String)CurrentVector.elementAt(ColumnNumber));
}

/***
 * 
 * @param ColumnNumber the column number of the current row to get.
**/
////////////////////////////////////////////////////////////////
public java.lang.Object getObject(int ColumnNumber)
////////////////////////////////////////////////////////////////
{
  return(CurrentVector.elementAt(--ColumnNumber)); // convert from SQL to zero based vectors
}


/***
 * Places the Cursor at the specified record.
 * @param RowNumber to set the Cursor. 
**/
////////////////////////////////////////////////////////////////
public boolean setCursor(int RowNumber)
////////////////////////////////////////////////////////////////
{
  boolean value=false;

  if( (RowNumber >= 0) && (RowNumber < NumberOfRows))
    {
      CursorLocation = RowNumber;
      CurrentVector = (Vector)vector.elementAt(CursorLocation);
      value = true;
    }
  else
    {
      value = false;
    }
  return(value);
}

/***
 * Places the Cursor at the specified record.  If a record is not found 
 * the cursor is left at the end of the ResultSet.
 * @param ColumnNumber the 1 based column to query 
 * @param StringToFind the String inside the ColumnNumber (case insensitive).
**/
////////////////////////////////////////////////////////////////
public boolean setCursor(int ColumnNumber, String StringToFind)
////////////////////////////////////////////////////////////////
{
  String temp="";
  setCursor(0);

  for(int i=0;i<NumberOfRows;i++)
    {
      setCursor(i);
      temp = getString(ColumnNumber);
      if(temp.equalsIgnoreCase(StringToFind))
	{
	  return(true);
	}
    }
  return(false);
}

/***
 * 
 * @return the current row location
**/
////////////////////////////////////////////////////////////////
public int getCursor()
////////////////////////////////////////////////////////////////
{
  return(CursorLocation);
}

/***
 * Advances the cursor one record.  If we have no more data, false is returned. 
**/
////////////////////////////////////////////////////////////////
public boolean next()
////////////////////////////////////////////////////////////////
{
  return(setCursor(getCursor()+1));
}

/***
 * Retreats the cursor one record.  If we have no more data, false is returned. 
**/
////////////////////////////////////////////////////////////////
public boolean previous()
////////////////////////////////////////////////////////////////
{
  return(setCursor(getCursor()-1));
}

/***
 * hmmm... this really hasn't any meaning in this context, 
 * but its in the interface
**/
////////////////////////////////////////////////////////////////
public void close()
////////////////////////////////////////////////////////////////
{
}

/***
**/
////////////////////////////////////////////////////////////////
public int getNumberOfRows()
////////////////////////////////////////////////////////////////
{
  return(NumberOfRows);
}

/***
 * Sets the column names to new values.
 * @param NamesVector a vector of new names for the columns.
**/
////////////////////////////////////////////////////////////////
public void setColumnNamesVector(Vector NamesVector)
////////////////////////////////////////////////////////////////
{
if(ColumnNamesVector.size() >= NamesVector.size())
  { ColumnNamesVector = NamesVector; }
else
  {
    System.out.println("SerializableResultSet.setColumnNamesVector: New Names Vector is not long enough." );System.out.flush();
  }
}

/***
 * Shows the contents of the ResultSet.
**/
////////////////////////////////////////////////////////////////
public String toString()
////////////////////////////////////////////////////////////////
{
  String desc="";

  desc += "\n***********************************************";
  desc += "\n SerializableResultSet.toString()";
  desc += "\n***********************************************";
  desc += "\n NumberOfColumns = " + NumberOfColumns;
  for(int i = 0; i<NumberOfColumns; i++)
    {
        desc += "\n ColumnName(" + i + "):" + ColumnNamesVector.elementAt(i);
	desc += " type = " + ColumnTypesVector.elementAt(i);
    }

  Vector rowVector=null;
  desc += "\nLooping over rows:";
  for(int j = 0;j<vector.size();j++)
    {
      desc += "\n   row " + j;
      rowVector = (Vector)vector.elementAt(j);
      for(int k = 0;k< rowVector.size();k++)
	{
	  desc += "\n      "+ColumnNamesVector.elementAt(k)+":"+rowVector.elementAt(k);
	}
    }
  return(desc);
}


////////////////////////////////////////////////////////////////
//Dummy functions, just so we can use the ResultSet Interface
////////////////////////////////////////////////////////////////
public boolean wasNull() { return(false); }
public boolean getBoolean(int int1) { return(false); }
public byte getByte(int int1) { return(1); }
public short getShort(int int1) { return(1); }
public long getLong(int int1) { return((long)1.0); }
public float getFloat(int int1) { return((float)1.0); }
public double getDouble(int int1) { return(1.0); }
public java.math.BigDecimal getBigDecimal(int int1, int int2) { return(null); }
public byte getBytes(int int1)[] { return(new byte[0]); }
public java.sql.Date getDate(int int1) { return(null); }
public java.sql.Time getTime(int int1) { return(null); }
public java.sql.Timestamp getTimestamp(int int1) { return(null); }
public java.io.InputStream getAsciiStream(int int1) { return(null); }
public java.io.InputStream getUnicodeStream(int int1) { return(null); }
public java.io.InputStream getBinaryStream(int int1) { return(null); }
public boolean getBoolean(java.lang.String string1) { return(false); }
public byte getByte(java.lang.String string1) { return(1); }
public short getShort(java.lang.String string1) { return(1); }
public long getLong(java.lang.String string1) { return((long)1.0); }
public float getFloat(java.lang.String string1) { return((float)1.0); }
public double getDouble(java.lang.String string1) { return(1.0); }
public java.math.BigDecimal getBigDecimal(java.lang.String string1, int int1) {return(null); }
public byte getBytes(java.lang.String string1)[] { return(new byte[1]); }
public java.sql.Date getDate(java.lang.String string1) { return(null); }
public java.sql.Time getTime(java.lang.String string1) { return(null); }
public java.sql.Timestamp getTimestamp(java.lang.String string1) { return(null); }
public java.io.InputStream getAsciiStream(java.lang.String string1) { return(null); }
public java.io.InputStream getUnicodeStream(java.lang.String string1) {return(null); }
public java.io.InputStream getBinaryStream(java.lang.String string1) { return(null);}
public java.sql.SQLWarning getWarnings() { return(null); }
public void clearWarnings() { return; }
public java.lang.String getCursorName() { return(null); }
public java.lang.Object getObject(java.lang.String string1) { return(null); }
public int findColumn(java.lang.String string1) { return(0); }


////////////////////////////////////////////////////////////////
public java.sql.ResultSetMetaData getMetaData() 
////////////////////////////////////////////////////////////////
{
  SerializableResultSetMetaData srsmd = new SerializableResultSetMetaData(NumberOfColumns);

  try {
    for(int i=1;i<=NumberOfColumns;i++)
      {
	srsmd.setColumnTypeName(i,ColumnTypeNames[i]);
	srsmd.setColumnName(i,(String)ColumnNamesVector.elementAt(i-1));
      }
  }
  catch(Exception ex){ System.err.println("SerializableResultSet.getMetaData" + ex);}
  return((java.sql.ResultSetMetaData)srsmd); 
}


////////////////////////////////////////////////////////////////
public static void  main(String argv[])
////////////////////////////////////////////////////////////////
  {
    System.out.println("running SerializableResultSet.main() ");System.out.flush();
    SerializableResultSet app = new SerializableResultSet(null);
  }


} //SerializableResultSet
