Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(2000)

Unified Diff: editor/tools/plugins/com.google.dart.tools.ui/src/com/google/dart/tools/ui/internal/text/editor/AutoSaveHelper.java

Issue 11364134: Merge libv1. (Closed) Base URL: https://dart.googlecode.com/svn/experimental/lib_v2/dart
Patch Set: Reupload due to error Created 8 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: editor/tools/plugins/com.google.dart.tools.ui/src/com/google/dart/tools/ui/internal/text/editor/AutoSaveHelper.java
diff --git a/editor/tools/plugins/com.google.dart.tools.ui/src/com/google/dart/tools/ui/internal/text/editor/AutoSaveHelper.java b/editor/tools/plugins/com.google.dart.tools.ui/src/com/google/dart/tools/ui/internal/text/editor/AutoSaveHelper.java
new file mode 100644
index 0000000000000000000000000000000000000000..a4052ef9f785d863858698f4a9ca6834258ade4a
--- /dev/null
+++ b/editor/tools/plugins/com.google.dart.tools.ui/src/com/google/dart/tools/ui/internal/text/editor/AutoSaveHelper.java
@@ -0,0 +1,421 @@
+/*
+ * Copyright (c) 2012, the Dart project authors.
+ *
+ * Licensed under the Eclipse Public License v1.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.dart.tools.ui.internal.text.editor;
+
+import com.google.common.collect.MapMaker;
+import com.google.common.io.Files;
+import com.google.dart.tools.internal.corext.refactoring.util.ReflectionUtils;
+import com.google.dart.tools.ui.DartToolsPlugin;
+import com.google.dart.tools.ui.DartUI;
+import com.google.dart.tools.ui.internal.util.PartListenerAdapter;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IWorkspaceRoot;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.source.ISourceViewer;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IEditorReference;
+import org.eclipse.ui.IFileEditorInput;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchListener;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchPart;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.ide.IDE;
+import org.eclipse.ui.texteditor.AbstractTextEditor;
+import org.eclipse.ui.texteditor.ITextEditor;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * Helper for auto-saving {@link ITextEditor}s.
+ */
+public class AutoSaveHelper {
+ private static class CloseTask extends Task {
+ private final IFile file;
+
+ public CloseTask(IFile file) {
+ this.file = file;
+ }
+
+ @Override
+ void execute() throws Exception {
+ instance.pathMapCloseFile(file);
+ }
+ }
+
+ private static class EditorInfo implements Serializable {
+ private final String content;
+ private final int selectionStart;
+ private final int selectionLength;
+
+ public EditorInfo(String content, int selectionStart, int selectionLength) {
+ this.content = content;
+ this.selectionStart = selectionStart;
+ this.selectionLength = selectionLength;
+ }
+ }
+ private static class OpenTask extends Task {
+ private final IFile file;
+
+ public OpenTask(IFile file) {
+ this.file = file;
+ }
+
+ @Override
+ void execute() throws Exception {
+ instance.pathMapOpenFile(file);
+ }
+ }
+
+ private static class SaveTask extends Task {
+ private final IFile file;
+ private final EditorInfo info;
+
+ public SaveTask(IFile file, EditorInfo info) {
+ this.file = file;
+ this.info = info;
+ }
+
+ @Override
+ void execute() throws Exception {
+ String path = getStringPath(file);
+ Integer id = instance.pathMap.get(path);
+ if (id != null) {
+ editorInfoWrite(id, info);
+ }
+ }
+
+ }
+
+ private abstract static class Task {
+ abstract void execute() throws Exception;
+ }
+
+ private static final AutoSaveHelper instance = new AutoSaveHelper();
+
+ public static void reconciled(IEditorInput input, ISourceViewer viewer) {
+ IFile file = getInputFile(input);
+ if (file != null && viewer != null) {
+ IDocument document = viewer.getDocument();
+ if (document != null) {
+ String content = document.get();
+ Point selection = DartUI.getSelectionRange(viewer);
+ EditorInfo info = new EditorInfo(content, selection.x, selection.y);
+ instance.taskQueue.add(new SaveTask(file, info));
+ }
+ }
+ }
+
+ /**
+ * Starts Thread to run main loop. Should be called only one time.
+ */
+ public static void start() {
+ final IWorkbench workbench = PlatformUI.getWorkbench();
+ Display.getDefault().asyncExec(new Runnable() {
+ @Override
+ public void run() {
+ // if workbench is still starting, reschedule this Runnable
+ if (workbench.isStarting()) {
+ Display.getDefault().asyncExec(this);
+ return;
+ }
+ // OK, workbench started, editors opened.
+ // Now we can restore content of editors.
+ instance.start(workbench);
+ }
+ });
+ }
+
+ /**
+ * Deletes {@link EditorInfo} into file with name "id".
+ */
+ private static void editorInfoDelete(Integer id) {
+ File saveFolder = getSaveFolder();
+ File saveFile = new File(saveFolder, id.toString());
+ if (saveFile.exists()) {
+ saveFile.delete();
+ }
+ }
+
+ /**
+ * @return the {@link EditorInfo} from file with given "id", may be <code>null</code> if file does
+ * not exist.
+ */
+ private static EditorInfo editorInfoRead(Integer id) throws Exception {
+ File saveFolder = getSaveFolder();
+ File saveFile = new File(saveFolder, id.toString());
+ if (saveFile.exists()) {
+ ObjectInputStream ois = new ObjectInputStream(new FileInputStream(saveFile));
+ try {
+ return (EditorInfo) ois.readObject();
+ } finally {
+ ois.close();
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Writes given {@link EditorInfo} into file with name "id".
+ */
+ private static void editorInfoWrite(Integer id, EditorInfo info) throws Exception {
+ File saveFolder = getSaveFolder();
+ File saveFile = new File(saveFolder, id.toString());
+ ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(saveFile));
+ try {
+ oos.writeObject(info);
+ } finally {
+ oos.close();
+ }
+ }
+
+ /**
+ * @return the input {@link IFile}, may be <code>null</code>.
+ */
+ private static IFile getInputFile(IEditorInput input) {
+ if (input instanceof IFileEditorInput) {
+ return ((IFileEditorInput) input).getFile();
+ }
+ return null;
+ }
+
+ /**
+ * @return the input {@link IFile}, may be <code>null</code>.
+ */
+ private static IFile getInputFile(IWorkbenchPart part) {
+ if (part instanceof ITextEditor) {
+ ITextEditor editor = (ITextEditor) part;
+ IEditorInput input = editor.getEditorInput();
+ return getInputFile(input);
+ }
+ return null;
+ }
+
+ /**
+ * @return the "auto save" folder in plugin state location.
+ */
+ private static File getSaveFolder() {
+ IPath stateLocation = DartToolsPlugin.getDefault().getStateLocation();
+ return stateLocation.append("autoSave").toFile();
+ }
+
+ /**
+ * @return the {@link String} presentation of {@link IFile} path, good to use as argument for
+ * {@link IWorkspaceRoot#getFile(IPath)}.
+ */
+ private static String getStringPath(IFile file) {
+ return file.getFullPath().toPortableString();
+ }
+
+ private final BlockingQueue<Task> taskQueue = new LinkedBlockingQueue<Task>();
+
+ private final AtomicInteger lastId = new AtomicInteger();
+
+ private final Map<String, Integer> pathMap = new MapMaker().makeMap();
+
+ /**
+ * Removes given {@link IFile} from the list of opened files.
+ */
+ private void pathMapCloseFile(IFile file) throws Exception {
+ String path = getStringPath(file);
+ Integer id = pathMap.remove(path);
+ if (id != null) {
+ pathMapWrite();
+ editorInfoDelete(id);
+ }
+ }
+
+ /**
+ * We remove "autoSave" folder on normal shutdown, we don't need to restore anything.
+ */
+ private void pathMapDelete() {
+ try {
+ File saveFolder = getSaveFolder();
+ Files.deleteRecursively(saveFolder);
+ } catch (Throwable e) {
+ }
+ }
+
+ private File pathMapGetFile() {
+ File saveFolder = getSaveFolder();
+ return new File(saveFolder, "map");
+ }
+
+ /**
+ * Remembers in {@link #pathMap} that given {@link IFile} was opened.
+ */
+ private void pathMapOpenFile(IFile file) throws Exception {
+ String path = getStringPath(file);
+ if (!pathMap.containsKey(path)) {
+ int id = lastId.getAndIncrement();
+ pathMap.put(path, id);
+ pathMapWrite();
+ }
+ }
+
+ /**
+ * Writes {@link #pathMap}.
+ */
+ private void pathMapWrite() throws Exception {
+ File saveFile = pathMapGetFile();
+ saveFile.getParentFile().mkdirs();
+ saveFile.delete();
+ ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(saveFile));
+ try {
+ oos.writeObject(pathMap);
+ } finally {
+ oos.close();
+ }
+ }
+
+ private void restoreEditors(IWorkbenchPage activePage) {
+ try {
+ File mapFile = pathMapGetFile();
+ if (mapFile.exists()) {
+ ObjectInputStream ois = new ObjectInputStream(new FileInputStream(mapFile));
+ try {
+ @SuppressWarnings("unchecked")
+ Map<String, Integer> map = (Map<String, Integer>) ois.readObject();
+ IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot();
+ for (Entry<String, Integer> entry : map.entrySet()) {
+ String pathStr = entry.getKey();
+ Integer id = entry.getValue();
+ try {
+ EditorInfo info = editorInfoRead(id);
+ if (info != null) {
+ IFile file = workspaceRoot.getFile(new Path(pathStr));
+ if (file.exists()) {
+ IEditorPart newEditor = IDE.openEditor(activePage, file);
+ if (newEditor instanceof AbstractTextEditor) {
+ ISourceViewer viewer = ReflectionUtils.invokeMethod(
+ newEditor,
+ "getSourceViewer()");
+ viewer.getDocument().set(info.content);
+ viewer.setSelectedRange(info.selectionStart, info.selectionLength);
+ }
+ }
+ }
+ } catch (Throwable e) {
+ DartToolsPlugin.log(e);
+ }
+ }
+ } finally {
+ ois.close();
+ }
+ }
+ } catch (Throwable e) {
+ DartToolsPlugin.log(e);
+ }
+ }
+
+ /**
+ * Main loop of {@link AutoSaveHelper}, executes {@link Task}.
+ */
+ private void runTasksLoop() {
+ while (true) {
+ try {
+ Task task = taskQueue.take();
+ try {
+ task.execute();
+ } catch (Throwable e) {
+ DartToolsPlugin.log(e);
+ }
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+
+ /**
+ * Start {@link AutoSaveHelper} with given started {@link IWorkbench}.
+ */
+ private void start(IWorkbench workbench) {
+ IWorkbenchPage activePage = workbench.getActiveWorkbenchWindow().getActivePage();
+ restoreEditors(activePage);
+ // remember all open editors
+ {
+ IEditorReference[] editorReferences = activePage.getEditorReferences();
+ for (IEditorReference reference : editorReferences) {
+ try {
+ IEditorInput input = reference.getEditorInput();
+ IFile file = getInputFile(input);
+ if (file != null) {
+ taskQueue.add(new OpenTask(file));
+ }
+ } catch (Throwable e) {
+ DartToolsPlugin.log(e);
+ }
+ }
+ }
+ // delete all saved data
+ pathMapDelete();
+ // add shutdown listener
+ workbench.addWorkbenchListener(new IWorkbenchListener() {
+ @Override
+ public void postShutdown(IWorkbench workbench) {
+ pathMapDelete();
+ }
+
+ @Override
+ public boolean preShutdown(IWorkbench workbench, boolean forced) {
+ return true;
+ }
+ });
+ // add part open/close listener
+ activePage.addPartListener(new PartListenerAdapter() {
+ @Override
+ public void partClosed(IWorkbenchPart part) {
+ IFile file = getInputFile(part);
+ if (file != null) {
+ taskQueue.add(new CloseTask(file));
+ }
+ }
+
+ @Override
+ public void partOpened(IWorkbenchPart part) {
+ IFile file = getInputFile(part);
+ if (file != null) {
+ taskQueue.add(new OpenTask(file));
+ }
+ }
+ });
+ // run tasks loop
+ {
+ Thread thread = new Thread() {
+ @Override
+ public void run() {
+ instance.runTasksLoop();
+ }
+ };
+ thread.setDaemon(true);
+ thread.start();
+ }
+ }
+}

Powered by Google App Engine
This is Rietveld 408576698