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