-1

I'm trying to create a simple JavaFX project for school that lets the user play the game Nim. In my head the concept was simple because I have already worked with java and JavaFX before. The error occures when I try to set items in TableView by passing in a observable list. I am sure that the list contains objects that I should be able to add. I have no idea whats wrong.

Here Is all of my code:

MainController class:

package controllers;

import format.FormatStringValueForTableView;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.TextField;
import javafx.stage.Stage;


import java.io.IOException;
import java.util.Random;

public class MainController {

    @FXML private TextField inputTextField;
    private final FormatStringValueForTableView formatter = new FormatStringValueForTableView();

    @FXML
    private void continueToGame(ActionEvent event) throws IOException {
        FXMLLoader parent = new FXMLLoader(getClass().getClassLoader().getResource("ui/nim_game.fxml"));
        Parent root = parent.load();

        NimGameController nimGameController = parent.getController();
        nimGameController.setList(formatter.formatString(inputTextField.getText()));

        Scene scene = new Scene(root);
        Stage appStage = (Stage) ((Node) event.getSource()).getScene().getWindow();
        appStage.setScene(scene);
        appStage.show();

    }
    @FXML
    private void generateRandomNumber(ActionEvent event){
        Random random = new Random();
        int generatedNumber = random.nextInt(999) + 22;
        inputTextField.setText(Integer.toString(generatedNumber));
    }
}

NimGameController class:

package controllers;

import entities.Node;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.stage.Stage;

import java.io.IOException;
import java.net.URL;
import java.util.List;
import java.util.ResourceBundle;

public class NimGameController implements Initializable {

    private ObservableList<Node> textInTextFieldToList;
    @FXML private TableView<Node> tableView;

    public void setList(ObservableList<Node> textInTextFieldToList) {
        this.textInTextFieldToList = textInTextFieldToList;
    }

    @Override
    public void initialize(URL url, ResourceBundle resourceBundle) {
        configureTable();
        populateTable();
    }

    @FXML
    private void showNumsForDebug(ActionEvent event) {
        textInTextFieldToList.forEach(System.out::println);
    }

    @FXML
    private void back(ActionEvent event) throws IOException {
        Parent parent = FXMLLoader.load(getClass().getClassLoader().getResource("ui/main.fxml"));
        Scene scene = new Scene(parent);
        Stage appStage = (Stage) ((javafx.scene.Node) event.getSource()).getScene().getWindow();
        appStage.setScene(scene);
        appStage.show();
    }

    private void configureTable() {
        TableColumn<Node, Integer> itemColumn = new TableColumn<>("Item");
        itemColumn.prefWidthProperty().bind(tableView.widthProperty().multiply(0.3));
        itemColumn.setCellValueFactory(new PropertyValueFactory<>("value"));
        itemColumn.setResizable(false);

        TableColumn<Node, Integer> levelColumn = new TableColumn<>("Level");
        levelColumn.prefWidthProperty().bind(tableView.widthProperty().multiply(0.3));
        levelColumn.setCellValueFactory(new PropertyValueFactory<>("level"));
        levelColumn.setResizable(false);

        TableColumn<Node, Integer> functionValColumn = new TableColumn<>("Func. Val.");
        functionValColumn.prefWidthProperty().bind(tableView.widthProperty().multiply(0.4));
        functionValColumn.setCellValueFactory(new PropertyValueFactory<>("functionValue"));
        functionValColumn.setResizable(false);

        tableView.getColumns().add(itemColumn);
        tableView.getColumns().add(levelColumn);
        tableView.getColumns().add(functionValColumn);
    }

    public void populateTable(){
        if(!textInTextFieldToList.isEmpty()){
            tableView.setItems(textInTextFieldToList);
        } else {
            System.out.println("List is empty");
        }
    }
}

FormatStringValueForTableView class:

package format;

import entities.Node;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;

public class FormatStringValueForTableView {

    public ObservableList<Node> formatString(String numberString) {
        ObservableList<Node> list = FXCollections.observableArrayList();
        for(int i = 0; i < numberString.length(); i++) {
            if(Character.isDigit(numberString.charAt(i)))
                list.add(new Node(getIntValueFromChar(numberString.charAt(i))));
        }
        return list;
    }

