/**
* 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.ui.wizards.files;
import java.util.List;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
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.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.DirectoryDialog;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Text;
import org.python.pydev.core.log.Log;
import org.python.pydev.editor.codecompletion.revisited.PythonPathHelper;
import org.python.pydev.shared_core.structure.LinkedListWarningOnSlowOperations;
import org.python.pydev.utils.PyFileListing;
public class PythonExistingSourceGroup {
private Text singleSelectionText;
private Button singleBrowseButton;
private IPath linkTarget;
protected String errorMessage;
protected String warningMessage;
public String getErrorMessage() {
return errorMessage;
}
public String getWarningMessage() {
return warningMessage;
}
protected void clearAllProblems() {
errorMessage = null;
warningMessage = null;
}
/**
* The source paths that are already referenced by the project.
*/
protected List<IPath> projectLinkTargets = new LinkedListWarningOnSlowOperations<IPath>();
protected IProject iProject;
public PythonExistingSourceGroup(Composite parent, IProject project, ModifyListener sourceChangeListener) {
createContents(parent, sourceChangeListener);
setActiveProject(project);
}
/**
* Use this constructor only when extending with {@link PythonExistingSourceListGroup}.
*/
protected PythonExistingSourceGroup() {
}
/**
* Tell this group what the active project is. Doing so will update its list of linked source paths
* that are already included in the project, which is necessary for proper conflict-checking.
* @param project
*/
public void setActiveProject(IProject project) {
if (iProject == project) {
return;
}
iProject = project;
projectLinkTargets.clear();
if (project != null) {
try {
IResource[] members = project.members();
for (IResource member : members) {
if (member.isLinked()) {
projectLinkTargets.add(member.getLocation());
}
}
} catch (CoreException e) {
Log.log(e);
}
conflictCheck();
}
}
protected void conflictCheck() {
clearAllProblems();
selectLinkTarget(Path.fromOSString(singleSelectionText.getText()));
}
private void createContents(final Composite parent, ModifyListener sourceChangeListener) {
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("Location of existing source");
singleSelectionText = new Text(group, SWT.SINGLE | SWT.BORDER);
gd = new GridData(GridData.FILL_HORIZONTAL);
singleSelectionText.setLayoutData(gd);
singleSelectionText.setFont(font);
singleSelectionText.addModifyListener(new ModifyListener() {
@Override
public void modifyText(ModifyEvent evt) {
clearAllProblems();
selectLinkTarget(Path.fromOSString(singleSelectionText.getText()));
}
});
singleBrowseButton = new Button(group, SWT.PUSH);
singleBrowseButton.setText("Browse...");
singleBrowseButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
DirectoryDialog dialog = new DirectoryDialog(parent.getShell());
String selection = dialog.open();
if (selection != null) {
singleSelectionText.setText(selection);
}
}
});
singleSelectionText.addModifyListener(sourceChangeListener);
}
/**
* Add the selected folder path to the list of all chosen paths. While doing so, check for conflicts.
* Issue a warning for the following selections:
* -folders that are subdirectories of other chosen folders, or contain other chosen folders
* -folders that have no .py files in them, or in one of their subdirectories
*
* Issue an error if the selection contains the destination of the link to be created. Don't add
* the selection to the list of source paths in case of an error.
*/
protected void selectLinkTarget(IPath linkPath) {
if (validateLinkPath(linkPath)) {
linkTarget = linkPath;
} else {
linkTarget = null;
}
}
protected boolean validateLinkPath(IPath linkPath) {
if (!linkPath.toFile().exists()) {
String segment = linkPath.lastSegment();
if (segment == null) {
errorMessage = "External source location must not be null.";
} else {
errorMessage = "External source location '" + segment + "' does not exist.";
}
return false;
}
IPath rootPath = (iProject == null ? ResourcesPlugin.getWorkspace().getRoot() : iProject).getLocation();
if (linkPath.isPrefixOf(rootPath) || (iProject != null && rootPath.isPrefixOf(linkPath))) {
errorMessage = "External source location '" + linkPath.lastSegment()
+ "' overlaps with the project directory.";
return false;
}
for (IPath otherPath : projectLinkTargets) {
if (linkPath.isPrefixOf(otherPath) || otherPath.isPrefixOf(linkPath)) {
warningMessage = "Location '" + linkPath.lastSegment()
+ "' overlaps with the project resource '"
+ otherPath.lastSegment()
+ "'. This can cause unexpected side-effects.";
break;
}
}
PyFileListing pyFileListing = PythonPathHelper.getModulesBelow(linkPath.toFile(), null);
if (pyFileListing == null || pyFileListing.getFoundPyFileInfos().size() == 0) {
warningMessage = "Folder '" + linkPath.lastSegment()
+ "' does not contain any Python files.";
}
return true;
}
/**
* Return the selected source.
* @return
*/
public IPath getLinkTarget() {
return linkTarget;
}
}