/*
* 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();
}
}
}
}