    public Integer getIntValueFromChar(char c) {
        return Integer.parseInt(String.valueOf(c));
    }

}

Node class:

package entities;

public class Node {

    private Integer value;
    private Integer level;
    private Integer functionValue;

    public Node(Integer value) {
        this.value = value;
        this.level = 0;
        this.functionValue = 0;
    }

    public Integer getValue() {
        return value;
    }

    public void setValue(Integer value) {
        this.value = value;
    }

    public Integer getLevel() {
        return level;
    }

    public void setLevel(Integer level) {
        this.level = level;
    }

    public Integer getFunctionValue() {
        return functionValue;
    }

    public void setFunctionValue(Integer functionValue) {
        this.functionValue = functionValue;
    }

    @Override
    public String toString() {
        return "Node{" +
                "value=" + value +
                ", level=" + level +
                ", functionValue=" + functionValue +
                '}';
    }
}

main.fxml file:

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.text.*?>

<GridPane alignment="center" hgap="10" maxHeight="300.0" maxWidth="500.0" minHeight="-Infinity" minWidth="-Infinity" prefHeight="200.0" prefWidth="431.0" vgap="10" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="controllers.MainController">
   <children>
      <AnchorPane maxHeight="300.0" maxWidth="500.0" minHeight="-Infinity" minWidth="-Infinity" prefHeight="200.0" prefWidth="400.0">
         <children>
            <SplitPane dividerPositions="0.17676767676767677" orientation="VERTICAL" prefHeight="200.0" prefWidth="400.0">
              <items>
                <AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="100.0" prefWidth="160.0">
                     <children>
                        <Label alignment="CENTER" layoutX="80.0" layoutY="-2.0" prefHeight="40.0" prefWidth="238.0" text="Welcome to NIM game." underline="true">
                           <font>
                              <Font name="System Bold" size="18.0" />
                           </font>
                        </Label>
                     </children>
                  </AnchorPane>
                <AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="160.0" prefWidth="341.0">
                     <children>
                        <Label layoutX="49.0" layoutY="14.0" prefHeight="30.0" prefWidth="300.0" text="Enter a string of numbers that will be split: ">
                           <font>
                              <Font size="15.0" />
                           </font></Label>
                        <TextField fx:id="inputTextField" layoutX="49.0" layoutY="54.0" prefHeight="25.0" prefWidth="300.0" />
                        <Button layoutX="47.0" layoutY="104.0" mnemonicParsing="false" onAction="#continueToGame" prefHeight="32.0" prefWidth="102.0" text="Continue" />
                        <Button layoutX="247.0" layoutY="104.0" mnemonicParsing="false" onAction="#generateRandomNumber" prefHeight="32.0" prefWidth="102.0" text="Random" />
                     </children></AnchorPane>
              </items>
            </SplitPane>
         </children>
      </AnchorPane>
   </children>
   <columnConstraints>
      <ColumnConstraints />
   </columnConstraints>
   <rowConstraints>
      <RowConstraints />
   </rowConstraints>
</GridPane>

nim_game.fxml file:

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>

<AnchorPane prefHeight="400" prefWidth="900" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="controllers.NimGameController">
   <children>
      <TableView fx:id="tableView" layoutX="681.0" layoutY="14.0" prefHeight="282.0" prefWidth="205.0">
         <columns>

         </columns>
      </TableView>
      <Button layoutX="787.0" layoutY="353.0" mnemonicParsing="false" onAction="#back" prefHeight="32.0" prefWidth="99.0" text="Back" />
      <Button layoutX="787.0" layoutY="305.0" mnemonicParsing="false" onAction="#showNumsForDebug" prefHeight="32.0" prefWidth="99.0" text="Show" />
   </children>
</AnchorPane>

Here is the stack trace:

