KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > ziclix > python > sql > DataHandler


1 /*
2  * Jython Database Specification API 2.0
3  *
4  * $Id: DataHandler.java,v 1.12 2005/05/12 02:38:59 fwierzbicki Exp $
5  *
6  * Copyright (c) 2001 brian zimmer <bzimmer@ziclix.com>
7  *
8  */

9 package com.ziclix.python.sql;
10
11 import org.python.core.Py;
12 import org.python.core.PyFile;
13 import org.python.core.PyLong;
14 import org.python.core.PyObject;
15 import org.python.core.PyList;
16 import org.python.core.PyString;
17
18 import java.io.BufferedInputStream JavaDoc;
19 import java.io.BufferedReader JavaDoc;
20 import java.io.IOException JavaDoc;
21 import java.io.InputStream JavaDoc;
22 import java.io.Reader JavaDoc;
23 import java.io.StringReader JavaDoc;
24 import java.lang.reflect.Constructor JavaDoc;
25 import java.math.BigDecimal JavaDoc;
26 import java.sql.CallableStatement JavaDoc;
27 import java.sql.Date JavaDoc;
28 import java.sql.PreparedStatement JavaDoc;
29 import java.sql.ResultSet JavaDoc;
30 import java.sql.SQLException JavaDoc;
31 import java.sql.Statement JavaDoc;
32 import java.sql.Time JavaDoc;
33 import java.sql.Timestamp JavaDoc;
34 import java.sql.Types JavaDoc;
35
36 /**
37  * The DataHandler is responsible mapping the JDBC data type to
38  * a Jython object. Depending on the version of the JDBC
39  * implementation and the particulars of the driver, the type
40  * mapping can be significantly different.
41  *
42  * This interface can also be used to change the behaviour of
43  * the default mappings provided by the cursor. This might be
44  * useful in handling more complicated data types such as BLOBs,
45  * CLOBs and Arrays.
46  *
47  * @author brian zimmer
48  * @author last revised by $Author: fwierzbicki $
49  * @version $Revision: 1.12 $
50  */

