/* * Copyright (C) 2006-2016 DLR, Germany * * All rights reserved * * http://www.rcenvironment.de/ */ package de.rcenvironment.core.gui.utils.common; import java.io.File; import java.util.concurrent.atomic.AtomicLong; import org.eclipse.core.filesystem.EFS; import org.eclipse.core.filesystem.IFileStore; import org.eclipse.core.resources.IFile; import org.eclipse.core.runtime.Path; import org.eclipse.ui.IEditorDescriptor; import org.eclipse.ui.IEditorInput; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.IPropertyListener; import org.eclipse.ui.IWorkbench; import org.eclipse.ui.IWorkbenchListener; import org.eclipse.ui.PartInitException; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.ide.FileStoreEditorInput; import org.eclipse.ui.part.FileInPlaceEditorInput; /** * Helpers for handling of temporary files and workspace files, including dirty-state observing and editor detection. * * @author Arne Bachmann */ public final class EditorsHelper { /** * Default editor extension (fallback editor). */ private static final String TXT = "txt"; /** * "Index of" constant. */ private static final int NOT_FOUND = -1; /** * This class has only static methods. */ private EditorsHelper() { // only static methods } /** * Helper to determine the file extension. * @param filename The file name to check * @return the pure extension without a dot or txt as a fallback */ public static String getExtension(final String filename) { if (filename == null) { return TXT; // default } final String f = filename.trim(); final int lastSlash = f.lastIndexOf(File.separator); // works only on current node (same OS) final int lastDot = f.lastIndexOf("."); if (lastDot > lastSlash) { // found an extension return f.substring(lastDot + 1); } return TXT; // fallback } /** * Helper to get just any matching editor. * A message dialog pops up if nothing found (!). * @param filename The filename to find an editor for * @return The editor descriptor */ private static IEditorDescriptor findEditorForFilename(final String filename) { IEditorDescriptor editor = PlatformUI.getWorkbench().getEditorRegistry().getDefaultEditor(filename); if (editor == null) { editor = PlatformUI.getWorkbench().getEditorRegistry().getEditors("*." + TXT)[0]; } return editor; } /** * Open an in-place editor within the editing view. Falls back to txt-editor. * @param ifile The file to open * @param callbacks The action to perform whenever save is activated, or null for none * @throws PartInitException on error */ public static void openFileInEditor(final IFile ifile, final Runnable... callbacks) throws PartInitException { if (ifile == null) { return; } openAndObserveFileInEditor(new ObservedFile(ifile), findEditorForFilename(ifile.getName()), new FileInPlaceEditorInput(ifile), callbacks); } /** * Helper to open an external file. * Set it to read-only if necessary * * @param file The file to open (mostly a temp file) * @param callbacks The action to perform whenever save is activated, or null for none * @throws PartInitException on error */ public static void openExternalFileInEditor(final File file, final Runnable... callbacks) throws PartInitException { final IFileStore fileStore = EFS.getLocalFileSystem().getStore(new Path(file.getAbsolutePath())); openAndObserveFileInEditor(new ObservedFile(file), findEditorForFilename(file.getName()), new FileStoreEditorInput(fileStore), callbacks); } private static void openAndObserveFileInEditor(final ObservedFile observedFile, final IEditorDescriptor editorDescriptor, final IEditorInput editorInput, final Runnable... callbacks) throws PartInitException { final IEditorPart editor = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().openEditor(editorInput, editorDescriptor.getId()); if (editor != null) { // if internal editor observeEditedFile(observedFile, editor, new Runnable() { @Override public void run() { if (callbacks != null) { for (final Runnable action: callbacks) { action.run(); } } } }); // close editor on RCE shutdown as the file-property relation is gone after RCE is started again PlatformUI.getWorkbench().addWorkbenchListener(new IWorkbenchListener() { @Override public boolean preShutdown(IWorkbench workbench, boolean arg1) { editor.getSite().getPage().closeEditor(editor, false); return true; } @Override public void postShutdown(IWorkbench workbench) {} }); } } /** * Observes a file and call the action upon each safe action. * @param observedFile The file in the local filesystem to observe * @param editor The editor we have opened * @param callback The action to perform on each safe (but not on undo that makes the editor clean again) */ private static void observeEditedFile(final ObservedFile observedFile, final IEditorPart editor, final Runnable callback) { final AtomicLong timeStamp = new AtomicLong(observedFile.getLastModified()); // we simply need a final reference here editor.addPropertyListener(new IPropertyListener() { @Override public void propertyChanged(final Object source, final int id) { if (id == IEditorPart.PROP_DIRTY) { if (!((IEditorPart) source).isDirty()) { // became clean, thus must have been saved final long ts = observedFile.getLastModified(); if (ts > timeStamp.longValue()) { timeStamp.set(ts); callback.run(); } } } } }); } /** * Abstracts {@link File} and {@link IFile} objects. * * @author Doreen Seider */ static class ObservedFile { private File file; private IFile iFile; ObservedFile(IFile iFile) { this.iFile = iFile; } ObservedFile(File file) { this.file = file; } public long getLastModified() { if (file != null) { return file.lastModified(); } else { return iFile.getModificationStamp(); } } } }