public int write(final ResultSet rs) throws IOException, SQLException {

		final ResultSetMetaData md = rs.getMetaData();
		final int colCount = md.getColumnCount();

		// Create a JTableModel to hold the data; map 1-origin SQL row/col to/from 0-origin JTable row/col
		TableModel dataModel = new TableModel() {

			public Class<?> getColumnClass(int columnIndex) {
				try {
					return Class.forName(md.getColumnClassName(1+columnIndex));
				} catch (Exception e) {
					return null;
				}
			}

			public int getColumnCount() {
				return colCount;
			}

			public String getColumnName(int columnIndex) {
				try {
					return md.getColumnLabel(1+columnIndex);
				} catch (SQLException e) {
					e.printStackTrace();
					return "unknown";
				}
			}

			public int getRowCount() {
				try {
					rs.last();
					return rs.getRow();
				} catch (SQLException e) {
					e.printStackTrace();
					return -1;
				}
			}

			public Object getValueAt(int rowIndex, int columnIndex) {
				try {
					rs.absolute(1+rowIndex);
					return rs.getObject(1+columnIndex);
				} catch (SQLException e) {
					e.printStackTrace();
					return null;
				}
			}

			/** All read-only for now */
			public boolean isCellEditable(int rowIndex, int columnIndex) {
				return false;
			}

			public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
				throw new IllegalArgumentException("Model is read-only");
			}

			// LISTENER SUPPORT
			public void addTableModelListener(TableModelListener l) {
                // ??
			}

			public void removeTableModelListener(TableModelListener l) {
                // ??
			}
		};

		// populate it
		table.setModel(dataModel);
		
		// Something the UI interface design forgot
		for (int i = 0; i < colCount; i++) {
			// XXX put column name as tooltip
		}

		return dataModel.getRowCount();
	}