Dynamically updating a JTable from a vector<vector<string>> - java

I have a function called FetchInbox() which fetches the header information (Sender, subject, date sent) of an email and then adds it to a Vector of String Vectors.
What I want to be able to do is to refresh this table as new emails come in and update the table by first running FetchInbox() again, and then using this to repopulate the table.
I know this can be done using a TableModel, but I have yet to find a example which uses Vectors and not Object[][]. Any assistance with this would be appreciated.

DefaultTableModel has constructors and methods that take Vectors instead of Object[]s.
The old version of DefaultTableModel only used Vectors, the Object[] parameters are newer methods that were added around the time Generics came to Java.
http://docs.oracle.com/javase/tutorial/uiswing/components/table.html

When you create a table without providing it a model, it will have DefaultTableModel as it's default model. This model has two function:
setDataVector(Vector dataVector, Vector columnIdentifiers): Where dataVector is a Vector(which represents the data rows of table) of Vector and comlumnIdentifiers is Vector containing identifiers. It will show your table as you are providing the Vector.
addRow(Vector dataRow): it will add a data row to your dataVector as defined above.
So it is really simple to get the model and invoke these function:
DefaultTableModel model = (DefaultTableModel) table.getModel();
model.setDataVector(dataVector, comlnIdentifiers);
In your context, dataVector has the type vector<vector<string> >. But depending on Vector is not really a good choice. It is much safer and effective if your directly work with Object[]. The DefaultTableModel has similar function with Object array too.
setDataVector(Object[][] dataVector, Object[] columnIdentifiers)
addRow(Object[] rowData)
Check out the Tutorial page: How to Use Table to know many more things you can do with table and it's model.

This should work, but #jzd's answer is probably what you want, with the caveat that, according to the documentation, the column Vectors might be truncated or padded if their length does not match the number of columns you want in your table.
import javax.swing.*;
import javax.swing.table.*;
import java.util.*;
class test{
public static void main(String[] _) {
// Test data.
final Vector<Vector<String>> rows = new Vector<Vector<String>>();
for (int i = 0; i < 4; i++) {
Vector<String> row = new Vector<String>();
for (int j = 0; j < 5; j++) {
row.add(String.format("%s, %s", i, j));
}
rows.add(row);
}
// With AbstractTableModel, you only need to implement three methods.
TableModel model = new AbstractTableModel() {
public int getRowCount() {
return rows.size();
}
public int getColumnCount() {
return rows.elementAt(0).size();
}
public Object getValueAt(int row, int column) {
return rows.elementAt(row).elementAt(column);
}
};
// Test the TableModel in a JTable.
JFrame jf = new JFrame("test");
jf.setSize(512, 384);
jf.setContentPane(new JScrollPane(new JTable(model)));
jf.show();
}
}

have a look at GlazedLists - it'll save you a ton of time.
with it you can dynamically bind a JTable to a List of objects such that any change in the objects is reflected in the table and vice-versa.

Related

ArrayList updated inserting one row but Jtable still not refresh

I've to create a java swing app that allows the user to menage some Oracle tables.
I've implemented the JTable in this way:
public class TableModel extends AbstractTableModel{
.
.
.
public List <List <String> > result = null;
.
.
.
public void createTable(ResultSet r) throws SQLException {
rs = r;
numcols = getColumnCount();
result = new ArrayList();
while (rs.next()) {
List <String> row = new ArrayList<>(numcols);
for (int i=1; i<= numcols; i++)
row.add(rs.getString(i).replace("00:00:00.0", "")); //just managing stuffs .. nothing important. Don't care of this replace.
result.add(row);
}
fireTableStructureChanged();
}
public void addRow(){
List <String> row = new ArrayList<>();
for(int i=1;i<=getColumnCount();i++)
row.add("");
result.add(row);
fireTableStructureChanged();
}
public Object getValueAt(int row, int col) {
return result.get(row).get(col);
}
public int getColumnCount() {
if (rs == null) {
return 0;
}
try {
return rs.getMetaData().getColumnCount();
} catch (SQLException e) {
System.err.println("error in colcount");
return 0;
}
}
public int getRowCount() {
int totalRows = 0;
try {
rs.last();
totalRows = rs.getRow();
rs.beforeFirst();
} catch(Exception ex) {
return 0;
}
return totalRows ;
}
}
so, I store database data into the arraylist and then I print the table using the arraylist.
Now in the addRow() function I add to that arraylist an empty record (in my mind an empty record is a record composed of all null ("") strings).
Finally using the fireTableStructureChanged() method I expect the table to refresh itself.. but this will never occur and I don't understand why.
I've already checked that the new row is successfully loaded into the arraylist:
I got all the data stored in the table of the Database into the arraylist and the new row is successfully loaded in the arraylist.
so I swear the problem is about that fireTableStructureChanged() method.
Thanks :3
Try using fireTableRowsInserted(int firstRow, int lastRow) instead of fireTableStructureChanged()
Here's the javadoc for the structure changed ...
Notifies all listeners that the table's structure has changed. The number of columns in the table, and the names and types of the new columns may be different from the previous state. If the JTable receives this event and its autoCreateColumnsFromModel flag is set it discards any table columns that it had and reallocates default columns in the order they appear in the model. This is the same as calling setModel(TableModel) on the JTable.
So that one will only work if the columns types changed ...
If you are lazy go for this one: fireTableDataChanged()
I found the solution.
That's quite embarassing..
#tsolakp you helped me a lot!
Since before using ArrayList I was working on ResultSet directly, I forgot to update getrowcount.
So, since the inserting was only "client side" on the arraylist, the rows displayed were only "n" (where n was the number of rows on the database). So I would never displayed my new row since it was not loaded into the database.
That's all :)
Maybe working hard on coding, makes these errors invisible :X

