package com.redhat.ceylon.eclipse.core.launch; import static com.redhat.ceylon.eclipse.core.launch.ICeylonLaunchConfigurationConstants.ATTR_MODULE_NAME; import static com.redhat.ceylon.eclipse.core.launch.ICeylonLaunchConfigurationConstants.ATTR_TOPLEVEL_NAME; import static com.redhat.ceylon.eclipse.core.launch.LaunchHelper.getTopLevelDisplayName; import static org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants.ATTR_PROJECT_NAME; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.LinkedList; import java.util.List; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.debug.core.DebugPlugin; 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.ui.DebugUITools; import org.eclipse.debug.ui.IDebugModelPresentation; import org.eclipse.debug.ui.ILaunchShortcut2; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.text.ITextSelection; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.window.Window; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.dialogs.ElementListSelectionDialog; import com.redhat.ceylon.compiler.typechecker.tree.Node; import com.redhat.ceylon.compiler.typechecker.tree.Tree; import com.redhat.ceylon.eclipse.code.editor.CeylonEditor; import com.redhat.ceylon.eclipse.code.parse.CeylonParseController; import com.redhat.ceylon.eclipse.util.EditorUtil; import com.redhat.ceylon.eclipse.util.Nodes; import com.redhat.ceylon.model.typechecker.model.Class; import com.redhat.ceylon.model.typechecker.model.Declaration; import com.redhat.ceylon.model.typechecker.model.Function; import com.redhat.ceylon.model.typechecker.model.Module; public abstract class CeylonModuleLaunchShortcut implements ILaunchShortcut2 { protected ILaunchManager getLaunchManager() { return DebugPlugin.getDefault().getLaunchManager(); } protected abstract ILaunchConfigurationType getConfigurationType(); private String getLaunchConfigurationName(String projectName, String moduleName, Declaration declarationToRun) { String topLevelDisplayName = LaunchHelper.getTopLevelDisplayName(declarationToRun); String configurationName = projectName.trim() + " \u2014 " + moduleName.trim() + " \u2014 " + topLevelDisplayName.trim() + " \u2192 " + launchType(); // configurationName = configurationName // .replaceAll("[\u00c0-\ufffe]", "_"); return getLaunchManager() .generateLaunchConfigurationName(configurationName); } /** * Creates a <b>new</b> configuration if none was found or chosen. * * @return the configuration created. */ protected ILaunchConfiguration createConfiguration(Declaration declarationToRun, IResource resource) { try { Module mod = LaunchHelper.getModule(declarationToRun); String moduleName = mod.getNameAsString(); String projectName = resource.getProject().getName(); String lcn = getLaunchConfigurationName(projectName, moduleName, declarationToRun); ILaunchConfigurationWorkingCopy wc = getConfigurationType() .newInstance(null, lcn); wc.setAttribute(ATTR_PROJECT_NAME, projectName); wc.setAttribute(ATTR_MODULE_NAME, LaunchHelper.getFullModuleName(mod)); // save the runnable display name, which may be exact name or 'run - default' wc.setAttribute(ATTR_TOPLEVEL_NAME, LaunchHelper.getTopLevelDisplayName(declarationToRun)); wc.setMappedResources(new IResource[] {resource}); return wc.doSave(); } catch (CoreException exception) { MessageDialog.openError(EditorUtil.getShell(), "Ceylon Module Launcher Error", exception.getStatus().getMessage()); return null; } } abstract String launchType(); /** * Finds and returns an <b>existing</b> configuration to re-launch for the given type, * or <code>null</code> if there is no existing configuration. * * @return a configuration to use for launching the given type or <code>null</code> if none */ protected ILaunchConfiguration findLaunchConfiguration(Declaration declaration, IResource resource, ILaunchConfigurationType configType) { List<ILaunchConfiguration> candidateConfigs = getLaunchConfigurations(declaration, resource, configType); int candidateCount = candidateConfigs.size(); if (candidateCount == 1) { return candidateConfigs.get(0); } else if (candidateCount > 1) { return chooseConfiguration(candidateConfigs); } return null; } private List<ILaunchConfiguration> getLaunchConfigurations(Declaration declaration, IResource resource, ILaunchConfigurationType configType) { List<ILaunchConfiguration> candidateConfigs = Collections.<ILaunchConfiguration>emptyList(); String projectName = resource.getProject().getName(); try { ILaunchConfiguration[] configs = getLaunchManager() .getLaunchConfigurations(configType); candidateConfigs = new ArrayList<ILaunchConfiguration>(configs.length); Module mod = LaunchHelper.getModule(declaration); String moduleName = LaunchHelper.getFullModuleName(mod); if (mod.isDefaultModule()) { moduleName = mod.getNameAsString(); } String topLevelDisplayName = getTopLevelDisplayName(declaration); for (int i = 0; i < configs.length; i++) { ILaunchConfiguration config = configs[i]; if (config.getAttribute(ATTR_TOPLEVEL_NAME, "").equals(topLevelDisplayName) && config.getAttribute(ATTR_PROJECT_NAME, "").equals(projectName) && config.getAttribute(ATTR_MODULE_NAME, "").equals(moduleName)) { candidateConfigs.add(config); } } } catch (CoreException e) { e.printStackTrace(); // TODO : Use a logger } return candidateConfigs; } /** * Choose a pre-defined configuration if there is more than one defined configuration * for the same combination of project, module and runnable * @param configuration list * @return the chosen configuration */ protected ILaunchConfiguration chooseConfiguration(List<ILaunchConfiguration> configList) { IDebugModelPresentation labelProvider = DebugUITools.newDebugModelPresentation(); ElementListSelectionDialog dialog = new ElementListSelectionDialog(EditorUtil.getShell(), labelProvider); dialog.setElements(configList.toArray()); dialog.setTitle("Ceylon Launcher"); dialog.setMessage("Please choose a configuration to start the Ceylon application"); dialog.setMultipleSelection(false); int result = dialog.open(); labelProvider.dispose(); if (result == Window.OK) { return (ILaunchConfiguration) dialog.getFirstResult(); } return null; } /** * Launch from a Navigation selection - right-click, run-as or debug-as */ @Override public void launch(ISelection selection, String mode) { if (! (selection instanceof IStructuredSelection)) { return; } IStructuredSelection structuredSelection = (IStructuredSelection) selection; List<IFile> files = new LinkedList<IFile>(); for (Object object : structuredSelection.toList()) { if (object instanceof IAdaptable) { IResource resource = (IResource) ((IAdaptable)object).getAdapter(IResource.class); if (resource != null) { if (resource instanceof IProject) { final IProject project = (IProject)resource; Module mod = LaunchHelper.getDefaultOrOnlyModule(project, true); if (mod == null) { mod = LaunchHelper.chooseModule(project, true); } if (mod != null) { launchModule(mod, resource, mode); return; // do not look at other parts of the selection } else { return; } } else if (resource instanceof IFolder && LaunchHelper.getModule((IFolder)resource) != null) { //check for module launchModule(LaunchHelper.getModule((IFolder)resource), resource, mode); return; } else { LaunchHelper.addFiles(files, resource); } } } } searchAndLaunch(files, mode); } /** * Launch from the current editor context */ @Override public void launch(IEditorPart editor, String mode) { IFile file = EditorUtil.getFile(editor.getEditorInput()); if (editor instanceof CeylonEditor) { CeylonEditor ce = (CeylonEditor) editor; CeylonParseController cpc = ce.getParseController(); if (cpc!=null) { Tree.CompilationUnit cu = cpc.getLastCompilationUnit(); if (cu!=null) { ISelection selection = ce.getSelectionProvider().getSelection(); if (selection instanceof ITextSelection) { Node node = Nodes.findToplevelStatement(cu, Nodes.findNode(cu, cpc.getTokens(), (ITextSelection) selection)); if (node instanceof Tree.AnyMethod) { Function method = ((Tree.AnyMethod) node).getDeclarationModel(); if (method!=null && method.isShared() && method.isToplevel() && !method.getParameterLists().isEmpty() && method.getParameterLists().get(0).getParameters().isEmpty()) { launch(method, file, mode); return; } } if (node instanceof Tree.AnyClass) { Class clazz = ((Tree.AnyClass) node).getDeclarationModel(); if (clazz!=null && clazz.isShared() && clazz.isToplevel() && !clazz.isAbstract() && clazz.getParameterList()!=null && clazz.getParameterList().getParameters().isEmpty()) { launch(clazz, file, mode); return; } } } } } } searchAndLaunch(Arrays.asList(file), mode); } /** * Launches a dialogue to help choose a declaration and its associated file * @param files - the files to search in * @param mode */ private void searchAndLaunch(List<IFile> files, String mode) { Object[] ret = LaunchHelper.findDeclarationFromFiles(files); if (ret != null && ret[0] != null && ret[1] != null) { launch((Declaration)ret[0], (IFile)ret[1], mode); } } /** * Launches a module after giving an opportunity to select a runnable declaration * @param mod - module * @param resource - associate Eclipse resource * @param mode */ private void launchModule(Module mod, IResource resource, String mode) { Declaration declarationToRun = LaunchHelper.getDefaultRunnableForModule(mod); List<Declaration> decls = new LinkedList<Declaration>(); if (declarationToRun != null) { decls.add(declarationToRun); // top } decls.addAll(LaunchHelper.getDeclarationsForModule( resource.getProject().getName(), LaunchHelper.getFullModuleName(mod))); declarationToRun = LaunchHelper.chooseDeclaration(decls); if (declarationToRun != null) { launch(declarationToRun, resource, mode); } } /** * The actual launch after the declaration has been chosen * @param declarationToRun - the chosen declaration * @param resource - the associated Eclipse resource * @param mode */ public void launch(Declaration declarationToRun, IResource resource, String mode) { ILaunchConfiguration config = findLaunchConfiguration(declarationToRun, resource, getConfigurationType()); if (config == null) { config = createConfiguration(declarationToRun, resource); } if (config != null) { DebugUITools.launch(config, mode); } } @Override public ILaunchConfiguration[] getLaunchConfigurations(IEditorPart editor) { IFile file = EditorUtil.getFile(editor.getEditorInput()); ArrayList<ILaunchConfiguration> list = new ArrayList<ILaunchConfiguration>(); if (editor instanceof CeylonEditor) { CeylonEditor ce = (CeylonEditor) editor; CeylonParseController cpc = ce.getParseController(); if (cpc!=null) { Tree.CompilationUnit cu = cpc.getLastCompilationUnit(); if (cu!=null) { ITextSelection selection = ce.getSelectionFromThread(); Node node = Nodes.findToplevelStatement(cu, Nodes.findNode(cu,cpc.getTokens(),selection)); if (node instanceof Tree.AnyMethod) { Function method = ((Tree.AnyMethod) node).getDeclarationModel(); if (method!=null && method.isShared() && method.isToplevel() && !method.getParameterLists().isEmpty() && method.getParameterLists().get(0).getParameters().isEmpty()) { list.addAll(getLaunchConfigurations(method, file, getConfigurationType())); } } if (node instanceof Tree.AnyClass) { Class clazz = ((Tree.AnyClass) node).getDeclarationModel(); if (clazz!=null && clazz.isShared() && clazz.isToplevel() && !clazz.isAbstract() && clazz.getParameterList()!=null && clazz.getParameterList().getParameters().isEmpty()) { list.addAll(getLaunchConfigurations(clazz, file, getConfigurationType())); } } } } } return list.toArray(new ILaunchConfiguration[0]); } @Override public ILaunchConfiguration[] getLaunchConfigurations(ISelection selection) { // TODO Auto-generated method stub return null; } @Override public IResource getLaunchableResource(ISelection selection) { // TODO Auto-generated method stub return null; } @Override public IResource getLaunchableResource(IEditorPart editor) { return EditorUtil.getFile(editor.getEditorInput()); } }