blob: 284949456645035918fac297cc67b0d517aba41e [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2013 Christian Pontesegger and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* Contributors:
* Christian Pontesegger - initial API and implementation
*******************************************************************************/
package org.eclipse.ease.ui.launching;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.Status;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchConfigurationType;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
import org.eclipse.debug.core.ILaunchManager;
import org.eclipse.debug.core.Launch;
import org.eclipse.debug.core.model.ISourceLocator;
import org.eclipse.ease.AbstractScriptEngine;
import org.eclipse.ease.IDebugEngine;
import org.eclipse.ease.IExecutionListener;
import org.eclipse.ease.IReplEngine;
import org.eclipse.ease.IScriptEngine;
import org.eclipse.ease.Script;
import org.eclipse.ease.service.EngineDescription;
import org.eclipse.ease.service.IScriptService;
import org.eclipse.ease.service.ScriptType;
import org.eclipse.ease.tools.ResourceTools;
import org.eclipse.ease.tools.RunnableWithResult;
import org.eclipse.ease.tools.StringTools;
import org.eclipse.ease.ui.console.ScriptConsole;
import org.eclipse.ease.ui.tools.AbstractLaunchDelegate;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.PlatformUI;
/**
* Quick launcher for EASE script files.
*/
public class EaseLaunchDelegate extends AbstractLaunchDelegate {
private class EASELaunch extends Launch implements IExecutionListener {
private boolean fIsTerminated = false;
/**
* Constructs a launch with the specified attributes.
*
* @param launchConfiguration
* the configuration that was launched
* @param mode
* the mode of this launch - run or debug (constants defined by <code>ILaunchManager</code>)
* @param locator
* the source locator to use for this debug session, or <code>null</code> if not supported
*/
public EASELaunch(ILaunchConfiguration launchConfiguration, String mode, ISourceLocator locator) {
super(launchConfiguration, mode, locator);
}
@Override
public boolean isTerminated() {
return fIsTerminated || super.isTerminated();
}
@Override
public void notify(IScriptEngine engine, Script script, int status) {
if (IExecutionListener.ENGINE_END == status) {
fIsTerminated = true;
if (DebugPlugin.getDefault() != null) {
fireTerminate();
// remove launch when it was triggered in RUN node
if (ILaunchManager.RUN_MODE.equals(getLaunchMode()))
getLaunchManager().removeLaunch(this);
}
}
}
}
private static final String LAUNCH_CONFIGURATION_ID = "org.eclipse.ease.launchConfigurationType";
@Override
public void launch(final ILaunchConfiguration configuration, final String mode, final ILaunch launch, final IProgressMonitor monitor) throws CoreException {
final String fileLocation = getFileLocation(configuration);
final Object resource = ResourceTools.resolve(fileLocation);
if (ResourceTools.isFile(resource)) {
// create engine
final String engineID = configuration.getAttribute(LaunchConstants.SCRIPT_ENGINE, "");
final IScriptService scriptService = PlatformUI.getWorkbench().getService(IScriptService.class);
EngineDescription engineDescription = scriptService.getEngineByID(engineID);
if ((ILaunchManager.DEBUG_MODE.equals(mode)) && (!engineDescription.supportsDebugging())) {
// we are trying to debug using an engine that does not support debugging
engineDescription = null;
// try to find an engine that supports debugging
final ScriptType scriptType = scriptService.getScriptType(ResourceTools.toAbsoluteLocation(resource, null));
if (scriptType != null) {
final List<EngineDescription> engines = scriptService.getEngines(scriptType.getName());
for (final EngineDescription description : engines) {
if (description.supportsDebugging()) {
// matching debug engine found
engineDescription = description;
break;
}
}
}
if (engineDescription != null) {
// ask user if he wants to change the engine: once, permanently or not
final RunnableWithResult<Boolean> runnable = new RunnableWithResult<Boolean>() {
@Override
public Boolean runWithTry() throws Throwable {
return MessageDialog.openConfirm(Display.getDefault().getActiveShell(), "Configuration change needed",
"The currently selected script engine does not support debugging. However an alternative engine is available. Do you want to debug your script using that alternative engine?");
}
};
Display.getDefault().syncExec(runnable);
if (!runnable.getResult())
// user does not want to switch engine
return;
} else {
// giving up
Display.getDefault().asyncExec(() -> MessageDialog.openError(Display.getDefault().getActiveShell(), "Launch error",
"No debug engine available for \"" + resource + "\""));
return;
}
}
final IScriptEngine engine = engineDescription.createEngine();
if (engine instanceof IReplEngine)
((IReplEngine) engine).setTerminateOnIdle(true);
if (engine instanceof AbstractScriptEngine)
((AbstractScriptEngine) engine).setLaunch(launch);
// initialize console
final ScriptConsole console = ScriptConsole.create(engine.getName() + ": " + resource, engine);
engine.setOutputStream(console.getOutputStream());
engine.setErrorStream(console.getErrorStream());
engine.setInputStream(console.getInputStream());
// setup debugger
if (ILaunchManager.DEBUG_MODE.equals(mode))
setupDebugger(engine, configuration, launch);
// register libraries
final Collection<String> libraries = LaunchConstants.getLibraries(configuration);
for (final String libraryPath : libraries) {
try {
engine.registerJar(new URL(libraryPath));
} catch (final MalformedURLException e) {
// malformed URL
Display.getDefault().asyncExec(() -> MessageDialog.openError(Display.getDefault().getActiveShell(), "Launch error",
"Invalid URL for the library \"" + libraryPath + "\""));
return;
}
}
// set startup parameters
final String parameterString = configuration.getAttribute(LaunchConstants.STARTUP_PARAMETERS, "").trim();
final String[] parameters = StringTools.parseArguments(parameterString);
engine.setVariable("argv", parameters);
// execute resource
engine.executeAsync(resource);
// start engine
engine.schedule();
// let launch know when engine terminates
if (launch instanceof IExecutionListener)
engine.addExecutionListener((IExecutionListener) launch);
} else {
Display.getDefault().asyncExec(
() -> MessageDialog.openError(Display.getDefault().getActiveShell(), "Launch error", "File could not be found: \"" + fileLocation + "\""));
}
}
@Override
protected ILaunchConfiguration createLaunchConfiguration(final IResource file, final String mode) throws CoreException {
final ILaunchManager manager = DebugPlugin.getDefault().getLaunchManager();
final ILaunchConfigurationType type = manager.getLaunchConfigurationType(LAUNCH_CONFIGURATION_ID);
final ILaunchConfigurationWorkingCopy configuration = type.newInstance(null, file.getName());
configuration.setAttribute(LaunchConstants.FILE_LOCATION, ResourceTools.toAbsoluteLocation(file, null));
// find a valid engine
final IScriptService scriptService = PlatformUI.getWorkbench().getService(IScriptService.class);
final ScriptType scriptType = scriptService.getScriptType(ResourceTools.toAbsoluteLocation(file, null));
final Collection<EngineDescription> engines;
if (scriptType == null) {
engines = Collections.emptyList();
} else {
engines = scriptType.getEngines();
}
if (engines.isEmpty())
// TODO use a better way to bail out and use the direct file launch
throw new CoreException(Status.CANCEL_STATUS);
configuration.setAttribute(LaunchConstants.SCRIPT_ENGINE, engines.iterator().next().getID());
// by default a debug configuration should suspend on script startup
if (ILaunchManager.DEBUG_MODE.equals(mode))
configuration.setAttribute(LaunchConstants.SUSPEND_ON_STARTUP, true);
// save and return new configuration
configuration.doSave();
return configuration;
}
@Override
protected String getFileLocation(final ILaunchConfiguration configuration) throws CoreException {
return configuration.getAttribute(LaunchConstants.FILE_LOCATION, "");
}
@Override
protected String getLaunchConfigurationId() {
return LAUNCH_CONFIGURATION_ID;
}
@Override
public ILaunch getLaunch(ILaunchConfiguration configuration, String mode) throws CoreException {
return new EASELaunch(configuration, mode, null);
}
private void setupDebugger(final IScriptEngine engine, final ILaunchConfiguration configuration, final ILaunch launch) {
if (engine instanceof IDebugEngine) {
((IDebugEngine) engine).setupDebugger(launch, getSuspendOnStartupValue(configuration), getSuspendOnScriptLoadValue(configuration),
getDisplayDynamicCodeValue(configuration));
}
}
public static boolean getSuspendOnStartupValue(ILaunchConfiguration configuration) {
try {
return configuration.getAttribute(LaunchConstants.SUSPEND_ON_STARTUP, false);
} catch (final CoreException e) {
}
return false;
}
public static boolean getSuspendOnScriptLoadValue(ILaunchConfiguration configuration) {
try {
return configuration.getAttribute(LaunchConstants.SUSPEND_ON_SCRIPT_LOAD, false);
} catch (final CoreException e) {
}
return false;
}
public static boolean getDisplayDynamicCodeValue(ILaunchConfiguration configuration) {
try {
return configuration.getAttribute(LaunchConstants.DISPLAY_DYNAMIC_CODE, false);
} catch (final CoreException e) {
}
return false;
}
}