Sort the contents of a JTable

I am working with a JTable That schould display Some informations sorted By the last two columns. The problem is the last two columns are filled with strings, one of them are Days of the Weak(Monday-Friday) the others are Hours(HH:mm), i would like to sort them going from Monday-Friday and if there are more elements of the same Day they schould be sorted by the Erliest Hour. Until now google was not a realy big help since it schows only ways to sorte something Alphabetically ore in Ascendant/descendant order for numbers, but i dont need an Alphabetically ordered JTable. Does anyone have an idea?
public class ScheduleFrame extends JFrame {
private JPanel contentPane;
private static JTable table;
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
ScheduleFrame frame = new ScheduleFrame();
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the frame.
* #throws Exception
*/
public ScheduleFrame(){
setTitle("Schedule");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 627, 405);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
contentPane.setLayout(new BorderLayout(0, 0));
setContentPane(contentPane);
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
JScrollPane scrollPane = new JScrollPane();
contentPane.add(scrollPane, BorderLayout.CENTER);
table = new JTable();
scrollPane.setViewportView(table);
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
table.setModel(new DefaultTableModel(
new Object[][] {
},
new String[] {
"Course Name", "Course Room","Course Day", "Course Hour"
}
));}
public static void loadTable()throws Exception{
DefaultTableModel tm = (DefaultTableModel) table.getModel();
BufferedReader bfw = new BufferedReader(new FileReader("Schedulecourses.txt"));
String line;
while( (line = bfw.readLine() ) != null ) {
tm.addRow( line.split("\t") );
}
bfw.close();
}
}
This is how it schould look like
one of them are Days of the Weak(Monday-Friday)
So the data in the TableModel could be stored as an Integer to represent the day of the week. Then you use a custom renderer to convert the Integer value to a descriptive value.
Read the section from the Swing tutorial on Using Custom Renderers for an example.
In your case the code in the setValue(...) method would be something like:
int dayOfWeek = (Integer)value;
switch (dayOfWeek)
{
case 1: setText("Monday"); break;
case 2: setText("Tuesday"); break;
case 3: ...
default: value.toString();
}
the others are Hours(HH:mm),
In this case you are storing two pieces of information. So this means you need to parse the data into two times and then create a custom Comparator to sort based on the first time.
Another option might be to create two columns: "Start Time", "End Time". Then you could store Date objects in the TableModel and then just use the default Comparator that will sort by Date.
Simply implement it yourself. Use some basic sorting algorithm, like quick sort, bubble sort or merge sort (as found on wikipedia), and create your own comparison function to put the entries in order, like, in pseudo code:
bool is_smaller(entry_1, entry_2){
if (entry_1.weekday < entry_2.weekday) return true;
if (entry_1.weekday > entry_2.weekday) return false;
if (entry_1.course hour < entry_2.course_hour) return true;
if (entry_1.course hour > entry_2.course_hour) return false;
return false;
}
(of course with another comparison function for weekday and course hour)
I suggest you try and map the data that you read from the file to some kind of a model object that you can later sort. For example:
public class Course implements Comparable<Course> {
private String name;
private String room;
private String day;
private String hour;
// constructor and getters are omitted. you can add setters as well, but it's best that you keep this class immutable
#Override
public int compareTo(Course course) {
// here you implement the logic of your comparison
}
}
The benefit here is that, by implementing the Comparable<T> interface provided by the Java API, you specify that the instances of your Course class have a natural ordering. Then you can use Collections.sort() and your list of courses will be automatically sorted in the way you want. Then you can use it to back your table model and render it in the table.
Edit 1:
A bit of a clarification on my suggestion:
Right now you read text data from a file, transform each row into an array of strings and pass it to a DefaultTableModel that supplies data to your JTable.
Instead of doing this, you can a bit more complexity to the code, but end up with a better solution from an architectural point of view. What are the steps:
Define a model class (like the example Course class) that will hold the data that is saved in your text file. Implement logic to transform each row of the file into an instance of the Course class. This instances will represent your data and you will use them to populate your table with data.
Implement your own TableModel that holds a sorted list of Course instances (those are the instances you read from the file). Use the implemented model to supply data to your JTable.