51 public class DataHandler {
52
53     // default size for buffers
54
private static final int INITIAL_SIZE = 1024 * 4;
55
56     private static final String JavaDoc[] SYSTEM_DATAHANDLERS = {
57         "com.ziclix.python.sql.JDBC20DataHandler"
58     };
59
60     /**
61      * Handle most generic Java data types.
62      */

63     public DataHandler() {}
64
65     /**
66      * Some database vendors are case sensitive on calls to DatabaseMetaData,
67      * most notably Oracle. This callback allows a DataHandler to affect the
68      * name.
69      */

70     public String JavaDoc getMetaDataName(PyObject name) {
71         return ((name == Py.None) ? null : name.__str__().toString());
72     }
73
74     /**
75      * A factory method for determing the correct procedure class to use
76      * per the cursor type.
77      * @param cursor an open cursor
78      * @param name the name of the procedure to invoke
79      * @return an instance of a Procedure
80      * @throws SQLException
81      */

82     public Procedure getProcedure(PyCursor cursor, PyObject name) throws SQLException JavaDoc {
83         return new Procedure(cursor, name);
84     }
85
86     /**
87      * Returns the row id of the last executed statement.
88      *
89      * @param stmt the current statement
90      * @return the row id of the last executed statement or None
91      * @throws SQLException thrown if an exception occurs
92      *
93      */

94     public PyObject getRowId(Statement JavaDoc stmt) throws SQLException JavaDoc {
95         return Py.None;
96     }
97
98     /**
99      * A callback prior to each execution of the statement. If the statement is
100      * a PreparedStatement, all the parameters will have been set.
101      */

102     public void preExecute(Statement JavaDoc stmt) throws SQLException JavaDoc {
103         return;
104     }
105
106     /**
107      * A callback after successfully executing the statement.
108      */

109     public void postExecute(Statement JavaDoc stmt) throws SQLException JavaDoc {
110         return;
111     }
112
113     /**
114      * Any .execute() which uses prepared statements will receive a callback for deciding
115      * how to map the PyObject to the appropriate JDBC type.
116      *
117      * @param stmt the current PreparedStatement
118      * @param index the index for which this object is bound
119      * @param object the PyObject in question
120      * @throws SQLException
121      */

122     public void setJDBCObject(PreparedStatement JavaDoc stmt, int index, PyObject object) throws SQLException JavaDoc {
123
124         try {
125             stmt.setObject(index, object.__tojava__(Object JavaDoc.class));
126         } catch (Exception JavaDoc e) {
127             SQLException JavaDoc cause = null, ex = new SQLException JavaDoc("error setting index [" + index + "]");
128
129             if (e instanceof SQLException JavaDoc) {
130                 cause = (SQLException JavaDoc) e;
131             } else {
132                 cause = new SQLException JavaDoc(e.getMessage());
133             }
134
135             ex.setNextException(cause);
136
137             throw ex;
138         }
139     }
140
141     /**
142      * Any .execute() which uses prepared statements will receive a callback for deciding
143      * how to map the PyObject to the appropriate JDBC type. The <i>type</i> is the JDBC
144      * type as obtained from <i>java.sql.Types</i>.
145      *
146      * @param stmt the current PreparedStatement
147      * @param index the index for which this object is bound
148      * @param object the PyObject in question
149      * @param type the <i>java.sql.Types</i> for which this PyObject should be bound
150      * @throws SQLException
151      */

152     public void setJDBCObject(PreparedStatement JavaDoc stmt, int index, PyObject object, int type) throws SQLException JavaDoc {
153
154         try {
155             if (checkNull(stmt, index, object, type)) {
156                 return;
157             }
158
159             switch (type) {
160
161                 case Types.DATE:
162                     Date JavaDoc date = (Date JavaDoc) object.__tojava__(Date JavaDoc.class);
163
164                     stmt.setDate(index, date);
165                     break;
166
167                 case Types.TIME:
168                     Time JavaDoc time = (Time JavaDoc) object.__tojava__(Time JavaDoc.class);
169
170                     stmt.setTime(index, time);
171                     break;
172
173                 case Types.TIMESTAMP:
174                     Timestamp JavaDoc timestamp = (Timestamp JavaDoc) object.__tojava__(Timestamp JavaDoc.class);
175
176                     stmt.setTimestamp(index, timestamp);
177                     break;
178
179                 case Types.LONGVARCHAR:
180                     if (object instanceof PyFile) {
181                         object = new PyString(((PyFile) object).read());
182                     }
183
184                     String JavaDoc varchar = (String JavaDoc) object.__tojava__(String JavaDoc.class);
185                     Reader JavaDoc reader = new BufferedReader JavaDoc(new StringReader JavaDoc(varchar));
186
187                     stmt.setCharacterStream(index, reader, varchar.length());
188                     break;
189
190                 case Types.BIT:
191                     stmt.setBoolean(index, object.__nonzero__());
192                     break;
193
194                 default :
195                     if (object instanceof PyFile) {
196                         object = new PyString(((PyFile) object).read());
197                     }
198
199                     stmt.setObject(index, object.__tojava__(Object JavaDoc.class), type);
200                     break;
201             }
202         } catch (Exception JavaDoc e) {
203             SQLException JavaDoc cause = null, ex = new SQLException JavaDoc("error setting index [" + index + "], type [" + type + "]");
204
205             if (e instanceof SQLException JavaDoc) {
206                 cause = (SQLException JavaDoc) e;
207             } else {
208                 cause = new SQLException JavaDoc(e.getMessage());
209             }
210
211             ex.setNextException(cause);
212
213             throw ex;
214         }
215     }
216
217     /**
218      * Given a ResultSet, column and type, return the appropriate
219      * Jython object.
220      *
221      * <p>Note: DO NOT iterate the ResultSet.
222      *
223      * @param set the current ResultSet set to the current row
224      * @param col the column number (adjusted properly for JDBC)
225      * @param type the column type
226      * @throws SQLException if the type is unmappable
227      */

228     public PyObject getPyObject(ResultSet JavaDoc set, int col, int type) throws SQLException JavaDoc {
229
230         PyObject obj = Py.None;
231
232         switch (type) {
233
234             case Types.CHAR:
235             case Types.VARCHAR:
236                 String JavaDoc string = set.getString(col);
237
238                 obj = (string == null) ? Py.None : Py.newString(string);
239                 break;
240
241             case Types.LONGVARCHAR:
242                 InputStream JavaDoc longvarchar = set.getAsciiStream(col);
243
244                 if (longvarchar == null) {
245                     obj = Py.None;
246                 } else {
247                     try {
248                         longvarchar = new BufferedInputStream JavaDoc(longvarchar);
249
250                         byte[] bytes = DataHandler.read(longvarchar);
251
252                         if (bytes != null) {
253                             obj = Py.newString(new String JavaDoc(bytes));
254                         }
255                     } finally {
256                         try {
257                             longvarchar.close();
258                         } catch (Throwable JavaDoc t) {}
259                     }
260                 }
261                 break;
262
263             case Types.NUMERIC:
264             case Types.DECIMAL:
265                 BigDecimal JavaDoc bd = null;
266
267                 try {
268                     bd = set.getBigDecimal(col, set.getMetaData().getPrecision(col));
269                 } catch (Throwable JavaDoc t) {
270                     bd = set.getBigDecimal(col, 10);
271                 }
272
273                 obj = (bd == null) ? Py.None : Py.newFloat(bd.doubleValue());
274                 break;
275
276             case Types.BIT:
277                 obj = set.getBoolean(col) ? Py.One : Py.Zero;
278                 break;
279
280             case Types.INTEGER:
281             case Types.TINYINT:
282             case Types.SMALLINT:
283                 obj = Py.newInteger(set.getInt(col));
284                 break;
285
286             case Types.BIGINT:
287                 obj = new PyLong(set.getLong(col));
288                 break;
289
290             case Types.FLOAT:
291             case Types.REAL:
292             case Types.DOUBLE:
293                 obj = Py.newFloat(set.getFloat(col));
294                 break;
295
296             case Types.TIME:
297                 obj = Py.java2py(set.getTime(col));
298                 break;
299
300             case Types.TIMESTAMP:
301                 obj = Py.java2py(set.getTimestamp(col));
302                 break;
303
304             case Types.DATE:
305                 obj = Py.java2py(set.getDate(col));
306                 break;
307
308             case Types.NULL:
309                 obj = Py.None;
310                 break;
311
312             case Types.OTHER:
313                 obj = Py.java2py(set.getObject(col));
314                 break;
315
316             case Types.BINARY:
317             case Types.VARBINARY:
318             case Types.LONGVARBINARY:
319                 obj = Py.java2py(set.getBytes(col));
320                 break;
321
322             default :
323                 Integer JavaDoc[] vals = {new Integer JavaDoc(col), new Integer JavaDoc(type)};
324                 String JavaDoc msg = zxJDBC.getString("errorGettingIndex", vals);
325
326                 throw new SQLException JavaDoc(msg);
327         }
328
329         return (set.wasNull() || (obj == null)) ? Py.None : obj;
330     }
331
332     /**
333      * Given a CallableStatement, column and type, return the appropriate
334      * Jython object.
335      *
336      * @param stmt the CallableStatement
337      * @param col the column number (adjusted properly for JDBC)
338      * @param type the column type
339      * @throws SQLException if the type is unmappable
340      */

341     public PyObject getPyObject(CallableStatement JavaDoc stmt, int col, int type) throws SQLException JavaDoc {
342
343         PyObject obj = Py.None;
344
345         switch (type) {
346
347             case Types.CHAR:
348             case Types.VARCHAR:
349             case Types.LONGVARCHAR:
350                 String JavaDoc string = stmt.getString(col);
351
352                 obj = (string == null) ? Py.None : Py.newString(string);
353                 break;
354
355             case Types.NUMERIC:
356             case Types.DECIMAL:
357                 BigDecimal JavaDoc bd = stmt.getBigDecimal(col, 10);
358
359                 obj = (bd == null) ? Py.None : Py.newFloat(bd.doubleValue());
360                 break;
361
362             case Types.BIT:
363                 obj = stmt.getBoolean(col) ? Py.One : Py.Zero;
364                 break;
365
366             case Types.INTEGER:
367             case Types.TINYINT:
368             case Types.SMALLINT:
369                 obj = Py.newInteger(stmt.getInt(col));
370                 break;
371
372             case Types.BIGINT:
373                 obj = new PyLong(stmt.getLong(col));
374                 break;
375
376             case Types.FLOAT:
377             case Types.REAL:
378             case Types.DOUBLE:
379                 obj = Py.newFloat(stmt.getFloat(col));
380                 break;
381
382             case Types.TIME:
383                 obj = Py.java2py(stmt.getTime(col));
384                 break;
385
386             case Types.TIMESTAMP:
387                 obj = Py.java2py(stmt.getTimestamp(col));
388                 break;
389
390             case Types.DATE:
391                 obj = Py.java2py(stmt.getDate(col));
392                 break;
393
394             case Types.NULL:
395                 obj = Py.None;
396                 break;
397
398             case Types.OTHER:
399                 obj = Py.java2py(stmt.getObject(col));
400                 break;
401
402             case Types.BINARY:
403             case Types.VARBINARY:
404             case Types.LONGVARBINARY:
405                 obj = Py.java2py(stmt.getBytes(col));
406                 break;
407
408             default :
409                 Integer JavaDoc[] vals = {new Integer JavaDoc(col), new Integer JavaDoc(type)};
410                 String JavaDoc msg = zxJDBC.getString("errorGettingIndex", vals);
411
412                 throw new SQLException JavaDoc(msg);
413         }
414
415         return (stmt.wasNull() || (obj == null)) ? Py.None : obj;
416     }
417
418     /**
419      * Called when a stored procedure or function is executed and OUT parameters
420      * need to be registered with the statement.
421      *
422      * @param statement
423      * @param index the JDBC offset column number
424      * @param colType the column as from DatabaseMetaData (eg, procedureColumnOut)
425      * @param dataType the JDBC datatype from Types
426      * @param dataTypeName the JDBC datatype name
427      *
428      * @throws SQLException
429      *
430      */

431     public void registerOut(CallableStatement JavaDoc statement, int index, int colType, int dataType, String JavaDoc dataTypeName) throws SQLException JavaDoc {
432
433         try {
434             statement.registerOutParameter(index, dataType);
435         } catch (Throwable JavaDoc t) {
436             SQLException JavaDoc cause = null;
437             SQLException JavaDoc ex = new SQLException JavaDoc("error setting index ["
438               + index + "], coltype [" + colType + "], datatype [" + dataType
439               + "], datatypename [" + dataTypeName + "]");
440
441             if (t instanceof SQLException JavaDoc) {
442                 cause = (SQLException JavaDoc)t;
443             } else {
444                 cause = new SQLException JavaDoc(t.getMessage());
445             }
446             ex.setNextException(cause);
447             throw ex;
448         }
449     }
450
451     /**
452      * Handles checking if the object is null or None and setting it on the statement.
453      *
454      * @return true if the object is null and was set on the statement, false otherwise
455      */

456     public static final boolean checkNull(PreparedStatement JavaDoc stmt, int index, PyObject object, int type) throws SQLException JavaDoc {
457
458         if ((object == null) || (Py.None == object)) {
459             stmt.setNull(index, type);
460             return true;
461         }
462         return false;
463     }
464
465     /**
466      * Since the driver needs to the know the length of all streams,
467      * read it into a byte[] array.
468      *
469      * @return the stream as a byte[]
470      */

471     public static final byte[] read(InputStream JavaDoc stream) {
472
473         int b = -1, read = 0;
474         byte[] results = new byte[INITIAL_SIZE];
475
476         try {
477             while ((b = stream.read()) != -1) {
478                 if (results.length < (read + 1)) {
479                     byte[] tmp = results;
480                     results = new byte[results.length * 2];
481                     System.arraycopy(tmp, 0, results, 0, tmp.length);
482                 }
483                 results[read++] = (byte) b;
484             }
485         } catch (IOException JavaDoc e) {
486             throw zxJDBC.makeException(e);
487         }
488
489         byte[] tmp = results;
490         results = new byte[read];
491         System.arraycopy(tmp, 0, results, 0, read);
492         return results;
493     }
494
495     /**
496      * Read all the chars from the Reader into the String.
497      *
498      * @return the contents of the Reader in a String
499      */

500     public static final String JavaDoc read(Reader JavaDoc reader) {
501
502         int c = 0;
503         StringBuffer JavaDoc buffer = new StringBuffer JavaDoc(INITIAL_SIZE);
504
505         try {
506             while ((c = reader.read()) != -1) {
507                 buffer.append((char) c);
508             }
509         } catch (IOException JavaDoc e) {
510             throw zxJDBC.makeException(e);
511         }
512
513         return buffer.toString();
514     }
515
516     /**
517      * Build the DataHandler chain depending on the VM. This guarentees a DataHandler
518      * but might additionally chain a JDBC2.0 or JDBC3.0 implementation.
519      * @return a DataHandler configured for the VM version
520      */

521     public static final DataHandler getSystemDataHandler() {
522         DataHandler dh = new DataHandler();
523
524         for (int i = 0; i < SYSTEM_DATAHANDLERS.length; i++) {
525             try {
526                 Class JavaDoc c = Class.forName(SYSTEM_DATAHANDLERS[i]);
527                 Constructor JavaDoc cons = c.getConstructor(new Class JavaDoc[]{DataHandler.class});
528                 dh = (DataHandler) cons.newInstance(new Object JavaDoc[]{dh});
529             } catch (Throwable JavaDoc t) {}
530         }
531
532         return dh;
533     }
534
535     /**
536      * Returns a list of datahandlers chained together through the use of delegation.
537      *
538      * @return a list of datahandlers
539      */

540     public PyObject __chain__() {
541         return new PyList(new PyObject[] { Py.java2py(this) });
542     }
543
544     /**
545      * Returns the classname of this datahandler.
546      */

547     public String JavaDoc toString() {
548         return getClass().getName();
549     }
550 }
551
552
Popular Tags