package com.redhat.ceylon.test.eclipse.plugin.launch; import static com.redhat.ceylon.eclipse.java2ceylon.Java2CeylonProxies.modelJ2C; import static com.redhat.ceylon.eclipse.java2ceylon.Java2CeylonProxies.vfsJ2C; import static com.redhat.ceylon.test.eclipse.plugin.CeylonTestMessages.errorCanNotFindSelectedTest; import static com.redhat.ceylon.test.eclipse.plugin.CeylonTestMessages.errorDialogTitle; import static com.redhat.ceylon.test.eclipse.plugin.CeylonTestPlugin.LAUNCH_CONFIG_ENTRIES_KEY; import static com.redhat.ceylon.test.eclipse.plugin.CeylonTestPlugin.LAUNCH_CONFIG_TYPE; import static com.redhat.ceylon.test.eclipse.plugin.CeylonTestPlugin.LAUNCH_CONFIG_TYPE_JS; import static com.redhat.ceylon.test.eclipse.plugin.launch.CeylonTestLaunchConfigEntry.Type.CLASS; import static com.redhat.ceylon.test.eclipse.plugin.launch.CeylonTestLaunchConfigEntry.Type.CLASS_LOCAL; import static com.redhat.ceylon.test.eclipse.plugin.launch.CeylonTestLaunchConfigEntry.Type.METHOD; import static com.redhat.ceylon.test.eclipse.plugin.launch.CeylonTestLaunchConfigEntry.Type.METHOD_LOCAL; import static com.redhat.ceylon.test.eclipse.plugin.launch.CeylonTestLaunchConfigEntry.Type.MODULE; import static com.redhat.ceylon.test.eclipse.plugin.launch.CeylonTestLaunchConfigEntry.Type.PACKAGE; import static com.redhat.ceylon.test.eclipse.plugin.launch.CeylonTestLaunchConfigEntry.Type.PROJECT; import static com.redhat.ceylon.test.eclipse.plugin.util.CeylonTestUtil.getModule; import static com.redhat.ceylon.test.eclipse.plugin.util.CeylonTestUtil.getShell; import static com.redhat.ceylon.test.eclipse.plugin.util.CeylonTestUtil.isCeylonFile; import static com.redhat.ceylon.test.eclipse.plugin.util.CeylonTestUtil.isCeylonProject; import static com.redhat.ceylon.test.eclipse.plugin.util.CeylonTestUtil.isTestable; import static org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants.ATTR_PROJECT_NAME; import java.util.ArrayList; import java.util.List; import java.util.Objects; 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.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.ui.DebugUITools; import org.eclipse.debug.ui.ILaunchShortcut; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IPackageFragment; import org.eclipse.jdt.core.IPackageFragmentRoot; import org.eclipse.jdt.core.JavaModelException; 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.common.Backend; import com.redhat.ceylon.compiler.typechecker.TypeChecker; import com.redhat.ceylon.compiler.typechecker.context.PhasedUnit; 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.core.builder.CeylonBuilder; import com.redhat.ceylon.eclipse.util.EditorUtil; import com.redhat.ceylon.ide.common.util.FindContainerVisitor; import com.redhat.ceylon.eclipse.util.Nodes; import com.redhat.ceylon.ide.common.model.CeylonProject; 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; import com.redhat.ceylon.model.typechecker.model.Package; import com.redhat.ceylon.test.eclipse.plugin.CeylonTestMessages; import com.redhat.ceylon.test.eclipse.plugin.CeylonTestPlugin; import com.redhat.ceylon.test.eclipse.plugin.launch.CeylonTestLaunchConfigEntry.Type; import com.redhat.ceylon.test.eclipse.plugin.util.CeylonTestUtil; import com.redhat.ceylon.test.eclipse.plugin.util.MethodWithContainer; public class CeylonTestLaunchShortcut implements ILaunchShortcut { private final String configTypeId; public CeylonTestLaunchShortcut() { this(LAUNCH_CONFIG_TYPE); } public CeylonTestLaunchShortcut(String configTypeId) { this.configTypeId = configTypeId; } @Override public void launch(ISelection selection, String mode) { if (selection instanceof IStructuredSelection) { IStructuredSelection structuredSelection = (IStructuredSelection) selection; List<String> names = new ArrayList<String>(); List<CeylonTestLaunchConfigEntry> entries = new ArrayList<CeylonTestLaunchConfigEntry>(); Object[] selectedElements = structuredSelection.toArray(); for (Object selectedElement : selectedElements) { processSelectedElement(selectedElement, mode, names, entries); } launch(getLaunchName(names), entries, mode, configTypeId); } } @Override public void launch(IEditorPart editor, String mode) { List<String> names = new ArrayList<String>(); List<CeylonTestLaunchConfigEntry> entries = new ArrayList<CeylonTestLaunchConfigEntry>(); if (editor instanceof CeylonEditor) { processCeylonEditorSelection(names, entries, (CeylonEditor) editor); } if (entries.isEmpty()) { IFile file = EditorUtil.getFile(editor.getEditorInput()); processFile(names, entries, file); } launch(getLaunchName(names), entries, mode, configTypeId); } public static void relaunch(ILaunch launch, String launchName, String launchMode, List<String> qualifiedNames) throws CoreException { String configTypeId = launch.getLaunchConfiguration().getType().getIdentifier(); List<CeylonTestLaunchConfigEntry> entries = new ArrayList<CeylonTestLaunchConfigEntry>(); IProject project = CeylonTestUtil.getProject(launch); if( project != null ) { for(String qualifiedName : qualifiedNames) { Object result = CeylonTestUtil.getPackageOrDeclaration(project, qualifiedName); if( result instanceof Package ) { Package pkg = (Package) result; entries.add(CeylonTestLaunchConfigEntry.build(project, Type.PACKAGE, pkg.getNameAsString())); } else if (result instanceof Class) { Class clazz = (Class) result; entries.add(CeylonTestLaunchConfigEntry.build(project, clazz.isShared() ? CLASS : CLASS_LOCAL, clazz.getQualifiedNameString())); } else if (result instanceof Function) { Function method = (Function) result; entries.add(CeylonTestLaunchConfigEntry.build(project, method.isShared() ? METHOD : METHOD_LOCAL, method.getQualifiedNameString())); } else if( result instanceof MethodWithContainer ) { MethodWithContainer methodWithContainer = (MethodWithContainer) result; entries.add(CeylonTestLaunchConfigEntry.build(project, methodWithContainer.getMethod().isShared() ? METHOD : METHOD_LOCAL, methodWithContainer.getContainer().getQualifiedNameString() + "." + methodWithContainer.getMethod().getName())); } } } if (entries.isEmpty()) { MessageDialog.openInformation(getShell(), errorDialogTitle, errorCanNotFindSelectedTest); } else { CeylonTestLaunchShortcut.launch(launchName, entries, launchMode, configTypeId); } } public static void launch(String name, List<CeylonTestLaunchConfigEntry> entries, String mode, String configTypeId) { if (entries.isEmpty()) { MessageDialog.openInformation(getShell(), CeylonTestMessages.launchDialogInfoTitle, CeylonTestMessages.launchNoTestsFound); return; } try { ILaunchConfigurationType configType = getLaunchManager().getLaunchConfigurationType(configTypeId); ILaunchConfiguration config = findExistingLaunchConfig(configType, entries); if (config == null) { config = createLaunchConfig(name, entries, configType); } DebugUITools.launch(config, mode); } catch (CoreException e) { CeylonTestPlugin.logError("", e); } } private void processSelectedElement(Object selectedElement, String mode, List<String> names, List<CeylonTestLaunchConfigEntry> entries) { if( selectedElement instanceof IProject ) { processProject(names, entries, (IProject) selectedElement); } else if (selectedElement instanceof IJavaProject) { processProject(names, entries, ((IJavaProject) selectedElement).getProject()); } else if (selectedElement instanceof IPackageFragmentRoot) { processPackageFragmentRoot(names, entries, (IPackageFragmentRoot) selectedElement); } else if (selectedElement instanceof IPackageFragment) { processPackage(names, entries, (IPackageFragment) selectedElement); } else if (selectedElement instanceof IFile) { processFile(names, entries, (IFile) selectedElement); } } private void processProject(List<String> names, List<CeylonTestLaunchConfigEntry> entries, IProject project) { if (isCeylonProject(project)) { names.add(project.getName()); entries.add(CeylonTestLaunchConfigEntry.build(project, PROJECT, null)); } } private void processPackageFragmentRoot(List<String> names, List<CeylonTestLaunchConfigEntry> entries, IPackageFragmentRoot packageFragmentRoot) { try { IProject project = packageFragmentRoot.getJavaProject().getProject(); if (isCeylonProject(project)) { names.add(packageFragmentRoot.getElementName()); IJavaElement[] children = packageFragmentRoot.getChildren(); for (IJavaElement child : children) { if (child instanceof IPackageFragment) { IPackageFragment packageFragment = (IPackageFragment) child; Module module = getModule(project, packageFragment.getElementName()); if (CeylonTestUtil.containsCeylonTestImport(module) && CeylonTestUtil.checkNativeBackend(module, configTypeId)) { entries.add(CeylonTestLaunchConfigEntry.build(project, MODULE, packageFragment.getElementName())); } } } } } catch (JavaModelException e) { throw new RuntimeException(e); } } private void processPackage(List<String> names, List<CeylonTestLaunchConfigEntry> entries, IPackageFragment packageFragment) { IProject project = packageFragment.getJavaProject().getProject(); if (isCeylonProject(project)) { names.add(packageFragment.getElementName()); Module module = getModule(project, packageFragment.getElementName()); if (module != null) { entries.add(CeylonTestLaunchConfigEntry.build(project, MODULE, packageFragment.getElementName())); } else { entries.add(CeylonTestLaunchConfigEntry.build(project, PACKAGE, packageFragment.getElementName())); } } } private void processFile(List<String> names, List<CeylonTestLaunchConfigEntry> entries, IFile file) { if (!isCeylonFile(file)) { return; } IProject project = file.getProject(); TypeChecker typeChecker = CeylonBuilder.getProjectTypeChecker(project); if (project == null || typeChecker == null) { return; } String fileName = file.getName().substring(0, file.getName().length() - file.getFileExtension().length() - 1); names.add(fileName); CeylonProject<IProject, IResource, IFolder, IFile> ceylonProject = modelJ2C().ceylonModel().getProject(project); PhasedUnit phasedUnit = typeChecker.getPhasedUnits().getPhasedUnit(vfsJ2C().createVirtualFile(file, ceylonProject.getIdeArtifact())); if (phasedUnit != null) { List<Declaration> declarations = phasedUnit.getDeclarations(); for (Declaration d : declarations) { if (d.isNative()) { if (Objects.equals(configTypeId, LAUNCH_CONFIG_TYPE) && !d.getNativeBackends().supports(Backend.Java.asSet())) { continue; } if (Objects.equals(configTypeId, LAUNCH_CONFIG_TYPE_JS) && !d.getNativeBackends().supports(Backend.JavaScript.asSet())) { continue; } } if (d.isToplevel()) { if (d instanceof Class) { Class clazz = (Class) d; if (isTestable(clazz)) { entries.add(CeylonTestLaunchConfigEntry.build(project, clazz.isShared() ? CLASS : CLASS_LOCAL, clazz.getQualifiedNameString())); } } else if (d instanceof Function) { Function method = (Function) d; if (isTestable(method)) { entries.add(CeylonTestLaunchConfigEntry.build(project, method.isShared() ? METHOD : METHOD_LOCAL, method.getQualifiedNameString())); } } } } } } private void processCeylonEditorSelection(List<String> names, List<CeylonTestLaunchConfigEntry> entries, CeylonEditor ce) { CeylonParseController cpc = ce.getParseController(); if (cpc == null) { return; } IProject project = cpc.getProject(); ISelection selection = ce.getSelectionProvider().getSelection(); Tree.CompilationUnit cu = cpc.getLastCompilationUnit(); if (project == null || selection == null || !(selection instanceof ITextSelection) || cu == null) { return; } Node node = Nodes.findNode(cu, cpc.getTokens(), (ITextSelection) selection); FindContainerVisitor fcv = new FindContainerVisitor(node); fcv.visit(cu); node = fcv.getDeclaration(); if (node instanceof Tree.AnyMethod) { Function method = ((Tree.AnyMethod) node).getDeclarationModel(); if ((method.getContainer() instanceof Class && isTestable(new MethodWithContainer((Class)method.getContainer(), method))) || (method.getContainer() instanceof Package && isTestable(method)) ) { if (method.isMember()) { names.add(((Declaration) method.getContainer()).getName() + "." + method.getName()); } else { names.add(method.getName()); } entries.add(CeylonTestLaunchConfigEntry.build(project, method.isShared() ? METHOD : METHOD_LOCAL, method.getQualifiedNameString())); } } if (node instanceof Tree.AnyClass) { Class clazz = ((Tree.AnyClass) node).getDeclarationModel(); if (isTestable(clazz)) { names.add(clazz.getName()); entries.add(CeylonTestLaunchConfigEntry.build(project, clazz.isShared() ? CLASS : CLASS_LOCAL, clazz.getQualifiedNameString())); } } if( node instanceof Tree.ObjectDefinition ) { Class clazz = ((Tree.ObjectDefinition) node).getAnonymousClass(); if (isTestable(clazz)) { names.add(clazz.getName()); entries.add(CeylonTestLaunchConfigEntry.build(project, clazz.isShared() ? CLASS : CLASS_LOCAL, clazz.getQualifiedNameString())); } } } private static ILaunchConfiguration createLaunchConfig(String name, List<CeylonTestLaunchConfigEntry> entries, ILaunchConfigurationType configType) throws CoreException { ILaunchConfigurationWorkingCopy configWorkingCopy = configType.newInstance(null, getLaunchManager() .generateLaunchConfigurationName(name)); configWorkingCopy.setAttribute(ATTR_PROJECT_NAME, entries.get(0).getProjectName()); configWorkingCopy.setAttribute(LAUNCH_CONFIG_ENTRIES_KEY, CeylonTestLaunchConfigEntry.buildLaunchConfigAttributes(entries)); return configWorkingCopy.doSave(); } private static ILaunchConfiguration findExistingLaunchConfig(ILaunchConfigurationType configType, List<CeylonTestLaunchConfigEntry> entries) throws CoreException { List<String> attributes = CeylonTestLaunchConfigEntry.buildLaunchConfigAttributes(entries); List<ILaunchConfiguration> candidateConfigs = new ArrayList<ILaunchConfiguration>(); for (ILaunchConfiguration candidateConfig : getLaunchManager().getLaunchConfigurations(configType)) { List<String> candidateAttributes = candidateConfig.getAttribute(LAUNCH_CONFIG_ENTRIES_KEY, new ArrayList<String>()); if (candidateAttributes.equals(attributes)) { candidateConfigs.add(candidateConfig); } } if (candidateConfigs.size() == 0) { return null; } else if (candidateConfigs.size() == 1) { return candidateConfigs.get(0); } else { return chooseExistingLaunchConfig(candidateConfigs); } } private static ILaunchConfiguration chooseExistingLaunchConfig(List<ILaunchConfiguration> configList) { ElementListSelectionDialog dialog = new ElementListSelectionDialog(getShell(), DebugUITools.newDebugModelPresentation()); dialog.setTitle(CeylonTestMessages.launchSelectLaunchConfigTitle); dialog.setMessage(CeylonTestMessages.launchSelectLaunchConfigMessage); dialog.setElements(configList.toArray()); dialog.setMultipleSelection(false); if (dialog.open() == Window.OK) { return (ILaunchConfiguration) dialog.getFirstResult(); } else { return null; } } private static ILaunchManager getLaunchManager() { return DebugPlugin.getDefault().getLaunchManager(); } private String getLaunchName(List<String> names) { StringBuilder nameBuilder = new StringBuilder(); boolean first = true; for (String name : names) { if (first) { first = false; } else { nameBuilder.append(", "); } nameBuilder.append(name); } return nameBuilder.toString()+ " \u2192 " + launchType(); } String launchType() { return "Java"; } }