Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: com/tripi/asp/ADODB/RecordSet.java


1   /**
2    * ArrowHead ASP Server 
3    * This is a source file for the ArrowHead ASP Server - an 100% Java
4    * VBScript interpreter and ASP server.
5    *
6    * For more information, see http://www.tripi.com/arrowhead
7    *
8    * Copyright (C) 2002  Terence Haddock
9    *
10   * This program is free software; you can redistribute it and/or modify
11   * it under the terms of the GNU General Public License as published by
12   * the Free Software Foundation; either version 2 of the License, or
13   * (at your option) any later version.
14   *
15   * This program is distributed in the hope that it will be useful,
16   * but WITHOUT ANY WARRANTY; without even the implied warranty of
17   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18   * GNU General Public License for more details.
19   *
20   * You should have received a copy of the GNU General Public License
21   * along with this program; if not, write to the Free Software
22   * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23   *
24   */
25  package com.tripi.asp.ADODB;
26  
27  import com.tripi.asp.*;
28  
29  import java.sql.SQLException;
30  import java.sql.ResultSet;
31  import java.sql.ResultSetMetaData;
32  import java.sql.Statement;
33  
34  import java.util.Enumeration;
35  import java.util.Hashtable;
36  
37  import java.io.*;
38  
39  import org.apache.log4j.Category;
40  
41  /**
42   * This class implements the ADODB.RecordSet object.
43   * RecordSet Implementation status:
44   * <ul>
45   * <li>AbsolutePage - Implemented <b>TODO</b> - locking/cursor type
46   * <li>AbsolutePosition - Implemented <b>TODO</b> - locking/cursor type
47   * <li>ActiveCommand - Fully implemented
48   * <li>ActiveConnection - Implemented
49   * <li>BOF - Implemented
50   * <li>EOF - Implemented
51   * <li>Bookmark - <b>TODO</b> Not implemented
52   * <li>CacheSize - <b>TODO</b> Not implemented
53   * <li>CursorLocation - <b>TODO</b> Not implemented
54   * <li>CursorType - <b>TODO</b> Not implemented
55   * <li>Filter - <b>TODO</b> Not implemented
56   * <li>Index - <b>TODO</b> Not implemented
57   * <li>LockType - <b>TODO</b> Not implemented
58   * <li>MarshalOptions - <b>TODO</b> Not implemented
59   * <li>MaxRecords - <b>TODO</b> Not implemented
60   * <li>PageCount - Implemented <b>TODO</b> - locking / cursor type
61   * <li>PageSize - <b>TODO</b> Not implemented
62   * <li>RecordCount - Implemented <b>TODO</b> - locking / cursor type
63   * <li>Sort - <B>TODO</b> Not implemented
64   * <li>Source - Implemented
65   * <li>State - Implemented
66   * <li>Status - Implemented
67   * <li>StayInSync - <b>TODO</b> Not implemented
68   * <li>AddNew - Implemented
69   * <li>Cancel - Not implemented, not usable in web-based ADODB
70   * <li>CancelBatch - <b>TODO</b> Not implemented
71   * <li>CancelUpdate - Implemented
72   * <li>Clone - <b>TODO</b> Not implemented
73   * <li>Close - Implemented
74   * <li>CompareBookmarks - <B>TODO</b> Not implemented
75   * <li>Delete - <B>TODO</b> Not implemented
76   * <li>Find - <B>TODO</b> Not implemented
77   * <li>GetRows - <B>TODO</b> Not implemented
78   * <li>GetString - <b>TODO</b> Not implemented
79   * <li>Move - Implemented
80   * <li>MoveFirst - Implemented
81   * <li>MoveLast - Implemented
82   * <li>MoveNext - Implemented
83   * <li>MovePrevious - Implemented
84   * <li>NextRecordSet - <B>TODO</b> Not implemented
85   * <li>Open - Implemented
86   * <li>Requery - Implemented
87   * <li>Resync - <B>TODO</b> Not implemented
88   * <li>Save - <B>TODO</b> Not implemented
89   * <li>Seek - <B>TODO</b> Not implemented
90   * <li>Supports - <B>TODO</b> Not implemented
91   * <li>Update - Implemented, <B>TODO</b> Array-updating not implemented
92   * <li>UpdateBatch - <B>TODO</b> Not implemented
93   * </ul>
94   * ADODB.Field implementation status:
95   * <ul>
96   * <li>ActualSize - <b>TODO</b> Not implemented
97   * <li>Attributes - <b>TODO</b> Not implemented
98   * <li>DefinedSize - Implemented
99   * <li>Name - Implemented
100  * <li>NumericScale - Implemented
101  * <li>OriginalValue - <B>TODO</b> Not implemented
102  * <li>Precision - Implemented
103  * <li>Status - <b>TODO</b> Not implemented
104  * <li>Type - <b>TODO</b> Not implemented
105  * <li>UnderlyingValue - <b>TODO</b> Not implemented
106  * <li>Value - Implemented
107  * <li>AppendChunk - <b>TODO</b> Not implemented
108  * <li>GetChunk - <b>TODO</b> Not implemented
109  * <li>Append - <b>TODO</b> Not implemented
110  * </ul>
111  * @author Terence Haddock
112  */
113 public class RecordSet implements SimpleMap
114 {
115     /** Debugging category */
116     static Category DBG = Category.getInstance(RecordSet.class.getName());
117 
118     /** Active record set, null if no active RS */
119     java.sql.ResultSet rs;
120 
121     /** Active statement, null if no active statement */
122     java.sql.Statement stmt;
123 
124     /** Meta data for the query */
125     ResultSetMetaData rsmd = null;
126 
127     /** Hashtable mapping column names to index. */
128     Hashtable columnNames;
129 
130     /** This variable is marked true when there are no results in the query */
131     boolean    hasNoElements = false;
132 
133     /** This variable is marked true when the query is currently on the insert row */
134     boolean     isOnInsertRow = false;
135 
136     /** This flag is true if we opened the connection through RS.Open */
137     boolean     ownConnection = false;
138 
139     /** ASP - Accessible fields array */
140     public FieldsMap Fields = new FieldsMap();
141 
142     /** ASP - Accessible EOF marker */
143     public boolean EOF;
144 
145     /** ASP - Accessible BOF marker */
146     public boolean BOF = true;
147 
148     /** Where is the cursor, server or client? */
149     public int CursorLocation = 0;
150 
151     /** Type of cursor */
152     public int CursorType = 0;
153 
154     /** Size for paging */
155     public int PageSize = 10;
156 
157     /** Size for caching */
158     public int CacheSize = 10;
159 
160     /** Internal variable used to re-query the SQL */
161     protected String _ActiveSQL = null;
162 
163     /** Internal variable to store the active connection */
164     protected Connection _ActiveConnection = null;
165 
166     /** Internal variable to store the active command */
167     protected Command _ActiveCommand = null;
168 
169     /** Constructor with no parameters. */
170     public RecordSet()
171     {
172         this.rs = null;
173     }
174 
175     /**
176      * ASP-Accessible function
177      * @return the active connection
178      */
179     public String ActiveConnection()
180     {
181         return _ActiveConnection.ConnectionString;
182     }
183 
184     /**
185      * ASP-Accessible function
186      * @return the active command
187      */
188     public Object ActiveCommand()
189     {
190         if (_ActiveCommand == null)
191             return "";
192         return _ActiveCommand;
193     }
194 
195     /**
196      * Internal function to set the active connection.
197      * @param con New connection
198      */
199     protected void setConnection(Connection con)
200     {
201         this._ActiveConnection = con;
202     }
203 
204     /**
205      * Internal function to set the active command.
206      * @param com New command
207      */
208     protected void setCommand(Command com)
209     {
210         this._ActiveCommand = com;
211     }
212 
213     /**
214      * Internal function to set the active statement.
215      * @param stmt Active statement
216      */
217     protected void setStatement(java.sql.Statement stmt)
218     {
219         this.stmt = stmt;
220     }
221 
222     /**
223      * Internal function to set the active result set 
224      * @param rs active result set
225      */
226     protected void setResultSet(java.sql.ResultSet rs) throws AspException
227     {
228         this.rs = rs;
229         BOF = true;
230         try {
231             if (rs == null)
232             {
233                 EOF = true;
234             } else {
235                 rsmd = rs.getMetaData();
236                 getColumnNames();
237                 EOF = !rs.next();
238                 if (!EOF) BOF = false;
239                 if (DBG.isDebugEnabled())
240                     DBG.debug("EOF: " + EOF);
241             }
242             hasNoElements = EOF;
243         } catch (SQLException ex) {
244             throw _ActiveConnection.processException(ex);
245         }
246     }
247 
248     /**
249      * Internal function which obtains the column names from the result set.
250      */
251     private void getColumnNames() throws AspException
252     {
253         try {
254             int i;
255             columnNames = new Hashtable();
256             if (DBG.isDebugEnabled()) DBG.debug("Number of columns: " +
257                 rsmd.getColumnCount());
258             for (i = 1; i <= rsmd.getColumnCount(); i++)
259             {
260                 String name = rsmd.getColumnName(i);
261                 if (DBG.isDebugEnabled())
262                     DBG.debug("Column name: " + name);
263                 columnNames.put(name.toLowerCase(), new Integer(i));
264             }
265         } catch (SQLException ex) {
266             throw _ActiveConnection.processException(ex);
267         }
268     }
269 
270     /**
271      * ASP-Accessible function to move to the next row.
272      * @throws AspException on SQL error
273      */
274     public void MoveNext() throws AspException
275     {
276         try {
277             EOF = !rs.next();
278             if (!EOF) BOF = false;
279         } catch (SQLException ex) {
280             throw _ActiveConnection.processException(ex);
281         }
282     }
283 
284     /**
285      * ASP-Accessible function to move to the first row.
286      * @throws AspException on SQL error
287      */
288     public void MoveFirst() throws AspException
289     {
290         try {
291             rs.first();
292         } catch (SQLException ex) {
293             throw _ActiveConnection.processException(ex);
294         }
295     }
296 
297     /**
298      * ASP-Accessible function to move back one row.
299      * @throws AspException on SQL error
300      */
301     public void MovePrevious() throws AspException
302     {
303         try {
304             rs.previous();
305         } catch (SQLException ex) {
306             throw _ActiveConnection.processException(ex);
307         }
308     }
309 
310     /**
311      * ASP-Accessible function to move to the last row.
312      * @throws AspException on SQL error
313      */
314     public void MoveLast() throws AspException
315     {
316         try {
317             rs.last();
318         } catch (SQLException ex) {
319             throw _ActiveConnection.processException(ex);
320         }
321     }
322 
323     /**
324      * ASP-Accessible function to move by the specified number of rows.
325      * @param numRows number of rows to move by, can be negative to move
326      * backwards.
327      * @throws AspException on SQL error
328      */
329     public void Move(int numRows) throws AspException
330     {
331         try {
332             boolean result = rs.relative(numRows);
333             if (!result)
334             {
335                 if (numRows>0) EOF = true;
336                     else BOF = true;
337             }
338             if (!EOF) BOF = false;
339         } catch (SQLException ex) {
340             throw _ActiveConnection.processException(ex);
341         }
342     }
343 
344     /**
345      * Open a connection, based on a string DSN.
346      * @param sql SQL statement to use
347      * @param DSN DSN to use to connect
348      * @throws AspException on SQL error
349      * @throws AspException on ASP error
350      */
351     public boolean Open(String sql, String DSN)
352         throws AspException
353     {
354         return Open(sql, DSN, 0, 0);
355     }
356 
357     /**
358      * Open a connection, based on a string DSN, with a specified cursor type.
359      * @param sql SQL statement to use
360      * @param DSN DSN to use to connect
361      * @param type Cursor type
362      * @throws AspException on ASP error
363      */
364     public boolean Open(String sql, String DSN, int type)
365         throws AspException
366     {
367         return Open(sql, DSN, type, 0);
368     }
369 
370     /**
371      * Open a connection, based on a string DSN, with a specified cursor type.
372      * and a specified lock type.
373      * @param sql SQL statement to use
374      * @param DSN DSN to use to connect
375      * @param type Cursor type
376      * @param lock Lock type
377      * @throws AspException on ASP error
378      */
379     public boolean Open(String sql, String DSN, int type, int lock)
380         throws AspException
381     {
382         ownConnection = true;
383         Connection con = new Connection();
384         con.Open(DSN);
385         return Open(sql, con, type, lock);
386     }
387 
388     /**
389      * Open a result set, based on the specified connection object.
390      * @param sql SQL statement to use
391      * @param con Connection object
392      * @throws AspException on SQL error
393      */
394     public boolean Open(String sql, Connection con)
395         throws AspException
396     {
397         return Open(sql, con, 0, 0);
398     }
399 
400     /**
401      * Open a result set, based on the specified connection object,
402      * with a specified lock type.
403      * @param sql SQL statement to use
404      * @param con Connection object
405      * @param type cursor type
406      * @throws AspException on SQL error
407      */
408     public boolean Open(String sql, Connection con, int type)
409         throws AspException
410     {
411         return Open(sql, con, type, 0);
412     }
413 
414     /**
415      * Open a result set, based on the specified connection object,
416      * with a specified cursor type, and specified lock semantics.
417      * @param sql SQL statement to use
418      * @param con Connection object
419      * @param type cursor type
420      * @param lock lock type
421      * @throws AspException on SQL error
422      */
423     public boolean Open(String sql, Connection con, int type, int lock)
424         throws AspException
425     {
426         return Open(sql, con, type, lock, 0);
427     }
428 
429     /**
430      * Open a result set, based on the specified connection object,
431      * with a specified cursor type, and specified lock semantics.
432      * @param sql SQL statement to use
433      * @param con Connection object
434      * @param type cursor type
435      * @param lock lock type
436      * @param flag some sort of flag
437      * @throws AspException on SQL error
438      */
439     public boolean Open(String sql, Connection con, int type, int lock, int flag)
440         throws AspException
441     {
442         try {
443             java.sql.Statement stmt = con.cx.createStatement(
444                 java.sql.ResultSet.TYPE_SCROLL_INSENSITIVE,
445                 java.sql.ResultSet.CONCUR_READ_ONLY);
446             _ActiveSQL = sql;
447             boolean result = stmt.execute(sql + ";");
448             setConnection(con);
449             setStatement(stmt);
450             if (result)
451             {
452                 setResultSet(stmt.getResultSet());
453             } else {
454                 setResultSet(null);
455             }
456             return true;
457         } catch (SQLException ex) {
458             throw con.processException(ex);
459         }
460     }
461 
462     /**
463      * Open a result set, based on the specified command object.
464      * @param sql SQL statement to use
465      * @param command Command object
466      * @throws AspException on SQL error
467      */
468     public int Open(Command command) throws AspException
469     {
470         return Open(command, null, 0, 0);
471     }
472 
473     /**
474      * Open a result set, based on the specified command object.
475      * <B>TODO</b> Figure out these parameters.
476      * @param sql SQL statement to use
477      * @param command Command object
478      * @param a Object A
479      * @param b Cursor type
480      * @param c Lock type
481      * @throws AspException on SQL error
482      */
483     public int Open(Command command, Object a, int b, int c)
484         throws AspException
485     {
486         return Open(command, a, b, c, 0);
487     }
488 
489     /**
490      * Open a result set, based on the specified command object.
491      * <B>TODO</b> Figure out these parameters.
492      * @param sql SQL statement to use
493      * @param command Command object
494      * @param a Object A
495      * @param b Cursor type
496      * @param c Lock type
497      * @param d More flags
498      * @throws AspException on SQL error
499      */
500     public int Open(Command command, Object a, int b, int c, int d)
501         throws AspException
502     {
503         try {
504             if (DBG.isDebugEnabled()) {
505                 DBG.debug("Command.CommandText: " +
506                     command.CommandText);
507                 DBG.debug("A = " + a);
508                 DBG.debug("B = " + b);
509                 DBG.debug("C = " + c);
510                 DBG.debug("D = " + d);
511             }
512             Connection con = command.ActiveConnection();
513             setConnection(con);
514             String sql = command.CommandText;
515             _ActiveSQL = sql;
516             java.sql.Statement stmt = con.cx.createStatement(
517                 java.sql.ResultSet.TYPE_SCROLL_INSENSITIVE,
518                 java.sql.ResultSet.CONCUR_READ_ONLY);
519             boolean result = stmt.execute(sql + ";");
520             setCommand(command);
521             setStatement(stmt);
522             if (result)
523             {
524                 setResultSet(stmt.getResultSet());
525             } else {
526                 setResultSet(null);
527             }
528             return stmt.getUpdateCount();
529         } catch (SQLException ex) {
530             throw _ActiveConnection.processException(ex);
531         }
532     }
533 
534     /**
535      * ASP-Accessible function to requery the database.
536      * @throws AspException on SQL error
537      */
538     public void Requery() throws AspException
539     {
540         Open(_ActiveSQL, _ActiveConnection, 0, 0);
541     }
542 
543     /**
544      * ASP-Accessible function to add a new row.
545      * @throws AspException on SQL error
546      */
547     public void AddNew() throws AspException
548     {
549         try {
550             rs.moveToInsertRow();
551             isOnInsertRow = true;
552         } catch (SQLException ex) {
553             throw _ActiveConnection.processException(ex);
554         }
555     }
556 
557     /**
558      * ASP-Accessible function to add a new row with predefined values.
559      * @param fields array of field names
560      * @param values arrow of column values
561      * @throws AspException on SQL error
562      */
563     public void AddNew(Object fields[], Object values[]) throws AspException
564     {
565         if (fields.length != values.length)
566         {
567             throw new AspException("Parameters to AddNew() should be the " +
568                 "same length");
569         }
570         AddNew();
571         for (int i = 0; i < fields.length; i++)
572         {
573             if (DBG.isDebugEnabled())
574             {
575                 DBG.debug("Field[" + i + "] = " + fields[i]);
576                 DBG.debug("Value[" + i + "] = " + values[i]);
577             }
578             Fields.put(fields[i], values[i]);
579         }
580         Update();
581     }
582 
583     /**
584      * ASP-Accessible function to return the cursor position.
585      * @return cursor position
586      * @throws AspException on SQL error
587      */
588     public int AbsolutePosition() throws AspException
589     {
590         try {
591             int pos = rs.getRow();
592             if (pos == 0) return -1;
593             return pos;
594         } catch (SQLException ex) {
595             throw _ActiveConnection.processException(ex);
596         }
597     }
598 
599     /**
600      * ASP-Accessible function to set the cursor position.
601      * @param pos new cursor position
602      * @throws AspException on SQL error
603      */
604     public void AbsolutePosition(int pos) throws AspException
605     {
606         try {
607             rs.absolute(pos);
608             if (hasNoElements)
609             {
610                 EOF = true;
611             } else {
612                 EOF = rs.isAfterLast();
613             }
614         } catch (SQLException ex) {
615             throw _ActiveConnection.processException(ex);
616         }
617     }
618 
619     /**
620      * ASP-Accessible function to return cursor page.
621      * @return cursor page
622      * @throws AspException on SQL error
623      */
624     public int AbsolutePage() throws AspException
625     {
626         int row = AbsolutePosition();
627         if (row <= 0) return -1;
628         return ((row - 1) / PageSize) + 1;
629     }
630 
631     /**
632      * ASP-Accessible function to obtain the number of pages in the query
633      * @return number of pages in the query 
634      * @throws AspException on SQL error
635      */
636     public int PageCount() throws AspException
637     {
638         int count = RecordCount();
639         return ((count - 1) / PageSize) + 1;
640     }
641 
642     /**
643      * ASP-Accessible function to obtain the number of records in the query.
644      * @return number of records
645      * @throws AspException on SQL error
646      */
647     public int RecordCount() throws AspException
648     {
649         try {
650             int pos, last;
651     
652             if (hasNoElements) {
653                 return 0;
654             }
655     
656             pos = rs.getRow();
657             rs.last();
658             last = rs.getRow();
659             if (pos != 0) {
660                 /* If pos = 0, there is no rows in the result set,
661                    so there is no reason to change the position. */
662                 DBG.debug("Setting position to: " + pos);
663                 rs.absolute(pos);
664             }
665             return last;
666         } catch (SQLException ex) {
667             throw _ActiveConnection.processException(ex);
668         }
669     }
670 
671     /**
672      * ASP-Accessible function to close the record set.
673      * @throws AspException on SQL error
674      */
675     public void Close() throws AspException
676     {
677         try {
678             rs.close();
679             stmt.close();
680             if (ownConnection)
681             {
682                 _ActiveConnection.Close();
683             }
684         } catch (SQLException ex) {
685             throw _ActiveConnection.processException(ex);
686         }
687     }
688 
689     /**
690      * ASP-Accessible function to obtain a set of rows in an array.
691      * @return array of data
692      */
693     public ArrayNode GetRows() throws AspException
694     {
695         return GetRows(-1);
696     }
697 
698     /**
699      * ASP-Accessible function to obtain a set of rows in an array.
700      * @param rows Number of rows to obtain
701      * @return array of data
702      */
703     public ArrayNode GetRows(int rows) throws AspException
704     {
705         if (rows == -1)
706             rows = (RecordCount() - AbsolutePosition()) + 1;
707         int fieldsCount = columnNames.size();
708         ArrayNode retArray = new ArrayNode(fieldsCount);
709         ArrayNode fields[] = new ArrayNode[fieldsCount];
710         for (int i = 0; i < fieldsCount; i++)
711         {
712             ArrayNode subArray = new ArrayNode(rows);
713             retArray._setValue(i, subArray);
714             fields[i] = subArray;
715         }
716         int row = 0;
717         while ((!EOF)&&(row < rows))
718         {
719             for (Enumeration e = columnNames.keys();e.hasMoreElements();)
720             {
721                 Object key = e.nextElement();
722                 Integer fieldIndex = (Integer)columnNames.get(key);
723                 Object value = Fields.get(key);
724                 value = Types.dereference(value);
725                 fields[fieldIndex.intValue() - 1]._setValue(row, value);
726             }
727             MoveNext();
728             row++;
729         }
730         return retArray;
731     }
732 
733     /**
734      * ASP-Accessible function to cancel any pending updates.
735      * @throws AspException on SQL error
736      */
737     public void CancelUpdate() throws AspException
738     {
739         try {
740             rs.cancelRowUpdates();
741         } catch (SQLException ex) {
742             throw _ActiveConnection.processException(ex);
743         }
744     }
745 
746     /**
747      * ASP-Accessible function to update all pending changes.
748      * @throws AspException on SQL error
749      */
750     public void Update() throws AspException
751     {
752         try {
753             if (isOnInsertRow)
754             {
755                 rs.insertRow();
756                 rs.moveToCurrentRow();
757                 isOnInsertRow = false;
758             } else {
759                 rs.updateRow();
760             }
761         } catch (SQLException ex) {
762             throw _ActiveConnection.processException(ex);
763         }
764     }
765 
766     /**
767      * SimpleMap function to obtain the value of the record at the specified
768      * index.
769      * @param key key to obtain
770      * @return value of the specified column
771      * @throws AspException on SQL error
772      */
773     public Object get(Object key) throws AspException
774     {
775         return Fields.get(key);
776     }
777 
778     /**
779      * SimpleMap function to set the value of the record at the specified
780      * index.
781      * @param key key to use
782      * @param value Value to set
783      */
784     public void put(Object key, Object value) throws AspException
785     {
786         Fields.put(key, value);
787     }
788 
789     /**
790      * SimpleMap function to obtain the keys in the record, effectivly the
791      * column names.
792      */
793     public Enumeration getKeys() throws AspException
794     {
795         return Fields.getKeys();
796     }
797 
798     /**
799      * Internal function called on page end.
800      */
801     public void OnPageEnd(Object ignoreme)
802     {
803         try {
804             Close();
805         } catch (AspException ex)
806         {
807             DBG.error("Error on ADODB.ResultSet.OnPageEnd", ex);
808         }
809     }
810 
811     /**
812      * This class implements the ADODB.Fields records.
813      */
814     public class Field implements SimpleReference
815     {
816         /**
817          * This class's column index.
818          */
819         int columnIndex;
820 
821         /**
822          * Internal constructor, column index based
823          */
824         protected Field(int columnIndex)
825         {
826             this.columnIndex = columnIndex;
827         }
828 
829         /**
830          * Getter, obtains the value.
831          * @return value of this object
832          * @throws AspException on error
833          */
834         public Object getValue() throws AspException
835         {
836             return Value();
837         }
838 
839         /**
840          * Setting, set the value.
841          * @param newValue new value of this object
842          * @throws AspException on error
843          */
844         public void setValue(Object newValue) throws AspException
845         {
846             Value(newValue);
847         }
848 
849         /**
850          * ASP-Accessible function to obtain the 'actual size' of a column.
851          * This function always return 4, as ActualSize makes no sense
852          * in Java/JDBC.
853          * @return size of column
854          */
855         public int ActualSize() throws SQLException, AspException
856         {
857             String typeName = rsmd.getColumnTypeName(columnIndex);
858             if (typeName.equalsIgnoreCase("varchar") || 
859                 typeName.equalsIgnoreCase("text")) {
860                 String value = (String)Value();
861                 return value.length();
862             }
863 
864             return 4;
865         }
866 
867         /**
868          * ASP-Accessible function to obtain the column's display size.
869          * @return display size of a column.
870          * @throws SQLException on error
871          */
872         public int DefinedSize() throws SQLException
873         {
874             return rsmd.getColumnDisplaySize(columnIndex);
875         }
876 
877         /**
878          * ASP-Accessible function to obtain the column's name.
879          * @return name of a column.
880          * @throws SQLException on error
881          */
882         public String Name() throws SQLException
883         {
884             return rsmd.getColumnName(columnIndex);
885         }
886 
887         /**
888          * ASP-Accessible function to obtain the column's numeric scale.
889          * @return numeric scale of a column.
890          * @throws SQLException on error
891          */
892         public int NumericScale() throws SQLException
893         {
894             return rsmd.getScale(columnIndex);
895         }
896 
897         /**
898          * ASP-Accessible function to obtain the column's precision.
899          * @return precision of a column.
900          * @throws SQLException on error
901          */
902         public int Precision() throws SQLException
903         {
904             return rsmd.getPrecision(columnIndex);
905         }
906 
907         /**
908          * ASP-Accessible function to obtain the value of the column.
909          * @return value of column
910          * @throws SQLException on SQL error
911          */
912         public Object Value() throws AspException
913         {
914             try {
915                 if (DBG.isDebugEnabled()) {
916                     DBG.debug("Index of column: " + columnIndex);
917                     DBG.debug("Type index: " + rsmd.getColumnType(columnIndex));
918                     DBG.debug("Type name: " + rsmd.getColumnTypeName(columnIndex));
919                 }
920 
921                 String typeName = rsmd.getColumnTypeName(columnIndex);
922                 if (typeName.equalsIgnoreCase("uniqueidentifier")) {
923                     InputStream is = rs.getBinaryStream(columnIndex);
924                     if (is == null) {
925                         return Constants.nullNode;
926                     }
927                     byte[] uniqueID = new byte[16];
928                     int nread;
929                     try {
930                         nread = is.read(uniqueID);
931                     } catch (java.io.IOException ex) {
932                         throw new RuntimeException(ex.toString());
933                     }
934                     if (nread == -1) {
935                         return Constants.nullNode;
936                     } else if (nread != 16) {
937                         throw new RuntimeException("Invalid unique identifier (length = " + nread + ")");
938                     }
939                     return new UniqueIdentifierObj(uniqueID);
940                 } else if (typeName.equalsIgnoreCase("timestamp")) {
941                     java.util.Date timestamp = rs.getTimestamp(columnIndex);
942                     if (timestamp == null) return Constants.nullNode;
943                     return new AspDate(timestamp);
944                 } else if (typeName.equalsIgnoreCase("date")) {
945                     java.util.Date date = rs.getDate(columnIndex);
946                     if (date == null) return Constants.nullNode;
947                     return new AspDate(date, false, true);
948                 } else {
949                     Object ret = rs.getObject(columnIndex);
950                     if (ret == null) return Constants.nullNode;
951                     return ret;
952                 }
953             } catch (SQLException ex)
954             {
955                 throw new AspNestedException(ex);
956             }
957         }
958 
959         /**
960          * ASP-Accessible function to set this column's value.
961          * @param value Column's new value
962          * @throws SQLException on SQL error
963          */
964         public void Value(Object value) throws AspException
965         {
966             try {
967                 if (DBG.isDebugEnabled()) {
968                     DBG.debug("Index of column: " + columnIndex);
969                     DBG.debug("Type index: " + rsmd.getColumnType(columnIndex));
970                     DBG.debug("Type name: " + rsmd.getColumnTypeName(columnIndex));
971                 }
972     
973                 String typeName = rsmd.getColumnTypeName(columnIndex);
974                 if (typeName.equalsIgnoreCase("uniqueidentifier")) {
975                     String uidValue = value.toString();
976                     rs.updateString(columnIndex, uidValue);
977                 } else {
978                     if (value == null)
979                     {
980                         rs.updateNull(columnIndex);
981                     } else {
982                         rs.updateObject(columnIndex, value);
983                     }
984                 }
985             } catch (SQLException ex) {
986                 throw new AspNestedException(ex);
987             }
988         }
989     }
990 
991     /**
992      * This class represents a mapping from a field name/index to a 
993      * ADODB.Fields record
994      */
995     public class FieldsMap implements SimpleMap
996     {
997         public Object get(Object key) throws AspException
998         {
999             while (key instanceof SimpleReference)
1000            {
1001                key = ((SimpleReference)key).getValue();
1002            }
1003            int keyIndex;
1004            if (key instanceof Integer)
1005            {
1006                keyIndex = ((Integer)key).intValue() + 1;
1007            } else {
1008                Integer iKey = (Integer)columnNames.get(key.toString().toLowerCase());
1009                if (iKey == null) 
1010                    throw new RuntimeException("Unknown column name: " + key.toString());
1011                keyIndex = iKey.intValue();
1012            }
1013            return new Field(keyIndex);
1014        }
1015    
1016        public void put(Object key, Object value) throws AspException
1017        {
1018            while (key instanceof SimpleReference)
1019            {
1020                key = ((SimpleReference)key).getValue();
1021            }
1022            int keyIndex;
1023            if (key instanceof Integer)
1024            {
1025                keyIndex = ((Integer)key).intValue() + 1;
1026            } else {
1027                Integer iKey = (Integer)columnNames.get(key.toString().toLowerCase());
1028                if (iKey == null) 
1029                    throw new RuntimeException("Unknown column name: " + key.toString());
1030                keyIndex = iKey.intValue();
1031            }
1032            Field field = new Field(keyIndex);
1033            field.setValue(value);
1034        }
1035    
1036        public Enumeration getKeys() throws AspException
1037        {
1038            throw new RuntimeException("Not implemented");
1039        }
1040
1041        /**
1042         * ASP-Accessible function to obtain the number of fields.
1043         */
1044        public int Count()
1045        {
1046            return columnNames.size();
1047        }
1048    }
1049
1050    public static class UniqueIdentifierObj 
1051        implements SimpleReference
1052    {
1053            byte    uniqueID[];
1054
1055        public UniqueIdentifierObj(byte bytes[]) {
1056            this.uniqueID = bytes;
1057        }
1058
1059        public Object getValue() {
1060            return toString();
1061        }
1062
1063        public void setValue(Object obj) throws AspException
1064        {
1065            throw new AspException("Modification read-only value");
1066        }
1067
1068        public String toString()
1069        {
1070            StringBuffer buf = new StringBuffer();
1071            buf.append('{');
1072            for (int i = 0; i < uniqueID.length; i++)
1073            {
1074                int j;
1075                if (i < 4)
1076                {
1077                      j = 3 - i; 
1078                } else if (i < 6) {
1079                      j = (5 - i) + 4;
1080                } else if (i < 8) {
1081                      j = (7 - i) + 6;
1082                } else {
1083                      j = i;
1084                }
1085                int ch = uniqueID[j]; 
1086                if (ch < 0) {
1087                      ch+=256;
1088                }
1089                if (ch<16) {
1090                      buf.append("0");
1091                }
1092                buf.append(Integer.toHexString(ch).toUpperCase());
1093                if (i == 3 || i == 5 || i == 7 || i == 9) {
1094                      buf.append('-');
1095                               }
1096            }
1097                    buf.append('}');
1098                    return buf.toString();
1099        }
1100    }
1101}
1102