/* ****************************************************************************** * Copyright (c) 2006-2012 XMind Ltd. and others. * * This file is a part of XMind 3. XMind releases 3 and * above are dual-licensed under the Eclipse Public License (EPL), * which is available at http://www.eclipse.org/legal/epl-v10.html * and the GNU Lesser General Public License (LGPL), * which is available at http://www.gnu.org/licenses/lgpl.html * See http://www.xmind.net/license.html for details. * * Contributors: * XMind Ltd. - initial API and implementation *******************************************************************************/ package org.xmind.ui.internal.editor; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.ui.IEditorInput; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.IElementFactory; import org.eclipse.ui.IMemento; import org.eclipse.ui.IPersistableElement; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.XMLMemento; import org.xmind.core.Core; import org.xmind.core.IWorkbook; import org.xmind.core.internal.dom.WorkbookImpl; import org.xmind.gef.command.CommandStack; import org.xmind.ui.internal.MindMapUIPlugin; import org.xmind.ui.mindmap.IWorkbookRef; import org.xmind.ui.mindmap.IWorkbookRefManager; import org.xmind.ui.util.Logger; public class WorkbookRefManager implements IWorkbookRefManager { private static final String TAG_OPENED_EDITORS = "editors"; //$NON-NLS-1$ private static final String TAG_OPENED_EDITOR = "editor"; //$NON-NLS-1$ private static final String TAG_OPENED_INPUT = "input"; //$NON-NLS-1$ private static final String ATTR_OPENED_ID = "factoryID"; //$NON-NLS-1$ private static final String ATTR_OPENED_TITLE = "title"; //$NON-NLS-1$ private static final String TAG_OPENED_TEMPLOCATION = "tempLocation"; //$NON-NLS-1$ private static final String ATTR_OPENED_FILE = "path"; //$NON-NLS-1$ private static final String ATTR_SKIP_REVISIONS = "skip-revisions"; //$NON-NLS-1$ private static class EditorState { String tempLocation; boolean skipRevisions; } private class AutoHibernateHandler implements Runnable { private Thread thread; public void run() { thread = Thread.currentThread(); try { Thread.sleep(getAutoHibernateIntervals()); } catch (InterruptedException e) { } while (!isCanceled()) { hibernateAll(); if (isCanceled()) break; try { Thread.sleep(getAutoHibernateIntervals()); } catch (InterruptedException e) { } } } protected boolean isCanceled() { return autoHibernateThread != thread; } } private Map<Object, WorkbookRef> registry = new HashMap<Object, WorkbookRef>(); private Map<IEditorInput, EditorState> lastSession = null; private static WorkbookRefManager instance; private Thread autoHibernateThread = null; /** * One minute delay between two hibernating operations. */ private long autoHibernateIntervals = 60000; private File sessionFile = null; private Object sessionFileLock = new Object(); private WorkbookRefManager() { } public Collection<IWorkbookRef> getWorkbookRefs() { return new ArrayList<IWorkbookRef>(registry.values()); } public synchronized WorkbookRef addReferrer(Object source, IWorkbookReferrer referrer) throws CoreException { WorkbookRef ref = registry.get(source); if (ref != null) { ref.addReferrer(referrer); return ref; } ref = createWorkbookRef(source, referrer); ref.addReferrer(referrer); ensureAutoHibernateStarted(); return ref; } private synchronized WorkbookRef createWorkbookRef(Object source, IWorkbookReferrer referrer) throws CoreException { WorkbookRef ref = new WorkbookRef(); WorkbookRefInitializer.getInstance().initialize(ref, source, referrer); if (!ref.isReady()) { IStatus status = new Status(IStatus.ERROR, MindMapUIPlugin.PLUGIN_ID, "Failed to create a workbook reference with valid source."); //$NON-NLS-1$ throw new CoreException(status); } initializeRef((WorkbookRef) ref, source); if (lastSession != null) { EditorState state = lastSession.get(source); if (state != null) { File file = MME.getFile(source); ref.setWorkbookLoader(new TempWorkbookLoader(ref, state.tempLocation, file == null ? null : file .getAbsolutePath(), state.skipRevisions)); } } registry.put(source, ref); return ref; } private void initializeRef(WorkbookRef ref, Object source) throws CoreException { if (ref.getCommandStack() == null) { ref.setCommandStack(new CommandStack()); } if (ref.getKey() == null) { ref.setKey(source); } } public synchronized void removeReferrer(Object source, IWorkbookReferrer referrer) { WorkbookRef ref = registry.get(source); try { if (ref != null) { try { ref.removeReferrer(referrer); } finally { if (!ref.isReferred()) { registry.remove(source); // Have to check if there's still other references // relying on this workbook's resources. boolean noRelatedRef = findRef(ref.getWorkbook()) == null; ref.dispose(noRelatedRef); hibernateAll(); } } } } finally { if (registry.isEmpty()) { stopAutoHibernate(); } } } public synchronized IWorkbookRef createRef(IEditorInput editorInput, IEditorPart editor) { IWorkbookReferrer referrer = findWorkbookReferrer(editor); if (referrer == null) return null; try { return addReferrer(editorInput, referrer); } catch (CoreException e) { Logger.log(e); return null; } } private IWorkbookReferrer findWorkbookReferrer(IEditorPart editor) { IWorkbookReferrer referrer = null; if (editor instanceof IWorkbookReferrer) referrer = (IWorkbookReferrer) editor; else referrer = (IWorkbookReferrer) editor .getAdapter(IWorkbookReferrer.class); return referrer; } public synchronized void disposeRef(IEditorInput editorInput, IEditorPart editor) { IWorkbookReferrer referrer = findWorkbookReferrer(editor); if (referrer != null) removeReferrer(editorInput, referrer); } public IWorkbookRef findRef(IWorkbook workbook) { if (workbook == null) return null; for (IWorkbookRef ref : registry.values()) { if (workbook.equals(ref.getWorkbook())) return ref; } return null; } public static WorkbookRefManager getInstance() { if (instance == null) { instance = new WorkbookRefManager(); } return instance; } void changeKey(WorkbookRef ref, Object oldKey, Object newKey) { WorkbookRef oldRef = registry.remove(oldKey); if (oldRef != ref) return; registry.put(newKey, ref); } public long getAutoHibernateIntervals() { return autoHibernateIntervals; } public void setAutoHibernateIntervals(long autoHibernateIntervals) { this.autoHibernateIntervals = Math.max(60000, autoHibernateIntervals); } private void ensureAutoHibernateStarted() { if (autoHibernateThread != null) return; Thread newThread = new Thread(new AutoHibernateHandler()); newThread.setDaemon(true); newThread.setName("XMind: Auto Save Temporary Workbooks"); //$NON-NLS-1$ newThread.setPriority(Thread.MIN_PRIORITY); autoHibernateThread = newThread; newThread.start(); } public void hibernateAll() { if (registry.isEmpty()) { stopAutoHibernate(); synchronized (sessionFileLock) { getSessionFile().delete(); } } else { synchronized (sessionFileLock) { saveSession(); } } } private void stopAutoHibernate() { Thread oldThread = autoHibernateThread; autoHibernateThread = null; if (oldThread != null) { oldThread.interrupt(); } } protected void saveSession() { XMLMemento memento = XMLMemento.createWriteRoot(TAG_OPENED_EDITORS); for (WorkbookRef ref : registry.values()) { IWorkbook workbook = ref.getWorkbook(); if (workbook != null) { try { workbook.saveTemp(); } catch (Throwable e) { } Object key = ref.getKey(); IMemento editorMem = memento.createChild(TAG_OPENED_EDITOR); if (key instanceof IEditorInput) { saveEditorInput(editorMem, (IEditorInput) key); } saveTempLocation(editorMem, workbook.getTempLocation()); editorMem.putBoolean(ATTR_SKIP_REVISIONS, ((WorkbookImpl) workbook).isSkipRevisionsWhenSaving()); } } if (memento != null) { try { memento.save(new OutputStreamWriter(new FileOutputStream( getSessionFile()), "utf-8")); //$NON-NLS-1$ } catch (IOException e) { Logger.log(e, "Failed to save session log."); //$NON-NLS-1$ } } else { getSessionFile().delete(); } } private File getSessionFile() { if (sessionFile == null) { sessionFile = new File(Core.getWorkspace().getTempFile(".opened")); //$NON-NLS-1$ } return sessionFile; } private void saveEditorInput(IMemento memento, IEditorInput input) { IMemento inputMem = memento.createChild(TAG_OPENED_INPUT); IPersistableElement p = input.getPersistable(); if (p != null) { p.saveState(inputMem); String id = p.getFactoryId(); inputMem.putString(ATTR_OPENED_ID, id); } else { inputMem.putString(ATTR_OPENED_TITLE, input.getName()); } } private IEditorInput loadEditorInput(IMemento memento) { IMemento inputMem = memento.getChild(TAG_OPENED_INPUT); if (inputMem == null) return new WorkbookEditorInput(); String factoryId = inputMem.getString(ATTR_OPENED_ID); if (factoryId != null) { IElementFactory factory = PlatformUI.getWorkbench() .getElementFactory(factoryId); return (IEditorInput) factory.createElement(inputMem); } else { String title = inputMem.getString(ATTR_OPENED_TITLE); return new WorkbookEditorInput(title); } } private void saveTempLocation(IMemento memento, String path) { IMemento pathMem = memento.createChild(TAG_OPENED_TEMPLOCATION); pathMem.putString(ATTR_OPENED_FILE, path); } private EditorState loadEditorState(IMemento memento) { IMemento tempMem = memento.getChild(TAG_OPENED_TEMPLOCATION); if (tempMem == null) return null; EditorState state = new EditorState(); state.tempLocation = tempMem.getString(ATTR_OPENED_FILE); Boolean skipRevisions = tempMem.getBoolean(ATTR_SKIP_REVISIONS); state.skipRevisions = skipRevisions != null && skipRevisions.booleanValue(); return state; } public List<IEditorInput> loadLastSession() { File file = new File(Core.getWorkspace().getTempFile(".opened")); //$NON-NLS-1$ if (!file.exists() || !file.isFile() || !file.canRead()) return null; lastSession = null; return loadSessionFromFile(file); } private List<IEditorInput> loadSessionFromFile(File file) { List<IEditorInput> list = null; FileInputStream fileStream = null; InputStreamReader reader = null; try { fileStream = new FileInputStream(file); reader = new InputStreamReader(fileStream, "utf-8"); //$NON-NLS-1$ XMLMemento memento = XMLMemento.createReadRoot(reader); IMemento[] elements = memento.getChildren(TAG_OPENED_EDITOR); for (IMemento editorMem : elements) { IEditorInput input = loadEditorInput(editorMem); EditorState state = loadEditorState(editorMem); if (state != null) { if (lastSession == null) lastSession = new HashMap<IEditorInput, EditorState>(); lastSession.put(input, state); } if (list == null) list = new ArrayList<IEditorInput>(); list.add(input); } } catch (Exception e) { Logger.log(e, "Failed to load session log."); //$NON-NLS-1$ } finally { if (reader != null) { try { reader.close(); } catch (IOException e2) { } } if (fileStream != null) { try { fileStream.close(); } catch (IOException e2) { } } } return list; } public void clearLastSession() { lastSession = null; } }