001: /*
002: * Jython Database Specification API 2.0
003: *
004: * $Id: zxJDBC.java 3125 2007-02-27 22:40:29Z cgroves $
005: *
006: * Copyright (c) 2001 brian zimmer <bzimmer@ziclix.com>
007: *
008: */
009: package com.ziclix.python.sql;
010:
011: import org.python.core.ClassDictInit;
012: import org.python.core.Options;
013: import org.python.core.Py;
014: import org.python.core.PyBuiltinFunctionSet;
015: import org.python.core.PyClass;
016: import org.python.core.PyDictionary;
017: import org.python.core.PyException;
018: import org.python.core.PyInteger;
019: import org.python.core.PyObject;
020: import org.python.core.PyString;
021: import org.python.core.PyStringMap;
022:
023: import java.lang.reflect.Field;
024: import java.sql.SQLException;
025: import java.text.MessageFormat;
026: import java.util.ArrayList;
027: import java.util.List;
028: import java.util.MissingResourceException;
029: import java.util.Properties;
030: import java.util.ResourceBundle;
031:
032: /**
033: * Creates database connections.
034: * <p/>
035: * <pre>
036: * from com.ziclix.python.sql import zxJDBC
037: * db = zxJDBC.connect("jdbc:mysql://localhost:3306/MySql", None, None, "org.gjt.mm.mysql.Driver")
038: * </pre>
039: *
040: * @author brian zimmer
041: * @author last revised by $Author: cgroves $
042: * @version $Revision: 3125 $
043: */
044: public class zxJDBC extends PyObject implements ClassDictInit {
045:
046: /**
047: * Field Error
048: */
049: public static PyObject Error = Py.None;
050:
051: /**
052: * Field Warning
053: */
054: public static PyObject Warning = Py.None;
055:
056: /**
057: * Field InterfaceError
058: */
059: public static PyObject InterfaceError = Py.None;
060:
061: /**
062: * Field DatabaseError
063: */
064: public static PyObject DatabaseError = Py.None;
065:
066: /**
067: * Field InternalError
068: */
069: public static PyObject InternalError = Py.None;
070:
071: /**
072: * Field OperationalError
073: */
074: public static PyObject OperationalError = Py.None;
075:
076: /**
077: * Field ProgrammingError
078: */
079: public static PyObject ProgrammingError = Py.None;
080:
081: /**
082: * Field IntegrityError
083: */
084: public static PyObject IntegrityError = Py.None;
085:
086: /**
087: * Field DataError
088: */
089: public static PyObject DataError = Py.None;
090:
091: /**
092: * Field NotSupportedError
093: */
094: public static PyObject NotSupportedError = Py.None;
095:
096: /**
097: * The ResourceBundle with error messages and doc strings
098: */
099: private static ResourceBundle resourceBundle = null;
100:
101: /**
102: * Instance used to create date-like objects as per the API
103: */
104: public static DateFactory datefactory = new JavaDateFactory();
105:
106: static {
107: try {
108: resourceBundle = ResourceBundle
109: .getBundle("com.ziclix.python.sql.resource.zxJDBCMessages");
110: } catch (MissingResourceException e) {
111: throw new RuntimeException("missing zxjdbc resource bundle");
112: }
113: }
114:
115: /**
116: * Initializes the module.
117: *
118: * @param dict
119: */
120: public static void classDictInit(PyObject dict) {
121:
122: dict.__setitem__("apilevel", new PyString("2.0"));
123: dict.__setitem__("threadsafety", new PyInteger(1));
124: dict.__setitem__("paramstyle", new PyString("qmark"));
125: dict.__setitem__("__version__", Py.newString(
126: "$Revision: 3125 $").__getslice__(Py.newInteger(11),
127: Py.newInteger(-2), null));
128: dict.__setitem__("Date", new zxJDBCFunc("Date", 1, 3, 3,
129: "construct a Date from year, month, day"));
130: dict.__setitem__("Time", new zxJDBCFunc("Time", 2, 3, 3,
131: "construct a Date from hour, minute, second"));
132: dict
133: .__setitem__(
134: "Timestamp",
135: new zxJDBCFunc("Timestamp", 3, 6, 6,
136: "construct a Timestamp from year, month, day, hour, minute, second"));
137: dict.__setitem__("DateFromTicks", new zxJDBCFunc(
138: "DateFromTicks", 4, 1, 1,
139: "construct a Date from seconds since the epoch"));
140: dict.__setitem__("TimeFromTicks", new zxJDBCFunc(
141: "TimeFromTicks", 5, 1, 1,
142: "construct a Time from seconds since the epoch"));
143: dict.__setitem__("TimestampFromTicks", new zxJDBCFunc(
144: "TimestampFromTicks", 6, 1, 1,
145: "construct a Timestamp from seconds since the epoch"));
146: dict.__setitem__("Binary", new zxJDBCFunc("Binary", 7, 1, 1,
147: "construct an object capable of holding binary data"));
148: zxJDBC._addSqlTypes(dict);
149: zxJDBC._addConnectors(dict);
150: zxJDBC._buildExceptions(dict);
151:
152: // hide from python
153: dict.__setitem__("initModule", null);
154: dict.__setitem__("toString", null);
155: dict.__setitem__("getPyClass", null);
156: dict.__setitem__("classDictInit", null);
157: dict.__setitem__("_addSqlTypes", null);
158: dict.__setitem__("_addConnectors", null);
159: dict.__setitem__("_buildExceptions", null);
160: dict.__setitem__("_empty__init__", null);
161: dict.__setitem__("buildClass", null);
162: dict.__setitem__("createExceptionMessage", null);
163: dict.__setitem__("resourceBundle", null);
164: dict.__setitem__("getString", null);
165: dict.__setitem__("makeException", null);
166: }
167:
168: /**
169: * Field __class__
170: */
171: public static PyClass __class__;
172:
173: /**
174: * Method getPyClass
175: *
176: * @return PyClass
177: */
178: protected PyClass getPyClass() {
179: return __class__;
180: }
181:
182: /**
183: * Add the types from java.sql.Types
184: *
185: * @param dict
186: * @throws PyException
187: */
188: protected static void _addSqlTypes(PyObject dict)
189: throws PyException {
190:
191: PyDictionary sqltype = new PyDictionary();
192:
193: dict.__setitem__("sqltype", sqltype);
194:
195: try {
196: Class c = Class.forName("java.sql.Types");
197: Field[] fields = c.getFields();
198:
199: for (int i = 0; i < fields.length; i++) {
200: Field f = fields[i];
201: PyString name = Py.newString(f.getName());
202: PyObject value = new DBApiType(f.getInt(c));
203:
204: dict.__setitem__(name, value);
205: sqltype.__setitem__(value, name);
206: }
207:
208: c = Class.forName("java.sql.ResultSet");
209: fields = c.getFields();
210:
211: for (int i = 0; i < fields.length; i++) {
212: Field f = fields[i];
213: PyString name = Py.newString(f.getName());
214: PyObject value = Py.newInteger(f.getInt(c));
215:
216: dict.__setitem__(name, value);
217: }
218: } catch (Throwable t) {
219: throw makeException(t);
220: }
221:
222: dict.__setitem__("ROWID", dict.__getitem__(Py
223: .newString("OTHER")));
224: dict.__setitem__("NUMBER", dict.__getitem__(Py
225: .newString("NUMERIC")));
226: dict.__setitem__("STRING", dict.__getitem__(Py
227: .newString("VARCHAR")));
228: dict.__setitem__("DATETIME", dict.__getitem__(Py
229: .newString("TIMESTAMP")));
230:
231: return;
232: }
233:
234: /**
235: * Add all the possible connectors
236: *
237: * @param dict
238: * @throws PyException
239: */
240: protected static void _addConnectors(PyObject dict)
241: throws PyException {
242:
243: PyObject connector = Py.None;
244: Properties props = new Properties();
245:
246: props.put("connect", "com.ziclix.python.sql.connect.Connect");
247: props.put("lookup", "com.ziclix.python.sql.connect.Lookup");
248: props.put("connectx", "com.ziclix.python.sql.connect.Connectx");
249:
250: java.util.Enumeration names = props.propertyNames();
251:
252: while (names.hasMoreElements()) {
253: String name = ((String) names.nextElement()).trim();
254: String className = props.getProperty(name).trim();
255:
256: try {
257: connector = (PyObject) Class.forName(className)
258: .newInstance();
259: dict.__setitem__(name, connector);
260: Py.writeComment("zxJDBC", "loaded connector ["
261: + className + "] as [" + name + "]");
262: } catch (Throwable t) {
263: Py.writeComment("zxJDBC", "failed to load connector ["
264: + name + "] using class [" + className + "]");
265: }
266: }
267:
268: return;
269: }
270:
271: /**
272: * Create the exception classes and get their descriptions from the resource bundle.
273: *
274: * @param dict
275: */
276: protected static void _buildExceptions(PyObject dict) {
277:
278: Error = buildClass("Error", Py.StandardError, "_empty__init__");
279: Warning = buildClass("Warning", Py.StandardError,
280: "_empty__init__");
281: InterfaceError = buildClass("InterfaceError", Error,
282: "_empty__init__");
283: DatabaseError = buildClass("DatabaseError", Error,
284: "_empty__init__");
285: InternalError = buildClass("InternalError", DatabaseError,
286: "_empty__init__");
287: OperationalError = buildClass("OperationalError",
288: DatabaseError, "_empty__init__");
289: ProgrammingError = buildClass("ProgrammingError",
290: DatabaseError, "_empty__init__");
291: IntegrityError = buildClass("IntegrityError", DatabaseError,
292: "_empty__init__");
293: DataError = buildClass("DataError", DatabaseError,
294: "_empty__init__");
295: NotSupportedError = buildClass("NotSupportedError",
296: DatabaseError, "_empty__init__");
297: }
298:
299: public static PyObject _empty__init__(PyObject[] arg, String[] kws) {
300: PyObject dict = new PyStringMap();
301: dict.__setitem__("__module__", new PyString("zxJDBC"));
302: return dict;
303: }
304:
305: /**
306: * Return the string associated with the key for the default resource bundle. It
307: * first checks for 'key.N' where N starts at 0 and increments by one. If any indexed
308: * key is found, the results of all the indexed values are concatenated with the line
309: * separator. If no indexed key is found, it defaults to checking the bundle by the
310: * key value alone.
311: *
312: * @param key
313: * @return String
314: */
315: public static String getString(String key) {
316: int i = 0;
317: List lines = null;
318: String resource = null;
319: while (true) {
320: try {
321: resource = resourceBundle.getString(key + "." + (i++));
322: if (lines == null) {
323: lines = new ArrayList();
324: }
325: lines.add(resource);
326: } catch (MissingResourceException e) {
327: break;
328: }
329: }
330: if ((lines == null) || (lines.size() == 0)) {
331: try {
332: resource = resourceBundle.getString(key);
333: } catch (MissingResourceException e) {
334: return key;
335: }
336: } else {
337: String sep = System.getProperty("line.separator");
338: StringBuffer sb = new StringBuffer();
339: for (i = 0; i < lines.size() - 1; i++) {
340: sb.append(lines.get(i)).append(sep);
341: }
342: sb.append(lines.get(lines.size() - 1));
343: resource = sb.toString();
344: }
345: return resource;
346: }
347:
348: /**
349: * Return a formatted string. The key is used to get the format and the values
350: * are passed, along with the format, to a MessageFormat who formats it appropriately.
351: *
352: * @param key
353: * @param values
354: * @return String
355: */
356: public static String getString(String key, Object[] values) {
357: String format = getString(key);
358: return MessageFormat.format(format, values);
359: }
360:
361: /**
362: * Return a newly instantiated PyException of the type Error.
363: *
364: * @param msg
365: * @return PyException
366: */
367: public static PyException makeException(String msg) {
368: return makeException(Error, msg);
369: }
370:
371: /**
372: * Return a newly instantiated PyException of the given type.
373: *
374: * @param type
375: * @param msg
376: * @return PyException
377: */
378: public static PyException makeException(PyObject type, String msg) {
379: return Py.makeException(type, Py.newString((msg == null) ? ""
380: : msg));
381: }
382:
383: /**
384: * Return a newly instantiated PyException of the type Error.
385: *
386: * @param throwable
387: * @return PyException
388: */
389: public static PyException makeException(Throwable throwable) {
390: return makeException(Error, throwable);
391: }
392:
393: /**
394: * Return a newly instantiated PyException of the given type.
395: *
396: * @param type
397: * @param t
398: * @return PyException
399: */
400: public static PyException makeException(PyObject type, Throwable t) {
401:
402: if (Options.showJavaExceptions) {
403: java.io.CharArrayWriter buf = new java.io.CharArrayWriter();
404: java.io.PrintWriter writer = new java.io.PrintWriter(buf);
405: writer.println("Java Traceback:");
406: if (t instanceof PyException) {
407: ((PyException) t).super __printStackTrace(writer);
408: } else {
409: t.printStackTrace(writer);
410: }
411: Py.stderr.print(buf.toString());
412: }
413:
414: if (t instanceof PyException) {
415: return (PyException) t;
416: } else if (t instanceof SQLException) {
417: SQLException sqlException = (SQLException) t;
418: StringBuffer buffer = new StringBuffer();
419: do {
420: buffer.append(sqlException.getMessage());
421: buffer.append(" [SQLCode: "
422: + sqlException.getErrorCode() + "]");
423: if (sqlException.getSQLState() != null) {
424: buffer.append(", [SQLState: "
425: + sqlException.getSQLState() + "]");
426: }
427: sqlException = sqlException.getNextException();
428: if (sqlException != null) {
429: buffer.append(System.getProperty("line.separator"));
430: }
431: } while (sqlException != null);
432:
433: return makeException(type, buffer.toString());
434: } else {
435: return makeException(type, t.getMessage());
436: }
437: }
438:
439: /**
440: * Method buildClass
441: *
442: * @param classname
443: * @param superclass
444: * @param classCodeName
445: * @return PyObject
446: */
447: protected static PyObject buildClass(String classname,
448: PyObject super class, String classCodeName) {
449: PyObject[] parents = (super class == null) ? Py.EmptyObjects
450: : new PyObject[] { super class };
451: PyString doc = Py.newString(getString(classname));
452: PyObject cls = Py.makeClass(classname, parents, Py.newJavaCode(
453: zxJDBC.class, classCodeName), doc);
454: return cls;
455: }
456: }
457:
458: class zxJDBCFunc extends PyBuiltinFunctionSet {
459:
460: zxJDBCFunc(String name, int index, int minargs, int maxargs,
461: String doc) {
462: super (name, index, minargs, maxargs, doc);
463: }
464:
465: public PyObject __call__(PyObject arg) {
466: long ticks;
467: switch (index) {
468: case 4:
469: ticks = ((Number) arg.__tojava__(Number.class)).longValue();
470: return zxJDBC.datefactory.DateFromTicks(ticks);
471: case 5:
472: ticks = ((Number) arg.__tojava__(Number.class)).longValue();
473: return zxJDBC.datefactory.TimeFromTicks(ticks);
474: case 6:
475: ticks = ((Number) arg.__tojava__(Number.class)).longValue();
476: return zxJDBC.datefactory.TimestampFromTicks(ticks);
477: case 7:
478: return arg;
479: default:
480: throw info.unexpectedCall(1, false);
481: }
482: }
483:
484: public PyObject __call__(PyObject arga, PyObject argb, PyObject argc) {
485: switch (index) {
486: case 1:
487: int year = ((Number) arga.__tojava__(Number.class))
488: .intValue();
489: int month = ((Number) argb.__tojava__(Number.class))
490: .intValue();
491: int day = ((Number) argc.__tojava__(Number.class))
492: .intValue();
493: return zxJDBC.datefactory.Date(year, month, day);
494: case 2:
495: int hour = ((Number) arga.__tojava__(Number.class))
496: .intValue();
497: int minute = ((Number) argb.__tojava__(Number.class))
498: .intValue();
499: int second = ((Number) argc.__tojava__(Number.class))
500: .intValue();
501: return zxJDBC.datefactory.Time(hour, minute, second);
502: default:
503: throw info.unexpectedCall(3, false);
504: }
505: }
506:
507: public PyObject fancyCall(PyObject[] args) {
508: switch (index) {
509: case 3:
510: int year = ((Number) args[0].__tojava__(Number.class))
511: .intValue();
512: int month = ((Number) args[1].__tojava__(Number.class))
513: .intValue();
514: int day = ((Number) args[2].__tojava__(Number.class))
515: .intValue();
516: int hour = ((Number) args[3].__tojava__(Number.class))
517: .intValue();
518: int minute = ((Number) args[4].__tojava__(Number.class))
519: .intValue();
520: int second = ((Number) args[5].__tojava__(Number.class))
521: .intValue();
522: return zxJDBC.datefactory.Timestamp(year, month, day, hour,
523: minute, second);
524: default:
525: throw info.unexpectedCall(args.length, false);
526: }
527: }
528: }
|