From 4f68510df5078132ec629449f64d197878c39416 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Galland?= Date: Thu, 8 Sep 2016 17:42:09 +0200 Subject: [PATCH] [eclipse] Add shortcuts for "Run as" and "Debug as". MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit close #475 Signed-off-by: Stéphane Galland --- .../io.sarl.eclipse/META-INF/MANIFEST.MF | 1 + .../OSGI-INF/l10n/bundle.properties | 2 + .../plugins/io.sarl.eclipse/plugin.xml | 43 ++ .../eclipse/launching/shortcuts/Messages.java | 50 ++ .../shortcuts/SARLLaunchShortcut.java | 579 ++++++++++++++++++ .../launching/shortcuts/messages.properties | 7 + .../src/io/sarl/eclipse/util/Jdt2Ecore.java | 23 + 7 files changed, 705 insertions(+) create mode 100644 eclipse-sarl/plugins/io.sarl.eclipse/src/io/sarl/eclipse/launching/shortcuts/Messages.java create mode 100644 eclipse-sarl/plugins/io.sarl.eclipse/src/io/sarl/eclipse/launching/shortcuts/SARLLaunchShortcut.java create mode 100644 eclipse-sarl/plugins/io.sarl.eclipse/src/io/sarl/eclipse/launching/shortcuts/messages.properties diff --git a/eclipse-sarl/plugins/io.sarl.eclipse/META-INF/MANIFEST.MF b/eclipse-sarl/plugins/io.sarl.eclipse/META-INF/MANIFEST.MF index 024b8888ea..053a0cab4b 100644 --- a/eclipse-sarl/plugins/io.sarl.eclipse/META-INF/MANIFEST.MF +++ b/eclipse-sarl/plugins/io.sarl.eclipse/META-INF/MANIFEST.MF @@ -33,6 +33,7 @@ Export-Package: io.sarl.eclipse, io.sarl.eclipse.launching.config, io.sarl.eclipse.launching.dialog, io.sarl.eclipse.launching.runner, + io.sarl.eclipse.launching.shortcuts, io.sarl.eclipse.launching.sreproviding;x-friends:="io.sarl.eclipse.tests", io.sarl.eclipse.natures;x-friends:="io.sarl.eclipse.tests", io.sarl.eclipse.navigator;x-friends:="io.sarl.eclipse.tests", diff --git a/eclipse-sarl/plugins/io.sarl.eclipse/OSGI-INF/l10n/bundle.properties b/eclipse-sarl/plugins/io.sarl.eclipse/OSGI-INF/l10n/bundle.properties index e04ed799a2..27472a6785 100644 --- a/eclipse-sarl/plugins/io.sarl.eclipse/OSGI-INF/l10n/bundle.properties +++ b/eclipse-sarl/plugins/io.sarl.eclipse/OSGI-INF/l10n/bundle.properties @@ -15,6 +15,8 @@ preference.errorWarning = Errors/Warnings launch.sarlApplication = SARL Application launch.sarlLauncher = SARL Standard Launcher launch.sarlLauncherDescription = The SARL application launcher supports running and debugging SARL applications on compatible platform. +launch.runas = Run SARL Agent +launch.debugas = Debug SARL Agent keyword.sarl = SARL keyword.sarl.sre = SRE keyword.sarl.runtime = Runtime diff --git a/eclipse-sarl/plugins/io.sarl.eclipse/plugin.xml b/eclipse-sarl/plugins/io.sarl.eclipse/plugin.xml index 3f99c5986d..df9f2da91e 100644 --- a/eclipse-sarl/plugins/io.sarl.eclipse/plugin.xml +++ b/eclipse-sarl/plugins/io.sarl.eclipse/plugin.xml @@ -200,6 +200,49 @@ id="io.sarl.eclipse.debug.LaunchConfigTabGroup" type="io.sarl.eclipse.debug.LaunchConfigType" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + { + final IParseResult parseRes = resource.getParseResult(); + if (parseRes == null) { + return null; + } + final ICompositeNode rootNode = parseRes.getRootNode(); + final ILeafNode node = NodeModelUtils.findLeafNodeAtOffset(rootNode, sel.getOffset()); + return NodeModelUtils.findActualSemanticObjectFor(node); + }); + if (obj != null) { + final SarlAgent agent = EcoreUtil2.getContainerOfType(obj, SarlAgent.class); + if (agent != null) { + searchAndLaunch(mode, agent); + return; + } + } + } else if (selection != null) { + launch(selection, mode); + return; + } + // Default launching + searchAndLaunch(mode, xtextEditor.getResource()); + } + + /** + * Launches the given agent type in the specified mode. + * + * @param projectName the name of the project. + * @param fullyQualifiedNameOfAgent the agent. + * @param mode launch mode + * @throws CoreException if something is going wrong. + */ + protected void launch(String projectName, String fullyQualifiedNameOfAgent, String mode) + throws CoreException { + final List configs = getCandidates(projectName, fullyQualifiedNameOfAgent); + ILaunchConfiguration config = null; + final int count = configs.size(); + if (count == 1) { + config = configs.get(0); + } else if (count > 1) { + config = chooseConfiguration(configs); + if (config == null) { + return; + } + } + if (config == null) { + config = createConfiguration(projectName, fullyQualifiedNameOfAgent); + } + if (config != null) { + DebugUITools.launch(config, mode); + } + } + + /** + * Creates and returns a new configuration based on the specified type. + * + * @param projectName the name of the project. + * @param fullyQualifiedNameOfAgent the agent. + * @return launch configuration configured to launch the specified type + */ + protected ILaunchConfiguration createConfiguration(String projectName, String fullyQualifiedNameOfAgent) { + try { + return this.configurator.newConfiguration(projectName, fullyQualifiedNameOfAgent); + } catch (CoreException exception) { + MessageDialog.openError(getShell(), Messages.SARLLaunchShortcut_0, exception.getStatus().getMessage()); + return null; + } + } + + /** + * Resolves an agent type that can be launched from the given scope and launches in the + * specified mode. + * + * @param mode launch mode. + * @param scope the elements to consider for an agent type that can be launched. + */ + private void searchAndLaunch(String mode, Object... scope) { + try { + final List agents = findAgents(scope, PlatformUI.getWorkbench().getProgressService()); + AgentDescription agent = null; + if (agents.isEmpty()) { + MessageDialog.openError(getShell(), Messages.SARLLaunchShortcut_0, Messages.SARLLaunchShortcut_2); + } else if (agents.size() > 1) { + agent = chooseType(agents); + } else { + agent = agents.get(0); + } + if (agent != null) { + try { + launch(agent.projectName, agent.agentName, mode); + } catch (CoreException e) { + MessageDialog.openError(getShell(), Messages.SARLLaunchShortcut_0, e.getMessage()); + } + } + } catch (InterruptedException exception) { + // + } catch (InvocationTargetException exception) { + MessageDialog.openError(getShell(), Messages.SARLLaunchShortcut_0, exception.getLocalizedMessage()); + } + } + + @SuppressWarnings("checkstyle:cyclomaticcomplexity") + private List findAgents(Object[] elements, IProgressService progress) + throws InvocationTargetException, InterruptedException { + final List descs = new ArrayList<>(); + progress.busyCursorWhile((monitor) -> { + try { + monitor.beginTask(Messages.SARLLaunchShortcut_5, elements.length); + for (final Object element : elements) { + if (element instanceof SarlAgent) { + final SarlAgent agent = (SarlAgent) element; + final URI fileURI = agent.eResource().getURI(); + for (final Pair storage: this.storage2UriMapper.getStorages(fileURI)) { + descs.add(new AgentDescription( + storage.getSecond().getName(), + this.nameProvider.getFullyQualifiedName(agent).toString())); + break; + } + } else { + final LinkedList stack = new LinkedList<>(); + stack.add(element); + while (!stack.isEmpty()) { + final Object current = stack.removeFirst(); + if (current instanceof IFile) { + final IFile file = (IFile) current; + final ResourceSet resourceSet = this.resourceSetProvider.get(file.getProject()); + final URI resourceURI = URI.createPlatformResourceURI(file.getFullPath().toString(), true); + final Resource resource = resourceSet.getResource(resourceURI, true); + if (resource != null) { + final String projectName = file.getProject().getName(); + for (final EObject content : resource.getContents()) { + if (content instanceof SarlScript) { + for (final SarlAgent agent : EcoreUtil2.getAllContentsOfType(content, SarlAgent.class)) { + descs.add(new AgentDescription( + projectName, + this.nameProvider.getFullyQualifiedName(agent).toString())); + } + } + } + } + } else if (current instanceof IType) { + final IType type = (IType) current; + final String qn = type.getFullyQualifiedName(); + final IJavaProject project = type.getJavaProject(); + if (this.jdt.isSubClassOf(this.jdt.toTypeFinder(project), qn, Agent.class.getName())) { + descs.add(new AgentDescription(project.getElementName(), qn)); + } + } else if (current instanceof IPackageFragment) { + final IPackageFragment fragment = (IPackageFragment) current; + stack.addAll(Arrays.asList(fragment.getNonJavaResources())); + for (final Object child : fragment.getChildren()) { + stack.add(child); + } + } else if (current instanceof IPackageFragmentRoot) { + final IPackageFragmentRoot fragment = (IPackageFragmentRoot) current; + stack.addAll(Arrays.asList(fragment.getNonJavaResources())); + for (final Object child : fragment.getChildren()) { + stack.add(child); + } + } else if (current instanceof IJavaProject) { + stack.addAll(Arrays.asList(((IJavaProject) current).getNonJavaResources())); + } + } + } + monitor.worked(1); + } + } catch (JavaModelException exception) { + throw new InvocationTargetException(exception); + } + }); + return descs; + } + + @SuppressWarnings("checkstyle:cyclomaticcomplexity") + private IResource findResource(Object[] elements) { + try { + for (final Object element : elements) { + if (element instanceof SarlAgent) { + final SarlAgent agent = (SarlAgent) element; + final URI fileURI = agent.eResource().getURI(); + for (final Pair storage: this.storage2UriMapper.getStorages(fileURI)) { + final Object obj = storage.getFirst(); + if (obj instanceof IResource) { + return (IResource) obj; + } + } + } else { + final LinkedList stack = new LinkedList<>(); + stack.add(element); + while (!stack.isEmpty()) { + final Object current = stack.removeFirst(); + if (current instanceof IFile) { + final IFile file = (IFile) current; + final ResourceSet resourceSet = this.resourceSetProvider.get(file.getProject()); + final URI resourceURI = URI.createPlatformResourceURI(file.getFullPath().toString(), true); + final Resource resource = resourceSet.getResource(resourceURI, true); + if (resource != null) { + for (final EObject content : resource.getContents()) { + if (content instanceof SarlScript + && !EcoreUtil2.getAllContentsOfType(content, SarlAgent.class).isEmpty()) { + return file; + } + } + } + } else if (current instanceof IType) { + final IType type = (IType) current; + final String qn = type.getFullyQualifiedName(); + final IJavaProject project = type.getJavaProject(); + if (this.jdt.isSubClassOf(this.jdt.toTypeFinder(project), qn, Agent.class.getName())) { + return type.getResource(); + } + } else if (current instanceof IPackageFragment) { + final IPackageFragment fragment = (IPackageFragment) current; + stack.addAll(Arrays.asList(fragment.getNonJavaResources())); + for (final Object child : fragment.getChildren()) { + stack.add(child); + } + } else if (current instanceof IPackageFragmentRoot) { + final IPackageFragmentRoot fragment = (IPackageFragmentRoot) current; + stack.addAll(Arrays.asList(fragment.getNonJavaResources())); + for (final Object child : fragment.getChildren()) { + stack.add(child); + } + } else if (current instanceof IJavaProject) { + stack.addAll(Arrays.asList(((IJavaProject) current).getNonJavaResources())); + } + } + } + } + } catch (JavaModelException exception) { + // + } + return null; + } + + /** Collect the listing of {@link ILaunchConfiguration}s that apply to the given agent. + * + * @param projectName the name of the project. + * @param fullyQualifiedNameOfAgent the agent. + * @return the list of {@link ILaunchConfiguration}s or an empty list, never null + * @throws CoreException if something is going wrong. + */ + private List getCandidates(String projectName, + String fullyQualifiedNameOfAgent) throws CoreException { + final ILaunchConfigurationType ctype = getLaunchManager().getLaunchConfigurationType( + this.accessor.getLaunchConfigurationType()); + List candidateConfigs = Collections.emptyList(); + final ILaunchConfiguration[] configs = getLaunchManager().getLaunchConfigurations(ctype); + candidateConfigs = new ArrayList<>(configs.length); + for (int i = 0; i < configs.length; i++) { + final ILaunchConfiguration config = configs[i]; + if (Objects.equals( + this.accessor.getAgent(config), + fullyQualifiedNameOfAgent) + && Objects.equals( + config.getAttribute(IJavaLaunchConfigurationConstants.ATTR_PROJECT_NAME, ""), //$NON-NLS-1$ + projectName)) { + candidateConfigs.add(config); + } + } + return candidateConfigs; + } + + /** + * Prompts the user to select an agent from the given agent types. + * + * @param agents the agent types to choose from. + * @return the selected agent or null if none. + */ + protected AgentDescription chooseType(List agents) { + final ElementListSelectionDialog dialog = new ElementListSelectionDialog(getShell(), + new LabelProvider()); + dialog.setElements(agents.toArray()); + dialog.setTitle(Messages.SARLLaunchShortcut_6); + dialog.setMessage(Messages.SARLLaunchShortcut_7); + dialog.setMultipleSelection(false); + final int result = dialog.open(); + if (result == Window.OK) { + return (AgentDescription) dialog.getFirstResult(); + } + return null; + } + + /** + * Returns a configuration from the given collection of configurations that should be launched, + * or null to cancel. Default implementation opens a selection dialog that allows + * the user to choose one of the specified launch configurations. Returns the chosen configuration, + * or null if the user cancels. + * + * @param configList list of configurations to choose from. + * @return configuration to launch or null to cancel. + */ + @SuppressWarnings("static-method") + protected ILaunchConfiguration chooseConfiguration(List configList) { + final IDebugModelPresentation labelProvider = DebugUITools.newDebugModelPresentation(); + final ElementListSelectionDialog dialog = new ElementListSelectionDialog(getShell(), labelProvider); + dialog.setElements(configList.toArray()); + dialog.setTitle(Messages.SARLLaunchShortcut_8); + dialog.setMessage(Messages.SARLLaunchShortcut_9); + dialog.setMultipleSelection(false); + final int result = dialog.open(); + labelProvider.dispose(); + if (result == Window.OK) { + return (ILaunchConfiguration) dialog.getFirstResult(); + } + return null; + } + + /** + * Convenience method to return the active workbench window shell. + * + * @return active workbench window shell + */ + protected static Shell getShell() { + return JDIDebugUIPlugin.getActiveWorkbenchShell(); + } + + /** + * Returns the singleton launch manager. + * + * @return launch manager + */ + protected static ILaunchManager getLaunchManager() { + return DebugPlugin.getDefault().getLaunchManager(); + } + + /** Description of an agent to launch. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ + private static class AgentDescription { + + /** Project name. + */ + @SuppressWarnings("checkstyle:visibilitymodifier") + public final String projectName; + + /** Agent fully qualified name. + */ + @SuppressWarnings("checkstyle:visibilitymodifier") + public final String agentName; + + AgentDescription(String project, String agent) { + this.projectName = project; + this.agentName = agent; + } + + @Override + public String toString() { + return this.agentName; + } + + } + + /** Label provider. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ + @SuppressWarnings("synthetic-access") + private class LabelProvider implements ILabelProvider { + + /** Constructor. + */ + LabelProvider() { + // + } + + @Override + public void addListener(ILabelProviderListener listener) { + SARLLaunchShortcut.this.labelProvider.addListener(listener); + } + + @Override + public void dispose() { + SARLLaunchShortcut.this.labelProvider.dispose(); + } + + @Override + public boolean isLabelProperty(Object element, String property) { + return SARLLaunchShortcut.this.labelProvider.isLabelProperty(element, property); + } + + @Override + public void removeListener(ILabelProviderListener listener) { + SARLLaunchShortcut.this.labelProvider.removeListener(listener); + } + + @Override + public Image getImage(Object element) { + if (element instanceof AgentDescription) { + return SARLLaunchShortcut.this.images.forAgent(JvmVisibility.PRIVATE, 0).createImage(); + } + return SARLLaunchShortcut.this.labelProvider.getImage(element); + } + + @Override + public String getText(Object element) { + if (element instanceof AgentDescription) { + return ((AgentDescription) element).agentName; + } + return SARLLaunchShortcut.this.labelProvider.getText(element); + } + + } + + @Override + public ILaunchConfiguration[] getLaunchConfigurations(ISelection selection) { + // let the framework resolve configurations based on resource mapping + return null; + } + + @Override + public ILaunchConfiguration[] getLaunchConfigurations(IEditorPart editorpart) { + // let the framework resolve configurations based on resource mapping + return null; + } + + @Override + public IResource getLaunchableResource(ISelection selection) { + if (selection instanceof IStructuredSelection) { + final IStructuredSelection sel = (IStructuredSelection) selection; + return findResource(sel.toArray()); + } + return null; + } + + @Override + public IResource getLaunchableResource(IEditorPart editorpart) { + final XtextEditor xtextEditor = EditorUtils.getXtextEditor(editorpart); + if (xtextEditor != null) { + return xtextEditor.getResource(); + } + return null; + } + +} diff --git a/eclipse-sarl/plugins/io.sarl.eclipse/src/io/sarl/eclipse/launching/shortcuts/messages.properties b/eclipse-sarl/plugins/io.sarl.eclipse/src/io/sarl/eclipse/launching/shortcuts/messages.properties new file mode 100644 index 0000000000..4069167bc6 --- /dev/null +++ b/eclipse-sarl/plugins/io.sarl.eclipse/src/io/sarl/eclipse/launching/shortcuts/messages.properties @@ -0,0 +1,7 @@ +SARLLaunchShortcut_0=Error +SARLLaunchShortcut_2=Selection does not contain an agent +SARLLaunchShortcut_5=Searching agents... +SARLLaunchShortcut_6=Select agent +SARLLaunchShortcut_7=&Select agent to launch: +SARLLaunchShortcut_8=Select configuration +SARLLaunchShortcut_9=&Select existing configuration: diff --git a/eclipse-sarl/plugins/io.sarl.eclipse/src/io/sarl/eclipse/util/Jdt2Ecore.java b/eclipse-sarl/plugins/io.sarl.eclipse/src/io/sarl/eclipse/util/Jdt2Ecore.java index 4f524707c5..f65817b37e 100644 --- a/eclipse-sarl/plugins/io.sarl.eclipse/src/io/sarl/eclipse/util/Jdt2Ecore.java +++ b/eclipse-sarl/plugins/io.sarl.eclipse/src/io/sarl/eclipse/util/Jdt2Ecore.java @@ -30,6 +30,7 @@ import java.util.List; import java.util.Map; import java.util.NoSuchElementException; +import java.util.Objects; import java.util.Set; import java.util.TreeSet; @@ -567,6 +568,28 @@ public void createActionsWith( } } + /** Replies if the given type is a subclass of the second type. + * + *

The type finder could be obtained with {@link #toTypeFinder(IJavaProject)}. + * + * @param typeFinder - the type finder to be used for finding the type definitions. + * @param subClass - the name of the sub class. + * @param superClass - the name of the expected super class. + * @return true if it is a subclass. + * @throws JavaModelException if the Java model is invalid. + * @see #toTypeFinder(IJavaProject) + */ + public boolean isSubClassOf(TypeFinder typeFinder, String subClass, String superClass) throws JavaModelException { + final SuperTypeIterator typeIterator = new SuperTypeIterator(typeFinder, false, subClass); + while (typeIterator.hasNext()) { + final IType type = typeIterator.next(); + if (Objects.equals(type.getFullyQualifiedName(), superClass)) { + return true; + } + } + return false; + } + /** Iterator on the super types of a given type. * * @author $Author: sgalland$