001 /* JAPI - (Yet another (hopefully) useful) Java API 002 * 003 * Copyright (C) 2004-2006 Christian Hujer 004 * 005 * This program is free software; you can redistribute it and/or 006 * modify it under the terms of the GNU General Public License as 007 * published by the Free Software Foundation; either version 2 of the 008 * License, or (at your option) any later version. 009 * 010 * This program is distributed in the hope that it will be useful, but 011 * WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013 * General Public License for more details. 014 * 015 * You should have received a copy of the GNU General Public License 016 * along with this program; if not, write to the Free Software 017 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 018 * 02111-1307, USA. 019 */ 020 021 package net.sf.japi.sql; 022 023 import java.sql.ResultSet; 024 import java.sql.SQLException; 025 import javax.swing.table.AbstractTableModel; 026 import javax.swing.table.TableModel; 027 import org.jetbrains.annotations.Nullable; 028 import net.sf.japi.util.ThrowableHandler; 029 030 /** An implementation of <code>javax.swing.TableModel</code> for an SQL ResultSet. 031 * In contrast to @see ResultSetTableModel this implementation reads all data upon setting the ResultSet. 032 * The advantage is that reading the ResultSet does not require the database connection anymore. 033 * The disadvantage is that this class requires much memory on large results and may fail for OutOfMemory on huge results. 034 * This class is fully serializable, at least in the way Swing classes are at all serializable. 035 * <p/> 036 * Serialized instances will also serialize the cached data. 037 * The data source information is not serialized. 038 * @see ResultSet 039 * @see TableModel 040 * @author <a href="mailto:chris@riedquat.de">Christian Hujer</a> 041 * @todo maybe setResultSet should throw SQLException? 042 */ 043 public class CachedResultSetTableModel extends AbstractTableModel implements ResultSetTableModel { 044 045 /** Serial Version. */ 046 private static final long serialVersionUID = 1L; 047 048 /** The number of rows. 049 * @serial include 050 */ 051 private int rowCount; 052 053 /** The number of columns. 054 * @serial include 055 */ 056 private int columnCount; 057 058 /** The column titles. 059 * @serial include 060 */ 061 private String[] columnTitles; 062 063 /** The Data. 064 * @serial include 065 */ 066 private Object[][] data; 067 068 /** The ResultSet. */ 069 private ResultSet resultSet; 070 071 /** Create a CachedResultSetTableModel. */ 072 public CachedResultSetTableModel() { 073 } 074 075 /** Create a CachedResultSetTableModel. 076 * @param rs Initial ResultSet 077 */ 078 public CachedResultSetTableModel(final ResultSet rs) { 079 setResultSet(rs); 080 } 081 082 /** Report an exception to all registered ThrowableHandlers. 083 * @param exception Exception to report 084 */ 085 private void handleException(final SQLException exception) { 086 final Object[] listeners = listenerList.getListenerList(); 087 for (int i = listeners.length - 2; i >= 0; i -= 2) { 088 //noinspection ObjectEquality 089 if (listeners[i] == ThrowableHandler.class) { 090 ((ThrowableHandler<? super SQLException>)listeners[i+1]).handleThrowable(exception); 091 } 092 } 093 } 094 095 /** {@inheritDoc} */ 096 public void setResultSet(final ResultSet resultSet) { 097 if (resultSet == null) { 098 this.resultSet = resultSet; 099 rowCount = 0; 100 columnCount = 0; 101 columnTitles = null; 102 data = null; 103 } else { 104 try { 105 columnTitles = SQLHelper.getColumnLabels(resultSet); 106 columnCount = columnTitles.length; 107 rowCount = SQLHelper.getRowCount(resultSet); 108 data = SQLHelper.getData(resultSet); 109 this.resultSet = resultSet; 110 } catch (final SQLException e) { 111 handleException(e); 112 rowCount = 0; 113 columnCount = 0; 114 columnTitles = null; 115 data = null; 116 return; // don't invoke fireTableStructureChanged() twice then. 117 } 118 } 119 fireTableStructureChanged(); 120 } 121 122 /** {@inheritDoc} */ 123 public ResultSet getResultSet() { 124 return resultSet; 125 } 126 127 /** {@inheritDoc} */ 128 public void addThrowableHandler(final ThrowableHandler<? super SQLException> throwableHandler) { 129 listenerList.add(ThrowableHandler.class, throwableHandler); 130 } 131 132 /** {@inheritDoc} */ 133 public void removeThrowableHandler(final ThrowableHandler<? super SQLException> throwableHandler) { 134 listenerList.remove(ThrowableHandler.class, throwableHandler); 135 } 136 137 /** @see TableModel */ 138 public int getColumnCount() { 139 return columnCount; 140 } 141 142 /** @see TableModel */ 143 public int getRowCount() { 144 return rowCount; 145 } 146 147 /** @see TableModel */ 148 @Nullable public Object getValueAt(final int rowIndex, final int columnIndex) { 149 try { 150 return data[rowIndex][columnIndex]; 151 } catch (final NullPointerException e) { 152 return null; 153 } catch (final ArrayIndexOutOfBoundsException e) { 154 return null; 155 } 156 } 157 158 /** {@inheritDoc} 159 * Always returns <code>String.class</code>. 160 */ 161 @Override public Class<?> getColumnClass(final int columnIndex) { 162 return String.class; 163 } 164 165 /** {@inheritDoc} */ 166 @Override public String getColumnName(final int column) { 167 return columnTitles[column]; 168 } 169 170 } // class CachedResultSetTableModel