/**
* Copyright (c) 2005-2013 by Appcelerator, Inc. All Rights Reserved.
* Licensed under the terms of the Eclipse Public License (EPL).
* Please see the license.txt included with this distribution for details.
* Any modifications to this file must keep this entire header intact.
*/
package org.python.pydev.debug.ui.blocks;
import java.io.File;
import java.util.ArrayList;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
import org.eclipse.debug.ui.AbstractLaunchConfigurationTab;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.Widget;
import org.python.pydev.core.IPythonNature;
import org.python.pydev.core.docutils.StringSubstitution;
import org.python.pydev.core.log.Log;
import org.python.pydev.debug.core.Constants;
import org.python.pydev.debug.ui.launching.FileOrResource;
import org.python.pydev.debug.ui.launching.LaunchConfigurationCreator;
import org.python.pydev.editorinput.PySourceLocatorBase;
import org.python.pydev.plugin.nature.PythonNature;
import org.python.pydev.shared_core.string.StringUtils;
import org.python.pydev.ui.dialogs.PythonModulePickerDialog;
/**
* A control for selecting a python module.
*/
public class MainModuleBlock extends AbstractLaunchConfigurationTab {
private Text fMainModuleText;
private Button fMainModuleBrowseButton;
private String fProjectName;
private ModifyListener fProjectModifyListener;
private boolean fUnitTesting;
/* (non-Javadoc)
* @see org.eclipse.debug.ui.ILaunchConfigurationTab#createControl(org.eclipse.swt.widgets.Composite)
*/
@Override
public void createControl(Composite parent) {
Font font = parent.getFont();
Group group = new Group(parent, SWT.NONE);
setControl(group);
GridLayout topLayout = new GridLayout();
topLayout.numColumns = 2;
group.setLayout(topLayout);
GridData gd = new GridData(GridData.FILL_HORIZONTAL);
group.setLayoutData(gd);
group.setFont(font);
group.setText("Main Module");
fMainModuleText = new Text(group, SWT.SINGLE | SWT.BORDER);
gd = new GridData(GridData.FILL_HORIZONTAL);
fMainModuleText.setLayoutData(gd);
fMainModuleText.setFont(font);
fMainModuleText.addModifyListener(new ModifyListener() {
@Override
public void modifyText(ModifyEvent evt) {
updateLaunchConfigurationDialog();
}
});
final Composite lParent = parent;
fMainModuleBrowseButton = createPushButton(group, "Browse...", null);
// On button click, this displays the python module picker dialog.
fMainModuleBrowseButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
IWorkspace workspace = ResourcesPlugin.getWorkspace();
IResource[] currentResources = getMainModuleResources();
IResource resource = workspace.getRoot().findMember(fProjectName);
if (resource instanceof IProject) {
IProject project = (IProject) resource;
String title, message;
if (!fUnitTesting) {
title = "Main Module";
message = "Choose Python module which starts execution";
}
else
{
title = "Main Modules";
message = "Choose Python module(s) and/or package(s) to test";
}
PythonModulePickerDialog dialog = new PythonModulePickerDialog(lParent.getShell(), title,
message, project, fUnitTesting);
// Fixed request 1407469: main module browse button forgets path
if (currentResources != null) {
dialog.setInitialSelections(currentResources);
}
int result = dialog.open();
if (result == PythonModulePickerDialog.OK) {
Object results[] = dialog.getResult();
if ((results != null) && (results.length > 0)) {
ArrayList<IResource> r_results = new ArrayList<IResource>();
for (int i = 0; i < results.length; i++) {
if (results[i] instanceof IResource) {
if (results[i] instanceof IFile) {
r_results.add((IFile) results[i]);
}
else {
r_results.add((IResource) results[i]);
}
}
}
fMainModuleText.setText(LaunchConfigurationCreator.getDefaultLocation(
FileOrResource.createArray(r_results.toArray(new IResource[r_results.size()])),
true));
}
}
}
}
});
// Create a ModifyListener, used to listen for project modifications in the ProjectBlock.
// This assumes that the Project is in a Text control...
fProjectModifyListener = new ModifyListener() {
@Override
public void modifyText(ModifyEvent e) {
Widget widget = e.widget;
if (widget instanceof Text) {
Text text = (Text) widget;
fProjectName = text.getText();
IWorkspace workspace = ResourcesPlugin.getWorkspace();
IResource resource = workspace.getRoot().findMember(fProjectName);
boolean enabled = false;
if ((resource != null) && (resource instanceof IProject)) {
IProject project = (IProject) resource;
PythonNature nature = PythonNature.getPythonNature(project);
enabled = (nature != null);
}
fMainModuleBrowseButton.setEnabled(enabled);
}
}
};
}
/*
* (non-Javadoc)
*
* @see org.eclipse.debug.ui.ILaunchConfigurationTab#getName()
*/
@Override
public String getName() {
return "Main module";
}
/*
* (non-Javadoc)
*
* @see org.eclipse.debug.ui.ILaunchConfigurationTab#initializeFrom(org.eclipse.debug.core.ILaunchConfiguration)
*/
@Override
public void initializeFrom(ILaunchConfiguration configuration) {
// Initialize the location field
String location = "";
try {
location = configuration.getAttribute(Constants.ATTR_LOCATION, "");
} catch (CoreException e) {
}
fMainModuleText.setText(location);
// Obtain a copy of the project name (not displayed)
String projectName = "";
try {
projectName = configuration.getAttribute(Constants.ATTR_PROJECT, "");
} catch (CoreException e) {
}
fProjectName = projectName;
try {
String identifier = configuration.getType().getIdentifier(); //configuration.getType().getIdentifier();
fUnitTesting = (identifier.equals(Constants.ID_PYTHON_UNITTEST_LAUNCH_CONFIGURATION_TYPE)
|| identifier.equals(Constants.ID_JYTHON_UNITTEST_LAUNCH_CONFIGURATION_TYPE)
|| identifier.equals(Constants.ID_IRONPYTHON_UNITTEST_LAUNCH_CONFIGURATION_TYPE)
|| identifier.equals(Constants.ID_PYTHON_COVERAGE_LAUNCH_CONFIGURATION_TYPE));
} catch (CoreException e) {
setErrorMessage("Unable to resolve location");
}
}
/*
* (non-Javadoc)
* @see org.eclipse.debug.ui.ILaunchConfigurationTab#performApply(org.eclipse.debug.core.ILaunchConfigurationWorkingCopy)
*/
@Override
public void performApply(ILaunchConfigurationWorkingCopy configuration) {
String value = fMainModuleText.getText().trim();
setAttribute(configuration, Constants.ATTR_LOCATION, value);
configuration.setMappedResources(getMainModuleResources());
}
/*
* (non-Javadoc)
* @see org.eclipse.debug.ui.ILaunchConfigurationTab#setDefaults(org.eclipse.debug.core.ILaunchConfigurationWorkingCopy)
*/
@Override
public void setDefaults(ILaunchConfigurationWorkingCopy configuration) {
//no defaults to set
}
/**
* Obtains an IFile that targets the current main module.
*
* This is used for initializing the module selection dialog.
*
* @return The main module file.
*/
private IResource[] getMainModuleResources() {
String path = fMainModuleText.getText();
ArrayList<IResource> resourceList = new ArrayList<IResource>();
if (path.length() > 0) {
IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
IPath projectPath = new Path(null, fProjectName).makeAbsolute();
if (projectPath.segmentCount() != 1) {
return null;
}
IResource resource = root.getProject(fProjectName);
IProject project = null;
if (resource != null) {
project = resource.getProject();
}
StringSubstitution stringSubstitution = getStringSubstitution(root);
if (stringSubstitution != null) {
try {
//may have multiple files selected for the run for unittest and code-coverage
for (String loc : StringUtils.splitAndRemoveEmptyTrimmed(path, '|')) {
String onepath = stringSubstitution.performStringSubstitution(loc, false);
IFile f = new PySourceLocatorBase().getFileForLocation(Path.fromOSString(onepath), project);
if (f != null) {
resourceList.add(f);
continue;
}
IContainer container = new PySourceLocatorBase().getContainerForLocation(
Path.fromOSString(onepath),
project);
if (container != null) {
resourceList.add(container);
}
}
} catch (CoreException e) {
Log.log(e);
}
}
}
if (resourceList.isEmpty()) {
return null;
}
return resourceList.toArray(new IResource[resourceList.size()]);
}
/**
* @param root the workspace root.
* @return an object capable on making string substitutions based on variables in the project and in the workspace.
*/
public StringSubstitution getStringSubstitution(IWorkspaceRoot root) {
IPath projectPath = new Path(null, fProjectName).makeAbsolute();
if (projectPath.segmentCount() != 1) {
// Path for project must have (only) one segment.
return null;
}
IProject resource = root.getProject(fProjectName);
IPythonNature nature = null;
if (resource != null) {
nature = PythonNature.getPythonNature(resource);
}
StringSubstitution stringSubstitution = new StringSubstitution(nature);
return stringSubstitution;
}
/**
* Sets attributes in the working copy
*
* @param configuration The configuration to set the attribute in
* @param name Name of the attribute to set
* @param value Value to set
*/
private void setAttribute(ILaunchConfigurationWorkingCopy configuration, String name, String value) {
if (value == null || value.length() == 0) {
configuration.setAttribute(name, (String) null);
} else {
configuration.setAttribute(name, value);
}
}
/*
* (non-Javadoc)
* @see org.eclipse.debug.ui.AbstractLaunchConfigurationTab#isValid(org.eclipse.debug.core.ILaunchConfiguration)
*/
@Override
public boolean isValid(ILaunchConfiguration launchConfig) {
boolean result = super.isValid(launchConfig);
if (result) {
setMessage(null);
setErrorMessage(null);
IPath projectPath = new Path(null, fProjectName).makeAbsolute();
if (projectPath.segmentCount() != 1) {
String message = "Path for project must have (only) one segment."; //$NON-NLS-1$
setErrorMessage(message);
return false;
}
IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
StringSubstitution stringSubstitution = getStringSubstitution(root);
if (stringSubstitution == null) {
String message = "Unable to get StringSubstitution (shouldn't happen)."; //$NON-NLS-1$
setErrorMessage(message);
return false;
}
String location = fMainModuleText.getText();
try {
if (fUnitTesting) {
//may have multiple files selected for the run for unitest and code-coverage
for (String loc : StringUtils.splitAndRemoveEmptyTrimmed(location, '|')) {
String expandedLocation = stringSubstitution.performStringSubstitution(loc);
File file = new File(expandedLocation);
if (!file.exists()) {
setErrorMessage(StringUtils.format(
"The file \"%s\" does not exist.", file));
result = false;
break;
}
}
} else {
String expandedLocation = stringSubstitution.performStringSubstitution(location);
File file = new File(expandedLocation);
if (!file.exists()) {
setErrorMessage(StringUtils.format(
"The file \"%s\" does not exist.", file));
result = false;
} else if (!file.isFile()) {
File mainModule = new File(expandedLocation + File.separator + "__main__.py");
if (!mainModule.isFile()) {
setErrorMessage(StringUtils.format(
"The file \"%s\" does not actually map to a file.", file));
result = false;
}
}
}
} catch (CoreException e) {
setErrorMessage("Unable to resolve location");
result = false;
}
}
return result;
}
/**
* Obtain a listener, used to detect changes of the currently selected project
* This updates the browse button, and allos the appropriate selection of the main module.
*
* @return a ModifyListener that updates the block controls.
*/
public ModifyListener getProjectModifyListener() {
return fProjectModifyListener;
}
}