I have some data I have to show through two JTables; the data is the same, but each table will have to show it a little differently.
Also, I receive the data from an external connection (JMS in this case, but it doesn't really matter, it could be a DB, or whatever).
Being that I am new to Swing, I am still a little confused about who should fire the events, who should listen to them and how to make so that for a modification to my dataset I will have both the tables to update.
Now, a little example of my dataset structure as well as some dummy data:
class Student{ String name; Classroom classroom; boolean goodStudent}
class Classroom{ Sting name; List<String> coursesTaught; List<Student> students;}
public List<Classroom> classes;
Basically, my dataset will be the classes field in a Controller class and the two JTables will have to show things in different ways.
Namely, Table1 will have to show something like:
Classroom Name | Courses
4a | CS101, CS102, CS103
4b | BM101, CS102
4c | I101, CS4100
So basically, for each Classroom, the list of courses.
Table2 should instead show things like:
Student Name | Good?
Mark Spencer | true
Philippe Mann | true
Tom Sayer | false
I should see ALL the students, from all classrooms.
As you can see, the data is the same, but it is shown in different way.
What I would like to do is that, when my data changes, the tables will automatically update too.
For what I understood so far, I will have to subclass AbstractTableModel and create two different TableModels for the kind of data I want to show; what I don't get is:
How will the Models get their data, once some change happens?
Who should notify the models of this change?
Is it enough to call "fireTableXXXEvent()" to trigger the view refresh?
I hope I made myself clear enough...
In any case, thank you very much!
Bye
#StanislavL is right about needing a TableModel for each JTable, but nothing says they can't usefully descend from a common, abstract parent. In the (somewhat contrived) outline below, the two models share a common getColumnCount() implementation, while the concrete children implement the remaining required TableModel methods.
abstract class SchoolModel extends AbstractTableModel {
#Override
public int getColumnCount() { return 2; }
}
class ClassroomModel extends SchoolModel {
#Override
public int getRowCount() {…}
#Override
public Object getValueAt(int rowIndex, int columnIndex) {…}
}
class StudentModel extends SchoolModel {
#Override
public int getRowCount() {…}
#Override
public Object getValueAt(int rowIndex, int columnIndex) {…}
}
You are right. You need 2 AbstractTableModels.
Suppose you have the list List classesList classes as main data source. The first model row count will just return size() of the list. The second one will return sum of Students counts for each classroom. Column count for both models is 2. The inteeresting methods are getValueAt/setValueAt you have to find proper row. For the first list it's easy just appropriate list item. For the second table model you have to calculate proper row and then iterate through the list of Students.
Suppose something is changed in DB. You retrieve a new List. You can either find what's changed, which rows/cols were inserted/removed/changed or just replase the data source list in both models and fire structure changed event to let JTable completely refresh content.
Thre is no automatic update of models. You can write e.g. timer to check the changes each second/minute/hour or refresh on reopening the dialog/frame where the table is shown.
Related
This is more of a "Design" or "Conception" sort of question.
So I have a simple problem; I want to print some workers info in an Excel sheet.
But I want to do it in a way that is easy to change in the future, let me explain : for now, people just want to see last name, first name and address in that excel table. But, what if all of a sudden they want more ? Or less ? How to add or remove a column (that actually refers to a field in the Worker class) hassle free ?
In a simple picutre, I want a simple system to go from this (these would be excel sheets) :
| first name | age | job |
-----------------------------------
| joe | 26 | developer |
| mary | 25 | tester |
to this :
| first name | last name | status | adress |
---------------------------------------------------------
| joe | johnson | employee | 8 sun street |
| mary | hoover | bos | 6 moon street |
So my class is Worker, I thought about making a class that is called WorkerTabular that would have a List of java.lang.reflect.Field references in it, and then I can check, but I don't want to break the encapsulation, that would kind of defeat the purpose of making an "easily variable system", if all of a sudden we tie to the implementation and oversee getters. So instead I thought of storing the references to the getter methods in this List of columns. But how would I call that function reference I stored on an instance of Worker?
Something like (using the builder pattern) WorkerTabular().addColumn(Worker::getName).addColumn(Worker::getHiringDate) and then, in a third class like ExcelMaker do something like worker.call(Worker::getName) to get the name.
I want to keep things as segregated as possible to make a truly reusable thing, by leaving the Worker Entity untouched, encapsulating the tabular data we want in the WorkerTabular, and the actual work of making the Excel stuffs in the ExcelMaker class.
Am I missing a well known pattern to do all this ? After all, making a kind of "variable excel sheet" must not be a new problem.
In other languages like Javascript, I can kind of see how that would be done, WorkerTabular would be made with that builder pattern just that it's a List of Strings, and then in ExcelMaker we would just do worker[listElement] while iterating on the list of attributes we chose to be in the Excel sheet. But in Java, I can't really see a clean and "Javaic" way to do it.
I'm sorry if I'm asking a stupid question.
EDIT 1 :
Thanks to Justin Albano for his nice answer :).
You really nailed it I think with the usage of the interface, even the fact that not every field is a String can be handled in the Implementation of TableEntry, by doing the conversion there (Dates to Strings, Ints to Strings, etc). There is a bit of coupling between TableEntry and Worker, but it's kept there and TableBuilder can really just concentrate of it's job of iterating over the List of Strings and build the Excel file.
I'm having a bit of trouble figuring out how to add a "columns titles" line to the TableBuilder, but I guess I'll just do it like this :
class TableBuilder {
List<String> columnTitles;
String tableTitle;
bytes[] build(List<TableEntry> lte) {
// make first row, make title & iterate over entries
// then do miscellaneous things like set the correct merged region for title, etc
}
}
And initialize those fields with a static factory or builder.
Actually this problem arose :
there are two types of Workers, and they have two fields : email and work_email.
A type of worker has both, and the other type has only work_email. So with my implementation I'm screwed ! I would have to put BOTH fields in the Excel table,
and one would be empty for the other type of worker, and people would complain !
Or I would need to split the function generating the Excel file in two thus duplicating a lot of code, or handle this little weird case in the function making it really ugly.
With the implementation with Entry interface to represent data, I can get around this easy ! I get the feeling that it's a bit "overengineered" and certainly my colleagues won't like stumbling through this implementation I think (the codebase is more of a "straight to the point and ugly don't matter" kind of thing), but it's also very clean I feel.
Moreover, the class TableBuilder could build an excel file for any Thing, as long as an implementation class of ThingEntry has been made. That's pretty sweet !
Maybe the only real "issue" I see with it is that 2n objects need to be created now, versus just the n of the 'ugly' implementation. But I feel that in 2018 that's not too big of an issue, right ?
A simple way to allow the internal representation of Worker to vary independently of the tabular representation of the Worker is to create a separate class for its representation. For example, given the following definition of Worker
public class Worker {
private final String firstName;
private final int age;
private final String job;
public Worker(String firstName, int age, String job) {
this.firstName = firstName;
this.age = age;
this.job = job;
}
public String getFirstName() {
return firstName;
}
public String getAge() {
return age;
}
public String getJob() {
return job;
}
}
the following wrapper can be created:
public interface TableEntry {
public List<String> getColumnValues();
}
public class WorkerTableEntry implements TableEntry {
private final Worker worker;
public WorkerEntry(Worker worker) {
this.worker = worker;
}
#Override
public List<String> getColumnValues() {
List<String> columns = new ArrayList<>();
columns.add(worker.getFirstName());
columns.add(String.valueOf(worker.getAge()));
columns.add(worker.getJob());
return columns;
}
}
This would now allow another class, TableBuilder to depend only on the TableEntry interface, not the internals of Worker:
public class TableBuilder {
public String buildTable(List<TableEntry> entries) {
// Print header
for (TableEntry entry: entries) {
List<String> columns = entry.getColumnValues();
// Print each column
}
}
}
Then the TableBuilder can be called as follows:
List<Worker> workers = ...
List<TableEntry> entries = new ArrayList<>();
for (Worker worker: workers) {
entries.add(new WorkerTableEntry(worker));
}
TableBuilder builder = new TableBulder();
builder.buildTable(entries);
This ensures that if the fields or methods of Worker change, its representation in the table does not necessarily change; and vice-versa, if the representation in the table needs to change, that the Worker class does not necessarily need to change (the two are independent). If other tables need to be built, you can simply create more implementations of TableEntry and reuse the TableBuilder class.
If you wanted to store the getters of the Worker class as the means of building the table, you can do so, although doing so is a bit more complicated. At its most basic level, the getters are Supplier<String> objects, meaning that take no arguments but produce a single String value. For example, you can develop a VariableTableEntry in the following manner:
public class VariableTableEntry {
private List<Supplier<String>> columnSuppliers;
public void addColumn(Supplier<String> supplier) {
columnSuppliers.add(supplier);
}
public String getRow() {
for (Supplier<String> columnSupplier: columnSuppliers) {
String columnValue = columnSupplier.get();
// Print each column
}
}
}
The difficult part is that not every getter will return a String. Some, like getAge(), will return an int. To be able to print those getter references as a String, a conversion would have to be performed (this method would be present in the VariableTableEntry class):
public void addIntColumn(Supplier<Integer> supplier) {
addColumn(() -> String.valueOf(supplier.get()));
}
This essential just wraps the supplied getter in a lambda expression that returns a String instead of an Integer/int. This process would be repeated for each of the other primitive values. Any Object (such as Worker) can be handled by deferring to toString:
public void addObjectColumn(Supplier<Object> supplier) {
addColumn(() -> supplier.get().toString());
}
If I have understood correctly, then Worker is your POJO and you are trying to create the list of POJOs and dumping in the excel sheet..So, what's the issue if person can add/remove the attributes in that bean class and corresponding getters/setters?
I have a custom AbstractTableModel
That model stores the data in a HashMap. So for my method for getValueAt(int rowIndex, int columnIndex)
I do
new ArrayList<Object>(data.values()).get(index);
However my data has over 2000 entries, so doing this every single time whenever I have to get the data for my table creates a huge performance hit.
So what solution can you recommend?
Should I try using List to store all my data in instead of HashMap?What is the accepted standard for storing data when using table models?
Thanks to anyone for their suggestion, and I aplogize for what might be a stupid question, but I am not too great when it comes to tables and how to store data in them.
A HashMap doesn't generally make a good fit for a table model because the table needs the ability to access data at an row/col location.
A ArrayList of ArrayLists is a reasonable way to store a table model. This still gives you fast access. Getting to a particular row is a constant time lookup, and then getting the column is also a constant time lookup.
If you don't want the overhead of the lists, you can always store the data in a 2D array.
Yes, the code you sight is going to suck in performance terms - for every cell you render, you're creating a new ArrayList based on the values in your Map (you can do the math).
At the very least, do the list creation once, probably in the constructor of your table model, like this (which assumes you've got some arbitary object, that you don't mention in your question, as the values of the map):
public class MyTableModel extends AbstractTableModel
{
private static final int COLUMN_0 = 0;
private static final int COLUMN_1 = 1;
private List<MyObject> data;
public MyTableModel(Map<?, MyObject> data)
{
this.data = new ArrayList<MyObject>(data.values());
}
public Object getValueAt(int rowIndex, int columnIndex)
{
switch (columnIndex)
{
case COLUMN_0: return this.data.get(rowIndex).getColumn0();
case COLUMN_1: return this.data.get(rowIndex).getColumn1();
...
case COLUMN_N: return this.data.get(rowIndex).getColumnN();
}
throw new IllegalStateException("Unhandled column index: " + columnIndex);
}
}
I am designing a system which assembles disperate data in a standard row/column type output.
Each column can:
Exist in an independent system.
Can be paginated.
Can be sorted.
Each column can contain millions of rows.
And the system:
Needs to be extensible so different tables of different columns can be outputted.
The final domain object is known (the row).
The key is constant across all systems.
My current implementation plan is to design two classes per column (or one class column that implements two interfaces). The interfaces would:
Implement a pagination and sorting.
Implement "garnishing"
The idea is that the table constructor would receive information about the current sort column and page. Which would then return a list of appropriate keys for the table. This information would be used to create a list of the domain object rows which would then be passed in turn to each of the column "garnishing" implementations so that each columns information could be added in turn.
I guess my question is - what design patterns would be recommended - or alternative design decisions would people use for assembling disperate data with common keys and variable columns.
I'm not sure if I completely understood what you're trying to do, but from what I gather, you want to store rows of arbitrary data in a way that will allow you to make structured tables from it later on. What I would do in this case (assuming you're using Java) is make a very simple Column interface that would just have a "value" property:
public interface Column {
String value;
}
Then, you could make columns by implementing Column:
public class Key implements Column {
String value = new String();
public Key(String keyValue){
this.value = keyValue;
}
}
So then you can make a class called DataRow (or whatever you like) whose objects would contain the actual data. For example, you could have a method in that class that would allow you to add data:
public class DataRow {
List<Column> data = new ArrayList<Column>();
public DataRow(String key){
this.setColumn(new Key(key));
}
public void setColumn(Column columnData) {
this.data.add(columnData);
}
public Column getColumn(Class column){
for(Column c : this.data){
if(c.getClass().equals(column)){
return c;
}
}
return null;
}
}
As you can see, you can call the method setColumn() by giving it a new Column object. This will allow you to add any data you like of any type to the DataRow Object. Then, to make some tables, you could have a function that takes a List of DataRows, and a List of classes, that would then return only the objects which have data from the row specified:
public List<DataRow> createTable(List<DataRow> data, List<Class<? extends Column>> columns){
List<DataRow> table = new ArrayList<DataRow>();
for(DataRow row : data){
DataRow ret = new DataRow(row.getColumn(Key.class).value);
for(Class column : columns){
if(row.getColumn(column.getClass()) != null )ret.setColumn(row.getColumn(column.getClass()));
}
table.add(ret);
}
return table;
}
This will allow you to "create" tables using your data, and the columns you want to include in the table.
Note that I wrote this code to convey an idea, and that it's pretty messy at the moment. But I hope this will help you in some small way.
I have question that how can I delete all datas from my jTable in GUI when a user entered a key?
thanks
You can set a new empty data model:
TableModel newModel = new DefaultTableModel();
jtable.setModel(newModel);
You need to understand that a JTable is a view of the data, while the actual data resides in the TableModel. If you need to clear out the table, then you need to clear out the TableModel.
If your TableModel is an AbstractTableModel, you must provide implementations of 3 methods:
public int getRowCount();
public int getColumnCount();
public Object getValueAt(int row, int column);
Frequently the actual data objects are stored in an additional data structure (e.g. a list), and then the AbstractTableModel queries that list.
List<DomainObject> objects = new ArrayList<DomainObject>();
public int getRowCount() { return objects.size(); }
// How many columns you make depends on what features of the objects you're exposing.
public int getColumnCount() { return NUMBER_OF_COLUMNS; }
public Object getValueAt(int row, int column) {
DomainObject object = objects.get(row);
... // pull out the property based on the column they pass in
}
// By exposing this method, you can allow your Controller code to reach into this model
// and delete all the rows.
public void clear() {
objects.clear()
}
What HH is suggesting you do is change the model of your JTable to reference an empty model, which will in effect clear out the table. However, the columns etc. will not be persisted correctly (the new DefaultTableModel has no idea what those column names would be).
After you've researched how the view and model fit together more, take a look at GlazedLists. It allows a very powerful way to create TableModels which provide dynamic views of your data, e.g. by filtering out rows that do not match certain criteria.
To sum up - you're not going to find a method on the JTable to clear out its contents, because that's the job of the TableModel. You need some way of ensuring that the TableModel's backing data structures are cleared out.
If you are using the DefaultTableModel then you can just use:
model.setRowCount(0);
This is better than creating a new DefaultTableModel. Creating a new TableModel causes the TableColumnModel to be recreated, which means all the TableColumns will be resize to default values and recreated in the order in which the columns exist in the model. The user may have changed these properties and shouldn't be forced to do it again.
If you are just deleting certain rows that contain a particulsar value, then you can use the DefaultTableModel.removeRow(...) method. Make sure you start by deleting row from the end of the model and count down to 0.
call removeAll of j_table method at addActionListener
button1.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
j_table.removeAll();
data_model_table.setRowCount(0);
}
});
There is a jbutton in my Jpanel. When I clicked it, it loads up my Jtable, sometimes a query return so many records (500 rows). So I want to restrict it to 5 records.
When query return I want to count it; if it's higher than 5 then Jtable shows up only first 5 record, when user click Forward button it will shows up next 5 record. When user click Back button it will show previous 5 record.
How can I do this? Is there any example for this with TableModel?
I suggest implementing a "Paged" TableModel which provides a window onto the entire dataset and methods for moving forwards and backwards throughout the data. This way you do not require two Lists to store the data but rather a single List holding all data along with a marker to your current position; e.g.
public class ImmutablePagedTableModel extends AbstractTableModel {
private final List<MyBusinessObject> allData;
private final int pageSize;
private int pos;
public ImmutablePagedTableModel(List<MyBusinessObject> allData) {
// Copy construct internal list. Use ArrayList for random access look-up efficiency.
this.allData = new ArrayList<MyBusinessObject>(allData);
}
/**
* Returns true if the model has another page of data or false otherwise.
*/
public boolean hasNextPage() {
return pos + pageSize < allData.size();
}
/**
* Flips to the next page of data available.
*/
public void nextPage() {
if (hasNextPage()) {
pos += pageSize;
// All data in the table has effectively "changed", so fire an event
// causing the JTable to repaint.
fireTableDataChanged();
} else {
throw new IndexOutOfBoundsException();
}
}
public int getRowcount() {
return Math.min(pageSize, allData.size() - pos);
}
// TODO: Implement hasPreviousPage(), previousPage();
}
As 00rush mentions a more ambitious approach would be to use a SwingWorker to stream in the data in the background. You could still use the paged TableModel approach for this; you'd just need to ensure that appropriate TableModelEvents are fired as you append to the end of the allData list.
If you wish to load a large table, you may want to use a SwingWorker (details here) thread to load the table in the background. Loading a table with 500 rows should not be a problem. You can then put the data into a suitable object format and pass it to your TableModel.
If you decide to use a List for example, in your table model you could have two lists:
List allData
List viewData
int startIndex
The viewData list is what is referenced by the getValueAt(..) method in your implementation of the TableModel interface. The viewData list is always a subset (bound by startIndex, of length 5) of allData. When the user clicks "Next", your action listener could call a method on the Table model that increments startIndex by 5 (or whatever). You then regenerate your viewData instance so that it is the appropriate 5 row subset of allData, and call fireTableChanged(). This will be easy if you have extended AbstractTableModel in the first place.
This should be pretty straightforward to implement. I think its better than making a database call every time you want to get the next set of data. IMHO, its better to take a little bit more time upfront to preload the data.