Applying a Counter to Item in List - java

I have a list that is poulated via a local text file. I have the following code that simple prints the selected item/items when the button is click.
private void jButton5ActionPerformed(java.awt.event.ActionEvent evt) {
int[] selection = jList3.getSelectedIndices();
// selection.toString();
for (int i = 0; i < selection.length; i++){
Object selString = jList3.getModel().getElementAt(selection[i]);
System.out.println(selString);
}
}
Instead of printing the item I would like each button click on each object to be recorded somehow. I have no idea what kind of component, method etc to implement. Any guidance is appreciated.
My end result will be something similar to this.
System.out.println(SelString has been clicked X amount of times);

Use a hash map with your objects (selString) as keys, and a counter as value. Something like:
private Map<Object, Integer> buttonMap = new HashMap<Object, Integer>
private void jButton5ActionPerformed(java.awt.event.ActionEvent evt) {
Integer counter = null;
int[] selection = jList3.getSelectedIndices();
for (int i = 0; i < selection.length; i++){
Object selString = jList3.getModel().getElementAt(selection[i]);
counter = buttonMap.get(selString);
if(counter == null) {
buttonMap.put(selString, new Integer(0));
}
buttonMap.put(selString, new Integer(counter.intValue() + 1));
System.out.println(selString + " has been clicked " + buttonMap.get(selString) + " times.");
}
}

You can use PropertyChangeSupport to notify each time jList items are clicked, besides you should create a listener to receive events notification (through propertyChangeSupport.addPropertyChangeListener).
Once there, you can get the event properties such as the property name and property's new value which will be selected item on jList3 in this case, for counting how many times some item was clicked, you could use a HashMap, setting the key as the item index of jList and the associated value how many time the item has been clicked:
PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this);
HashMap<Integer, Integer> clickCounter = new HashMap<Integer, Integer>();
public NewJFrame() {
initComponents();
propertyChangeSupport.addPropertyChangeListener(new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equals("selectedIndex")) {
System.out.println("Selected index: " + evt.getNewValue());
System.out.println("Selected text: " + jList3.getModel().getElementAt(evt.getNewValue()));
if (clickCounter.containsKey((Integer) evt.getNewValue())) {
clickCounter.put((Integer) evt.getNewValue(), clickCounter.get((Integer) evt.getNewValue()) + 1);
} else {
clickCounter.put((Integer) evt.getNewValue(), 1);
}
}
}
});
}
private void jList1MouseClicked(java.awt.event.MouseEvent evt) {
// TODO add your handling code here:
propertyChangeSupport.firePropertyChange("selectedIndex", -1, jList3.getSelectedIndex());
}
At any time you can retrive how many times certian item was clicked accessing clickCounter

I would suggest using an inner class which holds whatever object you are currently putting into the JList and adding a counter member variable as well as overriding the toString().
class MyListItem
{
int selectionCount;
Object listItem; //may consider generics here, but left them out cause they can be tricky
public MyListItem(Object item)
{
selectionCount=0;
listItem=item;
}
public void incrementSelectionCount()
{
selectionCount++;
}
public String toString()
{
return listItem.toString() + " has been clicked " + selectionCount + " times.");
}
}
Then in your action listener
private void jButton5ActionPerformed(java.awt.event.ActionEvent evt)
{
int[] selection = jList3.getSelectedIndices();
for (int selectedIndex : selection)
{
Object selString = jList3.getModel().getElementAt(selectedIndex);
if(selString instanceOf MyListItem)
{
MyListItem selItem = (MyListItem) selString;
selItem.incrementSelectionCount();
System.out.println(selString);
}
}
}
This should save time on look ups, boxing, etc. Also, this helps keep things sane as the project grows since MyListItem can be grown to deal with all types of actions you may want in the future in case you want different things to happen for things other than button presses. The basic idea here is that the MyListItem should keep track of everything you are interested in tracking so you don't need multiple lists and even worse, to remember to add an item to both a JList and a HashMap or any other data structure. This way it's either on every data structure it needs to be or not at all.

