My GUI shows the vehicles in my park, and vehicles that I want to set availables in two different VehicleTables (classes that extend JTable). For availables I intend that these vehicles can be observed from an agent (third-part software). Both the tables show the descriptions of Vehicles in the rows...for this I have created VehicleTableModel and Vehicle classes. The Vehicle class is an abstract class and his subclasses are: Car, Truck, Trailer, etc. .
You can see a snapshot of my software:
My problems are these: In my current implementation I don't think of manage really good the updates of the rows. You can see in VehicleTableModel (fire...() methods) and in ShipperAgentGUI (coordinators and listeners). I think I have partially resolved this problem with the use of the Coordinator inner class for the updates between tables, but I don't know how optimize these. For example in case of delete or update of a row I make xxxTable.repaint(); ... the WHOLE table...
...another way?
ShipperAgentGUI.java
public class ShipperAgentGUI extends JFrame implements ActionListener {
// Graphics variables..
// bla bla...
// Headers, TableModels, JTables for the tables
private COLUMNS[] parkModelHeader = {COLUMNS.IMAGE_COLUMN, COLUMNS.TARGA_COLUMN,
COLUMNS.CAR_TYPE_COLUMN, COLUMNS.MARCA_COLUMN, COLUMNS.STATE_COLUMN, COLUMNS.PTT_COLUMN };
private COLUMNS[] availablesModelHeader = {COLUMNS.IMAGE_COLUMN, COLUMNS.TARGA_COLUMN,
COLUMNS.CAR_TYPE_COLUMN, COLUMNS.MARCA_COLUMN };
private VehicleTableModel parkModel = new VehicleTableModel(parkModelHeader);
private VehicleTableModel availablesModel = new VehicleTableModel(availablesModelHeader);
private VehicleTable parkTable;
private VehicleTable availablesTable;
// My third-part software, a JADE agent:
protected ShipperAgent shipperAgent;
// --------------------------------------------------------------------------
// CONSTRUCTOR
ShipperAgentGUI(ShipperAgent agent) {
shipperAgent = agent; // valorizes the agent
setTitle("Shipper Agent: "+agent.getLocalName()+" GUI");
// graphic bla bla...
// Park Table and Available Table:
parkTable = new VehicleTable(parkModel);
// bla bla...
availablesTable = new VehicleTable(availablesModel);
// bla bla...
// JButtons: add/remove vehicle in Park Table and Available Table
btnPM_plus = new JButton();
btnPM_plus.setToolTipText("Add vehicle");
btnPM_plus.setIcon(...);
btnPM_plus.setActionCommand("+park");
btnPM_plus.addActionListener(this);
// similar things for other three buttons:
// remove from parkTable, add and remove from availablesTable
//bla bla...
// Data from agent:
Vector<Vehicle> veicoli = shipperAgent.getVehicles();
Iterator<Vehicle> I = veicoli.iterator();
while (I.hasNext()){
addVehicle(parkCoordinator, I.next());
}
showGui();
}
///////////////////////////////////////////////////////////////////////
// Methods:
public void showGui() {
// bla bla
}
//////////////////////////////////////////////
// actionPerformed method
@Override
public void actionPerformed(ActionEvent e) {
switch (e.getActionCommand()) {
case "+park": {
new InsertVehicleJDialog(this, parkCoordinator);
} break;
case "-park": {
int selectedRow = parkTable.getSelectedRow();
if (selectedRow != -1)
removeVehicle(parkCoordinator, selectedRow);
} break;
case "+available": {
int selectedRow = parkTable.getSelectedRow();
if (selectedRow != -1){
addVehicle(availablesCoordinator, parkModel.getVehicleAt(selectedRow));
}
} break;
case "-available": {
int selectedRow = availablesTable.getSelectedRow();
if (selectedRow != -1)
removeVehicle(availablesCoordinator, selectedRow);
} break;
}
}
///////////////////////////////////////
// Add/Remove vehicle methods:
void addVehicle(Coordinator coordinator, Vehicle v) {
coordinator.notifyAndAddRow(v);
}
// mhm...
void removeVehicle(Coordinator coordinator, Vehicle v) {
int row = coordinator.indexOf(v);
if (row!=-1)
coordinator.notifyAndDeleteRow(row);
}
void removeVehicle(Coordinator coordinator, int index) {
coordinator.notifyAndDeleteRow(index);
}
// on dispose, delete the agent
public void dispose() {
super.dispose();
shipperAgent.doDelete();
}
///////////////////////////////////////
// INNER CLASS COORDINATOR:
protected abstract class Coordinator {
private VehicleTableModel tableModel;
public Coordinator(VehicleTableModel tm) {
tableModel = tm;
notifyRowUpdated();
}
public abstract void notifyAndAddRow(Vehicle vehicle);
public abstract void notifyAndDeleteRow(int rowIndex);
public abstract void notifyRowUpdated();
public int indexOf(Vehicle v) {
return tableModel.indexOf(v);
}
boolean vehicleExists(Vehicle vehicle){
int bool = indexOf(vehicle);
if (bool==-1) return false;
else return true;
}
}
// Coordinator for parkTable
Coordinator parkCoordinator = new Coordinator(parkModel) {
@Override
public void notifyAndAddRow(final Vehicle vehicle) {
if (!vehicleExists(vehicle)){ // is this the right control? Or in VehicleTableModel ?
shipperAgent.newTruck(vehicle.getPlate());
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
parkModel.addRow(vehicle);
if (vehicle.getState().equals(Stato.DISPONIBILE))
availablesModel.addRow(vehicle);
// or with availablesCoordinator.notifyAndAddRow(vehicle) ?
// or with addVehicle(availablesCoordinator, vehicle) ?
// or with a kind of listener on vehicle's state ?
}
});
}
}
@Override
public void notifyAndDeleteRow(final int rowIndex) {
final Vehicle v = parkModel.getVehicleAt(rowIndex);
removeVehicle(availablesCoordinator, v); // Remove also from the "availables"
shipperAgent.removeTruck(v.getPlate());
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
parkModel.removeRow(rowIndex);
}
});
}
@Override
public void notifyRowUpdated() {
parkModel.addTableModelListener(new TableModelListener() {
public void tableChanged(TableModelEvent e) {
switch (e.getType()) {
case (TableModelEvent.DELETE):
parkTable.repaint();
break;
case (TableModelEvent.UPDATE):
int row = e.getLastRow();
Vehicle v = parkModel.getVehicleAt(row);
if (v.getState().equals(Stato.DISPONIBILE)){
addVehicle(availablesCoordinator, v);
availablesTable.repaint();
} else
removeVehicle(availablesCoordinator, v);
parkTable.repaint();
break;
}
}
});
}
};
// Coordinator for availablesTable
Coordinator availablesCoordinator = new Coordinator(availablesModel) {
@Override
public void notifyAndAddRow(final Vehicle vehicle) {
if (!vehicleExists(vehicle)){ // is this the right control? Or in VehicleTableModel ?
vehicle.setStato(Stato.DISPONIBILE);
parkTable.repaint();
shipperAgent.activateTruck(vehicle.getPlate());
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
availablesModel.addRow(vehicle);
}
});
}
}
@Override
public void notifyAndDeleteRow(final int rowIndex) {
Vehicle v = availablesModel.getVehicleAt(rowIndex);
if (v!=null){
v.setStato(Stato.NON_DISPONIBILE); // mhm
shipperAgent.deactivateTruck(v.getPlate());
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
availablesModel.removeRow(rowIndex);
}
});
}
}
@Override
public void notifyRowUpdated() {
availablesModel.addTableModelListener(new TableModelListener() {
public void tableChanged(TableModelEvent e) {
switch (e.getType()) {
case (TableModelEvent.DELETE):
parkTable.repaint();
break;
case (TableModelEvent.UPDATE):
parkTable.repaint();
break;
}
}
});
}
};
}
VehicleTableModel.java
public class VehicleTableModel extends AbstractTableModel {
private ArrayList<Vehicle> vehicles ;
private COLUMNS[] header;
// possible column names:
public enum COLUMNS {
IMAGE_COLUMN,
TARGA_COLUMN,
CAR_TYPE_COLUMN,
MARCA_COLUMN,
STATE_COLUMN,
PTT_COLUMN,
};
///////////////////////////////////////////////////////
// Constructor:
public VehicleTableModel(COLUMNS[] headerTable) {
this.vehicles = new ArrayList<Vehicle>();
this.header = headerTable;
}
///////////////////////////////////////////////////////
// obligatory override methods (from AbstractTableModel):
@Override
public int getColumnCount() {
return header.length;
}
@Override
public int getRowCount() {
return vehicles.size();
}
@Override
public Object getValueAt(int row, int col) {
Object value = "?";
Vehicle v = vehicles.get(row);
if (v!=null) {
COLUMNS column = header[col];
switch (column) {
case IMAGE_COLUMN:
value = VehicleUtils.findImageByColumnCarType(v.getType());
break;
case TARGA_COLUMN:
value = v.getPlate();
break;
case CAR_TYPE_COLUMN:
value = VehicleUtils.findStringByColumnCarType(v.getType());
break;
// other cases... bla bla...
}
}
return value;
}
///////////////////////////////////////////////////////
// My methods:
public void addRow(Vehicle vehicle) {
vehicles.add(vehicle);
fireTableRowsInserted(0, getRowCount()); // is right?
}
/*public boolean removeRow(Vehicle vehicle) {
boolean flag = vehicles.remove(vehicle);
fireTableRowsDeleted(0, getRowCount()); // is right?
return flag;
}*/
public void removeRow(int row) {
vehicles.remove(row);
fireTableRowsDeleted(row, row); // is right?
}
public Vehicle getVehicleAt(int row) {
return vehicles.get(row);
}
public int indexOf(Vehicle v){
return vehicles.indexOf(v);
}
// found the corresponding column index
public int findColumn(COLUMNS columnName) {
for (int i=0; i<getColumnCount(); i++)
if (columnName.equals(header[i]))
return i;
return -1;
}
// a value in that column exist in the table?
private boolean controllIfExist(Object value, int col) {
boolean bool = false;
for (int i=0; i<getRowCount();i++){
if (value.equals(getValueAt(i, col))){
bool=true;
break;
}
}
return bool;
}
public int getColumnIndex(COLUMNS column){
for(int i=0;i<header.length;i++){
if (column.equals(header[i])){
return i;
}
}
return -1;
}
///////////////////////////////////////////////////////
// other methods (from AbstractTableModel) to override:
@Override
public Class<?> getColumnClass(int col) {
Class<?> c;
COLUMNS column = header[col];
if (column.equals(COLUMNS.IMAGE_COLUMN))
c = ImageIcon.class;
else if (column.equals(COLUMNS.STATE_COLUMN))
c = JComboBox.class;
else c = super.getColumnClass(col);
return c;
}
@Override
public String getColumnName(int col) {
COLUMNS column = header[col];
if (column.equals(COLUMNS.IMAGE_COLUMN))
return " ";
else if (column.equals(COLUMNS.TARGA_COLUMN))
return "Targa";
// others... bla bla...
return super.getColumnName(col);
};
@Override
public boolean isCellEditable(int row, int col) {
return true;
}
@Override
public void setValueAt(Object value, int row, int col) {
Vehicle v = vehicles.get(row);
boolean flag = false;
if (v!=null) {
COLUMNS column = header[col];
switch (column) {
case TARGA_COLUMN:
if (!v.getPlate().equals(value)){
if (!controllIfExist(value, col)){ // mhm...
v.setPlate((String) value);
flag = true;
}
}
break;
case MARCA_COLUMN:
if (!v.getMark().equals(value)){
v.setMark((String) value);
flag = true;
}
break;
// others ... bla bla...
}
// update ONLY if necessary:
if (flag) fireTableRowsUpdated(row, row); // is right?
}
}
}