How to add rows on a JTable using AbstractTableModel?

I have a JTable with my own Model (extends AbstractTableModel), and I'd like to add a row to it when I Click a JButton.
I don't really know how to hadd the row to the Model.
here is my model:
public class MembersModel extends AbstractTableModel {
String[] columnNames = {
"Name",
"Money Spent",
"Percent",
"Current Deck"
};
Object[][] data = {
{"Cajo", new Integer(150), new Integer(0), "Event Deck"},
{"Sekiam", new Integer(200), new Integer(0), "Jeskay"},
{"Nuvas", new Integer(100), new Integer(0), "Big Shit"},
{"Dos", new Integer(100), new Integer(0), "Crap Deck"},
{"Atoj", new Integer(100), new Integer(0), "IDK"}
};
public MembersModel(){
super();
calcAllPercent();
}
public void calcAllPercent(){
for(int i = 0; i < data.length; ++i){
data[i][2] = calcPercetage((Integer) data[i][1]);
}
}
private int calcPercetage(int money){
return (money*100)/teamMoneySpent();
}
private int teamMoneySpent(){
int money = 0;
for(int i = 0; i < data.length; ++i){
money += (Integer) data[i][1];
}
return money;
}
public int getColumnCount() {
return columnNames.length;
}
public int getRowCount() {
return data.length;
}
public String getColumnName(int col) {
return columnNames[col];
}
public Object getValueAt(int row, int col) {
return data[row][col];
}
public Class getColumnClass(int c) {
return getValueAt(0, c).getClass();
}
public boolean isCellEditable(int row, int col) {
return false;
}
public void setValueAt(Object value, int row, int col) {
data[row][col] = value;
calcAllPercent();
fireTableRowsUpdated(0, 4);
}
}
Should i create my table aswell or add the method to the Model?
The key will be the data nucleus that you're using for your model, which here is your 2-D array, Object[][] data. The question then boils down to this: how do you add another row to the array and then notify the model's listener of the addition. While this can be done by creating a new data array with another row, copying all the data from this array, and adding the new data to the newly added row, why bother? For my money I'd
create a class to hold the data of a single table row, here I'll call it MyType, but you'll give it a better name, and it will have String, int, int, and String fields to correspond to the columns of your table.
Give this class some of the methods you have in your model above, such as doing row-specific calculations, calcPercentage(..), teamMoneySpent(...), and then the model can call the row object's method when this information is needed.
use an ArrayList<MyType> as my table model data nucleus, not a 2-dimensional hard-coded array.
give my model class an addRow(MyType myObj) method
in the method add to the ArrayList
and then call the appropriate model notification method, which here would be fireTableRowsInserted(...).
Note, I'm not sure what you mean by,
Should i create my table aswell or add the method to the Model?
Some problems with your current model:
The setValueAt() method is wrong. You should invoking tableCellUpdated(...). You don't want to repaint the data in the entire table when you update a single cell. Actually you don't even need to implement the setValueAt(...) method because your table is not editable.
You didn't override the getColumnClass(...) method. This method is required so the table can use the proper renderer to display the data.
Instead of creating a completely new TableModel, you could extend the DefaultTableModel. It already supports an addRow(...) method.
However, as hovercraft has already pointed out a better design is create a custom Object to store all the data of a single row. For a solution that uses this approach check out Row Table Model for a solution. The base TableModel is more complicated, but it makes it easier to create custom TableModels in the future since all the common code is in one class.
I would implement hovercraft's suggestion first so you better understand the concepts of creating a TableModel with a custom Object. I include this link as a suggestion for the future.

