package com.redhat.ceylon.eclipse.core.launch; import static com.redhat.ceylon.model.typechecker.model.Module.DEFAULT_MODULE_NAME; import static com.redhat.ceylon.model.typechecker.model.Module.LANGUAGE_MODULE_NAME; import static com.redhat.ceylon.eclipse.core.builder.CeylonBuilder.getPackage; import static com.redhat.ceylon.eclipse.core.builder.CeylonBuilder.getProjectDeclaredSourceModules; import static com.redhat.ceylon.eclipse.core.builder.CeylonBuilder.getProjectModules; import static com.redhat.ceylon.eclipse.core.builder.CeylonBuilder.getProjectTypeChecker; 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.ICeylonLaunchConfigurationConstants.CAN_LAUNCH_AS_CEYLON_JAVASCIPT_MODULE; import static com.redhat.ceylon.eclipse.core.launch.ICeylonLaunchConfigurationConstants.CAN_LAUNCH_AS_CEYLON_JAVA_MODULE; import static com.redhat.ceylon.eclipse.core.launch.ICeylonLaunchConfigurationConstants.DEFAULT_RUN_MARKER; import static com.redhat.ceylon.eclipse.util.EditorUtil.getShell; import static java.lang.Character.charCount; import static java.lang.Character.isUpperCase; import static org.eclipse.core.resources.ResourcesPlugin.getWorkspace; import static com.redhat.ceylon.eclipse.java2ceylon.Java2CeylonProxies.*; import java.util.Arrays; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Set; import org.eclipse.core.resources.IContainer; 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.resources.IWorkspace; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.debug.core.ILaunchConfiguration; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.dialogs.IDialogSettings; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.window.Window; import com.redhat.ceylon.compiler.typechecker.TypeChecker; import com.redhat.ceylon.compiler.typechecker.context.PhasedUnit; import com.redhat.ceylon.ide.common.model.CeylonProject; import com.redhat.ceylon.ide.common.vfs.FileVirtualFile; 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.Modules; import com.redhat.ceylon.model.typechecker.model.Package; import com.redhat.ceylon.eclipse.code.open.OpenDeclarationDialog; import com.redhat.ceylon.eclipse.core.builder.CeylonBuilder; import com.redhat.ceylon.eclipse.ui.CeylonPlugin; import com.redhat.ceylon.eclipse.util.EditorUtil; /** * This class is a stateless helper that groups together static utility methods */ public class LaunchHelper { static void addFiles(List<IFile> files, IResource resource) { switch (resource.getType()) { case IResource.FILE: IFile file = (IFile) resource; IPath path = file.getFullPath(); //getProjectRelativePath(); if (path!=null && "ceylon".equals(path.getFileExtension()) ) { files.add(file); } break; case IResource.FOLDER: case IResource.PROJECT: IContainer folder = (IContainer) resource; try { for (IResource child: folder.members()) { addFiles(files, child); } } catch (CoreException e) { e.printStackTrace(); } break; } } static Object[] findDeclarationFromFiles(List<IFile> files) { List<Declaration> topLevelDeclarations = new LinkedList<Declaration>(); List<IFile> correspondingfiles = new LinkedList<IFile>(); for (IFile file : files) { IProject project = file.getProject(); CeylonProject<IProject, IResource, IFolder, IFile> ceylonProject = modelJ2C().ceylonModel().getProject(project); TypeChecker typeChecker = getProjectTypeChecker(project); if (typeChecker != null) { FileVirtualFile<IProject, IResource, IFolder, IFile> virtualFile = vfsJ2C().createVirtualFile(file, ceylonProject.getIdeArtifact()); PhasedUnit phasedUnit = typeChecker.getPhasedUnits() .getPhasedUnit(virtualFile); if (phasedUnit!=null) { List<Declaration> declarations = phasedUnit.getDeclarations(); for (Declaration d : declarations) { if (isRunnable(d)) { topLevelDeclarations.add(d); correspondingfiles.add(file); } } } } } Declaration declarationToRun; IFile fileToRun; if (topLevelDeclarations.isEmpty()) { MessageDialog.openError(getShell(), "Ceylon Launcher", "No runnable function or class.\n(Only shared toplevel functions and classes with no parameters are runnable.)"); return null; } else if (topLevelDeclarations.size()>1) { declarationToRun = chooseDeclaration(topLevelDeclarations); if (declarationToRun!=null) { int index = topLevelDeclarations.indexOf(declarationToRun); fileToRun = correspondingfiles.get(index); } else { fileToRun = null; } } else { declarationToRun = topLevelDeclarations.get(0); fileToRun = correspondingfiles.get(0); } return new Object[] {declarationToRun, fileToRun}; } private static boolean isRunnable(Declaration d) { boolean candidateDeclaration = true; if (!d.isToplevel() || !d.isShared()) { candidateDeclaration = false; } if (d instanceof Function) { Function methodDecl = (Function) d; if (!methodDecl.getParameterLists().isEmpty() && !methodDecl.getParameterLists().get(0) .getParameters().isEmpty()) { candidateDeclaration = false; } } else if (d instanceof Class) { Class classDecl = (Class) d; if (classDecl.isAbstract() || classDecl.getParameterList()==null || !classDecl.getParameterList() .getParameters().isEmpty()) { candidateDeclaration = false; } } else { candidateDeclaration = false; } return candidateDeclaration; } static Module getModule(IProject project, String fullModuleName) { fullModuleName = normalizeFullModuleName(fullModuleName); if (fullModuleName != null) { String[] parts = fullModuleName.split("/"); if (parts != null && parts.length != 2) { return null; } for (Module module: getProjectDeclaredSourceModules(project)) { if (module.getNameAsString().equals(parts[0]) && module.getVersion().equals(parts[1])) { return module; } } if (isDefaultModulePresent(project)) { return getDefaultModule(project); } } return null; } private static String normalizeFullModuleName(String fullModuleName) { if (DEFAULT_MODULE_NAME.equals(fullModuleName)) { return getFullModuleName(getEmptyDefaultModule()); } else { return fullModuleName; } } private static Module getDefaultModule(IProject project) { Module defaultModule = getProjectModules(project) .getDefaultModule(); if (defaultModule == null) { defaultModule = getEmptyDefaultModule(); } return defaultModule; } private static Module getEmptyDefaultModule() { Module defaultModule = new Module(); defaultModule.setName(Arrays.asList(DEFAULT_MODULE_NAME)); defaultModule.setVersion("unversioned"); // defaultModule.setDefault(true); return defaultModule; } static Module getModule(Declaration decl) { Package pack = decl.getUnit().getPackage(); if (pack != null) { Module mod = pack.getModule(); if (mod != null) { return mod; } } return getEmptyDefaultModule(); } static String getModuleFullName(Declaration decl) { Module module = getModule(decl); if (module.isDefaultModule()) { return DEFAULT_MODULE_NAME; } else { return getFullModuleName(module); } } static Set<Module> getModules(IProject project, boolean includeDefault) { Set<Module> modules = new HashSet<Module>(); for(Module module: getProjectDeclaredSourceModules(project)) { if (module.isAvailable() && !module.getNameAsString() .startsWith(LANGUAGE_MODULE_NAME) && !module.isJava()) { if ((module.isDefaultModule() && includeDefault) // TODO : this is *never* true : the default module is not in the requested list || (!module.isDefaultModule() && module.getPackage(module.getNameAsString())!=null)){ modules.add(module); } } } if (modules.isEmpty() || isDefaultModulePresent(project)) { modules.add(getDefaultModule(project)); } return modules; } private static boolean isDefaultModulePresent(IProject project) { Modules modules = getProjectModules(project); if (modules != null) { Module defaultModule = modules.getDefaultModule(); if (defaultModule != null) { List<Declaration> decls = getDeclarationsForModule(project, defaultModule); if (!decls.isEmpty()) { return true; } } } return false; } static boolean isModuleInProject(IProject project, String fullModuleName) { if (fullModuleName.equals(Module.DEFAULT_MODULE_NAME) && isDefaultModulePresent(project)) { return true; } for (Module module: getModules(project, false)) { String name = getFullModuleName(module); if (fullModuleName!=null && fullModuleName.equals(name)) { return true; } } return false; } static String getFullModuleName(Module module) { return module.getNameAsString() + "/" + module.getVersion(); } static List<Declaration> getDeclarationsForModule(IProject project, Module module) { List<Declaration> modDecls = new LinkedList<Declaration>(); if (module != null) { List<Package> pkgs = module.getPackages(); // avoid concurrent exception for (Package pkg : pkgs) { if (pkg.getModule() != null && isPackageInProject(project, pkg)) for (Declaration decl : pkg.getMembers()) { if (isRunnable(decl)) { modDecls.add(decl); } } } } return modDecls; } private static boolean isPackageInProject(IProject project, Package pkg) { TypeChecker typeChecker = getProjectTypeChecker(project); List<PhasedUnit> pus = typeChecker.getPhasedUnits().getPhasedUnits(); for (PhasedUnit phasedUnit : pus) { if (pkg.equals(phasedUnit.getPackage())) { return true; } } return false; } static List<Declaration> getDeclarationsForModule(String projectName, String fullModuleName) { IProject project = getProjectFromName(projectName); Module module = getModule(project, fullModuleName); return getDeclarationsForModule(project, module); } /** * Does not attempt to get all declarations before it returns true * @param project * @param fullModuleName * @param topLevelName * @return boolean if a top-level is contained in a module */ static boolean isModuleContainsTopLevel(IProject project, String fullModuleName, String topLevelName) { if (!isModuleInProject(project, fullModuleName)) { return false; } if (Module.DEFAULT_MODULE_NAME.equals(fullModuleName)) { fullModuleName = getFullModuleName(getDefaultModule(project)); } Module mod = getModule(project, fullModuleName); if (mod == null) { return false; } for (Package pkg : mod.getPackages()) { for (Declaration decl : pkg.getMembers()) { if (getRunnableName(decl).equals(topLevelName)) { return true; } } } return false; } static String getRunnableName(Declaration d) { return d.getQualifiedNameString().replace("::", "."); } static Declaration chooseDeclaration(final List<Declaration> decls) { OpenDeclarationDialog sd = new OpenDeclarationDialog(false, false, EditorUtil.getShell(), "Ceylon Launcher", "&Type part of a name, with wildcard *, or a camel hump pattern:", "&Select a function to run:") { private static final String SETTINGS_ID = CeylonPlugin.PLUGIN_ID + ".selectRunnableDialog"; @Override protected String getFilterListAsString(String preference) { return ""; } @Override public boolean enableDocArea() { return false; } @Override protected IDialogSettings getDialogSettings() { IDialogSettings settings = CeylonPlugin.getInstance().getDialogSettings(); IDialogSettings section = settings.getSection(SETTINGS_ID); if (section == null) { section = settings.addNewSection(SETTINGS_ID); } return section; } @Override protected IDialogSettings getDialogBoundsSettings() { IDialogSettings settings = getDialogSettings(); IDialogSettings section = settings.getSection(DIALOG_BOUNDS_SETTINGS); if (section == null) { section = settings.addNewSection(DIALOG_BOUNDS_SETTINGS); section.put(DIALOG_HEIGHT, 500); section.put(DIALOG_WIDTH, 400); } return section; } @Override protected void fillViewMenu(IMenuManager menuManager) {} @Override protected ItemsFilter createFilter() { return new Filter() { @Override public String getPattern() { String pattern = super.getPattern(); return pattern.isEmpty() ? "**" : pattern; } }; } @Override protected void fillContentProvider( AbstractContentProvider contentProvider, ItemsFilter itemsFilter, IProgressMonitor monitor) throws CoreException { for (int i=0; i<decls.size(); i++) { DeclarationProxy item = new DeclarationProxy(decls.get(i)); contentProvider.add(item, itemsFilter); } } }; if (sd.open() == Window.OK) { return (Declaration) sd.getFirstResult(); } return null; } static Module chooseModule(IProject project, boolean includeDefault) { CeylonModuleSelectionDialog cmsd = new CeylonModuleSelectionDialog(getShell(), getModules(project, true)); if (cmsd.open() == Window.OK) { return (Module)cmsd.getFirstResult(); } return null; } static IProject getProjectFromName(String projectName) { if (projectName != null && projectName.length() > 0) { IWorkspace workspace = getWorkspace(); IStatus status = workspace.validateName(projectName, IResource.PROJECT); if (status.isOK()) { return workspace.getRoot() .getProject(projectName); } } return null; } static String getTopLevelNormalName(String moduleFullName, String displayName) { if (displayName.contains(DEFAULT_RUN_MARKER) && moduleFullName.indexOf('/') != -1) { return moduleFullName.substring(0, moduleFullName.indexOf('/')) + ".run"; } return displayName; } static String getTopLevelDisplayName(Declaration decl) { String topLevelName = getRunnableName(decl); Module module = getModule(decl); if (module!=null && decl.equals(getDefaultRunnableForModule(module))) { topLevelName = "run" + DEFAULT_RUN_MARKER; } return topLevelName; } static Module getDefaultOrOnlyModule(IProject project, boolean includeDefault) { Set<Module> modules = getModules(project, true); //if only one real module or just one default module, just send it back if (modules.size() == 1) { return modules.iterator().next(); } if (modules.size() ==2 && !includeDefault) { Iterator<Module> modIterator = modules.iterator(); while (modIterator.hasNext()) { Module realMod = modIterator.next(); if (!realMod.isDefaultModule()) { return realMod; } } } return null; } static Declaration getDefaultRunnableForModule(Module mod) { Declaration decl = null; if (mod.getRootPackage()!=null) { decl = mod.getRootPackage() .getDirectMember("run", null, false); } return decl; } static Module getModule(IFolder folder) { Package pkg = getPackage(folder); if (pkg != null) { return pkg.getModule(); } return null; } static boolean isBuilderEnabled(IProject project, String property) { if (CAN_LAUNCH_AS_CEYLON_JAVA_MODULE.equals(property)) { return CeylonBuilder.compileToJava(project); } else if (CAN_LAUNCH_AS_CEYLON_JAVASCIPT_MODULE.equals(property)) { return CeylonBuilder.compileToJs(project); } return false; } public static String getTopLevel(ILaunchConfiguration configuration) throws CoreException { String topLevelName = configuration.getAttribute(ATTR_TOPLEVEL_NAME, (String) null); int def = -1; if (topLevelName != null) { def = topLevelName.indexOf(DEFAULT_RUN_MARKER); } if (def != -1) { topLevelName = null; } if (topLevelName == null) { String moduleName = configuration.getAttribute(ATTR_MODULE_NAME, (String) null); if (moduleName != null) { String packageName = moduleName.replaceAll("/.*$", ""); return packageName + ".run"; } } return topLevelName; } public static String getStartLocation(ILaunchConfiguration configuration) throws CoreException { String location; String methodToStopIn = null; String toplevel = getTopLevel(configuration); if (toplevel != null) { int index = toplevel.lastIndexOf("."); if (index >= -1 && index < toplevel.length()-1) { int typeFirstChar = toplevel.codePointAt(index + 1); if (!isUpperCase(typeFirstChar)) { // It's a top-level method methodToStopIn = toplevel.substring(index + charCount(typeFirstChar)); toplevel += "_"; } else { // It's a top-level class methodToStopIn = "<init>"; // constructor } } location = toplevel + '/' + methodToStopIn; } else { location = null; } return location; } }