www.pudn.com > town-1[1].0.4.rar > DataSet.java
package com.workingdogs.town;
import java.io.*;
import java.sql.*;
/*
Town, a Java JDBC abstraction layer
Copyright (C) 1999 Serge Knystautas, Jon Stevens
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., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.
*/
import java.sql.*;
import java.util.*;
/**
The DataSet represents a table in the database. It is extended by
QueryDataSet and
TableDataSet and should not be used directly.
A DataSet contains a Schema and potentially a
collection of Records.
@author Jon S. Stevens jon@working-dogs.com
@author Serge Knystautas sergek@lokitech.com
@version 1.0
*/
public abstract class DataSet extends DBConnection
{
/**
* Keep column names exactly as presented by the database
*/
public static final int PRESERVE_CASE = 0;
/**
* Force column names to be lowercase, regardless of what the database says
*/
public static final int FORCE_LOWER_CASE = 1;
/**
* Force column names to be uppercase, regardless of what the database says
*/
public static final int FORCE_UPPER_CASE = 2;
/**
* Turn special characters into entities
*/
public static final int CHARACTER_ENTITIES = 0;
/**
* Surround text with CDATA tags
*/
public static final int CHARACTER_CDATA = 1;
/**
* Preserve special characters as is
*/
public static final int CHARACTER_PRESERVE = 2;
/**
* The method used to encode special characters
*/
protected int character_mode = CHARACTER_ENTITIES;
/**
* Omit null values completely
*/
public static final int OMIT_NULLS = 0;
/**
* Turn nulls into empty strings
*/
public static final int EMPTY_NULLS = 1;
/**
* How should we handle null values
*/
protected int null_mode = OMIT_NULLS;
/** this DataSet's schema object */
protected Schema schema;
/** this DataSet's collection of Record objects */
protected Vector records = null;
/** have all records been retrieved with the fetchRecords? */
protected boolean allRecordsRetrieved = false;
/** number of records retrieved */
protected int recordRetrievedCount = 0;
/** number of records that were last fetched */
protected int lastFetchSize = 0;
/** number of records total that have been fetched */
protected int totalFetchCount = 0;
/** the result set for this DataSet */
protected ResultSet resultSet;
/**
* This method was created in VisualAge.
* @param connString java.lang.String
*/
protected DataSet(String driver,
String connString) throws DataSetException, ConnectionException
{
this (driver, connString, (String) null, (String) null);
}
/**
* Create a new DataSet with a connection, tablename and KeyDef
*
* @param conn
* @param tableName
* @param keydef
* @exception ConnectionException
* @exception DataSetException
*/
protected DataSet (String driver, String connString,
KeyDef keydef) throws ConnectionException, DataSetException
{
this (driver, connString, null, null, keydef);
}
/**
* This method was created in VisualAge.
* @param connString java.lang.String
*/
protected DataSet(String driver, String connString,
String username, String password) throws DataSetException,
ConnectionException
{
super (driver, connString, username, password);
}
/**
* Create a new DataSet with a connection, tablename and KeyDef
*
* @param driver
* @param connString
* @param username
* @param password
* @param tableName
* @param keydef
* @exception ConnectionException
* @exception DataSetException
*/
protected DataSet (String driver, String connString,
String username, String password,
KeyDef keydef) throws ConnectionException, DataSetException
{
super (driver, connString, username, password);
if (dbconn == null)
throw new ConnectionException ("Database connection could not be established!");
else if (keydef == null)
throw new DataSetException ("You need to specify a valid keydef!");
}
/**
* Adds a record to this data set
*
* @return the added record
* @exception DataSetException
* @exception ConnectionException
*/
public abstract Record addRecord () throws DataSetException,
ConnectionException;
/**
* Check if all the records have been retrieve
*
* @return true if all records have been retrieved
*/
public boolean allRecordsRetrieved()
{
return this.allRecordsRetrieved;
}
/**
* Remove all records from the DataSet and nulls those records out
* and close() the DataSet.
*
* @return an instance of myself
*/
public DataSet clearRecords()
{
this.records.removeAllElements();
this.records = null;
return this;
}
/**
* Releases the records, closes the ResultSet and the Statement and nulls the schema
*
* @exception DataSetException
* @exception ConnectionException
*/
public void close () throws DataSetException, ConnectionException
{
try
{
releaseRecords();
schema = null;
if (this.stmt != null)
this.stmt.close();
if (this.resultSet != null && ! (this instanceof QueryDataSet))
resultSet().close();
this.resultSet = null;
super.close ();
}
catch (SQLException sqle)
{
throw new ConnectionException (sqle);
}
}
/**
* Check to see if the DataSet contains a Record at 0 based position
*
* @param pos
* @return true if record exists
*/
public boolean containsRecord(int pos) throws DataSetException,
ConnectionException
{
if (this.records == null)
fetchRecords ();
try
{
if (this.records.elementAt(pos) != null)
return true;
}
catch (Exception e)
{
return false;
}
return false;
}
/**
* Causes the DataSet to hit the database and fetch all the records.
*
* @return an instance of myself
* @exception ConnectionException
* @exception DataSetException
*/
public DataSet fetchRecords() throws ConnectionException,
DataSetException
{
return fetchRecords (0, -1);
}
/**
* Causes the DataSet to hit the database and fetch max records.
*
* @param max
* @return an instance of myself
* @exception ConnectionException
* @exception DataSetException
*/
public DataSet fetchRecords(int max) throws ConnectionException,
DataSetException
{
return fetchRecords (0, max);
}
/**
* Causes the DataSet to hit the database and fetch start to max records. Start is at Record 0.
*
* @param start
* @param max
* @return an instance of myself
* @exception ConnectionException
* @exception DataSetException
*/
public DataSet fetchRecords(int start,
int max) throws ConnectionException, DataSetException
{
if (max == 0)
throw new DataSetException ("Max is 1 based and must be greater than 0!");
if (max != -1 && start > max)
throw new DataSetException ("Start cannot be larger than max!");
else if (lastFetchSize() > 0 && this.records != null)
throw new DataSetException ("You must call DataSet.clearRecords() before executing DataSet.fetchRecords() again!");
String selectString = getSelectString ();
/*
if (selectString == null)
{
selectString = new StringBuffer (256);
selectString.append ("SELECT ");
selectString.append (schema().attributes());
selectString.append (" FROM ");
selectString.append (schema().tableName());
}
*/
try
{
if (stmt == null)
{
stmt = connection().createStatement();
this.resultSet =
stmt.executeQuery (selectString.toString());
}
if (this.resultSet != null)
{
if (this.records == null && max > 0)
this.records = new Vector (max);
else
this.records = new Vector ();
int startCounter = 0;
int fetchCount = 0;
while (! allRecordsRetrieved())
{
if (fetchCount == max)
break;
if (startCounter >= start)
{
if (this.resultSet.next())
{
Record rec = new Record (this);
records.addElement (rec);
fetchCount++;
}
else
{
setAllRecordsRetrieved (true);
break;
}
}
else
{
startCounter++;
}
}
lastFetchSize = fetchCount;
}
}
catch (SQLException e)
{
if (stmt != null)
{
try
{
stmt.close();
}
catch (SQLException sqle)
{
throw new ConnectionException (sqle);
}
}
//throw new SQLException (e.getMessage());
throw new ConnectionException (e);
}
return this;
}
/**
* This method was created in VisualAge.
* @return java.lang.String[]
*/
public String [] getColumnNames ()
{
return getColumnNames (PRESERVE_CASE);
}
/**
* This method was created in VisualAge.
* @return java.lang.String[]
* @param caseMode int
*/
public String[] getColumnNames (int caseMode)
{
Column headers[] = schema.getColumns ();
int columns = headers.length;
String names[] = new String[columns];
if (caseMode == PRESERVE_CASE)
for (int i = 1; i < columns; i++)
names[i] = headers[i].name ();
else if (caseMode == FORCE_LOWER_CASE)
for (int i = 1; i < columns; i++)
names[i] = headers[i].name ().toLowerCase();
else if (caseMode == FORCE_LOWER_CASE)
for (int i = 1; i < columns; i++)
names[i] = headers[i].name ().toUpperCase();
return names;
}
/**
* Returns the KeyDef for the DataSet
*
* @return a keydef
*/
public abstract KeyDef getKeyDef() throws DataSetException;
/**
* Get Record at 0 based index position
*
* @param pos
* @return an instance of the found Record
* @exception DataSetException
* @exception ConnectionException
*/
public Record getRecord (int pos) throws DataSetException,
ConnectionException
{
if (containsRecord (pos))
{
Record rec = (Record) this.records.elementAt(pos);
if (this instanceof TableDataSet)
rec.markForUpdate();
recordRetrievedCount++;
return rec;
}
throw new DataSetException ("Record not found at index: " + pos);
}
/**
* Find Record at 0 based index position. This is an internal alternative
* to getRecord which tries to be smart about the type of record it is.
*
* @param pos
* @return an instance of the found Record
* @exception DataSetException
*/
Record findRecord (int pos) throws DataSetException, ConnectionException
{
if (containsRecord (pos))
{
return (Record) this.records.elementAt(pos);
}
throw new DataSetException ("Record not found at index: " + pos);
}
/**
* Returns this schema for this Data Set
* @return com.workingdogs.town.Schema
*/
public Schema getSchema ()
{
return schema;
}
/**
* Classes extending this class must implement this method.
*
* @return the select string
*/
protected abstract String getSelectString()
throws ConnectionException, DataSetException;
/**
Gets the tableName upon table data set creation
@return string
*/
public abstract String getTableName() throws DataSetException,
ConnectionException;
/**
The number of records that were fetched with the last fetchRecords.
@return int
*/
public int lastFetchSize()
{
return lastFetchSize;
}
/**
* Removes the records from the DataSet, but does not null the records out
*
* @return an instance of myself
*/
public void releaseRecords()
{
this.records = null;
this.recordRetrievedCount = 0;
this.lastFetchSize = 0;
setAllRecordsRetrieved (false);
}
/**
* Remove a record from the DataSet's internal storage
*
* @param rec
* @return the record removed
* @exception DataSetException
*/
public Record removeRecord (Record rec) throws DataSetException
{
Record removeRec = null;
try
{
int loc = this.records.indexOf(rec);
removeRec = (Record) this.records.elementAt (loc);
this.records.removeElementAt (loc);
}
catch (Exception e)
{
throw new DataSetException ("Record could not be removed!");
}
return removeRec;
}
/**
* Gets the ResultSet for this DataSet
*
* @return the result set for this DataSet
* @exception ConnectionException
* @exception DataSetException
*/
protected ResultSet resultSet() throws ConnectionException,
DataSetException
{
if (this.resultSet == null)
throw new DataSetException ("ResultSet is null.");
return this.resultSet;
}
/**
* Set all records retrieved
*
*/
protected void setAllRecordsRetrieved(boolean set)
{
this.allRecordsRetrieved = set;
}
/**
Gets the number of Records in this DataSet. It is 0 based.
@return number of Records in this DataSet
*/
public int size() throws ConnectionException, DataSetException
{
if (records == null)
fetchRecords ();
return this.records.size();
}
/**
* This method was created in VisualAge.
* @return java.lang.String
*/
public String toDTD ()
{
return schema.toDTD ();
}
/**
This returns a represention of this DataSet
*/
public String toString()
{
try
{
if (records == null)
fetchRecords ();
ByteArrayOutputStream bout = new ByteArrayOutputStream ();
PrintWriter out = new PrintWriter (bout);
if (schema != null)
out.println (schema.toString ());
for (int i = 0; i < size (); i++)
out.println (findRecord (i));
out.flush ();
return bout.toString ();
}
catch (IOException ioe)
{
ioe.printStackTrace ();
}
return "{}";
}
/**
* Produces a valid XML document based on this data
* @param String listName for the entire XML document
* @param String itemName for each XML item (per record)
* @author Donald Ball balld@webslingerZ.com
* @author Serge Knystautas sergek@lokitech.com
* @return java.lang.String
*/
public String toXML (String listName,
String itemName) throws ConnectionException, DataSetException
{
return toXML (listName, itemName, PRESERVE_CASE, CHARACTER_ENTITIES, OMIT_NULLS);
}
/**
* Produces a valid XML document based on this data
* @param String listName for the entire XML document
* @param String itemName for each XML item (per record)
* @param int caseMode The case mode to be work in
* @author Donald Ball balld@webslingerZ.com
* @author Serge Knystautas sergek@lokitech.com
* @return java.lang.String
*/
public String toXML (String listName, String itemName,
int caseMode) throws ConnectionException, DataSetException
{
return toXML (listName, itemName, caseMode, CHARACTER_ENTITIES, OMIT_NULLS);
}
/**
* Produces a valid XML document based on this data
* @param String listName for the entire XML document
* @param String itemName for each XML item (per record)
* @param int caseMode The case mode to be work in
* @param int characterMode How to encode special characters
* @author Donald Ball balld@webslingerZ.com
* @author Serge Knystautas sergek@lokitech.com
* @return java.lang.String
*/
public String toXML (String listName, String itemName,
int caseMode, int characterMode) throws ConnectionException, DataSetException
{
return toXML (listName, itemName, caseMode, characterMode, OMIT_NULLS);
}
/**
* Produces a valid XML document based on this data
* @param String listName for the entire XML document
* @param String itemName for each XML item (per record)
* @param int caseMode The case mode to be work in
* @param int characterMode How to encode special characters
* @param int nullMode How should we handle null values
* @author Donald Ball balld@webslingerZ.com
* @author Serge Knystautas sergek@lokitech.com
* @return java.lang.String
*/
public String toXML (String listName, String itemName,
int caseMode, int characterMode, int nullMode) throws ConnectionException, DataSetException
{
if (records == null)
fetchRecords ();
String names[] = getColumnNames(caseMode);
Column columns[] = schema.getColumns ();
StringBuffer sb = new StringBuffer();
sb.append('<'+listName + ">\n");
for (int i = 0; i < size (); i++)
{
Record rec = findRecord (i);
sb.append("\t<"+itemName + ">\n");
for (int j = 1; j < names.length; j++)
{
String value = rec.getAsString (j);
if (value == null)
{
if (nullMode == OMIT_NULLS)
continue;
else if (nullMode == EMPTY_NULLS)
value = "";
}
sb.append("\t\t<"+names[j] + '>');
if (rec.getValue (i).isString () || rec.getValue (i).isNull ())
sb.append (getEscapedValue (characterMode, value));
else
sb.append (value);
sb.append (""+ names[j] + ">\n");
}
sb.append("\t"+itemName + ">\n");
}
sb.append(""+listName + ">");
return sb.toString();
}
protected static String getEscapedValue(int characterMode, String value)
{
if (value.length() == 0)
return value;
if (characterMode == CHARACTER_ENTITIES)
{
StringBuffer sb = new StringBuffer();
for (int i = 0; i < value.length (); i++)
{
char c = value.charAt (i);
switch(c)
{
case '&':
sb.append("&");
break;
case '<':
sb.append("<");
break;
case '>':
sb.append(">");
break;
case '"':
sb.append(""");
break;
case '\'':
sb.append("'");
break;
default:
sb.append(c);
}
}
return sb.toString();
} else if (characterMode == CHARACTER_CDATA)
{
int index;
while ((index = value.indexOf("]]>")) >= 0)
value = value.substring(0,index+2)+"<"+value.substring(index+3);
return "";
}
//else if (characterMode == CHARACTER_PRESERVE)
return value;
}
}