Class ComboItem
public class ComboItems {
private Long key;
private String value;
public ComboItems(Long key, String value) {
this.key = key;
this.value = value;
}
@Override
public String toString() {
return value;
}
public Long getKey() {
return key;
}
public String getValue() {
return value;
}
}
Set Class ComboItem to ComboBox
private void loadSupplier(FormBuy formBuy) {
List<Supplier> suppliers = supplierJdbc.selectSuppliers("%%");
DefaultComboBoxModel defaultComboBoxModel = new DefaultComboBoxModel();
suppliers.forEach(supplier -> {
defaultComboBoxModel.addElement(new ComboItems(supplier.getId(), supplier.getName()));
});
formBuy.getjComboBoxSupplier().setModel(defaultComboBoxModel);
}
Show to DefautTableModel
private void loadTable(DefaultTableModel defaultTableModel, FormBuy formBuy) {
defaultTableModel.getDataVector().removeAllElements();
defaultTableModel.fireTableDataChanged();
List<ResponseListTableBuy> buys = buyJdbc.selectBuys("%" + formBuy.getjTextFieldSearch().getText() + "%");
Object[] objects = new Object[10];
for (ResponseListTableBuy buy : buys) {
objects[0] = buy.getId();
objects[1] = buy.getSupplier().getName();
objects[2] = buy.getCategory().getName();
objects[3] = buy.getItem().getName();
objects[4] = buy.getUnit().getName();
objects[5] = buy.getCountItem();
objects[6] = buy.getBuyPrice();
objects[7] = new BigDecimal(buy.getCountItem() * buy.getBuyPrice().intValue());
objects[8] = buy.getSellPrice();
objects[9] = buy.getDate();
defaultTableModel.addRow(objects);
}
}
so far it's still fine!. I got an error when I put this code
get code from table to combobox
formBuy.getjComboBoxSupplier().getModel().setSelectedItem(defaultTableModel.getValueAt(formBuy.getjTableBuy().getSelectedRow(), 1).toString());
and put this an error
System.out.println(((ComboItems) formBuy.getjComboBoxSupplier().getModel().getSelectedItem()).getKey());
Exception in thread "AWT-EventQueue-0" java.lang.ClassCastException: java.lang.String cannot be cast to aliimron.combobox.ComboItems
at aliimron.controller.ControllerBuy$1.mouseClicked(ControllerBuy.java:97)
at java.awt.AWTEventMulticaster.mouseClicked(AWTEventMulticaster.java:270)
at java.awt.Component.processMouseEvent(Component.java:6542)
at javax.swing.JComponent.processMouseEvent(JComponent.java:3324)
at java.awt.Component.processEvent(Component.java:6304)
at java.awt.Container.processEvent(Container.java:2239)
at java.awt.Component.dispatchEventImpl(Component.java:4889)
at java.awt.Container.dispatchEventImpl(Container.java:2297)
at java.awt.Component.dispatchEvent(Component.java:4711)
at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4904)
at java.awt.LightweightDispatcher.processMouseEvent(Container.java:4544)
at java.awt.LightweightDispatcher.dispatchEvent(Container.java:4476)
at java.awt.Container.dispatchEventImpl(Container.java:2283)
at java.awt.Window.dispatchEventImpl(Window.java:2746)
at java.awt.Component.dispatchEvent(Component.java:4711)
at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:760)
at java.awt.EventQueue.access$500(EventQueue.java:97)
at java.awt.EventQueue$3.run(EventQueue.java:709)
at java.awt.EventQueue$3.run(EventQueue.java:703)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:74)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:84)
at java.awt.EventQueue$4.run(EventQueue.java:733)
at java.awt.EventQueue$4.run(EventQueue.java:731)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:74)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:730)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:205)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:82)
so the flow is from comboitem to combobox to table and back from table to combobox. how do you get it from the table back into the combobox
So, you build your model using ...
defaultComboBoxModel.addElement(new ComboItems(supplier.getId(), supplier.getName()));
This is good, you're encapsulating the information into an object, but, then you use
formBuy.getjComboBoxSupplier().getModel().setSelectedItem(defaultTableModel.getValueAt(formBuy.getjTableBuy().getSelectedRow(), 1).toString());
Wait, that's a lot, lets see if we can clean it up a bit...
formBuy
.getjComboBoxSupplier()
.getModel()
.setSelectedItem(
defaultTableModel.getValueAt(
formBuy.getjTableBuy().getSelectedRow(), 1
).toString()
);
So, basically, you're trying to use a String
to set the value of the model, but the model is expecting an instance of ComboItems
... 🤨
So, you have a couple of choices...
Supply the TableModel
with a ComboItems
and then use a custom cell renderer to render it, then you could use something like...
formBuy
.getjComboBoxSupplier()
.getModel()
.setSelectedItem(
(ComboItems)defaultTableModel.getValueAt(
formBuy.getjTableBuy().getSelectedRow(), 1
)
);
to set the selected combo box item.
For my money, this is the more "correct" approach, see
for more details
Look up the item in the model directly, based on the String
from the table.
Something maybe like this...
String value = (String)defaultTableModel.getValueAt(formBuy.getjTableBuy().getSelectedRow(), 1);
DefaultComboBoxModel model = formBuy.getjComboBoxSupplier().getModel()
for (int index = 0; index < model.getSize(); index++) {
ComboItems items = (ComboItems) model.getElementAt(index);
if (items.getValue().equalsIgnoreCase(value)) {
formBuy
.getjComboBoxSupplier()
.getModel()
.setSelectedItem(items);
break;
}
}
But to be honest, you're better off with option #1
One of things I might be focusing on, is trying to tie your data together, in a more meaningful way, so you don't need to do these "side lookups", for example, your data seems to be expressible as a key/value pair, lets start there...
public interface KeyValueExpressible {
public String getKey();
public String getValue();
}
public abstract class AbstractKeyValue implements KeyValueExpressible {
private String key;
private String value;
public AbstractKeyValue(String key, String value) {
this.key = key;
this.value = value;
}
public String getKey() {
return key;
}
public String getValue() {
return value;
}
}
Okay, but how's that going to help you? Well, now you can narrow the constraints to actual data types, for example...
public class Supplier extends AbstractKeyValue {
public Supplier(String key, String value) {
super(key, value);
}
}
The neat thing is, this is still usable anywhere you need KeyValueExpressible
, but can provide a finer level constraint when you need the Supplier
, sweet
Now you could construct you combobox using something like...
List<Supplier> suppliers = supplierJdbc.selectSuppliers("%%");
DefaultComboBoxModel<KeyValueExpressible> defaultComboBoxModel = new DefaultComboBoxModel<>();
suppliers.forEach(supplier -> {
defaultComboBoxModel.addElement(supplier);
});
formBuy.getjComboBoxSupplier().setModel(defaultComboBoxModel);
nb: DefaultComboBoxModel<KeyValueExpressible>
could also be DefaultComboBoxModel<Supplier>
, but DefaultComboBoxModel<KeyValueExpressible>
allows us to do cool magic like...
public class KeyValueExpressibleListCellRenderer extends DefaultListCellRenderer {
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
if (value instanceof KeyValueExpressible) {
KeyValueExpressible keyValue = (KeyValueExpressible) value;
setText(keyValue.getValue());
}
return this;
}
}
// Why doesn't DefaultListCellRenderer support generics ðŸ˜
which can then be applied using something like...
JComboBox<KeyValueExpressible> comboBox = new JComboBox<>(defaultComboBoxModel);
comboBox.setRenderer(new KeyValueExpressibleListCellRenderer());
Which is nice a reusable, sweet.
Now, we can start extending the concept, for example, assuming we have something like...
public interface ResponseListTableBuy {
public String getId();
public Supplier getSupplier();
//...
}
Then we can encapsulate the TableModel
, for example...
public class BuyTableModel extends AbstractTableModel {
private List<ResponseListTableBuy> buys;
private String[] columnNames = new String[]{
"id",
"Supplier",
"Category",
"Item",
"Unit",
"Count",
"Buy price",
"Total",
"Sell price",
"Date"
};
public BuyTableModel(List<ResponseListTableBuy> buys) {
this.buys = buys;
}
@Override
public int getRowCount() {
return buys.size();
}
@Override
public int getColumnCount() {
return columnNames.length;
}
@Override
public String getColumnName(int column) {
return columnNames[column];
}
@Override
public Class<?> getColumnClass(int columnIndex) {
switch (columnIndex) {
case 0: return String.class;
case 1: return KeyValueExpressible.class;
case 2: return KeyValueExpressible.class;
case 3: return KeyValueExpressible.class;
case 4: return KeyValueExpressible.class;
case 5: return Integer.class;
case 6: return Double.class;
case 7: return BigDecimal.class;
case 8: return Double.class;
case 9: return LocalDate.class;
}
return String.class;
}
@Override
public Object getValueAt(int rowIndex, int columnIndex) {
ResponseListTableBuy buy = buys.get(rowIndex);
switch (columnIndex) {
case 0: return buy.getId();
case 1: return buy.getSupplier();
case 2: return buy.getCategory();
case 3: return buy.getItem();
case 4: return buy.getUnit();
case 5: return buy.getCountItem();
case 6: return buy.getBuyPrice();
// This should be a computed porperty, just saying...
case 7: return new BigDecimal(buy.getCountItem() * buy.getBuyPrice().intValue());;
case 8: return buy.getSellPrice();
case 9: return buy.getDate();
}
}
}
And then....
public class KeyValueExpressibleTableCellRenderer extends DefaultTableCellRenderer {
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
if (value instanceof KeyValueExpressible) {
KeyValueExpressible keyValue = (KeyValueExpressible) value;
setText(keyValue.getValue());
}
return this;
}
}
more magic 🪄!
You apply it using something like...
JTable table = new JTable();
table.setDefaultRenderer(KeyValueExpressible.class, new KeyValueExpressibleTableCellRenderer());
And suddenly, every column you said was a KeyValueExpressible.class
will be rendered through this renderer! Sweet!
We've encapsulated the data in the model; we've got the view to render the data the way want it to and when needed...we can do...
formBuy
.getjComboBoxSupplier()
.getModel()
.setSelectedItem(
(KeyValueExpressible)defaultTableModel.getValueAt(
formBuy.getjTableBuy().getSelectedRow(), 1
)
);
nb: You could make the BuyTableModel
do the casting for you, but I think I might have already blown your mind 🤯
You might find yourself in a situation where you have two instances KeyValueExpressible
representing the same data (you could use a common factory to manage it, but that's another topic).
In those cases, you need some way to tell that those two instances are the same, this is where equals
and hashCode
come in, for example...
public abstract class AbstractKeyValue implements KeyValueExpressible {
private String key;
private String value;
public AbstractKeyValue(String key, String value) {
this.key = key;
this.value = value;
}
public String getKey() {
return key;
}
public String getValue() {
return value;
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (obj instanceof KeyValueExpressible) {
return false;
}
KeyValueExpressible keyValue = (KeyValueExpressible) obj;
return keyValue.getKey().equals(getKey()) && keyValue.getValue().equals(getValue());
}
@Override
public int hashCode() {
int hash = 7;
hash = 29 * hash + Objects.hashCode(this.key);
hash = 29 * hash + Objects.hashCode(this.value);
return hash;
}
}
Now, any two instances with the same key
and value
will be equal, sweet!
Now, if you prefer to tighten the constraint a little more, you could also do something like...
public class Supplier extends AbstractKeyValue {
public Supplier(String key, String value) {
super(key, value);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof Supplier) {
return false;
}
return super.equals(obj);
}
}
Now only two Supplier
s with the same key/value will be equal, sweet!