Object in JComboBox in JTable is not associated with same object in combo list

my problem is that when I put Object to table in cell with CellEditor set to work as JComboBox and it's fine, but when click on the cell i got list with Objects, but selected one is not that which were in cell before, but just first on the list. Is there simple way to fix it?
public void setValueAt(Object value, int row, int col) {
data.get(row).values.set(col, (Device) value);
fireTableCellUpdated(row, col);
}
and
for(int i = 0; i < deviceTable.getModel().getColumnCount(); i++){
ExtendedAbstractTableModel model = (ExtendedAbstractTableModel) deviceTable.getModel();
JComboBox<Device> combo = new JComboBox<Device>();
for(Device value : model.columnsCombo.get(i)){
combo.addItem(value);
}
TableColumn columnModel = deviceTable.getColumnModel().getColumn(i);
columnModel.setCellEditor(new DefaultCellEditor(combo));
}
As shown in this example, DefaultCellEditor handles this for you. You're adding multiple instances in a loop; a single instance can handle the entire column. DefaultCellEditor works by overriding setValue() in the nested EditorDelegate. It's not clear how you've defeated this feature, but understanding the default may guide your search.
public void setValue(Object value) {
comboBox.setSelectedItem(value);
}
Finally I found what was wrong. I didn't override equal method in my class, and that is why these components couldnt recognise same item. Anyway, thank you all.

How can i populate a JTable via a dynamic array or ArrayList fwith values from a database

I have 6 columns and a maximum of about 20 rows in the database. I like to make a general program in Java that populates the JTable with these values. I am very much conversant with using dynamic arrays, but not familiar with JTable attributes and model in Swing.
Can anyone guide me here on tho requirement? Thanks
You have to create your own table model. See the API documentation here: http://docs.oracle.com/javase/7/docs/api/javax/swing/table/TableModel.html
Basically, getColumnCount() would return your number of columns, getRowCount() the number of rows in your database, getColumnName(int columnIndex) some name for every column (the column name from the database or an arbitrary name, maybe from a constant string array). getColumnClass(int columnIndex) can, in the simple case, return String.class for every column. Then you have to convert every value to string.
getValueAt(int rowIndex, int columnIndex) has to return the value from the database for the given row and column. You should probably pre-load all these values in a 2D array or something like this (but that would be the answer to another question ;) ).
You can ignore the other methods for now, they are for editable tables.
Your code could look something like this:
String[][] tableData = readTableDataFromDatabase();
String columnNames = initColumnNames(); //From DB or constants
TableModel model = new DbTableModel(tableData, columnNames);
class DbTableModel extends AbstractTableModel {
private String[][] tableData;
private String[] columnNames;
public DbTableModel(String[][] tableData, columnNames) {
this.tableData = tableData;
this.columnNames = columnNames;
}
#Override
public Object getValueAt(int rowIndex, int columnIndex) {
return tableData[rowIndex][columnIndex];
}
#Override
public int getRowCount() {
return tableData.length;
}
#Override
public int getColumnCount() {
if(tableData.length == 0)
return 0;
return tableData[0].length;
}
#Override
public String getColumnName(int column) {
return columnNames[column]
}
}
This example assumes that you read the data from the database as a 2D array. If you use an O/R-Mapper (like the Java Persistence API - JPA), which I highly recommend, you would probably load a list of Entities. Each entity would contain the data for a table row. Anyway, the table model would not change much. Instead of accessing array values, you would get an entity object from the list and call "get"-Methods on the Entity.
There's more information in the Java tutorial: http://docs.oracle.com/javase/tutorial/uiswing/components/table.html#data
Nice but i hope readTableDataFromDatabase(); return resultset object
Have a look at the tutorial here: http://docs.oracle.com/javase/tutorial/uiswing/components/table.html#data
Here's a sample of a List-based read-only TableModel I wrote some time ago:
https://sourceforge.net/p/puces-samples/code/HEAD/tree/tags/sessionstate-1.0/sessionstate-suite/sessionstate-sample/src/blogspot/puce/sessionstate/sample/ParticipantTableModel.java

Categories