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 ("\n"); 
			} 
            sb.append("\t\n"); 
        } 
        sb.append(""); 
        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; 
	} 
 
}