I am making this JavaFX mass calculator, that essentially consists of a button that executes a SwingWorker
background thread to calculate certain ratios or factors or whatnot.
I've realized that for my factor and prime factor calculations (when I try to find all the factors and prime factors of a number the user inputs), I get this NullPointerException
after the calculation has been performed about 3-5 times.
Here's the script that executes when the button is clicked:
public static longCalculationThread calculationThread;
//Called by the calculate button
@FXML
void callCalculation(ActionEvent event) throws IOException, InterruptedException {
Long number = 0L;
Long lowerBound = 0L;
Long higherBound = 0L;
try {
//Bunch of input verification logic here. The logic here also creates the variables for the thread bundle
//Enabling progress UI since no errors were encoundered...
excelPane.setVisible(false);
progressPane.setDisable(false);
progressPane.setVisible(true);
//Preparing threadBundle, which is just a bunch of fields out into a data holding class [not shown]
calculationThread = new longCalculationThread(threadBundle);
calculationThread.execute();
System.gc(); //For the objects of the previous calculationThread
}
}
And here's how the longCalculationThread
is structured:
package PrimusForPC;
public class longCalculationThread extends SwingWorker<Void, Void> {
private ArrayList<Long> longResults = new ArrayList<>();
private ArrayList<String> stringResults = new ArrayList<>();
private calculationHolder.calculationType type;
private JFXProgressBar progressBar;
private Text progressText;
private GridView<String> answerGridView;
private Pane mainPane, progressPane;
private long mainNumber, lowerNum, upperNum;
private boolean shouldExport;
private ExcelWriter.spreadsheetTypes exportType;
private int excelExportLimit;
private JFXButton calcButton;
private Menu selectionMenu;
private static double acceptableAccuracy = 0.004; //The degree of accuracty for pi and phi couples
public static final double PHI = 1.6180339887498948482045;
public boolean shouldDie; //Called externally to kill the thread. Hasn't been implemented yet
longCalculationThread(threadArgumentBundle bundle){
progressBar = bundle.progressBar;
answerGridView = bundle.answerGridView;
type = bundle.type;
mainNumber = bundle.primaryNumber;
upperNum = bundle.upperNum;
lowerNum = bundle.lowerNum;
progressText = bundle.progressText;
mainPane = bundle.mainPane;
progressPane = bundle.progressPane;
exportType = bundle.exportType;
shouldExport = bundle.shouldExport;
excelExportLimit = bundle.excelExportLimit;
calcButton = bundle.button;
selectionMenu = bundle.selectionMenu;
}
@Override
public Void doInBackground(){
System.out.println("Thread started " + Thread.currentThread().getName());
Platform.runLater(new Runnable() {
@Override
public void run() {
progressBar.isIndeterminate();
progressBar.progressProperty().setValue(0);
calcButton.setDisable(true);
selectionMenu.setDisable(true);
}
});
switch (type){
case factors:
findfactors();
break;
case primeFactors:
findPrimeFactors();
break;
case primesUpToX:
findPrimesUpToX();
break;
//Bunch of other cases here...
}
try {
publishResults();
} catch (IOException | InterruptedException ex) {
Logger.getLogger(longCalculationThread.class.getName()).log(Level.SEVERE, null, ex);
}
reEndableInterface();
System.gc();
System.out.println("Thread ended " + Thread.currentThread().getName() + "\n");
return null;
}
static private long getSquareRoot (long number){
//For calculations square root only matters for larger numbers
//for numbers below 5 it doesn't really matter
if (number > 5){
number = (long) Math.ceil(Math.sqrt(number));
}
return number;
}
private void findfactors(){
progressText.setText("Finding factors...");
long root = getSquareRoot(mainNumber);
double incrementValue = 1/(double)root;
for (long divisor = 1; divisor <= root; divisor++) {
setProgressBar(progressBar.getProgress() + incrementValue);
if (mainNumber % divisor == 0) {
longResults.add(divisor);
longResults.add((mainNumber/divisor));
}
}
}
private void findPrimeFactors(){
findfactors();
progressText.setText("Separating prime factors from composite factors...");
progressBar.progressProperty().set(0);
double incrementValue = 1/(double)longResults.size();
ArrayList <Long> realResults = new ArrayList<>();
for (long factor: longResults){
if (isPrime(factor)) realResults.add(factor);
setProgressBar(progressBar.getProgress() + incrementValue);
}
longResults = realResults;
}
static boolean isPrime(long number){
if (number == 1) return false;
if (number == 2 || number == 3 || number == 5) return true;
long root = getSquareRoot(number);
for (int divisor = 2; divisor <= root; divisor++)
if (number % divisor == 0) return false;
return true;
}
private void publishResults() throws IOException, InterruptedException{
Platform.runLater(new Runnable() {
@Override
public void run() {
answerGridView.getItems().setAll(stringResults);
}
});
//To make UI interface useable again
private void reEndableInterface(){
Platform.runLater(new Runnable() {
@Override
public void run() {
progressPane.setDisable(true);
progressPane.setVisible(false);
mainPane.setDisable(false);
mainPane.setVisible(true);
calcButton.setDisable(false);
System.out.println("Button enabled " + Thread.currentThread().getName());
selectionMenu.setDisable(false);
}
});
}
I'm not exactly sure where this NullPointerException
is coming from, or why it seems to only surface when I'm using the "find factors" or "find prime factors" calculations. Here is a look at the error log (sorry it's so long, didn't know where to cut off).
Exception in thread "JavaFX Application Thread" java.lang.NullPointerException
at com.sun.javafx.text.PrismTextLayout.addTextRun(PrismTextLayout.java:755)
at com.sun.javafx.text.GlyphLayout.addTextRun(GlyphLayout.java:140)
at com.sun.javafx.text.GlyphLayout.breakRuns(GlyphLayout.java:312)
at com.sun.javafx.text.PrismTextLayout.buildRuns(PrismTextLayout.java:770)
at com.sun.javafx.text.PrismTextLayout.layout(PrismTextLayout.java:1021)
at com.sun.javafx.text.PrismTextLayout.ensureLayout(PrismTextLayout.java:223)
at com.sun.javafx.text.PrismTextLayout.getBounds(PrismTextLayout.java:246)
at javafx.scene.text.Text.getLogicalBounds(Text.java:358)
at javafx.scene.text.Text.impl_computeGeomBounds(Text.java:1168)
at javafx.scene.Node.updateGeomBounds(Node.java:3577)
at javafx.scene.Node.getGeomBounds(Node.java:3530)
at javafx.scene.Node.getLocalBounds(Node.java:3478)
at javafx.scene.Node.updateTxBounds(Node.java:3641)
at javafx.scene.Node.getTransformedBounds(Node.java:3424)
at javafx.scene.Parent.getChildTransformedBounds(Parent.java:1724)
at javafx.scene.Parent.updateCachedBounds(Parent.java:1588)
at javafx.scene.Parent.recomputeBounds(Parent.java:1527)
at javafx.scene.Parent.impl_computeGeomBounds(Parent.java:1380)
at javafx.scene.layout.Region.impl_computeGeomBounds(Region.java:3078)
at javafx.scene.Node.updateGeomBounds(Node.java:3577)
at javafx.scene.Node.getGeomBounds(Node.java:3530)
at javafx.scene.Node.getLocalBounds(Node.java:3478)
at javafx.scene.Node.updateTxBounds(Node.java:3641)
at javafx.scene.Node.getTransformedBounds(Node.java:3424)
at javafx.scene.Parent.getChildTransformedBounds(Parent.java:1724)
at javafx.scene.Parent.updateCachedBounds(Parent.java:1588)
at javafx.scene.Parent.recomputeBounds(Parent.java:1527)
at javafx.scene.Parent.impl_computeGeomBounds(Parent.java:1380)
at javafx.scene.layout.Region.impl_computeGeomBounds(Region.java:3078)
at javafx.scene.Node.updateGeomBounds(Node.java:3577)
at javafx.scene.Node.getGeomBounds(Node.java:3530)
at javafx.scene.Node.getLocalBounds(Node.java:3478)
at javafx.scene.Node.updateTxBounds(Node.java:3641)
at javafx.scene.Node.getTransformedBounds(Node.java:3424)
at javafx.scene.Parent.getChildTransformedBounds(Parent.java:1724)
at javafx.scene.Parent.updateCachedBounds(Parent.java:1588)
at javafx.scene.Parent.recomputeBounds(Parent.java:1527)
at javafx.scene.Parent.impl_computeGeomBounds(Parent.java:1380)
at javafx.scene.layout.Region.impl_computeGeomBounds(Region.java:3078)
at javafx.scene.Node.updateGeomBounds(Node.java:3577)
at javafx.scene.Node.getGeomBounds(Node.java:3530)
at javafx.scene.Node.getLocalBounds(Node.java:3478)
at javafx.scene.Node.impl_intersectsBounds(Node.java:5013)
at javafx.scene.layout.Region.impl_pickNodeLocal(Region.java:2931)
at javafx.scene.Node.impl_pickNode(Node.java:4912)
at javafx.scene.layout.Region.impl_pickNodeLocal(Region.java:2936)
at javafx.scene.Node.impl_pickNode(Node.java:4912)
at javafx.scene.Scene$MouseHandler.pickNode(Scene.java:3899)
at javafx.scene.Scene$MouseHandler.access$1600(Scene.java:3485)
at javafx.scene.Scene.pick(Scene.java:1942)
at javafx.scene.Scene.access$6700(Scene.java:159)
at javafx.scene.Scene$MouseHandler.process(Scene.java:3799)
at javafx.scene.Scene$MouseHandler.access$1500(Scene.java:3485)
at javafx.scene.Scene.impl_processMouseEvent(Scene.java:1762)
at javafx.scene.Scene$ScenePeerListener.mouseEvent(Scene.java:2494)
at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:352)
at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:275)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleMouseEvent$355(GlassViewEventHandler.java:388)
at com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:389)
at com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(GlassViewEventHandler.java:387)
at com.sun.glass.ui.View.handleMouseEvent(View.java:555)
at com.sun.glass.ui.View.notifyMouse(View.java:937)
at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at com.sun.glass.ui.win.WinApplication.lambda$null$149(WinApplication.java:191)
at java.lang.Thread.run(Thread.java:745)
And then at the end the console just spams:
java.lang.ArrayIndexOutOfBoundsException
indefinitely.
As I said, I really don't know why this is happening. The calculation works for the first few times, then this error occurs and freezes the application, requiring a complete restart.