Related

javafx user-interface app can't access all the values from an ArrayList

So im developing a javafx gui that it should return all the valid values found in an ArrayList input range, but instead it's functionality its only valid for the latest value added,
so it's only returning the latest entry on the button click, i leave an example picture of the gui
as i hope this would help to clarify:
so if i add 2 different registration, 2 makes and 2 model and try and get the button search by regNo it only works with the latest entry not the previous one;
I leave the code for the setOnAction Method for the button
public void searchByReg(javafx.event.ActionEvent e) {
// clear the text field from the previous message
txtOutput.clear();
// get the car from the user through the car reg
String carReg = txtReg.getText();
// method to check if the field its empty
for (int i = 0; i < cars.size(); i++) {
if (carReg.equalsIgnoreCase(cars.get(i).getRegNo())) {
txtOutput.setText("You have selected \n" + cars.get(i));
carFound = true;
} else {
txtOutput.setText("That car is not in our Database");
}
}
}
Thank you for your help in Advance!!!
Try this, looks like you override text every time until last valid number.
txtOutput.setText("You have selected");
for (int i = 0; i < cars.size(); i++) {
if (carReg.equalsIgnoreCase(cars.get(i).getRegNo())) {
txtOutput.append("\n" + cars.get(i));
carFound = true;
}
}
if(!carFound) {
txtOutput.setText("That car is not in our Database");
}
This is not a JavaFX issue, but just a muddled-up search loop.
There's better ways to do this since Java 8. The following is much simpler and easier to read and debug:
public class LookupSample {
record Car(String name, String regNo) {
}
public static void main(String[] args) {
List<Car> cars = List.of(new Car("Mazda", "123"), new Car("Ford", "123"), new Car("Dodge", "789"));
String carReg = "123";
String result = cars.stream().filter(car -> car.regNo().equals(carReg)).map(Car::name).collect(Collectors.joining("\n\t ", "You have selected:\n\t", ""));
System.out.println(result);
boolean carFound = cars.stream().anyMatch(car -> car.regNo().equals(carReg));
System.out.println("Car Found? " + carFound);
}
}

How can have reflect.field library the value of a Field?

