From 08f6007cee3cb7923cd4bd678fa3d5cc4e8d2bdc Mon Sep 17 00:00:00 2001 From: Sam Carlberg Date: Fri, 10 Feb 2017 14:05:11 -0500 Subject: [PATCH 1/4] Continuously backup changes and load them when GRIP is launched --- .../edu/wpi/grip/core/GripFileManager.java | 1 + ui/src/main/java/edu/wpi/grip/ui/Main.java | 19 ++++++++++++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/edu/wpi/grip/core/GripFileManager.java b/core/src/main/java/edu/wpi/grip/core/GripFileManager.java index 2a5083b49a..18e36a8d61 100644 --- a/core/src/main/java/edu/wpi/grip/core/GripFileManager.java +++ b/core/src/main/java/edu/wpi/grip/core/GripFileManager.java @@ -22,6 +22,7 @@ public class GripFileManager implements FileManager { public static final File GRIP_DIRECTORY = new File(System.getProperty("user.home") + File.separator + "GRIP"); public static final File IMAGE_DIRECTORY = new File(GRIP_DIRECTORY, "images"); + public static final File BACKUP_FILE = new File(GRIP_DIRECTORY, ".backup.grip"); @Override public void saveImage(byte[] image, String fileName) { diff --git a/ui/src/main/java/edu/wpi/grip/ui/Main.java b/ui/src/main/java/edu/wpi/grip/ui/Main.java index b797176548..07365d8e98 100644 --- a/ui/src/main/java/edu/wpi/grip/ui/Main.java +++ b/ui/src/main/java/edu/wpi/grip/ui/Main.java @@ -1,6 +1,8 @@ package edu.wpi.grip.ui; +import edu.wpi.grip.core.CoreCommandLineHelper; import edu.wpi.grip.core.GripCoreModule; +import edu.wpi.grip.core.GripFileManager; import edu.wpi.grip.core.GripFileModule; import edu.wpi.grip.core.PipelineRunner; import edu.wpi.grip.core.events.UnexpectedThrowableEvent; @@ -27,6 +29,7 @@ import org.apache.commons.cli.CommandLine; import java.io.IOException; +import java.util.Optional; import java.util.logging.Level; import java.util.logging.Logger; @@ -128,6 +131,15 @@ public void start(Stage stage) throws IOException { Platform.runLater(() -> stage.setTitle(MAIN_TITLE)); } }); + project.addIsSaveDirtyConsumer(dirty -> { + if (dirty) { + try { + project.save(GripFileManager.BACKUP_FILE); + } catch (IOException e) { + logger.log(Level.WARNING, "Could not save backup file", e); + } + } + }); stage.setTitle(MAIN_TITLE); stage.getIcons().add(new Image("/edu/wpi/grip/ui/icons/grip.png")); @@ -141,7 +153,12 @@ public void start(Stage stage) throws IOException { operations.addOperations(); cvOperations.addOperations(); - commandLineHelper.loadFile(parsedArgs, project); + if (parsedArgs.hasOption(CoreCommandLineHelper.FILE_OPTION)) { + commandLineHelper.loadFile(parsedArgs, project); + } else if (GripFileManager.BACKUP_FILE.exists()) { + project.open(GripFileManager.BACKUP_FILE); + project.setFile(Optional.empty()); + } commandLineHelper.setServerPort(parsedArgs, settingsProvider, eventBus); // This will throw an exception if the port specified by the save file or command line From e4464e4ca1abe0b008589cfd4f855ca4815b3b81 Mon Sep 17 00:00:00 2001 From: Sam Carlberg Date: Sat, 11 Feb 2017 18:15:19 -0500 Subject: [PATCH 2/4] Automatically load last saved file or backup --- .../edu/wpi/grip/core/GripFileManager.java | 1 + .../wpi/grip/core/serialization/Project.java | 33 ++++++++++-- ui/src/main/java/edu/wpi/grip/ui/Main.java | 52 +++++++++++++++++-- 3 files changed, 77 insertions(+), 9 deletions(-) diff --git a/core/src/main/java/edu/wpi/grip/core/GripFileManager.java b/core/src/main/java/edu/wpi/grip/core/GripFileManager.java index 18e36a8d61..88eef09370 100644 --- a/core/src/main/java/edu/wpi/grip/core/GripFileManager.java +++ b/core/src/main/java/edu/wpi/grip/core/GripFileManager.java @@ -23,6 +23,7 @@ public class GripFileManager implements FileManager { = new File(System.getProperty("user.home") + File.separator + "GRIP"); public static final File IMAGE_DIRECTORY = new File(GRIP_DIRECTORY, "images"); public static final File BACKUP_FILE = new File(GRIP_DIRECTORY, ".backup.grip"); + public static final File LAST_SAVE_FILE = new File(GRIP_DIRECTORY, ".last_save"); @Override public void saveImage(byte[] image, String fileName) { diff --git a/core/src/main/java/edu/wpi/grip/core/serialization/Project.java b/core/src/main/java/edu/wpi/grip/core/serialization/Project.java index 0324fcd77c..5be84c638b 100644 --- a/core/src/main/java/edu/wpi/grip/core/serialization/Project.java +++ b/core/src/main/java/edu/wpi/grip/core/serialization/Project.java @@ -1,5 +1,6 @@ package edu.wpi.grip.core.serialization; +import edu.wpi.grip.core.GripFileManager; import edu.wpi.grip.core.Pipeline; import edu.wpi.grip.core.PipelineRunner; import edu.wpi.grip.core.events.DirtiesSaveEvent; @@ -8,6 +9,7 @@ import com.google.common.annotations.VisibleForTesting; import com.google.common.eventbus.EventBus; import com.google.common.eventbus.Subscribe; +import com.google.common.io.Files; import com.google.common.reflect.ClassPath; import com.thoughtworks.xstream.XStream; import com.thoughtworks.xstream.annotations.XStreamAlias; @@ -22,11 +24,15 @@ import java.io.Reader; import java.io.StringReader; import java.io.Writer; +import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.LinkedList; import java.util.List; import java.util.Optional; import java.util.function.Consumer; +import java.util.logging.Level; +import java.util.logging.Logger; + import javax.inject.Inject; import javax.inject.Singleton; @@ -36,6 +42,8 @@ @Singleton public class Project { + private static final Logger logger = Logger.getLogger(Project.class.getName()); + protected final XStream xstream = new XStream(new PureJavaReflectionProvider()); @Inject private EventBus eventBus; @@ -74,7 +82,6 @@ public void initialize(StepConverter stepConverter, } catch (InternalError ex) { throw new AssertionError("Failed to load class: " + clazz.getName(), ex); } - }); } catch (IOException ex) { throw new AssertionError("Could not load classes for XStream annotation processing", ex); @@ -88,8 +95,22 @@ public Optional getFile() { return file; } + /** + * Sets the current project file to the given optional one. This also updates a file on disk + * so the app can "remember" the most recent save file when it's opened later. + * + * @param file the optional file that this project is associated with. + */ public void setFile(Optional file) { + file.ifPresent(f -> logger.info("Setting project file to: " + f.getAbsolutePath())); this.file = file; + try { + Files.write(file.get().getAbsolutePath(), + GripFileManager.LAST_SAVE_FILE, + Charset.defaultCharset()); + } catch (IOException e) { + logger.log(Level.WARNING, "Could not set last file", e); + } } /** @@ -100,7 +121,7 @@ public void open(File file) throws IOException { StandardCharsets.UTF_8)) { this.open(reader); } - this.file = Optional.of(file); + setFile(Optional.of(file)); } /** @@ -163,6 +184,10 @@ public void save(Writer writer) { saveIsDirty.set(false); } + public void saveRaw(Writer writer) { + xstream.toXML(pipeline, writer); + } + public boolean isSaveDirty() { return saveIsDirty.get(); } @@ -173,9 +198,7 @@ public void addIsSaveDirtyConsumer(Consumer consumer) { @Subscribe public void onDirtiesSaveEvent(DirtiesSaveEvent dirtySaveEvent) { - // Only update the flag the save isn't already dirty - // We don't need to be redundantly checking if the event dirties the save - if (!saveIsDirty.get() && dirtySaveEvent.doesDirtySave()) { + if (dirtySaveEvent.doesDirtySave()) { saveIsDirty.set(true); } } diff --git a/ui/src/main/java/edu/wpi/grip/ui/Main.java b/ui/src/main/java/edu/wpi/grip/ui/Main.java index 07365d8e98..6fcf9f4a7d 100644 --- a/ui/src/main/java/edu/wpi/grip/ui/Main.java +++ b/ui/src/main/java/edu/wpi/grip/ui/Main.java @@ -5,6 +5,7 @@ import edu.wpi.grip.core.GripFileManager; import edu.wpi.grip.core.GripFileModule; import edu.wpi.grip.core.PipelineRunner; +import edu.wpi.grip.core.events.DirtiesSaveEvent; import edu.wpi.grip.core.events.UnexpectedThrowableEvent; import edu.wpi.grip.core.exception.GripServerException; import edu.wpi.grip.core.http.GripServer; @@ -25,10 +26,16 @@ import com.google.inject.Injector; import com.google.inject.util.Modules; import com.sun.javafx.application.PlatformImpl; +import com.thoughtworks.xstream.XStreamException; import org.apache.commons.cli.CommandLine; +import java.io.File; import java.io.IOException; +import java.io.Writer; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.util.List; import java.util.Optional; import java.util.logging.Level; import java.util.logging.Logger; @@ -133,8 +140,9 @@ public void start(Stage stage) throws IOException { }); project.addIsSaveDirtyConsumer(dirty -> { if (dirty) { - try { - project.save(GripFileManager.BACKUP_FILE); + try (Writer fw = Files.newBufferedWriter( + GripFileManager.BACKUP_FILE.toPath(), StandardCharsets.UTF_8)) { + project.saveRaw(fw); } catch (IOException e) { logger.log(Level.WARNING, "Could not save backup file", e); } @@ -156,8 +164,44 @@ public void start(Stage stage) throws IOException { if (parsedArgs.hasOption(CoreCommandLineHelper.FILE_OPTION)) { commandLineHelper.loadFile(parsedArgs, project); } else if (GripFileManager.BACKUP_FILE.exists()) { - project.open(GripFileManager.BACKUP_FILE); - project.setFile(Optional.empty()); + try { + if (GripFileManager.LAST_SAVE_FILE.exists()) { + try { + List lines = Files.readAllLines(GripFileManager.LAST_SAVE_FILE.toPath()); + if (lines.size() == 1) { + final String lastSavePath = lines.get(0); + final File lastSaveFile = new File(lastSavePath); + logger.info("Last save file: " + lastSavePath); + List backupLines = Files.readAllLines(GripFileManager.BACKUP_FILE.toPath()); + List lastSaveLines = Files.readAllLines(lastSaveFile.toPath()); + if (backupLines.equals(lastSaveLines)) { + // No point in loading the backup since it's identical to the last save + logger.info("Backup is the same as the last save file"); + project.open(new File(lastSavePath)); + } else { + // Load backup, set the file to the last save file (instead of the backup), + // and post an event marking the save as dirty + logger.info("Backup is different from the last opened project, flagging as dirty"); + project.open(GripFileManager.BACKUP_FILE); + project.setFile(Optional.of(lastSaveFile)); + eventBus.post(new DirtiesSaveEvent() { + }); + } + } else { + logger.warning("Unexpected data in last_save: " + lines); + } + } catch (IOException e) { + logger.log(Level.WARNING, "Could not set file to the last save", e); + } + } else { + project.open(GripFileManager.BACKUP_FILE); + project.setFile(Optional.empty()); + eventBus.post(new DirtiesSaveEvent() { + }); + } + } catch (XStreamException | IOException e) { + logger.log(Level.WARNING, "Could not open backup file", e); + } } commandLineHelper.setServerPort(parsedArgs, settingsProvider, eventBus); From 18bb0f465a3c4afb370ce65e439ce2e0664be319 Mon Sep 17 00:00:00 2001 From: Sam Carlberg Date: Sat, 11 Feb 2017 20:10:31 -0500 Subject: [PATCH 3/4] Move loading backups to dedicated class, improve edge case handling --- .../main/java/edu/wpi/grip/core/Pipeline.java | 4 +- .../grip/core/events/DirtiesSaveEvent.java | 7 ++ .../wpi/grip/core/serialization/Project.java | 11 +- ui/src/main/java/edu/wpi/grip/ui/Main.java | 47 +------- .../edu/wpi/grip/ui/ProjectBackupLoader.java | 105 ++++++++++++++++++ 5 files changed, 125 insertions(+), 49 deletions(-) create mode 100644 ui/src/main/java/edu/wpi/grip/ui/ProjectBackupLoader.java diff --git a/core/src/main/java/edu/wpi/grip/core/Pipeline.java b/core/src/main/java/edu/wpi/grip/core/Pipeline.java index fc09cdd8d6..3868d0e167 100644 --- a/core/src/main/java/edu/wpi/grip/core/Pipeline.java +++ b/core/src/main/java/edu/wpi/grip/core/Pipeline.java @@ -28,7 +28,7 @@ import java.util.ArrayList; import java.util.Collections; -import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.List; import java.util.Set; import java.util.concurrent.locks.Lock; @@ -61,7 +61,7 @@ public class Pipeline implements ConnectionValidator, SettingsProvider, StepInde */ private final List sources = new ArrayList<>(); private final List steps = new ArrayList<>(); - private final Set connections = new HashSet<>(); + private final Set connections = new LinkedHashSet<>(); @Inject @XStreamOmitField private transient EventBus eventBus; diff --git a/core/src/main/java/edu/wpi/grip/core/events/DirtiesSaveEvent.java b/core/src/main/java/edu/wpi/grip/core/events/DirtiesSaveEvent.java index b66a591124..29aa833692 100644 --- a/core/src/main/java/edu/wpi/grip/core/events/DirtiesSaveEvent.java +++ b/core/src/main/java/edu/wpi/grip/core/events/DirtiesSaveEvent.java @@ -8,6 +8,13 @@ */ public interface DirtiesSaveEvent { + DirtiesSaveEvent DIRTIES_SAVE_EVENT = new DirtiesSaveEvent() { + @Override + public boolean doesDirtySave() { + return true; + } + }; + /** * Some events may have more logic regarding whether they make the save dirty or not. * diff --git a/core/src/main/java/edu/wpi/grip/core/serialization/Project.java b/core/src/main/java/edu/wpi/grip/core/serialization/Project.java index 5be84c638b..63bcd56de5 100644 --- a/core/src/main/java/edu/wpi/grip/core/serialization/Project.java +++ b/core/src/main/java/edu/wpi/grip/core/serialization/Project.java @@ -105,9 +105,14 @@ public void setFile(Optional file) { file.ifPresent(f -> logger.info("Setting project file to: " + f.getAbsolutePath())); this.file = file; try { - Files.write(file.get().getAbsolutePath(), - GripFileManager.LAST_SAVE_FILE, - Charset.defaultCharset()); + if (file.isPresent()) { + Files.write(file.get().getAbsolutePath(), + GripFileManager.LAST_SAVE_FILE, + Charset.defaultCharset()); + } else { + // No project file, delete the last_save file + GripFileManager.LAST_SAVE_FILE.delete(); + } } catch (IOException e) { logger.log(Level.WARNING, "Could not set last file", e); } diff --git a/ui/src/main/java/edu/wpi/grip/ui/Main.java b/ui/src/main/java/edu/wpi/grip/ui/Main.java index 6fcf9f4a7d..7bab8e5140 100644 --- a/ui/src/main/java/edu/wpi/grip/ui/Main.java +++ b/ui/src/main/java/edu/wpi/grip/ui/Main.java @@ -5,7 +5,6 @@ import edu.wpi.grip.core.GripFileManager; import edu.wpi.grip.core.GripFileModule; import edu.wpi.grip.core.PipelineRunner; -import edu.wpi.grip.core.events.DirtiesSaveEvent; import edu.wpi.grip.core.events.UnexpectedThrowableEvent; import edu.wpi.grip.core.exception.GripServerException; import edu.wpi.grip.core.http.GripServer; @@ -26,17 +25,13 @@ import com.google.inject.Injector; import com.google.inject.util.Modules; import com.sun.javafx.application.PlatformImpl; -import com.thoughtworks.xstream.XStreamException; import org.apache.commons.cli.CommandLine; -import java.io.File; import java.io.IOException; import java.io.Writer; import java.nio.charset.StandardCharsets; import java.nio.file.Files; -import java.util.List; -import java.util.Optional; import java.util.logging.Level; import java.util.logging.Logger; @@ -75,6 +70,7 @@ public class Main extends Application { @Inject private CVOperations cvOperations; @Inject private GripServer server; @Inject private HttpPipelineSwitcher pipelineSwitcher; + @Inject private ProjectBackupLoader backupLoader; private Parent root; private boolean headless; private final UICommandLineHelper commandLineHelper = new UICommandLineHelper(); @@ -163,45 +159,8 @@ public void start(Stage stage) throws IOException { if (parsedArgs.hasOption(CoreCommandLineHelper.FILE_OPTION)) { commandLineHelper.loadFile(parsedArgs, project); - } else if (GripFileManager.BACKUP_FILE.exists()) { - try { - if (GripFileManager.LAST_SAVE_FILE.exists()) { - try { - List lines = Files.readAllLines(GripFileManager.LAST_SAVE_FILE.toPath()); - if (lines.size() == 1) { - final String lastSavePath = lines.get(0); - final File lastSaveFile = new File(lastSavePath); - logger.info("Last save file: " + lastSavePath); - List backupLines = Files.readAllLines(GripFileManager.BACKUP_FILE.toPath()); - List lastSaveLines = Files.readAllLines(lastSaveFile.toPath()); - if (backupLines.equals(lastSaveLines)) { - // No point in loading the backup since it's identical to the last save - logger.info("Backup is the same as the last save file"); - project.open(new File(lastSavePath)); - } else { - // Load backup, set the file to the last save file (instead of the backup), - // and post an event marking the save as dirty - logger.info("Backup is different from the last opened project, flagging as dirty"); - project.open(GripFileManager.BACKUP_FILE); - project.setFile(Optional.of(lastSaveFile)); - eventBus.post(new DirtiesSaveEvent() { - }); - } - } else { - logger.warning("Unexpected data in last_save: " + lines); - } - } catch (IOException e) { - logger.log(Level.WARNING, "Could not set file to the last save", e); - } - } else { - project.open(GripFileManager.BACKUP_FILE); - project.setFile(Optional.empty()); - eventBus.post(new DirtiesSaveEvent() { - }); - } - } catch (XStreamException | IOException e) { - logger.log(Level.WARNING, "Could not open backup file", e); - } + } else { + backupLoader.loadBackupOrPreviousSave(); } commandLineHelper.setServerPort(parsedArgs, settingsProvider, eventBus); diff --git a/ui/src/main/java/edu/wpi/grip/ui/ProjectBackupLoader.java b/ui/src/main/java/edu/wpi/grip/ui/ProjectBackupLoader.java new file mode 100644 index 0000000000..7d320e0c18 --- /dev/null +++ b/ui/src/main/java/edu/wpi/grip/ui/ProjectBackupLoader.java @@ -0,0 +1,105 @@ +package edu.wpi.grip.ui; + +import edu.wpi.grip.core.GripFileManager; +import edu.wpi.grip.core.events.DirtiesSaveEvent; +import edu.wpi.grip.core.serialization.Project; + +import com.google.common.eventbus.EventBus; +import com.google.inject.Inject; +import com.google.inject.Singleton; +import com.thoughtworks.xstream.XStreamException; + +import org.apache.commons.lang3.StringUtils; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.util.List; +import java.util.Optional; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Class handling loading project backups when the app is launched. This improves the UX by letting + * users resume where they left off. + */ +@Singleton +public class ProjectBackupLoader { + + private static final Logger logger = Logger.getLogger(ProjectBackupLoader.class.getName()); + + @Inject + private EventBus eventBus; + @Inject + private Project project; + + /** + * Loads the backup project, or the previous save if it's identical to the backup. If there was + * no previous save, the project will have no file associated with it. Does nothing if the backup + * file does not exist. + */ + public void loadBackupOrPreviousSave() { + if (GripFileManager.LAST_SAVE_FILE.exists()) { + // Load whichever is readable and more recent, backup or previous save + try { + File lastSaveFile = lastSaveFile(); + if (lastSaveFile != null) { + // Compare last save to backup + String backupText = readFileText(GripFileManager.BACKUP_FILE); + String lastSaveText = readFileText(lastSaveFile); + if (backupText.equals(lastSaveText) || !GripFileManager.BACKUP_FILE.exists()) { + // No point in loading the backup since it's identical to the last save + logger.info("Loading the last save file"); + project.open(lastSaveFile); + } else if (GripFileManager.BACKUP_FILE.exists()) { + // Load backup, set the file to the last save file (instead of the backup), + // and post an event marking the save as dirty + loadBackup(); + project.setFile(Optional.of(lastSaveFile)); + eventBus.post(DirtiesSaveEvent.DIRTIES_SAVE_EVENT); + } + } else if (GripFileManager.BACKUP_FILE.exists()) { + // Couldn't read from the last save, just load the backup if possible + loadBackup(); + project.setFile(Optional.empty()); + } + } catch (XStreamException | IOException e) { + logger.log(Level.WARNING, "Could not open the last project file", e); + } + } else if (GripFileManager.BACKUP_FILE.exists()) { + // Load the backup, if possible + loadBackup(); + project.setFile(Optional.empty()); + } + } + + private File lastSaveFile() throws IOException { + List lines = Files.readAllLines(GripFileManager.LAST_SAVE_FILE.toPath()); + if (lines.size() == 1) { + return new File(lines.get(0)); + } else { + logger.warning("Unexpected data in last_save file: " + lines); + return null; + } + } + + private void loadBackup() { + try { + logger.info("Loading backup file"); + project.open(GripFileManager.BACKUP_FILE); + } catch (XStreamException | IOException e) { + logger.log(Level.WARNING, "Could not load backup file", e); + } + eventBus.post(DirtiesSaveEvent.DIRTIES_SAVE_EVENT); + } + + /** + * Reads all the text from the given file into a single string. + */ + private static String readFileText(File file) throws IOException { + return StringUtils.join(Files.readAllLines(file.toPath()), '\n'); + } + +} + + From 3fb962313478cc521be401e028bfabcd6d91bd45 Mon Sep 17 00:00:00 2001 From: Sam Carlberg Date: Tue, 14 Feb 2017 12:52:21 -0500 Subject: [PATCH 4/4] Use lastModified timestamp for choosing between last save and backup --- .../edu/wpi/grip/core/serialization/Project.java | 14 ++++++++++++++ .../java/edu/wpi/grip/ui/ProjectBackupLoader.java | 15 ++------------- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/core/src/main/java/edu/wpi/grip/core/serialization/Project.java b/core/src/main/java/edu/wpi/grip/core/serialization/Project.java index 63bcd56de5..a2e0147e32 100644 --- a/core/src/main/java/edu/wpi/grip/core/serialization/Project.java +++ b/core/src/main/java/edu/wpi/grip/core/serialization/Project.java @@ -184,11 +184,25 @@ public void save(File file) throws IOException { this.file = Optional.of(file); } + /** + * Save the project using a writer to write the data. This will clear the dirty flag. + * + * @param writer the writer to use to save the project + * + * @see #saveRaw(Writer) + */ public void save(Writer writer) { this.xstream.toXML(this.pipeline, writer); saveIsDirty.set(false); } + /** + * Save the project using a writer to write the data. This has no other side effects. + * + * @param writer the writer to use to save the project + * + * @see #save(Writer) + */ public void saveRaw(Writer writer) { xstream.toXML(pipeline, writer); } diff --git a/ui/src/main/java/edu/wpi/grip/ui/ProjectBackupLoader.java b/ui/src/main/java/edu/wpi/grip/ui/ProjectBackupLoader.java index 7d320e0c18..a47486cc73 100644 --- a/ui/src/main/java/edu/wpi/grip/ui/ProjectBackupLoader.java +++ b/ui/src/main/java/edu/wpi/grip/ui/ProjectBackupLoader.java @@ -9,8 +9,6 @@ import com.google.inject.Singleton; import com.thoughtworks.xstream.XStreamException; -import org.apache.commons.lang3.StringUtils; - import java.io.File; import java.io.IOException; import java.nio.file.Files; @@ -45,10 +43,8 @@ public void loadBackupOrPreviousSave() { File lastSaveFile = lastSaveFile(); if (lastSaveFile != null) { // Compare last save to backup - String backupText = readFileText(GripFileManager.BACKUP_FILE); - String lastSaveText = readFileText(lastSaveFile); - if (backupText.equals(lastSaveText) || !GripFileManager.BACKUP_FILE.exists()) { - // No point in loading the backup since it's identical to the last save + if (lastSaveFile.lastModified() >= GripFileManager.BACKUP_FILE.lastModified()) { + // Last save is more recent, load that one logger.info("Loading the last save file"); project.open(lastSaveFile); } else if (GripFileManager.BACKUP_FILE.exists()) { @@ -93,13 +89,6 @@ private void loadBackup() { eventBus.post(DirtiesSaveEvent.DIRTIES_SAVE_EVENT); } - /** - * Reads all the text from the given file into a single string. - */ - private static String readFileText(File file) throws IOException { - return StringUtils.join(Files.readAllLines(file.toPath()), '\n'); - } - }