001    package com.mockrunner.mock.jdbc;
002    
003    import java.io.ByteArrayInputStream;
004    import java.io.InputStream;
005    import java.io.Reader;
006    import java.io.StringReader;
007    import java.io.UnsupportedEncodingException;
008    import java.math.BigDecimal;
009    import java.net.MalformedURLException;
010    import java.net.URL;
011    import java.sql.Array;
012    import java.sql.Blob;
013    import java.sql.Clob;
014    import java.sql.Date;
015    import java.sql.Ref;
016    import java.sql.ResultSet;
017    import java.sql.ResultSetMetaData;
018    import java.sql.SQLException;
019    import java.sql.SQLWarning;
020    import java.sql.Statement;
021    import java.sql.Time;
022    import java.sql.Timestamp;
023    import java.util.ArrayList;
024    import java.util.Arrays;
025    import java.util.Calendar;
026    import java.util.Collections;
027    import java.util.Iterator;
028    import java.util.List;
029    import java.util.Map;
030    
031    import com.mockrunner.base.NestedApplicationException;
032    import com.mockrunner.jdbc.ParameterUtil;
033    import com.mockrunner.jdbc.SQLUtil;
034    import com.mockrunner.util.common.CaseAwareMap;
035    import com.mockrunner.util.common.CollectionUtil;
036    import com.mockrunner.util.common.StreamUtil;
037    import com.mockrunner.util.common.StringUtil;
038    
039    /**
040     * Mock implementation of <code>ResultSet</code>.
041     * Can be used to add simulated database entries.
042     * You can add Java objects of any type. This
043     * mock implementation does not care about SQL
044     * data types. It tries to perform the necessary
045     * type conversions for the Java objects (e.g. it will convert a 
046     * <code>String</code> "1" to <code>int</code> 1). 
047     * Please check out the documentation of <code>ResultSet</code> 
048     * for the description of the methods in this interface. 
049     * The additional methods are described here.
050     */
051    public class MockResultSet implements ResultSet, Cloneable
052    {
053        private Statement statement;
054        private String id;
055        private Map columnMap;
056        private Map columnMapCopy;
057        private Map insertRow;
058        private List columnNameList;
059        private List updatedRows;
060        private List deletedRows;
061        private List insertedRows;
062        private int cursor;
063        private boolean isCursorInInsertRow;
064        private boolean wasNull;
065        private String cursorName;
066        private int fetchSize = 0;
067        private int fetchDirection = ResultSet.FETCH_FORWARD;
068        private int resultSetType = ResultSet.TYPE_SCROLL_INSENSITIVE;
069        private int resultSetConcurrency = ResultSet.CONCUR_READ_ONLY;
070        private boolean isDatabaseView;
071        private ResultSetMetaData resultSetMetaData;
072        private boolean closed;
073        private boolean columnsCaseSensitive;
074        
075        public MockResultSet(String id)
076        {
077            this(id, "");
078        }
079        
080        public MockResultSet(String id, String cursorName)
081        {
082            init();
083            this.cursorName = cursorName;
084            this.id = id;
085            columnsCaseSensitive = false;
086        }
087        
088        private void init()
089        {
090            columnMap = createCaseAwareMap();
091            columnNameList = new ArrayList();
092            updatedRows = new ArrayList();
093            deletedRows = new ArrayList();
094            insertedRows = new ArrayList();
095            cursor = -1;
096            wasNull = false;
097            closed = false;
098            isCursorInInsertRow = false;
099            isDatabaseView = false;
100            resultSetMetaData = null;
101            copyColumnMap();
102            adjustInsertRow();
103        }
104        
105        /**
106         * Set if column names are case sensitive. Default is
107         * <code>false</code>. Please note, that switching this
108         * attribute clears and resets the complete <code>ResultSet</code>.
109         * @param columnsCaseSensitive are column names case sensitive
110         */
111        public void setColumnsCaseSensitive(boolean columnsCaseSensitive)
112        {
113            this.columnsCaseSensitive = columnsCaseSensitive;
114            init();
115        }
116    
117        /**
118         * Copies this <code>ResultSet</code>. The data of the
119         * <code>ResultSet</code> is copied using the
120         * {@link com.mockrunner.jdbc.ParameterUtil#copyParameter}
121         * method.
122         * @return a copy of this <code>ResultSet</code>
123         */
124        public Object clone()
125        {
126            try
127            {       
128                MockResultSet copy = (MockResultSet)super.clone();
129                copy.columnNameList = new ArrayList(columnNameList);
130                copy.updatedRows = new ArrayList(updatedRows);
131                copy.deletedRows = new ArrayList(deletedRows);
132                copy.insertedRows = new ArrayList(insertedRows);
133                copy.insertRow = copyColumnDataMap(insertRow);
134                copy.columnMap = copyColumnDataMap(columnMap);
135                copy.columnMapCopy = copyColumnDataMap(columnMapCopy);
136                return copy;
137            }
138            catch(CloneNotSupportedException exc)
139            {
140                throw new NestedApplicationException(exc);
141            }
142        }
143        
144        /**
145         * Returns the id of this <code>ResultSet</code>. Ids are used
146         * to identify <code>ResultSet</code> objects in tests, because
147         * they are usually cloned when executing statements, so
148         * you cannot rely on the object identity.
149         * @return the id of this <code>ResultSet</code>
150         */
151        public String getId()
152        {
153            return id;
154        }
155        
156        /**
157         * Returns if this <code>ResultSet</code> is closed.
158         * @return <code>true</code> if this <code>ResultSet</code> is closed,
159         *         <code>false</code> otherwise
160         */
161        public boolean isClosed()
162        {
163            return closed;
164        }
165        
166        /**
167         * Sets the <code>ResultSetMetaData</code> for this <code>ResultSet</code>.
168         * The specified object will be returned when calling {@link #getMetaData}.
169         * If no <code>ResultSetMetaData</code> is set, the method {@link #getMetaData}
170         * will return an object of {@link MockResultSetMetaData}. The
171         * <code>MockResultSetMetaData</code> returns default values for most
172         * of its attributes (however the correct number of columns will be
173         * returned). Usually you do not have to set the <code>ResultSetMetaData</code>.
174         * @param resultSetMetaData the <code>ResultSetMetaData</code>
175         */
176        public void setResultSetMetaData(ResultSetMetaData resultSetMetaData)
177        {
178            this.resultSetMetaData = resultSetMetaData;
179        }
180        
181        /**
182         * Sets the <code>Statement</code> for this <code>ResultSet</code>.
183         * The <code>ResultSet</code> takes the result set type, result
184         * set concurrency and the fetch direction from the specified
185         * <code>Statement</code>.
186         * @param statement the statement
187         */
188        public void setStatement(Statement statement)
189        {
190            this.statement = statement;
191            try
192            {
193                fetchDirection = statement.getFetchDirection();
194                resultSetType = statement.getResultSetType();
195                resultSetConcurrency = statement.getResultSetConcurrency();
196                fetchSize = statement.getFetchSize();
197                cursorName = ((MockStatement)statement).getCursorName();
198            }
199            catch(SQLException exc)
200            {
201    
202            }
203        }
204        
205        /**
206         * Sets the cursor name. It's not possible to set
207         * this in a real <code>ResultSet</code>.
208         * @param cursorName the cursor name
209         */
210        public void setCursorName(String cursorName)
211        {
212            this.cursorName = cursorName;
213        }
214        
215        /**
216         * Sets the result set type. It's not possible to set
217         * this in a real <code>ResultSet</code>, but in tests
218         * it can make sense to change it.
219         * @param resultSetType the result set type
220         */
221        public void setResultSetType(int resultSetType)
222        {
223            this.resultSetType = resultSetType;
224        }
225        
226        /**
227         * Sets the result set concurrency. It's not possible to set
228         * this in a real <code>ResultSet</code>, but in tests
229         * it can make sense to change it.
230         * @param resultSetConcurrency the result set concurrency
231         */
232        public void setResultSetConcurrency(int resultSetConcurrency)
233        {
234            this.resultSetConcurrency = resultSetConcurrency;
235        }
236        
237        /**
238         * The <code>MockResultSet</code> keeps the data that's
239         * stored in the simulated database and a copy of the data
240         * that represents the current <code>ResultSet</code> data.
241         * The <code>update</code> methods only update the 
242         * <code>ResultSet</code> data. This data will be persisted
243         * when you call {@link #updateRow}. When you set <i>databaseView</i> 
244         * to <code>true</code> the <code>get</code> methods will return the 
245         * data in the database, otherwise the current <code>ResultSet</code> 
246         * data is returned.
247         * @param databaseView <code>false</code> = get the data from the 
248         *        <code>ResultSet</code>, <code>true</code> = get the data
249         *        from the database, default is <code>false</code>
250         *
251         */
252        public void setDatabaseView(boolean databaseView)
253        {
254            this.isDatabaseView = databaseView;
255        }
256        
257        /**
258         * Adds a row to the simulated database table.
259         * If there are not enough columns (initially there
260         * are no columns, you have to specify them with the
261         * <code>addColumn</code> methods) the missing columns will
262         * be added automatically. Automatically created columns
263         * will get the name <i>ColumnX</i> where <i>X</i> is
264         * the column index.
265         * @param values the row data as array, the array index
266         *        corresponds to the column index, i.e.
267         *        values[0] will be stored in the first column
268         *        and so on
269         */
270        public void addRow(Object[] values)
271        {
272            List valueList = Arrays.asList(values);
273            addRow(valueList);
274        }
275        
276        /**
277         * Adds a row to the simulated database table.
278         * If there are not enough columns (initially there
279         * are no columns, you have to specify them with the
280         * <code>addColumn</code> methods) the missing columns will
281         * be added automatically. Automatically created columns
282         * will get the name <i>ColumnX</i> where <i>X</i> is
283         * the column index.
284         * @param values the row data as <code>List</code>, the index
285         *        in the <code>List</code> corresponds to the column 
286         *        index, i.e. values.get(0) will be stored in the first 
287         *        column and so on
288         */
289        public void addRow(List values)
290        {
291            int missingColumns = values.size() - columnNameList.size();
292            for(int yy = 0; yy < missingColumns; yy++)
293            {
294                addColumn();
295            }
296            adjustColumns();
297            for(int ii = 0; ii < values.size(); ii++)
298            {   
299               Object nextValue = values.get(ii);
300               String nextColumnName = (String)columnNameList.get(ii);
301               List nextColumnList = (List)columnMap.get(nextColumnName);
302               nextColumnList.add(nextValue);
303            }
304            adjustColumns();
305            copyColumnMap();
306            adjustFlags();
307        }
308        
309        /**
310         * Adds a column to the simulated database table.
311         * The column will get the name <i>ColumnX</i> where 
312         * <i>X</i> is the column index. The first added column
313         * will have the name <i>Column1</i>. No data will be stored
314         * in the column.
315         */
316        public void addColumn()
317        {
318            addColumn(determineValidColumnName());
319        }
320        
321        /**
322         * Adds a column to the simulated database table.
323         * The column will get the specified name.
324         * No data will be stored in the column.
325         * @param columnName the column name
326         */
327        public void addColumn(String columnName)
328        {
329            addColumn(columnName, new ArrayList());
330        }
331        
332        /**
333         * Adds a column to the simulated database table.
334         * The column will get the name <i>ColumnX</i> where 
335         * <i>X</i> is the column index. 
336         * The specified data will be stored in the new column. If there
337         * are other columns with not enough rows, the other
338         * columns will be extended and filled with <code>null</code>
339         * values.
340         * @param values the column data as array, the array index
341         *        corresponds to the row index, i.e.
342         *        values[0] will be stored in the first row
343         *        and so on
344         */
345        public void addColumn(Object[] values)
346        {
347            addColumn(determineValidColumnName(), values);
348        }
349    
350        /**
351         * Adds a column to the simulated database table.
352         * The column will get the name <i>ColumnX</i> where 
353         * <i>X</i> is the column index. 
354         * The specified data will be stored in the new column. If there
355         * are other columns with not enough rows, the other
356         * columns will be extended and filled with <code>null</code>
357         * values.
358         * @param values the column data as <code>List</code>, the index
359         *        in the <code>List</code> corresponds to the row 
360         *        index, i.e. values.get(0) will be stored in the first 
361         *        row and so on
362         */
363        public void addColumn(List values)
364        {
365            addColumn(determineValidColumnName(), values);
366        }
367        
368        /**
369         * Adds a column to the simulated database table.
370         * The column will get the specified name.
371         * The specified data will be stored in the new column. If there
372         * are other columns with not enough rows, the other
373         * columns will be extended and filled with <code>null</code>
374         * values.
375         * @param columnName the column name
376         * @param values the column data as array, the array index
377         *        corresponds to the row index, i.e.
378         *        values[0] will be stored in the first row
379         *        and so on
380         */
381        public void addColumn(String columnName, Object[] values)
382        {
383            List columnValues = Arrays.asList(values);
384            addColumn(columnName, columnValues);
385        }
386        
387        /**
388         * Adds a column to the simulated database table.
389         * The column will get the specified name.
390         * The specified data will be stored in the new column. If there
391         * are other columns with not enough rows, the other
392         * columns will be extended and filled with <code>null</code>
393         * values.
394         * @param columnName the column name
395         * @param values the column data as <code>List</code>, the index
396         *        in the <code>List</code> corresponds to the row 
397         *        index, i.e. values.get(0) will be stored in the first 
398         *        row and so on
399         */
400        public void addColumn(String columnName, List values)
401        {
402            List column = new ArrayList(values);
403            columnMap.put(columnName, column);
404            columnNameList.add(columnName);
405            adjustColumns();
406            adjustInsertRow();
407            copyColumnMap();
408            adjustFlags();
409        }
410        
411        /**
412         * Returns the current number of rows.
413         * @return the number of rows
414         */
415        public int getRowCount()
416        {
417            if(columnMapCopy.size() == 0) return 0;
418            List column = (List)columnMapCopy.values().iterator().next();
419            return column.size();
420        }
421        
422        /**
423         * Returns the current number of columns.
424         * @return the number of columns
425         */
426        public int getColumnCount()
427        {
428            return columnMapCopy.size();
429        }
430        
431        /**
432         * Returns if the row with the specified number was inserted
433         * The first row has the number 1.
434         * @param number the number of the row
435         * @return <code>true</code> if the row was inserted,
436         *         <code>false</code> otherwise
437         */
438        public boolean rowInserted(int number)
439        {
440            if(number < 1) return false;
441            return ((Boolean)insertedRows.get(number - 1)).booleanValue();
442        }
443        
444        /**
445         * Returns if the row with the specified number was deleted
446         * The first row has the number 1.
447         * @param number the number of the row
448         * @return <code>true</code> if the row was deleted,
449         *         <code>false</code> otherwise
450         */
451        public boolean rowDeleted(int number)
452        {
453            if(number < 1) return false;
454            return ((Boolean)deletedRows.get(number - 1)).booleanValue();
455        }
456        
457        /**
458         * Returns if the row with the specified number was updated
459         * The first row has the number 1.
460         * @param number the number of the row
461         * @return <code>true</code> if the row was updated,
462         *         <code>false</code> otherwise
463         */
464        public boolean rowUpdated(int number)
465        {
466            if(number < 1) return false;
467            return ((Boolean)updatedRows.get(number - 1)).booleanValue();
468        }
469        
470        /**
471         * Returns if the row with the specified number is
472         * equal to the specified data. Uses {@link com.mockrunner.jdbc.ParameterUtil#compareParameter}.
473         * The first row has the number 1. If the compared parameters are not of
474         * the same type (and cannot be equal according to the 
475         * {@link com.mockrunner.jdbc.ParameterUtil#compareParameter} method) they
476         * will be converted to a string with the <code>toString()</code> method before
477         * comparison.
478         * @param number the number of the row
479         * @param rowData the row data
480         * @return <code>true</code> if the row is equal to the specified data,
481         *         <code>false</code> otherwise
482         */
483        public boolean isRowEqual(int number, List rowData)
484        {
485            List currentRow = getRow(number);
486            if(null == currentRow) return false;
487            if(currentRow.size() != rowData.size()) return false;
488            for(int ii = 0; ii < currentRow.size(); ii++)
489            {
490                Object source = currentRow.get(ii);
491                Object target = rowData.get(ii);
492                if(!source.getClass().isAssignableFrom(target.getClass()) && !target.getClass().isAssignableFrom(source.getClass()))
493                {
494                    source = source.toString();
495                    target = target.toString();
496                }
497                if(!ParameterUtil.compareParameter(source, target))
498                {
499                    return false;
500                }
501            }
502            return true;
503        }
504        
505        /**
506         * Returns if the column with the specified number is
507         * equal to the specified data. Uses {@link com.mockrunner.jdbc.ParameterUtil#compareParameter}.
508         * The first column has the number 1. If the compared parameters are not of
509         * the same type (and cannot be equal according to the 
510         * {@link com.mockrunner.jdbc.ParameterUtil#compareParameter} method) they
511         * will be converted to a string with the <code>toString()</code> method before
512         * comparison.
513         * @param number the number of the column
514         * @param columnData the column data
515         * @return <code>true</code> if the column is equal to the specified data,
516         *         <code>false</code> otherwise
517         */
518        public boolean isColumnEqual(int number, List columnData)
519        {
520            List currentColumn = getColumn(number);
521            if(null == currentColumn) return false;
522            if(currentColumn.size() != columnData.size()) return false;
523            for(int ii = 0; ii < currentColumn.size(); ii++)
524            {
525                Object source = currentColumn.get(ii);
526                Object target = columnData.get(ii);
527                if(!source.getClass().isAssignableFrom(target.getClass()) && !target.getClass().isAssignableFrom(source.getClass()))
528                {
529                    source = source.toString();
530                    target = target.toString();
531                }
532                if(!ParameterUtil.compareParameter(source, target))
533                {
534                    return false;
535                }
536            }
537            return true;
538        }
539        
540        /**
541         * Returns if the column with the specified name is
542         * equal to the specified data. Uses {@link com.mockrunner.jdbc.ParameterUtil#compareParameter}.
543         * The first column has the number 1. If the compared parameters are not of
544         * the same type (and cannot be equal according to the 
545         * {@link com.mockrunner.jdbc.ParameterUtil#compareParameter} method) they
546         * will be converted to a string with the <code>toString()</code> method before
547         * comparison.
548         * @param name the name of the column
549         * @param columnData the column data
550         * @return <code>true</code> if the column is equal to the specified data,
551         *         <code>false</code> otherwise
552         */
553        public boolean isColumnEqual(String name, List columnData)
554        {
555            List currentColumn = getColumn(name);
556            if(null == currentColumn) return false;
557            if(currentColumn.size() != columnData.size()) return false;
558            for(int ii = 0; ii < currentColumn.size(); ii++)
559            {
560                Object source = currentColumn.get(ii);
561                Object target = columnData.get(ii);
562                if(!source.getClass().isAssignableFrom(target.getClass()) && !target.getClass().isAssignableFrom(source.getClass()))
563                {
564                    source = source.toString();
565                    target = target.toString();
566                }
567                if(!ParameterUtil.compareParameter(source, target))
568                {
569                    return false;
570                }
571            }
572            return true;
573        }
574        
575        /**
576         * Returns if the specified <code>ResultSet</code> is equal to
577         * this <code>ResultSet</code>. If the compared parameters are not of
578         * the same type (and cannot be equal according to the 
579         * {@link com.mockrunner.jdbc.ParameterUtil#compareParameter} method) they
580         * will be converted to a string with the <code>toString()</code> method before
581         * comparison.
582         * @return <code>true</code> if the two <code>ResultSet</code> objects are equal,
583         *         <code>false</code> otherwise
584         */
585        public boolean isEqual(MockResultSet resultSet)
586        {
587            if(null == resultSet) return false;
588            Map thisMap;
589            Map otherMap;
590            if(isDatabaseView)
591            {
592                thisMap = columnMap;    
593            }
594            else
595            {
596                thisMap = columnMapCopy;  
597            }
598            if(resultSet.isDatabaseView)
599            {
600                otherMap = resultSet.columnMap;
601            }
602            else
603            {
604                otherMap = resultSet.columnMapCopy;
605            }
606            Iterator keys = thisMap.keySet().iterator();
607            while(keys.hasNext())
608            {
609                String currentKey = (String)keys.next();
610                List thisList =  (List)thisMap.get(currentKey);
611                List otherList =  (List)otherMap.get(currentKey);
612                if(null == otherList) return false;
613                if(thisList.size() != otherList.size()) return false;
614                for(int ii = 0; ii < thisList.size(); ii++)
615                {
616                    Object source = thisList.get(ii);
617                    Object target = otherList.get(ii);
618                    if(!source.getClass().isAssignableFrom(target.getClass()) && !target.getClass().isAssignableFrom(source.getClass()))
619                    {
620                        source = source.toString();
621                        target = target.toString();
622                    }
623                    if(!ParameterUtil.compareParameter(source, target))
624                    {
625                        return false;
626                    }    
627                }
628            }
629            return true;
630        }
631        
632        /**
633         * Returns the row with the specified number.
634         * The first row has the number 1.
635         * If number is less than 1 or higher than the
636         * current row count, <code>null</code> will
637         * be returned. The result of this method depends
638         * on the setting of <i>databaseView</i>. 
639         * See {@link #setDatabaseView}.
640         * @param number the number of the row
641         * @return the row data as <code>List</code>
642         */
643        public List getRow(int number)
644        {
645            if(number > getRowCount()) return null;
646            if(number < 1) return null;
647            int index = number - 1;
648            List list = new ArrayList();
649            for(int ii = 0; ii < columnNameList.size(); ii++)
650            {
651                String nextColumnName = (String)columnNameList.get(ii);
652                List nextColumnList;
653                if(isDatabaseView)
654                {
655                    nextColumnList = (List)columnMap.get(nextColumnName);
656                }
657                else
658                {
659                    nextColumnList = (List)columnMapCopy.get(nextColumnName);
660                }
661                list.add(nextColumnList.get(index));
662            }
663            return list;
664        }
665        
666        /**
667         * Returns the column with the specified number.
668         * The first column has the number 1.
669         * If number is less than 1 or higher than the
670         * current column count, <code>null</code> will
671         * be returned.
672         * @param number the number of the column
673         * @return the column data as <code>List</code>
674         */
675        public List getColumn(int number)
676        {
677            if(number > getColumnCount()) return null;
678            if(number < 1) return null;
679            int index = number - 1;
680            String columnName = (String)columnNameList.get(index);
681            return getColumn(columnName);
682        }
683        
684        /**
685         * Returns the column with the specified name.
686         * If a column with that name does not exist, 
687         * <code>null</code> will be returned.
688         * @param name the name of the column
689         * @return the column data as <code>List</code>
690         */
691        public List getColumn(String name)
692        {
693            List list = new ArrayList();
694            List columnList;
695            if(isDatabaseView)
696            {
697                columnList = (List)columnMap.get(name);
698            }
699            else
700            {
701                columnList = (List)columnMapCopy.get(name);
702            }
703            if(null == columnList) return null;
704            list.addAll(columnList);
705            return list;
706        }
707        
708        public void close() throws SQLException
709        {
710            closed = true;
711        }
712    
713        public boolean wasNull() throws SQLException
714        {
715            return wasNull;
716        }
717        
718        public Object getObject(int columnIndex) throws SQLException
719        {
720            checkColumnBounds(columnIndex);
721            if(!isCurrentRowValid())
722            {
723                wasNull = true;
724                return null;
725            }
726            String columnName = (String)columnNameList.get(columnIndex - 1);
727            return getObject(columnName);
728        }
729        
730        public Object getObject(String columnName) throws SQLException
731        {
732            checkColumnName(columnName);
733            if(!isCurrentRowValid())
734            {
735                wasNull = true;
736                return null;
737            }
738            if(rowDeleted()) throw new SQLException("row was deleted");
739            List column;
740            if(isDatabaseView)
741            {
742                column = (List)columnMap.get(columnName);
743            }
744            else
745            {
746                column = (List)columnMapCopy.get(columnName);
747            }
748            Object value = column.get(cursor);
749            wasNull = (null == value);
750            return value;
751        }
752        
753        public Object getObject(int columnIndex, Map map) throws SQLException
754        {
755            return getObject(columnIndex);
756        }
757    
758        public Object getObject(String colName, Map map) throws SQLException
759        {
760            return getObject(colName);
761        }
762    
763        public String getString(int columnIndex) throws SQLException
764        {
765            Object value = getObject(columnIndex);
766            if(null != value) return value.toString();
767            return null;
768        }
769        
770        public String getString(String columnName) throws SQLException
771        {
772            Object value = getObject(columnName);
773            if(null != value) return value.toString();
774            return null;
775        }
776    
777    
778        public boolean getBoolean(int columnIndex) throws SQLException
779        {
780            Object value = getObject(columnIndex);
781            if(null != value)
782            {
783                if(value instanceof Boolean) return ((Boolean)value).booleanValue();
784                return new Boolean(value.toString()).booleanValue();
785            }
786            return false;
787        }
788        
789        public boolean getBoolean(String columnName) throws SQLException
790        {
791            Object value = getObject(columnName);
792            if(null != value)
793            {
794                if(value instanceof Boolean) return ((Boolean)value).booleanValue();
795                return new Boolean(value.toString()).booleanValue();
796            }
797            return false;
798        }
799    
800        public byte getByte(int columnIndex) throws SQLException
801        {
802            Object value = getObject(columnIndex);
803            if(null != value)
804            {
805                if(value instanceof Number) return ((Number)value).byteValue();
806                return new Byte(value.toString()).byteValue();
807            }
808            return 0;
809        }
810        
811        public byte getByte(String columnName) throws SQLException
812        {
813            Object value = getObject(columnName);
814            if(null != value)
815            {
816                if(value instanceof Number) return ((Number)value).byteValue();
817                return new Byte(value.toString()).byteValue();
818            }
819            return 0;
820        }
821    
822        public short getShort(int columnIndex) throws SQLException
823        {
824            Object value = getObject(columnIndex);
825            if(null != value)
826            {
827                if(value instanceof Number) return ((Number)value).shortValue();
828                return new Short(value.toString()).shortValue();
829            }
830            return 0;
831        }
832        
833        public short getShort(String columnName) throws SQLException
834        {
835            Object value = getObject(columnName);
836            if(null != value)
837            {
838                if(value instanceof Number) return ((Number)value).shortValue();
839                return new Short(value.toString()).shortValue();
840            }
841            return 0;
842        }
843    
844        public int getInt(int columnIndex) throws SQLException
845        {
846            Object value = getObject(columnIndex);
847            if(null != value)
848            {
849                if(value instanceof Number) return ((Number)value).intValue();
850                return new Integer(value.toString()).intValue();
851            }
852            return 0;
853        }
854        
855        public int getInt(String columnName) throws SQLException
856        {
857            Object value = getObject(columnName);
858            if(null != value)
859            {
860                if(value instanceof Number) return ((Number)value).intValue();
861                return new Integer(value.toString()).intValue();
862            }
863            return 0;
864        }
865    
866        public long getLong(int columnIndex) throws SQLException
867        {
868            Object value = getObject(columnIndex);
869            if(null != value)
870            {
871                if(value instanceof Number) return ((Number)value).longValue();
872                return new Long(value.toString()).longValue();
873            }
874            return 0;
875        }
876        
877        public long getLong(String columnName) throws SQLException
878        {
879            Object value = getObject(columnName);
880            if(null != value)
881            {
882                if(value instanceof Number) return ((Number)value).longValue();
883                return new Long(value.toString()).longValue();
884            }
885            return 0;
886        }
887    
888        public float getFloat(int columnIndex) throws SQLException
889        {
890            Object value = getObject(columnIndex);
891            if(null != value)
892            {
893                if(value instanceof Number) return ((Number)value).floatValue();
894                return new Float(value.toString()).floatValue();
895            }
896            return 0;
897        }
898        
899        public float getFloat(String columnName) throws SQLException
900        {
901            Object value = getObject(columnName);
902            if(null != value)
903            {
904                if(value instanceof Number) return ((Number)value).floatValue();
905                return new Float(value.toString()).floatValue();
906            }
907            return 0;
908        }
909        
910        public double getDouble(int columnIndex) throws SQLException
911        {
912            Object value = getObject(columnIndex);
913            if(null != value)
914            {
915                if(value instanceof Number) return ((Number)value).doubleValue();
916                return new Double(value.toString()).doubleValue();
917            }
918            return 0;
919        }
920        
921        public double getDouble(String columnName) throws SQLException
922        {
923            Object value = getObject(columnName);
924            if(null != value)
925            {
926                if(value instanceof Number) return ((Number)value).doubleValue();
927                return new Double(value.toString()).doubleValue();
928            }
929            return 0;
930        }
931    
932        public BigDecimal getBigDecimal(int columnIndex, int scale) throws SQLException
933        {
934            BigDecimal value = getBigDecimal(columnIndex);
935            if(null != value)
936            {
937                return value.setScale(scale);
938            }
939            return null;
940        }
941        
942        public BigDecimal getBigDecimal(String columnName, int scale) throws SQLException
943        {
944            BigDecimal value = getBigDecimal(columnName);
945            if(null != value)
946            {
947                return value.setScale(scale);
948            }
949            return null;
950        }
951        
952        public BigDecimal getBigDecimal(int columnIndex) throws SQLException
953        {
954            Object value = getObject(columnIndex);
955            if(null != value)
956            {
957                if(value instanceof Number) return new BigDecimal(((Number)value).doubleValue());
958                return new BigDecimal(value.toString());
959            }
960            return null;
961        }
962    
963        public BigDecimal getBigDecimal(String columnName) throws SQLException
964        {
965            Object value = getObject(columnName);
966            if(null != value)
967            {
968                if(value instanceof Number) return new BigDecimal(((Number)value).doubleValue());
969                return new BigDecimal(value.toString());
970            }
971            return null;
972        }
973    
974        public byte[] getBytes(int columnIndex) throws SQLException
975        {
976            Object value = getObject(columnIndex);
977            if(null != value)
978            {
979                if(value instanceof byte[]) return (byte[])value;
980                return value.toString().getBytes();
981            }
982            return null;
983        }
984        
985        public byte[] getBytes(String columnName) throws SQLException
986        {
987            Object value = getObject(columnName);
988            if(null != value)
989            {
990                if(value instanceof byte[]) return (byte[])value;
991                return value.toString().getBytes();
992            }
993            return null;
994        }
995    
996        public Date getDate(int columnIndex) throws SQLException
997        {
998            Object value = getObject(columnIndex);
999            if(null != value)
1000            {
1001                if(value instanceof Date) return (Date)value;
1002                return Date.valueOf(value.toString());
1003            }
1004            return null;
1005        }
1006        
1007        public Date getDate(String columnName) throws SQLException
1008        {
1009            Object value = getObject(columnName);
1010            if(null != value)
1011            {
1012                if(value instanceof Date) return (Date)value;
1013                return Date.valueOf(value.toString());
1014            }
1015            return null;
1016        }
1017        
1018        public Date getDate(int columnIndex, Calendar calendar) throws SQLException
1019        {
1020            return getDate(columnIndex);
1021        }
1022    
1023        public Date getDate(String columnName, Calendar calendar) throws SQLException
1024        {
1025            return getDate(columnName);
1026        }
1027    
1028        public Time getTime(int columnIndex) throws SQLException
1029        {
1030            Object value = getObject(columnIndex);
1031            if(null != value)
1032            {
1033                if(value instanceof Time) return (Time)value;
1034                return Time.valueOf(value.toString());
1035            }
1036            return null;
1037        }
1038        
1039        public Time getTime(String columnName) throws SQLException
1040        {
1041            Object value = getObject(columnName);
1042            if(null != value)
1043            {
1044                if(value instanceof Time) return (Time)value;
1045                return Time.valueOf(value.toString());
1046            }
1047            return null;
1048        }
1049        
1050        public Time getTime(int columnIndex, Calendar calendar) throws SQLException
1051        {
1052            return getTime(columnIndex);
1053        }
1054    
1055        public Time getTime(String columnName, Calendar calendar) throws SQLException
1056        {
1057            return getTime(columnName);
1058        }
1059    
1060        public Timestamp getTimestamp(int columnIndex) throws SQLException
1061        {
1062            Object value = getObject(columnIndex);
1063            if(null != value)
1064            {
1065                if(value instanceof Timestamp) return (Timestamp)value;
1066                return Timestamp.valueOf(value.toString());
1067            }
1068            return null;
1069        }
1070        
1071        public Timestamp getTimestamp(String columnName) throws SQLException
1072        {
1073            Object value = getObject(columnName);
1074            if(null != value)
1075            {
1076                if(value instanceof Timestamp) return (Timestamp)value;
1077                return Timestamp.valueOf(value.toString());
1078            }
1079            return null;
1080        }
1081        
1082        public Timestamp getTimestamp(int columnIndex, Calendar calendar) throws SQLException
1083        {
1084            return getTimestamp(columnIndex);
1085        }
1086    
1087        public Timestamp getTimestamp(String columnName, Calendar calendar) throws SQLException
1088        {
1089            return getTimestamp(columnName);
1090        }
1091        
1092        public URL getURL(int columnIndex) throws SQLException
1093        {
1094            Object value = getObject(columnIndex);
1095            if(null != value)
1096            {
1097                if(value instanceof URL) return (URL)value;
1098                try
1099                {
1100                    return new URL(value.toString());
1101                }
1102                catch(MalformedURLException exc)
1103                {
1104                
1105                }
1106            }
1107            return null;
1108        }
1109    
1110        public URL getURL(String columnName) throws SQLException
1111        {
1112            Object value = getObject(columnName);
1113            if(null != value)
1114            {
1115                if(value instanceof URL) return (URL)value;
1116                try
1117                {
1118                    return new URL(value.toString());
1119                }
1120                catch(MalformedURLException exc)
1121                {
1122                
1123                }
1124            }
1125            return null;
1126        }
1127        
1128        public Blob getBlob(int columnIndex) throws SQLException
1129        {
1130            Object value = getObject(columnIndex);
1131            if(null != value)
1132            {
1133                if(value instanceof Blob) return (Blob)value;
1134                return new MockBlob(getBytes(columnIndex));
1135            }
1136            return null;
1137        }
1138        
1139        public Blob getBlob(String columnName) throws SQLException
1140        {
1141            Object value = getObject(columnName);
1142            if(null != value)
1143            {
1144                if(value instanceof Blob) return (Blob)value;
1145                return new MockBlob(getBytes(columnName));
1146            }
1147            return null;
1148        }
1149    
1150        public Clob getClob(int columnIndex) throws SQLException
1151        {
1152            Object value = getObject(columnIndex);
1153            if(null != value)
1154            {
1155                if(value instanceof Clob) return (Clob)value;
1156                return new MockClob(getString(columnIndex));
1157            }
1158            return null;
1159        }
1160        
1161        public Clob getClob(String columnName) throws SQLException
1162        {
1163            Object value = getObject(columnName);
1164            if(null != value)
1165            {
1166                if(value instanceof Clob) return (Clob)value;
1167                return new MockClob(getString(columnName));
1168            }
1169            return null;
1170        }
1171        
1172        public Array getArray(int columnIndex) throws SQLException
1173        {
1174            Object value = getObject(columnIndex);
1175            if(null != value)
1176            {
1177                if(value instanceof Array) return (Array)value;
1178                return new MockArray(value);
1179            }
1180            return null;
1181        }
1182        
1183        public Array getArray(String columnName) throws SQLException
1184        {
1185            Object value = getObject(columnName);
1186            if(null != value)
1187            {
1188                if(value instanceof Array) return (Array)value;
1189                return new MockArray(value);
1190            }
1191            return null;
1192        }
1193        
1194        public Ref getRef(int columnIndex) throws SQLException
1195        {
1196            Object value = getObject(columnIndex);
1197            if(null != value)
1198            {
1199                if(value instanceof Ref) return (Ref)value;
1200                return new MockRef(value);
1201            }
1202            return null;
1203        }
1204    
1205        public Ref getRef(String columnName) throws SQLException
1206        {
1207            Object value = getObject(columnName);
1208            if(null != value)
1209            {
1210                if(value instanceof Ref) return (Ref)value;
1211                return new MockRef(value);
1212            }
1213            return null;
1214        }
1215    
1216        public InputStream getAsciiStream(int columnIndex) throws SQLException
1217        {
1218            return getBinaryStream(columnIndex);
1219        }
1220        
1221        public InputStream getAsciiStream(String columnName) throws SQLException
1222        {
1223            return getBinaryStream(columnName);
1224        }
1225    
1226        public InputStream getBinaryStream(int columnIndex) throws SQLException
1227        {
1228            Object value = getObject(columnIndex);
1229            if(null != value)
1230            {
1231                if(value instanceof InputStream) return (InputStream)value;
1232                return new ByteArrayInputStream(getBytes(columnIndex));
1233            }
1234            return null;
1235        }
1236    
1237        public InputStream getBinaryStream(String columnName) throws SQLException
1238        {
1239            Object value = getObject(columnName);
1240            if(null != value)
1241            {
1242                if(value instanceof InputStream) return (InputStream)value;
1243                return new ByteArrayInputStream(getBytes(columnName));
1244            }
1245            return null;
1246        }
1247        
1248        public InputStream getUnicodeStream(int columnIndex) throws SQLException
1249        {
1250            Object value = getObject(columnIndex);
1251            if(null != value)
1252            {
1253                if(value instanceof InputStream) return (InputStream)value;
1254                try
1255                {
1256                    return new ByteArrayInputStream(getString(columnIndex).getBytes("UTF-8"));
1257                }
1258                catch(UnsupportedEncodingException exc)
1259                {
1260                
1261                }
1262            }
1263            return null;
1264        }
1265    
1266        public InputStream getUnicodeStream(String columnName) throws SQLException
1267        {
1268            Object value = getObject(columnName);
1269            if(null != value)
1270            {
1271                if(value instanceof InputStream) return (InputStream)value;
1272                try
1273                {
1274                    return new ByteArrayInputStream(getString(columnName).getBytes("UTF-8"));
1275                }
1276                catch(UnsupportedEncodingException exc)
1277                {
1278                
1279                }
1280            }
1281            return null;
1282        }
1283        
1284        public Reader getCharacterStream(int columnIndex) throws SQLException
1285        {
1286            Object value = getObject(columnIndex);
1287            if(null != value)
1288            {
1289                if(value instanceof Reader) return (Reader)value;
1290                return new StringReader(getString(columnIndex));
1291            }
1292            return null;
1293        }
1294    
1295        public Reader getCharacterStream(String columnName) throws SQLException
1296        {
1297            Object value = getObject(columnName);
1298            if(null != value)
1299            {
1300                if(value instanceof Reader) return (Reader)value;
1301                return new StringReader(getString(columnName));
1302            }
1303            return null;
1304        }
1305    
1306    
1307        public SQLWarning getWarnings() throws SQLException
1308        {
1309            return null;
1310        }
1311    
1312        public void clearWarnings() throws SQLException
1313        {
1314    
1315        }
1316    
1317        public String getCursorName() throws SQLException
1318        {
1319            return cursorName;
1320        }
1321    
1322        public ResultSetMetaData getMetaData() throws SQLException
1323        {
1324            if(null != resultSetMetaData) return resultSetMetaData;
1325            MockResultSetMetaData metaData = new MockResultSetMetaData();
1326            metaData.setColumnCount(getColumnCount());
1327            for(int ii = 0; ii < columnNameList.size(); ii++)
1328            {
1329                metaData.setColumnName(ii + 1, (String)columnNameList.get(ii));
1330            }
1331            return metaData;
1332        }
1333        
1334        public Statement getStatement() throws SQLException
1335        {
1336            return statement;
1337        }
1338    
1339        public boolean isBeforeFirst() throws SQLException
1340        {
1341            // Counterintuitively, this method is supposed to return false when the
1342            // result set is empty.
1343            return (getRowCount() != 0) && (cursor == -1);
1344        }
1345    
1346        public boolean isAfterLast() throws SQLException
1347        {    
1348            return cursor >= getRowCount();
1349        }
1350    
1351        public boolean isFirst() throws SQLException
1352        {
1353            return cursor == 0;
1354        }
1355    
1356        public boolean isLast() throws SQLException
1357        {
1358            return (cursor != -1) && (cursor == getRowCount() - 1);
1359        }
1360    
1361        public void beforeFirst() throws SQLException
1362        {
1363            if(isCursorInInsertRow) throw new SQLException("cursor is in insert row");
1364            checkResultSetType();
1365            cursor = -1;
1366        }
1367    
1368        public void afterLast() throws SQLException
1369        {
1370            if(isCursorInInsertRow) throw new SQLException("cursor is in insert row");
1371            checkResultSetType();
1372            if(getRowCount() == 0) return;
1373            cursor = getRowCount();
1374        }
1375        
1376        public boolean next() throws SQLException
1377        {
1378            if(isCursorInInsertRow) throw new SQLException("cursor is in insert row");
1379            if(getRowCount() == 0) return false;
1380            cursor++;
1381            adjustCursor();
1382            return isCurrentRowValid();
1383        }
1384    
1385    
1386        public boolean first() throws SQLException
1387        {
1388            if(isCursorInInsertRow) throw new SQLException("cursor is in insert row");
1389            checkResultSetType();
1390            if(getRowCount() == 0) return false;
1391            cursor = 0;
1392            return true;
1393        }
1394    
1395        public boolean last() throws SQLException
1396        {
1397            if(isCursorInInsertRow) throw new SQLException("cursor is in insert row");
1398            checkResultSetType();
1399            if(getRowCount() == 0) return false;
1400            cursor = getRowCount() - 1;
1401            return true;
1402        }
1403        
1404        public boolean absolute(int row) throws SQLException
1405        {
1406            if(isCursorInInsertRow) throw new SQLException("cursor is in insert row");
1407            checkResultSetType();
1408            if(getRowCount() == 0) return false;
1409            if(row > 0) cursor = row - 1;
1410            if(row < 0) cursor = getRowCount() + row;
1411            adjustCursor();
1412            return isCurrentRowValid();
1413        }
1414    
1415        public boolean relative(int rows) throws SQLException
1416        {
1417            if(isCursorInInsertRow) throw new SQLException("cursor is in insert row");
1418            checkResultSetType();
1419            if(getRowCount() == 0) return false;
1420            cursor += rows;
1421            adjustCursor();
1422            return isCurrentRowValid();
1423        }
1424    
1425        public int getRow() throws SQLException
1426        {
1427            return cursor + 1;
1428        }
1429    
1430        public boolean previous() throws SQLException
1431        {
1432            if(isCursorInInsertRow) throw new SQLException("cursor is in insert row");
1433            checkResultSetType();
1434            if(getRowCount() == 0) return false;
1435            cursor--;
1436            adjustCursor();
1437            return isCurrentRowValid();
1438        }
1439        
1440        public void setFetchDirection(int fetchDirection) throws SQLException
1441        {
1442            checkFetchDirectionArguments(fetchDirection);
1443            if(this.fetchDirection == fetchDirection) return;
1444            if(this.fetchDirection == ResultSet.FETCH_UNKNOWN || fetchDirection == ResultSet.FETCH_UNKNOWN)
1445            {
1446                this.fetchDirection = fetchDirection;
1447                return;
1448            }
1449            this.fetchDirection = fetchDirection;
1450            Iterator columns = columnMapCopy.values().iterator();
1451            while(columns.hasNext())
1452            {
1453                List column = (List)columns.next();
1454                Collections.reverse(column);
1455            }
1456            if(-1 != cursor) cursor = getRowCount() - cursor - 1;
1457        }
1458    
1459        public int getFetchDirection() throws SQLException
1460        {
1461            return fetchDirection;
1462        }
1463    
1464        public void setFetchSize(int fetchSize) throws SQLException
1465        {
1466            this.fetchSize = fetchSize;
1467        }
1468    
1469        public int getFetchSize() throws SQLException
1470        {
1471            return fetchSize;
1472        }
1473    
1474        public int getType() throws SQLException
1475        {
1476            return resultSetType;
1477        }
1478    
1479        public int getConcurrency() throws SQLException
1480        {
1481            return resultSetConcurrency;
1482        }
1483        
1484        public int findColumn(String columnName) throws SQLException
1485        {
1486            for(int ii = 0; ii < columnNameList.size(); ii++)
1487            {
1488                if(columnName.equals(columnNameList.get(ii))) return ii + 1;
1489            }
1490            throw new SQLException("No column with name " + columnName + " found");
1491        }
1492    
1493        public void updateObject(int columnIndex, Object value) throws SQLException
1494        {
1495            checkColumnBounds(columnIndex);
1496            checkRowBounds();
1497            if(rowDeleted()) throw new SQLException("row was deleted");
1498            String columnName = (String)columnNameList.get(columnIndex - 1);
1499            updateObject(columnName, value);
1500        }
1501        
1502        public void updateObject(int columnIndex, Object value, int scale) throws SQLException
1503        {
1504            updateObject(columnIndex, value);
1505        }
1506        
1507        public void updateObject(String columnName, Object value, int scale) throws SQLException
1508        {
1509            updateObject(columnName, value);
1510        }
1511    
1512        public void updateObject(String columnName, Object value) throws SQLException
1513        {
1514            checkColumnName(columnName);
1515            checkRowBounds();
1516            checkResultSetConcurrency();
1517            if(rowDeleted()) throw new SQLException("row was deleted");
1518            if(isCursorInInsertRow)
1519            {
1520                List column = (List)insertRow.get(columnName);
1521                column.set(0, value);
1522            }
1523            else
1524            {
1525                List column = (List)columnMapCopy.get(columnName);
1526                column.set(cursor, value);
1527            }
1528        }
1529        
1530        public void updateString(int columnIndex, String value) throws SQLException
1531        {
1532            updateObject(columnIndex, value);
1533        }
1534    
1535        public void updateString(String columnName, String value) throws SQLException
1536        {
1537            updateObject(columnName, value);
1538        }
1539    
1540        public void updateNull(int columnIndex) throws SQLException
1541        {
1542            updateObject(columnIndex, null);
1543        }
1544        
1545        public void updateNull(String columnName) throws SQLException
1546        {
1547            updateObject(columnName, null);
1548        }
1549    
1550        public void updateBoolean(int columnIndex, boolean booleanValue) throws SQLException
1551        {
1552            updateObject(columnIndex, new Boolean(booleanValue));
1553        }
1554        
1555        public void updateBoolean(String columnName, boolean booleanValue) throws SQLException
1556        {
1557            updateObject(columnName, new Boolean(booleanValue));
1558        }
1559    
1560        public void updateByte(int columnIndex, byte byteValue) throws SQLException
1561        {
1562            updateObject(columnIndex, new Byte(byteValue));
1563        }
1564        
1565        public void updateByte(String columnName, byte byteValue) throws SQLException
1566        {
1567            updateObject(columnName, new Byte(byteValue));
1568        }
1569    
1570        public void updateShort(int columnIndex, short shortValue) throws SQLException
1571        {
1572            updateObject(columnIndex, new Short(shortValue));
1573        }
1574        
1575        public void updateShort(String columnName, short shortValue) throws SQLException
1576        {
1577            updateObject(columnName, new Short(shortValue));
1578        }
1579    
1580        public void updateInt(int columnIndex, int intValue) throws SQLException
1581        {
1582            updateObject(columnIndex, new Integer(intValue));
1583        }
1584        
1585        public void updateInt(String columnName, int intValue) throws SQLException
1586        {
1587            updateObject(columnName, new Integer(intValue));
1588        }
1589        
1590        public void updateLong(int columnIndex, long longValue) throws SQLException
1591        {
1592            updateObject(columnIndex, new Long(longValue));
1593        }
1594        
1595        public void updateLong(String columnName, long longValue) throws SQLException
1596        {
1597            updateObject(columnName, new Long(longValue));
1598        }
1599    
1600        public void updateFloat(int columnIndex, float floatValue) throws SQLException
1601        {
1602            updateObject(columnIndex, new Float(floatValue));
1603        }
1604        
1605        public void updateFloat(String columnName, float floatValue) throws SQLException
1606        {
1607            updateObject(columnName, new Float(floatValue));
1608        }
1609    
1610        public void updateDouble(int columnIndex, double doubleValue) throws SQLException
1611        {
1612            updateObject(columnIndex, new Double(doubleValue));
1613        }
1614        
1615        public void updateDouble(String columnName, double doubleValue) throws SQLException
1616        {
1617            updateObject(columnName, new Double(doubleValue));
1618        }
1619          
1620        public void updateBigDecimal(int columnIndex, BigDecimal bigDecimal) throws SQLException
1621        {
1622            updateObject(columnIndex, bigDecimal);
1623        }
1624        
1625        public void updateBigDecimal(String columnName, BigDecimal bigDecimal) throws SQLException
1626        {
1627            updateObject(columnName, bigDecimal);
1628        }
1629    
1630        public void updateBytes(int columnIndex, byte[] byteArray) throws SQLException
1631        {
1632            updateObject(columnIndex, byteArray);
1633        }
1634        
1635        public void updateBytes(String columnName, byte[] byteArray) throws SQLException
1636        {
1637            updateObject(columnName, byteArray);
1638        }
1639        
1640        public void updateDate(int columnIndex, Date date) throws SQLException
1641        {
1642            updateObject(columnIndex, date);
1643        }
1644    
1645        public void updateDate(String columnName, Date date) throws SQLException
1646        {
1647            updateObject(columnName, date);
1648        }
1649        
1650        public void updateTime(int columnIndex, Time time) throws SQLException
1651        {
1652            updateObject(columnIndex, time);
1653        }
1654    
1655        public void updateTime(String columnName, Time time) throws SQLException
1656        {
1657            updateObject(columnName, time);
1658        }
1659        
1660        public void updateTimestamp(int columnIndex, Timestamp timeStamp) throws SQLException
1661        {
1662            updateObject(columnIndex, timeStamp);
1663        }
1664    
1665        public void updateTimestamp(String columnName, Timestamp timeStamp) throws SQLException
1666        {
1667            updateObject(columnName, timeStamp);
1668        }
1669    
1670        public void updateAsciiStream(int columnIndex, InputStream stream, int length) throws SQLException
1671        {
1672            updateBinaryStream(columnIndex, stream, length);
1673        }
1674        
1675        public void updateAsciiStream(String columnName, InputStream stream, int length) throws SQLException
1676        {
1677            updateBinaryStream(columnName, stream, length);
1678        }
1679    
1680        public void updateBinaryStream(int columnIndex, InputStream stream, int length) throws SQLException
1681        {
1682            byte[] data = StreamUtil.getStreamAsByteArray(stream, length);
1683            updateObject(columnIndex, new ByteArrayInputStream(data));
1684        }
1685        
1686        public void updateBinaryStream(String columnName, InputStream stream, int length) throws SQLException
1687        {
1688            byte[] data = StreamUtil.getStreamAsByteArray(stream, length);
1689            updateObject(columnName, new ByteArrayInputStream(data));
1690        }
1691    
1692        public void updateCharacterStream(int columnIndex, Reader reader, int length) throws SQLException
1693        {
1694            String data = StreamUtil.getReaderAsString(reader, length);
1695            updateObject(columnIndex, new StringReader(data));
1696        }
1697    
1698        public void updateCharacterStream(String columnName, Reader reader, int length) throws SQLException
1699        {
1700            String data = StreamUtil.getReaderAsString(reader, length);
1701            updateObject(columnName, new StringReader(data));
1702        }
1703        
1704        public void updateRef(int columnIndex, Ref ref) throws SQLException
1705        {
1706            updateObject(columnIndex, ref);
1707        }
1708    
1709        public void updateRef(String columnName, Ref ref) throws SQLException
1710        {
1711            updateObject(columnName, ref);
1712        }
1713    
1714        public void updateBlob(int columnIndex, Blob blob) throws SQLException
1715        {
1716            updateObject(columnIndex, blob);
1717        }
1718    
1719        public void updateBlob(String columnName, Blob blob) throws SQLException
1720        {
1721            updateObject(columnName, blob);
1722        }
1723    
1724        public void updateClob(int columnIndex, Clob clob) throws SQLException
1725        {
1726            updateObject(columnIndex, clob);
1727        }
1728    
1729        public void updateClob(String columnName, Clob clob) throws SQLException
1730        {
1731            updateObject(columnName, clob);
1732        }
1733    
1734        public void updateArray(int columnIndex, Array array) throws SQLException
1735        {
1736            updateObject(columnIndex, array);
1737        }
1738    
1739        public void updateArray(String columnName, Array array) throws SQLException
1740        {
1741            updateObject(columnName, array);
1742        }
1743        
1744        public boolean rowUpdated() throws SQLException
1745        {
1746            checkRowBounds();
1747            return ((Boolean)updatedRows.get(cursor)).booleanValue();
1748        }
1749    
1750        public boolean rowInserted() throws SQLException
1751        {
1752            checkRowBounds();
1753            return ((Boolean)insertedRows.get(cursor)).booleanValue();
1754        }
1755    
1756        public boolean rowDeleted() throws SQLException
1757        {
1758            checkRowBounds();
1759            return ((Boolean)deletedRows.get(cursor)).booleanValue();
1760        }
1761        
1762        public void insertRow() throws SQLException
1763        {
1764            if(!isCursorInInsertRow) throw new SQLException("cursor is in insert row");
1765            insertRow(cursor);
1766        }
1767    
1768        public void updateRow() throws SQLException
1769        {
1770            if(isCursorInInsertRow) throw new SQLException("cursor is in insert row");
1771            if(rowDeleted()) throw new SQLException("row was deleted");
1772            checkRowBounds();
1773            updateRow(cursor, true);
1774            updatedRows.set(cursor, new Boolean(true));
1775        }
1776    
1777        public void deleteRow() throws SQLException
1778        {
1779            if(isCursorInInsertRow) throw new SQLException("cursor is in insert row");
1780            checkRowBounds();
1781            deleteRow(cursor);
1782            deletedRows.set(cursor, new Boolean(true));
1783        }
1784    
1785        public void refreshRow() throws SQLException
1786        {
1787            cancelRowUpdates();
1788        }
1789    
1790        public void cancelRowUpdates() throws SQLException
1791        {
1792            if(isCursorInInsertRow) throw new SQLException("cursor is in insert row");
1793            if(rowDeleted()) throw new SQLException("row was deleted");
1794            checkRowBounds();
1795            updateRow(cursor, false);
1796            updatedRows.set(cursor, new Boolean(false));
1797        }
1798    
1799        public void moveToInsertRow() throws SQLException
1800        {
1801            isCursorInInsertRow = true;
1802        }
1803    
1804        public void moveToCurrentRow() throws SQLException
1805        {
1806            isCursorInInsertRow = false;
1807        }
1808        
1809        private void checkColumnName(String columnName) throws SQLException
1810        {
1811            if(!columnMap.containsKey(columnName))
1812            {
1813                throw new SQLException("No column " + columnName);
1814            }
1815        }
1816        
1817        private void checkColumnBounds(int columnIndex) throws SQLException
1818        {
1819            if(!(columnIndex - 1 < columnNameList.size()))
1820            {
1821                throw new SQLException("Index " + columnIndex + " out of bounds");
1822            }
1823        }
1824        
1825        private void checkRowBounds() throws SQLException
1826        {
1827            if(!isCurrentRowValid())
1828            {
1829                throw new SQLException("Current row invalid");
1830            }
1831        }
1832        
1833        private boolean isCurrentRowValid()
1834        {
1835            return (cursor < getRowCount()) && (-1 != cursor);
1836        }
1837        
1838        private void checkResultSetType() throws SQLException
1839        {
1840            if(resultSetType == ResultSet.TYPE_FORWARD_ONLY)
1841            {
1842                throw new SQLException("ResultSet is TYPE_FORWARD_ONLY");
1843            }
1844        }
1845        
1846        private void checkResultSetConcurrency() throws SQLException
1847        {
1848            if(resultSetConcurrency == ResultSet.CONCUR_READ_ONLY)
1849            {
1850                throw new SQLException("ResultSet is CONCUR_READ_ONLY");
1851            }
1852        }
1853        
1854        private void checkFetchDirectionArguments(int fetchDirection) throws SQLException
1855        {
1856            SQLUtil.checkFetchDirection(fetchDirection);
1857            if(resultSetType == ResultSet.TYPE_FORWARD_ONLY && fetchDirection != ResultSet.FETCH_FORWARD)
1858            {
1859                throw new SQLException("resultSetType is TYPE_FORWARD_ONLY, only FETCH_FORWARD allowed");
1860            }
1861        }
1862        
1863        private void insertRow(int index)
1864        {
1865            Iterator columnNames = columnMapCopy.keySet().iterator();
1866            while(columnNames.hasNext())
1867            {
1868                String currentColumnName = (String)columnNames.next();
1869                List copyColumn = (List)columnMapCopy.get(currentColumnName);
1870                List databaseColumn = (List)columnMap.get(currentColumnName);
1871                List sourceColumn = (List)insertRow.get(currentColumnName);
1872                copyColumn.add(index, sourceColumn.get(0));
1873                databaseColumn.add(index, sourceColumn.get(0));  
1874            }
1875            updatedRows.add(index, new Boolean(false));
1876            deletedRows.add(index, new Boolean(false));
1877            insertedRows.add(index, new Boolean(true));
1878        }
1879        
1880        private void deleteRow(int index)
1881        {
1882            Iterator columnNames = columnMapCopy.keySet().iterator();
1883            while(columnNames.hasNext())
1884            {
1885                String currentColumnName = (String)columnNames.next();
1886                List copyColumn = (List)columnMapCopy.get(currentColumnName);
1887                List databaseColumn = (List)columnMap.get(currentColumnName);
1888                copyColumn.set(index, null);
1889                databaseColumn.set(index, null);
1890            }
1891        }
1892        
1893        private void updateRow(int index, boolean toDatabase)
1894        {
1895            Iterator columnNames = columnMapCopy.keySet().iterator();
1896            while(columnNames.hasNext())
1897            {
1898                String currentColumnName = (String)columnNames.next();
1899                List sourceColumn;
1900                List targetColumn;
1901                if(toDatabase)
1902                {
1903                    sourceColumn = (List)columnMapCopy.get(currentColumnName);
1904                    targetColumn = (List)columnMap.get(currentColumnName);
1905                }
1906                else
1907                {
1908                    sourceColumn = (List)columnMap.get(currentColumnName);
1909                    targetColumn = (List)columnMapCopy.get(currentColumnName);
1910                } 
1911                targetColumn.set(index, sourceColumn.get(index));
1912            }
1913        }
1914        
1915        private void adjustCursor()
1916        {
1917            if(cursor < 0) cursor = -1;
1918            if(cursor >= getRowCount()) cursor = getRowCount();
1919        }
1920        
1921        private void adjustColumns()
1922        {
1923            int rowCount = 0;
1924            Iterator columns = columnMap.values().iterator();
1925            while(columns.hasNext())
1926            {
1927                List nextColumn = (List)columns.next();
1928                rowCount = Math.max(rowCount, nextColumn.size());
1929            }
1930            columns = columnMap.values().iterator();
1931            while(columns.hasNext())
1932            {
1933                List nextColumn = (List)columns.next();
1934                CollectionUtil.fillList(nextColumn, rowCount);
1935            }
1936        }
1937        
1938        private void adjustFlags()
1939        {
1940            for(int ii = updatedRows.size(); ii < getRowCount(); ii++)
1941            {
1942                updatedRows.add(new Boolean(false));
1943            }
1944            for(int ii = deletedRows.size(); ii < getRowCount(); ii++)
1945            {
1946                deletedRows.add(new Boolean(false));
1947            }
1948            for(int ii = insertedRows.size(); ii < getRowCount(); ii++)
1949            {
1950                insertedRows.add(new Boolean(false));
1951            }
1952        }
1953        
1954        private void adjustInsertRow()
1955        {
1956            insertRow = createCaseAwareMap();
1957            Iterator columns = columnMap.keySet().iterator();
1958            while(columns.hasNext())
1959            {
1960                ArrayList list = new ArrayList(1);
1961                list.add(null);
1962                insertRow.put((String)columns.next(), list);
1963            }
1964        }
1965        
1966        private void copyColumnMap()
1967        {
1968            columnMapCopy = copyColumnDataMap(columnMap);
1969        }
1970        
1971        private String determineValidColumnName()
1972        {
1973            String name = "Column";
1974            int count = columnNameList.size() + 1;
1975            while(columnMap.containsKey(name + count))
1976            {
1977                count ++;
1978            }
1979            return name + count;
1980        }
1981        
1982        private Map copyColumnDataMap(Map columnMap)
1983        {
1984            Map copy = createCaseAwareMap();
1985            Iterator columns = columnMap.keySet().iterator();
1986            while(columns.hasNext())
1987            {
1988                List copyList = new ArrayList();
1989                String nextKey = (String)columns.next();
1990                List nextColumnList = (List)columnMap.get(nextKey);
1991                for(int ii = 0; ii < nextColumnList.size(); ii++)
1992                {
1993                    Object copyParameter = ParameterUtil.copyParameter(nextColumnList.get(ii));
1994                    copyList.add(copyParameter);
1995                }
1996                copy.put(nextKey, copyList);
1997            }
1998            return copy;
1999        }
2000        
2001        private Map createCaseAwareMap()
2002        {
2003            return new CaseAwareMap(columnsCaseSensitive);
2004        }
2005        
2006        public String toString()
2007        {
2008            StringBuffer buffer = new StringBuffer("ResultSet " + id + ":\n");
2009            buffer.append("Number of rows: " + getRowCount() + "\n");
2010            buffer.append("Number of columns: " + getColumnCount() + "\n");
2011            buffer.append("Column names:\n");
2012            StringUtil.appendObjectsAsString(buffer, columnNameList);
2013            buffer.append("Data:\n");
2014            for(int ii = 1; ii <= getRowCount(); ii++)
2015            {
2016                buffer.append("Row number " + ii + ":\n");
2017                StringUtil.appendObjectsAsString(buffer, getRow(ii));
2018            }
2019            return buffer.toString();
2020        }
2021    }