From aa3abaece3b2917a6959d5da1ba8af8ea52e2dbe Mon Sep 17 00:00:00 2001
From: Hallvard Traetteberg <hal@ntnu.no>
Date: Sun, 25 Aug 2019 23:39:31 +0200
Subject: [PATCH] Fjernet generell File-meny (#4)

---
 .../doc/AbstractDocumentStorageImpl.java      | 154 -------------
 .../java/fxutil/doc/FileMenuController.java   | 216 ------------------
 .../java/fxutil/doc/IDocumentImporter.java    |  21 --
 .../java/fxutil/doc/IDocumentListener.java    |  20 --
 .../main/java/fxutil/doc/IDocumentLoader.java |  23 --
 .../java/fxutil/doc/IDocumentPersistence.java |   4 -
 .../main/java/fxutil/doc/IDocumentSaver.java  |  21 --
 .../java/fxutil/doc/IDocumentStorage.java     |  68 ------
 .../fxutil/doc/IDocumentStorageListener.java  |  19 --
 .../fxutil/doc/SimpleJsonFileStorageImpl.java |  68 ------
 .../java/simpleex/ui/FxAppController.java     | 104 +++++++--
 .../main/java/simpleex/ui/LatLongsApp.java    | 101 --------
 .../java/simpleex/ui/LatLongsStorage.java     |  28 ---
 .../main/resources/simpleex/ui/FileMenu.fxml  |  23 --
 .../src/main/resources/simpleex/ui/FxApp.fxml |   7 +-
 .../test/resources/simpleex/ui/FileMenu.fxml  |  23 --
 .../src/test/resources/simpleex/ui/FxApp.fxml |   7 +-
 17 files changed, 96 insertions(+), 811 deletions(-)
 delete mode 100644 simpleexample/src/main/java/fxutil/doc/AbstractDocumentStorageImpl.java
 delete mode 100644 simpleexample/src/main/java/fxutil/doc/FileMenuController.java
 delete mode 100644 simpleexample/src/main/java/fxutil/doc/IDocumentImporter.java
 delete mode 100644 simpleexample/src/main/java/fxutil/doc/IDocumentListener.java
 delete mode 100644 simpleexample/src/main/java/fxutil/doc/IDocumentLoader.java
 delete mode 100644 simpleexample/src/main/java/fxutil/doc/IDocumentPersistence.java
 delete mode 100644 simpleexample/src/main/java/fxutil/doc/IDocumentSaver.java
 delete mode 100644 simpleexample/src/main/java/fxutil/doc/IDocumentStorage.java
 delete mode 100644 simpleexample/src/main/java/fxutil/doc/IDocumentStorageListener.java
 delete mode 100644 simpleexample/src/main/java/fxutil/doc/SimpleJsonFileStorageImpl.java
 delete mode 100644 simpleexample/src/main/java/simpleex/ui/LatLongsApp.java
 delete mode 100644 simpleexample/src/main/java/simpleex/ui/LatLongsStorage.java
 delete mode 100644 simpleexample/src/main/resources/simpleex/ui/FileMenu.fxml
 delete mode 100644 simpleexample/src/test/resources/simpleex/ui/FileMenu.fxml

diff --git a/simpleexample/src/main/java/fxutil/doc/AbstractDocumentStorageImpl.java b/simpleexample/src/main/java/fxutil/doc/AbstractDocumentStorageImpl.java
deleted file mode 100644
index 94fa81a..0000000
--- a/simpleexample/src/main/java/fxutil/doc/AbstractDocumentStorageImpl.java
+++ /dev/null
@@ -1,154 +0,0 @@
-package fxutil.doc;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URI;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.Collection;
-
-/**
- * Incomplete implementation of **IDocumentStorage**, to simplify implementing ones for specific
- * document and location types. The main missing methods are for getting and setting the current
- * document, creating an empty one and creating an **InputStream** from a location.
- *
- * @author hal
- *
- * @param <D> the document type
- * @param <L> the location type
- */
-public abstract class AbstractDocumentStorageImpl<D, L>
-implements IDocumentStorage<L>, IDocumentPersistence<D, L> {
-
-  private L documentLocation = null;
-
-  @Override
-  public L getDocumentLocation() {
-    return documentLocation;
-  }
-
-  @Override
-  public void setDocumentLocation(final L documentLocation) {
-    final L oldDocumentLocation = this.documentLocation;
-    this.documentLocation = documentLocation;
-    fireDocumentLocationChanged(oldDocumentLocation);
-  }
-
-  protected void setDocumentAndLocation(final D document, final L documentLocation) {
-    setDocument(document);
-    setDocumentLocation(documentLocation);
-  }
-
-  /**
-   * Returns the current document.
-   *
-   * @return the current document
-   */
-  protected abstract D getDocument();
-
-  /**
-   * Sets the current document.
-   *
-   * @param document the new document
-   */
-  protected abstract void setDocument(D document);
-
-  //
-
-  private final Collection<IDocumentStorageListener<L>> documentListeners =
-      new ArrayList<IDocumentStorageListener<L>>();
-
-  @Override
-  public void addDocumentStorageListener(
-      final IDocumentStorageListener<L> documentStorageListener) {
-    documentListeners.add(documentStorageListener);
-  }
-
-  @Override
-  public void removeDocumentStorageListener(
-      final IDocumentStorageListener<L> documentStorageListener) {
-    documentListeners.remove(documentStorageListener);
-  }
-
-  protected void fireDocumentLocationChanged(final L oldDocumentLocation) {
-    for (final IDocumentStorageListener<L> documentStorageListener : documentListeners) {
-      documentStorageListener.documentLocationChanged(documentLocation, oldDocumentLocation);
-    }
-  }
-
-  protected void fireDocumentChanged(final D oldDocument) {
-    for (final IDocumentStorageListener<L> documentListener : documentListeners) {
-      if (documentListener instanceof IDocumentListener) {
-        ((IDocumentListener<D, L>) documentListener).documentChanged(getDocument(), oldDocument);
-      }
-    }
-  }
-
-  /**
-   * Creates a new and empty document.
-   *
-   * @return
-   */
-  protected abstract D createDocument();
-
-  @Override
-  public void newDocument() {
-    setDocumentAndLocation(createDocument(), null);
-  }
-
-  /**
-   * Creates an InputStream from a location.
-   *
-   * @param location the location
-   * @return the location's InputStream
-   * @throws IOException when the InputStream cannot be created
-   */
-  protected abstract InputStream toInputStream(L location) throws IOException;
-
-  protected InputStream toInputStream(final File location) throws IOException {
-    return new FileInputStream(location);
-  }
-
-  protected InputStream toInputStream(final URL location) throws IOException {
-    return location.openStream();
-  }
-
-  protected InputStream toInputStream(final URI location) throws IOException {
-    return toInputStream(location.toURL());
-  }
-
-  @Override
-  public void openDocument(final L storage) throws IOException {
-    try (InputStream input = toInputStream(storage)) {
-      setDocumentAndLocation(loadDocument(input), storage);
-    } catch (final Exception e) {
-      throw new IOException(e);
-    }
-  }
-
-  @Override
-  public void saveDocument() throws IOException {
-    try {
-      saveDocument(getDocument(), getDocumentLocation());
-    } catch (final Exception e) {
-      throw new IOException(e);
-    }
-  }
-
-  public void saveDocumentAs(final L documentLocation) throws IOException {
-    final L oldDocumentLocation = getDocumentLocation();
-    setDocumentLocation(documentLocation);
-    try {
-      saveDocument();
-    } catch (final IOException e) {
-      setDocumentLocation(oldDocumentLocation);
-      throw e;
-    }
-  }
-
-  public void saveCopyAs(final L documentLocation) throws Exception {
-    saveDocument(getDocument(), documentLocation);
-  }
-}
diff --git a/simpleexample/src/main/java/fxutil/doc/FileMenuController.java b/simpleexample/src/main/java/fxutil/doc/FileMenuController.java
deleted file mode 100644
index afe841c..0000000
--- a/simpleexample/src/main/java/fxutil/doc/FileMenuController.java
+++ /dev/null
@@ -1,216 +0,0 @@
-package fxutil.doc;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Optional;
-import javafx.event.ActionEvent;
-import javafx.fxml.FXML;
-import javafx.scene.control.Alert;
-import javafx.scene.control.Alert.AlertType;
-import javafx.scene.control.ButtonType;
-import javafx.scene.control.Menu;
-import javafx.scene.control.MenuItem;
-import javafx.scene.control.TextInputDialog;
-import javafx.stage.FileChooser;
-
-public class FileMenuController {
-
-  private IDocumentStorage<File> documentStorage;
-
-  public void setDocumentStorage(final IDocumentStorage<File> documentStorage) {
-    this.documentStorage = documentStorage;
-    if (importMenu != null) {
-      importMenu.setDisable(documentStorage.getDocumentImporters().isEmpty());
-    }
-  }
-
-  @FXML
-  public void handleNewAction() {
-    documentStorage.newDocument();
-  }
-
-  private final List<File> recentFiles = new ArrayList<File>();
-
-  @FXML
-  private Menu recentMenu;
-
-  protected void updateRecentMenu(final File file) {
-    recentFiles.remove(file);
-    recentFiles.add(0, file);
-    recentMenu.getItems().clear();
-    for (final File recentFile : recentFiles) {
-      final MenuItem menuItem = new MenuItem();
-      menuItem.setText(recentFile.toString());
-      menuItem.setOnAction(event -> handleOpenAction(event));
-      recentMenu.getItems().add(menuItem);
-    }
-  }
-
-  private FileChooser fileChooser;
-
-  FileChooser getFileChooser() {
-    if (fileChooser == null) {
-      fileChooser = new FileChooser();
-    }
-    return fileChooser;
-  }
-
-  @FXML
-  public void handleOpenAction(final ActionEvent event) {
-    File selection = null;
-    if (event.getSource() instanceof MenuItem) {
-      final File file = new File(((MenuItem) event.getSource()).getText());
-      if (file.exists()) {
-        selection = file;
-      }
-    }
-    if (selection == null) {
-      final FileChooser fileChooser = getFileChooser();
-      selection = fileChooser.showOpenDialog(null);
-    }
-    if (selection != null) {
-      handleOpenAction(selection);
-    }
-  }
-
-  void handleOpenAction(final File selection) {
-    try {
-      documentStorage.openDocument(selection);
-      updateRecentMenu(selection);
-    } catch (final IOException e) {
-      showExceptionDialog("Oops, problem when opening " + selection, e);
-    }
-  }
-
-  private void showExceptionDialog(final String message) {
-    final Alert alert = new Alert(AlertType.ERROR, message, ButtonType.CLOSE);
-    alert.showAndWait();
-  }
-
-  private void showExceptionDialog(final String message, final Exception e) {
-    showExceptionDialog(message + ": " + e.getLocalizedMessage());
-  }
-
-  private void showSaveExceptionDialog(final File location, final Exception e) {
-    showExceptionDialog("Oops, problem saving to " + location, e);
-  }
-
-  @FXML
-  public void handleSaveAction() {
-    if (documentStorage.getDocumentLocation() == null) {
-      handleSaveAsAction();
-    } else {
-      try {
-        documentStorage.saveDocument();
-      } catch (final IOException e) {
-        showSaveExceptionDialog(documentStorage.getDocumentLocation(), e);
-      }
-    }
-  }
-
-  @FXML
-  public void handleSaveAsAction() {
-    final FileChooser fileChooser = getFileChooser();
-    final File selection = fileChooser.showSaveDialog(null);
-    handleSaveAsAction(selection);
-  }
-
-  void handleSaveAsAction(final File selection) {
-    final File oldStorage = documentStorage.getDocumentLocation();
-    try {
-      documentStorage.setDocumentLocation(selection);
-      documentStorage.saveDocument();
-    } catch (final IOException e) {
-      showSaveExceptionDialog(documentStorage.getDocumentLocation(), e);
-      documentStorage.setDocumentLocation(oldStorage);
-    }
-  }
-
-  @FXML
-  public void handleSaveCopyAsAction() {
-    final FileChooser fileChooser = getFileChooser();
-    final File selection = fileChooser.showSaveDialog(null);
-    handleSaveCopyAsAction(selection);
-  }
-
-  void handleSaveCopyAsAction(final File selection) {
-    final File oldStorage = documentStorage.getDocumentLocation();
-    try {
-      documentStorage.setDocumentLocation(selection);
-      documentStorage.saveDocument();
-    } catch (final IOException e) {
-      showSaveExceptionDialog(selection, e);
-    } finally {
-      documentStorage.setDocumentLocation(oldStorage);
-    }
-  }
-
-  @FXML
-  private Menu importMenu;
-
-  @FXML
-  public void handleFileImportAction() {
-    final FileChooser fileChooser = getFileChooser();
-    final File selection = fileChooser.showOpenDialog(null);
-    // String path = selection.getPath();
-    // int pos = path.lastIndexOf('.');
-    // String ext = (pos > 0 ? path.substring(pos + 1) : null);
-    handleFileImportAction(selection);
-  }
-
-  void handleFileImportAction(final File selection) {
-    for (final IDocumentImporter importer : documentStorage.getDocumentImporters()) {
-      try (InputStream input = new FileInputStream(selection)) {
-        importer.importDocument(input);
-        break;
-      } catch (final Exception e) {
-      }
-    }
-  }
-
-  private TextInputDialog inputDialog;
-
-  @FXML
-  public void handleUrlImportAction() {
-    if (inputDialog == null) {
-      inputDialog = new TextInputDialog();
-    }
-    inputDialog.setTitle("Import from URL");
-    inputDialog.setHeaderText("Enter URL to import from");
-    inputDialog.setContentText("Enter URL: ");
-    // https://developer.garmin.com/downloads/connect-api/sample_file.gpx
-    while (true) {
-      final Optional<String> result = inputDialog.showAndWait();
-      if (!result.isPresent()) {
-        break;
-      }
-      try {
-        if (handleUrlImportAction(new URL(result.get()))) {
-          break;
-        }
-        inputDialog.setHeaderText("Problems reading it...");
-        inputDialog.setContentText("Enter another URL: ");
-      } catch (final MalformedURLException e1) {
-        inputDialog.setContentText("Enter a valid URL: ");
-      }
-    }
-  }
-
-  boolean handleUrlImportAction(final URL url) {
-    for (final IDocumentImporter importer : documentStorage.getDocumentImporters()) {
-      try (InputStream input = url.openStream()) {
-        importer.importDocument(input);
-        return true;
-      } catch (final Exception e) {
-        System.err.println(e.getMessage());
-      }
-    }
-    return false;
-  }
-}
diff --git a/simpleexample/src/main/java/fxutil/doc/IDocumentImporter.java b/simpleexample/src/main/java/fxutil/doc/IDocumentImporter.java
deleted file mode 100644
index 1477a39..0000000
--- a/simpleexample/src/main/java/fxutil/doc/IDocumentImporter.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package fxutil.doc;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-/**
- * An interface with a method for importing domain data from a location. The main use is supporting
- * an **import** action in a **File** menu.
- *
- * @author hal
- *
- */
-public interface IDocumentImporter {
-  /**
-   * Loads a document from the input stream and sets it as the current document.
-   *
-   * @param inputStream the document to import
-   * @throws IOException when the document cannot imported
-   */
-  public void importDocument(InputStream inputStream) throws IOException;
-}
diff --git a/simpleexample/src/main/java/fxutil/doc/IDocumentListener.java b/simpleexample/src/main/java/fxutil/doc/IDocumentListener.java
deleted file mode 100644
index c8c3079..0000000
--- a/simpleexample/src/main/java/fxutil/doc/IDocumentListener.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package fxutil.doc;
-
-/**
- * Listener interface for the (contents of) the (current) document of an IDocumentStorage, e.g. when
- * an **open** action is performed.
- * 
- * @author hal
- *
- * @param <D> the document type
- * @param <L> the location type
- */
-public interface IDocumentListener<D, L> extends IDocumentStorageListener<L> {
-  /**
-   * Notifies that the current document has changed.
-   * 
-   * @param document the new document
-   * @param oldDocument the previous document
-   */
-  public void documentChanged(D document, D oldDocument);
-}
diff --git a/simpleexample/src/main/java/fxutil/doc/IDocumentLoader.java b/simpleexample/src/main/java/fxutil/doc/IDocumentLoader.java
deleted file mode 100644
index 5862261..0000000
--- a/simpleexample/src/main/java/fxutil/doc/IDocumentLoader.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package fxutil.doc;
-
-import java.io.InputStream;
-
-/**
- * An interface with a method for loading and returning a document (domain data container) from an
- * InputStream. This allows various ways of loading or importing domain data, with different sources
- * and formats.
- *
- * @author hal
- *
- * @param <D> the document type
- */
-public interface IDocumentLoader<D> {
-  /**
-   * Loads and returns a new document from an InputStream
-   *
-   * @param inputStream the InputStream to load from
-   * @return the loaded document
-   * @throws Exception when the document couldn't be loaded
-   */
-  public D loadDocument(InputStream inputStream) throws Exception;
-}
diff --git a/simpleexample/src/main/java/fxutil/doc/IDocumentPersistence.java b/simpleexample/src/main/java/fxutil/doc/IDocumentPersistence.java
deleted file mode 100644
index d53b8dd..0000000
--- a/simpleexample/src/main/java/fxutil/doc/IDocumentPersistence.java
+++ /dev/null
@@ -1,4 +0,0 @@
-package fxutil.doc;
-
-public interface IDocumentPersistence<D, L> extends IDocumentLoader<D>, IDocumentSaver<D, L> {
-}
diff --git a/simpleexample/src/main/java/fxutil/doc/IDocumentSaver.java b/simpleexample/src/main/java/fxutil/doc/IDocumentSaver.java
deleted file mode 100644
index 9bfa7be..0000000
--- a/simpleexample/src/main/java/fxutil/doc/IDocumentSaver.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package fxutil.doc;
-
-/**
- * An interface with a method for saving a document (domain data container) to a location. This
- * allows various ways of saving or exporting domain data, to different locations and formats.
- *
- * @author hal
- *
- * @param <D> the document type
- * @param <L> the location type
- */
-public interface IDocumentSaver<D, L> {
-  /**
-   * Saves the provided document to the provided location
-   *
-   * @param document the document to save
-   * @param documentLocation the location to save to
-   * @throws Exception when the document couldn't be saved
-   */
-  public void saveDocument(D document, L documentLocation) throws Exception;
-}
diff --git a/simpleexample/src/main/java/fxutil/doc/IDocumentStorage.java b/simpleexample/src/main/java/fxutil/doc/IDocumentStorage.java
deleted file mode 100644
index 5eecaa0..0000000
--- a/simpleexample/src/main/java/fxutil/doc/IDocumentStorage.java
+++ /dev/null
@@ -1,68 +0,0 @@
-package fxutil.doc;
-
-import java.io.IOException;
-import java.util.Collection;
-
-/**
- * An interface with the methods necessary for supporting the standard File menu actions. The class
- * representing the document (domain data container) is implicit in the implementation of this
- * interface. The interface includes methods for getting and setting the location and creating,
- * opening and saving the (current) document.
- *
- * @author hal
- *
- * @param <L> The type of the location, typically java.io.File.
- */
-public interface IDocumentStorage<L> {
-  /**
-   * Returns the current location (of the current document).
-   *
-   * @return the current location
-   */
-  public L getDocumentLocation();
-
-  /**
-   * Sets the current location (of the current document), can be used by a save-as action.
-   *
-   * @param documentLocation the new document location
-   */
-  public void setDocumentLocation(L documentLocation);
-
-  /**
-   * Adds an IDocumentStorageListener that will be notified when the current location changes.
-   *
-   * @param documentStorageListener the listener to add
-   */
-
-  public void addDocumentStorageListener(IDocumentStorageListener<L> documentStorageListener);
-
-  /**
-   * Removes an IDocumentStorageListener.
-   *
-   * @param documentStorageListener the listener to remove
-   */
-  public void removeDocumentStorageListener(IDocumentStorageListener<L> documentStorageListener);
-
-  /**
-   * Creates a new documents and sets it as the current one, can be used by a new action.
-   */
-  public void newDocument();
-
-  /**
-   * Loads a documents from the provided location and sets it as the current one, can be used by an
-   * open action.
-   */
-  public void openDocument(L documentLocation) throws IOException;
-
-  /**
-   * Saves the current document (to the current location), can be used by a save action.
-   */
-  public void saveDocument() throws IOException;
-
-  /**
-   * Returns the set of IDocumentImporters, can be used by an import action.
-   *
-   * @return
-   */
-  public Collection<IDocumentImporter> getDocumentImporters();
-}
diff --git a/simpleexample/src/main/java/fxutil/doc/IDocumentStorageListener.java b/simpleexample/src/main/java/fxutil/doc/IDocumentStorageListener.java
deleted file mode 100644
index c5d3f0f..0000000
--- a/simpleexample/src/main/java/fxutil/doc/IDocumentStorageListener.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package fxutil.doc;
-
-/**
- * Listener interface for the (current) location of the (current) document of an IDocumentStorage,
- * e.g. when a **save-as** action is performed.
- *
- * @author hal
- *
- * @param <L> the document location type
- */
-public interface IDocumentStorageListener<L> {
-  /**
-   * Notifies that the current document location has changed.
-   *
-   * @param documentLocation the new document location
-   * @param oldDocumentLocation the previous document location
-   */
-  public void documentLocationChanged(L documentLocation, L oldDocumentLocation);
-}
diff --git a/simpleexample/src/main/java/fxutil/doc/SimpleJsonFileStorageImpl.java b/simpleexample/src/main/java/fxutil/doc/SimpleJsonFileStorageImpl.java
deleted file mode 100644
index b081765..0000000
--- a/simpleexample/src/main/java/fxutil/doc/SimpleJsonFileStorageImpl.java
+++ /dev/null
@@ -1,68 +0,0 @@
-package fxutil.doc;
-
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.module.SimpleModule;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.Collection;
-import java.util.Collections;
-
-public abstract class SimpleJsonFileStorageImpl<T> extends AbstractDocumentStorageImpl<T, File>
-implements IDocumentStorage<File> {
-
-  private T document;
-
-  private final ObjectMapper objectMapper;
-
-  private final Class<T> documentClass;
-
-  public SimpleJsonFileStorageImpl(final Class<T> documentClass) {
-    this.documentClass = documentClass;
-    final SimpleModule module = new SimpleModule();
-    configureJacksonModule(module);
-    objectMapper = new ObjectMapper();
-    objectMapper.registerModule(module);
-  }
-
-  protected abstract void configureJacksonModule(final SimpleModule module);
-
-  @Override
-  public T loadDocument(final InputStream inputStream) throws Exception {
-    return objectMapper.readValue(inputStream, documentClass);
-  }
-
-  @Override
-  public void saveDocument(final T document, final File documentLocation) throws Exception {
-    try (OutputStream outputStream = new FileOutputStream(documentLocation, false)) {
-      objectMapper.writeValue(outputStream, document);
-    }
-  }
-
-  @Override
-  public Collection<IDocumentImporter> getDocumentImporters() {
-    return Collections.emptyList();
-  }
-
-  @Override
-  public T getDocument() {
-    return document;
-  }
-
-  @Override
-  public void setDocument(final T document) {
-    final T oldDocument = this.document;
-    this.document = document;
-    fireDocumentChanged(oldDocument);
-  }
-
-  @Override
-  protected T createDocument() {
-    try {
-      return documentClass.getDeclaredConstructor().newInstance();
-    } catch (final Exception e) {
-      throw new RuntimeException("Exception when instantiating " + documentClass, e);
-    }
-  }
-}
diff --git a/simpleexample/src/main/java/simpleex/ui/FxAppController.java b/simpleexample/src/main/java/simpleex/ui/FxAppController.java
index 9750644..1795682 100644
--- a/simpleexample/src/main/java/simpleex/ui/FxAppController.java
+++ b/simpleexample/src/main/java/simpleex/ui/FxAppController.java
@@ -1,40 +1,52 @@
 package simpleex.ui;
 
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.module.SimpleModule;
 import fxmapcontrol.Location;
 import fxmapcontrol.MapBase;
 import fxmapcontrol.MapItemsControl;
 import fxmapcontrol.MapNode;
 import fxmapcontrol.MapProjection;
-import fxutil.doc.FileMenuController;
-import fxutil.doc.IDocumentListener;
 import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
 import java.util.Optional;
 import javafx.collections.FXCollections;
+import javafx.event.ActionEvent;
 import javafx.fxml.FXML;
 import javafx.geometry.Point2D;
 import javafx.scene.Node;
+import javafx.scene.control.Alert;
+import javafx.scene.control.Alert.AlertType;
+import javafx.scene.control.ButtonType;
 import javafx.scene.control.ListView;
 import javafx.scene.control.Slider;
+import javafx.stage.FileChooser;
 import simpleex.core.LatLong;
 import simpleex.core.LatLongs;
+import simpleex.json.LatLongDeserializer;
+import simpleex.json.LatLongSerializer;
+import simpleex.json.LatLongsDeserializer;
+import simpleex.json.LatLongsSerializer;
 
-public class FxAppController extends FileMenuController
-    implements IDocumentListener<LatLongs, File> {
+public class FxAppController {
 
-  private final LatLongsStorage latLongsStorage;
+  private LatLongs latLongs;
 
   public FxAppController() {
-    latLongsStorage = new LatLongsStorage();
-    latLongsStorage.addDocumentStorageListener(this);
+    latLongs = new LatLongs();
   }
 
   public LatLongs getLatLongs() {
-    return latLongsStorage.getDocument();
+    return latLongs;
   }
 
   // to make it testable
   public void setLatLongs(final LatLongs latLongs) {
-    latLongsStorage.setDocument(latLongs);
+    this.latLongs = latLongs;
     updateLocationViewList(0);
   }
 
@@ -57,12 +69,10 @@ public class FxAppController extends FileMenuController
 
   @FXML
   private void initialize() {
-    // fileMenuController.
-    setDocumentStorage(latLongsStorage);
     // map stuff
     // mapView.getChildren().add(MapTileLayer.getOpenStreetMapLayer());
     zoomSlider.valueProperty()
-        .addListener((prop, oldValue, newValue) -> mapView.setZoomLevel(zoomSlider.getValue()));
+    .addListener((prop, oldValue, newValue) -> mapView.setZoomLevel(zoomSlider.getValue()));
     zoomSlider.setValue(8);
     markersParent = new MapItemsControl<MapNode>();
     mapView.getChildren().add(markersParent);
@@ -72,7 +82,7 @@ public class FxAppController extends FileMenuController
     draggableMarkerController = new DraggableNodeController(Optional.of(this::handleMarkerDragged));
     // the location list
     locationListView.getSelectionModel().selectedIndexProperty()
-        .addListener((prop, oldValue, newValue) -> updateMapMarker(true));
+    .addListener((prop, oldValue, newValue) -> updateMapMarker(true));
   }
 
   private void handleMapDragged(final Node node, final double dx, final double dy) {
@@ -155,14 +165,68 @@ public class FxAppController extends FileMenuController
     }
   }
 
-  // IDocumentListener
+  // File menu items
 
-  @Override
-  public void documentLocationChanged(final File documentLocation,
-      final File oldDocumentLocation) {}
+  private FileChooser fileChooser;
 
-  @Override
-  public void documentChanged(final LatLongs document, final LatLongs oldDocument) {
-    updateLocationViewList(0);
+  private FileChooser getFileChooser() {
+    if (fileChooser == null) {
+      fileChooser = new FileChooser();
+    }
+    return fileChooser;
+  }
+
+  @FXML
+  void handleOpenAction(final ActionEvent event) {
+    final FileChooser fileChooser = getFileChooser();
+    final File selection = fileChooser.showOpenDialog(null);
+    if (selection != null) {
+      try (InputStream input = new FileInputStream(selection) ){
+        setLatLongs(getObjectMapper().readValue(input, LatLongs.class));
+      } catch (final IOException e) {
+        showExceptionDialog("Oops, problem when opening " + selection, e);
+      }
+    }
+  }
+
+  private ObjectMapper objectMapper;
+
+  public ObjectMapper getObjectMapper() {
+    if (objectMapper == null) {
+      final SimpleModule module = new SimpleModule();
+      module.addSerializer(LatLong.class, new LatLongSerializer());
+      module.addSerializer(LatLongs.class, new LatLongsSerializer());
+      module.addDeserializer(LatLong.class, new LatLongDeserializer());
+      module.addDeserializer(LatLongs.class, new LatLongsDeserializer());
+      objectMapper = new ObjectMapper();
+      objectMapper.registerModule(module);
+    }
+    return objectMapper;
+  }
+
+  private void showExceptionDialog(final String message) {
+    final Alert alert = new Alert(AlertType.ERROR, message, ButtonType.CLOSE);
+    alert.showAndWait();
+  }
+
+  private void showExceptionDialog(final String message, final Exception e) {
+    showExceptionDialog(message + ": " + e.getLocalizedMessage());
+  }
+
+  private void showSaveExceptionDialog(final File location, final Exception e) {
+    showExceptionDialog("Oops, problem saving to " + location, e);
+  }
+
+  @FXML
+  void handleSaveAction() {
+    final FileChooser fileChooser = getFileChooser();
+    final File selection = fileChooser.showSaveDialog(null);
+    if (selection != null ) {
+      try (OutputStream outputStream = new FileOutputStream(selection, false)) {
+        getObjectMapper().writeValue(outputStream, getLatLongs());
+      } catch (final IOException e) {
+        showSaveExceptionDialog(selection, e);
+      }
+    }
   }
 }
diff --git a/simpleexample/src/main/java/simpleex/ui/LatLongsApp.java b/simpleexample/src/main/java/simpleex/ui/LatLongsApp.java
deleted file mode 100644
index df7517b..0000000
--- a/simpleexample/src/main/java/simpleex/ui/LatLongsApp.java
+++ /dev/null
@@ -1,101 +0,0 @@
-package simpleex.ui;
-
-import fxutil.doc.AbstractDocumentStorageImpl;
-import fxutil.doc.IDocumentImporter;
-import fxutil.doc.IDocumentLoader;
-import fxutil.doc.IDocumentPersistence;
-import fxutil.doc.IDocumentStorage;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.stream.Collectors;
-import simpleex.core.LatLongs;
-
-public class LatLongsApp {
-
-  private LatLongs latLongs = null;
-
-  private final IDocumentPersistence<LatLongs, File> documentPersistence =
-      new IDocumentPersistence<LatLongs, File>() {
-
-        @Override
-        public LatLongs loadDocument(final InputStream inputStream) throws Exception {
-          // TODO
-          return null;
-        }
-
-        @Override
-        public void saveDocument(final LatLongs document, final File documentLocation)
-            throws Exception {
-          try (OutputStream output = new FileOutputStream(documentLocation)) {
-            // TODO
-          }
-        }
-      };
-
-  private final AbstractDocumentStorageImpl<LatLongs, File> documentStorage =
-      new AbstractDocumentStorageImpl<LatLongs, File>() {
-
-        @Override
-        protected LatLongs getDocument() {
-          return latLongs;
-        }
-
-        @Override
-        protected void setDocument(final LatLongs document) {
-          final LatLongs oldDocument = getDocument();
-          LatLongsApp.this.latLongs = document;
-          fireDocumentChanged(oldDocument);
-        }
-
-        @Override
-        protected LatLongs createDocument() {
-          return new LatLongs();
-        }
-
-        @Override
-        protected InputStream toInputStream(final File storage) throws IOException {
-          return new FileInputStream(storage);
-        }
-
-        @Override
-        public LatLongs loadDocument(final InputStream inputStream) throws Exception {
-          return documentPersistence.loadDocument(inputStream);
-        }
-
-        @Override
-        public void saveDocument(final LatLongs document, final File documentLocation)
-            throws Exception {
-          documentPersistence.saveDocument(document, documentLocation);
-        }
-
-        @Override
-        public Collection<IDocumentImporter> getDocumentImporters() {
-          return documentLoaders.stream().map(loader -> new IDocumentImporter() {
-            @Override
-            public void importDocument(final InputStream inputStream) throws IOException {
-              try {
-                setDocumentAndLocation(loader.loadDocument(inputStream), null);
-              } catch (final Exception e) {
-                throw new IOException(e);
-              }
-            }
-          }).collect(Collectors.toList());
-        }
-      };
-
-  public IDocumentStorage<File> getDocumentStorage() {
-    return documentStorage;
-  }
-
-  private final Collection<IDocumentLoader<LatLongs>> documentLoaders = Arrays.asList();
-
-  public Iterable<IDocumentLoader<LatLongs>> getDocumentLoaders() {
-    return documentLoaders;
-  }
-}
diff --git a/simpleexample/src/main/java/simpleex/ui/LatLongsStorage.java b/simpleexample/src/main/java/simpleex/ui/LatLongsStorage.java
deleted file mode 100644
index ede6de2..0000000
--- a/simpleexample/src/main/java/simpleex/ui/LatLongsStorage.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package simpleex.ui;
-
-import com.fasterxml.jackson.databind.module.SimpleModule;
-import fxutil.doc.IDocumentStorage;
-import fxutil.doc.SimpleJsonFileStorageImpl;
-import java.io.File;
-import simpleex.core.LatLong;
-import simpleex.core.LatLongs;
-import simpleex.json.LatLongDeserializer;
-import simpleex.json.LatLongSerializer;
-import simpleex.json.LatLongsDeserializer;
-import simpleex.json.LatLongsSerializer;
-
-public class LatLongsStorage extends SimpleJsonFileStorageImpl<LatLongs>
-    implements IDocumentStorage<File> {
-
-  public LatLongsStorage() {
-    super(LatLongs.class);
-  }
-
-  @Override
-  protected void configureJacksonModule(final SimpleModule module) {
-    module.addSerializer(LatLong.class, new LatLongSerializer());
-    module.addSerializer(LatLongs.class, new LatLongsSerializer());
-    module.addDeserializer(LatLong.class, new LatLongDeserializer());
-    module.addDeserializer(LatLongs.class, new LatLongsDeserializer());
-  }
-}
diff --git a/simpleexample/src/main/resources/simpleex/ui/FileMenu.fxml b/simpleexample/src/main/resources/simpleex/ui/FileMenu.fxml
deleted file mode 100644
index 61fbe58..0000000
--- a/simpleexample/src/main/resources/simpleex/ui/FileMenu.fxml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<?import java.lang.*?>
-<?import javafx.scene.control.Menu?>
-<?import javafx.scene.control.MenuItem?>
-<?import javafx.scene.control.SeparatorMenuItem?>
-
-<Menu xmlns:fx="http://javafx.com/fxml" text="File" fx:controller="fxutil.doc.FileMenuController">
-	<items>
-		<MenuItem text="New" 		accelerator="Meta+N" 	onAction="#handleNewAction"/>
-		<MenuItem text="Open..."  	accelerator="Meta+O"		onAction="#handleOpenAction"/>
-		<Menu fx:id="recentMenu" text="Open Recent"/>
-		<SeparatorMenuItem/>
-		<MenuItem text="Save"		accelerator="Meta+S"		onAction="#handleSaveAction"/>
-		<MenuItem text="Save As..." 							onAction="#handleSaveAsAction"/>
-		<MenuItem text="Save Copy As..." 					onAction="#handleSaveCopyAsAction"/>
-		<SeparatorMenuItem/>
-		<Menu fx:id="importMenu" text="Import">
-			<MenuItem text="File..." 						onAction="#handleFileImportAction"/>
-			<MenuItem text="URL..." 							onAction="#handleUrlImportAction"/>
-		</Menu>
-	</items>
-</Menu>
diff --git a/simpleexample/src/main/resources/simpleex/ui/FxApp.fxml b/simpleexample/src/main/resources/simpleex/ui/FxApp.fxml
index a2410dd..01fbe1d 100644
--- a/simpleexample/src/main/resources/simpleex/ui/FxApp.fxml
+++ b/simpleexample/src/main/resources/simpleex/ui/FxApp.fxml
@@ -24,7 +24,12 @@
 		<VBox>
 			<MenuBar >
 				<menus>
-					<fx:include fx:id="fileMenu" source="FileMenu.fxml"/>
+					<Menu text="File">
+						<items>
+							<MenuItem text="Open..." accelerator="Meta+O" onAction="#handleOpenAction"/>
+							<MenuItem text="Save"	 accelerator="Meta+S" onAction="#handleSaveAction"/>
+						</items>
+					</Menu>
 				</menus>
 			</MenuBar>
 		</VBox>
diff --git a/simpleexample/src/test/resources/simpleex/ui/FileMenu.fxml b/simpleexample/src/test/resources/simpleex/ui/FileMenu.fxml
deleted file mode 100644
index 61fbe58..0000000
--- a/simpleexample/src/test/resources/simpleex/ui/FileMenu.fxml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<?import java.lang.*?>
-<?import javafx.scene.control.Menu?>
-<?import javafx.scene.control.MenuItem?>
-<?import javafx.scene.control.SeparatorMenuItem?>
-
-<Menu xmlns:fx="http://javafx.com/fxml" text="File" fx:controller="fxutil.doc.FileMenuController">
-	<items>
-		<MenuItem text="New" 		accelerator="Meta+N" 	onAction="#handleNewAction"/>
-		<MenuItem text="Open..."  	accelerator="Meta+O"		onAction="#handleOpenAction"/>
-		<Menu fx:id="recentMenu" text="Open Recent"/>
-		<SeparatorMenuItem/>
-		<MenuItem text="Save"		accelerator="Meta+S"		onAction="#handleSaveAction"/>
-		<MenuItem text="Save As..." 							onAction="#handleSaveAsAction"/>
-		<MenuItem text="Save Copy As..." 					onAction="#handleSaveCopyAsAction"/>
-		<SeparatorMenuItem/>
-		<Menu fx:id="importMenu" text="Import">
-			<MenuItem text="File..." 						onAction="#handleFileImportAction"/>
-			<MenuItem text="URL..." 							onAction="#handleUrlImportAction"/>
-		</Menu>
-	</items>
-</Menu>
diff --git a/simpleexample/src/test/resources/simpleex/ui/FxApp.fxml b/simpleexample/src/test/resources/simpleex/ui/FxApp.fxml
index a2410dd..01fbe1d 100644
--- a/simpleexample/src/test/resources/simpleex/ui/FxApp.fxml
+++ b/simpleexample/src/test/resources/simpleex/ui/FxApp.fxml
@@ -24,7 +24,12 @@
 		<VBox>
 			<MenuBar >
 				<menus>
-					<fx:include fx:id="fileMenu" source="FileMenu.fxml"/>
+					<Menu text="File">
+						<items>
+							<MenuItem text="Open..." accelerator="Meta+O" onAction="#handleOpenAction"/>
+							<MenuItem text="Save"	 accelerator="Meta+S" onAction="#handleSaveAction"/>
+						</items>
+					</Menu>
 				</menus>
 			</MenuBar>
 		</VBox>
-- 
GitLab