Simple question. Is there any way to check if a JTable contains a column before calling getColumn(Object identifier) ?
JTable table = ...;
table.getColumn("header");
getColumn() throws a IllegalArgumentException if the header doesn't exist. So, is catching that exception the only way to check for the column? Looking for a hasColumn() or doesColumnExist() or isColumnValid() but, alas, nothing.
You could simply iterate over the TableColumnModel's available columns and see if it contains the available column, for example...
JTable table = ...;
Object identifier = ...;
TableColumnModel model = table.getColumnModel();
boolean found = false;
for (int index = 0; index < model.getColumnCount(); index++) {
if (model.getColumn(index).getIdentifier().equals(identifier)) {
found = true;
break;
}
}
For example...And yes, I would write this into a helper method/class so you can reuse it.
You should avoid using exceptions as logic controllers, as there might a number of reasons an exception might be raised
Related
I have a table that shows the results of user defined queries in a swing project. I want to allow the user to extract the data from a particular names column, if present. At the moment I can select the data from a column when I click the column but I don't know how to do the same thing from a button so that only a particular column's data (the column is called HNum) is obtainable. The code I have so far is as follows. If this is impossible I could always try to make sure that HNum is the first column but I need something cleaner I think.
btnCompare.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
Object[] data_L = columnToArray(table,table.getSelectedColumn());
}
}
public Object[] columnToArray(JTable table, int columnIndex){
// get the row count
int rowCount = table.getModel().getRowCount();
// declare the array
Object [] data = new Object[rowCount];
// fetch the data
for(int i = 0; i < rowCount; i++){
data[i] = table.getModel().getValueAt(i, columnIndex);
}
return(data);
}
Do you tried to use a TableColumnModel ?
You can define all the treatment you need like a getColumnName{}
https://docs.oracle.com/javase/7/docs/api/javax/swing/table/TableColumnModel.html
edit :
an example
http://www.java2s.com/Tutorial/Java/0240__Swing/ExtendingAbstractTableModel.htm
Background
I have a JTable called table, and I have a column that is not part of the DefaultTableModel so its invisible:
final JTable table = new JTable(new DefaultTableModel(new Object[]{"Title", "Artist",
"Album", "Time"}, 0)
I add the respective rows like this:
int upTo = songList.size();
int idx = 0;
while (idx < upTo) {
SongObject curSong = songList.get(idx);
model.addRow(new Object[]{
curSong.toString(),
curSong.getArtist(),
"-",
curSong.getDuration(),
curSong});
idx++;
}
Where curSong is the the current song object that it is adding, the SongObject contains all data about the song. The toString() returns the title of the song.
Problem:
The problem is that when I try to access the column like this:
SongObject songToPlay = (SongObject) table.getModel().getValueAt(table.getSelectedRow(), 4);
It throws a java.lang.ArrayIndexOutOfBoundsException: 4 >= 4 exception.
Can anyone explain why and propose a solution?
Thanks in advance :)
DefaultTableModel.addRow() somewhere down the chain executes private justifyRows() method, which trims the unused columns from the row to the size equal to getColumnCount(). So the fifth column is never added to the model. As a result, you get ArrayIndexOutOfBoundsException when you're attempting to access this column.
If you need access to the actual SongObject then you can have a custom model that would return SongObject for a given row index. Make an extension of AbstractTableModel. See How to Use Tables tutorial for examples.
As an alternative, you can still use SongObject in a visible column. Just use a custom renderder that would renderer SongObject as a string for example. See Using Custom Renderers for details. You can reuse DefaultTableModel in this case.
Thanks to Aqua I overrode the following:
final JTable table = new JTable(new DefaultTableModel(new Object[]{"Title", "Artist", "Album", "Time"}, 0) {
#Override
public void addRow(Object[] rowData) {
Vector blah = DefaultTableModel.convertToVector(rowData);
insertRow(super.getRowCount(), blah);
}
#Override
public void insertRow(int row, Vector data) {
super.dataVector.insertElementAt(data, row);
super.fireTableRowsInserted(row, row);
}
});
Then I accessed the item in the fifth column (which is not part of the model!) like this:
SongObject songToPlay = (SongObject) table.getModel().getValueAt(table.convertRowIndexToModel(
table.getSelectedRow()), 4); //get the value at the VIEW location NOT THE MODEL collection
Sorry for the messy code but it worked. Home I could help someone with a similar problem. The solution just misses the justifyRows() method found in DefaultTableModel
I have a Cell Table that I am using to output some search results. The cell table uses a list data provider to update info. I want to separate different sections so I am attempting to add a custom row in between different sections that has one cell that spans all of the columns. I am extending AbstractCellTableBuilder to do this, but my issue comes when I use TableRowBuilder and startRow(), calling startRow() returns a null pointer exception, to AbstractCellTableBuilder.java:243, which refers to tbody. So this is leading me to believe that my cell table is not getting passed into AbstractCellTableBuilder properly. My understanding of gwt and java is pretty basic, so I might just not be understanding how exactly this is supposed to work, and the showcase example is pretty complicated for me to understand. If anyone can tell where I'm messing up or has any simpler examples of this that might help me I would appreciate it!
I had found a similar answer and tried to implement it, and that is how I came up with what I have, but it answer wasn't quite detailed enough for me to fully understand how it works. Here is what I referenced:
Building a custom row on demand with GWT CellTableBuilder
EDITED:
Basic format of how I add normal rows to the cell table
searchProvider = new ListDataProvider<SearchColumn>();
cellTable_2 = new CellTable<SearchColumn>();
//Add columns to the cellTable
searchProvider.addDataDisplay(cellTable_2);
//What I call when adding a row to the cellTable using the ListDataProvider
searchProvider.getList().add(new SearchColumn("",label,"","","","","","",""));
Adding the CustomCellTableBuilder to the cell table:
//Passing the CustomCellTableBuilder to the cell table
CustomCellTableBuilder buildRow = new CustomCellTableBuilder();
cellTable_2.setTableBuilder(buildRow);
The CustomCellTableBuilder for adding custom rows:
public class CustomCellTableBuilder extends AbstractCellTableBuilder<SearchColumn>{
public CustomCellTableBuilder() {
super(cellTable_2);
}
#Override
protected void buildRowImpl(SearchColumn rowValue, int absRowIndex){
//building main rows logic
if (labelrow == 1){
System.out.println("Going to build extra row if");
buildExtraRow(absRowIndex, rowValue);
}
else {
System.out.println("Getting into normal buildRow");
buildRow(rowValue,absRowIndex);
}
}
private void buildExtraRow(int absRowIndex, SearchColumn rowValue){
start(true);
TableRowBuilder row = startRow();
TableCellBuilder td = row.startTD().colSpan(getColumns().size());
td.text("Testing this out").endTD();
row.endTR();
}}
I think you should call start(true) before calling startRow() because tbody is initialized to null. Start() call will initialize tbody to HtmlBuilderFactory.get().createTBodyBuilder().
The source doesn't lie.
Just like that:
private void buildExtraRow(int absRowIndex, SearchColumn rowValue) {
start(true); // true makes builder to rebuild all rows
TableRowBuilder row = startRow();
// whatever
}
ive been trying to update the table after the insertion or deletion of items from a abstract table model but whenever i do that, instead of removing the old records and replace with the new ones, the old rows remains and it creates all the rows again without removing the old ones..so i get duplicate items, this is the code im using :
for the data inserted :
TestModel tm = new TestModel() ;
tm.fireTableRowsInserted(records.length, records.length);
and for the data deleted :
TestModel tm = new TestModel() ;
tm.fireTableRowsDeleted(records.length, records.length);
any clue of how to get around with that?
any help is greatly appreciated!
Kind regards,
Romulo Romero
Create a table with a boolean column. Since using this boolean column you can delete those rows that are selected for deletion. Just like the following screen shot,
Then in your TableModel make a List<StudentDO> such that it will hold all the table data.
Adding a Row:
To add a row just create a new StudentDO and send it to the table model and the model addRow method will add the object to the table list.
Deleting Rows:
For Deleting rows just call a delete method and this should fire event in TableModel such that model should traverse all the rows and check which ever row is selected and delete it.
Note: Deleting rows should be done from the end not from the beginning of the list.
StudentTableModel.java
class StudentTableModel {
// Required methods code goes here.
public void addRow(StudentDO do1) {
data.add(do1);
fireTableRowsInserted(getRowCount() - 1, getRowCount() - 1);
}
public void deleteRow() {
for(int rowIndex = data.size() - 1; rowIndex >= 0; rowIndex--) {
if(data.get(rowIndex).isSelect()) {
data.remove(rowIndex);
}
}
fireTableDataChanged();
}
}
P.S: fireXXXMethods should be called only in the model. Because any data change will be responsible of the model.
I am querying data from views that are subject to change. I need to know if the column exists before I do a crs.get******().
I have found that I can query the metadata like this to see if a column exist before I request the data from it:
ResultSetMetaData meta = crs.getMetaData();
int numCol = meta.getColumnCount();
for (int i = 1; i < numCol + 1; i++)
if (meta.getColumnName(i).equals("name"))
return true;
Is there a simpler way of checking to see if a column exists?
EDIT
It must be database agnostic. That is why I am referencing the CachedRowSet instead of the database.
There's not a simpler way with the general JDBC API (at least not that I know of, or can find...I've got exactly the same code in my home-grown toolset.)
Your code isn't complete:
ResultSetMetaData meta = crs.getMetaData();
int numCol = meta.getColumnCount();
for (int i = 1; i < numCol + 1; i++) {
if (meta.getColumnName(i).equals("name")) {
return true;
}
}
return false;
That being said, if you use proprietary, database-specific API's and/or SQL queries, I'm sure you can find more elegant ways of doing the same thing. Bbut you'd have to write custom code for each database you need to deal with. I'd stick with the JDBC APIs, if I were you.
Is there something about your proposed solution that makes you think it's incorrect? It seems simple enough to me.
you could take the shorter approach of using the fact that findColumn() will throw an SQLException for InvalidColumName if the column isn't in the CachedRowSet.
for example
try {
int foundColIndex = results.findColumn("nameOfColumn");
} catch {
// do whatever else makes sense
}
Likely an abuse of Exception Handling (per EffectiveJava 2nd ed item 57) but it is an alternative to looping through all the columns from the meta data.
Which Database?
I think in Oracle there are tables where the columns are listed.
I don't remember if it work for views also, but I guess they do, it was something like:
select colum_name from all_views where view_name like 'myview'
or
select name from all_objects where object_name like 'myview' and object_type='view'
I don't remember exactly the syntax. You should have spacial permissions though.
Every RDBMS should have something similar.
You can also perform the query
select * from myView where 1 = 0 ;
And from the metadata get the columns, if what you want it to avoid fetching the data before to know if the columns are present.
No, there really isn't a better way. You may want to relook at the problem. If you can redefine the problem, sometimes it makes the solution simpler because the problem has changed.
WARNING: following comment purely from memory without any supporting paperwork :)
If I recall correctly there is a mysterious problem that rears its ever-so-ugly-head when the oracle cached rowset implementation is used with connection pooling. There appears to be a silent reference to the connection held within the cached rowset object (even though it's supposed to be disconnected) which closes another connection subsequently opened from pool on garbage collection. For this reason I eventually gave up and wrote my own data object layer (these days I'd hand that over to spring & hibernate).
Old thread, but I've just faced the same problem and ended up with an utility function:
private Set<String> getColumnNames(ResultSet cached) throws SQLException {
ResultSetMetaData metaData = cached.getMetaData();
return IntStream.range(1, metaData.getColumnCount())
.mapToObj(i -> {
try {
return metaData.getColumnName(i);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}).collect(toSet());
}
It'd be quite elegent if we wouldn't have to catch exceptions inside a lambda (without some ugly hacks)
Following on from the top answer in this thread from Jared, and one of its under-rated comments from corsiKa:
ResultSetMetaData meta = crs.getMetaData();
int numCol = meta.getColumnCount();
Set<String> columns = new HashSet<>;
for (int i = 1; i <= numCol; i++)
{
columns.add(meta.getColumnName(i));
}
return columns.contains("name");