001: /*
002: * Created on Mar 3, 2004
003: */
004: package net.sourceforge.orbroker;
005:
006: import java.io.BufferedInputStream;
007: import java.io.IOException;
008: import java.io.InputStream;
009: import java.io.InputStreamReader;
010: import java.io.Reader;
011: import java.math.BigDecimal;
012: import java.net.URL;
013: import java.sql.Array;
014: import java.sql.Blob;
015: import java.sql.Clob;
016: import java.sql.Connection;
017: import java.sql.Date;
018: import java.sql.Ref;
019: import java.sql.ResultSet;
020: import java.sql.ResultSetMetaData;
021: import java.sql.SQLException;
022: import java.sql.Statement;
023: import java.sql.Time;
024: import java.sql.Timestamp;
025: import java.sql.Types;
026: import java.util.ArrayList;
027: import java.util.Map;
028:
029: /**
030: * Representation of a row. This wraps a result set with most ResultSet methods
031: * available. The next() method is not available to prevent unauthorized
032: * scrolling.
033: *
034: * @author Nils Kilden-Pedersen
035: */
036: final class ResultRow {
037:
038: /**
039: * @param metaData
040: * @return array of readable columns
041: * @throws SQLException
042: */
043: private static int[] buildReadableColumns(ResultSetMetaData metaData) {
044: int[] columns;
045: try {
046: columns = new int[metaData.getColumnCount()];
047: } catch (SQLException e) {
048: throw new BrokerException(e);
049: }
050: for (int i = 0; i < columns.length; i++) {
051: columns[i] = i + 1;
052: }
053: return columns;
054: }
055:
056: private static void checkLength(long length, int columnType,
057: String desiredType) {
058: if (length > Integer.MAX_VALUE) {
059: String msg = JdbcType.getInstance(columnType).getName()
060: + " length of " + length
061: + " exceeds maximum size for conversion to "
062: + desiredType;
063: throw new BrokerException(msg);
064: }
065: }
066:
067: private ArrayList aliasStack = null;
068:
069: private final Map cachedResultObjects;
070:
071: private final Connection connection;
072:
073: private final ConnectionContext context;
074:
075: private Map currentAliases = null;
076:
077: private final ResultSetMetaData metaData;
078:
079: private final int[] readableColumns;
080:
081: private final ResultSet resultSet;
082:
083: private final Statement statement;
084:
085: /**
086: * Constructor.
087: *
088: * @param resultSet
089: * @param context
090: * @param connection
091: */
092: ResultRow(ResultSet resultSet, ConnectionContext context,
093: Connection connection) {
094: this .context = context;
095: this .resultSet = resultSet;
096:
097: this .cachedResultObjects = Broker.getCachedObjects(connection);
098: this .connection = connection;
099: try {
100: this .metaData = resultSet.getMetaData();
101: } catch (SQLException e) {
102: throw new BrokerException(e);
103: }
104: if (this .metaData == null) {
105: throw new BrokerException(
106: "JDBC driver does not return metadata.");
107: }
108: if (resultSet.getClass() == ResultParameters.class) {
109: ResultParameters resultParms = (ResultParameters) resultSet;
110: this .readableColumns = resultParms.getOutputParameters();
111: } else {
112: this .readableColumns = buildReadableColumns(this .metaData);
113: }
114:
115: Statement tempStatement;
116: try {
117: tempStatement = resultSet.getStatement();
118: } catch (SQLException e) {
119: tempStatement = null;
120: }
121: this .statement = tempStatement;
122:
123: }
124:
125: /**
126: * Get column JDBC type. Guaranteed not to return null.
127: *
128: * @param columnIndex
129: * @param requiredType
130: * @return JDBC type
131: */
132: private JdbcType getColumnJdbcType(int columnIndex,
133: Class requiredType) {
134: JdbcType jdbcType = JdbcType
135: .getInstance(getColumnType(columnIndex));
136: if (jdbcType == null) {
137: jdbcType = JdbcType.getInstance(requiredType);
138: }
139: if (jdbcType != null) {
140: return jdbcType;
141: }
142: String msg = "Unknown JDBC type: "
143: + getColumnTypeName(columnIndex)
144: + ". Cannot find suitable type for "
145: + requiredType.toString();
146: throw new ConfigurationException(msg);
147: }
148:
149: private int getColumnType(int columnIndex) {
150: try {
151: return this .metaData.getColumnType(columnIndex);
152: } catch (SQLException e) {
153: throw new BrokerException(e);
154: }
155: }
156:
157: void cacheResultObject(ResultKey pk, Object resultObject) {
158: if (pk.isValidKey()) {
159: this .cachedResultObjects.put(pk, resultObject);
160: }
161: }
162:
163: int findColumn(String columnName) {
164: if (this .currentAliases == null && this .aliasStack != null
165: && !this .aliasStack.isEmpty()) {
166: this .currentAliases = (Map) this .aliasStack
167: .get(this .aliasStack.size() - 1);
168: }
169: if (this .currentAliases != null) {
170: String queryColumn = (String) this .currentAliases
171: .get(columnName);
172: if (queryColumn != null) {
173: columnName = queryColumn;
174: }
175: }
176:
177: try {
178: return this .resultSet.findColumn(columnName);
179: } catch (SQLException e) {
180: String msg = "Cannot find column '" + columnName + "'";
181: try {
182: String columnList = " among available: ";
183: int columns = this .metaData.getColumnCount();
184: for (int column = 1; column <= columns; column++) {
185: if (column > 1) {
186: columnList += ", ";
187: }
188: columnList += this .metaData.getColumnName(column);
189: }
190: msg += columnList;
191: } catch (SQLException e2) {
192: // Ok, too bad
193: }
194: throw new BrokerException(msg, e);
195: }
196: }
197:
198: Object getCachedResultObject(ResultKey pk) {
199: if (pk.isValidKey()) {
200: return this .cachedResultObjects.get(pk);
201: }
202: return null;
203: }
204:
205: /**
206: * @param columnIndex
207: * @return column label
208: * @see ResultSetMetaData#getColumnName(int)
209: */
210: String getColumnLabel(int columnIndex) {
211: try {
212: return this .metaData.getColumnLabel(columnIndex);
213: } catch (SQLException e) {
214: throw new BrokerException(e);
215: }
216: }
217:
218: /**
219: * @param columnIndex
220: * @return column name
221: * @see ResultSetMetaData#getColumnName(int)
222: */
223: String getColumnName(int columnIndex) {
224: try {
225: return this .metaData.getColumnName(columnIndex);
226: } catch (SQLException e) {
227: return "[unknown column]";
228: }
229: }
230:
231: /**
232: * @param columnIndex
233: * @return column type name
234: * @see ResultSetMetaData#getColumnTypeName(int)
235: */
236: String getColumnTypeName(int columnIndex) {
237: try {
238: return this .metaData.getColumnTypeName(columnIndex);
239: } catch (SQLException e) {
240: throw new BrokerException(e);
241: }
242: }
243:
244: Object getColumnValue(int columnIndex, Class requiredType) {
245: return getColumnJdbcType(columnIndex, requiredType)
246: .getColumnValue(this , columnIndex, requiredType);
247: }
248:
249: Connection getConnection() {
250: return this .connection;
251: }
252:
253: Object getMappingStrategyKey() {
254: if (this .statement != null) {
255: return this .statement;
256: }
257: return this .metaData;
258: }
259:
260: int[] getReadableColumns() {
261: return this .readableColumns;
262: }
263:
264: /**
265: * @param columnIndex
266: * @return table name
267: * @see ResultSetMetaData#getTableName(int)
268: */
269: String getTableName(int columnIndex) {
270: String unknownTable = "[unknown table]";
271: try {
272: String tableName = this .metaData.getTableName(columnIndex);
273: if (tableName != null && tableName.trim().length() > 0) {
274: return tableName;
275: }
276: } catch (SQLException e) {
277: return unknownTable;
278: }
279: return unknownTable;
280: }
281:
282: TextReplacements getTextReplacements() {
283: return this .context.getTextReplacements();
284: }
285:
286: /**
287: * @return Type
288: * @see java.sql.ResultSet#getType()
289: */
290: int getType() {
291: try {
292: return this .resultSet.getType();
293: } catch (SQLException e) {
294: throw new BrokerException(e);
295: }
296: }
297:
298: /**
299: * Pulls the current map of aliases off the stack.
300: *
301: * @see #pushAliases(Map)
302: */
303: void pullAliases() {
304: Object removedMap = this .aliasStack.remove(this .aliasStack
305: .size() - 1);
306: assert this .currentAliases == removedMap : "Current alias map does not match stack.";
307: this .currentAliases = null;
308: }
309:
310: /**
311: * Pushes a map of aliases on the alias stack.
312: *
313: * @param aliases
314: * @see #pullAliases()
315: */
316: void pushAliases(Map aliases) {
317: if (this .aliasStack == null) {
318: this .aliasStack = new ArrayList(15);
319: }
320: this .aliasStack.add(aliases);
321: }
322:
323: /**
324: * @return true, if column was null.
325: * @see java.sql.ResultSet#wasNull()
326: */
327: boolean wasNull() {
328: try {
329: return this .resultSet.wasNull();
330: } catch (SQLException e) {
331: throw new BrokerException(e);
332: }
333: }
334:
335: /**
336: * @param columnIndex
337: * @return Array
338: * @see java.sql.ResultSet#getArray(int)
339: */
340: public Array getArray(int columnIndex) {
341: try {
342: return this .resultSet.getArray(columnIndex);
343: } catch (SQLException e) {
344: throw new BrokerException(e);
345: }
346: }
347:
348: /**
349: * @param columnIndex
350: * @return InputStream
351: * @see java.sql.ResultSet#getAsciiStream(int)
352: */
353: public InputStream getAsciiStream(int columnIndex) {
354: int columnType = getColumnType(columnIndex);
355: try {
356: if (columnType == Types.CLOB) {
357: Clob clob = this .resultSet.getClob(columnIndex);
358: if (clob == null)
359: return null;
360: return clob.getAsciiStream();
361: }
362: return this .resultSet.getAsciiStream(columnIndex);
363: } catch (SQLException e) {
364: throw new BrokerException(e);
365: }
366: }
367:
368: /**
369: * @param columnIndex
370: * @return BigDecimal
371: * @see java.sql.ResultSet#getBigDecimal(int)
372: */
373: public BigDecimal getBigDecimal(int columnIndex) {
374: try {
375: return this .resultSet.getBigDecimal(columnIndex);
376: } catch (SQLException e) {
377: throw new BrokerException(e);
378: }
379: }
380:
381: /**
382: * @param columnIndex
383: * @return InputStream
384: * @see java.sql.ResultSet#getBinaryStream(int)
385: */
386: public InputStream getBinaryStream(int columnIndex) {
387: int columnType = getColumnType(columnIndex);
388: try {
389: if (columnType == Types.BLOB) {
390: Blob blob = this .resultSet.getBlob(columnIndex);
391: if (blob == null)
392: return null;
393: return blob.getBinaryStream();
394: }
395: return this .resultSet.getBinaryStream(columnIndex);
396:
397: } catch (SQLException e) {
398: throw new BrokerException(e);
399: }
400: }
401:
402: /**
403: * @param columnIndex
404: * @return Blob
405: * @see java.sql.ResultSet#getBlob(int)
406: */
407: public Blob getBlob(int columnIndex) {
408: try {
409: return this .resultSet.getBlob(columnIndex);
410: } catch (SQLException e) {
411: throw new BrokerException(e);
412: }
413: }
414:
415: /**
416: * @param columnIndex
417: * @return boolean
418: * @see java.sql.ResultSet#getBoolean(int)
419: */
420: public boolean getBoolean(int columnIndex) {
421: try {
422: return this .resultSet.getBoolean(columnIndex);
423: } catch (SQLException e) {
424: throw new BrokerException(e);
425: }
426: }
427:
428: /**
429: * @param columnIndex
430: * @return byte
431: * @see java.sql.ResultSet#getByte(int)
432: */
433: public byte getByte(int columnIndex) {
434: try {
435: return this .resultSet.getByte(columnIndex);
436: } catch (SQLException e) {
437: throw new BrokerException(e);
438: }
439: }
440:
441: /**
442: * @param columnIndex
443: * @return byte[]
444: * @see java.sql.ResultSet#getBytes(int)
445: */
446: public byte[] getBytes(int columnIndex) {
447: int columnType = getColumnType(columnIndex);
448: try {
449: switch (columnType) {
450:
451: case Types.BLOB:
452: Blob blob = this .resultSet.getBlob(columnIndex);
453: if (blob == null)
454: return null;
455: checkLength(blob.length(), columnType, "byte[]");
456: return blob.getBytes(1, (int) blob.length());
457:
458: case Types.CLOB:
459: Clob clob = this .resultSet.getClob(columnIndex);
460: if (clob == null)
461: return null;
462: checkLength(clob.length(), columnType, "byte[]");
463: byte[] bytes = new byte[(int) clob.length()];
464: BufferedInputStream is = new BufferedInputStream(clob
465: .getAsciiStream());
466: try {
467: is.read(bytes);
468: } catch (IOException e) {
469: throw new BrokerException(e);
470: }
471: return bytes;
472:
473: default:
474: return this .resultSet.getBytes(columnIndex);
475:
476: }
477: } catch (SQLException e) {
478: throw new BrokerException(e);
479: }
480: }
481:
482: /**
483: * @param columnIndex
484: * @return Reader
485: * @see java.sql.ResultSet#getCharacterStream(int)
486: */
487: public Reader getCharacterStream(int columnIndex) {
488: int columnType = getColumnType(columnIndex);
489: try {
490: switch (columnType) {
491:
492: case Types.CLOB:
493: Clob clob = this .resultSet.getClob(columnIndex);
494: if (clob == null)
495: return null;
496: return clob.getCharacterStream();
497:
498: case Types.BLOB:
499: Blob blob = this .resultSet.getBlob(columnIndex);
500: if (blob == null)
501: return null;
502: return new InputStreamReader(blob.getBinaryStream());
503:
504: default:
505: return this .resultSet.getCharacterStream(columnIndex);
506: }
507: } catch (SQLException e) {
508: throw new BrokerException(e);
509: }
510: }
511:
512: /**
513: * @param columnIndex
514: * @return Clob
515: * @see java.sql.ResultSet#getClob(int)
516: */
517: public Clob getClob(int columnIndex) {
518: try {
519: return this .resultSet.getClob(columnIndex);
520: } catch (SQLException e) {
521: throw new BrokerException(e);
522: }
523: }
524:
525: /**
526: * @param columnIndex
527: * @return Date
528: * @see java.sql.ResultSet#getDate(int)
529: */
530: public Date getDate(int columnIndex) {
531: try {
532: return this .resultSet.getDate(columnIndex);
533: } catch (SQLException e) {
534: throw new BrokerException(e);
535: }
536: }
537:
538: /**
539: * @param columnIndex
540: * @return double
541: * @see java.sql.ResultSet#getDouble(int)
542: */
543: public double getDouble(int columnIndex) {
544: try {
545: return this .resultSet.getDouble(columnIndex);
546: } catch (SQLException e) {
547: throw new BrokerException(e);
548: }
549: }
550:
551: /**
552: * @param columnIndex
553: * @return float
554: * @see java.sql.ResultSet#getFloat(int)
555: */
556: public float getFloat(int columnIndex) {
557: try {
558: return this .resultSet.getFloat(columnIndex);
559: } catch (SQLException e) {
560: throw new BrokerException(e);
561: }
562: }
563:
564: /**
565: * @param columnIndex
566: * @return int
567: * @see java.sql.ResultSet#getInt(int)
568: */
569: public int getInt(int columnIndex) {
570: try {
571: return this .resultSet.getInt(columnIndex);
572: } catch (SQLException e) {
573: throw new BrokerException(e);
574: }
575: }
576:
577: /**
578: * @param columnIndex
579: * @return long
580: * @see java.sql.ResultSet#getLong(int)
581: */
582: public long getLong(int columnIndex) {
583: try {
584: return this .resultSet.getLong(columnIndex);
585: } catch (SQLException e) {
586: throw new BrokerException(e);
587: }
588: }
589:
590: /**
591: * @param columnIndex
592: * @return Object
593: * @see java.sql.ResultSet#getObject(int)
594: */
595: public Object getObject(int columnIndex) {
596: int columnType = getColumnType(columnIndex);
597: try {
598: // Only boolean is not supported as getObject by JDBC.
599: if (columnType == Types.BOOLEAN) {
600: boolean value = this .resultSet.getBoolean(columnIndex);
601: return Boolean.valueOf(value);
602: }
603: return this .resultSet.getObject(columnIndex);
604: } catch (SQLException e) {
605: throw new BrokerException(e);
606: }
607: }
608:
609: /**
610: * @param columnIndex
611: * @return Ref
612: * @see java.sql.ResultSet#getRef(int)
613: */
614: public Ref getRef(int columnIndex) {
615: try {
616: return this .resultSet.getRef(columnIndex);
617: } catch (SQLException e) {
618: throw new BrokerException(e);
619: }
620: }
621:
622: /**
623: * @param columnIndex
624: * @return short
625: * @see java.sql.ResultSet#getShort(int)
626: */
627: public short getShort(int columnIndex) {
628: try {
629: return this .resultSet.getShort(columnIndex);
630: } catch (SQLException e) {
631: throw new BrokerException(e);
632: }
633: }
634:
635: /**
636: * @param columnIndex
637: * @return String
638: * @see java.sql.ResultSet#getString(int)
639: */
640: public String getString(int columnIndex) {
641: int columnType = getColumnType(columnIndex);
642: try {
643: switch (columnType) {
644:
645: case Types.CLOB:
646: Clob clob = this .resultSet.getClob(columnIndex);
647: if (clob == null)
648: return null;
649: checkLength(clob.length(), columnType, "String");
650: return clob.getSubString(1, (int) clob.length());
651:
652: case Types.BLOB:
653: byte[] bytes = getBytes(columnIndex);
654: if (bytes == null)
655: return null;
656: return new String(bytes);
657:
658: default:
659: return this .resultSet.getString(columnIndex);
660:
661: }
662: } catch (SQLException e) {
663: throw new BrokerException(e);
664: }
665: }
666:
667: /**
668: * @param columnIndex
669: * @return Time
670: * @see java.sql.ResultSet#getTime(int)
671: */
672: public Time getTime(int columnIndex) {
673: try {
674: return this .resultSet.getTime(columnIndex);
675: } catch (SQLException e) {
676: throw new BrokerException(e);
677: }
678: }
679:
680: /**
681: * @param columnIndex
682: * @return Timestamp
683: * @see java.sql.ResultSet#getTimestamp(int)
684: */
685: public Timestamp getTimestamp(int columnIndex) {
686: try {
687: return this .resultSet.getTimestamp(columnIndex);
688: } catch (SQLException e) {
689: throw new BrokerException(e);
690: }
691: }
692:
693: /**
694: * @param columnIndex
695: * @return URL
696: * @see java.sql.ResultSet#getURL(int)
697: */
698: public URL getURL(int columnIndex) {
699: try {
700: return this .resultSet.getURL(columnIndex);
701: } catch (SQLException e) {
702: throw new BrokerException(e);
703: }
704: }
705: }
|