Converting List<ClassName> to array object to set as JComboBoxModel - java

I am designing classes based on DAO Pattern.
I have 3 classes and 1 GUI Form.
public interface SchoolYearDao {
List<SchoolYear> getAllSchoolYearInfo();
List<SchoolYear> getAllSchoolYearStart();
List<SchoolYear> getAllSchoolYearEnd();
List<SchoolYear> getSchoolYearById(int aSchoolYearId);
int getSchoolYearId(SchoolYear schoolyear);
boolean addSchoolYear(SchoolYear schoolyear);
}
public class SchoolYear {
//setters and getters
}
public class SchoolYearDaoImpl implements SchoolYearDao{
#Override
public List<SchoolYear> getAllSchoolYearStart() {
List<SchoolYear> listOfSchoolYearStart = new ArrayList<>();
SchoolYear mySchoolYear = new SchoolYear();
String SQL = "{CALL getAllSchoolYearInfo()}";
try(Connection con = DBUtil.getConnection(DBType.MYSQL);
CallableStatement cs = con.prepareCall(SQL);) {
try(ResultSet rs = cs.executeQuery();){
while(rs.next()){
mySchoolYear.setStart(rs.getInt("yearFrom"));
}
listOfSchoolYearStart.add(mySchoolYear);
}
} catch (SQLException e) {
JOptionPane.showMessageDialog(null,e.getMessage());
}
System.out.println(listOfSchoolYearStart);
return listOfSchoolYearStart;
}
}
The problem is with the GUI.
public class SchoolYearGUI extends javax.swing.JPanel {
public SchoolYearGUI() {
initComponents();
schoolYearStartJcbx.setModel(new DefaultComboBoxModel(schoolyear.getAllSchoolYearInfo().toArray());
schoolYearEndJcbx.setModel(new DefaultComboBoxModel(schoolyear.getAllSchoolYearEnd().toArray()));
}
}
I can't get the years to show correctly. I get this.
Instead of the actual integer numbers 2015,2016,2017 and so on...
I research online and found similar problems but most of them were not using a list of class as List<SchoolYear>. In this case, "SchoolYear" is the name of class.
I used toArray(); and tried Arrays.toString(array); but can't get it right.
I thought I'd change the return type to DefaultComboBoxModel of getAllSchoolYearStart() method but I realized I have to keep my List<SchoolYear> as return type in case I need to use the result set as model for JTables etc..
So, I want to just stick with List<SchoolYear> as return type. (If it's a good idea?)
What is the best way to get the actual value?
Thanks in advance.
=============== Solution ==============================
Thanks to MadProgrammer for the advice and to other answerers.
So I studied the listcellrenderer overnight and finally got the basic idea of how to use it.
public MainFrame() {
initComponents();
SchoolYearDaoImpl sy = new SchoolYearDaoImpl();
DefaultComboBoxModel model = new DefaultComboBoxModel(sy.getAllSchoolYearStart().toArray());
jcmbSchoolYearStart.setModel(model);
jcmbSchoolYearStart.setRenderer(new DefaultListCellRenderer() {
#Override
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
if(value instanceof SchoolYear){
SchoolYear schoolyear = (SchoolYear) value;
setText(""+schoolyear.getStart());
}
return this;
}
} );
}
I overridden the getListCellRendererComponent and created an if-statement to test if value is an instance of my class which is "SchoolYear" Then I cast whatever the raw value is to SchoolYear then used the getter of SchoolYear model, getStart() to get the value stored in the list.
I'm now moving the renderer to an external class file in my project.

Exactly as #MadProgrammer said, in new DefaultComboBoxModel(schoolyear.getAllSchoolYearInfo().toArray(), you put in an array of Objects, and the constructor of a JComboBox will try to use the toString() method to convert every instance of SchoolYear to present it as plain text. If you don't overwrite the default toString() method to present it as you like, you will see what you see in the combobox you have now: the class's name with some numbers.
You can implement the toString() method, but it's not the best way. You can construct some utility method, along with getSchoolYearId() you have, to get the ID of every object in the List and fill an array with the IDs.
private int[] getIDAndFillAnArray(List<SchoolYear> syrs) {
int[] ids = new int[syrs.size()];
for (int i=0; i<syrs.size(); i++) {
ids[i] = syrs.get(i).getSchoolYearId();
}
return ids;
}
And just use it like:
schoolYearStartJcbx.setModel(new DefaultComboBoxModel(getIDAndFillAnArray(schoolyear.getAllSchoolYearInfo()));
That's simple enough.

Use this class:
public class ComboItem {
private String value;
private String label;
public ComboItem(String value, String label) {
this.value = value;
this.label = label;
}
public String getValue() {
return this.value;
}
public String getLabel() {
return this.label;
}
#Override
public String toString() {
return label;
}
}
Put this method where you connect to your DB:
public ComboItem[] getListOfSchoolYearStart(params..)
{
List<ComboItem> result = new ArrayList<ComboItem>();
ComboItem[] items;
.....
rs = stmt.executeQuery(query);
while (rs.next()) {
ComboItem item = new ComboItem(rs.getInt("id_school") + "", rs.getString("description"));
result.add(item);
}
items = result.toArray(new ComboItem[result.size()]);
return items;
}
Add your JComboBox:
private ComboItem[] listOfSchoolYearStart;
private int selectedIdSchool=-1;
....
listOfSchoolYearStart= getListOfSchoolYearStart();
JComboBox comboList = new JComboBox(listOfSchoolYearStart);
//If you want to keep previous selection
if (listOfSchoolYearStart.length > 0)
{
boolean isFound=false;
for (ComboItem comb : listOfSchoolYearStart) {
if(Integer.parseInt(comb.getValue())==selectedIdSchool)
{
comboList.setSelectedItem(comb);
isFound=true;
break;
}
}
if(!isFound)
{
comboList.setSelectedIndex(0);
selectedIdSchool=Integer.parseInt(listOfSchoolYearStart[0].getValue());
}
}
This works for me at least, I hope it helps.

Related

Enum based Combobox

cmbSablonSecim = new ComboBox<>();
cmbSablonSecim.setItems(EnumSablonSecim.values());
My combo box --> TUMU,GRAFIK,DAGILIM
I want fill my combobox with Enum->islemAdi
Combobox --> Tümü,Grafik,Dağılım (Enum->islemAdi)
public enum EnumSablonSecim {
TUMU(0, "Tümü"),
GRAFIK(1, "Grafik"),
DAGILIM(2, "Dağılım")
;
private final Integer islemKodu;
private final String islemAdi;
private EnumSablonSecim(Integer islemKodu, String islemAdi) {
this.islemKodu = islemKodu;
this.islemAdi = islemAdi;
}
public Integer getIslemKodu() {
return islemKodu;
}
public String getIslemAdi() {
ResourceBundle messages = I18n.getInstance(this.getClass());
if (messages.containsKey(islemAdi)) {
return messages.getString(islemAdi);
} else {
return islemAdi;
}
}
public static EnumSablonSecim get(Integer islemKodu) {
for (EnumSablonSecim enumSablonSecim : EnumSablonSecim.values()) {
if (enumSablonSecim.islemKodu == islemKodu) {
return enumSablonSecim;
}
}
return null;
}
}
My combobox must return (islemAdi).is it possible or not? Thank you...
ComboBox::setItemLabelGenerator
Are you asking if you can show the islemAdi field as the label in the combo box?
You can specify code to generate a label used for displaying each item in your enum. Call ComboBox::setItemLabelGenerator. Pass a method reference for your getter. Vaadin then calls this method as needed to display each item.
cmbSablonSecim.setItemLabelGenerator(EnumSablonSecim::getIslemAdi);
See Showing a List of Data with Data Providers in the manual.

A method that could return a Child Object from a Parent Array?

I'm looking for a method that could return a Child Object from a Parent Array - this means how can I make a method return a child object?
The problem is that i have this method to return a Parent Object form the array
but in the program i need a Child Object with its own attributes and the array have 3 different
type of Child Object
This is what I have so far:
class Store {
private BookParents store[];
private int ind, max;
public Store() {
ind=0; //Begin 0 and change with the method AddBooks;
max=100;
store = new BookParents[100];
}
public String AddBooks(BooksChild1 a){
if(ind<max){
store[ind++]=a;
return "TheBooksChild added correctly";
}
return "Full store";
}
public String AddBooks(BooksChild2 b){
if(ind<max){
store[ind++]=b;
return "TheBooksChild added correctly";
}
return "Full store";
}
public String AddBooks(BooksChild3 c){
if(ind<max){
store[ind++]=c;
return "TheBooksChild added correctly";
}
return "Full store";
}
public BooksParents SearchBook(String c) {
AnyBookChild null1 = new Book1("Unknown", 0,"Unknown","Unknown","Unknown","Unknown","Unknown");
if(ind!=0){
for(int i=0 ;i<ind;i++){
if(c.compareTo(store[i].getName(store[i]) )==0)
System.out.println(store[i].PrintlnBook());
return store[i];
}
System.out.println("Book didn't find, try another name.");
return null;
} else {
System.out.println("There is not books in the store");
return null;
}
}
}
Since you have multiple classes of BooksChild you will need a BookParents class that the BooksChild classes extend. You can use still use an array of BookParents:
store = new BookParents[100];
Inside of each BooksChild you will use a constructor that looks something like this:
public class BooksChild1 extends BookParents{
String myISBN, myTitle, myAuthor;
public BooksChild1(String isbn, String title, String author){
super(1, isbn, title, author); //1 denotes the class type
}
}
Inside of the BookParents class you will have another constructor:
public class BookParents{
int myType;
String myISBN, myTitle, myAuthor;
public BookParents(int type, String isbn, String title, String author){
//when a new BooksChild1 is created, it will set the myType equal to 1
myType = type;
myISBN = isbn;
myTitle = title;
myAuthor = author;
}
//this accessor will allow you to find the type of BooksChild
public int getMyType(){
return myType;
}
}
`
Now for the store portion, adding a BooksChild to the array of BooksParent:
public class Store{
//these declarations must be public if you want to use them in a public methods
public BookParents store[];
public int ind, max;
public Store() {
ind=0; //Begin 0 and change with the method AddBooks;
max=100;
store = new BookParents[100];
}
public String addBook(BookParnets bp){
/*must be max-1 or otherwise it will try
to push a final ind of 100 which is out of bounds*/
if(ind<max-1){
store[ind++]=bp;
return "TheBooksChild added correctly";
}
return "Full store";
}
}
The final part will exist in your driver class when you declare BooksChild type objects to insert into your store the declaration statement for a BooksChild1 will be as follows:
BookParents b = new BooksChild1("isbn", "title", "author");
If you ever need to find the type of a BooksChild in your store array, you can use the following to return the type (let store[0] contain a BooksChild of an unknown type):
store[0].getMyType();

JavaFX: ComboBox using Object property

Lets say I have a class:
public class Dummy {
private String name;
private String someOtherProperty;
public String getName() {
return name;
}
}
I have an ArrayList of this class ArrayList<Dummy> dummyList;
Can I create a JavaFX ComboBox with the Object name property as selection options without creating a new ArrayList<String> with the object names?
Pseudocode:
ObservableList<Dummy> dummyO = FXCollections.observableArrayList(dummyList);
final ComboBox combo = new ComboBox(dummyO); // -> here dummyO.name?
(Optional) Ideally, while the name should be displayed, when an option has been selected, the combo.getValue() should return me the reference of the selected Dummy and not only the name. Is that possible?
You can use a custom cellFactory to display the items in a way that suits your needs:
ComboBox<Dummy> comboBox = ...
Callback<ListView<Dummy>, ListCell<Dummy>> factory = lv -> new ListCell<Dummy>() {
#Override
protected void updateItem(Dummy item, boolean empty) {
super.updateItem(item, empty);
setText(empty ? "" : item.getName());
}
};
comboBox.setCellFactory(factory);
comboBox.setButtonCell(factory.call(null));
I'm assuming the ComboBox you're referring to is this: http://docs.oracle.com/javase/8/javafx/api/javafx/scene/control/ComboBoxBase.html. As getValue() is public, you can do:
public class MyComboBox<T> extends ComboBox<T> {
private final Dummy dummy;
public MyComboBox(Dummy dummy) {
this.dummy = dummy;
}
public T getValue() {
return dummy.getName();
}
}

Why we extends AbstractTabelModel when set values into JTabel?

Im developing a app for ordeing system and i have to set data into JTabels.
And this code is successfully working.I wanted to know what the importance of and whats happen in this class?
Why we need to import AbstractTabelModel.class?
OrderTabelModel Class:-
public class OrderTableModel extends AbstractTableModel{
protected static final String[] COLUMN_NAMES={"Item","Qty","Amount"};
private List<Order> rows;
public OrderTableModel(List<Order> rows){
this.rows = new ArrayList<>(rows);
}
#Override
public int getRowCount() {
return rows.size();
}
#Override
public int getColumnCount() {
return COLUMN_NAMES.length;
}
#Override
public String getColumnName(int column) {
return COLUMN_NAMES[column];
}
#Override
public Object getValueAt(int rowIndex, int columnIndex) {
Object value = null;
Order row = rows.get(rowIndex);
switch (columnIndex) {
case 0:
value = row.getItem();
break;
case 1:
value = row.getQty();
break;
case 2:
value = row.getAmount();
break;
}
return value;
}
}
this is other class
private void tblOrderListMouseClicked(java.awt.event.MouseEvent evt) {
int raw = tblOrderList.getSelectedRow();
Order or;
String item;
Double qty,amount,total;
ArrayList<Order> arrOrder = new ArrayList<Order>();
String selectedRaw = tblOrderList.getModel().getValueAt(raw, 0).toString();
System.out.println("order id : "+selectedRaw);
String sql = "select item,qty,amount from orderdetails where orderid='"+selectedRaw+"'";
con = new DBconnector().connect();
try {
Statement ps =con.createStatement();
ResultSet rs2 = ps.executeQuery(sql);
while(rs2.next()){
or = new Order();
or.setItem(rs2.getString("item"));
System.out.println("Item :" +rs2.getString("item"));
or.setQty(rs2.getDouble("qty"));
or.setAmount(rs2.getDouble("amount"));
arrOrder.add(or);
}
rs2.close();
ps.close();
OrderTableModel tblModel = new OrderTableModel(arrOrder);
tblOrderItems.setModel(tblModel);
} catch (Exception e) {
e.printStackTrace();
}
}
Can some one explain me the process of this please?
It is not always mandatory to extend the AbstractTableModel. You can simply extend the DefaultTableModel and only override the getValueAt() method if you have to.
But most of the time for simple usages it is not even needed to override the getValueAt() method either.
By using the DefaultTableModel, you have a limitation for the converting you data (imported from DB) to an object[][] or Vector types which may be a little boring.
But you asked what is the importance of using AbstractTabelModel?
In this case I can say when JTable is started to being rendered, it needs to determine the number of rows and number of the columns and also it needs to know which data should be renedered in each cell and so on. Based on this, JTable ask for this Information from the underlying TableModel. So it is needed for your TableModel(any child or implementation of TableModel) to have those methods which are used by JTable to retrieve the needed information.
Hope this would be helpful.
Good Luck.

TableView doesn't refresh

I've got a project written in JavaFX and I'm trying to get a refresh on a tableview without result.
I've googled around and tried some examples I've found but it still doesn't work.
I populate a tableview with information each row in this table can have new comments added to by double click on the row. The a new Tabpane is opened and the new comment can be added there. On close of this tabpane I'd like the one I clicked from to be refreshed.
I must be doing something wrong. I just don't know what.
In my StoreController
private void populateTableView(List<Store> stores) {
ObservableList<Store> data = FXCollections.observableArrayList(stores);
storeNumberColumn.setCellValueFactory(
new PropertyValueFactory<Store, String>("id"));
storePhoneColumn.setCellValueFactory(
new PropertyValueFactory<Store, String>("phoneNbr"));
chainColumn.setCellValueFactory(
new PropertyValueFactory<Store, String>("chainId"));
commentColumn.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<Store, ImageView>, ObservableValue<String>>() {
#Override
public ObservableValue<String> call(TableColumn.CellDataFeatures<Store, ImageView> p) {
Integer numberOfComments = p.getValue().getCommentsCount();
ReadOnlyObjectWrapper wrapper = null;
if (numberOfComments == 0) {
wrapper = null;
} else if (numberOfComments == 1) {
wrapper = new ReadOnlyObjectWrapper(new ImageView(COMMENT_SINGLE_FLAG_SOURCE));
} else {
wrapper = new ReadOnlyObjectWrapper(new ImageView(COMMENT_DOUBLE_FLAG_SOURCE));
}
return wrapper;
}
});
storeTable.setItems(data);
sortTable(storeTable, missedColumn);
}
#FXML
public void handleTableAction(MouseEvent event) {
if (event.getClickCount() == 2) {
showNewCommentStage();
}
}
private void showNewCommentStage() {
initCommentController();
Store store
= storeTable.getSelectionModel().selectedItemProperty().getValue();
commentController.showNewStage(commentPane, store);
}
It seems like the call-function doesn't get called when the commentpane is closed.
CommentController
public void showNewStage(Pane pane, Store store) {
this.store = store;
initStage(pane);
windowHandler = new WindowHandler(stage);
effectHandler.playEffect(pane);
constructCommentHeaders();
List<Comment> comments;
comments = commentService.listByStoreId(store.getId());
populateCommentTable(comments);
}
Like I said I've tried a lot of the solutions found here on Stackoverflow but with no results. The Tableview doesn't refresh. The Stores and the Comments are in different database tables if that's important
Can someone point me in the right direction?
Thanks!
****EDIT****
The Store.class
public class Store extends CommentEntity {
private String id;
private String chainId;
private String phoneNbr;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getChainId() {
return chainId;
}
public void setChainId(String chainId) {
this.chainId = chainId;
}
public String getPhoneNbr() {
return phoneNbr;
}
public void setPhoneNbr(String phoneNbr) {
this.phoneNbr = phoneNbr;
}
#Override
public String toString() {
return "Store{" + "id=" + id + ", chainId=" + chainId + '}';
}
#Override
public String getCommentIdentifier() {
return id;
}
}
The CommentEntity.Class
public abstract class CommentEntity {
private int commentsCount;
public int getCommentsCount() {
return commentsCount;
}
public void setCommentsCount(int commentsCount) {
this.commentsCount = commentsCount;
}
public abstract String getCommentIdentifier();
}
Thank you for input, I hadn't even reflected over the ImageView / String.
Two issues:
First, you need to distinguish between the data the cells in your column are displaying, and the cells that actually display those data. The cellValueFactory determines the data that are displayed. The PropertyValueFactory is a cellValueFactory implementation that references a JavaFX Property, so when you call
storeNumberColumn.setCellValueFactory(new PropertyValueFactory<Store, String>("id"));
it effectively tells the cells in the storeNumberColumn to call the idProperty() method on the Store object in the current row to get the data for the cell. (If no such method exists, it will try to use getId() as a backup plan.)
By default, you get a cellFactory that displays text resulting from calling toString() on the data generated by the cellValueFactory. In the case where your data are simply Strings, this is usually what you need. In other cases, you often need to provide a cellFactory of your own to get the correct way to display the data.
In your case, the data for the commentColumn are simply the number of comments. You are going to display that by choosing an image based on that numeric value.
So you should have
TableColumn<Store, Number> commentColumn = new TableColumn<>("Comments");
For the cellValueFactory, you can just use
commentColumn.setCellValueFactory(new PropertyValueFactory<>("commentsCount"));
Then you need a cellFactory that displays the appropriate ImageView:
commentColumn.setCellFactory(new Callback<TableColumn<Store, Number>, new TableCell<Store, Number>>() {
#Override
public TableCell<Store, Number>() {
private ImageView imageView = new ImageView();
#Override
public void updateItem(Number numberOfComments, boolean empty) {
super.updateItem(count, empty) ;
if (empty) {
setGraphic(null);
} else {
if (numberOfComments.intValue() == 0) {
setGraphic(null);
} else if (numberOfComments.intValue() == 1) {
imageView.setImage(new Image(COMMENT_SINGLE_FLAG_SOURCE));
setGraphic(imageView);
} else {
imageView.setImage(new Image(COMMENT_DOUBLE_FLAG_SOURCE));
setGraphic(imageView);
}
}
}
}
});
The second issue is actually about the update. A TableView keeps its contents "live" by observing JavaFX properties that are provided by the cellValueFactory as ObservableValues. If the value might change while the table is displayed, you must provide an actual property that can be observed: using a ReadOnlyObjectWrapper is no good (because it's read only, so it's wrapped value will not change). The PropertyValueFactory will also return a ReadOnlyObjectWrapper if you do not have JavaFX property accessor methods (i.e. if it is only using getXXX() methods to access the data). So your model class must provide JavaFX Properties.
You can make an immediate fix to this by updating CommentEntity to use an IntegerProperty:
public abstract class CommentEntity {
private final IntegerProperty commentsCount = new SimpleIntegerProperty();
public final int getCommentsCount() {
return commentsCountProperty().get();
}
public final void setCommentsCount(int commentsCount) {
commentsCountProperty().set(commentsCount);
}
public IntegerProperty commensCountProperty() {
return commentsCount ;
}
public abstract String getCommentIdentifier();
}
I would also strongly recommend updating the Store class to use JavaFX Properties in a similar manner.

Categories