View Javadoc
1   /*
2    * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4    *
5    * This code is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License version 2 only, as
7    * published by the Free Software Foundation.  Oracle designates this
8    * particular file as subject to the "Classpath" exception as provided
9    * by Oracle in the LICENSE file that accompanied this code.
10   *
11   * This code is distributed in the hope that it will be useful, but WITHOUT
12   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14   * version 2 for more details (a copy is included in the LICENSE file that
15   * accompanied this code).
16   *
17   * You should have received a copy of the GNU General Public License version
18   * 2 along with this work; if not, write to the Free Software Foundation,
19   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20   *
21   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22   * or visit www.oracle.com if you need additional information or have any
23   * questions.
24   */
25  
26  package com.sun.rowset;
27  
28  import java.sql.*;
29  import javax.sql.*;
30  import java.io.*;
31  import java.math.*;
32  import java.util.*;
33  import java.text.*;
34  
35  import javax.sql.rowset.*;
36  import javax.sql.rowset.spi.*;
37  import javax.sql.rowset.serial.*;
38  import com.sun.rowset.internal.*;
39  import com.sun.rowset.providers.*;
40  
41  /**
42   * The standard implementation of the <code>CachedRowSet</code> interface.
43   *
44   * See interface definition for full behavior and implementation requirements.
45   * This reference implementation has made provision for a one-to-one write back
46   * facility and it is curremtly be possible to change the peristence provider
47   * during the life-time of any CachedRowSetImpl.
48   *
49   * @author Jonathan Bruce, Amit Handa
50   */
51  
52  public class CachedRowSetImpl extends BaseRowSet implements RowSet, RowSetInternal, Serializable, Cloneable, CachedRowSet {
53  
54      /**
55       * The <code>SyncProvider</code> used by the CachedRowSet
56       */
57      private SyncProvider provider;
58  
59      /**
60       * The <code>RowSetReaderImpl</code> object that is the reader
61       * for this rowset.  The method <code>execute</code> uses this
62       * reader as part of its implementation.
63       * @serial
64       */
65      private RowSetReader rowSetReader;
66  
67      /**
68       * The <code>RowSetWriterImpl</code> object that is the writer
69       * for this rowset.  The method <code>acceptChanges</code> uses
70       * this writer as part of its implementation.
71       * @serial
72       */
73      private RowSetWriter rowSetWriter;
74  
75      /**
76       * The <code>Connection</code> object that connects with this
77       * <code>CachedRowSetImpl</code> object's current underlying data source.
78       */
79      private transient Connection conn;
80  
81      /**
82       * The <code>ResultSetMetaData</code> object that contains information
83       * about the columns in the <code>ResultSet</code> object that is the
84       * current source of data for this <code>CachedRowSetImpl</code> object.
85       */
86      private transient ResultSetMetaData RSMD;
87  
88      /**
89       * The <code>RowSetMetaData</code> object that contains information about
90       * the columns in this <code>CachedRowSetImpl</code> object.
91       * @serial
92       */
93      private RowSetMetaDataImpl RowSetMD;
94  
95      // Properties of this RowSet
96  
97      /**
98       * An array containing the columns in this <code>CachedRowSetImpl</code>
99       * object that form a unique identifier for a row. This array
100      * is used by the writer.
101      * @serial
102      */
103     private int keyCols[];
104 
105     /**
106      * The name of the table in the underlying database to which updates
107      * should be written.  This name is needed because most drivers
108      * do not return this information in a <code>ResultSetMetaData</code>
109      * object.
110      * @serial
111      */
112     private String tableName;
113 
114     /**
115      * A <code>Vector</code> object containing the <code>Row</code>
116      * objects that comprise  this <code>CachedRowSetImpl</code> object.
117      * @serial
118      */
119     private Vector<Object> rvh;
120 
121     /**
122      * The current position of the cursor in this <code>CachedRowSetImpl</code>
123      * object.
124      * @serial
125      */
126     private int cursorPos;
127 
128     /**
129      * The current position of the cursor in this <code>CachedRowSetImpl</code>
130      * object not counting rows that have been deleted, if any.
131      * <P>
132      * For example, suppose that the cursor is on the last row of a rowset
133      * that started with five rows and subsequently had the second and third
134      * rows deleted. The <code>absolutePos</code> would be <code>3</code>,
135      * whereas the <code>cursorPos</code> would be <code>5</code>.
136      * @serial
137      */
138     private int absolutePos;
139 
140     /**
141      * The number of deleted rows currently in this <code>CachedRowSetImpl</code>
142      * object.
143      * @serial
144      */
145     private int numDeleted;
146 
147     /**
148      * The total number of rows currently in this <code>CachedRowSetImpl</code>
149      * object.
150      * @serial
151      */
152     private int numRows;
153 
154     /**
155      * A special row used for constructing a new row. A new
156      * row is constructed by using <code>ResultSet.updateXXX</code>
157      * methods to insert column values into the insert row.
158      * @serial
159      */
160     private InsertRow insertRow;
161 
162     /**
163      * A <code>boolean</code> indicating whether the cursor is
164      * currently on the insert row.
165      * @serial
166      */
167     private boolean onInsertRow;
168 
169     /**
170      * The field that temporarily holds the last position of the
171      * cursor before it moved to the insert row, thus preserving
172      * the number of the current row to which the cursor may return.
173      * @serial
174      */
175     private int currentRow;
176 
177     /**
178      * A <code>boolean</code> indicating whether the last value
179      * returned was an SQL <code>NULL</code>.
180      * @serial
181      */
182     private boolean lastValueNull;
183 
184     /**
185      * A <code>SQLWarning</code> which logs on the warnings
186      */
187     private SQLWarning sqlwarn;
188 
189     /**
190      * Used to track match column for JoinRowSet consumption
191      */
192     private String strMatchColumn ="";
193 
194     /**
195      * Used to track match column for JoinRowSet consumption
196      */
197     private int iMatchColumn = -1;
198 
199     /**
200      * A <code>RowSetWarning</code> which logs on the warnings
201      */
202     private RowSetWarning rowsetWarning;
203 
204     /**
205      * The default SyncProvider for the RI CachedRowSetImpl
206      */
207     private String DEFAULT_SYNC_PROVIDER = "com.sun.rowset.providers.RIOptimisticProvider";
208 
209     /**
210      * The boolean variable indicating locatorsUpdateValue
211      */
212     private boolean dbmslocatorsUpdateCopy;
213 
214     /**
215      * The <code>ResultSet</code> object that is used to maintain the data when
216      * a ResultSet and start position are passed as parameters to the populate function
217      */
218     private transient ResultSet resultSet;
219 
220     /**
221      * The integer value indicating the end position in the ResultSetwhere the picking
222      * up of rows for populating a CachedRowSet object was left off.
223      */
224     private int endPos;
225 
226     /**
227      * The integer value indicating the end position in the ResultSetwhere the picking
228      * up of rows for populating a CachedRowSet object was left off.
229      */
230     private int prevEndPos;
231 
232     /**
233      * The integer value indicating the position in the ResultSet, to populate the
234      * CachedRowSet object.
235      */
236     private int startPos;
237 
238     /**
239      * The integer value indicating the position from where the page prior to this
240      * was populated.
241      */
242     private int startPrev;
243 
244     /**
245      * The integer value indicating size of the page.
246      */
247     private int pageSize;
248 
249     /**
250      * The integer value indicating number of rows that have been processed so far.
251      * Used for checking whether maxRows has been reached or not.
252      */
253     private int maxRowsreached;
254     /**
255      * The boolean value when true signifies that pages are still to follow and a
256      * false value indicates that this is the last page.
257      */
258     private boolean pagenotend = true;
259 
260     /**
261      * The boolean value indicating whether this is the first page or not.
262      */
263     private boolean onFirstPage;
264 
265     /**
266      * The boolean value indicating whether this is the last page or not.
267      */
268     private boolean onLastPage;
269 
270     /**
271      * The integer value indicating how many times the populate function has been called.
272      */
273     private int populatecallcount;
274 
275     /**
276      * The integer value indicating the total number of rows to be processed in the
277      * ResultSet object passed to the populate function.
278      */
279     private int totalRows;
280 
281     /**
282      * The boolean value indicating how the CahedRowSet object has been populated for
283      * paging purpose. True indicates that connection parameter is passed.
284      */
285     private boolean callWithCon;
286 
287     /**
288      * CachedRowSet reader object to read the data from the ResultSet when a connection
289      * parameter is passed to populate the CachedRowSet object for paging.
290      */
291     private CachedRowSetReader crsReader;
292 
293     /**
294      * The Vector holding the Match Columns
295      */
296     private Vector<Integer> iMatchColumns;
297 
298     /**
299      * The Vector that will hold the Match Column names.
300      */
301     private Vector<String> strMatchColumns;
302 
303     /**
304      * Trigger that indicates whether the active SyncProvider is exposes the
305      * additional TransactionalWriter method
306      */
307     private boolean tXWriter = false;
308 
309     /**
310      * The field object for a transactional RowSet writer
311      */
312     private TransactionalWriter tWriter = null;
313 
314     protected transient JdbcRowSetResourceBundle resBundle;
315 
316     private boolean updateOnInsert;
317 
318 
319 
320     /**
321      * Constructs a new default <code>CachedRowSetImpl</code> object with
322      * the capacity to hold 100 rows. This new object has no metadata
323      * and has the following default values:
324      * <pre>
325      *     onInsertRow = false
326      *     insertRow = null
327      *     cursorPos = 0
328      *     numRows = 0
329      *     showDeleted = false
330      *     queryTimeout = 0
331      *     maxRows = 0
332      *     maxFieldSize = 0
333      *     rowSetType = ResultSet.TYPE_SCROLL_INSENSITIVE
334      *     concurrency = ResultSet.CONCUR_UPDATABLE
335      *     readOnly = false
336      *     isolation = Connection.TRANSACTION_READ_COMMITTED
337      *     escapeProcessing = true
338      *     onInsertRow = false
339      *     insertRow = null
340      *     cursorPos = 0
341      *     absolutePos = 0
342      *     numRows = 0
343      * </pre>
344      * A <code>CachedRowSetImpl</code> object is configured to use the default
345      * <code>RIOptimisticProvider</code> implementation to provide connectivity
346      * and synchronization capabilities to the set data source.
347      * <P>
348      * @throws SQLException if an error occurs
349      */
350     public CachedRowSetImpl() throws SQLException {
351 
352         try {
353            resBundle = JdbcRowSetResourceBundle.getJdbcRowSetResourceBundle();
354         } catch(IOException ioe) {
355             throw new RuntimeException(ioe);
356         }
357 
358         // set the Reader, this maybe overridden latter
359         provider =
360         SyncFactory.getInstance(DEFAULT_SYNC_PROVIDER);
361 
362         if (!(provider instanceof RIOptimisticProvider)) {
363             throw new SQLException(resBundle.handleGetObject("cachedrowsetimpl.invalidp").toString());
364         }
365 
366         rowSetReader = (CachedRowSetReader)provider.getRowSetReader();
367         rowSetWriter = (CachedRowSetWriter)provider.getRowSetWriter();
368 
369         // allocate the parameters collection
370         initParams();
371 
372         initContainer();
373 
374         // set up some default values
375         initProperties();
376 
377         // insert row setup
378         onInsertRow = false;
379         insertRow = null;
380 
381         // set the warninings
382         sqlwarn = new SQLWarning();
383         rowsetWarning = new RowSetWarning();
384 
385     }
386 
387     /**
388      * Provides a <code>CachedRowSetImpl</code> instance with the same default properties as
389      * as the zero parameter constructor.
390      * <pre>
391      *     onInsertRow = false
392      *     insertRow = null
393      *     cursorPos = 0
394      *     numRows = 0
395      *     showDeleted = false
396      *     queryTimeout = 0
397      *     maxRows = 0
398      *     maxFieldSize = 0
399      *     rowSetType = ResultSet.TYPE_SCROLL_INSENSITIVE
400      *     concurrency = ResultSet.CONCUR_UPDATABLE
401      *     readOnly = false
402      *     isolation = Connection.TRANSACTION_READ_COMMITTED
403      *     escapeProcessing = true
404      *     onInsertRow = false
405      *     insertRow = null
406      *     cursorPos = 0
407      *     absolutePos = 0
408      *     numRows = 0
409      * </pre>
410      *
411      * However, applications will have the means to specify at runtime the
412      * desired <code>SyncProvider</code> object.
413      * <p>
414      * For example, creating a <code>CachedRowSetImpl</code> object as follows ensures
415      * that a it is established with the <code>com.foo.provider.Impl</code> synchronization
416      * implementation providing the synchronization mechanism for this disconnected
417      * <code>RowSet</code> object.
418      * <pre>
419      *     Hashtable env = new Hashtable();
420      *     env.put(javax.sql.rowset.spi.SyncFactory.ROWSET_PROVIDER_NAME,
421      *         "com.foo.provider.Impl");
422      *     CachedRowSetImpl crs = new CachedRowSet(env);
423      * </pre>
424      * <p>
425      * Calling this constructor with a <code>null</code> parameter will
426      * cause the <code>SyncFactory</code> to provide the reference
427      * optimistic provider <code>com.sun.rowset.providers.RIOptimisticProvider</code>.
428      * <p>
429      * In addition, the following properties can be associated with the
430      * provider to assist in determining the choice of the synchronizaton
431      * provider such as:
432      * <ul>
433      * <li><code>ROWSET_SYNC_PROVIDER</code> - the property specifying the the
434      * <code>SyncProvider</code> class name to be instantiated by the
435      * <code>SyncFacttory</code>
436      * <li><code>ROWSET_SYNC_VENDOR</code> - the property specifying the software
437      * vendor associated with a <code>SyncProvider</code> implementation.
438      * <li><code>ROWSET_SYNC_PROVIDER_VER</code> - the property specifying the
439      * version of the <code>SyncProvider</code> implementation provided by the
440      * software vendor.
441      * </ul>
442      * More specific detailes are available in the <code>SyncFactory</code>
443      * and <code>SyncProvider</code> specificiations later in this document.
444      * <p>
445      * @param env a <code>Hashtable</code> object with a list of desired
446      *        synchronization providers
447      * @throws SQLException if the requested provider cannot be found by the
448      * synchronization factory
449      * @see SyncProvider
450      */
451     public CachedRowSetImpl(@SuppressWarnings("rawtypes") Hashtable env) throws SQLException {
452 
453 
454         try {
455            resBundle = JdbcRowSetResourceBundle.getJdbcRowSetResourceBundle();
456         } catch(IOException ioe) {
457             throw new RuntimeException(ioe);
458         }
459 
460         if (env == null) {
461             throw new SQLException(resBundle.handleGetObject("cachedrowsetimpl.nullhash").toString());
462         }
463 
464         String providerName = (String)env.get(
465         javax.sql.rowset.spi.SyncFactory.ROWSET_SYNC_PROVIDER);
466 
467         // set the Reader, this maybe overridden latter
468         provider =
469         SyncFactory.getInstance(providerName);
470 
471         rowSetReader = provider.getRowSetReader();
472         rowSetWriter = provider.getRowSetWriter();
473 
474         initParams(); // allocate the parameters collection
475         initContainer();
476         initProperties(); // set up some default values
477     }
478 
479     /**
480      * Sets the <code>rvh</code> field to a new <code>Vector</code>
481      * object with a capacity of 100 and sets the
482      * <code>cursorPos</code> and <code>numRows</code> fields to zero.
483      */
484     private void initContainer() {
485 
486         rvh = new Vector<Object>(100);
487         cursorPos = 0;
488         absolutePos = 0;
489         numRows = 0;
490         numDeleted = 0;
491     }
492 
493     /**
494      * Sets the properties for this <code>CachedRowSetImpl</code> object to
495      * their default values. This method is called internally by the
496      * default constructor.
497      */
498 
499     private void initProperties() throws SQLException {
500 
501         if(resBundle == null) {
502             try {
503                resBundle = JdbcRowSetResourceBundle.getJdbcRowSetResourceBundle();
504             } catch(IOException ioe) {
505                 throw new RuntimeException(ioe);
506             }
507         }
508         setShowDeleted(false);
509         setQueryTimeout(0);
510         setMaxRows(0);
511         setMaxFieldSize(0);
512         setType(ResultSet.TYPE_SCROLL_INSENSITIVE);
513         setConcurrency(ResultSet.CONCUR_UPDATABLE);
514         if((rvh.size() > 0) && (isReadOnly() == false))
515             setReadOnly(false);
516         else
517             setReadOnly(true);
518         setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
519         setEscapeProcessing(true);
520         //setTypeMap(null);
521         checkTransactionalWriter();
522 
523         //Instantiating the vector for MatchColumns
524 
525         iMatchColumns = new Vector<Integer>(10);
526         for(int i = 0; i < 10 ; i++) {
527            iMatchColumns.add(i, -1);
528         }
529 
530         strMatchColumns = new Vector<String>(10);
531         for(int j = 0; j < 10; j++) {
532            strMatchColumns.add(j,null);
533         }
534     }
535 
536     /**
537      * Determine whether the SyncProvider's writer implements the
538      * <code>TransactionalWriter<code> interface
539      */
540     private void checkTransactionalWriter() {
541         if (rowSetWriter != null) {
542             Class<?> c = rowSetWriter.getClass();
543             if (c != null) {
544                 Class<?>[] theInterfaces = c.getInterfaces();
545                 for (int i = 0; i < theInterfaces.length; i++) {
546                     if ((theInterfaces[i].getName()).indexOf("TransactionalWriter") > 0) {
547                         tXWriter = true;
548                         establishTransactionalWriter();
549                     }
550                 }
551             }
552         }
553     }
554 
555     /**
556      * Sets an private field to all transaction bounddaries to be set
557      */
558     private void establishTransactionalWriter() {
559         tWriter = (TransactionalWriter)provider.getRowSetWriter();
560     }
561 
562     //-----------------------------------------------------------------------
563     // Properties
564     //-----------------------------------------------------------------------
565 
566     /**
567      * Sets this <code>CachedRowSetImpl</code> object's command property
568      * to the given <code>String</code> object and clears the parameters,
569      * if any, that were set for the previous command.
570      * <P>
571      * The command property may not be needed
572      * if the rowset is produced by a data source, such as a spreadsheet,
573      * that does not support commands. Thus, this property is optional
574      * and may be <code>null</code>.
575      *
576      * @param cmd a <code>String</code> object containing an SQL query
577      *            that will be set as the command; may be <code>null</code>
578      * @throws SQLException if an error occurs
579      */
580     public void setCommand(String cmd) throws SQLException {
581 
582         super.setCommand(cmd);
583 
584         if(!buildTableName(cmd).equals("")) {
585             this.setTableName(buildTableName(cmd));
586         }
587     }
588 
589 
590     //---------------------------------------------------------------------
591     // Reading and writing data
592     //---------------------------------------------------------------------
593 
594     /**
595      * Populates this <code>CachedRowSetImpl</code> object with data from
596      * the given <code>ResultSet</code> object.  This
597      * method is an alternative to the method <code>execute</code>
598      * for filling the rowset with data.  The method <code>populate</code>
599      * does not require that the properties needed by the method
600      * <code>execute</code>, such as the <code>command</code> property,
601      * be set. This is true because the method <code>populate</code>
602      * is given the <code>ResultSet</code> object from
603      * which to get data and thus does not need to use the properties
604      * required for setting up a connection and executing this
605      * <code>CachedRowSetImpl</code> object's command.
606      * <P>
607      * After populating this rowset with data, the method
608      * <code>populate</code> sets the rowset's metadata and
609      * then sends a <code>RowSetChangedEvent</code> object
610      * to all registered listeners prior to returning.
611      *
612      * @param data the <code>ResultSet</code> object containing the data
613      *             to be read into this <code>CachedRowSetImpl</code> object
614      * @throws SQLException if an error occurs; or the max row setting is
615      *          violated while populating the RowSet
616      * @see #execute
617      */
618 
619      public void populate(ResultSet data) throws SQLException {
620         int rowsFetched;
621         Row currentRow;
622         int numCols;
623         int i;
624         Map<String, Class<?>> map = getTypeMap();
625         Object obj;
626         int mRows;
627 
628         if (data == null) {
629             throw new SQLException(resBundle.handleGetObject("cachedrowsetimpl.populate").toString());
630         }
631         this.resultSet = data;
632 
633         // get the meta data for this ResultSet
634         RSMD = data.getMetaData();
635 
636         // set up the metadata
637         RowSetMD = new RowSetMetaDataImpl();
638         initMetaData(RowSetMD, RSMD);
639 
640         // release the meta-data so that aren't tempted to use it.
641         RSMD = null;
642         numCols = RowSetMD.getColumnCount();
643         mRows = this.getMaxRows();
644         rowsFetched = 0;
645         currentRow = null;
646 
647         while ( data.next()) {
648 
649             currentRow = new Row(numCols);
650 
651             if ( rowsFetched > mRows && mRows > 0) {
652                 rowsetWarning.setNextWarning(new RowSetWarning("Populating rows "
653                 + "setting has exceeded max row setting"));
654             }
655             for ( i = 1; i <= numCols; i++) {
656                 /*
657                  * check if the user has set a map. If no map
658                  * is set then use plain getObject. This lets
659                  * us work with drivers that do not support
660                  * getObject with a map in fairly sensible way
661                  */
662                 if (map == null || map.isEmpty()) {
663                     obj = data.getObject(i);
664                 } else {
665                     obj = data.getObject(i, map);
666                 }
667                 /*
668                  * the following block checks for the various
669                  * types that we have to serialize in order to
670                  * store - right now only structs have been tested
671                  */
672                 if (obj instanceof Struct) {
673                     obj = new SerialStruct((Struct)obj, map);
674                 } else if (obj instanceof SQLData) {
675                     obj = new SerialStruct((SQLData)obj, map);
676                 } else if (obj instanceof Blob) {
677                     obj = new SerialBlob((Blob)obj);
678                 } else if (obj instanceof Clob) {
679                     obj = new SerialClob((Clob)obj);
680                 } else if (obj instanceof java.sql.Array) {
681                     if(map != null)
682                         obj = new SerialArray((java.sql.Array)obj, map);
683                     else
684                         obj = new SerialArray((java.sql.Array)obj);
685                 }
686 
687                 currentRow.initColumnObject(i, obj);
688             }
689             rowsFetched++;
690             rvh.add(currentRow);
691         }
692 
693         numRows = rowsFetched ;
694         // Also rowsFetched should be equal to rvh.size()
695 
696         // notify any listeners that the rowset has changed
697         notifyRowSetChanged();
698 
699 
700     }
701 
702     /**
703      * Initializes the given <code>RowSetMetaData</code> object with the values
704      * in the given <code>ResultSetMetaData</code> object.
705      *
706      * @param md the <code>RowSetMetaData</code> object for this
707      *           <code>CachedRowSetImpl</code> object, which will be set with
708      *           values from rsmd
709      * @param rsmd the <code>ResultSetMetaData</code> object from which new
710      *             values for md will be read
711      * @throws SQLException if an error occurs
712      */
713     private void initMetaData(RowSetMetaDataImpl md, ResultSetMetaData rsmd) throws SQLException {
714         int numCols = rsmd.getColumnCount();
715 
716         md.setColumnCount(numCols);
717         for (int col=1; col <= numCols; col++) {
718             md.setAutoIncrement(col, rsmd.isAutoIncrement(col));
719             if(rsmd.isAutoIncrement(col))
720                 updateOnInsert = true;
721             md.setCaseSensitive(col, rsmd.isCaseSensitive(col));
722             md.setCurrency(col, rsmd.isCurrency(col));
723             md.setNullable(col, rsmd.isNullable(col));
724             md.setSigned(col, rsmd.isSigned(col));
725             md.setSearchable(col, rsmd.isSearchable(col));
726              /*
727              * The PostgreSQL drivers sometimes return negative columnDisplaySize,
728              * which causes an exception to be thrown.  Check for it.
729              */
730             int size = rsmd.getColumnDisplaySize(col);
731             if (size < 0) {
732                 size = 0;
733             }
734             md.setColumnDisplaySize(col, size);
735             md.setColumnLabel(col, rsmd.getColumnLabel(col));
736             md.setColumnName(col, rsmd.getColumnName(col));
737             md.setSchemaName(col, rsmd.getSchemaName(col));
738             /*
739              * Drivers return some strange values for precision, for non-numeric data, including reports of
740              * non-integer values; maybe we should check type, & set to 0 for non-numeric types.
741              */
742             int precision = rsmd.getPrecision(col);
743             if (precision < 0) {
744                 precision = 0;
745             }
746             md.setPrecision(col, precision);
747 
748             /*
749              * It seems, from a bug report, that a driver can sometimes return a negative
750              * value for scale.  javax.sql.rowset.RowSetMetaDataImpl will throw an exception
751              * if we attempt to set a negative value.  As such, we'll check for this case.
752              */
753             int scale = rsmd.getScale(col);
754             if (scale < 0) {
755                 scale = 0;
756             }
757             md.setScale(col, scale);
758             md.setTableName(col, rsmd.getTableName(col));
759             md.setCatalogName(col, rsmd.getCatalogName(col));
760             md.setColumnType(col, rsmd.getColumnType(col));
761             md.setColumnTypeName(col, rsmd.getColumnTypeName(col));
762         }
763 
764         if( conn != null){
765            // JDBC 4.0 mandates as does the Java EE spec that all DataBaseMetaData methods
766            // must be implemented, therefore, the previous fix for 5055528 is being backed out
767             dbmslocatorsUpdateCopy = conn.getMetaData().locatorsUpdateCopy();
768         }
769     }
770 
771     /**
772      * Populates this <code>CachedRowSetImpl</code> object with data,
773      * using the given connection to produce the result set from
774      * which data will be read.  A second form of this method,
775      * which takes no arguments, uses the values from this rowset's
776      * user, password, and either url or data source properties to
777      * create a new database connection. The form of <code>execute</code>
778      * that is given a connection ignores these properties.
779      *
780      * @param conn A standard JDBC <code>Connection</code> object that this
781      * <code>CachedRowSet</code> object can pass to a synchronization provider
782      * to establish a connection to the data source
783      * @throws SQLException if an invalid <code>Connection</code> is supplied
784      *           or an error occurs in establishing the connection to the
785      *           data source
786      * @see #populate
787      * @see java.sql.Connection
788      */
789     public void execute(Connection conn) throws SQLException {
790         // store the connection so the reader can find it.
791         setConnection(conn);
792 
793         if(getPageSize() != 0){
794             crsReader = (CachedRowSetReader)provider.getRowSetReader();
795             crsReader.setStartPosition(1);
796             callWithCon = true;
797             crsReader.readData((RowSetInternal)this);
798         }
799 
800         // Now call the current reader's readData method
801         else {
802            rowSetReader.readData((RowSetInternal)this);
803         }
804         RowSetMD = (RowSetMetaDataImpl)this.getMetaData();
805 
806         if(conn != null){
807             // JDBC 4.0 mandates as does the Java EE spec that all DataBaseMetaData methods
808             // must be implemented, therefore, the previous fix for 5055528 is being backed out
809             dbmslocatorsUpdateCopy = conn.getMetaData().locatorsUpdateCopy();
810         }
811 
812     }
813 
814     /**
815      * Sets this <code>CachedRowSetImpl</code> object's connection property
816      * to the given <code>Connection</code> object.  This method is called
817      * internally by the version of the method <code>execute</code> that takes a
818      * <code>Connection</code> object as an argument. The reader for this
819      * <code>CachedRowSetImpl</code> object can retrieve the connection stored
820      * in the rowset's connection property by calling its
821      * <code>getConnection</code> method.
822      *
823      * @param connection the <code>Connection</code> object that was passed in
824      *                   to the method <code>execute</code> and is to be stored
825      *                   in this <code>CachedRowSetImpl</code> object's connection
826      *                   property
827      */
828     private void setConnection (Connection connection) {
829         conn = connection;
830     }
831 
832 
833     /**
834      * Propagates all row update, insert, and delete changes to the
835      * underlying data source backing this <code>CachedRowSetImpl</code>
836      * object.
837      * <P>
838      * <b>Note</b>In the reference implementation an optimistic concurrency implementation
839      * is provided as a sample implementation of a the <code>SyncProvider</code>
840      * abstract class.
841      * <P>
842      * This method fails if any of the updates cannot be propagated back
843      * to the data source.  When it fails, the caller can assume that
844      * none of the updates are reflected in the data source.
845      * When an exception is thrown, the current row
846      * is set to the first "updated" row that resulted in an exception
847      * unless the row that caused the exception is a "deleted" row.
848      * In that case, when deleted rows are not shown, which is usually true,
849      * the current row is not affected.
850      * <P>
851      * If no <code>SyncProvider</code> is configured, the reference implementation
852      * leverages the <code>RIOptimisticProvider</code> available which provides the
853      * default and reference synchronization capabilities for disconnected
854      * <code>RowSets</code>.
855      *
856      * @throws SQLException if the cursor is on the insert row or the underlying
857      *          reference synchronization provider fails to commit the updates
858      *          to the datasource
859      * @throws SyncProviderException if an internal error occurs within the
860      *          <code>SyncProvider</code> instance during either during the
861      *          process or at any time when the <code>SyncProvider</code>
862      *          instance touches the data source.
863      * @see #acceptChanges(java.sql.Connection)
864      * @see javax.sql.RowSetWriter
865      * @see javax.sql.rowset.spi.SyncProvider
866      */
867     public void acceptChanges() throws SyncProviderException {
868         if (onInsertRow == true) {
869             throw new SyncProviderException(resBundle.handleGetObject("cachedrowsetimpl.invalidop").toString());
870         }
871 
872         int saveCursorPos = cursorPos;
873         boolean success = false;
874         boolean conflict = false;
875 
876         try {
877             if (rowSetWriter != null) {
878                 saveCursorPos = cursorPos;
879                 conflict = rowSetWriter.writeData((RowSetInternal)this);
880                 cursorPos = saveCursorPos;
881             }
882 
883             if (tXWriter) {
884                 // do commit/rollback's here
885                 if (!conflict) {
886                     tWriter = (TransactionalWriter)rowSetWriter;
887                     tWriter.rollback();
888                     success = false;
889                 } else {
890                     tWriter = (TransactionalWriter)rowSetWriter;
891                     if (tWriter instanceof CachedRowSetWriter) {
892                         ((CachedRowSetWriter)tWriter).commit(this, updateOnInsert);
893                     } else {
894                         tWriter.commit();
895                     }
896 
897                     success = true;
898                 }
899             }
900 
901             if (success == true) {
902                 setOriginal();
903             } else if (!(success) ) {
904                 throw new SyncProviderException(resBundle.handleGetObject("cachedrowsetimpl.accfailed").toString());
905             }
906 
907         } catch (SyncProviderException spe) {
908                throw spe;
909         } catch (SQLException e) {
910             e.printStackTrace();
911             throw new SyncProviderException(e.getMessage());
912         } catch (SecurityException e) {
913             throw new SyncProviderException(e.getMessage());
914         }
915     }
916 
917     /**
918      * Propagates all row update, insert, and delete changes to the
919      * data source backing this <code>CachedRowSetImpl</code> object
920      * using the given <code>Connection</code> object.
921      * <P>
922      * The reference implementation <code>RIOptimisticProvider</code>
923      * modifies its synchronization to a write back function given
924      * the updated connection
925      * The reference implementation modifies its synchronization behaviour
926      * via the <code>SyncProvider</code> to ensure the synchronization
927      * occurs according to the updated JDBC <code>Connection</code>
928      * properties.
929      *
930      * @param con a standard JDBC <code>Connection</code> object
931      * @throws SQLException if the cursor is on the insert row or the underlying
932      *                   synchronization provider fails to commit the updates
933      *                   back to the data source
934      * @see #acceptChanges
935      * @see javax.sql.RowSetWriter
936      * @see javax.sql.rowset.spi.SyncFactory
937      * @see javax.sql.rowset.spi.SyncProvider
938      */
939     public void acceptChanges(Connection con) throws SyncProviderException{
940       setConnection(con);
941       acceptChanges();
942     }
943 
944     /**
945      * Restores this <code>CachedRowSetImpl</code> object to its original state,
946      * that is, its state before the last set of changes.
947      * <P>
948      * Before returning, this method moves the cursor before the first row
949      * and sends a <code>rowSetChanged</code> event to all registered
950      * listeners.
951      * @throws SQLException if an error is occurs rolling back the RowSet
952      *           state to the definied original value.
953      * @see javax.sql.RowSetListener#rowSetChanged
954      */
955     public void restoreOriginal() throws SQLException {
956         Row currentRow;
957         for (Iterator<?> i = rvh.iterator(); i.hasNext();) {
958             currentRow = (Row)i.next();
959             if (currentRow.getInserted() == true) {
960                 i.remove();
961                 --numRows;
962             } else {
963                 if (currentRow.getDeleted() == true) {
964                     currentRow.clearDeleted();
965                 }
966                 if (currentRow.getUpdated() == true) {
967                     currentRow.clearUpdated();
968                 }
969             }
970         }
971         // move to before the first
972         cursorPos = 0;
973 
974         // notify any listeners
975         notifyRowSetChanged();
976     }
977 
978     /**
979      * Releases the current contents of this <code>CachedRowSetImpl</code>
980      * object and sends a <code>rowSetChanged</code> event object to all
981      * registered listeners.
982      *
983      * @throws SQLException if an error occurs flushing the contents of
984      *           RowSet.
985      * @see javax.sql.RowSetListener#rowSetChanged
986      */
987     public void release() throws SQLException {
988         initContainer();
989         notifyRowSetChanged();
990     }
991 
992     /**
993      * Cancels deletion of the current row and notifies listeners that
994      * a row has changed.
995      * <P>
996      * Note:  This method can be ignored if deleted rows are not being shown,
997      * which is the normal case.
998      *
999      * @throws SQLException if the cursor is not on a valid row
1000      */
1001     public void undoDelete() throws SQLException {
1002         if (getShowDeleted() == false) {
1003             return;
1004         }
1005         // make sure we are on a row
1006         checkCursor();
1007 
1008         // don't want this to happen...
1009         if (onInsertRow == true) {
1010             throw new SQLException(resBundle.handleGetObject("cachedrowsetimpl.invalidcp").toString());
1011         }
1012 
1013         Row currentRow = (Row)getCurrentRow();
1014         if (currentRow.getDeleted() == true) {
1015             currentRow.clearDeleted();
1016             --numDeleted;
1017             notifyRowChanged();
1018         }
1019     }
1020 
1021     /**
1022      * Immediately removes the current row from this
1023      * <code>CachedRowSetImpl</code> object if the row has been inserted, and
1024      * also notifies listeners the a row has changed.  An exception is thrown
1025      * if the row is not a row that has been inserted or the cursor is before
1026      * the first row, after the last row, or on the insert row.
1027      * <P>
1028      * This operation cannot be undone.
1029      *
1030      * @throws SQLException if an error occurs,
1031      *                         the cursor is not on a valid row,
1032      *                         or the row has not been inserted
1033      */
1034     public void undoInsert() throws SQLException {
1035         // make sure we are on a row
1036         checkCursor();
1037 
1038         // don't want this to happen...
1039         if (onInsertRow == true) {
1040             throw new SQLException(resBundle.handleGetObject("cachedrowsetimpl.invalidcp").toString());
1041         }
1042 
1043         Row currentRow = (Row)getCurrentRow();
1044         if (currentRow.getInserted() == true) {
1045             rvh.remove(cursorPos-1);
1046             --numRows;
1047             notifyRowChanged();
1048         } else {
1049             throw new SQLException(resBundle.handleGetObject("cachedrowsetimpl.illegalop").toString());
1050         }
1051     }
1052 
1053     /**
1054      * Immediately reverses the last update operation if the
1055      * row has been modified. This method can be
1056      * called to reverse updates on a all columns until all updates in a row have
1057      * been rolled back to their originating state since the last synchronization
1058      * (<code>acceptChanges</code>) or population. This method may also be called
1059      * while performing updates to the insert row.
1060      * <P>
1061      * <code>undoUpdate</code may be called at any time during the life-time of a
1062      * rowset, however after a synchronization has occurs this method has no
1063      * affect until further modification to the RowSet data occurs.
1064      *
1065      * @throws SQLException if cursor is before the first row, after the last
1066      *     row in rowset.
1067      * @see #undoDelete
1068      * @see #undoInsert
1069      * @see java.sql.ResultSet#cancelRowUpdates
1070      */
1071     public void undoUpdate() throws SQLException {
1072         // if on insert row, cancel the insert row
1073         // make the insert row flag,
1074         // cursorPos back to the current row
1075         moveToCurrentRow();
1076 
1077         // else if not on insert row
1078         // call undoUpdate or undoInsert
1079         undoDelete();
1080 
1081         undoInsert();
1082 
1083     }
1084 
1085     //--------------------------------------------------------------------
1086     // Views
1087     //--------------------------------------------------------------------
1088 
1089     /**
1090      * Returns a new <code>RowSet</code> object backed by the same data as
1091      * that of this <code>CachedRowSetImpl</code> object and sharing a set of cursors
1092      * with it. This allows cursors to interate over a shared set of rows, providing
1093      * multiple views of the underlying data.
1094      *
1095      * @return a <code>RowSet</code> object that is a copy of this <code>CachedRowSetImpl</code>
1096      * object and shares a set of cursors with it
1097      * @throws SQLException if an error occurs or cloning is
1098      *                         not supported
1099      * @see javax.sql.RowSetEvent
1100      * @see javax.sql.RowSetListener
1101      */
1102     public RowSet createShared() throws SQLException {
1103         RowSet clone;
1104         try {
1105             clone = (RowSet)clone();
1106         } catch (CloneNotSupportedException ex) {
1107             throw new SQLException(ex.getMessage());
1108         }
1109         return clone;
1110     }
1111 
1112     /**
1113      * Returns a new <code>RowSet</code> object containing by the same data
1114      * as this <code>CachedRowSetImpl</code> object.  This method
1115      * differs from the method <code>createCopy</code> in that it throws a
1116      * <code>CloneNotSupportedException</code> object instead of an
1117      * <code>SQLException</code> object, as the method <code>createShared</code>
1118      * does.  This <code>clone</code>
1119      * method is called internally by the method <code>createShared</code>,
1120      * which catches the <code>CloneNotSupportedException</code> object
1121      * and in turn throws a new <code>SQLException</code> object.
1122      *
1123      * @return a copy of this <code>CachedRowSetImpl</code> object
1124      * @throws CloneNotSupportedException if an error occurs when
1125      * attempting to clone this <code>CachedRowSetImpl</code> object
1126      * @see #createShared
1127      */
1128     protected Object clone() throws CloneNotSupportedException  {
1129         return (super.clone());
1130     }
1131 
1132     /**
1133      * Creates a <code>RowSet</code> object that is a deep copy of
1134      * this <code>CachedRowSetImpl</code> object's data, including
1135      * constraints.  Updates made
1136      * on a copy are not visible to the original rowset;
1137      * a copy of a rowset is completely independent from the original.
1138      * <P>
1139      * Making a copy saves the cost of creating an identical rowset
1140      * from first principles, which can be quite expensive.
1141      * For example, it can eliminate the need to query a
1142      * remote database server.
1143      * @return a new <code>CachedRowSet</code> object that is a deep copy
1144      *           of this <code>CachedRowSet</code> object and is
1145      *           completely independent from this <code>CachedRowSetImpl</code>
1146      *           object.
1147      * @throws SQLException if an error occurs in generating the copy of this
1148      *           of the <code>CachedRowSetImpl</code>
1149      * @see #createShared
1150      * @see javax.sql.RowSetEvent
1151      * @see javax.sql.RowSetListener
1152      */
1153     public CachedRowSet createCopy() throws SQLException {
1154         ObjectOutputStream out;
1155         ByteArrayOutputStream bOut = new ByteArrayOutputStream();
1156         try {
1157             out = new ObjectOutputStream(bOut);
1158             out.writeObject(this);
1159         } catch (IOException ex) {
1160             throw new SQLException(MessageFormat.format(resBundle.handleGetObject("cachedrowsetimpl.clonefail").toString() , ex.getMessage()));
1161         }
1162 
1163         ObjectInputStream in;
1164 
1165         try {
1166             ByteArrayInputStream bIn = new ByteArrayInputStream(bOut.toByteArray());
1167             in = new ObjectInputStream(bIn);
1168         } catch (StreamCorruptedException ex) {
1169             throw new SQLException(MessageFormat.format(resBundle.handleGetObject("cachedrowsetimpl.clonefail").toString() , ex.getMessage()));
1170         } catch (IOException ex) {
1171             throw new SQLException(MessageFormat.format(resBundle.handleGetObject("cachedrowsetimpl.clonefail").toString() , ex.getMessage()));
1172         }
1173 
1174         try {
1175             //return ((CachedRowSet)(in.readObject()));
1176             CachedRowSetImpl crsTemp = (CachedRowSetImpl)in.readObject();
1177             crsTemp.resBundle = this.resBundle;
1178             return ((CachedRowSet)crsTemp);
1179 
1180         } catch (ClassNotFoundException ex) {
1181             throw new SQLException(MessageFormat.format(resBundle.handleGetObject("cachedrowsetimpl.clonefail").toString() , ex.getMessage()));
1182         } catch (OptionalDataException ex) {
1183             throw new SQLException(MessageFormat.format(resBundle.handleGetObject("cachedrowsetimpl.clonefail").toString() , ex.getMessage()));
1184         } catch (IOException ex) {
1185             throw new SQLException(MessageFormat.format(resBundle.handleGetObject("cachedrowsetimpl.clonefail").toString() , ex.getMessage()));
1186         }
1187     }
1188 
1189     /**
1190      * Creates a <code>RowSet</code> object that is a copy of
1191      * this <code>CachedRowSetImpl</code> object's table structure
1192      * and the constraints only.
1193      * There will be no data in the object being returned.
1194      * Updates made on a copy are not visible to the original rowset.
1195      * <P>
1196      * This helps in getting the underlying XML schema which can
1197      * be used as the basis for populating a <code>WebRowSet</code>.
1198      *
1199      * @return a new <code>CachedRowSet</code> object that is a copy
1200      * of this <code>CachedRowSetImpl</code> object's schema and
1201      * retains all the constraints on the original rowset but contains
1202      * no data
1203      * @throws SQLException if an error occurs in generating the copy
1204      * of the <code>CachedRowSet</code> object
1205      * @see #createShared
1206      * @see #createCopy
1207      * @see #createCopyNoConstraints
1208      * @see javax.sql.RowSetEvent
1209      * @see javax.sql.RowSetListener
1210      */
1211     public CachedRowSet createCopySchema() throws SQLException {
1212         // Copy everything except data i.e all constraints
1213 
1214         // Store the number of rows of "this"
1215         // and make numRows equals zero.
1216         // and make data also zero.
1217         int nRows = numRows;
1218         numRows = 0;
1219 
1220         CachedRowSet crs = this.createCopy();
1221 
1222         // reset this object back to number of rows.
1223         numRows = nRows;
1224 
1225         return crs;
1226     }
1227 
1228     /**
1229      * Creates a <code>CachedRowSet</code> object that is a copy of
1230      * this <code>CachedRowSetImpl</code> object's data only.
1231      * All constraints set in this object will not be there
1232      * in the returning object.  Updates made
1233      * on a copy are not visible to the original rowset.
1234      *
1235      * @return a new <code>CachedRowSet</code> object that is a deep copy
1236      * of this <code>CachedRowSetImpl</code> object and is
1237      * completely independent from this <code>CachedRowSetImpl</code> object
1238      * @throws SQLException if an error occurs in generating the copy of the
1239      * of the <code>CachedRowSet</code>
1240      * @see #createShared
1241      * @see #createCopy
1242      * @see #createCopySchema
1243      * @see javax.sql.RowSetEvent
1244      * @see javax.sql.RowSetListener
1245      */
1246     public CachedRowSet createCopyNoConstraints() throws SQLException {
1247         // Copy the whole data ONLY without any constraints.
1248         CachedRowSetImpl crs;
1249         crs = (CachedRowSetImpl)this.createCopy();
1250 
1251         crs.initProperties();
1252         try {
1253             crs.unsetMatchColumn(crs.getMatchColumnIndexes());
1254         } catch(SQLException sqle) {
1255             //do nothing, if the setMatchColumn is not set.
1256         }
1257 
1258         try {
1259             crs.unsetMatchColumn(crs.getMatchColumnNames());
1260         } catch(SQLException sqle) {
1261             //do nothing, if the setMatchColumn is not set.
1262         }
1263 
1264         return crs;
1265     }
1266 
1267     /**
1268      * Converts this <code>CachedRowSetImpl</code> object to a collection
1269      * of tables. The sample implementation utilitizes the <code>TreeMap</code>
1270      * collection type.
1271      * This class guarantees that the map will be in ascending key order,
1272      * sorted according to the natural order for the key's class.
1273      *
1274      * @return a <code>Collection</code> object consisting of tables,
1275      *         each of which is a copy of a row in this
1276      *         <code>CachedRowSetImpl</code> object
1277      * @throws SQLException if an error occurs in generating the collection
1278      * @see #toCollection(int)
1279      * @see #toCollection(String)
1280      * @see java.util.TreeMap
1281      */
1282     public Collection<?> toCollection() throws SQLException {
1283 
1284         TreeMap<Integer, Object> tMap = new TreeMap<>();
1285 
1286         for (int i = 0; i<numRows; i++) {
1287             tMap.put(i, rvh.get(i));
1288         }
1289 
1290         return (tMap.values());
1291     }
1292 
1293     /**
1294      * Returns the specified column of this <code>CachedRowSetImpl</code> object
1295      * as a <code>Collection</code> object.  This method makes a copy of the
1296      * column's data and utilitizes the <code>Vector</code> to establish the
1297      * collection. The <code>Vector</code> class implements a growable array
1298      * objects allowing the individual components to be accessed using an
1299      * an integer index similar to that of an array.
1300      *
1301      * @return a <code>Collection</code> object that contains the value(s)
1302      *         stored in the specified column of this
1303      *         <code>CachedRowSetImpl</code>
1304      *         object
1305      * @throws SQLException if an error occurs generated the collection; or
1306      *          an invalid column is provided.
1307      * @see #toCollection()
1308      * @see #toCollection(String)
1309      * @see java.util.Vector
1310      */
1311     public Collection<?> toCollection(int column) throws SQLException {
1312 
1313         int nRows = numRows;
1314         Vector<Object> vec = new Vector<>(nRows);
1315 
1316         // create a copy
1317         CachedRowSetImpl crsTemp;
1318         crsTemp = (CachedRowSetImpl) this.createCopy();
1319 
1320         while(nRows!=0) {
1321             crsTemp.next();
1322             vec.add(crsTemp.getObject(column));
1323             nRows--;
1324         }
1325 
1326         return (Collection)vec;
1327     }
1328 
1329     /**
1330      * Returns the specified column of this <code>CachedRowSetImpl</code> object
1331      * as a <code>Collection</code> object.  This method makes a copy of the
1332      * column's data and utilitizes the <code>Vector</code> to establish the
1333      * collection. The <code>Vector</code> class implements a growable array
1334      * objects allowing the individual components to be accessed using an
1335      * an integer index similar to that of an array.
1336      *
1337      * @return a <code>Collection</code> object that contains the value(s)
1338      *         stored in the specified column of this
1339      *         <code>CachedRowSetImpl</code>
1340      *         object
1341      * @throws SQLException if an error occurs generated the collection; or
1342      *          an invalid column is provided.
1343      * @see #toCollection()
1344      * @see #toCollection(int)
1345      * @see java.util.Vector
1346      */
1347     public Collection<?> toCollection(String column) throws SQLException {
1348         return toCollection(getColIdxByName(column));
1349     }
1350 
1351     //--------------------------------------------------------------------
1352     // Advanced features
1353     //--------------------------------------------------------------------
1354 
1355 
1356     /**
1357      * Returns the <code>SyncProvider</code> implementation being used
1358      * with this <code>CachedRowSetImpl</code> implementation rowset.
1359      *
1360      * @return the SyncProvider used by the rowset. If not provider was
1361      *          set when the rowset was instantiated, the reference
1362      *          implementation (default) provider is returned.
1363      * @throws SQLException if error occurs while return the
1364      *          <code>SyncProvider</code> instance.
1365      */
1366     public SyncProvider getSyncProvider() throws SQLException {
1367         return provider;
1368     }
1369 
1370     /**
1371      * Sets the active <code>SyncProvider</code> and attempts to load
1372      * load the new provider using the <code>SyncFactory</code> SPI.
1373      *
1374      * @throws SQLException if an error occurs while resetting the
1375      *          <code>SyncProvider</code>.
1376      */
1377     public void setSyncProvider(String providerStr) throws SQLException {
1378         provider =
1379         SyncFactory.getInstance(providerStr);
1380 
1381         rowSetReader = provider.getRowSetReader();
1382         rowSetWriter = provider.getRowSetWriter();
1383     }
1384 
1385 
1386     //-----------------
1387     // methods inherited from RowSet
1388     //-----------------
1389 
1390 
1391 
1392 
1393 
1394 
1395     //---------------------------------------------------------------------
1396     // Reading and writing data
1397     //---------------------------------------------------------------------
1398 
1399     /**
1400      * Populates this <code>CachedRowSetImpl</code> object with data.
1401      * This form of the method uses the rowset's user, password, and url or
1402      * data source name properties to create a database
1403      * connection.  If properties that are needed
1404      * have not been set, this method will throw an exception.
1405      * <P>
1406      * Another form of this method uses an existing JDBC <code>Connection</code>
1407      * object instead of creating a new one; therefore, it ignores the
1408      * properties used for establishing a new connection.
1409      * <P>
1410      * The query specified by the command property is executed to create a
1411      * <code>ResultSet</code> object from which to retrieve data.
1412      * The current contents of the rowset are discarded, and the
1413      * rowset's metadata is also (re)set.  If there are outstanding updates,
1414      * they are also ignored.
1415      * <P>
1416      * The method <code>execute</code> closes any database connections that it
1417      * creates.
1418      *
1419      * @throws SQLException if an error occurs or the
1420      *                         necessary properties have not been set
1421      */
1422     public void execute() throws SQLException {
1423         execute(null);
1424     }
1425 
1426 
1427 
1428     //-----------------------------------
1429     // Methods inherited from ResultSet
1430     //-----------------------------------
1431 
1432     /**
1433      * Moves the cursor down one row from its current position and
1434      * returns <code>true</code> if the new cursor position is a
1435      * valid row.
1436      * The cursor for a new <code>ResultSet</code> object is initially
1437      * positioned before the first row. The first call to the method
1438      * <code>next</code> moves the cursor to the first row, making it
1439      * the current row; the second call makes the second row the
1440      * current row, and so on.
1441      *
1442      * <P>If an input stream from the previous row is open, it is
1443      * implicitly closed. The <code>ResultSet</code> object's warning
1444      * chain is cleared when a new row is read.
1445      *
1446      * @return <code>true</code> if the new current row is valid;
1447      *         <code>false</code> if there are no more rows
1448      * @throws SQLException if an error occurs or
1449      *            the cursor is not positioned in the rowset, before
1450      *            the first row, or after the last row
1451      */
1452     public boolean next() throws SQLException {
1453         /*
1454          * make sure things look sane. The cursor must be
1455          * positioned in the rowset or before first (0) or
1456          * after last (numRows + 1)
1457          */
1458         if (cursorPos < 0 || cursorPos >= numRows + 1) {
1459             throw new SQLException(resBundle.handleGetObject("cachedrowsetimpl.invalidcp").toString());
1460         }
1461         // now move and notify
1462         boolean ret = this.internalNext();
1463         notifyCursorMoved();
1464 
1465         return ret;
1466     }
1467 
1468     /**
1469      * Moves this <code>CachedRowSetImpl</code> object's cursor to the next
1470      * row and returns <code>true</code> if the cursor is still in the rowset;
1471      * returns <code>false</code> if the cursor has moved to the position after
1472      * the last row.
1473      * <P>
1474      * This method handles the cases where the cursor moves to a row that
1475      * has been deleted.
1476      * If this rowset shows deleted rows and the cursor moves to a row
1477      * that has been deleted, this method moves the cursor to the next
1478      * row until the cursor is on a row that has not been deleted.
1479      * <P>
1480      * The method <code>internalNext</code> is called by methods such as
1481      * <code>next</code>, <code>absolute</code>, and <code>relative</code>,
1482      * and, as its name implies, is only called internally.
1483      * <p>
1484      * This is a implementation only method and is not required as a standard
1485      * implementation of the <code>CachedRowSet</code> interface.
1486      *
1487      * @return <code>true</code> if the cursor is on a valid row in this
1488      *         rowset; <code>false</code> if it is after the last row
1489      * @throws SQLException if an error occurs
1490      */
1491     protected boolean internalNext() throws SQLException {
1492         boolean ret = false;
1493 
1494         do {
1495             if (cursorPos < numRows) {
1496                 ++cursorPos;
1497                 ret = true;
1498             } else if (cursorPos == numRows) {
1499                 // increment to after last
1500                 ++cursorPos;
1501                 ret = false;
1502                 break;
1503             }
1504         } while ((getShowDeleted() == false) && (rowDeleted() == true));
1505 
1506         /* each call to internalNext may increment cursorPos multiple
1507          * times however, the absolutePos only increments once per call.
1508          */
1509         if (ret == true)
1510             absolutePos++;
1511         else
1512             absolutePos = 0;
1513 
1514         return ret;
1515     }
1516 
1517     /**
1518      * Closes this <code>CachedRowSetImpl</code> objecy and releases any resources
1519      * it was using.
1520      *
1521      * @throws SQLException if an error occurs when releasing any resources in use
1522      * by this <code>CachedRowSetImpl</code> object
1523      */
1524     public void close() throws SQLException {
1525 
1526         // close all data structures holding
1527         // the disconnected rowset
1528 
1529         cursorPos = 0;
1530         absolutePos = 0;
1531         numRows = 0;
1532         numDeleted = 0;
1533 
1534         // set all insert(s), update(s) & delete(s),
1535         // if at all, to their initial values.
1536         initProperties();
1537 
1538         // clear the vector of it's present contents
1539         rvh.clear();
1540 
1541         // this will make it eligible for gc
1542         // rvh = null;
1543     }
1544 
1545     /**
1546      * Reports whether the last column read was SQL <code>NULL</code>.
1547      * Note that you must first call the method <code>getXXX</code>
1548      * on a column to try to read its value and then call the method
1549      * <code>wasNull</code> to determine whether the value was
1550      * SQL <code>NULL</code>.
1551      *
1552      * @return <code>true</code> if the value in the last column read
1553      *         was SQL <code>NULL</code>; <code>false</code> otherwise
1554      * @throws SQLException if an error occurs
1555      */
1556     public boolean wasNull() throws SQLException {
1557         return lastValueNull;
1558     }
1559 
1560     /**
1561      * Sets the field <code>lastValueNull</code> to the given
1562      * <code>boolean</code> value.
1563      *
1564      * @param value <code>true</code> to indicate that the value of
1565      *        the last column read was SQL <code>NULL</code>;
1566      *        <code>false</code> to indicate that it was not
1567      */
1568     private void setLastValueNull(boolean value) {
1569         lastValueNull = value;
1570     }
1571 
1572     // Methods for accessing results by column index
1573 
1574     /**
1575      * Checks to see whether the given index is a valid column number
1576      * in this <code>CachedRowSetImpl</code> object and throws
1577      * an <code>SQLException</code> if it is not. The index is out of bounds
1578      * if it is less than <code>1</code> or greater than the number of
1579      * columns in this rowset.
1580      * <P>
1581      * This method is called internally by the <code>getXXX</code> and
1582      * <code>updateXXX</code> methods.
1583      *
1584      * @param idx the number of a column in this <code>CachedRowSetImpl</code>
1585      *            object; must be between <code>1</code> and the number of
1586      *            rows in this rowset
1587      * @throws SQLException if the given index is out of bounds
1588      */
1589     private void checkIndex(int idx) throws SQLException {
1590         if (idx < 1 || idx > RowSetMD.getColumnCount()) {
1591             throw new SQLException(resBundle.handleGetObject("cachedrowsetimpl.invalidcol").toString());
1592         }
1593     }
1594 
1595     /**
1596      * Checks to see whether the cursor for this <code>CachedRowSetImpl</code>
1597      * object is on a row in the rowset and throws an
1598      * <code>SQLException</code> if it is not.
1599      * <P>
1600      * This method is called internally by <code>getXXX</code> methods, by
1601      * <code>updateXXX</code> methods, and by methods that update, insert,
1602      * or delete a row or that cancel a row update, insert, or delete.
1603      *
1604      * @throws SQLException if the cursor for this <code>CachedRowSetImpl</code>
1605      *         object is not on a valid row
1606      */
1607     private void checkCursor() throws SQLException {
1608         if (isAfterLast() == true || isBeforeFirst() == true) {
1609             throw new SQLException(resBundle.handleGetObject("cachedrowsetimpl.invalidcp").toString());
1610         }
1611     }
1612 
1613     /**
1614      * Returns the column number of the column with the given name in this
1615      * <code>CachedRowSetImpl</code> object.  This method throws an
1616      * <code>SQLException</code> if the given name is not the name of
1617      * one of the columns in this rowset.
1618      *
1619      * @param name a <code>String</code> object that is the name of a column in
1620      *              this <code>CachedRowSetImpl</code> object
1621      * @throws SQLException if the given name does not match the name of one of
1622      *         the columns in this rowset
1623      */
1624     private int getColIdxByName(String name) throws SQLException {
1625         RowSetMD = (RowSetMetaDataImpl)this.getMetaData();
1626         int cols = RowSetMD.getColumnCount();
1627 
1628         for (int i=1; i <= cols; ++i) {
1629             String colName = RowSetMD.getColumnName(i);
1630             if (colName != null)
1631                 if (name.equalsIgnoreCase(colName))
1632                     return (i);
1633                 else
1634                     continue;
1635         }
1636         throw new SQLException(resBundle.handleGetObject("cachedrowsetimpl.invalcolnm").toString());
1637 
1638     }
1639 
1640     /**
1641      * Returns the insert row or the current row of this
1642      * <code>CachedRowSetImpl</code>object.
1643      *
1644      * @return the <code>Row</code> object on which this <code>CachedRowSetImpl</code>
1645      * objects's cursor is positioned
1646      */
1647     protected BaseRow getCurrentRow() {
1648         if (onInsertRow == true) {
1649             return (BaseRow)insertRow;
1650         } else {
1651             return (BaseRow)(rvh.get(cursorPos - 1));
1652         }
1653     }
1654 
1655     /**
1656      * Removes the row on which the cursor is positioned.
1657      * <p>
1658      * This is a implementation only method and is not required as a standard
1659      * implementation of the <code>CachedRowSet</code> interface.
1660      *
1661      * @throws SQLException if the cursor is positioned on the insert
1662      *            row
1663      */
1664     protected void removeCurrentRow() {
1665         ((Row)getCurrentRow()).setDeleted();
1666         rvh.remove(cursorPos - 1);
1667         --numRows;
1668     }
1669 
1670 
1671     /**
1672      * Retrieves the value of the designated column in the current row
1673      * of this <code>CachedRowSetImpl</code> object as a
1674      * <code>String</code> object.
1675      *
1676      * @param columnIndex the first column is <code>1</code>, the second
1677      *        is <code>2</code>, and so on; must be <code>1</code> or larger
1678      *        and equal to or less than the number of columns in the rowset
1679      * @return the column value; if the value is SQL <code>NULL</code>, the
1680      *         result is <code>null</code>
1681      * @throws SQLException if (1) the given column index is out of bounds,
1682      * (2) the cursor is not on one of this rowset's rows or its
1683      * insert row, or (3) the designated column does not store an
1684      * SQL <code>TINYINT, SMALLINT, INTEGER, BIGINT, REAL,
1685      * FLOAT, DOUBLE, DECIMAL, NUMERIC, BIT, <b>CHAR</b>, <b>VARCHAR</b></code>
1686      * or <code>LONGVARCHAR</code> value. The bold SQL type designates the
1687      * recommended return type.
1688      */
1689     public String getString(int columnIndex) throws SQLException {
1690         Object value;
1691 
1692         // sanity check.
1693         checkIndex(columnIndex);
1694         // make sure the cursor is on a valid row
1695         checkCursor();
1696 
1697         setLastValueNull(false);
1698         value = getCurrentRow().getColumnObject(columnIndex);
1699 
1700         // check for SQL NULL
1701         if (value == null) {
1702             setLastValueNull(true);
1703             return null;
1704         }
1705 
1706         return value.toString();
1707     }
1708 
1709     /**
1710      * Retrieves the value of the designated column in the current row
1711      * of this <code>CachedRowSetImpl</code> object as a
1712      * <code>boolean</code> value.
1713      *
1714      * @param columnIndex the first column is <code>1</code>, the second
1715      *        is <code>2</code>, and so on; must be <code>1</code> or larger
1716      *        and equal to or less than the number of columns in the rowset
1717      * @return the column value as a <code>boolean</code> in the Java progamming language;
1718      *        if the value is SQL <code>NULL</code>, the result is <code>false</code>
1719      * @throws SQLException if (1) the given column index is out of bounds,
1720      *            (2) the cursor is not on one of this rowset's rows or its
1721      *            insert row, or (3) the designated column does not store an
1722      *            SQL <code>BOOLEAN</code> value
1723      * @see #getBoolean(String)
1724      */
1725     public boolean getBoolean(int columnIndex) throws SQLException {
1726         Object value;
1727 
1728         // sanity check.
1729         checkIndex(columnIndex);
1730         // make sure the cursor is on a valid row
1731         checkCursor();
1732 
1733         setLastValueNull(false);
1734         value = getCurrentRow().getColumnObject(columnIndex);
1735 
1736         // check for SQL NULL
1737         if (value == null) {
1738             setLastValueNull(true);
1739             return false;
1740         }
1741 
1742         // check for Boolean...
1743         if (value instanceof Boolean) {
1744             return ((Boolean)value).booleanValue();
1745         }
1746 
1747         // convert to a Double and compare to zero
1748         try {
1749             return Double.compare(Double.parseDouble(value.toString()), 0) != 0;
1750         } catch (NumberFormatException ex) {
1751             throw new SQLException(MessageFormat.format(resBundle.handleGetObject("cachedrowsetimpl.boolfail").toString(),
1752                   new Object[] {value.toString().trim(), columnIndex}));
1753         }
1754     }
1755 
1756     /**
1757      * Retrieves the value of the designated column in the current row
1758      * of this <code>CachedRowSetImpl</code> object as a
1759      * <code>byte</code> value.
1760      *
1761      * @param columnIndex the first column is <code>1</code>, the second
1762      *        is <code>2</code>, and so on; must be <code>1</code> or larger
1763      *        and equal to or less than the number of columns in the rowset
1764      * @return the column value as a <code>byte</code> in the Java programming
1765      * language; if the value is SQL <code>NULL</code>, the result is <code>0</code>
1766      * @throws SQLException if (1) the given column index is out of bounds,
1767      *            (2) the cursor is not on one of this rowset's rows or its
1768      *            insert row, or (3) the designated column does not store an
1769      *            SQL <code><b>TINYINT</b>, SMALLINT, INTEGER, BIGINT, REAL,
1770      *            FLOAT, DOUBLE, DECIMAL, NUMERIC, BIT, CHAR, VARCHAR</code>
1771      *            or <code>LONGVARCHAR</code> value. The bold SQL type
1772      *            designates the recommended return type.
1773      * @see #getByte(String)
1774      */
1775     public byte getByte(int columnIndex) throws SQLException {
1776         Object value;
1777 
1778         // sanity check.
1779         checkIndex(columnIndex);
1780         // make sure the cursor is on a valid row
1781         checkCursor();
1782 
1783         setLastValueNull(false);
1784         value = getCurrentRow().getColumnObject(columnIndex);
1785 
1786         // check for SQL NULL
1787         if (value == null) {
1788             setLastValueNull(true);
1789             return (byte)0;
1790         }
1791         try {
1792             return ((Byte.valueOf(value.toString())).byteValue());
1793         } catch (NumberFormatException ex) {
1794             throw new SQLException(MessageFormat.format(resBundle.handleGetObject("cachedrowsetimpl.bytefail").toString(),
1795                   new Object[] {value.toString().trim(), columnIndex}));
1796         }
1797     }
1798 
1799     /**
1800      * Retrieves the value of the designated column in the current row
1801      * of this <code>CachedRowSetImpl</code> object as a
1802      * <code>short</code> value.
1803      *
1804      * @param columnIndex the first column is <code>1</code>, the second
1805      *        is <code>2</code>, and so on; must be <code>1</code> or larger
1806      *        and equal to or less than the number of columns in the rowset
1807      * @return the column value; if the value is SQL <code>NULL</code>, the
1808      *         result is <code>0</code>
1809      * @throws SQLException if (1) the given column index is out of bounds,
1810      * (2) the cursor is not on one of this rowset's rows or its
1811      * insert row, or (3) the designated column does not store an
1812      * SQL <code>TINYINT, <b>SMALLINT</b>, INTEGER, BIGINT, REAL
1813      * FLOAT, DOUBLE, DECIMAL, NUMERIC, BIT, CHAR, VARCHAR</code>
1814      * or <code>LONGVARCHAR</code> value. The bold SQL type designates the
1815      * recommended return type.
1816      * @see #getShort(String)
1817      */
1818     public short getShort(int columnIndex) throws SQLException {
1819         Object value;
1820 
1821         // sanity check.
1822         checkIndex(columnIndex);
1823         // make sure the cursor is on a valid row
1824         checkCursor();
1825 
1826         setLastValueNull(false);
1827         value = getCurrentRow().getColumnObject(columnIndex);
1828 
1829         // check for SQL NULL
1830         if (value == null) {
1831             setLastValueNull(true);
1832             return (short)0;
1833         }
1834 
1835         try {
1836             return ((Short.valueOf(value.toString().trim())).shortValue());
1837         } catch (NumberFormatException ex) {
1838             throw new SQLException(MessageFormat.format(resBundle.handleGetObject("cachedrowsetimpl.shortfail").toString(),
1839                   new Object[] {value.toString().trim(), columnIndex}));
1840         }
1841     }
1842 
1843     /**
1844      * Retrieves the value of the designated column in the current row
1845      * of this <code>CachedRowSetImpl</code> object as an
1846      * <code>int</code> value.
1847      *
1848      * @param columnIndex the first column is <code>1</code>, the second
1849      *        is <code>2</code>, and so on; must be <code>1</code> or larger
1850      *        and equal to or less than the number of columns in the rowset
1851      * @return the column value; if the value is SQL <code>NULL</code>, the
1852      *         result is <code>0</code>
1853      * @throws SQLException if (1) the given column index is out of bounds,
1854      * (2) the cursor is not on one of this rowset's rows or its
1855      * insert row, or (3) the designated column does not store an
1856      * SQL <code>TINYINT, SMALLINT, <b>INTEGER</b>, BIGINT, REAL
1857      * FLOAT, DOUBLE, DECIMAL, NUMERIC, BIT, CHAR, VARCHAR</code>
1858      * or <code>LONGVARCHAR</code> value. The bold SQL type designates the
1859      * recommended return type.
1860      */
1861     public int getInt(int columnIndex) throws SQLException {
1862         Object value;
1863 
1864         // sanity check.
1865         checkIndex(columnIndex);
1866         // make sure the cursor is on a valid row
1867         checkCursor();
1868 
1869         setLastValueNull(false);
1870         value = getCurrentRow().getColumnObject(columnIndex);
1871 
1872         // check for SQL NULL
1873         if (value == null) {
1874             setLastValueNull(true);
1875             return 0;
1876         }
1877 
1878         try {
1879             return ((Integer.valueOf(value.toString().trim())).intValue());
1880         } catch (NumberFormatException ex) {
1881             throw new SQLException(MessageFormat.format(resBundle.handleGetObject("cachedrowsetimpl.intfail").toString(),
1882                   new Object[] {value.toString().trim(), columnIndex}));
1883         }
1884     }
1885 
1886     /**
1887      * Retrieves the value of the designated column in the current row
1888      * of this <code>CachedRowSetImpl</code> object as a
1889      * <code>long</code> value.
1890      *
1891      * @param columnIndex the first column is <code>1</code>, the second
1892      *        is <code>2</code>, and so on; must be <code>1</code> or larger
1893      *        and equal to or less than the number of columns in the rowset
1894      * @return the column value; if the value is SQL <code>NULL</code>, the
1895      *         result is <code>0</code>
1896      * @throws SQLException if (1) the given column index is out of bounds,
1897      * (2) the cursor is not on one of this rowset's rows or its
1898      * insert row, or (3) the designated column does not store an
1899      * SQL <code>TINYINT, SMALLINT, INTEGER, <b>BIGINT</b>, REAL
1900      * FLOAT, DOUBLE, DECIMAL, NUMERIC, BIT, CHAR, VARCHAR</code>
1901      * or <code>LONGVARCHAR</code> value. The bold SQL type designates the
1902      * recommended return type.
1903      * @see #getLong(String)
1904      */
1905     public long getLong(int columnIndex) throws SQLException {
1906         Object value;
1907 
1908         // sanity check.
1909         checkIndex(columnIndex);
1910         // make sure the cursor is on a valid row
1911         checkCursor();
1912 
1913         setLastValueNull(false);
1914         value = getCurrentRow().getColumnObject(columnIndex);
1915 
1916         // check for SQL NULL
1917         if (value == null) {
1918             setLastValueNull(true);
1919             return (long)0;
1920         }
1921         try {
1922             return ((Long.valueOf(value.toString().trim())).longValue());
1923         } catch (NumberFormatException ex) {
1924             throw new SQLException(MessageFormat.format(resBundle.handleGetObject("cachedrowsetimpl.longfail").toString(),
1925                   new Object[] {value.toString().trim(), columnIndex}));
1926         }
1927     }
1928 
1929     /**
1930      * Retrieves the value of the designated column in the current row
1931      * of this <code>CachedRowSetImpl</code> object as a
1932      * <code>float</code> value.
1933      *
1934      * @param columnIndex the first column is <code>1</code>, the second
1935      *        is <code>2</code>, and so on; must be <code>1</code> or larger
1936      *        and equal to or less than the number of columns in the rowset
1937      * @return the column value; if the value is SQL <code>NULL</code>, the
1938      *         result is <code>0</code>
1939      * @throws SQLException if (1) the given column index is out of bounds,
1940      * (2) the cursor is not on one of this rowset's rows or its
1941      * insert row, or (3) the designated column does not store an
1942      * SQL <code>TINYINT, SMALLINT, INTEGER, BIGINT, <b>REAL</b>,
1943      * FLOAT, DOUBLE, DECIMAL, NUMERIC, BIT, CHAR, VARCHAR</code>
1944      * or <code>LONGVARCHAR</code> value. The bold SQL type designates the
1945      * recommended return type.
1946      * @see #getFloat(String)
1947      */
1948     public float getFloat(int columnIndex) throws SQLException {
1949         Object value;
1950 
1951         // sanity check.
1952         checkIndex(columnIndex);
1953         // make sure the cursor is on a valid row
1954         checkCursor();
1955 
1956         setLastValueNull(false);
1957         value = getCurrentRow().getColumnObject(columnIndex);
1958 
1959         // check for SQL NULL
1960         if (value == null) {
1961             setLastValueNull(true);
1962             return (float)0;
1963         }
1964         try {
1965             return ((new Float(value.toString())).floatValue());
1966         } catch (NumberFormatException ex) {
1967             throw new SQLException(MessageFormat.format(resBundle.handleGetObject("cachedrowsetimpl.floatfail").toString(),
1968                   new Object[] {value.toString().trim(), columnIndex}));
1969         }
1970     }
1971 
1972     /**
1973      * Retrieves the value of the designated column in the current row
1974      * of this <code>CachedRowSetImpl</code> object as a
1975      * <code>double</code> value.
1976      *
1977      * @param columnIndex the first column is <code>1</code>, the second
1978      *        is <code>2</code>, and so on; must be <code>1</code> or larger
1979      *        and equal to or less than the number of columns in the rowset
1980      * @return the column value; if the value is SQL <code>NULL</code>, the
1981      *         result is <code>0</code>
1982      * @throws SQLException if (1) the given column index is out of bounds,
1983      * (2) the cursor is not on one of this rowset's rows or its
1984      * insert row, or (3) the designated column does not store an
1985      * SQL <code>TINYINT, SMALLINT, INTEGER, BIGINT, REAL,
1986      * <b>FLOAT</b>, <b>DOUBLE</b>, DECIMAL, NUMERIC, BIT, CHAR, VARCHAR</code>
1987      * or <code>LONGVARCHAR</code> value. The bold SQL type designates the
1988      * recommended return type.
1989      * @see #getDouble(String)
1990      *
1991      */
1992     public double getDouble(int columnIndex) throws SQLException {
1993         Object value;
1994 
1995         // sanity check.
1996         checkIndex(columnIndex);
1997         // make sure the cursor is on a valid row
1998         checkCursor();
1999 
2000         setLastValueNull(false);
2001         value = getCurrentRow().getColumnObject(columnIndex);
2002 
2003         // check for SQL NULL
2004         if (value == null) {
2005             setLastValueNull(true);
2006             return (double)0;
2007         }
2008         try {
2009             return ((new Double(value.toString().trim())).doubleValue());
2010         } catch (NumberFormatException ex) {
2011             throw new SQLException(MessageFormat.format(resBundle.handleGetObject("cachedrowsetimpl.doublefail").toString(),
2012                   new Object[] {value.toString().trim(), columnIndex}));
2013         }
2014     }
2015 
2016     /**
2017      * Retrieves the value of the designated column in the current row
2018      * of this <code>CachedRowSetImpl</code> object as a
2019      * <code>java.math.BigDecimal</code> object.
2020      * <P>
2021      * This method is deprecated; use the version of <code>getBigDecimal</code>
2022      * that does not take a scale parameter and returns a value with full
2023      * precision.
2024      *
2025      * @param columnIndex the first column is <code>1</code>, the second
2026      *        is <code>2</code>, and so on; must be <code>1</code> or larger
2027      *        and equal to or less than the number of columns in the rowset
2028      * @param scale the number of digits to the right of the decimal point in the
2029      *        value returned
2030      * @return the column value with the specified number of digits to the right
2031      *         of the decimal point; if the value is SQL <code>NULL</code>, the
2032      *         result is <code>null</code>
2033      * @throws SQLException if the given column index is out of bounds,
2034      *            the cursor is not on a valid row, or this method fails
2035      * @deprecated
2036      */
2037     @Deprecated
2038     public BigDecimal getBigDecimal(int columnIndex, int scale) throws SQLException {
2039         Object value;
2040         BigDecimal bDecimal, retVal;
2041 
2042         // sanity check.
2043         checkIndex(columnIndex);
2044         // make sure the cursor is on a valid row
2045         checkCursor();
2046 
2047         setLastValueNull(false);
2048         value = getCurrentRow().getColumnObject(columnIndex);
2049 
2050         // check for SQL NULL
2051         if (value == null) {
2052             setLastValueNull(true);
2053             return (new BigDecimal(0));
2054         }
2055 
2056         bDecimal = this.getBigDecimal(columnIndex);
2057 
2058         retVal = bDecimal.setScale(scale);
2059 
2060         return retVal;
2061     }
2062 
2063     /**
2064      * Retrieves the value of the designated column in the current row
2065      * of this <code>CachedRowSetImpl</code> object as a
2066      * <code>byte</code> array value.
2067      *
2068      * @param columnIndex the first column is <code>1</code>, the second
2069      *        is <code>2</code>, and so on; must be <code>1</code> or larger
2070      *        and equal to or less than the number of columns in the rowset
2071      * @return the column value as a <code>byte</code> array in the Java programming
2072      * language; if the value is SQL <code>NULL</code>, the
2073      * result is <code>null</code>
2074      *
2075      * @throws SQLException if (1) the given column index is out of bounds,
2076      * (2) the cursor is not on one of this rowset's rows or its
2077      * insert row, or (3) the designated column does not store an
2078      * SQL <code><b>BINARY</b>, <b>VARBINARY</b> or
2079      * LONGVARBINARY</code> value.
2080      * The bold SQL type designates the recommended return type.
2081      * @see #getBytes(String)
2082      */
2083     public byte[] getBytes(int columnIndex) throws SQLException {
2084         // sanity check.
2085         checkIndex(columnIndex);
2086         // make sure the cursor is on a valid row
2087         checkCursor();
2088 
2089         if (isBinary(RowSetMD.getColumnType(columnIndex)) == false) {
2090             throw new SQLException(resBundle.handleGetObject("cachedrowsetimpl.dtypemismt").toString());
2091         }
2092 
2093         return (byte[])(getCurrentRow().getColumnObject(columnIndex));
2094     }
2095 
2096     /**
2097      * Retrieves the value of the designated column in the current row
2098      * of this <code>CachedRowSetImpl</code> object as a
2099      * <code>java.sql.Date</code> object.
2100      *
2101      * @param columnIndex the first column is <code>1</code>, the second
2102      *        is <code>2</code>, and so on; must be <code>1</code> or larger
2103      *        and equal to or less than the number of columns in the rowset
2104      * @return the column value as a <code>java.sql.Data</code> object; if
2105      *        the value is SQL <code>NULL</code>, the
2106      *        result is <code>null</code>
2107      * @throws SQLException if the given column index is out of bounds,
2108      *            the cursor is not on a valid row, or this method fails
2109      */
2110     public java.sql.Date getDate(int columnIndex) throws SQLException {
2111         Object value;
2112 
2113         // sanity check.
2114         checkIndex(columnIndex);
2115         // make sure the cursor is on a valid row
2116         checkCursor();
2117 
2118         setLastValueNull(false);
2119         value = getCurrentRow().getColumnObject(columnIndex);
2120 
2121         // check for SQL NULL
2122         if (value == null) {
2123             setLastValueNull(true);
2124             return null;
2125         }
2126 
2127         /*
2128          * The object coming back from the db could be
2129          * a date, a timestamp, or a char field variety.
2130          * If it's a date type return it, a timestamp
2131          * we turn into a long and then into a date,
2132          * char strings we try to parse. Yuck.
2133          */
2134         switch (RowSetMD.getColumnType(columnIndex)) {
2135             case java.sql.Types.DATE: {
2136                 long sec = ((java.sql.Date)value).getTime();
2137                 return new java.sql.Date(sec);
2138             }
2139             case java.sql.Types.TIMESTAMP: {
2140                 long sec = ((java.sql.Timestamp)value).getTime();
2141                 return new java.sql.Date(sec);
2142             }
2143             case java.sql.Types.CHAR:
2144             case java.sql.Types.VARCHAR:
2145             case java.sql.Types.LONGVARCHAR: {
2146                 try {
2147                     DateFormat df = DateFormat.getDateInstance();
2148                     return ((java.sql.Date)(df.parse(value.toString())));
2149                 } catch (ParseException ex) {
2150                     throw new SQLException(MessageFormat.format(resBundle.handleGetObject("cachedrowsetimpl.datefail").toString(),
2151                         new Object[] {value.toString().trim(), columnIndex}));
2152                 }
2153             }
2154             default: {
2155                 throw new SQLException(MessageFormat.format(resBundle.handleGetObject("cachedrowsetimpl.datefail").toString(),
2156                         new Object[] {value.toString().trim(), columnIndex}));
2157             }
2158         }
2159     }
2160 
2161     /**
2162      * Retrieves the value of the designated column in the current row
2163      * of this <code>CachedRowSetImpl</code> object as a
2164      * <code>java.sql.Time</code> object.
2165      *
2166      * @param columnIndex the first column is <code>1</code>, the second
2167      *        is <code>2</code>, and so on; must be <code>1</code> or larger
2168      *        and equal to or less than the number of columns in the rowset
2169      * @return the column value; if the value is SQL <code>NULL</code>, the
2170      *         result is <code>null</code>
2171      * @throws SQLException if the given column index is out of bounds,
2172      *         the cursor is not on a valid row, or this method fails
2173      */
2174     public java.sql.Time getTime(int columnIndex) throws SQLException {
2175         Object value;
2176 
2177         // sanity check.
2178         checkIndex(columnIndex);
2179         // make sure the cursor is on a valid row
2180         checkCursor();
2181 
2182         setLastValueNull(false);
2183         value = getCurrentRow().getColumnObject(columnIndex);
2184 
2185         // check for SQL NULL
2186         if (value == null) {
2187             setLastValueNull(true);
2188             return null;
2189         }
2190 
2191         /*
2192          * The object coming back from the db could be
2193          * a date, a timestamp, or a char field variety.
2194          * If it's a date type return it, a timestamp
2195          * we turn into a long and then into a date,
2196          * char strings we try to parse. Yuck.
2197          */
2198         switch (RowSetMD.getColumnType(columnIndex)) {
2199             case java.sql.Types.TIME: {
2200                 return (java.sql.Time)value;
2201             }
2202             case java.sql.Types.TIMESTAMP: {
2203                 long sec = ((java.sql.Timestamp)value).getTime();
2204                 return new java.sql.Time(sec);
2205             }
2206             case java.sql.Types.CHAR:
2207             case java.sql.Types.VARCHAR:
2208             case java.sql.Types.LONGVARCHAR: {
2209                 try {
2210                     DateFormat tf = DateFormat.getTimeInstance();
2211                     return ((java.sql.Time)(tf.parse(value.toString())));
2212                 } catch (ParseException ex) {
2213                     throw new SQLException(MessageFormat.format(resBundle.handleGetObject("cachedrowsetimpl.timefail").toString(),
2214                         new Object[] {value.toString().trim(), columnIndex}));
2215                 }
2216             }
2217             default: {
2218                 throw new SQLException(MessageFormat.format(resBundle.handleGetObject("cachedrowsetimpl.timefail").toString(),
2219                         new Object[] {value.toString().trim(), columnIndex}));
2220             }
2221         }
2222     }
2223 
2224     /**
2225      * Retrieves the value of the designated column in the current row
2226      * of this <code>CachedRowSetImpl</code> object as a
2227      * <code>java.sql.Timestamp</code> object.
2228      *
2229      * @param columnIndex the first column is <code>1</code>, the second
2230      *        is <code>2</code>, and so on; must be <code>1</code> or larger
2231      *        and equal to or less than the number of columns in the rowset
2232      * @return the column value; if the value is SQL <code>NULL</code>, the
2233      *         result is <code>null</code>
2234      * @throws SQLException if the given column index is out of bounds,
2235      *            the cursor is not on a valid row, or this method fails
2236      */
2237     public java.sql.Timestamp getTimestamp(int columnIndex) throws SQLException {
2238         Object value;
2239 
2240         // sanity check.
2241         checkIndex(columnIndex);
2242         // make sure the cursor is on a valid row
2243         checkCursor();
2244 
2245         setLastValueNull(false);
2246         value = getCurrentRow().getColumnObject(columnIndex);
2247 
2248         // check for SQL NULL
2249         if (value == null) {
2250             setLastValueNull(true);
2251             return null;
2252         }
2253 
2254         /*
2255          * The object coming back from the db could be
2256          * a date, a timestamp, or a char field variety.
2257          * If it's a date type return it; a timestamp
2258          * we turn into a long and then into a date;
2259          * char strings we try to parse. Yuck.
2260          */
2261         switch (RowSetMD.getColumnType(columnIndex)) {
2262             case java.sql.Types.TIMESTAMP: {
2263                 return (java.sql.Timestamp)value;
2264             }
2265             case java.sql.Types.TIME: {
2266                 long sec = ((java.sql.Time)value).getTime();
2267                 return new java.sql.Timestamp(sec);
2268             }
2269             case java.sql.Types.DATE: {
2270                 long sec = ((java.sql.Date)value).getTime();
2271                 return new java.sql.Timestamp(sec);
2272             }
2273             case java.sql.Types.CHAR:
2274             case java.sql.Types.VARCHAR:
2275             case java.sql.Types.LONGVARCHAR: {
2276                 try {
2277                     DateFormat tf = DateFormat.getTimeInstance();
2278                     return ((java.sql.Timestamp)(tf.parse(value.toString())));
2279                 } catch (ParseException ex) {
2280                     throw new SQLException(MessageFormat.format(resBundle.handleGetObject("cachedrowsetimpl.timefail").toString(),
2281                         new Object[] {value.toString().trim(), columnIndex}));
2282                 }
2283             }
2284             default: {
2285                 throw new SQLException(MessageFormat.format(resBundle.handleGetObject("cachedrowsetimpl.timefail").toString(),
2286                         new Object[] {value.toString().trim(), columnIndex}));
2287             }
2288         }
2289     }
2290 
2291     /**
2292      * Retrieves the value of the designated column in the current row of this
2293      * <code>CachedRowSetImpl</code> object as a <code>java.io.InputStream</code>
2294      * object.
2295      *
2296      * A column value can be retrieved as a stream of ASCII characters
2297      * and then read in chunks from the stream.  This method is particularly
2298      * suitable for retrieving large <code>LONGVARCHAR</code> values.  The JDBC
2299      * driver will do any necessary conversion from the database format into ASCII.
2300      *
2301      * <P><B>Note:</B> All the data in the returned stream must be
2302      * read prior to getting the value of any other column. The next
2303      * call to a get method implicitly closes the stream. . Also, a
2304      * stream may return <code>0</code> for <code>CachedRowSetImpl.available()</code>
2305      * whether there is data available or not.
2306      *
2307      * @param columnIndex the first column is <code>1</code>, the second
2308      *        is <code>2</code>, and so on; must be <code>1</code> or larger
2309      *        and equal to or less than the number of columns in this rowset
2310      * @return a Java input stream that delivers the database column value
2311      *         as a stream of one-byte ASCII characters.  If the value is SQL
2312      *         <code>NULL</code>, the result is <code>null</code>.
2313      * @throws SQLException if (1) the given column index is out of bounds,
2314      * (2) the cursor is not on one of this rowset's rows or its
2315      * insert row, or (3) the designated column does not store an
2316      * SQL <code>CHAR, VARCHAR</code>, <code><b>LONGVARCHAR</b></code>
2317      * <code>BINARY, VARBINARY</code> or <code>LONGVARBINARY</code> value. The
2318      * bold SQL type designates the recommended return types that this method is
2319      * used to retrieve.
2320      * @see #getAsciiStream(String)
2321      */
2322     public java.io.InputStream getAsciiStream(int columnIndex) throws SQLException {
2323         Object value;
2324 
2325         // always free an old stream
2326         asciiStream = null;
2327 
2328         // sanity check
2329         checkIndex(columnIndex);
2330         //make sure the cursor is on a vlid row
2331         checkCursor();
2332 
2333         value =  getCurrentRow().getColumnObject(columnIndex);
2334         if (value == null) {
2335             lastValueNull = true;
2336             return null;
2337         }
2338 
2339         try {
2340             if (isString(RowSetMD.getColumnType(columnIndex))) {
2341                 asciiStream = new ByteArrayInputStream(((String)value).getBytes("ASCII"));
2342             } else {
2343                 throw new SQLException(resBundle.handleGetObject("cachedrowsetimpl.dtypemismt").toString());
2344             }
2345         } catch (java.io.UnsupportedEncodingException ex) {
2346             throw new SQLException(ex.getMessage());
2347         }
2348 
2349         return asciiStream;
2350     }
2351 
2352     /**
2353      * A column value can be retrieved as a stream of Unicode characters
2354      * and then read in chunks from the stream.  This method is particularly
2355      * suitable for retrieving large LONGVARCHAR values.  The JDBC driver will
2356      * do any necessary conversion from the database format into Unicode.
2357      *
2358      * <P><B>Note:</B> All the data in the returned stream must be
2359      * read prior to getting the value of any other column. The next
2360      * call to a get method implicitly closes the stream. . Also, a
2361      * stream may return 0 for available() whether there is data
2362      * available or not.
2363      *
2364      * @param columnIndex the first column is <code>1</code>, the second
2365      *        is <code>2</code>, and so on; must be <code>1</code> or larger
2366      *        and equal to or less than the number of columns in this rowset
2367      * @return a Java input stream that delivers the database column value
2368      * as a stream of two byte Unicode characters.  If the value is SQL NULL
2369      * then the result is null.
2370      * @throws SQLException if an error occurs
2371      * @deprecated
2372      */
2373     @Deprecated
2374     public java.io.InputStream getUnicodeStream(int columnIndex) throws SQLException {
2375         // always free an old stream
2376         unicodeStream = null;
2377 
2378         // sanity check.
2379         checkIndex(columnIndex);
2380         // make sure the cursor is on a valid row
2381         checkCursor();
2382 
2383         if (isBinary(RowSetMD.getColumnType(columnIndex)) == false &&
2384         isString(RowSetMD.getColumnType(columnIndex)) == false) {
2385             throw new SQLException(resBundle.handleGetObject("cachedrowsetimpl.dtypemismt").toString());
2386         }
2387 
2388         Object value = getCurrentRow().getColumnObject(columnIndex);
2389         if (value == null) {
2390             lastValueNull = true;
2391             return null;
2392         }
2393 
2394         unicodeStream = new StringBufferInputStream(value.toString());
2395 
2396         return unicodeStream;
2397     }
2398 
2399     /**
2400      * Retrieves the value of the designated column in the current row of this
2401      * <code>CachedRowSetImpl</code> object as a <code>java.io.InputStream</code>
2402      * object.
2403      * <P>
2404      * A column value can be retrieved as a stream of uninterpreted bytes
2405      * and then read in chunks from the stream.  This method is particularly
2406      * suitable for retrieving large <code>LONGVARBINARY</code> values.
2407      *
2408      * <P><B>Note:</B> All the data in the returned stream must be
2409      * read prior to getting the value of any other column. The next
2410      * call to a get method implicitly closes the stream. Also, a
2411      * stream may return <code>0</code> for
2412      * <code>CachedRowSetImpl.available()</code> whether there is data
2413      * available or not.
2414      *
2415      * @param columnIndex the first column is <code>1</code>, the second
2416      * is <code>2</code>, and so on; must be <code>1</code> or larger
2417      * and equal to or less than the number of columns in the rowset
2418      * @return a Java input stream that delivers the database column value
2419      * as a stream of uninterpreted bytes.  If the value is SQL <code>NULL</code>
2420      * then the result is <code>null</code>.
2421      * @throws SQLException if (1) the given column index is out of bounds,
2422      * (2) the cursor is not on one of this rowset's rows or its
2423      * insert row, or (3) the designated column does not store an
2424      * SQL <code>BINARY, VARBINARY</code> or <code><b>LONGVARBINARY</b></code>
2425      * The bold type indicates the SQL type that this method is recommened
2426      * to retrieve.
2427      * @see #getBinaryStream(String)
2428      */
2429     public java.io.InputStream getBinaryStream(int columnIndex) throws SQLException {
2430 
2431         // always free an old stream
2432         binaryStream = null;
2433 
2434         // sanity check.
2435         checkIndex(columnIndex);
2436         // make sure the cursor is on a valid row
2437         checkCursor();
2438 
2439         if (isBinary(RowSetMD.getColumnType(columnIndex)) == false) {
2440             throw new SQLException(resBundle.handleGetObject("cachedrowsetimpl.dtypemismt").toString());
2441         }
2442 
2443         Object value = getCurrentRow().getColumnObject(columnIndex);
2444         if (value == null) {
2445             lastValueNull = true;
2446             return null;
2447         }
2448 
2449         binaryStream = new ByteArrayInputStream((byte[])value);
2450 
2451         return binaryStream;
2452 
2453     }
2454 
2455 
2456     // Methods for accessing results by column name
2457 
2458     /**
2459      * Retrieves the value stored in the designated column
2460      * of the current row as a <code>String</code> object.
2461      *
2462      * @param columnName a <code>String</code> object giving the SQL name of
2463      *        a column in this <code>CachedRowSetImpl</code> object
2464      * @return the column value; if the value is SQL <code>NULL</code>,
2465      *         the result is <code>null</code>
2466      * @throws SQLException if (1) the given column name is not the name of
2467      * a column in this rowset, (2) the cursor is not on one of
2468      * this rowset's rows or its insert row, or (3) the designated
2469      * column does not store an SQL <code>TINYINT, SMALLINT, INTEGER
2470      * BIGINT, REAL, FLOAT, DOUBLE, DECIMAL, NUMERIC, BIT, <b>CHAR</b>,
2471      * <b>VARCHAR</b></code> or <code>LONGVARCHAR<</code> value. The bold SQL type
2472      * designates the recommended return type.
2473      */
2474     public String getString(String columnName) throws SQLException {
2475         return getString(getColIdxByName(columnName));
2476     }
2477 
2478     /**
2479      * Retrieves the value stored in the designated column
2480      * of the current row as a <code>boolean</code> value.
2481      *
2482      * @param columnName a <code>String</code> object giving the SQL name of
2483      *        a column in this <code>CachedRowSetImpl</code> object
2484      * @return the column value as a <code>boolean</code> in the Java programming
2485      *        language; if the value is SQL <code>NULL</code>,
2486      *        the result is <code>false</code>
2487      * @throws SQLException if (1) the given column name is not the name of
2488      *            a column in this rowset, (2) the cursor is not on one of
2489      *            this rowset's rows or its insert row, or (3) the designated
2490      *            column does not store an SQL <code>BOOLEAN</code> value
2491      * @see #getBoolean(int)
2492      */
2493     public boolean getBoolean(String columnName) throws SQLException {
2494         return getBoolean(getColIdxByName(columnName));
2495     }
2496 
2497     /**
2498      * Retrieves the value stored in the designated column
2499      * of the current row as a <code>byte</code> value.
2500      *
2501      * @param columnName a <code>String</code> object giving the SQL name of
2502      *        a column in this <code>CachedRowSetImpl</code> object
2503      * @return the column value as a <code>byte</code> in the Java programming
2504      * language; if the value is SQL <code>NULL</code>, the result is <code>0</code>
2505      * @throws SQLException if (1) the given column name is not the name of
2506      * a column in this rowset, (2) the cursor is not on one of
2507      * this rowset's rows or its insert row, or (3) the designated
2508      * column does not store an SQL <code><B>TINYINT</B>, SMALLINT, INTEGER,
2509      * BIGINT, REAL, FLOAT, DOUBLE, DECIMAL, NUMERIC, BIT, CHAR,
2510      * VARCHAR</code> or <code>LONGVARCHAR</code> value. The
2511      * bold type designates the recommended return type
2512      */
2513     public byte getByte(String columnName) throws SQLException {
2514         return getByte(getColIdxByName(columnName));
2515     }
2516 
2517     /**
2518      * Retrieves the value stored in the designated column
2519      * of the current row as a <code>short</code> value.
2520      *
2521      * @param columnName a <code>String</code> object giving the SQL name of
2522      *        a column in this <code>CachedRowSetImpl</code> object
2523      * @return the column value; if the value is SQL <code>NULL</code>,
2524      *         the result is <code>0</code>
2525      * @throws SQLException if (1) the given column name is not the name of
2526      * a column in this rowset, (2) the cursor is not on one of
2527      * this rowset's rows or its insert row, or (3) the designated
2528      * column does not store an SQL <code>TINYINT, <b>SMALLINT</b>, INTEGER
2529      * BIGINT, REAL, FLOAT, DOUBLE, DECIMAL, NUMERIC, BIT, CHAR,
2530      * VARCHAR</code> or <code>LONGVARCHAR</code> value. The bold SQL type
2531      * designates the recommended return type.
2532      * @see #getShort(int)
2533      */
2534     public short getShort(String columnName) throws SQLException {
2535         return getShort(getColIdxByName(columnName));
2536     }
2537 
2538     /**
2539      * Retrieves the value stored in the designated column
2540      * of the current row as an <code>int</code> value.
2541      *
2542      * @param columnName a <code>String</code> object giving the SQL name of
2543      *        a column in this <code>CachedRowSetImpl</code> object
2544      * @return the column value; if the value is SQL <code>NULL</code>,
2545      *         the result is <code>0</code>
2546      * @throws SQLException if (1) the given column name is not the name
2547      * of a column in this rowset,
2548      * (2) the cursor is not on one of this rowset's rows or its
2549      * insert row, or (3) the designated column does not store an
2550      * SQL <code>TINYINT, SMALLINT, <b>INTEGER</b>, BIGINT, REAL
2551      * FLOAT, DOUBLE, DECIMAL, NUMERIC, BIT, CHAR, VARCHAR</code>
2552      * or <code>LONGVARCHAR</code> value. The bold SQL type designates the
2553      * recommended return type.
2554      */
2555     public int getInt(String columnName) throws SQLException {
2556         return getInt(getColIdxByName(columnName));
2557     }
2558 
2559     /**
2560      * Retrieves the value stored in the designated column
2561      * of the current row as a <code>long</code> value.
2562      *
2563      * @param columnName a <code>String</code> object giving the SQL name of
2564      *        a column in this <code>CachedRowSetImpl</code> object
2565      * @return the column value; if the value is SQL <code>NULL</code>,
2566      *         the result is <code>0</code>
2567      * @throws SQLException if (1) the given column name is not the name of
2568      * a column in this rowset, (2) the cursor is not on one of
2569      * this rowset's rows or its insert row, or (3) the designated
2570      * column does not store an SQL <code>TINYINT, SMALLINT, INTEGER
2571      * <b>BIGINT</b>, REAL, FLOAT, DOUBLE, DECIMAL, NUMERIC, BIT, CHAR,
2572      * VARCHAR</code> or <code>LONGVARCHAR</code> value. The bold SQL type
2573      * designates the recommended return type.
2574      * @see #getLong(int)
2575      */
2576     public long getLong(String columnName) throws SQLException {
2577         return getLong(getColIdxByName(columnName));
2578     }
2579 
2580     /**
2581      * Retrieves the value stored in the designated column
2582      * of the current row as a <code>float</code> value.
2583      *
2584      * @param columnName a <code>String</code> object giving the SQL name of
2585      *        a column in this <code>CachedRowSetImpl</code> object
2586      * @return the column value; if the value is SQL <code>NULL</code>,
2587      *         the result is <code>0</code>
2588      * @throws SQLException if (1) the given column name is not the name of
2589      * a column in this rowset, (2) the cursor is not on one of
2590      * this rowset's rows or its insert row, or (3) the designated
2591      * column does not store an SQL <code>TINYINT, SMALLINT, INTEGER
2592      * BIGINT, <b>REAL</b>, FLOAT, DOUBLE, DECIMAL, NUMERIC, BIT, CHAR,
2593      * VARCHAR</code> or <code>LONGVARCHAR</code> value. The bold SQL type
2594      * designates the recommended return type.
2595      * @see #getFloat(String)
2596      */
2597     public float getFloat(String columnName) throws SQLException {
2598         return getFloat(getColIdxByName(columnName));
2599     }
2600 
2601     /**
2602      * Retrieves the value stored in the designated column
2603      * of the current row of this <code>CachedRowSetImpl</code> object
2604      * as a <code>double</code> value.
2605      *
2606      * @param columnName a <code>String</code> object giving the SQL name of
2607      *        a column in this <code>CachedRowSetImpl</code> object
2608      * @return the column value; if the value is SQL <code>NULL</code>,
2609      *         the result is <code>0</code>
2610      * @throws SQLException if (1) the given column name is not the name of
2611      * a column in this rowset, (2) the cursor is not on one of
2612      * this rowset's rows or its insert row, or (3) the designated
2613      * column does not store an SQL <code>TINYINT, SMALLINT, INTEGER
2614      * BIGINT, REAL, <b>FLOAT</b>, <b>DOUBLE</b>, DECIMAL, NUMERIC, BIT, CHAR,
2615      * VARCHAR</code> or <code>LONGVARCHAR</code> value. The bold SQL type
2616      * designates the recommended return types.
2617      * @see #getDouble(int)
2618      */
2619     public double getDouble(String columnName) throws SQLException {
2620         return getDouble(getColIdxByName(columnName));
2621     }
2622 
2623     /**
2624      * Retrieves the value stored in the designated column
2625      * of the current row as a <code>java.math.BigDecimal</code> object.
2626      *
2627      * @param columnName a <code>String</code> object giving the SQL name of
2628      *        a column in this <code>CachedRowSetImpl</code> object
2629      * @param scale the number of digits to the right of the decimal point
2630      * @return a java.math.BugDecimal object with <code><i>scale</i></code>
2631      * number of digits to the right of the decimal point.
2632      * @throws SQLException if (1) the given column name is not the name of
2633      * a column in this rowset, (2) the cursor is not on one of
2634      * this rowset's rows or its insert row, or (3) the designated
2635      * column does not store an SQL <code>TINYINT, SMALLINT, INTEGER
2636      * BIGINT, REAL, FLOAT, DOUBLE, <b>DECIMAL</b>, <b>NUMERIC</b>, BIT CHAR,
2637      * VARCHAR</code> or <code>LONGVARCHAR</code> value. The bold SQL type
2638      * designates the recommended return type that this method is used to
2639      * retrieve.
2640      * @deprecated Use the <code>getBigDecimal(String columnName)</code>
2641      *             method instead
2642      */
2643     @Deprecated
2644     public BigDecimal getBigDecimal(String columnName, int scale) throws SQLException {
2645         return getBigDecimal(getColIdxByName(columnName), scale);
2646     }
2647 
2648     /**
2649      * Retrieves the value stored in the designated column
2650      * of the current row as a <code>byte</code> array.
2651      * The bytes represent the raw values returned by the driver.
2652      *
2653      * @param columnName a <code>String</code> object giving the SQL name of
2654      *        a column in this <code>CachedRowSetImpl</code> object
2655      * @return the column value as a <code>byte</code> array in the Java programming
2656      * language; if the value is SQL <code>NULL</code>, the result is <code>null</code>
2657      * @throws SQLException if (1) the given column name is not the name of
2658      * a column in this rowset, (2) the cursor is not on one of
2659      * this rowset's rows or its insert row, or (3) the designated
2660      * column does not store an SQL <code><b>BINARY</b>, <b>VARBINARY</b>
2661      * </code> or <code>LONGVARBINARY</code> values
2662      * The bold SQL type designates the recommended return type.
2663      * @see #getBytes(int)
2664      */
2665     public byte[] getBytes(String columnName) throws SQLException {
2666         return getBytes(getColIdxByName(columnName));
2667     }
2668 
2669     /**
2670      * Retrieves the value stored in the designated column
2671      * of the current row as a <code>java.sql.Date</code> object.
2672      *
2673      * @param columnName a <code>String</code> object giving the SQL name of
2674      *        a column in this <code>CachedRowSetImpl</code> object
2675      * @return the column value; if the value is SQL <code>NULL</code>,
2676      *         the result is <code>null</code>
2677      * @throws SQLException if (1) the given column name is not the name of
2678      *            a column in this rowset, (2) the cursor is not on one of
2679      *            this rowset's rows or its insert row, or (3) the designated
2680      *            column does not store an SQL <code>DATE</code> or
2681      *            <code>TIMESTAMP</code> value
2682      */
2683     public java.sql.Date getDate(String columnName) throws SQLException {
2684         return getDate(getColIdxByName(columnName));
2685     }
2686 
2687     /**
2688      * Retrieves the value stored in the designated column
2689      * of the current row as a <code>java.sql.Time</code> object.
2690      *
2691      * @param columnName a <code>String</code> object giving the SQL name of
2692      *        a column in this <code>CachedRowSetImpl</code> object
2693      * @return the column value; if the value is SQL <code>NULL</code>,
2694      *         the result is <code>null</code>
2695      * @throws SQLException if the given column name does not match one of
2696      *            this rowset's column names or the cursor is not on one of
2697      *            this rowset's rows or its insert row
2698      */
2699     public java.sql.Time getTime(String columnName) throws SQLException {
2700         return getTime(getColIdxByName(columnName));
2701     }
2702 
2703     /**
2704      * Retrieves the value stored in the designated column
2705      * of the current row as a <code>java.sql.Timestamp</code> object.
2706      *
2707      * @param columnName a <code>String</code> object giving the SQL name of
2708      *        a column in this <code>CachedRowSetImpl</code> object
2709      * @return the column value; if the value is SQL <code>NULL</code>,
2710      *         the result is <code>null</code>
2711      * @throws SQLException if the given column name does not match one of
2712      *            this rowset's column names or the cursor is not on one of
2713      *            this rowset's rows or its insert row
2714      */
2715     public java.sql.Timestamp getTimestamp(String columnName) throws SQLException {
2716         return getTimestamp(getColIdxByName(columnName));
2717     }
2718 
2719     /**
2720      * Retrieves the value of the designated column in the current row of this
2721      * <code>CachedRowSetImpl</code> object as a <code>java.io.InputStream</code>
2722      * object.
2723      *
2724      * A column value can be retrieved as a stream of ASCII characters
2725      * and then read in chunks from the stream. This method is particularly
2726      * suitable for retrieving large <code>LONGVARCHAR</code> values. The
2727      * <code>SyncProvider</code> will rely on the JDBC driver to do any necessary
2728      * conversion from the database format into ASCII format.
2729      *
2730      * <P><B>Note:</B> All the data in the returned stream must
2731      * be read prior to getting the value of any other column. The
2732      * next call to a <code>getXXX</code> method implicitly closes the stream.
2733      *
2734      * @param columnName a <code>String</code> object giving the SQL name of
2735      *        a column in this <code>CachedRowSetImpl</code> object
2736      * @return a Java input stream that delivers the database column value
2737      *         as a stream of one-byte ASCII characters.  If the value is SQL
2738      *         <code>NULL</code>, the result is <code>null</code>.
2739      * @throws SQLException if (1) the given column name is not the name of
2740      * a column in this rowset
2741      * (2) the cursor is not on one of this rowset's rows or its
2742      * insert row, or (3) the designated column does not store an
2743      * SQL <code>CHAR, VARCHAR</code>, <code><b>LONGVARCHAR</b></code>
2744      * <code>BINARY, VARBINARY</code> or <code>LONGVARBINARY</code> value. The
2745      * bold SQL type designates the recommended return types that this method is
2746      * used to retrieve.
2747      * @see #getAsciiStream(int)
2748      */
2749     public java.io.InputStream getAsciiStream(String columnName) throws SQLException {
2750         return getAsciiStream(getColIdxByName(columnName));
2751 
2752     }
2753 
2754     /**
2755      * A column value can be retrieved as a stream of Unicode characters
2756      * and then read in chunks from the stream.  This method is particularly
2757      * suitable for retrieving large <code>LONGVARCHAR</code> values.
2758      * The JDBC driver will do any necessary conversion from the database
2759      * format into Unicode.
2760      *
2761      * <P><B>Note:</B> All the data in the returned stream must
2762      * be read prior to getting the value of any other column. The
2763      * next call to a <code>getXXX</code> method implicitly closes the stream.
2764      *
2765      * @param columnName a <code>String</code> object giving the SQL name of
2766      *        a column in this <code>CachedRowSetImpl</code> object
2767      * @return a Java input stream that delivers the database column value
2768      *         as a stream of two-byte Unicode characters.  If the value is
2769      *         SQL <code>NULL</code>, the result is <code>null</code>.
2770      * @throws SQLException if the given column name does not match one of
2771      *            this rowset's column names or the cursor is not on one of
2772      *            this rowset's rows or its insert row
2773      * @deprecated use the method <code>getCharacterStream</code> instead
2774      */
2775     @Deprecated
2776     public java.io.InputStream getUnicodeStream(String columnName) throws SQLException {
2777         return getUnicodeStream(getColIdxByName(columnName));
2778     }
2779 
2780     /**
2781      * Retrieves the value of the designated column in the current row of this
2782      * <code>CachedRowSetImpl</code> object as a <code>java.io.InputStream</code>
2783      * object.
2784      * <P>
2785      * A column value can be retrieved as a stream of uninterpreted bytes
2786      * and then read in chunks from the stream.  This method is particularly
2787      * suitable for retrieving large <code>LONGVARBINARY</code> values.
2788      *
2789      * <P><B>Note:</B> All the data in the returned stream must be
2790      * read prior to getting the value of any other column. The next
2791      * call to a get method implicitly closes the stream. Also, a
2792      * stream may return <code>0</code> for <code>CachedRowSetImpl.available()</code>
2793      * whether there is data available or not.
2794      *
2795      * @param columnName a <code>String</code> object giving the SQL name of
2796      *        a column in this <code>CachedRowSetImpl</code> object
2797      * @return a Java input stream that delivers the database column value
2798      *         as a stream of uninterpreted bytes.  If the value is SQL
2799      *         <code>NULL</code>, the result is <code>null</code>.
2800      * @throws SQLException if (1) the given column name is unknown,
2801      * (2) the cursor is not on one of this rowset's rows or its
2802      * insert row, or (3) the designated column does not store an
2803      * SQL <code>BINARY, VARBINARY</code> or <code><b>LONGVARBINARY</b></code>
2804      * The bold type indicates the SQL type that this method is recommened
2805      * to retrieve.
2806      * @see #getBinaryStream(int)
2807      *
2808      */
2809     public java.io.InputStream getBinaryStream(String columnName) throws SQLException {
2810         return getBinaryStream(getColIdxByName(columnName));
2811     }
2812 
2813 
2814     // Advanced features:
2815 
2816     /**
2817      * The first warning reported by calls on this <code>CachedRowSetImpl</code>
2818      * object is returned. Subsequent <code>CachedRowSetImpl</code> warnings will
2819      * be chained to this <code>SQLWarning</code>.
2820      *
2821      * <P>The warning chain is automatically cleared each time a new
2822      * row is read.
2823      *
2824      * <P><B>Note:</B> This warning chain only covers warnings caused
2825      * by <code>ResultSet</code> methods.  Any warning caused by statement
2826      * methods (such as reading OUT parameters) will be chained on the
2827      * <code>Statement</code> object.
2828      *
2829      * @return the first SQLWarning or null
2830      */
2831     public SQLWarning getWarnings() {
2832         return sqlwarn;
2833     }
2834 
2835     /**
2836      * Clears all the warnings reporeted for the <code>CachedRowSetImpl</code>
2837      * object. After a call to this method, the <code>getWarnings</code> method
2838      * returns <code>null</code> until a new warning is reported for this
2839      * <code>CachedRowSetImpl</code> object.
2840      */
2841     public void clearWarnings() {
2842         sqlwarn = null;
2843     }
2844 
2845     /**
2846      * Retrieves the name of the SQL cursor used by this
2847      * <code>CachedRowSetImpl</code> object.
2848      *
2849      * <P>In SQL, a result table is retrieved through a cursor that is
2850      * named. The current row of a <code>ResultSet</code> can be updated or deleted
2851      * using a positioned update/delete statement that references the
2852      * cursor name. To ensure that the cursor has the proper isolation
2853      * level to support an update operation, the cursor's <code>SELECT</code>
2854      * statement should be of the form <code>select for update</code>.
2855      * If the <code>for update</code> clause
2856      * is omitted, positioned updates may fail.
2857      *
2858      * <P>JDBC supports this SQL feature by providing the name of the
2859      * SQL cursor used by a <code>ResultSet</code> object. The current row
2860      * of a result set is also the current row of this SQL cursor.
2861      *
2862      * <P><B>Note:</B> If positioned updates are not supported, an
2863      * <code>SQLException</code> is thrown.
2864      *
2865      * @return the SQL cursor name for this <code>CachedRowSetImpl</code> object's
2866      *         cursor
2867      * @throws SQLException if an error occurs
2868      */
2869     public String getCursorName() throws SQLException {
2870         throw new SQLException(resBundle.handleGetObject("cachedrowsetimpl.posupdate").toString());
2871     }
2872 
2873     /**
2874      * Retrieves a <code>ResultSetMetaData</code> object instance that
2875      * contains information about the <code>CachedRowSet</code> object.
2876      * However, applications should cast the returned object to a
2877      * <code>RowSetMetaData</code> interface implementation. In the
2878      * reference implementation, this cast can be done on the
2879      * <code>RowSetMetaDataImpl</code> class.
2880      * <P>
2881      * For example:
2882      * <pre>
2883      * CachedRowSet crs = new CachedRowSetImpl();
2884      * RowSetMetaDataImpl metaData =
2885      *     (RowSetMetaDataImpl)crs.getMetaData();
2886      * // Set the number of columns in the RowSet object for
2887      * // which this RowSetMetaDataImpl object was created to the
2888      * // given number.
2889      * metaData.setColumnCount(3);
2890      * crs.setMetaData(metaData);
2891      * </pre>
2892      *
2893      * @return the <code>ResultSetMetaData</code> object that describes this
2894      *         <code>CachedRowSetImpl</code> object's columns
2895      * @throws SQLException if an error occurs in generating the RowSet
2896      * meta data; or if the <code>CachedRowSetImpl</code> is empty.
2897      * @see javax.sql.RowSetMetaData
2898      */
2899     public ResultSetMetaData getMetaData() throws SQLException {
2900         return (ResultSetMetaData)RowSetMD;
2901     }
2902 
2903 
2904     /**
2905      * Retrieves the value of the designated column in the current row
2906      * of this <code>CachedRowSetImpl</code> object as an
2907      * <code>Object</code> value.
2908      * <P>
2909      * The type of the <code>Object</code> will be the default
2910      * Java object type corresponding to the column's SQL type,
2911      * following the mapping for built-in types specified in the JDBC 3.0
2912      * specification.
2913      * <P>
2914      * This method may also be used to read datatabase-specific
2915      * abstract data types.
2916      * <P>
2917      * This implementation of the method <code>getObject</code> extends its
2918      * behavior so that it gets the attributes of an SQL structured type
2919      * as an array of <code>Object</code> values.  This method also custom
2920      * maps SQL user-defined types to classes in the Java programming language.
2921      * When the specified column contains
2922      * a structured or distinct value, the behavior of this method is as
2923      * if it were a call to the method <code>getObject(columnIndex,
2924      * this.getStatement().getConnection().getTypeMap())</code>.
2925      *
2926      * @param columnIndex the first column is <code>1</code>, the second
2927      *        is <code>2</code>, and so on; must be <code>1</code> or larger
2928      *        and equal to or less than the number of columns in the rowset
2929      * @return a <code>java.lang.Object</code> holding the column value;
2930      *         if the value is SQL <code>NULL</code>, the result is <code>null</code>
2931      * @throws SQLException if the given column index is out of bounds,
2932      *            the cursor is not on a valid row, or there is a problem getting
2933      *            the <code>Class</code> object for a custom mapping
2934      * @see #getObject(String)
2935      */
2936     public Object getObject(int columnIndex) throws SQLException {
2937         Object value;
2938         Map<String, Class<?>> map;
2939 
2940         // sanity check.
2941         checkIndex(columnIndex);
2942         // make sure the cursor is on a valid row
2943         checkCursor();
2944 
2945         setLastValueNull(false);
2946         value = getCurrentRow().getColumnObject(columnIndex);
2947 
2948         // check for SQL NULL
2949         if (value == null) {
2950             setLastValueNull(true);
2951             return null;
2952         }
2953         if (value instanceof Struct) {
2954             Struct s = (Struct)value;
2955             map = getTypeMap();
2956             // look up the class in the map
2957             Class<?> c = map.get(s.getSQLTypeName());
2958             if (c != null) {
2959                 // create new instance of the class
2960                 SQLData obj = null;
2961                 try {
2962                     obj = (SQLData)c.newInstance();
2963                 } catch (java.lang.InstantiationException ex) {
2964                     throw new SQLException(MessageFormat.format(resBundle.handleGetObject("cachedrowsetimpl.unableins").toString(),
2965                     ex.getMessage()));
2966                 } catch (java.lang.IllegalAccessException ex) {
2967                     throw new SQLException(MessageFormat.format(resBundle.handleGetObject("cachedrowsetimpl.unableins").toString(),
2968                     ex.getMessage()));
2969                 }
2970                 // get the attributes from the struct
2971                 Object attribs[] = s.getAttributes(map);
2972                 // create the SQLInput "stream"
2973                 SQLInputImpl sqlInput = new SQLInputImpl(attribs, map);
2974                 // read the values...
2975                 obj.readSQL(sqlInput, s.getSQLTypeName());
2976                 return (Object)obj;
2977             }
2978         }
2979         return value;
2980     }
2981 
2982     /**
2983      * Retrieves the value of the designated column in the current row
2984      * of this <code>CachedRowSetImpl</code> object as an
2985      * <code>Object</code> value.
2986      * <P>
2987      * The type of the <code>Object</code> will be the default
2988      * Java object type corresponding to the column's SQL type,
2989      * following the mapping for built-in types specified in the JDBC 3.0
2990      * specification.
2991      * <P>
2992      * This method may also be used to read datatabase-specific
2993      * abstract data types.
2994      * <P>
2995      * This implementation of the method <code>getObject</code> extends its
2996      * behavior so that it gets the attributes of an SQL structured type
2997      * as an array of <code>Object</code> values.  This method also custom
2998      * maps SQL user-defined types to classes
2999      * in the Java programming language. When the specified column contains
3000      * a structured or distinct value, the behavior of this method is as
3001      * if it were a call to the method <code>getObject(columnIndex,
3002      * this.getStatement().getConnection().getTypeMap())</code>.
3003      *
3004      * @param columnName a <code>String</code> object that must match the
3005      *        SQL name of a column in this rowset, ignoring case
3006      * @return a <code>java.lang.Object</code> holding the column value;
3007      *         if the value is SQL <code>NULL</code>, the result is <code>null</code>
3008      * @throws SQLException if (1) the given column name does not match one of
3009      *            this rowset's column names, (2) the cursor is not
3010      *            on a valid row, or (3) there is a problem getting
3011      *            the <code>Class</code> object for a custom mapping
3012      * @see #getObject(int)
3013      */
3014     public Object getObject(String columnName) throws SQLException {
3015         return getObject(getColIdxByName(columnName));
3016     }
3017 
3018     //----------------------------------------------------------------
3019 
3020     /**
3021      * Maps the given column name for one of this <code>CachedRowSetImpl</code>
3022      * object's columns to its column number.
3023      *
3024      * @param columnName a <code>String</code> object that must match the
3025      *        SQL name of a column in this rowset, ignoring case
3026      * @return the column index of the given column name
3027      * @throws SQLException if the given column name does not match one
3028      *            of this rowset's column names
3029      */
3030     public int findColumn(String columnName) throws SQLException {
3031         return getColIdxByName(columnName);
3032     }
3033 
3034 
3035     //--------------------------JDBC 2.0-----------------------------------
3036 
3037     //---------------------------------------------------------------------
3038     // Getter's and Setter's
3039     //---------------------------------------------------------------------
3040 
3041     /**
3042      * Retrieves the value stored in the designated column
3043      * of the current row as a <code>java.io.Reader</code> object.
3044      *
3045      * <P><B>Note:</B> All the data in the returned stream must
3046      * be read prior to getting the value of any other column. The
3047      * next call to a <code>getXXX</code> method implicitly closes the stream.
3048      *
3049      * @param columnIndex the first column is <code>1</code>, the second
3050      *        is <code>2</code>, and so on; must be <code>1</code> or larger
3051      *        and equal to or less than the number of columns in the rowset
3052      * @return a Java character stream that delivers the database column value
3053      * as a stream of two-byte unicode characters in a
3054      * <code>java.io.Reader</code> object.  If the value is
3055      * SQL <code>NULL</code>, the result is <code>null</code>.
3056      * @throws SQLException if (1) the given column index is out of bounds,
3057      * (2) the cursor is not on one of this rowset's rows or its
3058      * insert row, or (3) the designated column does not store an
3059      * SQL <code>CHAR, VARCHAR, <b>LONGVARCHAR</b>, BINARY, VARBINARY</code> or
3060      * <code>LONGVARBINARY</code> value.
3061      * The bold SQL type designates the recommended return type.
3062      * @see #getCharacterStream(String)
3063      */
3064     public java.io.Reader getCharacterStream(int columnIndex) throws SQLException{
3065 
3066         // sanity check.
3067         checkIndex(columnIndex);
3068         // make sure the cursor is on a valid row
3069         checkCursor();
3070 
3071         if (isBinary(RowSetMD.getColumnType(columnIndex))) {
3072             Object value = getCurrentRow().getColumnObject(columnIndex);
3073             if (value == null) {
3074                 lastValueNull = true;
3075                 return null;
3076             }
3077             charStream = new InputStreamReader
3078             (new ByteArrayInputStream((byte[])value));
3079         } else if (isString(RowSetMD.getColumnType(columnIndex))) {
3080             Object value = getCurrentRow().getColumnObject(columnIndex);
3081             if (value == null) {
3082                 lastValueNull = true;
3083                 return null;
3084             }
3085             charStream = new StringReader(value.toString());
3086         } else {
3087             throw new SQLException(resBundle.handleGetObject("cachedrowsetimpl.dtypemismt").toString());
3088         }
3089 
3090         return charStream;
3091     }
3092 
3093     /**
3094      * Retrieves the value stored in the designated column
3095      * of the current row as a <code>java.io.Reader</code> object.
3096      *
3097      * <P><B>Note:</B> All the data in the returned stream must
3098      * be read prior to getting the value of any other column. The
3099      * next call to a <code>getXXX</code> method implicitly closes the stream.
3100      *
3101      * @param columnName a <code>String</code> object giving the SQL name of
3102      *        a column in this <code>CachedRowSetImpl</code> object
3103      * @return a Java input stream that delivers the database column value
3104      *         as a stream of two-byte Unicode characters.  If the value is
3105      *         SQL <code>NULL</code>, the result is <code>null</code>.
3106      * @throws SQLException if (1) the given column name is not the name of
3107      * a column in this rowset, (2) the cursor is not on one of
3108      * this rowset's rows or its insert row, or (3) the designated
3109      * column does not store an SQL <code>CHAR, VARCHAR, <b>LONGVARCHAR</b>,
3110      * BINARY, VARYBINARY</code> or <code>LONGVARBINARY</code> value.
3111      * The bold SQL type designates the recommended return type.
3112      */
3113     public java.io.Reader getCharacterStream(String columnName) throws SQLException {
3114         return getCharacterStream(getColIdxByName(columnName));
3115     }
3116 
3117     /**
3118      * Retrieves the value of the designated column in the current row
3119      * of this <code>CachedRowSetImpl</code> object as a
3120      * <code>java.math.BigDecimal</code> object.
3121      *
3122      * @param columnIndex the first column is <code>1</code>, the second
3123      *        is <code>2</code>, and so on; must be <code>1</code> or larger
3124      *        and equal to or less than the number of columns in the rowset
3125      * @return a <code>java.math.BigDecimal</code> value with full precision;
3126      *         if the value is SQL <code>NULL</code>, the result is <code>null</code>
3127      * @throws SQLException if (1) the given column index is out of bounds,
3128      * (2) the cursor is not on one of this rowset's rows or its
3129      * insert row, or (3) the designated column does not store an
3130      * SQL <code>TINYINT, SMALLINT, INTEGER, BIGINT, REAL,
3131      * FLOAT, DOUBLE, <b>DECIMAL</b>, <b>NUMERIC</b>, BIT, CHAR, VARCHAR</code>
3132      * or <code>LONGVARCHAR</code> value. The bold SQL type designates the
3133      * recommended return types that this method is used to retrieve.
3134      * @see #getBigDecimal(String)
3135      */
3136     public BigDecimal getBigDecimal(int columnIndex) throws SQLException {
3137         Object value;
3138 
3139         // sanity check.
3140         checkIndex(columnIndex);
3141         // make sure the cursor is on a valid row
3142         checkCursor();
3143 
3144         setLastValueNull(false);
3145         value = getCurrentRow().getColumnObject(columnIndex);
3146 
3147         // check for SQL NULL
3148         if (value == null) {
3149             setLastValueNull(true);
3150             return null;
3151         }
3152         try {
3153             return (new BigDecimal(value.toString().trim()));
3154         } catch (NumberFormatException ex) {
3155             throw new SQLException(MessageFormat.format(resBundle.handleGetObject("cachedrowsetimpl.doublefail").toString(),
3156                 new Object[] {value.toString().trim(), columnIndex}));
3157         }
3158     }
3159 
3160     /**
3161      * Retrieves the value of the designated column in the current row
3162      * of this <code>CachedRowSetImpl</code> object as a
3163      * <code>java.math.BigDecimal</code> object.
3164      *
3165      * @param columnName a <code>String</code> object that must match the
3166      *        SQL name of a column in this rowset, ignoring case
3167      * @return a <code>java.math.BigDecimal</code> value with full precision;
3168      *         if the value is SQL <code>NULL</code>, the result is <code>null</code>
3169      * @throws SQLException if (1) the given column name is not the name of
3170      * a column in this rowset, (2) the cursor is not on one of
3171      * this rowset's rows or its insert row, or (3) the designated
3172      * column does not store an SQL <code>TINYINT, SMALLINT, INTEGER
3173      * BIGINT, REAL, FLOAT, DOUBLE, <b>DECIMAL</b>, <b>NUMERIC</b>, BIT CHAR,
3174      * VARCHAR</code> or <code>LONGVARCHAR</code> value. The bold SQL type
3175      * designates the recommended return type that this method is used to
3176      * retrieve
3177      * @see #getBigDecimal(int)
3178      */
3179     public BigDecimal getBigDecimal(String columnName) throws SQLException {
3180         return getBigDecimal(getColIdxByName(columnName));
3181     }
3182 
3183     //---------------------------------------------------------------------
3184     // Traversal/Positioning
3185     //---------------------------------------------------------------------
3186 
3187     /**
3188      * Returns the number of rows in this <code>CachedRowSetImpl</code> object.
3189      *
3190      * @return number of rows in the rowset
3191      */
3192     public int size() {
3193         return numRows;
3194     }
3195 
3196     /**
3197      * Indicates whether the cursor is before the first row in this
3198      * <code>CachedRowSetImpl</code> object.
3199      *
3200      * @return <code>true</code> if the cursor is before the first row;
3201      *         <code>false</code> otherwise or if the rowset contains no rows
3202      * @throws SQLException if an error occurs
3203      */
3204     public boolean isBeforeFirst() throws SQLException {
3205         if (cursorPos == 0 && numRows > 0) {
3206             return true;
3207         } else {
3208             return false;
3209         }
3210     }
3211 
3212     /**
3213      * Indicates whether the cursor is after the last row in this
3214      * <code>CachedRowSetImpl</code> object.
3215      *
3216      * @return <code>true</code> if the cursor is after the last row;
3217      *         <code>false</code> otherwise or if the rowset contains no rows
3218      * @throws SQLException if an error occurs
3219      */
3220     public boolean isAfterLast() throws SQLException {
3221         if (cursorPos == numRows+1 && numRows > 0) {
3222             return true;
3223         } else {
3224             return false;
3225         }
3226     }
3227 
3228     /**
3229      * Indicates whether the cursor is on the first row in this
3230      * <code>CachedRowSetImpl</code> object.
3231      *
3232      * @return <code>true</code> if the cursor is on the first row;
3233      *         <code>false</code> otherwise or if the rowset contains no rows
3234      * @throws SQLException if an error occurs
3235      */
3236     public boolean isFirst() throws SQLException {
3237         // this becomes nasty because of deletes.
3238         int saveCursorPos = cursorPos;
3239         int saveAbsoluteCursorPos = absolutePos;
3240         internalFirst();
3241         if (cursorPos == saveCursorPos) {
3242             return true;
3243         } else {
3244             cursorPos = saveCursorPos;
3245             absolutePos = saveAbsoluteCursorPos;
3246             return false;
3247         }
3248     }
3249 
3250     /**
3251      * Indicates whether the cursor is on the last row in this
3252      * <code>CachedRowSetImpl</code> object.
3253      * <P>
3254      * Note: Calling the method <code>isLast</code> may be expensive
3255      * because the JDBC driver might need to fetch ahead one row in order
3256      * to determine whether the current row is the last row in this rowset.
3257      *
3258      * @return <code>true</code> if the cursor is on the last row;
3259      *         <code>false</code> otherwise or if this rowset contains no rows
3260      * @throws SQLException if an error occurs
3261      */
3262     public boolean isLast() throws SQLException {
3263         int saveCursorPos = cursorPos;
3264         int saveAbsoluteCursorPos = absolutePos;
3265         boolean saveShowDeleted = getShowDeleted();
3266         setShowDeleted(true);
3267         internalLast();
3268         if (cursorPos == saveCursorPos) {
3269             setShowDeleted(saveShowDeleted);
3270             return true;
3271         } else {
3272             setShowDeleted(saveShowDeleted);
3273             cursorPos = saveCursorPos;
3274             absolutePos = saveAbsoluteCursorPos;
3275             return false;
3276         }
3277     }
3278 
3279     /**
3280      * Moves this <code>CachedRowSetImpl</code> object's cursor to the front of
3281      * the rowset, just before the first row. This method has no effect if
3282      * this rowset contains no rows.
3283      *
3284      * @throws SQLException if an error occurs or the type of this rowset
3285      *            is <code>ResultSet.TYPE_FORWARD_ONLY</code>
3286      */
3287     public void beforeFirst() throws SQLException {
3288        if (getType() == ResultSet.TYPE_FORWARD_ONLY) {
3289             throw new SQLException(resBundle.handleGetObject("cachedrowsetimpl.beforefirst").toString());
3290         }
3291         cursorPos = 0;
3292         absolutePos = 0;
3293         notifyCursorMoved();
3294     }
3295 
3296     /**
3297      * Moves this <code>CachedRowSetImpl</code> object's cursor to the end of
3298      * the rowset, just after the last row. This method has no effect if
3299      * this rowset contains no rows.
3300      *
3301      * @throws SQLException if an error occurs
3302      */
3303     public void afterLast() throws SQLException {
3304         if (numRows > 0) {
3305             cursorPos = numRows + 1;
3306             absolutePos = 0;
3307             notifyCursorMoved();
3308         }
3309     }
3310 
3311     /**
3312      * Moves this <code>CachedRowSetImpl</code> object's cursor to the first row
3313      * and returns <code>true</code> if the operation was successful.  This
3314      * method also notifies registered listeners that the cursor has moved.
3315      *
3316      * @return <code>true</code> if the cursor is on a valid row;
3317      *         <code>false</code> otherwise or if there are no rows in this
3318      *         <code>CachedRowSetImpl</code> object
3319      * @throws SQLException if the type of this rowset
3320      *            is <code>ResultSet.TYPE_FORWARD_ONLY</code>
3321      */
3322     public boolean first() throws SQLException {
3323         if(getType() == ResultSet.TYPE_FORWARD_ONLY) {
3324             throw new SQLException(resBundle.handleGetObject("cachedrowsetimpl.first").toString());
3325         }
3326 
3327         // move and notify
3328         boolean ret = this.internalFirst();
3329         notifyCursorMoved();
3330 
3331         return ret;
3332     }
3333 
3334     /**
3335      * Moves this <code>CachedRowSetImpl</code> object's cursor to the first
3336      * row and returns <code>true</code> if the operation is successful.
3337      * <P>
3338      * This method is called internally by the methods <code>first</code>,
3339      * <code>isFirst</code>, and <code>absolute</code>.
3340      * It in turn calls the method <code>internalNext</code> in order to
3341      * handle the case where the first row is a deleted row that is not visible.
3342      * <p>
3343      * This is a implementation only method and is not required as a standard
3344      * implementation of the <code>CachedRowSet</code> interface.
3345      *
3346      * @return <code>true</code> if the cursor moved to the first row;
3347      *         <code>false</code> otherwise
3348      * @throws SQLException if an error occurs
3349      */
3350     protected boolean internalFirst() throws SQLException {
3351         boolean ret = false;
3352 
3353         if (numRows > 0) {
3354             cursorPos = 1;
3355             if ((getShowDeleted() == false) && (rowDeleted() == true)) {
3356                 ret = internalNext();
3357             } else {
3358                 ret = true;
3359             }
3360         }
3361 
3362         if (ret == true)
3363             absolutePos = 1;
3364         else
3365             absolutePos = 0;
3366 
3367         return ret;
3368     }
3369 
3370     /**
3371      * Moves this <code>CachedRowSetImpl</code> object's cursor to the last row
3372      * and returns <code>true</code> if the operation was successful.  This
3373      * method also notifies registered listeners that the cursor has moved.
3374      *
3375      * @return <code>true</code> if the cursor is on a valid row;
3376      *         <code>false</code> otherwise or if there are no rows in this
3377      *         <code>CachedRowSetImpl</code> object
3378      * @throws SQLException if the type of this rowset
3379      *            is <code>ResultSet.TYPE_FORWARD_ONLY</code>
3380      */
3381     public boolean last() throws SQLException {
3382         if (getType() == ResultSet.TYPE_FORWARD_ONLY) {
3383             throw new SQLException(resBundle.handleGetObject("cachedrowsetimpl.last").toString());
3384         }
3385 
3386         // move and notify
3387         boolean ret = this.internalLast();
3388         notifyCursorMoved();
3389 
3390         return ret;
3391     }
3392 
3393     /**
3394      * Moves this <code>CachedRowSetImpl</code> object's cursor to the last
3395      * row and returns <code>true</code> if the operation is successful.
3396      * <P>
3397      * This method is called internally by the method <code>last</code>
3398      * when rows have been deleted and the deletions are not visible.
3399      * The method <code>internalLast</code> handles the case where the
3400      * last row is a deleted row that is not visible by in turn calling
3401      * the method <code>internalPrevious</code>.
3402      * <p>
3403      * This is a implementation only method and is not required as a standard
3404      * implementation of the <code>CachedRowSet</code> interface.
3405      *
3406      * @return <code>true</code> if the cursor moved to the last row;
3407      *         <code>false</code> otherwise
3408      * @throws SQLException if an error occurs
3409      */
3410     protected boolean internalLast() throws SQLException {
3411         boolean ret = false;
3412 
3413         if (numRows > 0) {
3414             cursorPos = numRows;
3415             if ((getShowDeleted() == false) && (rowDeleted() == true)) {
3416                 ret = internalPrevious();
3417             } else {
3418                 ret = true;
3419             }
3420         }
3421         if (ret == true)
3422             absolutePos = numRows - numDeleted;
3423         else
3424             absolutePos = 0;
3425         return ret;
3426     }
3427 
3428     /**
3429      * Returns the number of the current row in this <code>CachedRowSetImpl</code>
3430      * object. The first row is number 1, the second number 2, and so on.
3431      *
3432      * @return the number of the current row;  <code>0</code> if there is no
3433      *         current row
3434      * @throws SQLException if an error occurs; or if the <code>CacheRowSetImpl</code>
3435      *         is empty
3436      */
3437     public int getRow() throws SQLException {
3438         // are we on a valid row? Valid rows are between first and last
3439         if (numRows > 0 &&
3440         cursorPos > 0 &&
3441         cursorPos < (numRows + 1) &&
3442         (getShowDeleted() == false && rowDeleted() == false)) {
3443             return absolutePos;
3444         } else if (getShowDeleted() == true) {
3445             return cursorPos;
3446         } else {
3447             return 0;
3448         }
3449     }
3450 
3451     /**
3452      * Moves this <code>CachedRowSetImpl</code> object's cursor to the row number
3453      * specified.
3454      *
3455      * <p>If the number is positive, the cursor moves to an absolute row with
3456      * respect to the beginning of the rowset.  The first row is row 1, the second
3457      * is row 2, and so on.  For example, the following command, in which
3458      * <code>crs</code> is a <code>CachedRowSetImpl</code> object, moves the cursor
3459      * to the fourth row, starting from the beginning of the rowset.
3460      * <PRE><code>
3461      *
3462      *    crs.absolute(4);
3463      *
3464      * </code> </PRE>
3465      * <P>
3466      * If the number is negative, the cursor moves to an absolute row position
3467      * with respect to the end of the rowset.  For example, calling
3468      * <code>absolute(-1)</code> positions the cursor on the last row,
3469      * <code>absolute(-2)</code> moves it on the next-to-last row, and so on.
3470      * If the <code>CachedRowSetImpl</code> object <code>crs</code> has five rows,
3471      * the following command moves the cursor to the fourth-to-last row, which
3472      * in the case of a  rowset with five rows, is also the second row, counting
3473      * from the beginning.
3474      * <PRE><code>
3475      *
3476      *    crs.absolute(-4);
3477      *
3478      * </code> </PRE>
3479      *
3480      * If the number specified is larger than the number of rows, the cursor
3481      * will move to the position after the last row. If the number specified
3482      * would move the cursor one or more rows before the first row, the cursor
3483      * moves to the position before the first row.
3484      * <P>
3485      * Note: Calling <code>absolute(1)</code> is the same as calling the
3486      * method <code>first()</code>.  Calling <code>absolute(-1)</code> is the
3487      * same as calling <code>last()</code>.
3488      *
3489      * @param row a positive number to indicate the row, starting row numbering from
3490      *        the first row, which is <code>1</code>; a negative number to indicate
3491      *        the row, starting row numbering from the last row, which is
3492      *        <code>-1</code>; it must not be <code>0</code>
3493      * @return <code>true</code> if the cursor is on the rowset; <code>false</code>
3494      *         otherwise
3495      * @throws SQLException if the given cursor position is <code>0</code> or the
3496      *            type of this rowset is <code>ResultSet.TYPE_FORWARD_ONLY</code>
3497      */
3498     public boolean absolute( int row ) throws SQLException {
3499         if (row == 0 || getType() == ResultSet.TYPE_FORWARD_ONLY) {
3500             throw new SQLException(resBundle.handleGetObject("cachedrowsetimpl.absolute").toString());
3501         }
3502 
3503         if (row > 0) { // we are moving foward
3504             if (row > numRows) {
3505                 // fell off the end
3506                 afterLast();
3507                 return false;
3508             } else {
3509                 if (absolutePos <= 0)
3510                     internalFirst();
3511             }
3512         } else { // we are moving backward
3513             if (cursorPos + row < 0) {
3514                 // fell off the front
3515                 beforeFirst();
3516                 return false;
3517             } else {
3518                 if (absolutePos >= 0)
3519                     internalLast();
3520             }
3521         }
3522 
3523         // Now move towards the absolute row that we're looking for
3524         while (absolutePos != row) {
3525             if (absolutePos < row) {
3526                 if (!internalNext())
3527                     break;
3528             }
3529             else {
3530                 if (!internalPrevious())
3531                     break;
3532             }
3533         }
3534 
3535         notifyCursorMoved();
3536 
3537         if (isAfterLast() || isBeforeFirst()) {
3538             return false;
3539         } else {
3540             return true;
3541         }
3542     }
3543 
3544     /**
3545      * Moves the cursor the specified number of rows from the current
3546      * position, with a positive number moving it forward and a
3547      * negative number moving it backward.
3548      * <P>
3549      * If the number is positive, the cursor moves the specified number of
3550      * rows toward the end of the rowset, starting at the current row.
3551      * For example, the following command, in which
3552      * <code>crs</code> is a <code>CachedRowSetImpl</code> object with 100 rows,
3553      * moves the cursor forward four rows from the current row.  If the
3554      * current row is 50, the cursor would move to row 54.
3555      * <PRE><code>
3556      *
3557      *    crs.relative(4);
3558      *
3559      * </code> </PRE>
3560      * <P>
3561      * If the number is negative, the cursor moves back toward the beginning
3562      * the specified number of rows, starting at the current row.
3563      * For example, calling the method
3564      * <code>absolute(-1)</code> positions the cursor on the last row,
3565      * <code>absolute(-2)</code> moves it on the next-to-last row, and so on.
3566      * If the <code>CachedRowSetImpl</code> object <code>crs</code> has five rows,
3567      * the following command moves the cursor to the fourth-to-last row, which
3568      * in the case of a  rowset with five rows, is also the second row
3569      * from the beginning.
3570      * <PRE><code>
3571      *
3572      *    crs.absolute(-4);
3573      *
3574      * </code> </PRE>
3575      *
3576      * If the number specified is larger than the number of rows, the cursor
3577      * will move to the position after the last row. If the number specified
3578      * would move the cursor one or more rows before the first row, the cursor
3579      * moves to the position before the first row. In both cases, this method
3580      * throws an <code>SQLException</code>.
3581      * <P>
3582      * Note: Calling <code>absolute(1)</code> is the same as calling the
3583      * method <code>first()</code>.  Calling <code>absolute(-1)</code> is the
3584      * same as calling <code>last()</code>.  Calling <code>relative(0)</code>
3585      * is valid, but it does not change the cursor position.
3586      *
3587      * @param rows an <code>int</code> indicating the number of rows to move
3588      *             the cursor, starting at the current row; a positive number
3589      *             moves the cursor forward; a negative number moves the cursor
3590      *             backward; must not move the cursor past the valid
3591      *             rows
3592      * @return <code>true</code> if the cursor is on a row in this
3593      *         <code>CachedRowSetImpl</code> object; <code>false</code>
3594      *         otherwise
3595      * @throws SQLException if there are no rows in this rowset, the cursor is
3596      *         positioned either before the first row or after the last row, or
3597      *         the rowset is type <code>ResultSet.TYPE_FORWARD_ONLY</code>
3598      */
3599     public boolean relative(int rows) throws SQLException {
3600         if (numRows == 0 || isBeforeFirst() ||
3601         isAfterLast() || getType() == ResultSet.TYPE_FORWARD_ONLY) {
3602             throw new SQLException(resBundle.handleGetObject("cachedrowsetimpl.relative").toString());
3603         }
3604 
3605         if (rows == 0) {
3606             return true;
3607         }
3608 
3609         if (rows > 0) { // we are moving forward
3610             if (cursorPos + rows > numRows) {
3611                 // fell off the end
3612                 afterLast();
3613             } else {
3614                 for (int i=0; i < rows; i++) {
3615                     if (!internalNext())
3616                         break;
3617                 }
3618             }
3619         } else { // we are moving backward
3620             if (cursorPos + rows < 0) {
3621                 // fell off the front
3622                 beforeFirst();
3623             } else {
3624                 for (int i=rows; i < 0; i++) {
3625                     if (!internalPrevious())
3626                         break;
3627                 }
3628             }
3629         }
3630         notifyCursorMoved();
3631 
3632         if (isAfterLast() || isBeforeFirst()) {
3633             return false;
3634         } else {
3635             return true;
3636         }
3637     }
3638 
3639     /**
3640      * Moves this <code>CachedRowSetImpl</code> object's cursor to the
3641      * previous row and returns <code>true</code> if the cursor is on
3642      * a valid row or <code>false</code> if it is not.
3643      * This method also notifies all listeners registered with this
3644      * <code>CachedRowSetImpl</code> object that its cursor has moved.
3645      * <P>
3646      * Note: calling the method <code>previous()</code> is not the same
3647      * as calling the method <code>relative(-1)</code>.  This is true
3648      * because it is possible to call <code>previous()</code> from the insert
3649      * row, from after the last row, or from the current row, whereas
3650      * <code>relative</code> may only be called from the current row.
3651      * <P>
3652      * The method <code>previous</code> may used in a <code>while</code>
3653      * loop to iterate through a rowset starting after the last row
3654      * and moving toward the beginning. The loop ends when <code>previous</code>
3655      * returns <code>false</code>, meaning that there are no more rows.
3656      * For example, the following code fragment retrieves all the data in
3657      * the <code>CachedRowSetImpl</code> object <code>crs</code>, which has
3658      * three columns.  Note that the cursor must initially be positioned
3659      * after the last row so that the first call to the method
3660      * <code>previous</code> places the cursor on the last line.
3661      * <PRE> <code>
3662      *
3663      *     crs.afterLast();
3664      *     while (previous()) {
3665      *         String name = crs.getString(1);
3666      *         int age = crs.getInt(2);
3667      *         short ssn = crs.getShort(3);
3668      *         System.out.println(name + "   " + age + "   " + ssn);
3669      *     }
3670      *
3671      * </code> </PRE>
3672      * This method throws an <code>SQLException</code> if the cursor is not
3673      * on a row in the rowset, before the first row, or after the last row.
3674      *
3675      * @return <code>true</code> if the cursor is on a valid row;
3676      *         <code>false</code> if it is before the first row or after the
3677      *         last row
3678      * @throws SQLException if the cursor is not on a valid position or the
3679      *           type of this rowset is <code>ResultSet.TYPE_FORWARD_ONLY</code>
3680      */
3681     public boolean previous() throws SQLException {
3682         if (getType() == ResultSet.TYPE_FORWARD_ONLY) {
3683             throw new SQLException(resBundle.handleGetObject("cachedrowsetimpl.last").toString());
3684         }
3685         /*
3686          * make sure things look sane. The cursor must be
3687          * positioned in the rowset or before first (0) or
3688          * after last (numRows + 1)
3689          */
3690         if (cursorPos < 0 || cursorPos > numRows + 1) {
3691             throw new SQLException(resBundle.handleGetObject("cachedrowsetimpl.invalidcp").toString());
3692         }
3693         // move and notify
3694         boolean ret = this.internalPrevious();
3695         notifyCursorMoved();
3696 
3697         return ret;
3698     }
3699 
3700     /**
3701      * Moves the cursor to the previous row in this <code>CachedRowSetImpl</code>
3702      * object, skipping past deleted rows that are not visible; returns
3703      * <code>true</code> if the cursor is on a row in this rowset and
3704      * <code>false</code> when the cursor goes before the first row.
3705      * <P>
3706      * This method is called internally by the method <code>previous</code>.
3707      * <P>
3708      * This is a implementation only method and is not required as a standard
3709      * implementation of the <code>CachedRowSet</code> interface.
3710      *
3711      * @return <code>true</code> if the cursor is on a row in this rowset;
3712      *         <code>false</code> when the cursor reaches the position before
3713      *         the first row
3714      * @throws SQLException if an error occurs
3715      */
3716     protected boolean internalPrevious() throws SQLException {
3717         boolean ret = false;
3718 
3719         do {
3720             if (cursorPos > 1) {
3721                 --cursorPos;
3722                 ret = true;
3723             } else if (cursorPos == 1) {
3724                 // decrement to before first
3725                 --cursorPos;
3726                 ret = false;
3727                 break;
3728             }
3729         } while ((getShowDeleted() == false) && (rowDeleted() == true));
3730 
3731         /*
3732          * Each call to internalPrevious may move the cursor
3733          * over multiple rows, the absolute position moves one one row
3734          */
3735         if (ret == true)
3736             --absolutePos;
3737         else
3738             absolutePos = 0;
3739 
3740         return ret;
3741     }
3742 
3743 
3744     //---------------------------------------------------------------------
3745     // Updates
3746     //---------------------------------------------------------------------
3747 
3748     /**
3749      * Indicates whether the current row of this <code>CachedRowSetImpl</code>
3750      * object has been updated.  The value returned
3751      * depends on whether this rowset can detect updates: <code>false</code>
3752      * will always be returned if it does not detect updates.
3753      *
3754      * @return <code>true</code> if the row has been visibly updated
3755      *         by the owner or another and updates are detected;
3756      *         <code>false</code> otherwise
3757      * @throws SQLException if the cursor is on the insert row or not
3758      *            not on a valid row
3759      *
3760      * @see DatabaseMetaData#updatesAreDetected
3761      */
3762     public boolean rowUpdated() throws SQLException {
3763         // make sure the cursor is on a valid row
3764         checkCursor();
3765         if (onInsertRow == true) {
3766             throw new SQLException(resBundle.handleGetObject("cachedrowsetimpl.invalidop").toString());
3767         }
3768         return(((Row)getCurrentRow()).getUpdated());
3769     }
3770 
3771     /**
3772      * Indicates whether the designated column of the current row of
3773      * this <code>CachedRowSetImpl</code> object has been updated. The
3774      * value returned depends on whether this rowset can detcted updates:
3775      * <code>false</code> will always be returned if it does not detect updates.
3776      *
3777      * @param idx the index identifier of the column that may be have been updated.
3778      * @return <code>true</code> is the designated column has been updated
3779      * and the rowset detects updates; <code>false</code> if the rowset has not
3780      * been updated or the rowset does not detect updates
3781      * @throws SQLException if the cursor is on the insert row or not
3782      *          on a valid row
3783      * @see DatabaseMetaData#updatesAreDetected
3784      */
3785     public boolean columnUpdated(int idx) throws SQLException {
3786         // make sure the cursor is on a valid row
3787         checkCursor();
3788         if (onInsertRow == true) {
3789             throw new SQLException(resBundle.handleGetObject("cachedrowsetimpl.invalidop").toString());
3790         }
3791         return (((Row)getCurrentRow()).getColUpdated(idx - 1));
3792     }
3793 
3794     /**
3795      * Indicates whether the designated column of the current row of
3796      * this <code>CachedRowSetImpl</code> object has been updated. The
3797      * value returned depends on whether this rowset can detcted updates:
3798      * <code>false</code> will always be returned if it does not detect updates.
3799      *
3800      * @param columnName the <code>String</code> column name column that may be have
3801      * been updated.
3802      * @return <code>true</code> is the designated column has been updated
3803      * and the rowset detects updates; <code>false</code> if the rowset has not
3804      * been updated or the rowset does not detect updates
3805      * @throws SQLException if the cursor is on the insert row or not
3806      *          on a valid row
3807      * @see DatabaseMetaData#updatesAreDetected
3808      */
3809     public boolean columnUpdated(String columnName) throws SQLException {
3810         return columnUpdated(getColIdxByName(columnName));
3811     }
3812 
3813     /**
3814      * Indicates whether the current row has been inserted.  The value returned
3815      * depends on whether or not the rowset can detect visible inserts.
3816      *
3817      * @return <code>true</code> if a row has been inserted and inserts are detected;
3818      *         <code>false</code> otherwise
3819      * @throws SQLException if the cursor is on the insert row or not
3820      *            not on a valid row
3821      *
3822      * @see DatabaseMetaData#insertsAreDetected
3823      */
3824     public boolean rowInserted() throws SQLException {
3825         // make sure the cursor is on a valid row
3826         checkCursor();
3827         if (onInsertRow == true) {
3828             throw new SQLException(resBundle.handleGetObject("cachedrowsetimpl.invalidop").toString());
3829         }
3830         return(((Row)getCurrentRow()).getInserted());
3831     }
3832 
3833     /**
3834      * Indicates whether the current row has been deleted.  A deleted row
3835      * may leave a visible "hole" in a rowset.  This method can be used to
3836      * detect such holes if the rowset can detect deletions. This method
3837      * will always return <code>false</code> if this rowset cannot detect
3838      * deletions.
3839      *
3840      * @return <code>true</code> if (1)the current row is blank, indicating that
3841      *         the row has been deleted, and (2)deletions are detected;
3842      *         <code>false</code> otherwise
3843      * @throws SQLException if the cursor is on a valid row in this rowset
3844      * @see DatabaseMetaData#deletesAreDetected
3845      */
3846     public boolean rowDeleted() throws SQLException {
3847         // make sure the cursor is on a valid row
3848 
3849         if (isAfterLast() == true ||
3850         isBeforeFirst() == true ||
3851         onInsertRow == true) {
3852 
3853             throw new SQLException(resBundle.handleGetObject("cachedrowsetimpl.invalidcp").toString());
3854         }
3855         return(((Row)getCurrentRow()).getDeleted());
3856     }
3857 
3858     /**
3859      * Indicates whether the given SQL data type is a numberic type.
3860      *
3861      * @param type one of the constants from <code>java.sql.Types</code>
3862      * @return <code>true</code> if the given type is <code>NUMERIC</code>,'
3863      *         <code>DECIMAL</code>, <code>BIT</code>, <code>TINYINT</code>,
3864      *         <code>SMALLINT</code>, <code>INTEGER</code>, <code>BIGINT</code>,
3865      *         <code>REAL</code>, <code>DOUBLE</code>, or <code>FLOAT</code>;
3866      *         <code>false</code> otherwise
3867      */
3868     private boolean isNumeric(int type) {
3869         switch (type) {
3870             case java.sql.Types.NUMERIC:
3871             case java.sql.Types.DECIMAL:
3872             case java.sql.Types.BIT:
3873             case java.sql.Types.TINYINT:
3874             case java.sql.Types.SMALLINT:
3875             case java.sql.Types.INTEGER:
3876             case java.sql.Types.BIGINT:
3877             case java.sql.Types.REAL:
3878             case java.sql.Types.DOUBLE:
3879             case java.sql.Types.FLOAT:
3880                 return true;
3881             default:
3882                 return false;
3883         }
3884     }
3885 
3886     /**
3887      * Indicates whether the given SQL data type is a string type.
3888      *
3889      * @param type one of the constants from <code>java.sql.Types</code>
3890      * @return <code>true</code> if the given type is <code>CHAR</code>,'
3891      *         <code>VARCHAR</code>, or <code>LONGVARCHAR</code>;
3892      *         <code>false</code> otherwise
3893      */
3894     private boolean isString(int type) {
3895         switch (type) {
3896             case java.sql.Types.CHAR:
3897             case java.sql.Types.VARCHAR:
3898             case java.sql.Types.LONGVARCHAR:
3899                 return true;
3900             default:
3901                 return false;
3902         }
3903     }
3904 
3905     /**
3906      * Indicates whether the given SQL data type is a binary type.
3907      *
3908      * @param type one of the constants from <code>java.sql.Types</code>
3909      * @return <code>true</code> if the given type is <code>BINARY</code>,'
3910      *         <code>VARBINARY</code>, or <code>LONGVARBINARY</code>;
3911      *         <code>false</code> otherwise
3912      */
3913     private boolean isBinary(int type) {
3914         switch (type) {
3915             case java.sql.Types.BINARY:
3916             case java.sql.Types.VARBINARY:
3917             case java.sql.Types.LONGVARBINARY:
3918                 return true;
3919             default:
3920                 return false;
3921         }
3922     }
3923 
3924     /**
3925      * Indicates whether the given SQL data type is a temporal type.
3926      * This method is called internally by the conversion methods
3927      * <code>convertNumeric</code> and <code>convertTemporal</code>.
3928      *
3929      * @param type one of the constants from <code>java.sql.Types</code>
3930      * @return <code>true</code> if the given type is <code>DATE</code>,
3931      *         <code>TIME</code>, or <code>TIMESTAMP</code>;
3932      *         <code>false</code> otherwise
3933      */
3934     private boolean isTemporal(int type) {
3935         switch (type) {
3936             case java.sql.Types.DATE:
3937             case java.sql.Types.TIME:
3938             case java.sql.Types.TIMESTAMP:
3939                 return true;
3940             default:
3941                 return false;
3942         }
3943     }
3944 
3945     /**
3946      * Indicates whether the given SQL data type is a boolean type.
3947      * This method is called internally by the conversion methods
3948      * <code>convertNumeric</code> and <code>convertBoolean</code>.
3949      *
3950      * @param type one of the constants from <code>java.sql.Types</code>
3951      * @return <code>true</code> if the given type is <code>BIT</code>,
3952      *         , or <code>BOOLEAN</code>;
3953      *         <code>false</code> otherwise
3954      */
3955     private boolean isBoolean(int type) {
3956         switch (type) {
3957             case java.sql.Types.BIT:
3958             case java.sql.Types.BOOLEAN:
3959                 return true;
3960             default:
3961                 return false;
3962         }
3963     }
3964 
3965 
3966     /**
3967      * Converts the given <code>Object</code> in the Java programming language
3968      * to the standard mapping for the specified SQL target data type.
3969      * The conversion must be to a string or numeric type, but there are no
3970      * restrictions on the type to be converted.  If the source type and target
3971      * type are the same, the given object is simply returned.
3972      *
3973      * @param srcObj the <code>Object</code> in the Java programming language
3974      *               that is to be converted to the target type
3975      * @param srcType the data type that is the standard mapping in SQL of the
3976      *                object to be converted; must be one of the constants in
3977      *                <code>java.sql.Types</code>
3978      * @param trgType the SQL data type to which to convert the given object;
3979      *                must be one of the following constants in
3980      *                <code>java.sql.Types</code>: <code>NUMERIC</code>,
3981      *         <code>DECIMAL</code>, <code>BIT</code>, <code>TINYINT</code>,
3982      *         <code>SMALLINT</code>, <code>INTEGER</code>, <code>BIGINT</code>,
3983      *         <code>REAL</code>, <code>DOUBLE</code>, <code>FLOAT</code>,
3984      *         <code>VARCHAR</code>, <code>LONGVARCHAR</code>, or <code>CHAR</code>
3985      * @return an <code>Object</code> value.that is
3986      *         the standard object mapping for the target SQL type
3987      * @throws SQLException if the given target type is not one of the string or
3988      *         numeric types in <code>java.sql.Types</code>
3989      */
3990     private Object convertNumeric(Object srcObj, int srcType,
3991     int trgType) throws SQLException {
3992 
3993         if (srcType == trgType) {
3994             return srcObj;
3995         }
3996 
3997         if (isNumeric(trgType) == false && isString(trgType) == false) {
3998             throw new SQLException(resBundle.handleGetObject("cachedrowsetimpl.dtypemismt").toString() + trgType);
3999         }
4000 
4001         try {
4002             switch (trgType) {
4003                 case java.sql.Types.BIT:
4004                     Integer i = Integer.valueOf(srcObj.toString().trim());
4005                     return i.equals(0) ?
4006                     Boolean.valueOf(false) :
4007                         Boolean.valueOf(true);
4008                 case java.sql.Types.TINYINT:
4009                     return Byte.valueOf(srcObj.toString().trim());
4010                 case java.sql.Types.SMALLINT:
4011                     return Short.valueOf(srcObj.toString().trim());
4012                 case java.sql.Types.INTEGER:
4013                     return Integer.valueOf(srcObj.toString().trim());
4014                 case java.sql.Types.BIGINT:
4015                     return Long.valueOf(srcObj.toString().trim());
4016                 case java.sql.Types.NUMERIC:
4017                 case java.sql.Types.DECIMAL:
4018                     return new BigDecimal(srcObj.toString().trim());
4019                 case java.sql.Types.REAL:
4020                 case java.sql.Types.FLOAT:
4021                     return new Float(srcObj.toString().trim());
4022                 case java.sql.Types.DOUBLE:
4023                     return new Double(srcObj.toString().trim());
4024                 case java.sql.Types.CHAR:
4025                 case java.sql.Types.VARCHAR:
4026                 case java.sql.Types.LONGVARCHAR:
4027                     return srcObj.toString();
4028                 default:
4029                     throw new SQLException(resBundle.handleGetObject("cachedrowsetimpl.dtypemismt").toString()+ trgType);
4030             }
4031         } catch (NumberFormatException ex) {
4032             throw new SQLException(resBundle.handleGetObject("cachedrowsetimpl.dtypemismt").toString() + trgType);
4033         }
4034     }
4035 
4036     /**
4037      * Converts the given <code>Object</code> in the Java programming language
4038      * to the standard object mapping for the specified SQL target data type.
4039      * The conversion must be to a string or temporal type, and there are also
4040      * restrictions on the type to be converted.
4041      * <P>
4042      * <TABLE ALIGN="CENTER" BORDER CELLPADDING=10 BORDERCOLOR="#0000FF"
4043      * <CAPTION ALIGN="CENTER"><B>Parameters and Return Values</B></CAPTION>
4044      * <TR>
4045      *   <TD><B>Source SQL Type</B>
4046      *   <TD><B>Target SQL Type</B>
4047      *   <TD><B>Object Returned</B>
4048      * </TR>
4049      * <TR>
4050      *   <TD><code>TIMESTAMP</code>
4051      *   <TD><code>DATE</code>
4052      *   <TD><code>java.sql.Date</code>
4053      * </TR>
4054      * <TR>
4055      *   <TD><code>TIMESTAMP</code>
4056      *   <TD><code>TIME</code>
4057      *   <TD><code>java.sql.Time</code>
4058      * </TR>
4059      * <TR>
4060      *   <TD><code>TIME</code>
4061      *   <TD><code>TIMESTAMP</code>
4062      *   <TD><code>java.sql.Timestamp</code>
4063      * </TR>
4064      * <TR>
4065      *   <TD><code>DATE</code>, <code>TIME</code>, or <code>TIMESTAMP</code>
4066      *   <TD><code>CHAR</code>, <code>VARCHAR</code>, or <code>LONGVARCHAR</code>
4067      *   <TD><code>java.lang.String</code>
4068      * </TR>
4069      * </TABLE>
4070      * <P>
4071      * If the source type and target type are the same,
4072      * the given object is simply returned.
4073      *
4074      * @param srcObj the <code>Object</code> in the Java programming language
4075      *               that is to be converted to the target type
4076      * @param srcType the data type that is the standard mapping in SQL of the
4077      *                object to be converted; must be one of the constants in
4078      *                <code>java.sql.Types</code>
4079      * @param trgType the SQL data type to which to convert the given object;
4080      *                must be one of the following constants in
4081      *                <code>java.sql.Types</code>: <code>DATE</code>,
4082      *         <code>TIME</code>, <code>TIMESTAMP</code>, <code>CHAR</code>,
4083      *         <code>VARCHAR</code>, or <code>LONGVARCHAR</code>
4084      * @return an <code>Object</code> value.that is
4085      *         the standard object mapping for the target SQL type
4086      * @throws SQLException if the given target type is not one of the string or
4087      *         temporal types in <code>java.sql.Types</code>
4088      */
4089     private Object convertTemporal(Object srcObj,
4090     int srcType, int trgType) throws SQLException {
4091 
4092         if (srcType == trgType) {
4093             return srcObj;
4094         }
4095 
4096         if (isNumeric(trgType) == true ||
4097         (isString(trgType) == false && isTemporal(trgType) == false)) {
4098             throw new SQLException(resBundle.handleGetObject("cachedrowsetimpl.dtypemismt").toString());
4099         }
4100 
4101         try {
4102             switch (trgType) {
4103                 case java.sql.Types.DATE:
4104                     if (srcType == java.sql.Types.TIMESTAMP) {
4105                         return new java.sql.Date(((java.sql.Timestamp)srcObj).getTime());
4106                     } else {
4107                         throw new SQLException(resBundle.handleGetObject("cachedrowsetimpl.dtypemismt").toString());
4108                     }
4109                 case java.sql.Types.TIMESTAMP:
4110                     if (srcType == java.sql.Types.TIME) {
4111                         return new Timestamp(((java.sql.Time)srcObj).getTime());
4112                     } else {
4113                         return new Timestamp(((java.sql.Date)srcObj).getTime());
4114                     }
4115                 case java.sql.Types.TIME:
4116                     if (srcType == java.sql.Types.TIMESTAMP) {
4117                         return new Time(((java.sql.Timestamp)srcObj).getTime());
4118                     } else {
4119                         throw new SQLException(resBundle.handleGetObject("cachedrowsetimpl.dtypemismt").toString());
4120                     }
4121                 case java.sql.Types.CHAR:
4122                 case java.sql.Types.VARCHAR:
4123                 case java.sql.Types.LONGVARCHAR:
4124                     return srcObj.toString();
4125                 default:
4126                     throw new SQLException(resBundle.handleGetObject("cachedrowsetimpl.dtypemismt").toString());
4127             }
4128         } catch (NumberFormatException ex) {
4129             throw new SQLException(resBundle.handleGetObject("cachedrowsetimpl.dtypemismt").toString());
4130         }
4131 
4132     }
4133 
4134     /**
4135      * Converts the given <code>Object</code> in the Java programming language
4136      * to the standard mapping for the specified SQL target data type.
4137      * The conversion must be to a string or numeric type, but there are no
4138      * restrictions on the type to be converted.  If the source type and target
4139      * type are the same, the given object is simply returned.
4140      *
4141      * @param srcObj the <code>Object</code> in the Java programming language
4142      *               that is to be converted to the target type
4143      * @param srcType the data type that is the standard mapping in SQL of the
4144      *                object to be converted; must be one of the constants in
4145      *                <code>java.sql.Types</code>
4146      * @param trgType the SQL data type to which to convert the given object;
4147      *                must be one of the following constants in
4148      *                <code>java.sql.Types</code>: <code>BIT</code>,
4149      *         or <code>BOOLEAN</code>
4150      * @return an <code>Object</code> value.that is
4151      *         the standard object mapping for the target SQL type
4152      * @throws SQLException if the given target type is not one of the Boolean
4153      *         types in <code>java.sql.Types</code>
4154      */
4155     private Object convertBoolean(Object srcObj, int srcType,
4156     int trgType) throws SQLException {
4157 
4158         if (srcType == trgType) {
4159             return srcObj;
4160         }
4161 
4162         if (isNumeric(trgType) == true ||
4163         (isString(trgType) == false && isBoolean(trgType) == false)) {
4164             throw new SQLException(resBundle.handleGetObject("cachedrowsetimpl.dtypemismt").toString());
4165         }
4166 
4167 
4168         try {
4169             switch (trgType) {
4170                 case java.sql.Types.BIT:
4171                     Integer i = Integer.valueOf(srcObj.toString().trim());
4172                     return i.equals(0) ?
4173                     Boolean.valueOf(false) :
4174                         Boolean.valueOf(true);
4175                 case java.sql.Types.BOOLEAN:
4176                     return Boolean.valueOf(srcObj.toString().trim());
4177                 default:
4178                     throw new SQLException(resBundle.handleGetObject("cachedrowsetimpl.dtypemismt").toString()+ trgType);
4179             }
4180         } catch (NumberFormatException ex) {
4181             throw new SQLException(resBundle.handleGetObject("cachedrowsetimpl.dtypemismt").toString() + trgType);
4182         }
4183     }
4184 
4185     /**
4186      * Sets the designated nullable column in the current row or the
4187      * insert row of this <code>CachedRowSetImpl</code> object with
4188      * <code>null</code> value.
4189      * <P>
4190      * This method updates a column value in the current row or the insert
4191      * row of this rowset; however, another method must be called to complete
4192      * the update process. If the cursor is on a row in the rowset, the
4193      * method {@link #updateRow} must be called to mark the row as updated
4194      * and to notify listeners that the row has changed.
4195      * If the cursor is on the insert row, the method {@link #insertRow}
4196      * must be called to insert the new row into this rowset and to notify
4197      * listeners that a row has changed.
4198      * <P>
4199      * In order to propagate updates in this rowset to the underlying
4200      * data source, an application must call the method {@link #acceptChanges}
4201      * after it calls either <code>updateRow</code> or <code>insertRow</code>.
4202      *
4203      * @param columnIndex the first column is <code>1</code>, the second
4204      *        is <code>2</code>, and so on; must be <code>1</code> or larger
4205      *        and equal to or less than the number of columns in this rowset
4206      * @throws SQLException if (1) the given column index is out of bounds,
4207      *            (2) the cursor is not on one of this rowset's rows or its
4208      *            insert row, or (3) this rowset is
4209      *            <code>ResultSet.CONCUR_READ_ONLY</code>
4210      */
4211     public void updateNull(int columnIndex) throws SQLException {
4212         // sanity check.
4213         checkIndex(columnIndex);
4214         // make sure the cursor is on a valid row
4215         checkCursor();
4216 
4217         BaseRow row = getCurrentRow();
4218         row.setColumnObject(columnIndex, null);
4219 
4220     }
4221 
4222     /**
4223      * Sets the designated column in either the current row or the insert
4224      * row of this <code>CachedRowSetImpl</code> object with the given
4225      * <code>boolean</code> value.
4226      * <P>
4227      * This method updates a column value in the current row or the insert
4228      * row of this rowset, but it does not update the database.
4229      * If the cursor is on a row in the rowset, the
4230      * method {@link #updateRow} must be called to update the database.
4231      * If the cursor is on the insert row, the method {@link #insertRow}
4232      * must be called, which will insert the new row into both this rowset
4233      * and the database. Both of these methods must be called before the
4234      * cursor moves to another row.
4235      *
4236      * @param columnIndex the first column is <code>1</code>, the second
4237      *        is <code>2</code>, and so on; must be <code>1</code> or larger
4238      *        and equal to or less than the number of columns in this rowset
4239      * @param x the new column value
4240      * @throws SQLException if (1) the given column index is out of bounds,
4241      *            (2) the cursor is not on one of this rowset's rows or its
4242      *            insert row, or (3) this rowset is
4243      *            <code>ResultSet.CONCUR_READ_ONLY</code>
4244      */
4245     public void updateBoolean(int columnIndex, boolean x) throws SQLException {
4246         // sanity check.
4247         checkIndex(columnIndex);
4248         // make sure the cursor is on a valid row
4249         checkCursor();
4250         Object obj = convertBoolean(Boolean.valueOf(x),
4251         java.sql.Types.BIT,
4252         RowSetMD.getColumnType(columnIndex));
4253 
4254         getCurrentRow().setColumnObject(columnIndex, obj);
4255     }
4256 
4257     /**
4258      * Sets the designated column in either the current row or the insert
4259      * row of this <code>CachedRowSetImpl</code> object with the given
4260      * <code>byte</code> value.
4261      * <P>
4262      * This method updates a column value in the current row or the insert
4263      * row of this rowset, but it does not update the database.
4264      * If the cursor is on a row in the rowset, the
4265      * method {@link #updateRow} must be called to update the database.
4266      * If the cursor is on the insert row, the method {@link #insertRow}
4267      * must be called, which will insert the new row into both this rowset
4268      * and the database. Both of these methods must be called before the
4269      * cursor moves to another row.
4270      *
4271      * @param columnIndex the first column is <code>1</code>, the second
4272      *        is <code>2</code>, and so on; must be <code>1</code> or larger
4273      *        and equal to or less than the number of columns in this rowset
4274      * @param x the new column value
4275      * @throws SQLException if (1) the given column index is out of bounds,
4276      *            (2) the cursor is not on one of this rowset's rows or its
4277      *            insert row, or (3) this rowset is
4278      *            <code>ResultSet.CONCUR_READ_ONLY</code>
4279      */
4280     public void updateByte(int columnIndex, byte x) throws SQLException {
4281         // sanity check.
4282         checkIndex(columnIndex);
4283         // make sure the cursor is on a valid row
4284         checkCursor();
4285 
4286         Object obj = convertNumeric(Byte.valueOf(x),
4287         java.sql.Types.TINYINT,
4288         RowSetMD.getColumnType(columnIndex));
4289 
4290         getCurrentRow().setColumnObject(columnIndex, obj);
4291     }
4292 
4293     /**
4294      * Sets the designated column in either the current row or the insert
4295      * row of this <code>CachedRowSetImpl</code> object with the given
4296      * <code>short</code> value.
4297      * <P>
4298      * This method updates a column value in the current row or the insert
4299      * row of this rowset, but it does not update the database.
4300      * If the cursor is on a row in the rowset, the
4301      * method {@link #updateRow} must be called to update the database.
4302      * If the cursor is on the insert row, the method {@link #insertRow}
4303      * must be called, which will insert the new row into both this rowset
4304      * and the database. Both of these methods must be called before the
4305      * cursor moves to another row.
4306      *
4307      * @param columnIndex the first column is <code>1</code>, the second
4308      *        is <code>2</code>, and so on; must be <code>1</code> or larger
4309      *        and equal to or less than the number of columns in this rowset
4310      * @param x the new column value
4311      * @throws SQLException if (1) the given column index is out of bounds,
4312      *            (2) the cursor is not on one of this rowset's rows or its
4313      *            insert row, or (3) this rowset is
4314      *            <code>ResultSet.CONCUR_READ_ONLY</code>
4315      */
4316     public void updateShort(int columnIndex, short x) throws SQLException {
4317         // sanity check.
4318         checkIndex(columnIndex);
4319         // make sure the cursor is on a valid row
4320         checkCursor();
4321 
4322         Object obj = convertNumeric(Short.valueOf(x),
4323         java.sql.Types.SMALLINT,
4324         RowSetMD.getColumnType(columnIndex));
4325 
4326         getCurrentRow().setColumnObject(columnIndex, obj);
4327     }
4328 
4329     /**
4330      * Sets the designated column in either the current row or the insert
4331      * row of this <code>CachedRowSetImpl</code> object with the given
4332      * <code>int</code> value.
4333      * <P>
4334      * This method updates a column value in the current row or the insert
4335      * row of this rowset, but it does not update the database.
4336      * If the cursor is on a row in the rowset, the
4337      * method {@link #updateRow} must be called to update the database.
4338      * If the cursor is on the insert row, the method {@link #insertRow}
4339      * must be called, which will insert the new row into both this rowset
4340      * and the database. Both of these methods must be called before the
4341      * cursor moves to another row.
4342      *
4343      * @param columnIndex the first column is <code>1</code>, the second
4344      *        is <code>2</code>, and so on; must be <code>1</code> or larger
4345      *        and equal to or less than the number of columns in this rowset
4346      * @param x the new column value
4347      * @throws SQLException if (1) the given column index is out of bounds,
4348      *            (2) the cursor is not on one of this rowset's rows or its
4349      *            insert row, or (3) this rowset is
4350      *            <code>ResultSet.CONCUR_READ_ONLY</code>
4351      */
4352     public void updateInt(int columnIndex, int x) throws SQLException {
4353         // sanity check.
4354         checkIndex(columnIndex);
4355         // make sure the cursor is on a valid row
4356         checkCursor();
4357         Object obj = convertNumeric(x,
4358         java.sql.Types.INTEGER,
4359         RowSetMD.getColumnType(columnIndex));
4360 
4361         getCurrentRow().setColumnObject(columnIndex, obj);
4362     }
4363 
4364     /**
4365      * Sets the designated column in either the current row or the insert
4366      * row of this <code>CachedRowSetImpl</code> object with the given
4367      * <code>long</code> value.
4368      * <P>
4369      * This method updates a column value in the current row or the insert
4370      * row of this rowset, but it does not update the database.
4371      * If the cursor is on a row in the rowset, the
4372      * method {@link #updateRow} must be called to update the database.
4373      * If the cursor is on the insert row, the method {@link #insertRow}
4374      * must be called, which will insert the new row into both this rowset
4375      * and the database. Both of these methods must be called before the
4376      * cursor moves to another row.
4377      *
4378      * @param columnIndex the first column is <code>1</code>, the second
4379      *        is <code>2</code>, and so on; must be <code>1</code> or larger
4380      *        and equal to or less than the number of columns in this rowset
4381      * @param x the new column value
4382      * @throws SQLException if (1) the given column index is out of bounds,
4383      *            (2) the cursor is not on one of this rowset's rows or its
4384      *            insert row, or (3) this rowset is
4385      *            <code>ResultSet.CONCUR_READ_ONLY</code>
4386      */
4387     public void updateLong(int columnIndex, long x) throws SQLException {
4388         // sanity check.
4389         checkIndex(columnIndex);
4390         // make sure the cursor is on a valid row
4391         checkCursor();
4392 
4393         Object obj = convertNumeric(Long.valueOf(x),
4394         java.sql.Types.BIGINT,
4395         RowSetMD.getColumnType(columnIndex));
4396 
4397         getCurrentRow().setColumnObject(columnIndex, obj);
4398 
4399     }
4400 
4401     /**
4402      * Sets the designated column in either the current row or the insert
4403      * row of this <code>CachedRowSetImpl</code> object with the given
4404      * <code>float</code> value.
4405      * <P>
4406      * This method updates a column value in the current row or the insert
4407      * row of this rowset, but it does not update the database.
4408      * If the cursor is on a row in the rowset, the
4409      * method {@link #updateRow} must be called to update the database.
4410      * If the cursor is on the insert row, the method {@link #insertRow}
4411      * must be called, which will insert the new row into both this rowset
4412      * and the database. Both of these methods must be called before the
4413      * cursor moves to another row.
4414      *
4415      * @param columnIndex the first column is <code>1</code>, the second
4416      *        is <code>2</code>, and so on; must be <code>1</code> or larger
4417      *        and equal to or less than the number of columns in this rowset
4418      * @param x the new column value
4419      * @throws SQLException if (1) the given column index is out of bounds,
4420      *            (2) the cursor is not on one of this rowset's rows or its
4421      *            insert row, or (3) this rowset is
4422      *            <code>ResultSet.CONCUR_READ_ONLY</code>
4423      */
4424     public void updateFloat(int columnIndex, float x) throws SQLException {
4425         // sanity check.
4426         checkIndex(columnIndex);
4427         // make sure the cursor is on a valid row
4428         checkCursor();
4429 
4430         Object obj = convertNumeric(Float.valueOf(x),
4431         java.sql.Types.REAL,
4432         RowSetMD.getColumnType(columnIndex));
4433 
4434         getCurrentRow().setColumnObject(columnIndex, obj);
4435     }
4436 
4437     /**
4438      * Sets the designated column in either the current row or the insert
4439      * row of this <code>CachedRowSetImpl</code> object with the given
4440      * <code>double</code> value.
4441      *
4442      * This method updates a column value in either the current row or
4443      * the insert row of this rowset, but it does not update the
4444      * database.  If the cursor is on a row in the rowset, the
4445      * method {@link #updateRow} must be called to update the database.
4446      * If the cursor is on the insert row, the method {@link #insertRow}
4447      * must be called, which will insert the new row into both this rowset
4448      * and the database. Both of these methods must be called before the
4449      * cursor moves to another row.
4450      *
4451      * @param columnIndex the first column is <code>1</code>, the second
4452      *        is <code>2</code>, and so on; must be <code>1</code> or larger
4453      *        and equal to or less than the number of columns in this rowset
4454      * @param x the new column value
4455      * @throws SQLException if (1) the given column index is out of bounds,
4456      *            (2) the cursor is not on one of this rowset's rows or its
4457      *            insert row, or (3) this rowset is
4458      *            <code>ResultSet.CONCUR_READ_ONLY</code>
4459      */
4460     public void updateDouble(int columnIndex, double x) throws SQLException {
4461         // sanity check.
4462         checkIndex(columnIndex);
4463         // make sure the cursor is on a valid row
4464         checkCursor();
4465         Object obj = convertNumeric(Double.valueOf(x),
4466         java.sql.Types.DOUBLE,
4467         RowSetMD.getColumnType(columnIndex));
4468 
4469         getCurrentRow().setColumnObject(columnIndex, obj);
4470     }
4471 
4472     /**
4473      * Sets the designated column in either the current row or the insert
4474      * row of this <code>CachedRowSetImpl</code> object with the given
4475      * <code>java.math.BigDecimal</code> object.
4476      * <P>
4477      * This method updates a column value in the current row or the insert
4478      * row of this rowset, but it does not update the database.
4479      * If the cursor is on a row in the rowset, the
4480      * method {@link #updateRow} must be called to update the database.
4481      * If the cursor is on the insert row, the method {@link #insertRow}
4482      * must be called, which will insert the new row into both this rowset
4483      * and the database. Both of these methods must be called before the
4484      * cursor moves to another row.
4485      *
4486      * @param columnIndex the first column is <code>1</code>, the second
4487      *        is <code>2</code>, and so on; must be <code>1</code> or larger
4488      *        and equal to or less than the number of columns in this rowset
4489      * @param x the new column value
4490      * @throws SQLException if (1) the given column index is out of bounds,
4491      *            (2) the cursor is not on one of this rowset's rows or its
4492      *            insert row, or (3) this rowset is
4493      *            <code>ResultSet.CONCUR_READ_ONLY</code>
4494      */
4495     public void updateBigDecimal(int columnIndex, BigDecimal x) throws SQLException {
4496         // sanity check.
4497         checkIndex(columnIndex);
4498         // make sure the cursor is on a valid row
4499         checkCursor();
4500 
4501         Object obj = convertNumeric(x,
4502         java.sql.Types.NUMERIC,
4503         RowSetMD.getColumnType(columnIndex));
4504 
4505         getCurrentRow().setColumnObject(columnIndex, obj);
4506     }
4507 
4508     /**
4509      * Sets the designated column in either the current row or the insert
4510      * row of this <code>CachedRowSetImpl</code> object with the given
4511      * <code>String</code> object.
4512      * <P>
4513      * This method updates a column value in either the current row or
4514      * the insert row of this rowset, but it does not update the
4515      * database.  If the cursor is on a row in the rowset, the
4516      * method {@link #updateRow} must be called to mark the row as updated.
4517      * If the cursor is on the insert row, the method {@link #insertRow}
4518      * must be called to insert the new row into this rowset and mark it
4519      * as inserted. Both of these methods must be called before the
4520      * cursor moves to another row.
4521      * <P>
4522      * The method <code>acceptChanges</code> must be called if the
4523      * updated values are to be written back to the underlying database.
4524      *
4525      * @param columnIndex the first column is <code>1</code>, the second
4526      *        is <code>2</code>, and so on; must be <code>1</code> or larger
4527      *        and equal to or less than the number of columns in this rowset
4528      * @param x the new column value
4529      * @throws SQLException if (1) the given column index is out of bounds,
4530      *            (2) the cursor is not on one of this rowset's rows or its
4531      *            insert row, or (3) this rowset is
4532      *            <code>ResultSet.CONCUR_READ_ONLY</code>
4533      */
4534     public void updateString(int columnIndex, String x) throws SQLException {
4535         // sanity check.
4536         checkIndex(columnIndex);
4537         // make sure the cursor is on a valid row
4538         checkCursor();
4539 
4540         getCurrentRow().setColumnObject(columnIndex, x);
4541     }
4542 
4543     /**
4544      * Sets the designated column in either the current row or the insert
4545      * row of this <code>CachedRowSetImpl</code> object with the given
4546      * <code>byte</code> array.
4547      *
4548      * This method updates a column value in either the current row or
4549      * the insert row of this rowset, but it does not update the
4550      * database.  If the cursor is on a row in the rowset, the
4551      * method {@link #updateRow} must be called to update the database.
4552      * If the cursor is on the insert row, the method {@link #insertRow}
4553      * must be called, which will insert the new row into both this rowset
4554      * and the database. Both of these methods must be called before the
4555      * cursor moves to another row.
4556      *
4557      * @param columnIndex the first column is <code>1</code>, the second
4558      *        is <code>2</code>, and so on; must be <code>1</code> or larger
4559      *        and equal to or less than the number of columns in this rowset
4560      * @param x the new column value
4561      * @throws SQLException if (1) the given column index is out of bounds,
4562      *            (2) the cursor is not on one of this rowset's rows or its
4563      *            insert row, or (3) this rowset is
4564      *            <code>ResultSet.CONCUR_READ_ONLY</code>
4565      */
4566     public void updateBytes(int columnIndex, byte x[]) throws SQLException {
4567         // sanity check.
4568         checkIndex(columnIndex);
4569         // make sure the cursor is on a valid row
4570         checkCursor();
4571 
4572         if (isBinary(RowSetMD.getColumnType(columnIndex)) == false) {
4573             throw new SQLException(resBundle.handleGetObject("cachedrowsetimpl.dtypemismt").toString());
4574         }
4575 
4576         getCurrentRow().setColumnObject(columnIndex, x);
4577     }
4578 
4579     /**
4580      * Sets the designated column in either the current row or the insert
4581      * row of this <code>CachedRowSetImpl</code> object with the given
4582      * <code>Date</code> object.
4583      *
4584      * This method updates a column value in either the current row or
4585      * the insert row of this rowset, but it does not update the
4586      * database.  If the cursor is on a row in the rowset, the
4587      * method {@link #updateRow} must be called to update the database.
4588      * If the cursor is on the insert row, the method {@link #insertRow}
4589      * must be called, which will insert the new row into both this rowset
4590      * and the database. Both of these methods must be called before the
4591      * cursor moves to another row.
4592      *
4593      * @param columnIndex the first column is <code>1</code>, the second
4594      *        is <code>2</code>, and so on; must be <code>1</code> or larger
4595      *        and equal to or less than the number of columns in this rowset
4596      * @param x the new column value
4597      * @throws SQLException if (1) the given column index is out of bounds,
4598      *            (2) the cursor is not on one of this rowset's rows or its
4599      *            insert row, (3) the type of the designated column is not
4600      *            an SQL <code>DATE</code> or <code>TIMESTAMP</code>, or
4601      *            (4) this rowset is <code>ResultSet.CONCUR_READ_ONLY</code>
4602      */
4603     public void updateDate(int columnIndex, java.sql.Date x) throws SQLException {
4604         // sanity check.
4605         checkIndex(columnIndex);
4606         // make sure the cursor is on a valid row
4607         checkCursor();
4608 
4609         Object obj = convertTemporal(x,
4610         java.sql.Types.DATE,
4611         RowSetMD.getColumnType(columnIndex));
4612 
4613         getCurrentRow().setColumnObject(columnIndex, obj);
4614     }
4615 
4616     /**
4617      * Sets the designated column in either the current row or the insert
4618      * row of this <code>CachedRowSetImpl</code> object with the given
4619      * <code>Time</code> object.
4620      *
4621      * This method updates a column value in either the current row or
4622      * the insert row of this rowset, but it does not update the
4623      * database.  If the cursor is on a row in the rowset, the
4624      * method {@link #updateRow} must be called to update the database.
4625      * If the cursor is on the insert row, the method {@link #insertRow}
4626      * must be called, which will insert the new row into both this rowset
4627      * and the database. Both of these methods must be called before the
4628      * cursor moves to another row.
4629      *
4630      * @param columnIndex the first column is <code>1</code>, the second
4631      *        is <code>2</code>, and so on; must be <code>1</code> or larger
4632      *        and equal to or less than the number of columns in this rowset
4633      * @param x the new column value
4634      * @throws SQLException if (1) the given column index is out of bounds,
4635      *            (2) the cursor is not on one of this rowset's rows or its
4636      *            insert row, (3) the type of the designated column is not
4637      *            an SQL <code>TIME</code> or <code>TIMESTAMP</code>, or
4638      *            (4) this rowset is <code>ResultSet.CONCUR_READ_ONLY</code>
4639      */
4640     public void updateTime(int columnIndex, java.sql.Time x) throws SQLException {
4641         // sanity check.
4642         checkIndex(columnIndex);
4643         // make sure the cursor is on a valid row
4644         checkCursor();
4645 
4646         Object obj = convertTemporal(x,
4647         java.sql.Types.TIME,
4648         RowSetMD.getColumnType(columnIndex));
4649 
4650         getCurrentRow().setColumnObject(columnIndex, obj);
4651     }
4652 
4653     /**
4654      * Sets the designated column in either the current row or the insert
4655      * row of this <code>CachedRowSetImpl</code> object with the given
4656      * <code>Timestamp</code> object.
4657      *
4658      * This method updates a column value in either the current row or
4659      * the insert row of this rowset, but it does not update the
4660      * database.  If the cursor is on a row in the rowset, the
4661      * method {@link #updateRow} must be called to update the database.
4662      * If the cursor is on the insert row, the method {@link #insertRow}
4663      * must be called, which will insert the new row into both this rowset
4664      * and the database. Both of these methods must be called before the
4665      * cursor moves to another row.
4666      *
4667      * @param columnIndex the first column is <code>1</code>, the second
4668      *        is <code>2</code>, and so on; must be <code>1</code> or larger
4669      *        and equal to or less than the number of columns in this rowset
4670      * @param x the new column value
4671      * @throws SQLException if (1) the given column index is out of bounds,
4672      *            (2) the cursor is not on one of this rowset's rows or its
4673      *            insert row, (3) the type of the designated column is not
4674      *            an SQL <code>DATE</code>, <code>TIME</code>, or
4675      *            <code>TIMESTAMP</code>, or (4) this rowset is
4676      *            <code>ResultSet.CONCUR_READ_ONLY</code>
4677      */
4678     public void updateTimestamp(int columnIndex, java.sql.Timestamp x) throws SQLException {
4679         // sanity check.
4680         checkIndex(columnIndex);
4681         // make sure the cursor is on a valid row
4682         checkCursor();
4683 
4684         Object obj = convertTemporal(x,
4685         java.sql.Types.TIMESTAMP,
4686         RowSetMD.getColumnType(columnIndex));
4687 
4688         getCurrentRow().setColumnObject(columnIndex, obj);
4689     }
4690 
4691     /**
4692      * Sets the designated column in either the current row or the insert
4693      * row of this <code>CachedRowSetImpl</code> object with the given
4694      * ASCII stream value.
4695      * <P>
4696      * This method updates a column value in either the current row or
4697      * the insert row of this rowset, but it does not update the
4698      * database.  If the cursor is on a row in the rowset, the
4699      * method {@link #updateRow} must be called to update the database.
4700      * If the cursor is on the insert row, the method {@link #insertRow}
4701      * must be called, which will insert the new row into both this rowset
4702      * and the database. Both of these methods must be called before the
4703      * cursor moves to another row.
4704      *
4705      * @param columnIndex the first column is <code>1</code>, the second
4706      *        is <code>2</code>, and so on; must be <code>1</code> or larger
4707      *        and equal to or less than the number of columns in this rowset
4708      * @param x the new column value
4709      * @param length the number of one-byte ASCII characters in the stream
4710      * @throws SQLException if this method is invoked
4711      */
4712     public void updateAsciiStream(int columnIndex, java.io.InputStream x, int length) throws SQLException {
4713         // sanity Check
4714         checkIndex(columnIndex);
4715         // make sure the cursor is on a valid row
4716         checkCursor();
4717 
4718 
4719         if (isString(RowSetMD.getColumnType(columnIndex)) == false &&
4720         isBinary(RowSetMD.getColumnType(columnIndex)) == false) {
4721             throw new SQLException(resBundle.handleGetObject("cachedrowsetimpl.dtypemismt").toString());
4722         }
4723 
4724         byte buf[] = new byte[length];
4725         try {
4726             int charsRead = 0;
4727             do {
4728                 charsRead += x.read(buf, charsRead, length - charsRead);
4729             } while (charsRead != length);
4730             //Changed the condition check to check for length instead of -1
4731         } catch (java.io.IOException ex) {
4732             throw new SQLException(resBundle.handleGetObject("cachedrowsetimpl.asciistream").toString());
4733         }
4734         String str = new String(buf);
4735 
4736         getCurrentRow().setColumnObject(columnIndex, str);
4737 
4738     }
4739 
4740     /**
4741      * Sets the designated column in either the current row or the insert
4742      * row of this <code>CachedRowSetImpl</code> object with the given
4743      * <code>java.io.InputStream</code> object.
4744      * <P>
4745      * This method updates a column value in either the current row or
4746      * the insert row of this rowset, but it does not update the
4747      * database.  If the cursor is on a row in the rowset, the
4748      * method {@link #updateRow} must be called to update the database.
4749      * If the cursor is on the insert row, the method {@link #insertRow}
4750      * must be called, which will insert the new row into both this rowset
4751      * and the database. Both of these methods must be called before the
4752      * cursor moves to another row.
4753      *
4754      * @param columnIndex the first column is <code>1</code>, the second
4755      *        is <code>2</code>, and so on; must be <code>1</code> or larger
4756      *        and equal to or less than the number of columns in this rowset
4757      * @param x the new column value; must be a <code>java.io.InputStream</code>
4758      *          containing <code>BINARY</code>, <code>VARBINARY</code>, or
4759      *          <code>LONGVARBINARY</code> data
4760      * @param length the length of the stream in bytes
4761      * @throws SQLException if (1) the given column index is out of bounds,
4762      *            (2) the cursor is not on one of this rowset's rows or its
4763      *            insert row, (3) the data in the stream is not binary, or
4764      *            (4) this rowset is <code>ResultSet.CONCUR_READ_ONLY</code>
4765      */
4766     public void updateBinaryStream(int columnIndex, java.io.InputStream x,int length) throws SQLException {
4767         // sanity Check
4768         checkIndex(columnIndex);
4769         // make sure the cursor is on a valid row
4770         checkCursor();
4771 
4772         if (isBinary(RowSetMD.getColumnType(columnIndex)) == false) {
4773             throw new SQLException(resBundle.handleGetObject("cachedrowsetimpl.dtypemismt").toString());
4774         }
4775 
4776         byte buf[] = new byte[length];
4777         try {
4778             int bytesRead = 0;
4779             do {
4780                 bytesRead += x.read(buf, bytesRead, length - bytesRead);
4781             } while (bytesRead != -1);
4782         } catch (java.io.IOException ex) {
4783             throw new SQLException(resBundle.handleGetObject("cachedrowsetimpl.binstream").toString());
4784         }
4785 
4786         getCurrentRow().setColumnObject(columnIndex, buf);
4787     }
4788 
4789     /**
4790      * Sets the designated column in either the current row or the insert
4791      * row of this <code>CachedRowSetImpl</code> object with the given
4792      * <code>java.io.Reader</code> object.
4793      * <P>
4794      * This method updates a column value in either the current row or
4795      * the insert row of this rowset, but it does not update the
4796      * database.  If the cursor is on a row in the rowset, the
4797      * method {@link #updateRow} must be called to update the database.
4798      * If the cursor is on the insert row, the method {@link #insertRow}
4799      * must be called, which will insert the new row into both this rowset
4800      * and the database. Both of these methods must be called before the
4801      * cursor moves to another row.
4802      *
4803      * @param columnIndex the first column is <code>1</code>, the second
4804      *        is <code>2</code>, and so on; must be <code>1</code> or larger
4805      *        and equal to or less than the number of columns in this rowset
4806      * @param x the new column value; must be a <code>java.io.Reader</code>
4807      *          containing <code>BINARY</code>, <code>VARBINARY</code>,
4808      *          <code>LONGVARBINARY</code>, <code>CHAR</code>, <code>VARCHAR</code>,
4809      *          or <code>LONGVARCHAR</code> data
4810      * @param length the length of the stream in characters
4811      * @throws SQLException if (1) the given column index is out of bounds,
4812      *            (2) the cursor is not on one of this rowset's rows or its
4813      *            insert row, (3) the data in the stream is not a binary or
4814      *            character type, or (4) this rowset is
4815      *            <code>ResultSet.CONCUR_READ_ONLY</code>
4816      */
4817     public void updateCharacterStream(int columnIndex, java.io.Reader x, int length) throws SQLException {
4818         // sanity Check
4819         checkIndex(columnIndex);
4820         // make sure the cursor is on a valid row
4821         checkCursor();
4822 
4823         if (isString(RowSetMD.getColumnType(columnIndex)) == false &&
4824         isBinary(RowSetMD.getColumnType(columnIndex)) == false) {
4825             throw new SQLException(resBundle.handleGetObject("cachedrowsetimpl.dtypemismt").toString());
4826         }
4827 
4828         char buf[] = new char[length];
4829         try {
4830             int charsRead = 0;
4831             do {
4832                 charsRead += x.read(buf, charsRead, length - charsRead);
4833             } while (charsRead != length);
4834             //Changed the condition checking to check for length instead of -1
4835         } catch (java.io.IOException ex) {
4836             throw new SQLException(resBundle.handleGetObject("cachedrowsetimpl.binstream").toString());
4837         }
4838         String str = new String(buf);
4839 
4840         getCurrentRow().setColumnObject(columnIndex, str);
4841     }
4842 
4843     /**
4844      * Sets the designated column in either the current row or the insert
4845      * row of this <code>CachedRowSetImpl</code> object with the given
4846      * <code>Object</code> value.  The <code>scale</code> parameter indicates
4847      * the number of digits to the right of the decimal point and is ignored
4848      * if the new column value is not a type that will be mapped to an SQL
4849      * <code>DECIMAL</code> or <code>NUMERIC</code> value.
4850      * <P>
4851      * This method updates a column value in either the current row or
4852      * the insert row of this rowset, but it does not update the
4853      * database.  If the cursor is on a row in the rowset, the
4854      * method {@link #updateRow} must be called to update the database.
4855      * If the cursor is on the insert row, the method {@link #insertRow}
4856      * must be called, which will insert the new row into both this rowset
4857      * and the database. Both of these methods must be called before the
4858      * cursor moves to another row.
4859      *
4860      * @param columnIndex the first column is <code>1</code>, the second
4861      *        is <code>2</code>, and so on; must be <code>1</code> or larger
4862      *        and equal to or less than the number of columns in this rowset
4863      * @param x the new column value
4864      * @param scale the number of digits to the right of the decimal point (for
4865      *              <code>DECIMAL</code> and <code>NUMERIC</code> types only)
4866      * @throws SQLException if (1) the given column index is out of bounds,
4867      *            (2) the cursor is not on one of this rowset's rows or its
4868      *            insert row, or (3) this rowset is
4869      *            <code>ResultSet.CONCUR_READ_ONLY</code>
4870      */
4871     public void updateObject(int columnIndex, Object x, int scale) throws SQLException {
4872         // sanity check.
4873         checkIndex(columnIndex);
4874         // make sure the cursor is on a valid row
4875         checkCursor();
4876 
4877         int type = RowSetMD.getColumnType(columnIndex);
4878         if (type == Types.DECIMAL || type == Types.NUMERIC) {
4879             ((java.math.BigDecimal)x).setScale(scale);
4880         }
4881         getCurrentRow().setColumnObject(columnIndex, x);
4882     }
4883 
4884     /**
4885      * Sets the designated column in either the current row or the insert
4886      * row of this <code>CachedRowSetImpl</code> object with the given
4887      * <code>Object</code> value.
4888      * <P>
4889      * This method updates a column value in either the current row or
4890      * the insert row of this rowset, but it does not update the
4891      * database.  If the cursor is on a row in the rowset, the
4892      * method {@link #updateRow} must be called to update the database.
4893      * If the cursor is on the insert row, the method {@link #insertRow}
4894      * must be called, which will insert the new row into both this rowset
4895      * and the database. Both of these methods must be called before the
4896      * cursor moves to another row.
4897      *
4898      * @param columnIndex the first column is <code>1</code>, the second
4899      *        is <code>2</code>, and so on; must be <code>1</code> or larger
4900      *        and equal to or less than the number of columns in this rowset
4901      * @param x the new column value
4902      * @throws SQLException if (1) the given column index is out of bounds,
4903      *            (2) the cursor is not on one of this rowset's rows or its
4904      *            insert row, or (3) this rowset is
4905      *            <code>ResultSet.CONCUR_READ_ONLY</code>
4906      */
4907     public void updateObject(int columnIndex, Object x) throws SQLException {
4908         // sanity check.
4909         checkIndex(columnIndex);
4910         // make sure the cursor is on a valid row
4911         checkCursor();
4912 
4913         getCurrentRow().setColumnObject(columnIndex, x);
4914     }
4915 
4916     /**
4917      * Sets the designated nullable column in the current row or the
4918      * insert row of this <code>CachedRowSetImpl</code> object with
4919      * <code>null</code> value.
4920      * <P>
4921      * This method updates a column value in the current row or the insert
4922      * row of this rowset, but it does not update the database.
4923      * If the cursor is on a row in the rowset, the
4924      * method {@link #updateRow} must be called to update the database.
4925      * If the cursor is on the insert row, the method {@link #insertRow}
4926      * must be called, which will insert the new row into both this rowset
4927      * and the database.
4928      *
4929      * @param columnName a <code>String</code> object that must match the
4930      *        SQL name of a column in this rowset, ignoring case
4931      * @throws SQLException if (1) the given column name does not match the
4932      *            name of a column in this rowset, (2) the cursor is not on
4933      *            one of this rowset's rows or its insert row, or (3) this
4934      *            rowset is <code>ResultSet.CONCUR_READ_ONLY</code>
4935      */
4936     public void updateNull(String columnName) throws SQLException {
4937         updateNull(getColIdxByName(columnName));
4938     }
4939 
4940     /**
4941      * Sets the designated column in either the current row or the insert
4942      * row of this <code>CachedRowSetImpl</code> object with the given
4943      * <code>boolean</code> value.
4944      * <P>
4945      * This method updates a column value in the current row or the insert
4946      * row of this rowset, but it does not update the database.
4947      * If the cursor is on a row in the rowset, the
4948      * method {@link #updateRow} must be called to update the database.
4949      * If the cursor is on the insert row, the method {@link #insertRow}
4950      * must be called, which will insert the new row into both this rowset
4951      * and the database. Both of these methods must be called before the
4952      * cursor moves to another row.
4953      *
4954      * @param columnName a <code>String</code> object that must match the
4955      *        SQL name of a column in this rowset, ignoring case
4956      * @param x the new column value
4957      * @throws SQLException if (1) the given column name does not match the
4958      *            name of a column in this rowset, (2) the cursor is not on
4959      *            one of this rowset's rows or its insert row, or (3) this
4960      *            rowset is <code>ResultSet.CONCUR_READ_ONLY</code>
4961      */
4962     public void updateBoolean(String columnName, boolean x) throws SQLException {
4963         updateBoolean(getColIdxByName(columnName), x);
4964     }
4965 
4966     /**
4967      * Sets the designated column in either the current row or the insert
4968      * row of this <code>CachedRowSetImpl</code> object with the given
4969      * <code>byte</code> value.
4970      * <P>
4971      * This method updates a column value in the current row or the insert
4972      * row of this rowset, but it does not update the database.
4973      * If the cursor is on a row in the rowset, the
4974      * method {@link #updateRow} must be called to update the database.
4975      * If the cursor is on the insert row, the method {@link #insertRow}
4976      * must be called, which will insert the new row into both this rowset
4977      * and the database. Both of these methods must be called before the
4978      * cursor moves to another row.
4979      *
4980      * @param columnName a <code>String</code> object that must match the
4981      *        SQL name of a column in this rowset, ignoring case
4982      * @param x the new column value
4983      * @throws SQLException if (1) the given column name does not match the
4984      *            name of a column in this rowset, (2) the cursor is not on
4985      *            one of this rowset's rows or its insert row, or (3) this
4986      *            rowset is <code>ResultSet.CONCUR_READ_ONLY</code>
4987      */
4988     public void updateByte(String columnName, byte x) throws SQLException {
4989         updateByte(getColIdxByName(columnName), x);
4990     }
4991 
4992     /**
4993      * Sets the designated column in either the current row or the insert
4994      * row of this <code>CachedRowSetImpl</code> object with the given
4995      * <code>short</code> value.
4996      * <P>
4997      * This method updates a column value in the current row or the insert
4998      * row of this rowset, but it does not update the database.
4999      * If the cursor is on a row in the rowset, the
5000      * method {@link #updateRow} must be called to update the database.
5001      * If the cursor is on the insert row, the method {@link #insertRow}
5002      * must be called, which will insert the new row into both this rowset
5003      * and the database. Both of these methods must be called before the
5004      * cursor moves to another row.
5005      *
5006      * @param columnName a <code>String</code> object that must match the
5007      *        SQL name of a column in this rowset, ignoring case
5008      * @param x the new column value
5009      * @throws SQLException if (1) the given column name does not match the
5010      *            name of a column in this rowset, (2) the cursor is not on
5011      *            one of this rowset's rows or its insert row, or (3) this
5012      *            rowset is <code>ResultSet.CONCUR_READ_ONLY</code>
5013      */
5014     public void updateShort(String columnName, short x) throws SQLException {
5015         updateShort(getColIdxByName(columnName), x);
5016     }
5017 
5018     /**
5019      * Sets the designated column in either the current row or the insert
5020      * row of this <code>CachedRowSetImpl</code> object with the given
5021      * <code>int</code> value.
5022      * <P>
5023      * This method updates a column value in the current row or the insert
5024      * row of this rowset, but it does not update the database.
5025      * If the cursor is on a row in the rowset, the
5026      * method {@link #updateRow} must be called to update the database.
5027      * If the cursor is on the insert row, the method {@link #insertRow}
5028      * must be called, which will insert the new row into both this rowset
5029      * and the database. Both of these methods must be called before the
5030      * cursor moves to another row.
5031      *
5032      * @param columnName a <code>String</code> object that must match the
5033      *        SQL name of a column in this rowset, ignoring case
5034      * @param x the new column value
5035      * @throws SQLException if (1) the given column name does not match the
5036      *            name of a column in this rowset, (2) the cursor is not on
5037      *            one of this rowset's rows or its insert row, or (3) this
5038      *            rowset is <code>ResultSet.CONCUR_READ_ONLY</code>
5039      */
5040     public void updateInt(String columnName, int x) throws SQLException {
5041         updateInt(getColIdxByName(columnName), x);
5042     }
5043 
5044     /**
5045      * Sets the designated column in either the current row or the insert
5046      * row of this <code>CachedRowSetImpl</code> object with the given
5047      * <code>long</code> value.
5048      * <P>
5049      * This method updates a column value in the current row or the insert
5050      * row of this rowset, but it does not update the database.
5051      * If the cursor is on a row in the rowset, the
5052      * method {@link #updateRow} must be called to update the database.
5053      * If the cursor is on the insert row, the method {@link #insertRow}
5054      * must be called, which will insert the new row into both this rowset
5055      * and the database. Both of these methods must be called before the
5056      * cursor moves to another row.
5057      *
5058      * @param columnName a <code>String</code> object that must match the
5059      *        SQL name of a column in this rowset, ignoring case
5060      * @param x the new column value
5061      * @throws SQLException if (1) the given column name does not match the
5062      *            name of a column in this rowset, (2) the cursor is not on
5063      *            one of this rowset's rows or its insert row, or (3) this
5064      *            rowset is <code>ResultSet.CONCUR_READ_ONLY</code>
5065      */
5066     public void updateLong(String columnName, long x) throws SQLException {
5067         updateLong(getColIdxByName(columnName), x);
5068     }
5069 
5070     /**
5071      * Sets the designated column in either the current row or the insert
5072      * row of this <code>CachedRowSetImpl</code> object with the given
5073      * <code>float</code> value.
5074      * <P>
5075      * This method updates a column value in the current row or the insert
5076      * row of this rowset, but it does not update the database.
5077      * If the cursor is on a row in the rowset, the
5078      * method {@link #updateRow} must be called to update the database.
5079      * If the cursor is on the insert row, the method {@link #insertRow}
5080      * must be called, which will insert the new row into both this rowset
5081      * and the database. Both of these methods must be called before the
5082      * cursor moves to another row.
5083      *
5084      * @param columnName a <code>String</code> object that must match the
5085      *        SQL name of a column in this rowset, ignoring case
5086      * @param x the new column value
5087      * @throws SQLException if (1) the given column name does not match the
5088      *            name of a column in this rowset, (2) the cursor is not on
5089      *            one of this rowset's rows or its insert row, or (3) this
5090      *            rowset is <code>ResultSet.CONCUR_READ_ONLY</code>
5091      */
5092     public void updateFloat(String columnName, float x) throws SQLException {
5093         updateFloat(getColIdxByName(columnName), x);
5094     }
5095 
5096     /**
5097      * Sets the designated column in either the current row or the insert
5098      * row of this <code>CachedRowSetImpl</code> object with the given
5099      * <code>double</code> value.
5100      *
5101      * This method updates a column value in either the current row or
5102      * the insert row of this rowset, but it does not update the
5103      * database.  If the cursor is on a row in the rowset, the
5104      * method {@link #updateRow} must be called to update the database.
5105      * If the cursor is on the insert row, the method {@link #insertRow}
5106      * must be called, which will insert the new row into both this rowset
5107      * and the database. Both of these methods must be called before the
5108      * cursor moves to another row.
5109      *
5110      * @param columnName a <code>String</code> object that must match the
5111      *        SQL name of a column in this rowset, ignoring case
5112      * @param x the new column value
5113      * @throws SQLException if (1) the given column name does not match the
5114      *            name of a column in this rowset, (2) the cursor is not on
5115      *            one of this rowset's rows or its insert row, or (3) this
5116      *            rowset is <code>ResultSet.CONCUR_READ_ONLY</code>
5117      */
5118     public void updateDouble(String columnName, double x) throws SQLException {
5119         updateDouble(getColIdxByName(columnName), x);
5120     }
5121 
5122     /**
5123      * Sets the designated column in either the current row or the insert
5124      * row of this <code>CachedRowSetImpl</code> object with the given
5125      * <code>java.math.BigDecimal</code> object.
5126      * <P>
5127      * This method updates a column value in the current row or the insert
5128      * row of this rowset, but it does not update the database.
5129      * If the cursor is on a row in the rowset, the
5130      * method {@link #updateRow} must be called to update the database.
5131      * If the cursor is on the insert row, the method {@link #insertRow}
5132      * must be called, which will insert the new row into both this rowset
5133      * and the database. Both of these methods must be called before the
5134      * cursor moves to another row.
5135      *
5136      * @param columnName a <code>String</code> object that must match the
5137      *        SQL name of a column in this rowset, ignoring case
5138      * @param x the new column value
5139      * @throws SQLException if (1) the given column name does not match the
5140      *            name of a column in this rowset, (2) the cursor is not on
5141      *            one of this rowset's rows or its insert row, or (3) this
5142      *            rowset is <code>ResultSet.CONCUR_READ_ONLY</code>
5143      */
5144     public void updateBigDecimal(String columnName, BigDecimal x) throws SQLException {
5145         updateBigDecimal(getColIdxByName(columnName), x);
5146     }
5147 
5148     /**
5149      * Sets the designated column in either the current row or the insert
5150      * row of this <code>CachedRowSetImpl</code> object with the given
5151      * <code>String</code> object.
5152      *
5153      * This method updates a column value in either the current row or
5154      * the insert row of this rowset, but it does not update the
5155      * database.  If the cursor is on a row in the rowset, the
5156      * method {@link #updateRow} must be called to update the database.
5157      * If the cursor is on the insert row, the method {@link #insertRow}
5158      * must be called, which will insert the new row into both this rowset
5159      * and the database. Both of these methods must be called before the
5160      * cursor moves to another row.
5161      *
5162      * @param columnName a <code>String</code> object that must match the
5163      *        SQL name of a column in this rowset, ignoring case
5164      * @param x the new column value
5165      * @throws SQLException if (1) the given column name does not match the
5166      *            name of a column in this rowset, (2) the cursor is not on
5167      *            one of this rowset's rows or its insert row, or (3) this
5168      *            rowset is <code>ResultSet.CONCUR_READ_ONLY</code>
5169      */
5170     public void updateString(String columnName, String x) throws SQLException {
5171         updateString(getColIdxByName(columnName), x);
5172     }
5173 
5174     /**
5175      * Sets the designated column in either the current row or the insert
5176      * row of this <code>CachedRowSetImpl</code> object with the given
5177      * <code>byte</code> array.
5178      *
5179      * This method updates a column value in either the current row or
5180      * the insert row of this rowset, but it does not update the
5181      * database.  If the cursor is on a row in the rowset, the
5182      * method {@link #updateRow} must be called to update the database.
5183      * If the cursor is on the insert row, the method {@link #insertRow}
5184      * must be called, which will insert the new row into both this rowset
5185      * and the database. Both of these methods must be called before the
5186      * cursor moves to another row.
5187      *
5188      * @param columnName a <code>String</code> object that must match the
5189      *        SQL name of a column in this rowset, ignoring case
5190      * @param x the new column value
5191      * @throws SQLException if (1) the given column name does not match the
5192      *            name of a column in this rowset, (2) the cursor is not on
5193      *            one of this rowset's rows or its insert row, or (3) this
5194      *            rowset is <code>ResultSet.CONCUR_READ_ONLY</code>
5195      */
5196     public void updateBytes(String columnName, byte x[]) throws SQLException {
5197         updateBytes(getColIdxByName(columnName), x);
5198     }
5199 
5200     /**
5201      * Sets the designated column in either the current row or the insert
5202      * row of this <code>CachedRowSetImpl</code> object with the given
5203      * <code>Date</code> object.
5204      *
5205      * This method updates a column value in either the current row or
5206      * the insert row of this rowset, but it does not update the
5207      * database.  If the cursor is on a row in the rowset, the
5208      * method {@link #updateRow} must be called to update the database.
5209      * If the cursor is on the insert row, the method {@link #insertRow}
5210      * must be called, which will insert the new row into both this rowset
5211      * and the database. Both of these methods must be called before the
5212      * cursor moves to another row.
5213      *
5214      * @param columnName a <code>String</code> object that must match the
5215      *        SQL name of a column in this rowset, ignoring case
5216      * @param x the new column value
5217      * @throws SQLException if (1) the given column name does not match the
5218      *            name of a column in this rowset, (2) the cursor is not on
5219      *            one of this rowset's rows or its insert row, (3) the type
5220      *            of the designated column is not an SQL <code>DATE</code> or
5221      *            <code>TIMESTAMP</code>, or (4) this rowset is
5222      *            <code>ResultSet.CONCUR_READ_ONLY</code>
5223      */
5224     public void updateDate(String columnName, java.sql.Date x) throws SQLException {
5225         updateDate(getColIdxByName(columnName), x);
5226     }
5227 
5228     /**
5229      * Sets the designated column in either the current row or the insert
5230      * row of this <code>CachedRowSetImpl</code> object with the given
5231      * <code>Time</code> object.
5232      *
5233      * This method updates a column value in either the current row or
5234      * the insert row of this rowset, but it does not update the
5235      * database.  If the cursor is on a row in the rowset, the
5236      * method {@link #updateRow} must be called to update the database.
5237      * If the cursor is on the insert row, the method {@link #insertRow}
5238      * must be called, which will insert the new row into both this rowset
5239      * and the database. Both of these methods must be called before the
5240      * cursor moves to another row.
5241      *
5242      * @param columnName a <code>String</code> object that must match the
5243      *        SQL name of a column in this rowset, ignoring case
5244      * @param x the new column value
5245      * @throws SQLException if (1) the given column name does not match the
5246      *            name of a column in this rowset, (2) the cursor is not on
5247      *            one of this rowset's rows or its insert row, (3) the type
5248      *            of the designated column is not an SQL <code>TIME</code> or
5249      *            <code>TIMESTAMP</code>, or (4) this rowset is
5250      *            <code>ResultSet.CONCUR_READ_ONLY</code>
5251      */
5252     public void updateTime(String columnName, java.sql.Time x) throws SQLException {
5253         updateTime(getColIdxByName(columnName), x);
5254     }
5255 
5256     /**
5257      * Sets the designated column in either the current row or the insert
5258      * row of this <code>CachedRowSetImpl</code> object with the given
5259      * <code>Timestamp</code> object.
5260      *
5261      * This method updates a column value in either the current row or
5262      * the insert row of this rowset, but it does not update the
5263      * database.  If the cursor is on a row in the rowset, the
5264      * method {@link #updateRow} must be called to update the database.
5265      * If the cursor is on the insert row, the method {@link #insertRow}
5266      * must be called, which will insert the new row into both this rowset
5267      * and the database. Both of these methods must be called before the
5268      * cursor moves to another row.
5269      *
5270      * @param columnName a <code>String</code> object that must match the
5271      *        SQL name of a column in this rowset, ignoring case
5272      * @param x the new column value
5273      * @throws SQLException if the given column index is out of bounds or
5274      *            the cursor is not on one of this rowset's rows or its
5275      *            insert row
5276      * @throws SQLException if (1) the given column name does not match the
5277      *            name of a column in this rowset, (2) the cursor is not on
5278      *            one of this rowset's rows or its insert row, (3) the type
5279      *            of the designated column is not an SQL <code>DATE</code>,
5280      *            <code>TIME</code>, or <code>TIMESTAMP</code>, or (4) this
5281      *            rowset is <code>ResultSet.CONCUR_READ_ONLY</code>
5282      */
5283     public void updateTimestamp(String columnName, java.sql.Timestamp x) throws SQLException {
5284         updateTimestamp(getColIdxByName(columnName), x);
5285     }
5286 
5287     /**
5288      * Sets the designated column in either the current row or the insert
5289      * row of this <code>CachedRowSetImpl</code> object with the given
5290      * ASCII stream value.
5291      * <P>
5292      * This method updates a column value in either the current row or
5293      * the insert row of this rowset, but it does not update the
5294      * database.  If the cursor is on a row in the rowset, the
5295      * method {@link #updateRow} must be called to update the database.
5296      * If the cursor is on the insert row, the method {@link #insertRow}
5297      * must be called, which will insert the new row into both this rowset
5298      * and the database. Both of these methods must be called before the
5299      * cursor moves to another row.
5300      *
5301      * @param columnName a <code>String</code> object that must match the
5302      *        SQL name of a column in this rowset, ignoring case
5303      * @param x the new column value
5304      * @param length the number of one-byte ASCII characters in the stream
5305      */
5306     public void updateAsciiStream(String columnName,
5307     java.io.InputStream x,
5308     int length) throws SQLException {
5309         updateAsciiStream(getColIdxByName(columnName), x, length);
5310     }
5311 
5312     /**
5313      * Sets the designated column in either the current row or the insert
5314      * row of this <code>CachedRowSetImpl</code> object with the given
5315      * <code>java.io.InputStream</code> object.
5316      * <P>
5317      * This method updates a column value in either the current row or
5318      * the insert row of this rowset, but it does not update the
5319      * database.  If the cursor is on a row in the rowset, the
5320      * method {@link #updateRow} must be called to update the database.
5321      * If the cursor is on the insert row, the method {@link #insertRow}
5322      * must be called, which will insert the new row into both this rowset
5323      * and the database. Both of these methods must be called before the
5324      * cursor moves to another row.
5325      *
5326      * @param columnName a <code>String</code> object that must match the
5327      *        SQL name of a column in this rowset, ignoring case
5328      * @param x the new column value; must be a <code>java.io.InputStream</code>
5329      *          containing <code>BINARY</code>, <code>VARBINARY</code>, or
5330      *          <code>LONGVARBINARY</code> data
5331      * @param length the length of the stream in bytes
5332      * @throws SQLException if (1) the given column name does not match the
5333      *            name of a column in this rowset, (2) the cursor is not on
5334      *            one of this rowset's rows or its insert row, (3) the data
5335      *            in the stream is not binary, or (4) this rowset is
5336      *            <code>ResultSet.CONCUR_READ_ONLY</code>
5337      */
5338     public void updateBinaryStream(String columnName, java.io.InputStream x, int length) throws SQLException {
5339         updateBinaryStream(getColIdxByName(columnName), x, length);
5340     }
5341 
5342     /**
5343      * Sets the designated column in either the current row or the insert
5344      * row of this <code>CachedRowSetImpl</code> object with the given
5345      * <code>java.io.Reader</code> object.
5346      * <P>
5347      * This method updates a column value in either the current row or
5348      * the insert row of this rowset, but it does not update the
5349      * database.  If the cursor is on a row in the rowset, the
5350      * method {@link #updateRow} must be called to update the database.
5351      * If the cursor is on the insert row, the method {@link #insertRow}
5352      * must be called, which will insert the new row into both this rowset
5353      * and the database. Both of these methods must be called before the
5354      * cursor moves to another row.
5355      *
5356      * @param columnName a <code>String</code> object that must match the
5357      *        SQL name of a column in this rowset, ignoring case
5358      * @param reader the new column value; must be a
5359      * <code>java.io.Reader</code> containing <code>BINARY</code>,
5360      * <code>VARBINARY</code>, <code>LONGVARBINARY</code>, <code>CHAR</code>,
5361      * <code>VARCHAR</code>, or <code>LONGVARCHAR</code> data
5362      * @param length the length of the stream in characters
5363      * @throws SQLException if (1) the given column name does not match the
5364      *            name of a column in this rowset, (2) the cursor is not on
5365      *            one of this rowset's rows or its insert row, (3) the data
5366      *            in the stream is not a binary or character type, or (4) this
5367      *            rowset is <code>ResultSet.CONCUR_READ_ONLY</code>
5368      */
5369     public void updateCharacterStream(String columnName,
5370     java.io.Reader reader,
5371     int length) throws SQLException {
5372         updateCharacterStream(getColIdxByName(columnName), reader, length);
5373     }
5374 
5375     /**
5376      * Sets the designated column in either the current row or the insert
5377      * row of this <code>CachedRowSetImpl</code> object with the given
5378      * <code>Object</code> value.  The <code>scale</code> parameter
5379      * indicates the number of digits to the right of the decimal point
5380      * and is ignored if the new column value is not a type that will be
5381      *  mapped to an SQL <code>DECIMAL</code> or <code>NUMERIC</code> value.
5382      * <P>
5383      * This method updates a column value in either the current row or
5384      * the insert row of this rowset, but it does not update the
5385      * database.  If the cursor is on a row in the rowset, the
5386      * method {@link #updateRow} must be called to update the database.
5387      * If the cursor is on the insert row, the method {@link #insertRow}
5388      * must be called, which will insert the new row into both this rowset
5389      * and the database. Both of these methods must be called before the
5390      * cursor moves to another row.
5391      *
5392      * @param columnName a <code>String</code> object that must match the
5393      *        SQL name of a column in this rowset, ignoring case
5394      * @param x the new column value
5395      * @param scale the number of digits to the right of the decimal point (for
5396      *              <code>DECIMAL</code> and <code>NUMERIC</code> types only)
5397      * @throws SQLException if (1) the given column name does not match the
5398      *            name of a column in this rowset, (2) the cursor is not on
5399      *            one of this rowset's rows or its insert row, or (3) this
5400      *            rowset is <code>ResultSet.CONCUR_READ_ONLY</code>
5401      */
5402     public void updateObject(String columnName, Object x, int scale) throws SQLException {
5403         updateObject(getColIdxByName(columnName), x, scale);
5404     }
5405 
5406     /**
5407      * Sets the designated column in either the current row or the insert
5408      * row of this <code>CachedRowSetImpl</code> object with the given
5409      * <code>Object</code> value.
5410      * <P>
5411      * This method updates a column value in either the current row or
5412      * the insert row of this rowset, but it does not update the
5413      * database.  If the cursor is on a row in the rowset, the
5414      * method {@link #updateRow} must be called to update the database.
5415      * If the cursor is on the insert row, the method {@link #insertRow}
5416      * must be called, which will insert the new row into both this rowset
5417      * and the database. Both of these methods must be called before the
5418      * cursor moves to another row.
5419      *
5420      * @param columnName a <code>String</code> object that must match the
5421      *        SQL name of a column in this rowset, ignoring case
5422      * @param x the new column value
5423      * @throws SQLException if (1) the given column name does not match the
5424      *            name of a column in this rowset, (2) the cursor is not on
5425      *            one of this rowset's rows or its insert row, or (3) this
5426      *            rowset is <code>ResultSet.CONCUR_READ_ONLY</code>
5427      */
5428     public void updateObject(String columnName, Object x) throws SQLException {
5429         updateObject(getColIdxByName(columnName), x);
5430     }
5431 
5432     /**
5433      * Inserts the contents of this <code>CachedRowSetImpl</code> object's insert
5434      * row into this rowset immediately following the current row.
5435      * If the current row is the
5436      * position after the last row or before the first row, the new row will
5437      * be inserted at the end of the rowset.  This method also notifies
5438      * listeners registered with this rowset that the row has changed.
5439      * <P>
5440      * The cursor must be on the insert row when this method is called.
5441      *
5442      * @throws SQLException if (1) the cursor is not on the insert row,
5443      *            (2) one or more of the non-nullable columns in the insert
5444      *            row has not been given a value, or (3) this rowset is
5445      *            <code>ResultSet.CONCUR_READ_ONLY</code>
5446      */
5447     public void insertRow() throws SQLException {
5448         int pos;
5449 
5450         if (onInsertRow == false ||
5451             insertRow.isCompleteRow(RowSetMD) == false) {
5452                 throw new SQLException(resBundle.handleGetObject("cachedrowsetimpl.failedins").toString());
5453         }
5454         // Added the setting of parameters that are passed
5455         // to setXXX methods after an empty CRS Object is
5456         // created through RowSetMetaData object
5457         Object [] toInsert = getParams();
5458 
5459         for(int i = 0;i < toInsert.length; i++) {
5460           insertRow.setColumnObject(i+1,toInsert[i]);
5461         }
5462 
5463         Row insRow = new Row(RowSetMD.getColumnCount(),
5464         insertRow.getOrigRow());
5465         insRow.setInserted();
5466         /*
5467          * The new row is inserted into the RowSet
5468          * immediately following the current row.
5469          *
5470          * If we are afterlast then the rows are
5471          * inserted at the end.
5472          */
5473         if (currentRow >= numRows || currentRow < 0) {
5474             pos = numRows;
5475         } else {
5476             pos = currentRow;
5477         }
5478 
5479         rvh.add(pos, insRow);
5480         ++numRows;
5481         // notify the listeners that the row changed.
5482         notifyRowChanged();
5483     }
5484 
5485     /**
5486      * Marks the current row of this <code>CachedRowSetImpl</code> object as
5487      * updated and notifies listeners registered with this rowset that the
5488      * row has changed.
5489      * <P>
5490      * This method  cannot be called when the cursor is on the insert row, and
5491      * it should be called before the cursor moves to another row.  If it is
5492      * called after the cursor moves to another row, this method has no effect,
5493      * and the updates made before the cursor moved will be lost.
5494      *
5495      * @throws SQLException if the cursor is on the insert row or this
5496      *            rowset is <code>ResultSet.CONCUR_READ_ONLY</code>
5497      */
5498     public void updateRow() throws SQLException {
5499         // make sure we aren't on the insert row
5500         if (onInsertRow == true) {
5501             throw new SQLException(resBundle.handleGetObject("cachedrowsetimpl.updateins").toString());
5502         }
5503 
5504         ((Row)getCurrentRow()).setUpdated();
5505 
5506         // notify the listeners that the row changed.
5507         notifyRowChanged();
5508     }
5509 
5510     /**
5511      * Deletes the current row from this <code>CachedRowSetImpl</code> object and
5512      * notifies listeners registered with this rowset that a row has changed.
5513      * This method cannot be called when the cursor is on the insert row.
5514      * <P>
5515      * This method marks the current row as deleted, but it does not delete
5516      * the row from the underlying data source.  The method
5517      * <code>acceptChanges</code> must be called to delete the row in
5518      * the data source.
5519      *
5520      * @throws SQLException if (1) this method is called when the cursor
5521      *            is on the insert row, before the first row, or after the
5522      *            last row or (2) this rowset is
5523      *            <code>ResultSet.CONCUR_READ_ONLY</code>
5524      */
5525     public void deleteRow() throws SQLException {
5526         // make sure the cursor is on a valid row
5527         checkCursor();
5528 
5529         ((Row)getCurrentRow()).setDeleted();
5530         ++numDeleted;
5531 
5532         // notify the listeners that the row changed.
5533         notifyRowChanged();
5534     }
5535 
5536     /**
5537      * Sets the current row with its original value and marks the row as
5538      * not updated, thus undoing any changes made to the row since the
5539      * last call to the methods <code>updateRow</code> or <code>deleteRow</code>.
5540      * This method should be called only when the cursor is on a row in
5541      * this rowset.
5542      *
5543      * @throws SQLException if the cursor is on the insert row, before the
5544      *            first row, or after the last row
5545      */
5546     public void refreshRow() throws SQLException {
5547         // make sure we are on a row
5548         checkCursor();
5549 
5550         // don't want this to happen...
5551         if (onInsertRow == true) {
5552             throw new SQLException(resBundle.handleGetObject("cachedrowsetimpl.invalidcp").toString());
5553         }
5554 
5555         Row currentRow = (Row)getCurrentRow();
5556         // just undo any changes made to this row.
5557         currentRow.clearUpdated();
5558 
5559     }
5560 
5561     /**
5562      * Rolls back any updates made to the current row of this
5563      * <code>CachedRowSetImpl</code> object and notifies listeners that
5564      * a row has changed.  To have an effect, this method
5565      * must be called after an <code>updateXXX</code> method has been
5566      * called and before the method <code>updateRow</code> has been called.
5567      * If no updates have been made or the method <code>updateRow</code>
5568      * has already been called, this method has no effect.
5569      *
5570      * @throws SQLException if the cursor is on the insert row, before the
5571      *            first row, or after the last row
5572      */
5573     public void cancelRowUpdates() throws SQLException {
5574         // make sure we are on a row
5575         checkCursor();
5576 
5577         // don't want this to happen...
5578         if (onInsertRow == true) {
5579             throw new SQLException(resBundle.handleGetObject("cachedrowsetimpl.invalidcp").toString());
5580         }
5581 
5582         Row currentRow = (Row)getCurrentRow();
5583         if (currentRow.getUpdated() == true) {
5584             currentRow.clearUpdated();
5585             notifyRowChanged();
5586         }
5587     }
5588 
5589     /**
5590      * Moves the cursor for this <code>CachedRowSetImpl</code> object
5591      * to the insert row.  The current row in the rowset is remembered
5592      * while the cursor is on the insert row.
5593      * <P>
5594      * The insert row is a special row associated with an updatable
5595      * rowset.  It is essentially a buffer where a new row may
5596      * be constructed by calling the appropriate <code>updateXXX</code>
5597      * methods to assign a value to each column in the row.  A complete
5598      * row must be constructed; that is, every column that is not nullable
5599      * must be assigned a value.  In order for the new row to become part
5600      * of this rowset, the method <code>insertRow</code> must be called
5601      * before the cursor is moved back to the rowset.
5602      * <P>
5603      * Only certain methods may be invoked while the cursor is on the insert
5604      * row; many methods throw an exception if they are called while the
5605      * cursor is there.  In addition to the <code>updateXXX</code>
5606      * and <code>insertRow</code> methods, only the <code>getXXX</code> methods
5607      * may be called when the cursor is on the insert row.  A <code>getXXX</code>
5608      * method should be called on a column only after an <code>updateXXX</code>
5609      * method has been called on that column; otherwise, the value returned is
5610      * undetermined.
5611      *
5612      * @throws SQLException if this <code>CachedRowSetImpl</code> object is
5613      *            <code>ResultSet.CONCUR_READ_ONLY</code>
5614      */
5615     public void moveToInsertRow() throws SQLException {
5616         if (getConcurrency() == ResultSet.CONCUR_READ_ONLY) {
5617             throw new SQLException(resBundle.handleGetObject("cachedrowsetimpl.movetoins").toString());
5618         }
5619         if (insertRow == null) {
5620             if (RowSetMD == null)
5621                 throw new SQLException(resBundle.handleGetObject("cachedrowsetimpl.movetoins1").toString());
5622             int numCols = RowSetMD.getColumnCount();
5623             if (numCols > 0) {
5624                 insertRow = new InsertRow(numCols);
5625             } else {
5626                 throw new SQLException(resBundle.handleGetObject("cachedrowsetimpl.movetoins2").toString());
5627             }
5628         }
5629         onInsertRow = true;
5630         // %%% setCurrentRow called in BaseRow
5631 
5632         currentRow = cursorPos;
5633         cursorPos = -1;
5634 
5635         insertRow.initInsertRow();
5636     }
5637 
5638     /**
5639      * Moves the cursor for this <code>CachedRowSetImpl</code> object to
5640      * the current row.  The current row is the row the cursor was on
5641      * when the method <code>moveToInsertRow</code> was called.
5642      * <P>
5643      * Calling this method has no effect unless it is called while the
5644      * cursor is on the insert row.
5645      *
5646      * @throws SQLException if an error occurs
5647      */
5648     public void moveToCurrentRow() throws SQLException {
5649         if (onInsertRow == false) {
5650             return;
5651         } else {
5652             cursorPos = currentRow;
5653             onInsertRow = false;
5654         }
5655     }
5656 
5657     /**
5658      * Returns <code>null</code>.
5659      *
5660      * @return <code>null</code>
5661      * @throws SQLException if an error occurs
5662      */
5663     public Statement getStatement() throws SQLException {
5664         return null;
5665     }
5666 
5667     /**
5668      * Retrieves the value of the designated column in this
5669      * <code>CachedRowSetImpl</code> object as an <code>Object</code> in
5670      * the Java programming language, using the given
5671      * <code>java.util.Map</code> object to custom map the value if
5672      * appropriate.
5673      *
5674      * @param columnIndex the first column is <code>1</code>, the second
5675      *        is <code>2</code>, and so on; must be <code>1</code> or larger
5676      *        and equal to or less than the number of columns in this rowset
5677      * @param map a <code>java.util.Map</code> object showing the mapping
5678      *            from SQL type names to classes in the Java programming
5679      *            language
5680      * @return an <code>Object</code> representing the SQL value
5681      * @throws SQLException if the given column index is out of bounds or
5682      *            the cursor is not on one of this rowset's rows or its
5683      *            insert row
5684      */
5685      public Object getObject(int columnIndex,
5686                              java.util.Map<String,Class<?>> map)
5687          throws SQLException
5688      {
5689         Object value;
5690 
5691         // sanity check.
5692         checkIndex(columnIndex);
5693         // make sure the cursor is on a valid row
5694         checkCursor();
5695 
5696         setLastValueNull(false);
5697         value = getCurrentRow().getColumnObject(columnIndex);
5698 
5699         // check for SQL NULL
5700         if (value == null) {
5701             setLastValueNull(true);
5702             return null;
5703         }
5704         if (value instanceof Struct) {
5705             Struct s = (Struct)value;
5706 
5707             // look up the class in the map
5708             Class<?> c = map.get(s.getSQLTypeName());
5709             if (c != null) {
5710                 // create new instance of the class
5711                 SQLData obj = null;
5712                 try {
5713                     obj = (SQLData)c.newInstance();
5714                 } catch (java.lang.InstantiationException ex) {
5715                     throw new SQLException(MessageFormat.format(resBundle.handleGetObject("cachedrowsetimpl.unableins").toString(),
5716                     ex.getMessage()));
5717                 } catch (java.lang.IllegalAccessException ex) {
5718                     throw new SQLException(MessageFormat.format(resBundle.handleGetObject("cachedrowsetimpl.unableins").toString(),
5719                     ex.getMessage()));
5720                 }
5721                 // get the attributes from the struct
5722                 Object attribs[] = s.getAttributes(map);
5723                 // create the SQLInput "stream"
5724                 SQLInputImpl sqlInput = new SQLInputImpl(attribs, map);
5725                 // read the values...
5726                 obj.readSQL(sqlInput, s.getSQLTypeName());
5727                 return (Object)obj;
5728             }
5729         }
5730         return value;
5731     }
5732 
5733     /**
5734      * Retrieves the value of the designated column in this
5735      * <code>CachedRowSetImpl</code> object as a <code>Ref</code> object
5736      * in the Java programming language.
5737      *
5738      * @param columnIndex the first column is <code>1</code>, the second
5739      *        is <code>2</code>, and so on; must be <code>1</code> or larger
5740      *        and equal to or less than the number of columns in this rowset
5741      * @return a <code>Ref</code> object representing an SQL<code> REF</code> value
5742      * @throws SQLException if (1) the given column index is out of bounds,
5743      *            (2) the cursor is not on one of this rowset's rows or its
5744      *            insert row, or (3) the designated column does not store an
5745      *            SQL <code>REF</code> value
5746      * @see #getRef(String)
5747      */
5748     public Ref getRef(int columnIndex) throws SQLException {
5749         Ref value;
5750 
5751         // sanity check.
5752         checkIndex(columnIndex);
5753         // make sure the cursor is on a valid row
5754         checkCursor();
5755 
5756         if (RowSetMD.getColumnType(columnIndex) != java.sql.Types.REF) {
5757             throw new SQLException(resBundle.handleGetObject("cachedrowsetimpl.dtypemismt").toString());
5758         }
5759 
5760         setLastValueNull(false);
5761         value = (Ref)(getCurrentRow().getColumnObject(columnIndex));
5762 
5763         // check for SQL NULL
5764         if (value == null) {
5765             setLastValueNull(true);
5766             return null;
5767         }
5768 
5769         return value;
5770     }
5771 
5772     /**
5773      * Retrieves the value of the designated column in this
5774      * <code>CachedRowSetImpl</code> object as a <code>Blob</code> object
5775      * in the Java programming language.
5776      *
5777      * @param columnIndex the first column is <code>1</code>, the second
5778      *        is <code>2</code>, and so on; must be <code>1</code> or larger
5779      *        and equal to or less than the number of columns in this rowset
5780      * @return a <code>Blob</code> object representing an SQL <code>BLOB</code> value
5781      * @throws SQLException if (1) the given column index is out of bounds,
5782      *            (2) the cursor is not on one of this rowset's rows or its
5783      *            insert row, or (3) the designated column does not store an
5784      *            SQL <code>BLOB</code> value
5785      * @see #getBlob(String)
5786      */
5787     public Blob getBlob(int columnIndex) throws SQLException {
5788         Blob value;
5789 
5790         // sanity check.
5791         checkIndex(columnIndex);
5792         // make sure the cursor is on a valid row
5793         checkCursor();
5794 
5795         if (RowSetMD.getColumnType(columnIndex) != java.sql.Types.BLOB) {
5796             System.out.println(MessageFormat.format(resBundle.handleGetObject("cachedrowsetimpl.type").toString(), RowSetMD.getColumnType(columnIndex)));
5797             throw new SQLException(resBundle.handleGetObject("cachedrowsetimpl.dtypemismt").toString());
5798         }
5799 
5800         setLastValueNull(false);
5801         value = (Blob)(getCurrentRow().getColumnObject(columnIndex));
5802 
5803         // check for SQL NULL
5804         if (value == null) {
5805             setLastValueNull(true);
5806             return null;
5807         }
5808 
5809         return value;
5810     }
5811 
5812     /**
5813      * Retrieves the value of the designated column in this
5814      * <code>CachedRowSetImpl</code> object as a <code>Clob</code> object
5815      * in the Java programming language.
5816      *
5817      * @param columnIndex the first column is <code>1</code>, the second
5818      *        is <code>2</code>, and so on; must be <code>1</code> or larger
5819      *        and equal to or less than the number of columns in this rowset
5820      * @return a <code>Clob</code> object representing an SQL <code>CLOB</code> value
5821      * @throws SQLException if (1) the given column index is out of bounds,
5822      *            (2) the cursor is not on one of this rowset's rows or its
5823      *            insert row, or (3) the designated column does not store an
5824      *            SQL <code>CLOB</code> value
5825      * @see #getClob(String)
5826      */
5827     public Clob getClob(int columnIndex) throws SQLException {
5828         Clob value;
5829 
5830         // sanity check.
5831         checkIndex(columnIndex);
5832         // make sure the cursor is on a valid row
5833         checkCursor();
5834 
5835         if (RowSetMD.getColumnType(columnIndex) != java.sql.Types.CLOB) {
5836             System.out.println(MessageFormat.format(resBundle.handleGetObject("cachedrowsetimpl.type").toString(), RowSetMD.getColumnType(columnIndex)));
5837             throw new SQLException(resBundle.handleGetObject("cachedrowsetimpl.dtypemismt").toString());
5838         }
5839 
5840         setLastValueNull(false);
5841         value = (Clob)(getCurrentRow().getColumnObject(columnIndex));
5842 
5843         // check for SQL NULL
5844         if (value == null) {
5845             setLastValueNull(true);
5846             return null;
5847         }
5848 
5849         return value;
5850     }
5851 
5852     /**
5853      * Retrieves the value of the designated column in this
5854      * <code>CachedRowSetImpl</code> object as an <code>Array</code> object
5855      * in the Java programming language.
5856      *
5857      * @param columnIndex the first column is <code>1</code>, the second
5858      *        is <code>2</code>, and so on; must be <code>1</code> or larger
5859      *        and equal to or less than the number of columns in this rowset
5860      * @return an <code>Array</code> object representing an SQL
5861      *         <code>ARRAY</code> value
5862      * @throws SQLException if (1) the given column index is out of bounds,
5863      *            (2) the cursor is not on one of this rowset's rows or its
5864      *            insert row, or (3) the designated column does not store an
5865      *            SQL <code>ARRAY</code> value
5866      * @see #getArray(String)
5867      */
5868     public Array getArray(int columnIndex) throws SQLException {
5869         java.sql.Array value;
5870 
5871         // sanity check.
5872         checkIndex(columnIndex);
5873         // make sure the cursor is on a valid row
5874         checkCursor();
5875 
5876         if (RowSetMD.getColumnType(columnIndex) != java.sql.Types.ARRAY) {
5877             throw new SQLException(resBundle.handleGetObject("cachedrowsetimpl.dtypemismt").toString());
5878         }
5879 
5880         setLastValueNull(false);
5881         value = (java.sql.Array)(getCurrentRow().getColumnObject(columnIndex));
5882 
5883         // check for SQL NULL
5884         if (value == null) {
5885             setLastValueNull(true);
5886             return null;
5887         }
5888 
5889         return value;
5890     }
5891 
5892     /**
5893      * Retrieves the value of the designated column in this
5894      * <code>CachedRowSetImpl</code> object as an <code>Object</code> in
5895      * the Java programming language, using the given
5896      * <code>java.util.Map</code> object to custom map the value if
5897      * appropriate.
5898      *
5899      * @param columnName a <code>String</code> object that must match the
5900      *        SQL name of a column in this rowset, ignoring case
5901      * @param map a <code>java.util.Map</code> object showing the mapping
5902      *        from SQL type names to classes in the Java programming
5903      *        language
5904      * @return an <code>Object</code> representing the SQL value
5905      * @throws SQLException if the given column name is not the name of
5906      *         a column in this rowset or the cursor is not on one of
5907      *         this rowset's rows or its insert row
5908      */
5909     public Object getObject(String columnName,
5910                             java.util.Map<String,Class<?>> map)
5911     throws SQLException {
5912         return getObject(getColIdxByName(columnName), map);
5913     }
5914 
5915     /**
5916      * Retrieves the value of the designated column in this
5917      * <code>CachedRowSetImpl</code> object as a <code>Ref</code> object
5918      * in the Java programming language.
5919      *
5920      * @param colName a <code>String</code> object that must match the
5921      *        SQL name of a column in this rowset, ignoring case
5922      * @return a <code>Ref</code> object representing an SQL<code> REF</code> value
5923      * @throws SQLException  if (1) the given column name is not the name of
5924      *            a column in this rowset, (2) the cursor is not on one of
5925      *            this rowset's rows or its insert row, or (3) the column value
5926      *            is not an SQL <code>REF</code> value
5927      * @see #getRef(int)
5928      */
5929     public Ref getRef(String colName) throws SQLException {
5930         return getRef(getColIdxByName(colName));
5931     }
5932 
5933     /**
5934      * Retrieves the value of the designated column in this
5935      * <code>CachedRowSetImpl</code> object as a <code>Blob</code> object
5936      * in the Java programming language.
5937      *
5938      * @param colName a <code>String</code> object that must match the
5939      *        SQL name of a column in this rowset, ignoring case
5940      * @return a <code>Blob</code> object representing an SQL <code>BLOB</code> value
5941      * @throws SQLException if (1) the given column name is not the name of
5942      *            a column in this rowset, (2) the cursor is not on one of
5943      *            this rowset's rows or its insert row, or (3) the designated
5944      *            column does not store an SQL <code>BLOB</code> value
5945      * @see #getBlob(int)
5946      */
5947     public Blob getBlob(String colName) throws SQLException {
5948         return getBlob(getColIdxByName(colName));
5949     }
5950 
5951     /**
5952      * Retrieves the value of the designated column in this
5953      * <code>CachedRowSetImpl</code> object as a <code>Clob</code> object
5954      * in the Java programming language.
5955      *
5956      * @param colName a <code>String</code> object that must match the
5957      *        SQL name of a column in this rowset, ignoring case
5958      * @return a <code>Clob</code> object representing an SQL
5959      *         <code>CLOB</code> value
5960      * @throws SQLException if (1) the given column name is not the name of
5961      *            a column in this rowset, (2) the cursor is not on one of
5962      *            this rowset's rows or its insert row, or (3) the designated
5963      *            column does not store an SQL <code>CLOB</code> value
5964      * @see #getClob(int)
5965      */
5966     public Clob getClob(String colName) throws SQLException {
5967         return getClob(getColIdxByName(colName));
5968     }
5969 
5970     /**
5971      * Retrieves the value of the designated column in this
5972      * <code>CachedRowSetImpl</code> object as an <code>Array</code> object
5973      * in the Java programming langugage.
5974      *
5975      * @param colName a <code>String</code> object that must match the
5976      *        SQL name of a column in this rowset, ignoring case
5977      * @return an <code>Array</code> object representing an SQL
5978      *         <code>ARRAY</code> value
5979      * @throws SQLException if (1) the given column name is not the name of
5980      *            a column in this rowset, (2) the cursor is not on one of
5981      *            this rowset's rows or its insert row, or (3) the designated
5982      *            column does not store an SQL <code>ARRAY</code> value
5983      * @see #getArray(int)
5984      */
5985     public Array getArray(String colName) throws SQLException {
5986         return getArray(getColIdxByName(colName));
5987     }
5988 
5989     /**
5990      * Retrieves the value of the designated column in the current row
5991      * of this <code>CachedRowSetImpl</code> object as a <code>java.sql.Date</code>
5992      * object, using the given <code>Calendar</code> object to construct an
5993      * appropriate millisecond value for the date.
5994      *
5995      * @param columnIndex the first column is <code>1</code>, the second
5996      *        is <code>2</code>, and so on; must be <code>1</code> or larger
5997      *        and equal to or less than the number of columns in the rowset
5998      * @param cal the <code>java.util.Calendar</code> object to use in
5999      *            constructing the date
6000      * @return the column value; if the value is SQL <code>NULL</code>,
6001      *         the result is <code>null</code>
6002      * @throws SQLException if (1) the given column name is not the name of
6003      *            a column in this rowset, (2) the cursor is not on one of
6004      *            this rowset's rows or its insert row, or (3) the designated
6005      *            column does not store an SQL <code>DATE</code> or
6006      *            <code>TIMESTAMP</code> value
6007      */
6008     public java.sql.Date getDate(int columnIndex, Calendar cal) throws SQLException {
6009         Object value;
6010 
6011         // sanity check.
6012         checkIndex(columnIndex);
6013         // make sure the cursor is on a valid row
6014         checkCursor();
6015 
6016         setLastValueNull(false);
6017         value = getCurrentRow().getColumnObject(columnIndex);
6018 
6019         // check for SQL NULL
6020         if (value == null) {
6021             setLastValueNull(true);
6022             return null;
6023         }
6024 
6025         value = convertTemporal(value,
6026         RowSetMD.getColumnType(columnIndex),
6027         java.sql.Types.DATE);
6028 
6029         // create a default calendar
6030         Calendar defaultCal = Calendar.getInstance();
6031         // set this Calendar to the time we have
6032         defaultCal.setTime((java.util.Date)value);
6033 
6034         /*
6035          * Now we can pull the pieces of the date out
6036          * of the default calendar and put them into
6037          * the user provided calendar
6038          */
6039         cal.set(Calendar.YEAR, defaultCal.get(Calendar.YEAR));
6040         cal.set(Calendar.MONTH, defaultCal.get(Calendar.MONTH));
6041         cal.set(Calendar.DAY_OF_MONTH, defaultCal.get(Calendar.DAY_OF_MONTH));
6042 
6043         /*
6044          * This looks a little odd but it is correct -
6045          * Calendar.getTime() returns a Date...
6046          */
6047         return new java.sql.Date(cal.getTime().getTime());
6048     }
6049 
6050     /**
6051      * Retrieves the value of the designated column in the current row
6052      * of this <code>CachedRowSetImpl</code> object as a <code>java.sql.Date</code>
6053      * object, using the given <code>Calendar</code> object to construct an
6054      * appropriate millisecond value for the date.
6055      *
6056      * @param columnName a <code>String</code> object that must match the
6057      *        SQL name of a column in this rowset, ignoring case
6058      * @param cal the <code>java.util.Calendar</code> object to use in
6059      *            constructing the date
6060      * @return the column value; if the value is SQL <code>NULL</code>,
6061      *         the result is <code>null</code>
6062      * @throws SQLException if (1) the given column name is not the name of
6063      *            a column in this rowset, (2) the cursor is not on one of
6064      *            this rowset's rows or its insert row, or (3) the designated
6065      *            column does not store an SQL <code>DATE</code> or
6066      *            <code>TIMESTAMP</code> value
6067      */
6068     public java.sql.Date getDate(String columnName, Calendar cal) throws SQLException {
6069         return getDate(getColIdxByName(columnName), cal);
6070     }
6071 
6072     /**
6073      * Retrieves the value of the designated column in the current row
6074      * of this <code>CachedRowSetImpl</code> object as a <code>java.sql.Time</code>
6075      * object, using the given <code>Calendar</code> object to construct an
6076      * appropriate millisecond value for the date.
6077      *
6078      * @param columnIndex the first column is <code>1</code>, the second
6079      *        is <code>2</code>, and so on; must be <code>1</code> or larger
6080      *        and equal to or less than the number of columns in the rowset
6081      * @param cal the <code>java.util.Calendar</code> object to use in
6082      *            constructing the date
6083      * @return the column value; if the value is SQL <code>NULL</code>,
6084      *         the result is <code>null</code>
6085      * @throws SQLException if (1) the given column name is not the name of
6086      *            a column in this rowset, (2) the cursor is not on one of
6087      *            this rowset's rows or its insert row, or (3) the designated
6088      *            column does not store an SQL <code>TIME</code> or
6089      *            <code>TIMESTAMP</code> value
6090      */
6091     public java.sql.Time getTime(int columnIndex, Calendar cal) throws SQLException {
6092         Object value;
6093 
6094         // sanity check.
6095         checkIndex(columnIndex);
6096         // make sure the cursor is on a valid row
6097         checkCursor();
6098 
6099         setLastValueNull(false);
6100         value = getCurrentRow().getColumnObject(columnIndex);
6101 
6102         // check for SQL NULL
6103         if (value == null) {
6104             setLastValueNull(true);
6105             return null;
6106         }
6107 
6108         value = convertTemporal(value,
6109         RowSetMD.getColumnType(columnIndex),
6110         java.sql.Types.TIME);
6111 
6112         // create a default calendar
6113         Calendar defaultCal = Calendar.getInstance();
6114         // set the time in the default calendar
6115         defaultCal.setTime((java.util.Date)value);
6116 
6117         /*
6118          * Now we can pull the pieces of the date out
6119          * of the default calendar and put them into
6120          * the user provided calendar
6121          */
6122         cal.set(Calendar.HOUR_OF_DAY, defaultCal.get(Calendar.HOUR_OF_DAY));
6123         cal.set(Calendar.MINUTE, defaultCal.get(Calendar.MINUTE));
6124         cal.set(Calendar.SECOND, defaultCal.get(Calendar.SECOND));
6125 
6126         return new java.sql.Time(cal.getTime().getTime());
6127     }
6128 
6129     /**
6130      * Retrieves the value of the designated column in the current row
6131      * of this <code>CachedRowSetImpl</code> object as a <code>java.sql.Time</code>
6132      * object, using the given <code>Calendar</code> object to construct an
6133      * appropriate millisecond value for the date.
6134      *
6135      * @param columnName a <code>String</code> object that must match the
6136      *        SQL name of a column in this rowset, ignoring case
6137      * @param cal the <code>java.util.Calendar</code> object to use in
6138      *            constructing the date
6139      * @return the column value; if the value is SQL <code>NULL</code>,
6140      *         the result is <code>null</code>
6141      * @throws SQLException if (1) the given column name is not the name of
6142      *            a column in this rowset, (2) the cursor is not on one of
6143      *            this rowset's rows or its insert row, or (3) the designated
6144      *            column does not store an SQL <code>TIME</code> or
6145      *            <code>TIMESTAMP</code> value
6146      */
6147     public java.sql.Time getTime(String columnName, Calendar cal) throws SQLException {
6148         return getTime(getColIdxByName(columnName), cal);
6149     }
6150 
6151     /**
6152      * Retrieves the value of the designated column in the current row
6153      * of this <code>CachedRowSetImpl</code> object as a <code>java.sql.Timestamp</code>
6154      * object, using the given <code>Calendar</code> object to construct an
6155      * appropriate millisecond value for the date.
6156      *
6157      * @param columnIndex the first column is <code>1</code>, the second
6158      *        is <code>2</code>, and so on; must be <code>1</code> or larger
6159      *        and equal to or less than the number of columns in the rowset
6160      * @param cal the <code>java.util.Calendar</code> object to use in
6161      *            constructing the date
6162      * @return the column value; if the value is SQL <code>NULL</code>,
6163      *         the result is <code>null</code>
6164      * @throws SQLException if (1) the given column name is not the name of
6165      *            a column in this rowset, (2) the cursor is not on one of
6166      *            this rowset's rows or its insert row, or (3) the designated
6167      *            column does not store an SQL <code>TIME</code> or
6168      *            <code>TIMESTAMP</code> value
6169      */
6170     public java.sql.Timestamp getTimestamp(int columnIndex, Calendar cal) throws SQLException {
6171         Object value;
6172 
6173         // sanity check.
6174         checkIndex(columnIndex);
6175         // make sure the cursor is on a valid row
6176         checkCursor();
6177 
6178         setLastValueNull(false);
6179         value = getCurrentRow().getColumnObject(columnIndex);
6180 
6181         // check for SQL NULL
6182         if (value == null) {
6183             setLastValueNull(true);
6184             return null;
6185         }
6186 
6187         value = convertTemporal(value,
6188         RowSetMD.getColumnType(columnIndex),
6189         java.sql.Types.TIMESTAMP);
6190 
6191         // create a default calendar
6192         Calendar defaultCal = Calendar.getInstance();
6193         // set the time in the default calendar
6194         defaultCal.setTime((java.util.Date)value);
6195 
6196         /*
6197          * Now we can pull the pieces of the date out
6198          * of the default calendar and put them into
6199          * the user provided calendar
6200          */
6201         cal.set(Calendar.YEAR, defaultCal.get(Calendar.YEAR));
6202         cal.set(Calendar.MONTH, defaultCal.get(Calendar.MONTH));
6203         cal.set(Calendar.DAY_OF_MONTH, defaultCal.get(Calendar.DAY_OF_MONTH));
6204         cal.set(Calendar.HOUR_OF_DAY, defaultCal.get(Calendar.HOUR_OF_DAY));
6205         cal.set(Calendar.MINUTE, defaultCal.get(Calendar.MINUTE));
6206         cal.set(Calendar.SECOND, defaultCal.get(Calendar.SECOND));
6207 
6208         return new java.sql.Timestamp(cal.getTime().getTime());
6209     }
6210 
6211     /**
6212      * Retrieves the value of the designated column in the current row
6213      * of this <code>CachedRowSetImpl</code> object as a
6214      * <code>java.sql.Timestamp</code> object, using the given
6215      * <code>Calendar</code> object to construct an appropriate
6216      * millisecond value for the date.
6217      *
6218      * @param columnName a <code>String</code> object that must match the
6219      *        SQL name of a column in this rowset, ignoring case
6220      * @param cal the <code>java.util.Calendar</code> object to use in
6221      *            constructing the date
6222      * @return the column value; if the value is SQL <code>NULL</code>,
6223      *         the result is <code>null</code>
6224      * @throws SQLException if (1) the given column name is not the name of
6225      *            a column in this rowset, (2) the cursor is not on one of
6226      *            this rowset's rows or its insert row, or (3) the designated
6227      *            column does not store an SQL <code>DATE</code>,
6228      *            <code>TIME</code>, or <code>TIMESTAMP</code> value
6229      */
6230     public java.sql.Timestamp getTimestamp(String columnName, Calendar cal) throws SQLException {
6231         return getTimestamp(getColIdxByName(columnName), cal);
6232     }
6233 
6234     /*
6235      * RowSetInternal Interface
6236      */
6237 
6238     /**
6239      * Retrieves the <code>Connection</code> object passed to this
6240      * <code>CachedRowSetImpl</code> object.  This connection may be
6241      * used to populate this rowset with data or to write data back
6242      * to its underlying data source.
6243      *
6244      * @return the <code>Connection</code> object passed to this rowset;
6245      *         may be <code>null</code> if there is no connection
6246      * @throws SQLException if an error occurs
6247      */
6248     public Connection getConnection() throws SQLException{
6249         return conn;
6250     }
6251 
6252     /**
6253      * Sets the metadata for this <code>CachedRowSetImpl</code> object
6254      * with the given <code>RowSetMetaData</code> object.
6255      *
6256      * @param md a <code>RowSetMetaData</code> object instance containing
6257      *            metadata about the columsn in the rowset
6258      * @throws SQLException if invalid meta data is supplied to the
6259      *            rowset
6260      */
6261     public void setMetaData(RowSetMetaData md) throws SQLException {
6262         RowSetMD =(RowSetMetaDataImpl) md;
6263     }
6264 
6265     /**
6266      * Returns a result set containing the original value of the rowset. The
6267      * original value is the state of the <code>CachedRowSetImpl</code> after the
6268      * last population or synchronization (whichever occurred most recently) with
6269      * the data source.
6270      * <p>
6271      * The cursor is positioned before the first row in the result set.
6272      * Only rows contained in the result set returned by <code>getOriginal()</code>
6273      * are said to have an original value.
6274      *
6275      * @return the original result set of the rowset
6276      * @throws SQLException if an error occurs produce the
6277      *           <code>ResultSet</code> object
6278      */
6279     public ResultSet getOriginal() throws SQLException {
6280         CachedRowSetImpl crs = new CachedRowSetImpl();
6281         crs.RowSetMD = RowSetMD;
6282         crs.numRows = numRows;
6283         crs.cursorPos = 0;
6284 
6285         // make sure we don't get someone playing with these
6286         // %%% is this now necessary ???
6287         //crs.setReader(null);
6288         //crs.setWriter(null);
6289         int colCount = RowSetMD.getColumnCount();
6290         Row orig;
6291 
6292         for (Iterator<?> i = rvh.iterator(); i.hasNext();) {
6293             orig = new Row(colCount, ((Row)i.next()).getOrigRow());
6294             crs.rvh.add(orig);
6295         }
6296         return (ResultSet)crs;
6297     }
6298 
6299     /**
6300      * Returns a result set containing the original value of the current
6301      * row only.
6302      * The original value is the state of the <code>CachedRowSetImpl</code> after
6303      * the last population or synchronization (whichever occurred most recently)
6304      * with the data source.
6305      *
6306      * @return the original result set of the row
6307      * @throws SQLException if there is no current row
6308      * @see #setOriginalRow
6309      */
6310     public ResultSet getOriginalRow() throws SQLException {
6311         CachedRowSetImpl crs = new CachedRowSetImpl();
6312         crs.RowSetMD = RowSetMD;
6313         crs.numRows = 1;
6314         crs.cursorPos = 0;
6315         crs.setTypeMap(this.getTypeMap());
6316 
6317         // make sure we don't get someone playing with these
6318         // %%% is this now necessary ???
6319         //crs.setReader(null);
6320         //crs.setWriter(null);
6321 
6322         Row orig = new Row(RowSetMD.getColumnCount(),
6323         getCurrentRow().getOrigRow());
6324 
6325         crs.rvh.add(orig);
6326 
6327         return (ResultSet)crs;
6328 
6329     }
6330 
6331     /**
6332      * Marks the current row in this rowset as being an original row.
6333      *
6334      * @throws SQLException if there is no current row
6335      * @see #getOriginalRow
6336      */
6337     public void setOriginalRow() throws SQLException {
6338         if (onInsertRow == true) {
6339             throw new SQLException(resBundle.handleGetObject("cachedrowsetimpl.invalidop").toString());
6340         }
6341 
6342         Row row = (Row)getCurrentRow();
6343         makeRowOriginal(row);
6344 
6345         // this can happen if deleted rows are being shown
6346         if (row.getDeleted() == true) {
6347             removeCurrentRow();
6348         }
6349     }
6350 
6351     /**
6352      * Makes the given row of this rowset the original row by clearing any
6353      * settings that mark the row as having been inserted, deleted, or updated.
6354      * This method is called internally by the methods
6355      * <code>setOriginalRow</code>
6356      * and <code>setOriginal</code>.
6357      *
6358      * @param row the row to be made the original row
6359      */
6360     private void makeRowOriginal(Row row) {
6361         if (row.getInserted() == true) {
6362             row.clearInserted();
6363         }
6364 
6365         if (row.getUpdated() == true) {
6366             row.moveCurrentToOrig();
6367         }
6368     }
6369 
6370     /**
6371      * Marks all rows in this rowset as being original rows. Any updates
6372      * made to the rows become the original values for the rowset.
6373      * Calls to the method <code>setOriginal</code> connot be reversed.
6374      *
6375      * @throws SQLException if an error occurs
6376      */
6377     public void setOriginal() throws SQLException {
6378         for (Iterator<?> i = rvh.iterator(); i.hasNext();) {
6379             Row row = (Row)i.next();
6380             makeRowOriginal(row);
6381             // remove deleted rows from the collection.
6382             if (row.getDeleted() == true) {
6383                 i.remove();
6384                 --numRows;
6385             }
6386         }
6387         numDeleted = 0;
6388 
6389         // notify any listeners that the rowset has changed
6390         notifyRowSetChanged();
6391     }
6392 
6393     /**
6394      * Returns an identifier for the object (table) that was used to create this
6395      * rowset.
6396      *
6397      * @return a <code>String</code> object that identifies the table from
6398      *         which this <code>CachedRowSetImpl</code> object was derived
6399      * @throws SQLException if an error occurs
6400      */
6401     public String getTableName() throws SQLException {
6402         return tableName;
6403     }
6404 
6405     /**
6406      * Sets the identifier for the table from which this rowset was derived
6407      * to the given table name.
6408      *
6409      * @param tabName a <code>String</code> object that identifies the
6410      *          table from which this <code>CachedRowSetImpl</code> object
6411      *          was derived
6412      * @throws SQLException if an error occurs
6413      */
6414     public void setTableName(String tabName) throws SQLException {
6415         if (tabName == null)
6416             throw new SQLException(resBundle.handleGetObject("cachedrowsetimpl.tablename").toString());
6417         else
6418             tableName = tabName;
6419     }
6420 
6421     /**
6422      * Returns the columns that make a key to uniquely identify a
6423      * row in this <code>CachedRowSetImpl</code> object.
6424      *
6425      * @return an array of column numbers that constitutes a primary
6426      *           key for this rowset. This array should be empty
6427      *           if no column is representitive of a primary key
6428      * @throws SQLException if the rowset is empty or no columns
6429      *           are designated as primary keys
6430      * @see #setKeyColumns
6431      */
6432     public int[] getKeyColumns() throws SQLException {
6433         int[]keyColumns  = this.keyCols;
6434         return (keyColumns == null) ? null : Arrays.copyOf(keyColumns, keyColumns.length);
6435     }
6436 
6437 
6438     /**
6439      * Sets this <code>CachedRowSetImpl</code> object's
6440      * <code>keyCols</code> field with the given array of column
6441      * numbers, which forms a key for uniquely identifying a row
6442      * in this rowset.
6443      *
6444      * @param keys an array of <code>int</code> indicating the
6445      *        columns that form a primary key for this
6446      *        <code>CachedRowSetImpl</code> object; every
6447      *        element in the array must be greater than
6448      *        <code>0</code> and less than or equal to the number
6449      *        of columns in this rowset
6450      * @throws SQLException if any of the numbers in the
6451      *            given array is not valid for this rowset
6452      * @see #getKeyColumns
6453      */
6454     public void setKeyColumns(int [] keys) throws SQLException {
6455         int numCols = 0;
6456         if (RowSetMD != null) {
6457             numCols = RowSetMD.getColumnCount();
6458             if (keys.length > numCols)
6459                 throw new SQLException(resBundle.handleGetObject("cachedrowsetimpl.keycols").toString());
6460         }
6461         keyCols = new int[keys.length];
6462         for (int i = 0; i < keys.length; i++) {
6463             if (RowSetMD != null && (keys[i] <= 0 ||
6464             keys[i] > numCols)) {
6465                 throw new SQLException(resBundle.handleGetObject("cachedrowsetimpl.invalidcol").toString() +
6466                 keys[i]);
6467             }
6468             keyCols[i] = keys[i];
6469         }
6470     }
6471 
6472     /**
6473      * Sets the designated column in either the current row or the insert
6474      * row of this <code>CachedRowSetImpl</code> object with the given
6475      * <code>Ref</code> value.
6476      *
6477      * This method updates a column value in either the current row or
6478      * the insert row of this rowset, but it does not update the
6479      * database.  If the cursor is on a row in the rowset, the
6480      * method {@link #updateRow} must be called to update the database.
6481      * If the cursor is on the insert row, the method {@link #insertRow}
6482      * must be called, which will insert the new row into both this rowset
6483      * and the database. Both of these methods must be called before the
6484      * cursor moves to another row.
6485      *
6486      * @param columnIndex the first column is <code>1</code>, the second
6487      *        is <code>2</code>, and so on; must be <code>1</code> or larger
6488      *        and equal to or less than the number of columns in this rowset
6489      * @param ref the new column <code>java.sql.Ref</code> value
6490      * @throws SQLException if (1) the given column index is out of bounds,
6491      *        (2) the cursor is not on one of this rowset's rows or its
6492      *        insert row, or (3) this rowset is
6493      *        <code>ResultSet.CONCUR_READ_ONLY</code>
6494      */
6495     public void updateRef(int columnIndex, java.sql.Ref ref) throws SQLException {
6496         // sanity check.
6497         checkIndex(columnIndex);
6498         // make sure the cursor is on a valid row
6499         checkCursor();
6500 
6501         // SerialClob will help in getting the byte array and storing it.
6502         // We need to be checking DatabaseMetaData.locatorsUpdatorCopy()
6503         // or through RowSetMetaData.locatorsUpdatorCopy()
6504         getCurrentRow().setColumnObject(columnIndex, new SerialRef(ref));
6505     }
6506 
6507     /**
6508      * Sets the designated column in either the current row or the insert
6509      * row of this <code>CachedRowSetImpl</code> object with the given
6510      * <code>double</code> value.
6511      *
6512      * This method updates a column value in either the current row or
6513      * the insert row of this rowset, but it does not update the
6514      * database.  If the cursor is on a row in the rowset, the
6515      * method {@link #updateRow} must be called to update the database.
6516      * If the cursor is on the insert row, the method {@link #insertRow}
6517      * must be called, which will insert the new row into both this rowset
6518      * and the database. Both of these methods must be called before the
6519      * cursor moves to another row.
6520      *
6521      * @param columnName a <code>String</code> object that must match the
6522      *        SQL name of a column in this rowset, ignoring case
6523      * @param ref the new column <code>java.sql.Ref</code> value
6524      * @throws SQLException if (1) the given column name does not match the
6525      *        name of a column in this rowset, (2) the cursor is not on
6526      *        one of this rowset's rows or its insert row, or (3) this
6527      *        rowset is <code>ResultSet.CONCUR_READ_ONLY</code>
6528      */
6529     public void updateRef(String columnName, java.sql.Ref ref) throws SQLException {
6530         updateRef(getColIdxByName(columnName), ref);
6531     }
6532 
6533     /**
6534      * Sets the designated column in either the current row or the insert
6535      * row of this <code>CachedRowSetImpl</code> object with the given
6536      * <code>double</code> value.
6537      *
6538      * This method updates a column value in either the current row or
6539      * the insert row of this rowset, but it does not update the
6540      * database.  If the cursor is on a row in the rowset, the
6541      * method {@link #updateRow} must be called to update the database.
6542      * If the cursor is on the insert row, the method {@link #insertRow}
6543      * must be called, which will insert the new row into both this rowset
6544      * and the database. Both of these methods must be called before the
6545      * cursor moves to another row.
6546      *
6547      * @param columnIndex the first column is <code>1</code>, the second
6548      *        is <code>2</code>, and so on; must be <code>1</code> or larger
6549      *        and equal to or less than the number of columns in this rowset
6550      * @param c the new column <code>Clob</code> value
6551      * @throws SQLException if (1) the given column index is out of bounds,
6552      *        (2) the cursor is not on one of this rowset's rows or its
6553      *        insert row, or (3) this rowset is
6554      *        <code>ResultSet.CONCUR_READ_ONLY</code>
6555      */
6556     public void updateClob(int columnIndex, Clob c) throws SQLException {
6557         // sanity check.
6558         checkIndex(columnIndex);
6559         // make sure the cursor is on a valid row
6560         checkCursor();
6561 
6562         // SerialClob will help in getting the byte array and storing it.
6563         // We need to be checking DatabaseMetaData.locatorsUpdatorCopy()
6564         // or through RowSetMetaData.locatorsUpdatorCopy()
6565 
6566         if(dbmslocatorsUpdateCopy){
6567            getCurrentRow().setColumnObject(columnIndex, new SerialClob(c));
6568         }
6569         else{
6570            throw new SQLException(resBundle.handleGetObject("cachedrowsetimpl.opnotsupp").toString());
6571         }
6572     }
6573 
6574     /**
6575      * Sets the designated column in either the current row or the insert
6576      * row of this <code>CachedRowSetImpl</code> object with the given
6577      * <code>double</code> value.
6578      *
6579      * This method updates a column value in either the current row or
6580      * the insert row of this rowset, but it does not update the
6581      * database.  If the cursor is on a row in the rowset, the
6582      * method {@link #updateRow} must be called to update the database.
6583      * If the cursor is on the insert row, the method {@link #insertRow}
6584      * must be called, which will insert the new row into both this rowset
6585      * and the database. Both of these methods must be called before the
6586      * cursor moves to another row.
6587      *
6588      * @param columnName a <code>String</code> object that must match the
6589      *        SQL name of a column in this rowset, ignoring case
6590      * @param c the new column <code>Clob</code> value
6591      * @throws SQLException if (1) the given column name does not match the
6592      *            name of a column in this rowset, (2) the cursor is not on
6593      *            one of this rowset's rows or its insert row, or (3) this
6594      *            rowset is <code>ResultSet.CONCUR_READ_ONLY</code>
6595      */
6596     public void updateClob(String columnName, Clob c) throws SQLException {
6597         updateClob(getColIdxByName(columnName), c);
6598     }
6599 
6600     /**
6601      * Sets the designated column in either the current row or the insert
6602      * row of this <code>CachedRowSetImpl</code> object with the given
6603      * <code>java.sql.Blob</code> value.
6604      *
6605      * This method updates a column value in either the current row or
6606      * the insert row of this rowset, but it does not update the
6607      * database.  If the cursor is on a row in the rowset, the
6608      * method {@link #updateRow} must be called to update the database.
6609      * If the cursor is on the insert row, the method {@link #insertRow}
6610      * must be called, which will insert the new row into both this rowset
6611      * and the database. Both of these methods must be called before the
6612      * cursor moves to another row.
6613      *
6614      * @param columnIndex the first column is <code>1</code>, the second
6615      *        is <code>2</code>, and so on; must be <code>1</code> or larger
6616      *        and equal to or less than the number of columns in this rowset
6617      * @param b the new column <code>Blob</code> value
6618      * @throws SQLException if (1) the given column index is out of bounds,
6619      *            (2) the cursor is not on one of this rowset's rows or its
6620      *            insert row, or (3) this rowset is
6621      *            <code>ResultSet.CONCUR_READ_ONLY</code>
6622      */
6623     public void updateBlob(int columnIndex, Blob b) throws SQLException {
6624         // sanity check.
6625         checkIndex(columnIndex);
6626         // make sure the cursor is on a valid row
6627         checkCursor();
6628 
6629         // SerialBlob will help in getting the byte array and storing it.
6630         // We need to be checking DatabaseMetaData.locatorsUpdatorCopy()
6631         // or through RowSetMetaData.locatorsUpdatorCopy()
6632 
6633         if(dbmslocatorsUpdateCopy){
6634            getCurrentRow().setColumnObject(columnIndex, new SerialBlob(b));
6635         }
6636         else{
6637            throw new SQLException(resBundle.handleGetObject("cachedrowsetimpl.opnotsupp").toString());
6638         }
6639     }
6640 
6641     /**
6642      * Sets the designated column in either the current row or the insert
6643      * row of this <code>CachedRowSetImpl</code> object with the given
6644      * <code>java.sql.Blob </code> value.
6645      *
6646      * This method updates a column value in either the current row or
6647      * the insert row of this rowset, but it does not update the
6648      * database.  If the cursor is on a row in the rowset, the
6649      * method {@link #updateRow} must be called to update the database.
6650      * If the cursor is on the insert row, the method {@link #insertRow}
6651      * must be called, which will insert the new row into both this rowset
6652      * and the database. Both of these methods must be called before the
6653      * cursor moves to another row.
6654      *
6655      * @param columnName a <code>String</code> object that must match the
6656      *        SQL name of a column in this rowset, ignoring case
6657      * @param b the new column <code>Blob</code> value
6658      * @throws SQLException if (1) the given column name does not match the
6659      *            name of a column in this rowset, (2) the cursor is not on
6660      *            one of this rowset's rows or its insert row, or (3) this
6661      *            rowset is <code>ResultSet.CONCUR_READ_ONLY</code>
6662      */
6663     public void updateBlob(String columnName, Blob b) throws SQLException {
6664         updateBlob(getColIdxByName(columnName), b);
6665     }
6666 
6667     /**
6668      * Sets the designated column in either the current row or the insert
6669      * row of this <code>CachedRowSetImpl</code> object with the given
6670      * <code>java.sql.Array</code> values.
6671      *
6672      * This method updates a column value in either the current row or
6673      * the insert row of this rowset, but it does not update the
6674      * database.  If the cursor is on a row in the rowset, the
6675      * method {@link #updateRow} must be called to update the database.
6676      * If the cursor is on the insert row, the method {@link #insertRow}
6677      * must be called, which will insert the new row into both this rowset
6678      * and the database. Both of these methods must be called before the
6679      * cursor moves to another row.
6680      *
6681      * @param columnIndex the first column is <code>1</code>, the second
6682      *        is <code>2</code>, and so on; must be <code>1</code> or larger
6683      *        and equal to or less than the number of columns in this rowset
6684      * @param a the new column <code>Array</code> value
6685      * @throws SQLException if (1) the given column index is out of bounds,
6686      *            (2) the cursor is not on one of this rowset's rows or its
6687      *            insert row, or (3) this rowset is
6688      *            <code>ResultSet.CONCUR_READ_ONLY</code>
6689      */
6690     public void updateArray(int columnIndex, Array a) throws SQLException {
6691         // sanity check.
6692         checkIndex(columnIndex);
6693         // make sure the cursor is on a valid row
6694         checkCursor();
6695 
6696         // SerialArray will help in getting the byte array and storing it.
6697         // We need to be checking DatabaseMetaData.locatorsUpdatorCopy()
6698         // or through RowSetMetaData.locatorsUpdatorCopy()
6699         getCurrentRow().setColumnObject(columnIndex, new SerialArray(a));
6700     }
6701 
6702     /**
6703      * Sets the designated column in either the current row or the insert
6704      * row of this <code>CachedRowSetImpl</code> object with the given
6705      * <code>java.sql.Array</code> value.
6706      *
6707      * This method updates a column value in either the current row or
6708      * the insert row of this rowset, but it does not update the
6709      * database.  If the cursor is on a row in the rowset, the
6710      * method {@link #updateRow} must be called to update the database.
6711      * If the cursor is on the insert row, the method {@link #insertRow}
6712      * must be called, which will insert the new row into both this rowset
6713      * and the database. Both of these methods must be called before the
6714      * cursor moves to another row.
6715      *
6716      * @param columnName a <code>String</code> object that must match the
6717      *        SQL name of a column in this rowset, ignoring case
6718      * @param a the new column <code>Array</code> value
6719      * @throws SQLException if (1) the given column name does not match the
6720      *            name of a column in this rowset, (2) the cursor is not on
6721      *            one of this rowset's rows or its insert row, or (3) this
6722      *            rowset is <code>ResultSet.CONCUR_READ_ONLY</code>
6723      */
6724     public void updateArray(String columnName, Array a) throws SQLException {
6725         updateArray(getColIdxByName(columnName), a);
6726     }
6727 
6728 
6729     /**
6730      * Retrieves the value of the designated column in this
6731      * <code>CachedRowSetImpl</code> object as a <code>java.net.URL</code> object
6732      * in the Java programming language.
6733      *
6734      * @return a java.net.URL object containing the resource reference described by
6735      * the URL
6736      * @throws SQLException if (1) the given column index is out of bounds,
6737      * (2) the cursor is not on one of this rowset's rows or its
6738      * insert row, or (3) the designated column does not store an
6739      * SQL <code>DATALINK</code> value.
6740      * @see #getURL(String)
6741      */
6742     public java.net.URL getURL(int columnIndex) throws SQLException {
6743         //throw new SQLException("Operation not supported");
6744 
6745         java.net.URL value;
6746 
6747         // sanity check.
6748         checkIndex(columnIndex);
6749         // make sure the cursor is on a valid row
6750         checkCursor();
6751 
6752         if (RowSetMD.getColumnType(columnIndex) != java.sql.Types.DATALINK) {
6753             throw new SQLException(resBundle.handleGetObject("cachedrowsetimpl.dtypemismt").toString());
6754         }
6755 
6756         setLastValueNull(false);
6757         value = (java.net.URL)(getCurrentRow().getColumnObject(columnIndex));
6758 
6759         // check for SQL NULL
6760         if (value == null) {
6761             setLastValueNull(true);
6762             return null;
6763         }
6764 
6765         return value;
6766     }
6767 
6768     /**
6769      * Retrieves the value of the designated column in this
6770      * <code>CachedRowSetImpl</code> object as a <code>java.net.URL</code> object
6771      * in the Java programming language.
6772      *
6773      * @return a java.net.URL object containing the resource reference described by
6774      * the URL
6775      * @throws SQLException if (1) the given column name not the name of a column
6776      * in this rowset, or
6777      * (2) the cursor is not on one of this rowset's rows or its
6778      * insert row, or (3) the designated column does not store an
6779      * SQL <code>DATALINK</code> value.
6780      * @see #getURL(int)
6781      */
6782     public java.net.URL getURL(String columnName) throws SQLException {
6783         return getURL(getColIdxByName(columnName));
6784 
6785     }
6786 
6787     /**
6788      * The first warning reported by calls on this <code>CachedRowSetImpl</code>
6789      * object is returned. Subsequent <code>CachedRowSetImpl</code> warnings will
6790      * be chained to this <code>SQLWarning</code>. All <code>RowSetWarnings</code>
6791      * warnings are generated in the disconnected environment and remain a
6792      * seperate warning chain to that provided by the <code>getWarnings</code>
6793      * method.
6794      *
6795      * <P>The warning chain is automatically cleared each time a new
6796      * row is read.
6797      *
6798      * <P><B>Note:</B> This warning chain only covers warnings caused
6799      * by <code>CachedRowSet</code> (and their child interface)
6800      * methods. All <code>SQLWarnings</code> can be obtained using the
6801      * <code>getWarnings</code> method which tracks warnings generated
6802      * by the underlying JDBC driver.
6803      * @return the first SQLWarning or null
6804      *
6805      */
6806     public RowSetWarning getRowSetWarnings() {
6807         try {
6808             notifyCursorMoved();
6809         } catch (SQLException e) {} // mask exception
6810         return rowsetWarning;
6811     }
6812 
6813 
6814     /**
6815      * The function tries to isolate the tablename when only setCommand
6816      * is set and not setTablename is called provided there is only one table
6817      * name in the query else just leaves the setting of table name as such.
6818      * If setTablename is set later it will over ride this table name
6819      * value so retrieved.
6820      *
6821      * @return the tablename if only one table in query else return ""
6822      */
6823     private String buildTableName(String command) throws SQLException {
6824 
6825         // If we have a query from one table,
6826         // we set the table name implicitly
6827         // else user has to explicitly set the table name.
6828 
6829         int indexFrom, indexComma;
6830         String strTablename ="";
6831         command = command.trim();
6832 
6833         // Query can be a select, insert or  update
6834 
6835         if(command.toLowerCase().startsWith("select")) {
6836             // look for "from" keyword, after that look for a
6837             // comma after from. If comma is there don't set
6838             // table name else isolate table name.
6839 
6840             indexFrom = command.toLowerCase().indexOf("from");
6841             indexComma = command.indexOf(",", indexFrom);
6842 
6843             if(indexComma == -1) {
6844                 // implies only one table
6845                 strTablename = (command.substring(indexFrom+"from".length(),command.length())).trim();
6846 
6847                 String tabName = strTablename;
6848 
6849                 int idxWhere = tabName.toLowerCase().indexOf("where");
6850 
6851                 /**
6852                   * Adding the addtional check for conditions following the table name.
6853                   * If a condition is found truncate it.
6854                   **/
6855 
6856                 if(idxWhere != -1)
6857                 {
6858                    tabName = tabName.substring(0,idxWhere).trim();
6859                 }
6860 
6861                 strTablename = tabName;
6862 
6863             } else {
6864                 //strTablename="";
6865             }
6866 
6867         } else if(command.toLowerCase().startsWith("insert")) {
6868             //strTablename="";
6869         } else if(command.toLowerCase().startsWith("update")) {
6870             //strTablename="";
6871         }
6872         return strTablename;
6873     }
6874 
6875     /**
6876      * Commits all changes performed by the <code>acceptChanges()</code>
6877      * methods
6878      *
6879      * @see java.sql.Connection#commit
6880      */
6881     public void commit() throws SQLException {
6882         conn.commit();
6883     }
6884 
6885     /**
6886      * Rolls back all changes performed by the <code>acceptChanges()</code>
6887      * methods
6888      *
6889      * @see java.sql.Connection#rollback
6890      */
6891     public void rollback() throws SQLException {
6892         conn.rollback();
6893     }
6894 
6895     /**
6896      * Rolls back all changes performed by the <code>acceptChanges()</code>
6897      * to the last <code>Savepoint</code> transaction marker.
6898      *
6899      * @see java.sql.Connection#rollback(Savepoint)
6900      */
6901     public void rollback(Savepoint s) throws SQLException {
6902         conn.rollback(s);
6903     }
6904 
6905     /**
6906      * Unsets the designated parameter to the given int array.
6907      * This was set using <code>setMatchColumn</code>
6908      * as the column which will form the basis of the join.
6909      * <P>
6910      * The parameter value unset by this method should be same
6911      * as was set.
6912      *
6913      * @param columnIdxes the index into this rowset
6914      *        object's internal representation of parameter values
6915      * @throws SQLException if an error occurs or the
6916      *  parameter index is out of bounds or if the columnIdx is
6917      *  not the same as set using <code>setMatchColumn(int [])</code>
6918      */
6919     public void unsetMatchColumn(int[] columnIdxes) throws SQLException {
6920 
6921          int i_val;
6922          for( int j= 0 ;j < columnIdxes.length; j++) {
6923             i_val = (Integer.parseInt(iMatchColumns.get(j).toString()));
6924             if(columnIdxes[j] != i_val) {
6925                throw new SQLException(resBundle.handleGetObject("cachedrowsetimpl.matchcols").toString());
6926             }
6927          }
6928 
6929          for( int i = 0;i < columnIdxes.length ;i++) {
6930             iMatchColumns.set(i, -1);
6931          }
6932     }
6933 
6934    /**
6935      * Unsets the designated parameter to the given String array.
6936      * This was set using <code>setMatchColumn</code>
6937      * as the column which will form the basis of the join.
6938      * <P>
6939      * The parameter value unset by this method should be same
6940      * as was set.
6941      *
6942      * @param columnIdxes the index into this rowset
6943      *        object's internal representation of parameter values
6944      * @throws SQLException if an error occurs or the
6945      *  parameter index is out of bounds or if the columnName is
6946      *  not the same as set using <code>setMatchColumn(String [])</code>
6947      */
6948     public void unsetMatchColumn(String[] columnIdxes) throws SQLException {
6949 
6950         for(int j = 0 ;j < columnIdxes.length; j++) {
6951            if( !columnIdxes[j].equals(strMatchColumns.get(j)) ){
6952               throw new SQLException(resBundle.handleGetObject("cachedrowsetimpl.matchcols").toString());
6953            }
6954         }
6955 
6956         for(int i = 0 ; i < columnIdxes.length; i++) {
6957            strMatchColumns.set(i,null);
6958         }
6959     }
6960 
6961     /**
6962      * Retrieves the column name as <code>String</code> array
6963      * that was set using <code>setMatchColumn(String [])</code>
6964      * for this rowset.
6965      *
6966      * @return a <code>String</code> array object that contains the column names
6967      *         for the rowset which has this the match columns
6968      *
6969      * @throws SQLException if an error occurs or column name is not set
6970      */
6971     public String[] getMatchColumnNames() throws SQLException {
6972 
6973         String []str_temp = new String[strMatchColumns.size()];
6974 
6975         if( strMatchColumns.get(0) == null) {
6976            throw new SQLException(resBundle.handleGetObject("cachedrowsetimpl.setmatchcols").toString());
6977         }
6978 
6979         strMatchColumns.copyInto(str_temp);
6980         return str_temp;
6981     }
6982 
6983     /**
6984      * Retrieves the column id as <code>int</code> array that was set using
6985      * <code>setMatchColumn(int [])</code> for this rowset.
6986      *
6987      * @return a <code>int</code> array object that contains the column ids
6988      *         for the rowset which has this as the match columns.
6989      *
6990      * @throws SQLException if an error occurs or column index is not set
6991      */
6992     public int[] getMatchColumnIndexes() throws SQLException {
6993 
6994         Integer []int_temp = new Integer[iMatchColumns.size()];
6995         int [] i_temp = new int[iMatchColumns.size()];
6996         int i_val;
6997 
6998         i_val = iMatchColumns.get(0);
6999 
7000         if( i_val == -1 ) {
7001            throw new SQLException(resBundle.handleGetObject("cachedrowsetimpl.setmatchcols").toString());
7002         }
7003 
7004 
7005         iMatchColumns.copyInto(int_temp);
7006 
7007         for(int i = 0; i < int_temp.length; i++) {
7008            i_temp[i] = (int_temp[i]).intValue();
7009         }
7010 
7011         return i_temp;
7012     }
7013 
7014     /**
7015      * Sets the designated parameter to the given int array.
7016      * This forms the basis of the join for the
7017      * <code>JoinRowSet</code> as the column which will form the basis of the
7018      * join.
7019      * <P>
7020      * The parameter value set by this method is stored internally and
7021      * will be supplied as the appropriate parameter in this rowset's
7022      * command when the method <code>getMatchColumnIndexes</code> is called.
7023      *
7024      * @param columnIdxes the indexes into this rowset
7025      *        object's internal representation of parameter values; the
7026      *        first parameter is 0, the second is 1, and so on; must be
7027      *        <code>0</code> or greater
7028      * @throws SQLException if an error occurs or the
7029      *                         parameter index is out of bounds
7030      */
7031     public void setMatchColumn(int[] columnIdxes) throws SQLException {
7032 
7033         for(int j = 0 ; j < columnIdxes.length; j++) {
7034            if( columnIdxes[j] < 0 ) {
7035               throw new SQLException(resBundle.handleGetObject("cachedrowsetimpl.matchcols1").toString());
7036            }
7037         }
7038         for(int i = 0 ;i < columnIdxes.length; i++) {
7039            iMatchColumns.add(i,columnIdxes[i]);
7040         }
7041     }
7042 
7043     /**
7044      * Sets the designated parameter to the given String array.
7045      *  This forms the basis of the join for the
7046      * <code>JoinRowSet</code> as the column which will form the basis of the
7047      * join.
7048      * <P>
7049      * The parameter value set by this method is stored internally and
7050      * will be supplied as the appropriate parameter in this rowset's
7051      * command when the method <code>getMatchColumn</code> is called.
7052      *
7053      * @param columnNames the name of the column into this rowset
7054      *        object's internal representation of parameter values
7055      * @throws SQLException if an error occurs or the
7056      *  parameter index is out of bounds
7057      */
7058     public void setMatchColumn(String[] columnNames) throws SQLException {
7059 
7060         for(int j = 0; j < columnNames.length; j++) {
7061            if( columnNames[j] == null || columnNames[j].equals("")) {
7062               throw new SQLException(resBundle.handleGetObject("cachedrowsetimpl.matchcols2").toString());
7063            }
7064         }
7065         for( int i = 0; i < columnNames.length; i++) {
7066            strMatchColumns.add(i,columnNames[i]);
7067         }
7068     }
7069 
7070 
7071     /**
7072      * Sets the designated parameter to the given <code>int</code>
7073      * object.  This forms the basis of the join for the
7074      * <code>JoinRowSet</code> as the column which will form the basis of the
7075      * join.
7076      * <P>
7077      * The parameter value set by this method is stored internally and
7078      * will be supplied as the appropriate parameter in this rowset's
7079      * command when the method <code>getMatchColumn</code> is called.
7080      *
7081      * @param columnIdx the index into this rowset
7082      *        object's internal representation of parameter values; the
7083      *        first parameter is 0, the second is 1, and so on; must be
7084      *        <code>0</code> or greater
7085      * @throws SQLException if an error occurs or the
7086      *                         parameter index is out of bounds
7087      */
7088     public void setMatchColumn(int columnIdx) throws SQLException {
7089         // validate, if col is ok to be set
7090         if(columnIdx < 0) {
7091             throw new SQLException(resBundle.handleGetObject("cachedrowsetimpl.matchcols1").toString());
7092         } else {
7093             // set iMatchColumn
7094             iMatchColumns.set(0, columnIdx);
7095             //strMatchColumn = null;
7096         }
7097     }
7098 
7099     /**
7100      * Sets the designated parameter to the given <code>String</code>
7101      * object.  This forms the basis of the join for the
7102      * <code>JoinRowSet</code> as the column which will form the basis of the
7103      * join.
7104      * <P>
7105      * The parameter value set by this method is stored internally and
7106      * will be supplied as the appropriate parameter in this rowset's
7107      * command when the method <code>getMatchColumn</code> is called.
7108      *
7109      * @param columnName the name of the column into this rowset
7110      *        object's internal representation of parameter values
7111      * @throws SQLException if an error occurs or the
7112      *  parameter index is out of bounds
7113      */
7114     public void setMatchColumn(String columnName) throws SQLException {
7115         // validate, if col is ok to be set
7116         if(columnName == null || (columnName= columnName.trim()).equals("") ) {
7117             throw new SQLException(resBundle.handleGetObject("cachedrowsetimpl.matchcols2").toString());
7118         } else {
7119             // set strMatchColumn
7120             strMatchColumns.set(0, columnName);
7121             //iMatchColumn = -1;
7122         }
7123     }
7124 
7125     /**
7126      * Unsets the designated parameter to the given <code>int</code>
7127      * object.  This was set using <code>setMatchColumn</code>
7128      * as the column which will form the basis of the join.
7129      * <P>
7130      * The parameter value unset by this method should be same
7131      * as was set.
7132      *
7133      * @param columnIdx the index into this rowset
7134      *        object's internal representation of parameter values
7135      * @throws SQLException if an error occurs or the
7136      *  parameter index is out of bounds or if the columnIdx is
7137      *  not the same as set using <code>setMatchColumn(int)</code>
7138      */
7139     public void unsetMatchColumn(int columnIdx) throws SQLException {
7140         // check if we are unsetting the SAME column
7141         if(! iMatchColumns.get(0).equals(Integer.valueOf(columnIdx) )  ) {
7142             throw new SQLException(resBundle.handleGetObject("cachedrowsetimpl.unsetmatch").toString());
7143         } else if(strMatchColumns.get(0) != null) {
7144             throw new SQLException(resBundle.handleGetObject("cachedrowsetimpl.unsetmatch1").toString());
7145         } else {
7146                 // that is, we are unsetting it.
7147                iMatchColumns.set(0, -1);
7148         }
7149     }
7150 
7151     /**
7152      * Unsets the designated parameter to the given <code>String</code>
7153      * object.  This was set using <code>setMatchColumn</code>
7154      * as the column which will form the basis of the join.
7155      * <P>
7156      * The parameter value unset by this method should be same
7157      * as was set.
7158      *
7159      * @param columnName the index into this rowset
7160      *        object's internal representation of parameter values
7161      * @throws SQLException if an error occurs or the
7162      *  parameter index is out of bounds or if the columnName is
7163      *  not the same as set using <code>setMatchColumn(String)</code>
7164      */
7165     public void unsetMatchColumn(String columnName) throws SQLException {
7166         // check if we are unsetting the same column
7167         columnName = columnName.trim();
7168 
7169         if(!((strMatchColumns.get(0)).equals(columnName))) {
7170             throw new SQLException(resBundle.handleGetObject("cachedrowsetimpl.unsetmatch").toString());
7171         } else if(iMatchColumns.get(0) > 0) {
7172             throw new SQLException(resBundle.handleGetObject("cachedrowsetimpl.unsetmatch2").toString());
7173         } else {
7174             strMatchColumns.set(0, null);   // that is, we are unsetting it.
7175         }
7176     }
7177 
7178     /**
7179      * Notifies registered listeners that a RowSet object in the given RowSetEvent
7180      * object has populated a number of additional rows. The <code>numRows</code> parameter
7181      * ensures that this event will only be fired every <code>numRow</code>.
7182      * <p>
7183      * The source of the event can be retrieved with the method event.getSource.
7184      *
7185      * @param event a <code>RowSetEvent</code> object that contains the
7186      *     <code>RowSet</code> object that is the source of the events
7187      * @param numRows when populating, the number of rows interval on which the
7188      *     <code>CachedRowSet</code> populated should fire; the default value
7189      *     is zero; cannot be less than <code>fetchSize</code> or zero
7190      */
7191     public void rowSetPopulated(RowSetEvent event, int numRows) throws SQLException {
7192 
7193         if( numRows < 0 || numRows < getFetchSize()) {
7194            throw new SQLException(resBundle.handleGetObject("cachedrowsetimpl.numrows").toString());
7195         }
7196 
7197         if(size() % numRows == 0) {
7198             RowSetEvent event_temp = new RowSetEvent(this);
7199             event = event_temp;
7200             notifyRowSetChanged();
7201         }
7202     }
7203 
7204     /**
7205      * Populates this <code>CachedRowSet</code> object with data from
7206      * the given <code>ResultSet</code> object. While related to the <code>populate(ResultSet)</code>
7207      * method, an additional parameter is provided to allow starting position within
7208      * the <code>ResultSet</code> from where to populate the CachedRowSet
7209      * instance.
7210      *
7211      * This method is an alternative to the method <code>execute</code>
7212      * for filling the rowset with data.  The method <code>populate</code>
7213      * does not require that the properties needed by the method
7214      * <code>execute</code>, such as the <code>command</code> property,
7215      * be set. This is true because the method <code>populate</code>
7216      * is given the <code>ResultSet</code> object from
7217      * which to get data and thus does not need to use the properties
7218      * required for setting up a connection and executing this
7219      * <code>CachedRowSetImpl</code> object's command.
7220      * <P>
7221      * After populating this rowset with data, the method
7222      * <code>populate</code> sets the rowset's metadata and
7223      * then sends a <code>RowSetChangedEvent</code> object
7224      * to all registered listeners prior to returning.
7225      *
7226      * @param data the <code>ResultSet</code> object containing the data
7227      *             to be read into this <code>CachedRowSetImpl</code> object
7228      * @param start the integer specifing the position in the
7229      *        <code>ResultSet</code> object to popultate the
7230      *        <code>CachedRowSetImpl</code> object.
7231      * @throws SQLException if an error occurs; or the max row setting is
7232      *          violated while populating the RowSet.Also id the start position
7233      *          is negative.
7234      * @see #execute
7235      */
7236      public void populate(ResultSet data, int start) throws SQLException{
7237 
7238         int rowsFetched;
7239         Row currentRow;
7240         int numCols;
7241         int i;
7242         Map<String, Class<?>> map = getTypeMap();
7243         Object obj;
7244         int mRows;
7245 
7246         cursorPos = 0;
7247         if(populatecallcount == 0){
7248             if(start < 0){
7249                throw new SQLException(resBundle.handleGetObject("cachedrowsetimpl.startpos").toString());
7250             }
7251             if(getMaxRows() == 0){
7252                data.absolute(start);
7253                while(data.next()){
7254                    totalRows++;
7255                }
7256                totalRows++;
7257             }
7258             startPos = start;
7259         }
7260         populatecallcount = populatecallcount +1;
7261         resultSet = data;
7262         if((endPos - startPos) >= getMaxRows() && (getMaxRows() > 0)){
7263             endPos = prevEndPos;
7264             pagenotend = false;
7265             return;
7266         }
7267 
7268         if((maxRowsreached != getMaxRows() || maxRowsreached != totalRows) && pagenotend) {
7269            startPrev = start - getPageSize();
7270         }
7271 
7272         if( pageSize == 0){
7273            prevEndPos = endPos;
7274            endPos = start + getMaxRows() ;
7275         }
7276         else{
7277             prevEndPos = endPos;
7278             endPos = start + getPageSize();
7279         }
7280 
7281 
7282         if (start == 1){
7283             resultSet.beforeFirst();
7284         }
7285         else {
7286             resultSet.absolute(start -1);
7287         }
7288         if( pageSize == 0) {
7289            rvh = new Vector<Object>(getMaxRows());
7290 
7291         }
7292         else{
7293             rvh = new Vector<Object>(getPageSize());
7294         }
7295 
7296         if (data == null) {
7297             throw new SQLException(resBundle.handleGetObject("cachedrowsetimpl.populate").toString());
7298         }
7299 
7300         // get the meta data for this ResultSet
7301         RSMD = data.getMetaData();
7302 
7303         // set up the metadata
7304         RowSetMD = new RowSetMetaDataImpl();
7305         initMetaData(RowSetMD, RSMD);
7306 
7307         // release the meta-data so that aren't tempted to use it.
7308         RSMD = null;
7309         numCols = RowSetMD.getColumnCount();
7310         mRows = this.getMaxRows();
7311         rowsFetched = 0;
7312         currentRow = null;
7313 
7314         if(!data.next() && mRows == 0){
7315             endPos = prevEndPos;
7316             pagenotend = false;
7317             return;
7318         }
7319 
7320         data.previous();
7321 
7322         while ( data.next()) {
7323 
7324             currentRow = new Row(numCols);
7325           if(pageSize == 0){
7326             if ( rowsFetched >= mRows && mRows > 0) {
7327                 rowsetWarning.setNextException(new SQLException("Populating rows "
7328                 + "setting has exceeded max row setting"));
7329                 break;
7330             }
7331           }
7332           else {
7333               if ( (rowsFetched >= pageSize) ||( maxRowsreached >= mRows && mRows > 0)) {
7334                 rowsetWarning.setNextException(new SQLException("Populating rows "
7335                 + "setting has exceeded max row setting"));
7336                 break;
7337             }
7338           }
7339 
7340             for ( i = 1; i <= numCols; i++) {
7341                 /*
7342                  * check if the user has set a map. If no map
7343                  * is set then use plain getObject. This lets
7344                  * us work with drivers that do not support
7345                  * getObject with a map in fairly sensible way
7346                  */
7347                 if (map == null) {
7348                     obj = data.getObject(i);
7349                 } else {
7350                     obj = data.getObject(i, map);
7351                 }
7352                 /*
7353                  * the following block checks for the various
7354                  * types that we have to serialize in order to
7355                  * store - right now only structs have been tested
7356                  */
7357                 if (obj instanceof Struct) {
7358                     obj = new SerialStruct((Struct)obj, map);
7359                 } else if (obj instanceof SQLData) {
7360                     obj = new SerialStruct((SQLData)obj, map);
7361                 } else if (obj instanceof Blob) {
7362                     obj = new SerialBlob((Blob)obj);
7363                 } else if (obj instanceof Clob) {
7364                     obj = new SerialClob((Clob)obj);
7365                 } else if (obj instanceof java.sql.Array) {
7366                     obj = new SerialArray((java.sql.Array)obj, map);
7367                 }
7368 
7369                 currentRow.initColumnObject(i, obj);
7370             }
7371             rowsFetched++;
7372             maxRowsreached++;
7373             rvh.add(currentRow);
7374         }
7375         numRows = rowsFetched ;
7376         // Also rowsFetched should be equal to rvh.size()
7377         // notify any listeners that the rowset has changed
7378         notifyRowSetChanged();
7379 
7380      }
7381 
7382     /**
7383      * The nextPage gets the next page, that is a <code>CachedRowSetImpl</code> object
7384      * containing the number of rows specified by page size.
7385      * @return boolean value true indicating whether there are more pages to come and
7386      *         false indicating that this is the last page.
7387      * @throws SQLException if an error occurs or this called before calling populate.
7388      */
7389      public boolean nextPage() throws SQLException {
7390 
7391          if (populatecallcount == 0){
7392              throw new SQLException(resBundle.handleGetObject("cachedrowsetimpl.nextpage").toString());
7393          }
7394          // Fix for 6554186
7395          onFirstPage = false;
7396          if(callWithCon){
7397             crsReader.setStartPosition(endPos);
7398             crsReader.readData((RowSetInternal)this);
7399             resultSet = null;
7400          }
7401          else {
7402             populate(resultSet,endPos);
7403          }
7404          return pagenotend;
7405      }
7406 
7407     /**
7408      * This is the setter function for setting the size of the page, which specifies
7409      * how many rows have to be retrived at a time.
7410      *
7411      * @param size which is the page size
7412      * @throws SQLException if size is less than zero or greater than max rows.
7413      */
7414      public void setPageSize (int size) throws SQLException {
7415         if (size < 0) {
7416             throw new SQLException(resBundle.handleGetObject("cachedrowsetimpl.pagesize").toString());
7417         }
7418         if (size > getMaxRows() && getMaxRows() != 0) {
7419             throw new SQLException(resBundle.handleGetObject("cachedrowsetimpl.pagesize1").toString());
7420         }
7421         pageSize = size;
7422      }
7423 
7424     /**
7425      * This is the getter function for the size of the page.
7426      *
7427      * @return an integer that is the page size.
7428      */
7429     public int getPageSize() {
7430         return pageSize;
7431     }
7432 
7433 
7434     /**
7435      * Retrieves the data present in the page prior to the page from where it is
7436      * called.
7437      * @return boolean value true if it retrieves the previous page, flase if it
7438      *         is on the first page.
7439      * @throws SQLException if it is called before populate is called or ResultSet
7440      *         is of type <code>ResultSet.TYPE_FORWARD_ONLY</code> or if an error
7441      *         occurs.
7442      */
7443     public boolean previousPage() throws SQLException {
7444         int pS;
7445         int mR;
7446         int rem;
7447 
7448         pS = getPageSize();
7449         mR = maxRowsreached;
7450 
7451         if (populatecallcount == 0){
7452              throw new SQLException(resBundle.handleGetObject("cachedrowsetimpl.nextpage").toString());
7453          }
7454