I am doing an application, and I need to create 10 TextFields and save value in a XML file that i have. (I know how to save it in XML)
The issue I have is I want to save it all (in a XML) automatically without repeating the code like 10 times for each variable.
I am trying reflect.Field library to do it (Not accomplished) but i don't know if it's the best solution.
public TextField Tf_TestI1;
public TextField Tf_TestF1;
public TextField Tf_TestI2;
public TextField Tf_TestF2;
public TextField Tf_TestI3;
public TextField Tf_TestF3;
public TextField Tf_TestI4;
public TextField Tf_TestF4;
public TextField Tf_TestI5;
public TextField Tf_TestF5;
//Pair them and save it in XML
private void stuffVariables(){
String nameField1= "Tf_TestI";
String nameField2= "Tf_TestF";
Field[] fields = Controller.class.getFields();
for (int i = 0; i <fields.length ; i++) {
if (fields[i].getName().startsWith(nameField1)){
for (int j = 0; j < fields.length ; j++) {
if (fields[j].getName().equals(nameField2+fields[i].getName().substring(fields[i].getName().length()-1))){
System.out.println("EQUALS : "+fields[i].getName() + " = "+ fields[j].getName());
try {
//System.out.println("1: "+fields[i].getValue);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
}
I am trying to get the pair of Test Initial and Final to finally save it in XML like this...
<TESTS>
<TEST>
<INITIAL>AD</INITIAL>
<FINAL>AVB</FINAL>
</TEST>
<TEST>
<INITIAL>AQEW</INITIAL>
<FINAL>AVFE</FINAL>
</TEST>
<!-- ... MORE TEST -->
<TESTS>
This is not the way you want to do it. As a general rule of thumb, if you feel the need to use reflection, you're doing it wrong.
It looks like you have an arbitrary number of pairs of TextField objects. So that's the first port of call - create a separate class to house those pairs of TextField:
class TextFieldPair {
private TextField initialVal;
private TextField finalVal;
public TextFieldPair(TextField initialVal, TextField finalVal) {
this.initialVal = initialVal;
this.finalVal = finalVal;
}
public TextField getInitialVal() {
return initialVal;
}
public TextField getFinalVal() {
return finalVal;
}
}
...you then want an arbitrary number of these objects. Every time you have an arbitrary number of objects you'll want to use a collection of some sort, such as a list, so we can then loop through them like so:
public class Main {
private List<TextFieldPair> list = new ArrayList<>();
//...Other code that adds textfield objects to the above list
private void stuffVariables() {
for(TextFieldPair pair : list) {
System.out.println("Initial value: " + pair.getInitialVal().getText());
System.out.println("Initial value: " + pair.getFinalVal().getText());
}
}
}
You'll then have all the values printed out from each of those TextField objects, and you can manipulate them (such as outputting XML instead) as you see fit.
My main objective was to make it easy when I add more TextFields...
Using HashMap I only have to add a Key to his Value.
Using numbers in the Key made it more easy in my case.
//[...]
Map<String, TextField> map; //Global variable
//I just have to add the new TextFields to the HashMap
private void mapSaveRangs() {
map = new HashMap<>();
map.put("rangInitial1", tfrangInitial1);
map.put("rangFinal1", tfrangFinal1);
map.put("rangInitial2", tfrangInitial2);
map.put("rangFinal2", tfrangFinal2);
map.put("rangInitial3", tfrangInitial3);
map.put("rangFinal3", tfrangFinal3);
map.put("rangInitial4", tfrangInitial4);
map.put("rangFinal4", tfrangFinal4);
map.put("rangInitial5", tfrangInitial5);
map.put("rangFinal5", tfrangFinal5);
}
private void rangsInPairsXML(Element peeRangs) {
String sKeyrInitial = "rangInitial";
String sValuerInitial;
String sKeyrFinal = "rangFinal";
String sValuerFinal;
//Making sure that we have pair of 2
if (map.size() % 2 == 0) {
//map half size loop
for (int numRang = 1; numRang <= map.size() / 2; numRang++) {
sValuerInitial = map.get(sKeyrInitial + numRang).getText();
sValuerFinal = map.get(sKeyrFinal + numRang).getText();
//Verifying values and saving it in peeRang (XML Element)
if (!(sValuerInitial.equals("") || sValuerFinal.equals(""))) {
Element eRang = new Element("RANG");
eRang.addContent(new Element("INITIAL").addContent(sValuerInitial));
eRang.addContent(new Element("FINAL").addContent(sValuerFinal));
peeRangs.addContent(eRang);
}
}
} else {
System.out.println("ERROR!!");
//[...]
}
}

Dynamically add table columns?

I'm looking for a way to dynamically add columns to a vaadin table.
I tried this:
private Button createAddColumnButton() {
Button addProductButton = new Button("Add column");
addProductButton.addClickListener(new ClickListener() {
public void buttonClick(ClickEvent event) {
count = count++;
table.addGeneratedColumn("Column "+count, new ColumnGenerator() {
#Override
public Object generateCell(final Table source, Object itemId, Object columnId) {
String x = "some stuff";
return x;
}
});
}
});
return addProductButton;
}
This button allowed me dynamically add a column, however only one column before I recieved an error saying I cannot have two columns with the same id. How can I change the ID so it is unique & add lots of columns?
TL;DR
Simple change your code to:
count = count + 1;
Explenation
That's beacause assigment
count = count++;
does not work in the way you expect. Take a look at the following code:
public class HelloStackOverflow {
public static void main(String[] args) {
int count = 0;
count = count++;
System.out.println(count);
}
}
This prints on standard output 0. You will even get warning (The assignment to variable count has no effect) if you change your code to:
count = ++count;
You can find even better explanation here.

How to search through arraylist containing string, int and double with JTextField and JButton

I currently have a working GUI program that has a few buttons on it to simply step through my arraylist of items. These simply display the first, last or next and previous index results. This list has String, int and double inside of it. Now I have added a JTextField for input and a search button. My question is how do I get my search button to search through this array list? I was reading this answer but I don't understand the datum thing. Do I have to convert the entire arraylist to string before searching through it? Would something like
ArrayList<inventoryItem> inventory = new ArrayList<>(); ....
JTextField input = new JTextField(18); ...
JButton searchButton = new JButton("Search");
searchButton.setToolTipText("Search for entry");
searchButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent ae) {
String usrInput = input.getText();
for (String s : inventory) {
if (usrInput.contains(s)) {
inventory.get(currentIndex);
outputText.append(" somehow put whatever the index is equal to here");
}
}
}
});
The error I get is that inventoryItem cannot be converted to string. The second problem: I am having is how to I make it output everything in that index. For example my output looks like this:
class officeSupplyItem extends inventoryItem {
public officeSupplyItem(String itemName, int itemNumber, int inStock, double unitPrice) {
super(itemName, itemNumber, inStock, unitPrice);
}
#Override
public void output(JTextArea outputText) {
outputText.setText("Item Name = " + itemName + " \n"); //print out the item name
outputText.append("Item Number = " + itemNumber + " \n"); //print out the item number
outputText.append("In Stock = " + inStock + " \n"); //print out how many of the item are in stock
outputText.append("Item Price = $" + formatted.format(unitPrice) + " \n"); //print out the price per item
outputText.append("Restocking fee is $" + formatted.format(restockingFee) + " per item \n");
outputText.append("Value of item inventory = $" + formatted.format(value) + " \n"); //print out the value of the item inventory
outputText.append("Cost of inventory w/restocking fee = $" + formatted.format(inventoryValue) + " \n"); //print out the total cost of inventory with restocking fee
}
}
I would also like to understand what the datum portion of the mentioned link means.
I am not quite clear on what you mean by "This list has String, int and double inside of it".
You are comparing an object field with the text entered. You need not convert InventoryItem to a string. What you need to do is identify which fields you want to compare and use them in the comparison.
From what I see the text being entered to the JTextField is the search criteria for your code. If I assume it to be itemName, your code should be as follows :
searchButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent ae) {
String usrInput = input.getText();
for (InventoryItem s : inventory) {
if (usrInput.equalsIgnoreCase(s.getItemName())) {
//you can call output string here
outputText.append(" somehow put whatever the index is equal to here");
}
}
}
});
This is for the case if the JTextField input is the itemName. If this is any different than you expect, please comment.
From the link you shared, the difference is that his List contains only Strings, that is why "datum" is a String. This cannot be used for your case.
Hope this helps!
As per my understanding, what ever the searchText is (itemName or itemNumber) the results should be listed. Therefore, you could write a search method that compares and returns whether it matches the search string as follows in the InventoryItem class.
public boolean isSearchTextAvailable(String searchText) {
if (this.itemName.equals(searchText)) {
return true;
} else {
try {
int no = Integer.parseInt(searchText);
if (this.itemNumber == no) {
return true;
}
} catch (NumberFormatException e) {
System.out.println("Not a integer");
}
}
return false;
}
You can enhance this method to any number of fields you want to be searched within.
Then use this method in the action for the searchButton.
searchButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent ae) {
String usrInput = input.getText();
for (InventoryItem invItem : inventory) {
if (invItem.isSearchTextAvailable(usrInput)) {
invItem.output(outputText);
}
}
}
});
You have to make input (JTextField) a field in this class to make it work.
Few tips to increase quality of your code:
Separate InventoryItem and OfficeSupplyItem classes to different files
Notice the naming convention for class names is PascalCase
Better to have JavaGUIFixed class extended from JFrame and all its components defined as fields and not local variables since you use them in various other methods other than makeWindow() method where you actually create the JFrame
Do not use invItem.output(outputText) type of methods, where you pass a JTextField to a object Method to write to it. What you could do instead is write a method like getOutputString() in InventoryItem class which will return a formatted string of what needs to be printed and then call outputText.setText(invItem.getOutputString()) - Your implementation is restricting you to have all classes as inner classes to JavaGUIFixed.
Hope this helps!
Your enhanced for loop is saying for each String element s in ArrayList inventory... But inventory is declared to be an ArrayList of inventoryItem objects, not a list of strings, and the for loop isn't accessing the variables where the values you are trying to search are stored, as each index of inventory is just storing a reference to an object.
If your main goal is taking input, storing, sorting, and outputting it, you might consider taking and storing it as strings in a string collection. You can always parse to int or double if you need to at some point, but it will be easier to sort and search with a homogenous data type.
Where I saw datum used in that link was just as a variable name, the same way you used 's' in your code.
I have marked #maheeka 's response as the answer. I however had to modify his code because of the way that I had previously written my program. It now looks as follows:
searchButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent ae) {
String usrInput = input.getText();
for (int i = 0; i < inventory.size(); i++) {
if (usrInput.equalsIgnoreCase(inventory.get(i).getItemName())) {
currentIndex = i;
displayItem(outputText);
I also had to set up my getter on the itemName as I apparently had forgotten that part. Thank you #Aadi Droid for that.

Problems of checkName() and storing user input to ArrayList and boolean[]

Here I have a GUI window and it basically ask the user to select a JRadioButton and type something in a JTextField, then choose confirm/cancel.
It is a project which we have to make a UML-to-Java text file. User would enter class information and choose a UML relationship, and this programme have to print out the Java clsas text on a JTextField. Just like when you create a new class in eclipse.
what I want to do is make a boolean[] to store an array of booleans, when user selects JRadioButton_A it'll store true and when user select JRadioButton_B it'll store false.And also I want the things typed in JTextField to be checked by a checkName(), if the method returns false, the string will be stored in an ArrayList.
Below is my code - there's some problems in getName() method and the boolean[] for storing true and false. When user needs to input name again, it would save the discarded sting/boolean into the array. (Sorry for my bad english!) Is there any better way to make this programme? I feel like I am complicating things and there should be a simpler way to make it.
Here's the UI stuffs asking user to enter class information. User have to select public/private and then type in class name and JTextField
private class Handler implements ActionListener{
public void actionPerformed(ActionEvent event){
String name = inputClassName.getText();
classObject.addName(name);
while (classObject.checkName(name) == true){
JOptionPane.showMessageDialog(null, "Class name invalid. " +
"\nEntered name should not contain java keywords or equal to other existing names. " +
"\nPlease try again."); // doesn't work
name = inputClassName.getText();
classObject.addName(name);
}// end if
JOptionPane.showMessageDialog(null, "Class saved."); // doesn't work
name = inputClassName.getText();
classObject.addName(name);
}// end actionPerformed()
}// end Handler class
private class Handler2 implements ActionListener{
public void actionPerformed(ActionEvent event){
boolean b = true;
b = classObject.setPP();
}
}
private class Handler3 implements ActionListener{
public void actionPerformed(ActionEvent event){
boolean b = false;
b = classObject.setPP();
}
}
Here's the methods for storing the inputs to the ArrayList and boolean[]
Scanner input = new Scanner(System.in);
JavaKeywords keyObject = new JavaKeywords();
private ArrayList<String> className = new ArrayList<String>();
private String name = new String();
private int size = className.size();
private Boolean[] bArray = new Boolean[size];
public boolean checkName(String name){
boolean check = true;
for (int i=0; i<=size; i++){
if (keyObject.containsKeyword(className.get(i)) || name.equals(className.get(i))){
boolean o = false;
check = o;
}// end if
}// end for
return check;
}// end checkName
public boolean setPP(){
boolean b = true;
return b;
}
public void addPP(Boolean[] bArray){
this.bArray = bArray;
for (int i=0; i>=size; i++){
bArray[i] = setPP();
}
}// add a Array of boolean. for className[i], its class type = item[i] in bArray.
// public = true, private = false
public String getPublicPrivate(){
String p = "";
for (int i =0; i<=size; i++){
if(bArray[i]=true)
p = "public";
else
p = "private";
}
return p;
}
Solved
Solution: store the string className and boolean isPrivate in a class and make the class into an ArrayList can save me from all the trouble. But then i faced anther problem, that is the checkName() doesn't work after I changed my code.
here is the ActionListener
private class Handler implements ActionListener{
public void actionPerformed(ActionEvent event){
VirtualClass virtualObject = new VirtualClass();
classObject.addClass(virtualObject);
String name = inputClassName.getText();
virtualObject.className = name;
if (classObject.checkName(name) == false){
JOptionPane.showMessageDialog(null, "Class name invalid. " +
"\nEntered name should not contain java keywords or equal to other existing names. " +
"\nPlease try again."); // Always return "invalid" message
} else {
JOptionPane.showMessageDialog(null, "Class saved.");
name = inputClassName.getText();
virtualObject.className = name;
}
if (event.getSource() == publicButton) {
virtualObject.isPrivate = false;
} else if (event.getSource() == privateButton) {
virtualObject.isPrivate = true;
}
}// end actionPerformed()
and here is the checkName() method
public boolean checkName(String name){
boolean check = true;
for (int i=0; i<=size; i++){
if (keyObject.containsKeyword(classes.get(i).className) || name.equals(classes.get(i).className)){
boolean o = false;
check = o;
}// end if
}// end for
return check;
}// end checkName
For containsKeyword() in checkName() I've used a JavaKeywords class from How to check if the class name is valid? by #MrLore.
Probably what I would do is create a simple class to represent your fields so you don't have to use multiple lists at all.
public class VirtualClass {
public boolean isPrivate;
public String className = "Object";
}
ArrayList<VirtualClass> classes = new ArrayList<VirtualClass>(0);
public void addClass(VirtualClass clazz) {
classes.add(clazz);
}
Otherwise you will have to create a second list of some kind to hold the public/private. You will just have to change them in parallel.
// in actionPerformed
ClassObject.VirtualClass clazz = new ClassObject.VirtualClass();
clazz.isPrivate = rbPrivate.isSelected();
clazz.className = tfClassName.getText();
classObject.addClass(clazz);
And just ignore the listening on the radio buttons since you technically do not need their states until you go to add the class to the list.
To access the fields later you just need to
for (VirtualClass clazz : classes) {
System.out.println((clazz.isPrivate ? "private" : "public") + " " + clazz.className);
}
// or something like
for (int i = 0; i < classes.size(); i++) {
System.out.println(classes.get(i).className + ":");
if (classes.get(i).isPrivate) {
System.out.println(" private");
} else {
System.out.println(" public");
}
}
I'm not entirely convinced by your over all approach. What I think "should"/"could" happen is, the user enters all the information you ask, they hit "accept", you valid the information that the user has entered and if it is correct, you create a new object representing the results of this input as you need.
I would, personally, avoid using an array of booleans, or at least, expose them differently. The main problem I have with it is keeping it all straight in my head, what does the element at 0 actually mean?
Instead, I would provide getter/setters on the ClassName class that allowed me to set/get particular properties. You could, of course, keep the values in an array internally, but anyone using the class wouldn't need to know how you store these values.
The problem with your check name Handler is the fact you are blocking the Event Dispatching Thread with your while-loop
This will stop you program from responding to user input (and painting itself)
while (classObject.checkName(name) == true){
JOptionPane.showMessageDialog(null, "Class name invalid. " +
"\nEntered name should not contain java keywords or equal to other existing names. " +
"\nPlease try again."); // doesn't work
name = inputClassName.getText();
classObject.addName(name);
}// end if
Swing is a single threaded framework, meaning that all interactions and changes to the UI are expected to be executed within the context of the EDT. Equally, anything that blocks or stops this thread from processing the Event Queue, will stop it from responding to new events, including repaint requests, making your program hang
Instead, you should simply check the name within a if-else statement. If it's valid, create the new ClassName object, if it's not, display a message and let the method exit.
Check out Concurrency in Swing for more details

Categories