I am doing a midterm where we have to make a GUI grocery list. The professor is asking us to make the list into a .txt file and read from that file. There is also a delete section and delete all which I haven't coded yet. Usually I test the code whenever I add something new into it. When trying to test this one I noticed that it crashes. The code is writing to the Grocery.txt but isn't showing what is wrote into the list box. I know I am most likely missing something small. When I look at my textbook "Murach's Java programming 6th Edition" It looks almost the same except the example they are using is reading three different things from the file. They are using a array to read. I am trying to place each one on its own line. I know right now it won't. I am trying to test if it reads it at all which currently it isn't.
Update: I changed some of the code around now when I click add I am getting an empty alert box that says error. Updated code is placed. I will be afk for a few hours and will be back to work on it. Thank you for all your help.
FINAL update: found the error it was in my if (errorMsg == " ") {
for some reason it wasn't reading it correctly changing it to if(errorMsg.isBlank())
fixed it. Thank you all for your input and I will be better at posting the question whenever I need to post again. The program still won't run correctly but the issue I was having has been fixed and I can figure the rest out myself.
Again thank you and happy programming
Here is my current code:
package com.mycompany.zaph_midterm;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ListView;
import javafx.scene.control.SelectionMode;
import javafx.scene.control.TextField;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class App extends Application {
public TextField addGrocery;
public ListView<String> groceryList = new ListView<>();
@Override
public void start(Stage stage) {
stage.setTitle("Grocery List");
//Setting grid
GridPane grid = new GridPane();
grid.setAlignment(Pos.CENTER);
grid.setPadding(new Insets(15,15,15,15));
grid.setHgap(10);
grid.setVgap(10);
//attaching grid to scene
Scene scene = new Scene(grid);
//Setting the add section
HBox hbox = new HBox();
GridPane addHbox = new GridPane();
addHbox.add(new Label ("Items: "), 0, 0);
addHbox.add(new Label(" "), 1, 0);
addGrocery = new TextField();
addHbox.add(addGrocery, 2, 0);
Button addButton = new Button("Add");
addButton.setOnAction(event -> addButtonClicked());
addHbox.add(new Label(" "), 3, 0);
addHbox.add(addButton, 4, 0);
hbox.getChildren().add(addHbox);
grid.add(hbox, 0, 0);
//Setting list to show
HBox groceryBox = new HBox();
GridPane groceryGrid = new GridPane();
groceryGrid.add(new Label("List: "), 0, 0);
groceryList.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
groceryList.getItems().add(grocery.readFromFile(addGrocery.getText()));
groceryGrid.add(groceryList, 2, 0);
groceryBox.getChildren().add(groceryGrid);
grid.add(groceryBox, 0, 1);
stage.setScene(scene);
stage.show();
}
// Add button clicked
private void addButtonClicked() {
//Validation of user Input
Validation v = new Validation();
String errorMsg = "";
errorMsg += v.hasValue(addGrocery.getText());
errorMsg += v.isBlank(addGrocery.getText());
// Using if/else to add to grocery List
if (errorMsg.isBlank()){
GroceryList grocery = new GroceryList();
//Adding to Grocery List
grocery.printToFile(addGrocery.getText());
}else {
Alert wrong = new Alert(Alert.AlertType.ERROR);
wrong.setHeaderText("Error");
wrong.setContentText(errorMsg);
wrong.showAndWait();
}
}
public static void main(String[] args) {
launch();
}
Validation class:
package com.mycompany.zaph_midterm;
public class Validation {
private final String lineEnd;
public Validation() {
this.lineEnd = "\n";
}
public Validation(String lineEnd) {
this.lineEnd = lineEnd;
}
public String isBlank(String name) {
String error = "";
if(name.isBlank()) {
error = "Must have groceries in the add text box.";
}
return error;
}// end of isBlank
public String hasValue(String name) {
//if user input is able to be parsed then an error message will return
String error = "";
try {
Double.parseDouble(name);
} catch (NumberFormatException e) {
return error;
}
error ="Text box must only contain letters";
return error;
}
}
GroceryList Class:
package com.mycompany.zaph_midterm;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import static java.lang.System.out;
public class GroceryList {
private String grocery;
public GroceryList() {
grocery = " ";
}
public void setGroceryList(String args){
this.grocery = grocery;
}
public String getGroceryList() {
return grocery;
}
public void printToFile(String grocery) {
//creating file
try (PrintWriter out = new PrintWriter(
new BufferedWriter(
new FileWriter("Grocery.txt")))) {
//writing to file
out.print(grocery);
}
//catching error if found
catch (IOException e){
System.out.println(e);
}
}
public void deleteFromFile(String grocery) {
}
public String readFromFile(String grocery) throws IOException{
try (BufferedReader in = new BufferedReader(
new FileReader("Grocery.txt"))){
String line = in.readLine();
while (line != null){
line = grocery;
return grocery;
}
}
catch (IOException e) {
Alert error = new Alert(Alert.AlertType.ERROR);
error.setHeaderText("Didn't work");
error.setContentText("Something is wrong");
error.showAndWait();
}
return grocery;
}
}
}
I tried moving the try(BufferedReader) to the addButtonCLicked() still crashed so I moved it up under the //setting list to show and it still crashes.
Your Question is unfocused and unclear. But, for fun, I will take a stab at revamping your code.
One big useful concept in programming in separation of concerns. This means organizing your code in separate chunks that each focus on a particular job.
One job is writing your grocery list items to storage, and reading them back. That should be in one specific class. We will call this class Repository
.
We need to define the objects to go into the repository. Your GroceryList
class is ill-conceived as the list is just a collection of items. It is the item we need to define. We can that simply with a record. The one and only job of this class is to represent validated state necessary to represent a grocery item from the real world.
public record GroceryItem( String name )
{
public GroceryItem ( final String name )
{
Objects.requireNonNull ( name );
if ( name.isBlank ( ) ) throw new IllegalArgumentException ( "Grocery item name cannot be blank." );
this.name = name;
}
}
The repository needs to save grocery items and retrieve again. The one and only job of this class is the persistent storage and retrieval of our objects’ state. Here we write a simple bogus implementation, good enough to get going.
public class Repository
{
private List < GroceryItem > storedGroceryItems =
new ArrayList <> (
List.of (
new GroceryItem ( "Bananas" ) ,
new GroceryItem ( "Eggs" ) ,
new GroceryItem ( "Olive oil" )
)
);
public List < GroceryItem > fetchGroceryItems ( )
{
return List.copyOf ( this.storedGroceryItems );
}
public boolean saveGroceryItem ( final GroceryItem groceryItem )
{
if ( this.storedGroceryItems.contains ( groceryItem ) ) return false;
return this.storedGroceryItems.add ( groceryItem );
}
public boolean deleteGroceryItem ( final GroceryItem groceryItem )
{
return this.storedGroceryItems.remove ( groceryItem );
}
public boolean removeAllGroceryItems ( )
{
List < GroceryItem > emptyList = List.of ( );
return this.storedGroceryItems.retainAll ( emptyList );
}
}
Write some tests to verify. We expect all “true” results.
public class TestRepository
{
public static void main ( String[] args )
{
TestRepository.testPreloadedRepository ( );
}
public static void testPreloadedRepository ( )
{
Repository repository = new Repository ( );
boolean success;
List < GroceryItem > list = repository.fetchGroceryItems ( );
System.out.println ( "list has 3 elements: " + ( list.size ( ) == 3 ) );
List < GroceryItem > expectedList =
List.of (
new GroceryItem ( "Bananas" ) ,
new GroceryItem ( "Eggs" ) ,
new GroceryItem ( "Olive oil" )
);
System.out.println ( "list has expected items: " + ( list.equals ( expectedList ) ) );
GroceryItem broccoli = new GroceryItem ( "Broccoli" );
success = repository.saveGroceryItem ( broccoli );
System.out.println ( "success = " + success );
list = repository.fetchGroceryItems ( );
System.out.println ( "list has 4 elements: " + ( list.size ( ) == 4 ) );
expectedList =
List.of (
new GroceryItem ( "Bananas" ) ,
new GroceryItem ( "Eggs" ) ,
new GroceryItem ( "Olive oil" ) ,
broccoli
);
System.out.println ( "list has expected items: " + ( list.equals ( expectedList ) ) );
GroceryItem eggs = new GroceryItem ( "Eggs" );
success = repository.deleteGroceryItem ( eggs );
System.out.println ( "success = " + success );
list = repository.fetchGroceryItems ( );
System.out.println ( "list has 3 elements: " + ( list.size ( ) == 3 ) );
expectedList =
List.of (
new GroceryItem ( "Bananas" ) ,
new GroceryItem ( "Olive oil" ) ,
broccoli
);
System.out.println ( "list has expected items: " + ( list.equals ( expectedList ) ) );
}
}
All our tests passed.
list has 3 elements: true
list has expected items: true
success = true
list has 4 elements: true
list has expected items: true
success = true
list has 3 elements: true
list has expected items: true
Tip to the advanced student: In real work, we would write tests using a framework such as Jupiter. Then we run the tests in a test-harness such as JUnit 5.
Now build the GUI in JavaFX implemented as OpenJFX.
public class HelloApplication extends Application
{
private Repository repository = new Repository ( );
@Override
public void start ( Stage stage ) throws IOException
{
Pane pane = this.buildPane ();
Scene scene = new Scene ( pane , 500 , 350 );
stage.setTitle ( "Grocery List" );
stage.setScene ( scene );
stage.show ( );
}
private Pane buildPane () {
// Data
List < GroceryItem > groceryItems = this.repository.fetchGroceryItems ( );
ObservableList < GroceryItem > observableList = FXCollections.observableArrayList ( groceryItems );
// Widgets
ListView < GroceryItem > listView = new ListView <> ( observableList );
TextField newGroceryItemField = new TextField ( );
newGroceryItemField.setPromptText ( "new grocery item" );
Button addButton = new Button ( "Add…" );
Button removeButton = new Button ( "Delete" );
// Behavior
addButton.setOnMouseClicked ( ( MouseEvent mouseEvent ) ->
{
String input = newGroceryItemField.getText ( );
if ( input.isBlank ( ) ) return;
GroceryItem groceryItem = new GroceryItem ( input );
if ( observableList.contains ( groceryItem ) ) return;
if ( this.repository.saveGroceryItem ( groceryItem ) )
{
observableList.add ( groceryItem );
}
} );
removeButton.setOnMouseClicked ( ( MouseEvent mouseEvent ) ->
{
GroceryItem groceryItem = listView.getSelectionModel ( ).getSelectedItem ( );
if ( Objects.nonNull ( groceryItem ) )
{
if ( this.repository.deleteGroceryItem ( groceryItem ) )
{
observableList.remove ( groceryItem );
}
}
} );
// Arrange
BorderPane borderPane = new BorderPane ( );
borderPane.setCenter ( listView );
borderPane.setBottom ( new HBox ( newGroceryItemField , addButton , removeButton ) );
return borderPane;
}
public static void main ( String[] args ) { launch ( ); }
}
With that all working, we can go back to our Repository
. We can rewrite to actually save to a file rather than use our dummy list in memory.
Look to the Java NIO classes such as Path
, Paths
, and Files
to simplify your file handling.
package work.basil.example.exgrocerylist;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
import java.util.List;
public class Repository
{
private Path dataFilePath = Paths.get ( "/Users/basil_dot_work/GroceryItems.txt" );
public List < GroceryItem > fetchGroceryItems ( )
{
boolean go = this.createAndPopulateFileIfNotExists ( );
try
{
List < String > lines = Files.readAllLines ( this.dataFilePath , StandardCharsets.UTF_8 );
List < GroceryItem > groceryItems = new ArrayList <> ( );
for ( String line : lines )
{
if ( line.isBlank ( ) )
{
continue;
}
GroceryItem groceryItem = new GroceryItem ( line );
groceryItems.add ( groceryItem );
}
return List.copyOf ( groceryItems );
}
catch ( IOException e )
{
throw new RuntimeException ( e );
}
}
private boolean createAndPopulateFileIfNotExists ( )
{
if ( Files.exists ( this.dataFilePath ) ) return true;
List < GroceryItem > defaultGroceryItems =
List.of (
new GroceryItem ( "Bananas" ) ,
new GroceryItem ( "Eggs" ) ,
new GroceryItem ( "Olive oil" )
);
List < String > lines =
defaultGroceryItems.stream ( ).map ( GroceryItem :: name ).toList ( );
try
{
Files.write ( this.dataFilePath , lines , StandardCharsets.UTF_8 );
return ( this.fetchGroceryItems ( ).equals ( defaultGroceryItems ) );
}
catch ( IOException e )
{
throw new RuntimeException ( e );
}
}
public boolean saveGroceryItem ( final GroceryItem groceryItem )
{
List < GroceryItem > groceryItems = this.fetchGroceryItems ( );
if ( groceryItems.contains ( groceryItem ) ) return false;
try
{
Files.write ( this.dataFilePath , groceryItem.name ( ).getBytes ( ) , StandardOpenOption.APPEND );
}
catch ( IOException e )
{
throw new RuntimeException ( e );
}
return this.fetchGroceryItems ( ).contains ( groceryItem );
}
public boolean deleteGroceryItem ( final GroceryItem groceryItem )
{
List < GroceryItem > groceryItems = this.fetchGroceryItems ( );
if ( ! groceryItems.contains ( groceryItem ) ) return false;
List < GroceryItem > groceryItemsModifiable = new ArrayList <> ( groceryItems );
boolean go = groceryItemsModifiable.remove ( groceryItem );
if ( ! go )
{
return false;
}
List < String > lines = groceryItemsModifiable.stream ( ).map ( GroceryItem :: name ).toList ( );
try
{
Files.write ( this.dataFilePath , lines , StandardCharsets.UTF_8 );
return true;
}
catch ( IOException e )
{
throw new RuntimeException ( e );
}
}
public boolean removeAllGroceryItems ( )
{
List < String > lines = List.of ( );
try
{
Files.write ( this.dataFilePath , lines , StandardCharsets.UTF_8 );
return ( this.fetchGroceryItems ( ).isEmpty ( ) );
}
catch ( IOException e )
{
throw new RuntimeException ( e );
}
}
}
Tip to the advanced student: Repository
should be an interface, with two concrete implementations: one for our in-memory list, another for our file storage.
Beware… This file-based Repository
has a bug: User cannot add more than one grocery item. We can deterimine with certainty that the bug lives in this file-based Repository
implementation because we can switch back to the in-memory list based implementation to see the bug disappear. This bug-verification is a big benefit to writing the simpler bogus implementation. I will leave the undiscovered bug in place, as I have already done too much for a schoolwork assignment.
Another issue: All our tests pass despite the bug in this file-based implementation. That means our tests are insufficient, and need revision. I will leave that too as an exercise for the reader.