001: /**
002: * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE, version 2.1, dated February 1999.
003: *
004: * This program is free software; you can redistribute it and/or modify
005: * it under the terms of the latest version of the GNU Lesser General
006: * Public License as published by the Free Software Foundation;
007: *
008: * This program is distributed in the hope that it will be useful,
009: * but WITHOUT ANY WARRANTY; without even the implied warranty of
010: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
011: * GNU Lesser General Public License for more details.
012: *
013: * You should have received a copy of the GNU Lesser General Public License
014: * along with this program (LICENSE.txt); if not, write to the Free Software
015: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
016: */package org.jamwiki.db;
017:
018: import java.sql.ResultSet;
019: import java.sql.ResultSetMetaData;
020: import java.sql.Date;
021: import java.sql.SQLException;
022: import java.sql.Timestamp;
023: import java.util.Vector;
024: import java.util.LinkedHashMap;
025: import org.jamwiki.utils.WikiLogger;
026:
027: /**
028: * This class is a wrapper around the java.sql.ResultSet class, allowing the data
029: * from a SQL query to be accessed without requiring that a database connection be
030: * held. The main advantage of this approach is that all connection handling can
031: * be done by low-level database functions, and the user can process a result set
032: * without the need to ensure that the connection is properly closed after the
033: * data is processed.
034: */
035: public class WikiResultSet {
036:
037: private static final WikiLogger logger = WikiLogger
038: .getLogger(WikiResultSet.class.getName());
039: private final Vector rows = new Vector();
040: private int rowPointer = -1;
041: private int totalRows = -1;
042: private LinkedHashMap currentRow = null;
043:
044: /**
045: * Constructor used primarily for building new result sets. Use this
046: * constructor to create an empty result set, then fill it using the
047: * {@link WikiResultSet#addRow addRow()} method.
048: */
049: public WikiResultSet() {
050: }
051:
052: /**
053: * Create a WikiResultSet from a standard ResultSet.
054: *
055: * @see ResultSet
056: * @param rs The ResultSet used to populate this WikiResultSet.
057: */
058: protected WikiResultSet(ResultSet rs) throws Exception {
059: ResultSetMetaData rsmd = rs.getMetaData();
060: int size = rsmd.getColumnCount();
061: int type;
062: while (rs.next()) {
063: LinkedHashMap column = new LinkedHashMap();
064: for (int i = 1; i <= size; i++) {
065: String columnName = rsmd.getColumnName(i);
066: type = rsmd.getColumnType(i);
067: switch (type) {
068: case java.sql.Types.VARCHAR:
069: case java.sql.Types.CLOB:
070: String varchar = rs.getString(columnName);
071: column.put(columnName.toLowerCase(), varchar);
072: break;
073: case java.sql.Types.INTEGER:
074: case java.sql.Types.NUMERIC:
075: case java.sql.Types.SMALLINT:
076: case java.sql.Types.TINYINT:
077: int integer = rs.getInt(columnName);
078: column.put(columnName.toLowerCase(), new Integer(
079: integer));
080: break;
081: case java.sql.Types.BIGINT:
082: long longint = rs.getLong(columnName);
083: column.put(columnName.toLowerCase(), new Long(
084: longint));
085: break;
086: case java.sql.Types.DATE:
087: Date date = rs.getDate(columnName);
088: column.put(columnName.toLowerCase(), date);
089: break;
090: case java.sql.Types.TIMESTAMP:
091: Timestamp timestamp = rs.getTimestamp(columnName);
092: column.put(columnName.toLowerCase(), timestamp);
093: break;
094: case java.sql.Types.CHAR:
095: String value = rs.getString(columnName);
096: char character = '0';
097: if (value != null && value.length() > 0) {
098: character = value.charAt(0);
099: }
100: column.put(columnName.toLowerCase(), new Character(
101: character));
102: break;
103: default:
104: Object object = rs.getObject(columnName);
105: column.put(columnName.toLowerCase(), object);
106: }
107: }
108: this .rows.add(column);
109: }
110: this .totalRows = this .rows.size();
111: }
112:
113: /**
114: * <p>Moves the cursor to the given row number in this ResultSet object.</p>
115: *
116: * <p>If the row number is positive, the cursor moves to the given row number
117: * with respect to the beginning of the result set. The first row is row 1, the
118: * second is row 2, and so on.</p>
119: *
120: * <p>If the given row number is negative, the cursor moves to an absolute row
121: * position with respect to the end of the result set. For example, calling the
122: * method absolute(-1) positions the cursor on the last row; calling the method
123: * absolute(-2) moves the cursor to the next-to-last row, and so on.</p>
124: *
125: * <p>An attempt to position the cursor beyond the first/last row in the result
126: * set leaves the cursor before the first row or after the last row.</p>
127: *
128: * <p>This method duplicates the functionality of the
129: * {@link java.sql.ResultSet#absolute ResultSet.absolute(int row)}
130: * method.</p>
131: *
132: * @param row The number of the row to which the cursor should move. A positive
133: * number indicates the row number counting from the beginning of the result
134: * set; a negative number indicates the row number counting from the end of
135: * the result set.
136: * @return <code>true</code> if the cursor is on the result set; <code>false</code> otherwise.
137: */
138: public boolean absolute(int row) {
139: // row starts at 1, rowPointer starts at 0
140: if (row > 0) {
141: this .rowPointer = (row - 1);
142: } else {
143: this .rowPointer = (this .totalRows + row);
144: }
145: if (this .rowPointer < 0) {
146: this .rowPointer = -1;
147: return false;
148: }
149: if (this .rowPointer >= this .totalRows) {
150: this .rowPointer = this .totalRows;
151: return false;
152: }
153: return true;
154: }
155:
156: /**
157: * Method used to create a new WikiResultSet by copying rows of other
158: * WikiResultSet objects.
159: *
160: * @param rs The SQLResult that is being copied. Only the current row will
161: * be copied into the new WikiResultSet object.
162: * @throws Exception Thrown if the row pointer of the WikiResultSet being
163: * copied has passed the end of the WikiResultSet.
164: */
165: public void addRow(WikiResultSet rs) throws Exception {
166: if (rs.rowPointer == -1) {
167: rs.rowPointer++;
168: }
169: if (rs.rowPointer >= rs.totalRows) {
170: throw new Exception(
171: "Attempt to access beyond final row of WikiResultSet");
172: }
173: this .rows.add(rs.rows.elementAt(rs.rowPointer));
174: this .totalRows = this .rows.size();
175: }
176:
177: /**
178: * <p>Moves the cursor to the front of this <code>WikiResultSet</code> object, just
179: * before the first row. This method has no effect if the result set contains no
180: * rows.</p>
181: *
182: * <p>This method duplicates the
183: * {@link java.sql.ResultSet#beforeFirst ResultSet.beforeFirst()} method.</p>
184: */
185: public void beforeFirst() {
186: this .rowPointer = -1;
187: }
188:
189: /**
190: * <p>Moves the cursor to the first row in this <code>WikiResultSet</code> object.</p>
191: *
192: * <p>This method duplicates the {@link java.sql.ResultSet#first ResultSet.first()} method.</p>
193: *
194: * @return <code>true</code> if the cursor is on a valid row; <code>false</code>
195: * if there are no rows in the result set.
196: */
197: public boolean first() {
198: this .rowPointer = 0;
199: return (this .totalRows > 0);
200: }
201:
202: /**
203: * <p>Retrieves the value of the designated column in the current row of this
204: * <code>WikiResultSet</code> object as a <code>java.sql.Date</code> object in
205: * the Java programming language.</p>
206: *
207: * <p>This method duplicates the
208: * {@link java.sql.ResultSet#getDate ResultSet.getDate(String columnName)} method.</p>
209: *
210: * @param columnName The SQL name of the column.
211: * @return The column value; if the value is SQL <code>NULL</code>, the value
212: * returned is <code>null</code>.
213: * @throws SQLException If the cursor position is invalid or if the column name does
214: * not exist in the result set.
215: */
216: public Date getDate(String columnName) throws SQLException {
217: this .verifyColumn(columnName);
218: return (Date) this .currentRow.get(columnName.toLowerCase());
219: }
220:
221: /**
222: * <p>Retrieves the value of the designated column in the current row of this
223: * <code>WikiResultSet</code> object as a <code>char</code> value in
224: * the Java programming language.</p>
225: *
226: * @param columnName The SQL name of the column.
227: * @return The column value; if the value is SQL <code>NULL</code>, the value
228: * returned is <code>0</code>.
229: * @throws SQLException If the cursor position is invalid or if the column name does
230: * not exist in the result set.
231: */
232: public char getChar(String columnName) throws SQLException {
233: this .verifyColumn(columnName);
234: Character value = null;
235: try {
236: value = (Character) this .currentRow.get(columnName
237: .toLowerCase());
238: } catch (Exception e) {
239: // ignore, probably null
240: }
241: return (value == null) ? '0' : value.charValue();
242: }
243:
244: /**
245: * <p>Retrieves the value of the designated column in the current row of this
246: * <code>WikiResultSet</code> object as an <code>int</code> in the Java
247: * programming language.</p>
248: *
249: * <p>This method duplicates the
250: * {@link java.sql.ResultSet#getInt ResultSet.getInt(String columnName)} method.</p>
251: *
252: * @param columnName The SQL name of the column.
253: * @return The column value; if the value is SQL <code>NULL</code>, the value
254: * returned is <code>0</code>.
255: * @throws SQLException If the cursor position is invalid or if the column name does
256: * not exist in the result set.
257: */
258: public int getInt(String columnName) throws SQLException {
259: this .verifyColumn(columnName);
260: Integer value = null;
261: try {
262: value = (Integer) this .currentRow.get(columnName
263: .toLowerCase());
264: } catch (Exception e) {
265: // is it a long?
266: try {
267: value = new Integer(((Long) this .currentRow
268: .get(columnName.toLowerCase())).intValue());
269: } catch (Exception ex) {
270: }
271: }
272: return (value == null) ? 0 : value.intValue();
273: }
274:
275: /**
276: * <p>Retrieves the value of the designated column in the current row of this
277: * <code>WikiResultSet</code> object as a <code>long</code> in the Java
278: * programming language.</p>
279: *
280: * <p>This method duplicates the
281: * {@link java.sql.ResultSet#getLong ResultSet.getLong(String columnName)} method.</p>
282: *
283: * @param columnName The SQL name of the column.
284: * @return The column value; if the value is SQL <code>NULL</code>, the value
285: * returned is <code>0</code>.
286: * @throws SQLException If the cursor position is invalid or if the column name does
287: * not exist in the result set.
288: */
289: public long getLong(String columnName) throws SQLException {
290: this .verifyColumn(columnName);
291: Long value = (Long) this .currentRow.get(columnName
292: .toLowerCase());
293: return (value == null) ? 0 : value.longValue();
294: }
295:
296: /**
297: * <p>Retrieves the value of the designated column in the current row of this
298: * <code>WikiResultSet</code> object as a <code>java.lang.Object</code> object in
299: * the Java programming language.</p>
300: *
301: * <p>This method duplicates the
302: * {@link java.sql.ResultSet#getObject ResultSet.getObject(String columnName)} method.</p>
303: *
304: * @param columnName The SQL name of the column.
305: * @return The column value; if the value is SQL <code>NULL</code>, the value
306: * returned is <code>null</code>.
307: * @throws SQLException If the cursor position is invalid or if the column name does
308: * not exist in the result set.
309: */
310: public Object getObject(String columnName) throws SQLException {
311: this .verifyColumn(columnName);
312: return this .currentRow.get(columnName.toLowerCase());
313: }
314:
315: /**
316: * <p>Retrieves the value of the designated column in the current row of this
317: * <code>WikiResultSet</code> object as a <code>java.lang.String</code> object in
318: * the Java programming language.</p>
319: *
320: * <p>This method duplicates the
321: * {@link java.sql.ResultSet#getString ResultSet.getString(String columnName)} method.</p>
322: *
323: * @param columnName The SQL name of the column.
324: * @return The column value; if the value is SQL <code>NULL</code>, the value
325: * returned is <code>null</code>.
326: * @throws SQLException If the cursor position is invalid or if the column name does
327: * not exist in the result set.
328: */
329: public String getString(String columnName) throws SQLException {
330: this .verifyColumn(columnName);
331: return (String) this .currentRow.get(columnName.toLowerCase());
332: }
333:
334: /**
335: * <p>Retrieves the value of the designated column in the current row of this
336: * <code>WikiResultSet</code> object as a <code>java.lang.Timestamp</code> object in
337: * the Java programming language.</p>
338: *
339: * <p>This method duplicates the
340: * {@link java.sql.ResultSet#getTimestamp ResultSet.getTimestamp(String columnName)} method.</p>
341: *
342: * @param columnName The SQL name of the column.
343: * @return The column value; if the value is SQL <code>NULL</code>, the value
344: * returned is <code>null</code>.
345: * @throws SQLException If the cursor position is invalid or if the column name does
346: * not exist in the result set.
347: */
348: public Timestamp getTimestamp(String columnName)
349: throws SQLException {
350: this .verifyColumn(columnName);
351: return (Timestamp) this .currentRow
352: .get(columnName.toLowerCase());
353: }
354:
355: /**
356: * <p>Moves the cursor to the last row in this ResultSet object.</p>
357: *
358: * <p>This method duplicates the
359: * {@link java.sql.ResultSet#last ResultSet.last()} method.</p>
360: *
361: * @return <code>true</code> if the cursor is on a valid row; <code>false</code>
362: * if there are no rows in the result set.
363: */
364: public boolean last() {
365: if (this .totalRows > 0) {
366: this .rowPointer = (this .totalRows - 1);
367: return true;
368: }
369: return false;
370: }
371:
372: /**
373: * <p>Moves the cursor down one row from its current position. A
374: * <code>ResultSet</code> cursor is initially positioned before the
375: * first row; the first call to the method <code>next</code> makes the
376: * first row the current row; the second call makes the second row the
377: * current row, and so on.</p>
378: *
379: * <p>This method duplicates the
380: * {@link java.sql.ResultSet#next ResultSet.next()} method.</p>
381: *
382: * @return <code>true</code> if the new current row is valid; <code>false</code>
383: * if there are no more rows.
384: */
385: public boolean next() {
386: this .rowPointer++;
387: return (this .rowPointer < this .totalRows);
388: }
389:
390: /**
391: * <p>Return the total number of rows that exist in this result set.</p>
392: *
393: * @return The total number of rows that exist in this result set.
394: */
395: public int size() {
396: return this .totalRows;
397: }
398:
399: /**
400: * Utility method used when calling any of the <code>get</code> methods
401: * in this class.
402: */
403: private void verifyColumn(String columnName) throws SQLException {
404: if (this .rowPointer == -1) {
405: this .rowPointer++;
406: }
407: if (this .rowPointer >= this .totalRows) {
408: throw new SQLException(
409: "Attempt to access beyond last row of result set");
410: }
411: this .currentRow = (LinkedHashMap) this .rows
412: .elementAt(this .rowPointer);
413: if (columnName == null
414: || !this .currentRow.containsKey(columnName)) {
415: throw new SQLException("Invalid column name " + columnName);
416: }
417: }
418: }
|