1

Is there any way to save shapes and layouts in javafx to an external file. Which can then be restored at a later time. I have tried following YouTube videos and I know how I could save/load files with "Text/Int" fields. However, I need a way of loading the last saved layout.

For example say I want to save a Polygon, I have created the following class:

import javafx.scene.shape.Polygon;

public class saveFile implements java.io.Serializable {
    private static final long serialVersionUID = 1L;
    public Polygon fp; 
    }

Resource Manager Class:

import java.io.ObjectInputStream; 
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.nio.file.Files;
import java.nio.file.Paths;

public class ResourceManager {

public static void save(Serializable data, String fileName) throws Exception  {
    try (ObjectOutputStream oos = new ObjectOutputStream(Files.newOutputStream(Paths.get(fileName)))) {
        oos.writeObject(data);
    }
}

public static Object load(String fileName) throws Exception {
    try (ObjectInputStream ois = new ObjectInputStream(Files.newInputStream(Paths.get(fileName)))) {
        return ois.readObject();
    }
}
}

Controller Class:

//Field
private Polygon fp = new Polygon();
@FXML
private AnchorPane container2;

//Methods
public void saveLayout(){
    saveFile data = new saveFile();
    data.fp = fp;
    try {
        ResourceManager.save(data, "savefile");
    } catch (Exception e) {
        System.out.println("Error");
    }
}

public void loadLayout() throws IOException {
    try {
        saveFile data = (saveFile) ResourceManager.load("save");
        container2.getChildren().add(data.fp);
    }
    catch(Exception e){
        System.out.println("Error");
    }
}

I know the load method is completely wrong right now, however I thought it would be good to provide it anyways.

Edit: e.printStackTrace(); returned:

java.io.NotSerializableException: javafx.scene.shape.Polygon
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184)
at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1548)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1509)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1432)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1178)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)
at Model.ResourceManager.save(ResourceManager.java:14)
at Controller.NewLayoutController.saveLayout(NewLayoutController.java:191)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at sun.reflect.misc.Trampoline.invoke(MethodUtil.java:71)
at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at sun.reflect.misc.MethodUtil.invoke(MethodUtil.java:275)
at javafx.fxml.FXMLLoader$MethodHandler.invoke(FXMLLoader.java:1771)
at javafx.fxml.FXMLLoader$ControllerMethodEventHandler.handle(FXMLLoader.java:1657)
at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:49)
at javafx.event.Event.fireEvent(Event.java:198)
at javafx.scene.control.MenuItem.fire(MenuItem.java:462)
at com.sun.javafx.scene.control.skin.ContextMenuContent$MenuItemContainer.doSelect(ContextMenuContent.java:1405)
at com.sun.javafx.scene.control.skin.ContextMenuContent$MenuItemContainer.lambda$createChildren$343(ContextMenuContent.java:1358)
at com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(CompositeEventHandler.java:218)
at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:80)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54)
at javafx.event.Event.fireEvent(Event.java:198)
at javafx.scene.Scene$MouseHandler.process(Scene.java:3757)
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 what is the exact problem? Your code should work as-is now. – Enigo Jan 16 '17 at 17:36
  • The problem is when I click on the "Save" button in the program it returns the "Error" exception. It does not save it to an external file. – TheBeliever12 Jan 16 '17 at 19:02
  • And what is the exception? – Enigo Jan 16 '17 at 19:51
  • The exception prints the "Error" message I have declared in the exception. I don't understand why it does not save it. Its been confusing me all day – TheBeliever12 Jan 16 '17 at 20:18
  • Well, you should print `e`, as well to unravel that mystery)) `System.out.println("Error", e);` but you should better use logback or something – Enigo Jan 16 '17 at 20:52
  • The error message it returned has been added to my question. Can you tell where I am going wrong? Thanks – TheBeliever12 Jan 16 '17 at 21:39
  • Yep, the problem you're facing is like [this one](http://stackoverflow.com/a/13895894/5151575). I would suggest you to use [gson](https://github.com/google/gson). – Enigo Jan 17 '17 at 07:29

1 Answers1

3

The problem is that Polygon class does not implement Serializable interface, that's why it cannot be serialized with standard java means. For Polygon class it would be enough to save only its points in order to retrieve them later:

public static void save(ObservableList<Double> points, String fileName) throws Exception  {
    try (ObjectOutputStream oos = new ObjectOutputStream(Files.newOutputStream(Paths.get(fileName)))) {
          oos.writeObject(new ArrayList<>(points));
    }
}

and then to read it back:

public static ObservableList<Double> load(String fileName) throws Exception {
    try (ObjectInputStream ois = new ObjectInputStream(Files.newInputStream(Paths.get(fileName)))) {
        List<Double> list = (List<Double>) ois.readObject();
        return FXCollections.observableList(list);
    }
}

And then you can construct new Polygon like:

ObservableList<Double> points = ResourceManager.load("save");
Polygon polygon = new Polygon()
polygon.getPoints().addAll(points);
Enigo
  • 3,685
  • 5
  • 29
  • 54
  • And once again, for more complex serialization you should take a look on more complex tools for it (like gson). For Polygon it's rather simple, but this approach may be enough for other shapes you need to serialize. – Enigo Jan 17 '17 at 08:02
  • Nice to hear that) – Enigo Jan 17 '17 at 19:42