Utilisation d’une JTable avec Rest
Cet article est la suite de l’article implémentant un Java client REST.
Dans cet article nous allons voir l’utilisation d’une JTable en Swing en utilisant le protocle REST pour l’échange des données.
Une JTable est donc un composant Swing permettant d’afficher un tableau formé d’un certain nombre de lignes et d’un certain nombre de colonnes. En plus des lignes de contenu, la JTable a également une ligne d’en-tête présentant un titre pour chaque colonne.
Une JTable a donc d’un côté des données et de l’autre des données d’en-tête. On peut voir les données comme un tableau à deux dimensions dans lequel chaque valeur correspond à la valeur d’une cellule du tableau. Quant aux en-têtes, on peut les voir comme un tableau de chaînes de caractères.
La JTable utilise différents concepts de Swing :
– Un modèle pour stocker les données. Un JTable utilise une classe implémentant TableModel. Nous verrons plus loin comment spécifier un modèle pour une JTable.
– Un renderer pour le rendu des cellules. On peut spécifier un TableCellRenderer pour chaque classe de données.
– Un éditeur pour l’édition du contenu d’une cellule. On peut spécifier un TableCellEditor pour chaque classe de données.
Une chose indispensable à faire lorsqu’on utilise des JTable est d’utiliser un modèle de tableau pour stocker les données. Il faut donc créer une classe étendant TableModel. En pratique, on implémente rarement directement TableModel, mais on hérite plutôt d’AbstractTableModel et l’on redéfinit les méthodes nécessaires. Pour commencer, voici les méthodes qu’il faudra redéfinir pour notre modèle de données statique :
int getRowCount() : doit retourner le nombre de lignes du tableau
int getColumnCount() : doit retourner le nombre de colonnes du tableau
Object getValueAt(int rowIndex, int columnIndex) : doit retourner la valeur du tableau à la colonne et la ligne spécifiées
String getColumnName(int columnIndex) : doit retourner l’en-tête de la colonne spécifiée
On va donc créer notre modèle en reprenant l’objet Product de notre article précédent et nous implémenterons PropertyChangeListener pour communiqué avec notre thread ConnectionRest :
public class ProductTableModel extends AbstractTableModel implements PropertyChangeListener{
private String[] columnNames = {"Id", "Nom", "Type", "Prix"};
private ArrayList<Product> listProducts = new ArrayList<Product>();
private ConnectionRest connectionRest = null;
public ProductTableModel(){
super();
}
public void addLine(){
try {
JSONObject jsonProduct = new JSONObject();
jsonProduct.put("name","<NAME>");
jsonProduct.put("type","<TYPE>");
jsonProduct.put("price", 1.0);
connectionRest = new ConnectionRest("POST");
connectionRest.setObj(jsonProduct);
connectionRest.addPropertyChangeListener(this);
connectionRest.start();
} catch (JSONException e) {
e.printStackTrace();
}
}
public void refreshProducts() {
connectionRest = new ConnectionRest("GET");
connectionRest.addPropertyChangeListener(this);
connectionRest.start();
}
public void propertyChange(PropertyChangeEvent evt) {
if(evt.getSource().equals(connectionRest)){
if(evt.getPropertyName().equals("GET")){
listProducts = (ArrayList<Product>)evt.getNewValue();
}else if(evt.getPropertyName().equals("POST")){
String newID = (String)evt.getNewValue();
int id = Integer.parseInt(newID.trim());
Product product = new Product(id);
if(listProducts == null) {
listProducts = new ArrayList();
}
listProducts.add(product);
}
connectionRest.removePropertyChangeListener(this);
fireTableChanged(null); // Dis aux classes qui écoutent qu'une nouvelle table est arrivé.
}
}
public void setValueAt(Object value, int row, int column) {
try {
Product product = null;
if(row < listProducts.size()){
product = listProducts.get(row);
switch (column) {
case 1:
product.setName((String) value);
break;
case 2:
product.setType((String) value);
break;
case 3:
product.setPrice((Double) value);
break;
default:
break;
}
//Envoie le produit qui a ete modifier
listProducts.set(row, product);
JSONObject jsonProduct = new JSONObject();
jsonProduct.put("id",product.getId());
jsonProduct.put("name",product.getName());
jsonProduct.put("type",product.getType());
jsonProduct.put("price", product.price());
connectionRest = new ConnectionRest("PUT");
connectionRest.setObj(jsonProduct);
connectionRest.addPropertyChangeListener(this);
connectionRest.start();
}
} catch (JSONException e) {
e.printStackTrace();
}
}
public Class getColumnClass(int c) {
if(getValueAt(0, c)!=null){
return getValueAt(0, c).getClass();
}else{
return null;
}
}
public int getColumnCount() {
return columnNames.length;
}
public String getColumnName(int column) {
if (columnNames[column] != null) {
return columnNames[column];
} else {
return "";
}
}
public int getRowCount() {
if (listProducts!=null)
return listProducts.size();
else
return 0;
}
public Object getValueAt(int aRow, int aColumn) {
Product obj = null;
if(aRow < listProducts.size()){
obj = listProducts.get(aRow);
}
if(obj!= null){
return obj.elementAt(aColumn);
}else{
return null;
}
}
public boolean isCellEditable(int row, int column) {
if (column != 0){
return true;
}
return false;
}
public Product get(int gRow) {
return listProducts.get(gRow);
}
public void remove(int gRow) {
try {
JSONObject jsonProduct = new JSONObject();
jsonProduct.put("id",((Product)listProducts.get(gRow)).getId());
connectionRest = new ConnectionRest("DELETE");
connectionRest.setObj(jsonProduct);
connectionRest.addPropertyChangeListener(this);
connectionRest.start();
listProducts.remove(gRow);
} catch (JSONException e) {
e.printStackTrace();
}
}
}
Nous intégrerons ce TableModel à une fenêtre contenant une JTable :
public class RestJFrame extends JFrame implements ActionListener{
private static final long serialVersionUID = 1L;
private JTable table = null;
private ProductTableModel productTableModel = null;
private JScrollPane scrollPane = null;
private JButton boutonAdd = null;
private JButton boutonDel = null;
public RestJFrame(){
initialize();
refresh();
this.setVisible(true);
}
@Override
public void actionPerformed(ActionEvent e) {
if (e.getSource().equals(boutonAdd)) {
productTableModel.addLine();
}else if (e.getSource().equals(boutonDel) && table.getSelectedRow()>-1) {
productTableModel.remove(table.getSelectedRow());
}
}
private void refresh(){
productTableModel.refreshProducts();
table.updateUI();
}
private void initialize() {
try {
UIManager.setLookAndFeel(new NimbusLookAndFeel());
this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
this.setSize(320, 240);
this.setLocationRelativeTo(null);
this.setVisible(true);
table = new JTable();
productTableModel = new ProductTableModel();
table.setModel(productTableModel);
table.setAutoCreateRowSorter(true);
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
scrollPane = new JScrollPane(table);
scrollPane.setBorder(new BevelBorder(BevelBorder.LOWERED));
JPanel boutons = new JPanel();
boutonAdd = new JButton("Ajouter");
boutonDel = new JButton("Supprimer");
boutons.add(boutonAdd);
boutons.add(boutonDel);
getContentPane().add(scrollPane, BorderLayout.CENTER);
getContentPane().add(boutons, BorderLayout.SOUTH);
boutonAdd.addActionListener(this);
boutonDel.addActionListener(this);
} catch (Exception exc) {
exc.printStackTrace();
}
}
public static void main(String[] args) {new RestJFrame();}
}
Vous devrez modifier votre ConnectionRest en y ajoutant un PropertyChangeSupport et apporter les modifications a votre méthode run() comme ci dessous :
public class ConnectionRest extends Thread{
private PropertyChangeSupport ptChSupport = new PropertyChangeSupport(this);
public void run(){
try {
if(methode.equals("GET")){
ArrayList<Product> listProduct = parse(get(methode));
ptChSupport.firePropertyChange(methode, null, listProduct);
}else {
ptChSupport.firePropertyChange(methode, null, get(methode));
}
} catch (IOException | JSONException e) {
e.printStackTrace();
}
}
public void addPropertyChangeListener(PropertyChangeListener l){
ptChSupport.addPropertyChangeListener(l);
}
public void removePropertyChangeListener(PropertyChangeListener l){
ptChSupport.removePropertyChangeListener(l);
}
}
Vous devrez modifier votre Product en y ajoutant un constructeur, elementAt et des setter comme ci dessous :
public Product(int id2) {
id=id2;
name="";
type="";
price=0.0;
}
public Object elementAt(int pNumColonne){
switch (pNumColonne) {
case 0:
return getId();
case 1:
return getName();
case 2:
return getType();
case 3:
return getPrice();
default:
return null;
}
}
public void setId(long id) {this.id = id;}
public void setName(String name) {this.name = name;}
public void setType(String type) {this.type = type;}
public void setPrice(double price) {this.price = price;}
La suite : Token JWT
https://eric.munier.me/wp-content/uploads/2023/05/rest-jtable.zip
Sources :
https://baptiste-wicht.developpez.com/tutoriels/java/swing/jtable/
https://baptiste-wicht.com/posts/2009/12/swing-user-interface-jtable.html
