001: package net.sourceforge.squirrel_sql.fw.datasetviewer;
002:
003: /*
004: * Copyright (C) 2001-2003 Colin Bell
005: * colbell@users.sourceforge.net
006: * Copyright (C) 2001-2003 Johan Compagner
007: * jcompagner@j-com.nl
008: *
009: * This library is free software; you can redistribute it and/or
010: * modify it under the terms of the GNU Lesser General Public
011: * License as published by the Free Software Foundation; either
012: * version 2.1 of the License, or (at your option) any later version.
013: *
014: * This library is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
017: * Lesser General Public License for more details.
018: *
019: * You should have received a copy of the GNU Lesser General Public
020: * License along with this library; if not, write to the Free Software
021: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
022: */
023: import java.sql.ResultSet;
024: import java.sql.ResultSetMetaData;
025: import java.sql.SQLException;
026: import java.util.ArrayList;
027: import java.util.List;
028:
029: import net.sourceforge.squirrel_sql.fw.sql.ResultSetReader;
030: import net.sourceforge.squirrel_sql.fw.util.IMessageHandler;
031: import net.sourceforge.squirrel_sql.fw.util.log.ILogger;
032: import net.sourceforge.squirrel_sql.fw.util.log.LoggerController;
033:
034: public class ResultSetDataSet implements IDataSet {
035: private final static ILogger s_log = LoggerController
036: .createLogger(ResultSetDataSet.class);
037:
038: // TODO: These 2 should be handled with an Iterator.
039: private int _iCurrent = -1;
040: private Object[] _currentRow;
041:
042: private int _columnCount;
043: private DataSetDefinition _dataSetDefinition;
044: private List<Object[]> _alData;
045:
046: /** If <TT>true</TT> cancel has been requested. */
047: private volatile boolean _cancel = false;
048:
049: /** the result set reader, which we will notify of cancel requests */
050: private ResultSetReader rdr = null;
051:
052: public ResultSetDataSet() {
053: super ();
054: }
055:
056: /**
057: * Form used by Tabs other than ContentsTab
058: */
059: public void setResultSet(ResultSet rs) throws DataSetException {
060: setResultSet(rs, null, false);
061: }
062:
063: /**
064: * Form used by ContentsTab, and for SQL results
065: */
066: public void setContentsTabResultSet(ResultSet rs,
067: String fullTableName) throws DataSetException {
068: setResultSet(rs, fullTableName, null, false, true);
069: }
070:
071: public void setResultSet(ResultSet rs, int[] columnIndices)
072: throws DataSetException {
073: setResultSet(rs, columnIndices, false);
074: }
075:
076: /**
077: * External method to read the contents of a ResultSet that is used by
078: * all Tab classes except ContentsTab. This tunrs all the data into strings
079: * for simplicity of operation.
080: */
081: public void setResultSet(ResultSet rs, int[] columnIndices,
082: boolean computeWidths) throws DataSetException {
083: setResultSet(rs, null, columnIndices, computeWidths, false);
084: }
085:
086: /**
087: * Internal method to read the contents of a ResultSet that is used by
088: * all Tab classes
089: */
090: private void setResultSet(ResultSet rs, String fullTableName,
091: int[] columnIndices, boolean computeWidths,
092: boolean useColumnDefs) throws DataSetException {
093: reset();
094:
095: if (columnIndices != null && columnIndices.length == 0) {
096: columnIndices = null;
097: }
098: _iCurrent = -1;
099: _alData = new ArrayList<Object[]>();
100:
101: if (rs != null) {
102: try {
103: ResultSetMetaData md = rs.getMetaData();
104: _columnCount = columnIndices != null ? columnIndices.length
105: : md.getColumnCount();
106:
107: // Done before actually reading the data from the ResultSet. If done after
108: // reading the data from the ResultSet Oracle throws a NullPointerException
109: // when processing ResultSetMetaData methods for the ResultSet returned for
110: // DatabasemetaData.getExportedKeys.
111: ColumnDisplayDefinition[] colDefs = createColumnDefinitions(
112: md, fullTableName, columnIndices, computeWidths);
113: _dataSetDefinition = new DataSetDefinition(colDefs);
114:
115: // Read the entire row, since some drivers complain if columns are read out of sequence
116: rdr = new ResultSetReader(rs, null);
117: Object[] row = null;
118:
119: while (true) {
120: if (useColumnDefs)
121: row = rdr.readRow(colDefs);
122: else
123: row = rdr.readRow();
124:
125: if (row == null)
126: break;
127:
128: if (_cancel) {
129: return;
130: }
131:
132: // SS: now select/reorder columns
133: if (columnIndices != null) {
134: Object[] newRow = new Object[_columnCount];
135: for (int i = 0; i < _columnCount; i++) {
136: if (columnIndices[i] - 1 < row.length) {
137: newRow[i] = row[columnIndices[i] - 1];
138: } else {
139: newRow[i] = "Unknown";
140: }
141: }
142: row = newRow;
143: }
144: _alData.add(row);
145: }
146:
147: // ColumnDisplayDefinition[] colDefs = createColumnDefinitions(md, columnIndices, computeWidths);
148: // _dataSetDefinition = new DataSetDefinition(colDefs);
149: } catch (SQLException ex) {
150: // Don't log an error message here. It is possible that the user
151: // interrupted the query because it was taking too long. Just
152: // throw the exception, and let the caller decide whether or not
153: // the exception should be logged.
154: throw new DataSetException(ex);
155: }
156: }
157: }
158:
159: public final int getColumnCount() {
160: return _columnCount;
161: }
162:
163: public DataSetDefinition getDataSetDefinition() {
164: return _dataSetDefinition;
165: }
166:
167: public synchronized boolean next(IMessageHandler msgHandler)
168: throws DataSetException {
169: // TODO: This should be handled with an Iterator
170: if (++_iCurrent < _alData.size()) {
171: _currentRow = _alData.get(_iCurrent);
172: return true;
173: }
174: return false;
175: }
176:
177: /* (non-Javadoc)
178: * @see net.sourceforge.squirrel_sql.fw.datasetviewer.IDataSet#get(int)
179: */
180: public Object get(int columnIndex) {
181: if (_currentRow != null) {
182: return _currentRow[columnIndex];
183: } else {
184: return null;
185: }
186: }
187:
188: public void cancelProcessing() {
189: rdr.setStopExecution(true);
190: _cancel = true;
191: }
192:
193: // SS: Modified to auto-compute column widths if <computeWidths> is true
194: private ColumnDisplayDefinition[] createColumnDefinitions(
195: ResultSetMetaData md, String fullTableName,
196: int[] columnIndices, boolean computeWidths)
197: throws SQLException {
198: // TODO?? ColumnDisplayDefinition should also have the Type (String, Date, Double,Integer,Boolean)
199: int[] colWidths = null;
200:
201: // SS: update dynamic column widths
202: if (computeWidths) {
203: colWidths = new int[_columnCount];
204: for (int i = 0; i < _alData.size(); i++) {
205: Object[] row = _alData.get(i);
206: for (int col = 0; i < _columnCount; i++) {
207: if (row[col] != null) {
208: int colWidth = row[col].toString().length();
209: if (colWidth > colWidths[col]) {
210: colWidths[col] = colWidth + 2;
211: }
212: }
213: }
214: }
215: }
216:
217: ColumnDisplayDefinition[] columnDefs = new ColumnDisplayDefinition[_columnCount];
218: for (int i = 0; i < _columnCount; ++i) {
219: int idx = columnIndices != null ? columnIndices[i] : i + 1;
220:
221: // save various info about the column for use in user input validation
222: // when editing table contents.
223: // Note that the columnDisplaySize is included two times, where the first
224: // entry may be adjusted for actual display while the second entry is the
225: // size expected by the DB.
226: // The isNullable() method returns three values that we convert into two
227: // by saying that if it is not known whether or not a column allows nulls,
228: // we will allow the user to enter nulls and any problems will be caught
229: // when they try to save the data to the DB
230: boolean isNullable = true;
231: if (md.isNullable(idx) == ResultSetMetaData.columnNoNulls)
232: isNullable = false;
233:
234: int precis;
235: try {
236: precis = md.getPrecision(idx);
237: } catch (NumberFormatException ignore) {
238: precis = Integer.MAX_VALUE; // Oracle throws this ex on BLOB data types
239: }
240:
241: boolean isSigned = true;
242: try {
243: isSigned = md.isSigned(idx); // HSQLDB 1.7.1 throws error.
244: } catch (SQLException ignore) {
245: // Empty block
246: }
247:
248: boolean isCurrency = false;
249:
250: try {
251: // Matt Dahlman: this causes problems with the JDBC driver delivered with Teradata V2R05.00.00.11
252: isCurrency = md.isCurrency(idx);
253: } catch (SQLException e) {
254: s_log
255: .error(
256: "Failed to call ResultSetMetaData.isCurrency()",
257: e);
258: }
259:
260: boolean isAutoIncrement = false;
261: try {
262: isAutoIncrement = md.isAutoIncrement(idx);
263: } catch (SQLException e) {
264: s_log
265: .error(
266: "Failed to call ResultSetMetaData.isAutoIncrement()",
267: e);
268: }
269: columnDefs[i] = new ColumnDisplayDefinition(
270: computeWidths ? colWidths[i] : md
271: .getColumnDisplaySize(idx), fullTableName
272: + ":" + md.getColumnLabel(idx), md
273: .getColumnName(idx),
274: md.getColumnLabel(idx), md.getColumnType(idx), md
275: .getColumnTypeName(idx), isNullable, md
276: .getColumnDisplaySize(idx), precis, md
277: .getScale(idx), isSigned, isCurrency,
278: isAutoIncrement);
279: }
280: return columnDefs;
281: }
282:
283: private void reset() {
284: _iCurrent = -1;
285: _currentRow = null;
286: _columnCount = 0;
287: _dataSetDefinition = null;
288: _alData = null;
289: }
290:
291: public void resetCursor() {
292: _iCurrent = -1;
293: _currentRow = null;
294: }
295:
296: /**
297: * Removes the row at the specified index.
298: *
299: * @param index the row number starting at 0.
300: * @return the object at the specified row or null if there is not row at the
301: * specified index.
302: */
303: public Object removeRow(int index) {
304: if (_alData.size() > index) {
305: return _alData.remove(index);
306: } else {
307: return null;
308: }
309: }
310: }
|