/*******************************************************************************
* Copyright (c) 2015, 2016 itemis AG and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Tamas Miklossy (itemis AG) - Refactoring of DOT Graph view live update/live export (bug #337644)
*
*******************************************************************************/
package org.eclipse.gef.dot.internal.ui.handlers;
import java.io.File;
import java.net.MalformedURLException;
import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceChangeEvent;
import org.eclipse.core.resources.IResourceChangeListener;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.IResourceDeltaVisitor;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.IWorkspaceRunnable;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.gef.dot.internal.DotExecutableUtils;
import org.eclipse.gef.dot.internal.DotFileUtils;
import org.eclipse.gef.dot.internal.ui.GraphvizPreferencePage;
import org.eclipse.gef.dot.internal.ui.language.internal.DotActivator;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.ToolItem;
import org.eclipse.ui.IEditorRegistry;
import org.eclipse.ui.IPartListener2;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchPartReference;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.handlers.HandlerUtil;
import org.eclipse.ui.part.FileEditorInput;
import org.eclipse.xtext.ui.editor.XtextEditor;
public class SyncGraphvizExportHandler extends AbstractHandler {
private static final String EXTENSION = "dot"; //$NON-NLS-1$
@Override
public Object execute(ExecutionEvent executionEvent)
throws ExecutionException {
Event event = (Event) executionEvent.getTrigger();
ToolItem toolItem = (ToolItem) event.widget;
if (toolItem.getSelection()) {// the toggle button was switched on
// check if Graphviz is configured properly
if (!GraphvizPreferencePage.isGraphvizConfigured()) {
GraphvizPreferencePage.showGraphvizConfigurationDialog();
}
// if Graphviz is still not configured properly, do not export
if (!GraphvizPreferencePage.isGraphvizConfigured()) {
toolItem.setSelection(false);
return null;
}
addListeners();
IWorkbenchPart activeEditor = HandlerUtil
.getActiveWorkbenchWindow(executionEvent).getActivePage()
.getActiveEditor();
checkActiveEditorAndExportGraph(activeEditor);
} else { // the toggle button was switched off
removeListeners();
}
return null;
}
private void addListeners() {
PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage()
.addPartListener(partListener);
ResourcesPlugin.getWorkspace().addResourceChangeListener(
resourceChangeListener, IResourceChangeEvent.POST_BUILD
| IResourceChangeEvent.POST_CHANGE);
}
private void removeListeners() {
PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage()
.removePartListener(partListener);
ResourcesPlugin.getWorkspace()
.removeResourceChangeListener(resourceChangeListener);
}
/**
* Listen to part life-cycle events(part activated) and export the graph.
*/
private IPartListener2 partListener = new IPartListener2() {
@Override
public void partActivated(IWorkbenchPartReference partRef) {
IWorkbenchPart part = partRef.getPart(false);
checkActiveEditorAndExportGraph(part);
}
@Override
public void partBroughtToTop(IWorkbenchPartReference partRef) {
}
@Override
public void partClosed(IWorkbenchPartReference partRef) {
}
@Override
public void partDeactivated(IWorkbenchPartReference partRef) {
}
@Override
public void partOpened(IWorkbenchPartReference partRef) {
}
@Override
public void partHidden(IWorkbenchPartReference partRef) {
}
@Override
public void partVisible(IWorkbenchPartReference partRef) {
}
@Override
public void partInputChanged(IWorkbenchPartReference partRef) {
}
};
/** Listener that passes a visitor if a resource is changed. */
private IResourceChangeListener resourceChangeListener = new IResourceChangeListener() {
@Override
public void resourceChanged(IResourceChangeEvent event) {
if (event.getType() != IResourceChangeEvent.POST_BUILD
&& event.getType() != IResourceChangeEvent.POST_CHANGE) {
return;
}
IResourceDelta rootDelta = event.getDelta();
try {
rootDelta.accept(resourceDeltaVisitor);
} catch (CoreException e) {
e.printStackTrace();
}
}
};
/** If a *.dot file is visited, export the graph. */
private IResourceDeltaVisitor resourceDeltaVisitor = new IResourceDeltaVisitor() {
@Override
public boolean visit(final IResourceDelta delta) {
IResource resource = delta.getResource();
if (resource.getType() == IResource.FILE
&& ((IFile) resource).getName().endsWith(EXTENSION)) {
try {
IWorkspace workspace = ResourcesPlugin.getWorkspace();
if (!workspace.isTreeLocked()) {
IFile file = (IFile) resource;
workspace.run(new DotExportRunnable(file), null);
}
} catch (CoreException e) {
e.printStackTrace();
}
}
return true;
}
};
private class DotExportRunnable implements IWorkspaceRunnable {
private IFile file;
DotExportRunnable(IFile file) {
this.file = file;
}
@Override
public void run(IProgressMonitor monitor) throws CoreException {
exportGraph(file);
}
};
/**
* if the active editor is the DOT Editor, export the graph, otherwise do
* nothing
*/
private void checkActiveEditorAndExportGraph(IWorkbenchPart part) {
if (part instanceof XtextEditor) {
XtextEditor editor = (XtextEditor) part;
if (DotActivator.ORG_ECLIPSE_GEF_DOT_INTERNAL_LANGUAGE_DOT
.equals(editor.getLanguageName())
&& editor.getEditorInput() instanceof FileEditorInput) {
IFile file = ((FileEditorInput) editor.getEditorInput())
.getFile();
exportGraph(file);
}
}
}
private void exportGraph(IFile inputFile) {
File resolvedInputFile = null;
try {
resolvedInputFile = DotFileUtils
.resolve(inputFile.getLocationURI().toURL());
} catch (MalformedURLException e) {
e.printStackTrace();
return;
}
String dotExportFormat = GraphvizPreferencePage.getDotExportFormat();
if (dotExportFormat.isEmpty()) {
return;
}
String[] outputs = new String[2];
File outputFile = DotExecutableUtils.renderImage(
new File(GraphvizPreferencePage.getDotExecutablePath()),
resolvedInputFile, dotExportFormat, null, outputs);
// whenever the dot executable call produced any error message, show it
// to the user within an error message box
if (!outputs[1].isEmpty()) {
Display.getDefault().asyncExec(new Runnable() {
@Override
public void run() {
MessageDialog.openError(
Display.getDefault().getActiveShell(),
"Errors from dot call:", outputs[1]); //$NON-NLS-1$
}
});
}
// refresh the parent folder and open the output file if the export
// was successful
if (outputFile != null) {
refreshParent(outputFile);
openFile(outputFile);
}
}
private void refreshParent(File file) {
try {
ResourcesPlugin.getWorkspace().getRoot()
.getFileForLocation(
Path.fromOSString(file.getAbsolutePath()))
.getParent().refreshLocal(IResource.DEPTH_ONE, null);
} catch (CoreException e) {
e.printStackTrace();
}
}
private void openFile(File resolvedFile) {
IPath location = Path.fromOSString(resolvedFile.getAbsolutePath());
final IFile file = ResourcesPlugin.getWorkspace().getRoot()
.getFileForLocation(location);
IEditorRegistry registry = PlatformUI.getWorkbench()
.getEditorRegistry();
if (registry.isSystemExternalEditorAvailable(file.getName())) {
/**
* in case of opening the exported file from an other thread e.g. in
* case of listening to an IResourceChangeEvent
*/
Display.getDefault().asyncExec(new Runnable() {
@Override
public void run() {
IWorkbenchPage page = PlatformUI.getWorkbench()
.getActiveWorkbenchWindow().getActivePage();
try {
page.openEditor(new FileEditorInput(file),
IEditorRegistry.SYSTEM_EXTERNAL_EDITOR_ID);
} catch (PartInitException e) {
e.printStackTrace();
}
}
});
}
}
}