Exception in thread "JavaFX Application Thread" java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
    at javafx.fxml/javafx.fxml.FXMLLoader$MethodHandler.invoke(FXMLLoader.java:1787)
    at javafx.fxml/javafx.fxml.FXMLLoader$ControllerMethodEventHandler.handle(FXMLLoader.java:1670)
    at javafx.base/com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86)
    at javafx.base/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
    at javafx.base/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
    at javafx.base/com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
    at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
    at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base/com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
    at javafx.base/com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:49)
    at javafx.base/javafx.event.Event.fireEvent(Event.java:198)
    at javafx.graphics/javafx.scene.Node.fireEvent(Node.java:8879)
    at javafx.controls/javafx.scene.control.Button.fire(Button.java:200)
    at javafx.controls/com.sun.javafx.scene.control.behavior.ButtonBehavior.mouseReleased(ButtonBehavior.java:206)
    at javafx.controls/com.sun.javafx.scene.control.inputmap.InputMap.handle(InputMap.java:274)
    at javafx.base/com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(CompositeEventHandler.java:218)
    at javafx.base/com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:80)
    at javafx.base/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
    at javafx.base/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
    at javafx.base/com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
    at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
    at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base/com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
    at javafx.base/com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54)
    at javafx.base/javafx.event.Event.fireEvent(Event.java:198)
    at javafx.graphics/javafx.scene.Scene$MouseHandler.process(Scene.java:3851)
    at javafx.graphics/javafx.scene.Scene$MouseHandler.access$1200(Scene.java:3579)
    at javafx.graphics/javafx.scene.Scene.processMouseEvent(Scene.java:1849)
    at javafx.graphics/javafx.scene.Scene$ScenePeerListener.mouseEvent(Scene.java:2588)
    at javafx.graphics/com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:397)
    at javafx.graphics/com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:295)
    at java.base/java.security.AccessController.doPrivileged(Native Method)
    at javafx.graphics/com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleMouseEvent$2(GlassViewEventHandler.java:434)
    at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:390)
    at javafx.graphics/com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(GlassViewEventHandler.java:433)
    at javafx.graphics/com.sun.glass.ui.View.handleMouseEvent(View.java:556)
    at javafx.graphics/com.sun.glass.ui.View.notifyMouse(View.java:942)
    at javafx.graphics/com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
    at javafx.graphics/com.sun.glass.ui.win.WinApplication.lambda$runLoop$3(WinApplication.java:174)
    at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: java.lang.reflect.InvocationTargetException
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    at com.sun.javafx.reflect.Trampoline.invoke(MethodUtil.java:76)
    at jdk.internal.reflect.GeneratedMethodAccessor2.invoke(Unknown Source)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    at javafx.base/com.sun.javafx.reflect.MethodUtil.invoke(MethodUtil.java:273)
    at javafx.fxml/com.sun.javafx.fxml.MethodHelper.invoke(MethodHelper.java:83)
    at javafx.fxml/javafx.fxml.FXMLLoader$MethodHandler.invoke(FXMLLoader.java:1782)
    ... 51 more
Caused by: javafx.fxml.LoadException: 
/C:/Users/marci/IDEAPROJECTS/NimGameWithMINIMAX/target/classes/ui/nim_game.fxml

    at javafx.fxml/javafx.fxml.FXMLLoader.constructLoadException(FXMLLoader.java:2625)
    at javafx.fxml/javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2603)
    at javafx.fxml/javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2466)
    at javafx.fxml/javafx.fxml.FXMLLoader.load(FXMLLoader.java:2435)
    at controllers.MainController.continueToGame(MainController.java:25)
    ... 62 more
Caused by: java.lang.NullPointerException
    at controllers.NimGameController.populateTable(NimGameController.java:73)
    at controllers.NimGameController.initialize(NimGameController.java:34)
    at javafx.fxml/javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2573)
    ... 65 more

1 Answers1

2

initialize(), and hence populateTable(), is called during the call to FXMLLoader.load(), which necessarily happens before you call setList(...).

Consequently when populateTable() is invoked, textInTextFieldToList is still null and textInTextFieldToList.isEmpty() throws a NullPointerException.

Simply move the call to populateTable to setList(). This makes sense since you can't populate the table until the list is set, and if you were to call setList() another time, you'd presumably want to repopulate the table.

public void setList(ObservableList<Node> textInTextFieldToList) {
    this.textInTextFieldToList = textInTextFieldToList;
    populateTable();
}

@Override
public void initialize(URL url, ResourceBundle resourceBundle) {
    configureTable();
}
James_D
  • 201,275
  • 16
  • 291
  • 322