--- /dev/null
+package org.simantics.internal;\r
+\r
+import java.io.File;\r
+import java.io.FileFilter;\r
+import java.io.IOException;\r
+import java.util.ArrayDeque;\r
+import java.util.Collection;\r
+\r
+import org.eclipse.core.runtime.IProgressMonitor;\r
+import org.eclipse.core.runtime.IStatus;\r
+import org.eclipse.core.runtime.Status;\r
+import org.eclipse.core.runtime.jobs.Job;\r
+import org.simantics.utils.FileService;\r
+import org.simantics.utils.IOperation;\r
+import org.simantics.utils.IOperationListener;\r
+\r
+/**\r
+ * @author Tuukka Lehtonen\r
+ */\r
+public class FileServiceImpl implements FileService {\r
+\r
+ private static final long DELETION_DELAY_MS = 10000;\r
+ private static final long DELETION_ATTEMPT_INTERVAL_MS = 10000;\r
+\r
+ private static abstract class Op<R, E extends Exception> implements IOperation<R, E> {\r
+ protected boolean done;\r
+ protected R result;\r
+ @SuppressWarnings("unused")\r
+ protected E lastException;\r
+\r
+ @Override\r
+ public R waitFor() throws E {\r
+ synchronized(this) {\r
+ while (!isDone()) {\r
+ try {\r
+ wait();\r
+ } catch (InterruptedException e) {\r
+ }\r
+ }\r
+ }\r
+ return result;\r
+ }\r
+\r
+ @Override\r
+ public boolean isDone() {\r
+ return done;\r
+ }\r
+\r
+ @Override\r
+ public void addListener(IOperationListener<R, E> listener) {\r
+ throw new UnsupportedOperationException();\r
+ }\r
+\r
+ public abstract boolean tryExecute();\r
+\r
+ }\r
+\r
+ private static class Deletion extends Op<Boolean, IOException> {\r
+\r
+ File f;\r
+\r
+ EffortOption effort;\r
+ int tries;\r
+\r
+ public Deletion(File f, DeleteOption... options) {\r
+ this.f = f;\r
+ parseOptions(options);\r
+ }\r
+\r
+ private void parseOptions(DeleteOption[] options) {\r
+ for (DeleteOption opt : options) {\r
+ if (opt instanceof EffortOption) {\r
+ effort = (EffortOption) opt;\r
+ }\r
+ }\r
+ }\r
+\r
+ @Override\r
+ public boolean tryExecute() {\r
+ if (effort != null) {\r
+ if (tries > effort.maxTries) {\r
+ // Give up.\r
+ return true;\r
+ }\r
+ }\r
+ try {\r
+ deleteAll(f);\r
+ lastException = null;\r
+ result = true;\r
+ return true;\r
+ } catch (IOException e) {\r
+ ++tries;\r
+ lastException = e;\r
+ return false;\r
+ }\r
+ }\r
+\r
+ public void deleteAll(File dir) throws IOException {\r
+ if (dir.isFile()) {\r
+ if (!dir.delete())\r
+ throw new IOException("Could not delete file: " + dir.getAbsolutePath());\r
+ return;\r
+ }\r
+ if (dir.isDirectory()) {\r
+ File[] fs = dir.listFiles((FileFilter) null);\r
+ if (fs == null)\r
+ return;\r
+\r
+ for (File f : fs) {\r
+ if (f.isDirectory()) {\r
+ deleteAll(f);\r
+ } else {\r
+ if (!f.delete()) {\r
+ throw new IOException("Could not delete file: " + f.getAbsolutePath());\r
+ }\r
+ }\r
+ }\r
+\r
+ if (!dir.delete()) {\r
+ throw new IOException("Could not delete directory: " + dir.getAbsolutePath());\r
+ }\r
+ } else if (dir.exists()) {\r
+ if (!dir.delete()) {\r
+ throw new IOException("Could not delete file: " + dir.getAbsolutePath());\r
+ }\r
+ }\r
+ }\r
+\r
+ }\r
+\r
+ private ArrayDeque<Deletion> deletionQueue = new ArrayDeque<Deletion>();\r
+ private DeletionJob deletionJob = new DeletionJob();\r
+\r
+ @Override\r
+ public IOperation<Boolean, IOException> scheduleDeleteIfExists(File file, DeleteOption... options) {\r
+ if (!file.exists())\r
+ return null;\r
+ synchronized (deletionQueue) {\r
+ Deletion d = new Deletion(file, options);\r
+ if (deletionQueue.contains(d))\r
+ return null;\r
+ deletionQueue.addLast(d);\r
+ deletionJob.schedule(DELETION_DELAY_MS);\r
+ return d;\r
+ }\r
+ }\r
+\r
+ class DeletionJob extends Job {\r
+\r
+ public DeletionJob() {\r
+ super("File background deletion");\r
+ }\r
+\r
+ @SuppressWarnings("unchecked")\r
+ private <O extends Op<?,?>> void process(Collection<O> ops) {\r
+ Object[] opa = ops.toArray();\r
+ for (int i = 0; i < opa.length; ++i) {\r
+ O op = (O) opa[i];\r
+ if (op.tryExecute()) {\r
+ // Success. Will be removed from queue.\r
+ } else {\r
+ opa[i] = null;\r
+ }\r
+ }\r
+ synchronized (ops) {\r
+ for (int i = 0; i < opa.length; ++i) {\r
+ if (opa[i] != null)\r
+ ops.remove(opa[i]);\r
+ }\r
+ }\r
+ }\r
+\r
+ @Override\r
+ protected IStatus run(IProgressMonitor monitor) {\r
+ process(deletionQueue);\r
+ if (!deletionQueue.isEmpty())\r
+ schedule(DELETION_ATTEMPT_INTERVAL_MS);\r
+ return Status.OK_STATUS;\r
+ }\r
+\r
+ }\r
+\r
+}\r