Search code examples
javajavafxstringbuffer

Cannot reset StringBuffer in JavaFX Application


I am working on a college course project, a JavaFX application that is simulating a garage with vehicles moving around. Following on this answer, I created a mechanism for constantnly refreshing GUI without flooding the JavaFX thread.

The entire code can be found here.

Observer is a daemon thread whose task is computing a String value for output and sending it it to a TextArea, which is constantly being updated.

Observer run method:

    @Override
    public void run() {
        while (true) {
            synchronized (Garage.getLock()) {
                try {
//                  buffer = new StringBuffer("");

                    buffer.append('\n');
                    garage.print(buffer);
                    buffer.append('\n');
                    garage.outputText.set(buffer.toString());
                    System.out.println(buffer.toString());
                    Garage.getLock().wait(3000);
                } catch (InterruptedException exception) {
                    exception.printStackTrace();
                } finally {
                    Garage.getLock().notifyAll();
                }
            }
        }
    }

Observer object has a reference to the model class - garage. print() method of the Garage class basically appends stuff to the buffer.

The complete output is printed to the console, which works fine. The output is also used to set outputText, a SimpleStringProperty, which has a listener attached in the MainControllerClass.

outputText member:

public class Garage implements Externalizable {
    ...
    public SimpleStringProperty outputText = new SimpleStringProperty();
    ...
}

Refreshing the GUI is initiated with a button click.

MainController class:

package garage.controller;

import java.util.concurrent.atomic.AtomicInteger;

import garage.model.*;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.fxml.FXML;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextArea;
import javafx.stage.Stage;

public class MainController {

    @FXML
    private TextArea output;

    @FXML
    private Button startButton;

    private Garage model;

    public MainController() {

    }

    private AtomicInteger control = new AtomicInteger(-1);

    @FXML
    public void initialize() {

    }

    @FXML
    private void handleStartButton() {
        model.outputText.addListener(new ChangeListener<String>() {
            @Override
            public void changed(final ObservableValue<? extends String> observable, final String oldValue,
                    final String newValue) {
                if (control.getAndSet(1) == -1) {
                    javafx.application.Platform.runLater(new Runnable() {
                        @Override
                        public void run() {
                            control.set(-1);
                            output.setText(newValue);
                        }
                    });
                }

            }
        });

    }

    public void setModel(Garage model) {
        this.model = model;
    }

}

When I run the application and click the button, everything is going well. TextArea is updated in real time. Since the output is constantly being appended to the StringBuffer, I wanted to refresh it in every cycle and get something like a simulation.

This is where the problems start.

When I uncomment the line in the Observer run method

//                  buffer = new StringBuffer("");

nothing is being printed to the TextArea. The console output is working well.

I also tried with other StringBuffer methods like delete and setLenght but nothing seems to work. However I try to clean the buffer, TextArea is no longer updated.

I cannot seem to reset the StringBuffer.

What am I missing here?

EDIT: print() methods

Garage print method

public void print(StringBuffer buffer) {
        synchronized (Garage.lock) {
            synchronized (lock) {
                for (int i = 0; i < platformCount; i++)
                    platforms.get(i).print(buffer);
            }
        }
    }

GarageItem print method

package garage.model;

import java.io.*;

public interface GarageItem extends Serializable {
    public void print(StringBuffer buffer);

}

Lane print method

package garage.model;

public class Lane implements GarageItem {
    private static final long serialVersionUID = 5L;

    @Override
    public void print(StringBuffer buffer) {
        synchronized (Garage.getLock()) {
            buffer.append('.');
        }
    }
}

ParkingSpot print method

package garage.model;

public class ParkingSpot implements GarageItem {
    private static final long serialVersionUID = 4L;

    @Override
    public void print(StringBuffer buffer) {
        synchronized (Garage.getLock()) {
            buffer.append('*');
        }

    }

}

Vehicle print method

@Override
    public void print(StringBuffer buffer) {
        synchronized (Garage.getLock()) {
            buffer.append(symbol);
        }
    }

where symbol is 'V'.


Solution

  • Found the answer today. My text example was trivial and by the time I clicked the handleStartButton, all of the cars have finished moving and parked in the garage.

    Because I reset the buffer in every loop iteration, it meant that the state of the garage didn't change anymore and it's String representation was the same. Because of this, change listener was not triggered and nothing was printed out to the TextArea.