]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.acorn/src/org/simantics/acorn/MainState.java
Sharing org.simantics.acorn for everyone to use
[simantics/platform.git] / bundles / org.simantics.acorn / src / org / simantics / acorn / MainState.java
diff --git a/bundles/org.simantics.acorn/src/org/simantics/acorn/MainState.java b/bundles/org.simantics.acorn/src/org/simantics/acorn/MainState.java
new file mode 100644 (file)
index 0000000..7733528
--- /dev/null
@@ -0,0 +1,135 @@
+package org.simantics.acorn;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.simantics.databoard.file.RuntimeIOException;
+import org.simantics.utils.FileUtils;
+
+public class MainState implements Serializable {
+
+    private static final long serialVersionUID = 6237383147637270225L;
+
+    public int headDir = 0;
+
+    public MainState() {
+    }
+
+    public MainState(int headDir) {
+        this.headDir = headDir;
+    }
+
+    public static MainState load(Path directory) throws IOException {
+        Files.createDirectories(directory);
+        Path f = directory.resolve("main.state");
+        try {
+            MainState state = null;
+            try (ObjectInputStream in = new ObjectInputStream(new BufferedInputStream(Files.newInputStream(f)))) {
+                state = (MainState) in.readObject();
+            }
+            while (true) {
+                Path last = directory.resolve(Integer.toString(state.headDir - 1));
+                try {
+                    Path headState = last.resolve("head.state");
+                    HeadState.validateHeadStateIntegrity(headState);
+                    break;
+                } catch (InvalidHeadStateException e) {
+                    e.printStackTrace();
+                    state.headDir--;
+                    uncheckedDeleteAll(last);
+                }
+            }
+            return state;
+        } catch(IOException i) {
+            return new MainState( findNewHeadState(directory) );
+        } catch(ClassNotFoundException c) {
+            throw new Error("MainState class not found", c);
+        } finally {
+            if (Files.exists(f)) {
+                Files.delete(f);
+            }
+        }
+    }
+
+    public void save(Path directory) throws IOException {
+        Path f = directory.resolve("main.state");
+        try (ObjectOutputStream out = new ObjectOutputStream(new BufferedOutputStream(Files.newOutputStream(f)))) {
+            out.writeObject(this);
+        }
+        FileIO.syncPath(f);
+    }
+
+    private static boolean isInteger(Path p) {
+        try {
+            Integer.parseInt(p.getFileName().toString());
+            return true;
+        } catch (NumberFormatException e) {
+            return false;
+        }
+    }
+
+    /**
+     * TODO> shouldn't do two things in the same function, this does both head.state search and directory cleanup
+     *  
+     * @param directory
+     * @return
+     * @throws IOException
+     */
+    private static int findNewHeadState(Path directory) throws IOException {
+        try (Stream<Path> s = Files.walk(directory, 1)) {
+            List<Path> reverseSortedPaths = s
+            .filter(p -> !p.equals(directory) && isInteger(p) && Files.isDirectory(p))
+            .sorted((p1, p2) -> {
+                int p1Name = Integer.parseInt(p1.getFileName().toString()); 
+                int p2Name = Integer.parseInt(p2.getFileName().toString());
+                return Integer.compare(p2Name, p1Name);
+            }).collect(Collectors.toList());
+
+            int largest = -1;
+            for (Path last : reverseSortedPaths) {
+                Path headState = last.resolve("head.state");
+                if (Files.exists(headState)) {
+                    try {
+                        HeadState.validateHeadStateIntegrity(headState);
+                        largest = safeParseInt(-1, last.getFileName().toString());
+                        break;
+                    } catch (IOException | InvalidHeadStateException e) {
+                        e.printStackTrace();
+                        uncheckedDeleteAll(last);
+                    }
+                } else {
+                    uncheckedDeleteAll(last);
+                }
+            }
+            // +1 because we want to return the next head version to use,
+            // not the latest existing version.
+            return largest + 1;
+        }
+    }
+
+    private static int safeParseInt(int defaultValue, String s) {
+        try {
+            return Integer.parseInt(s);
+        } catch (NumberFormatException e) {
+            return defaultValue;
+        }
+    }
+
+    private static void uncheckedDeleteAll(Path path) {
+        try {
+            FileUtils.deleteAll(path.toFile());
+        } catch (IOException e) {
+            throw new RuntimeIOException(e);
+        }
+    }
+
+}