📄 pythonbasemodelprovider.java
字号:
/*
* Created on Oct 11, 2006
* @author Fabio
*/
package org.python.pydev.navigator;
import java.io.File;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
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.IResourceChangeEvent;
import org.eclipse.core.resources.IResourceChangeListener;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.jface.viewers.AbstractTreeViewer;
import org.eclipse.jface.viewers.StructuredViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.widgets.Control;
import org.eclipse.ui.IWorkingSet;
import org.eclipse.ui.model.BaseWorkbenchContentProvider;
import org.python.pydev.core.ICodeCompletionASTManager;
import org.python.pydev.core.IModule;
import org.python.pydev.core.IModulesManager;
import org.python.pydev.core.ModulesKey;
import org.python.pydev.editor.codecompletion.revisited.ProjectModulesManager;
import org.python.pydev.editor.codecompletion.revisited.PythonPathHelper;
import org.python.pydev.editor.codecompletion.revisited.modules.SourceModule;
import org.python.pydev.navigator.elements.IWrappedResource;
import org.python.pydev.navigator.elements.PythonFile;
import org.python.pydev.navigator.elements.PythonFolder;
import org.python.pydev.navigator.elements.PythonNode;
import org.python.pydev.navigator.elements.PythonProjectSourceFolder;
import org.python.pydev.navigator.elements.PythonResource;
import org.python.pydev.navigator.elements.PythonSourceFolder;
import org.python.pydev.outline.ParsedItem;
import org.python.pydev.parser.visitors.scope.ASTEntryWithChildren;
import org.python.pydev.parser.visitors.scope.OutlineCreatorVisitor;
import org.python.pydev.plugin.PydevPlugin;
import org.python.pydev.plugin.nature.IPythonNatureListener;
import org.python.pydev.plugin.nature.PythonNature;
import org.python.pydev.plugin.nature.PythonNatureListenersManager;
/**
* A good part of the refresh for the model was gotten from org.eclipse.ui.model.WorkbenchContentProvider
* (mostly just changed the way to get content changes in python files)
*
* There are other important notifications that we need to learn about.
* Namely:
* - When a source folder is created
* - When the way to see it changes (flat or not)
*
* @author Fabio
*/
public class PythonBaseModelProvider extends BaseWorkbenchContentProvider implements IResourceChangeListener, IPythonNatureListener {
/**
* Object representing an empty array.
*/
private static final Object[] EMPTY = new Object[0];
/**
* These are the source folders that can be found in this file provider. The way we
* see things in this provider, the python model starts only after some source folder
* is found.
*/
private Map<IProject, Set<PythonSourceFolder>> projectToSourceFolders = new HashMap<IProject, Set<PythonSourceFolder>>();
/**
* This is the viewer that we're using to see the contents of this file provider.
*/
protected Viewer viewer;
/**
* Constructor... registers itself as a python nature listener
*/
public PythonBaseModelProvider(){
PythonNatureListenersManager.addPythonNatureListener(this);
}
public static final boolean DEBUG = false;
/**
* Notification received when the pythonpath has been changed or rebuilt.
*/
public void notifyPythonPathRebuilt(IProject project, List<String> projectPythonpath) {
internalDoNotifyPythonPathRebuilt(project, projectPythonpath);
}
/**
* This is the actual implementation of the rebuild.
*
* @return the element that should be refreshed.
*/
public IResource internalDoNotifyPythonPathRebuilt(IProject project, List<String> projectPythonpath) {
IResource refreshObject = project;
if(DEBUG){
System.out.println("\n\nRebuilding pythonpath: "+project+" - "+projectPythonpath);
}
HashSet<Path> projectPythonpathSet = new HashSet<Path>();
for (String string : projectPythonpath) {
Path newPath = new Path(string);
if(project.getLocation().equals(newPath)){
refreshObject = project.getParent();
}
projectPythonpathSet.add(newPath);
}
Map<IProject, Set<PythonSourceFolder>> p = projectToSourceFolders;
if(p != null){
Set<PythonSourceFolder> existingSourceFolders = p.get(project);
//iterate in a copy
for (PythonSourceFolder pythonSourceFolder : new HashSet<PythonSourceFolder>(existingSourceFolders)) {
IPath fullPath = pythonSourceFolder.container.getLocation();
if(!projectPythonpathSet.contains(fullPath)){
if(pythonSourceFolder instanceof PythonProjectSourceFolder){
refreshObject = project.getParent();
}
existingSourceFolders.remove(pythonSourceFolder);//it's not a valid source folder anymore...
if(DEBUG){
System.out.println("Removing:"+pythonSourceFolder+" - "+fullPath);
}
}
}
}
Runnable refreshRunnable = getRefreshRunnable(refreshObject);
final Collection<Runnable> runnables = new ArrayList<Runnable>();
runnables.add(refreshRunnable);
processRunnables(runnables);
return refreshObject;
}
/**
* @see PythonModelProvider#getResourceInPythonModel(IResource, boolean, boolean)
*/
protected Object getResourceInPythonModel(IResource object) {
return getResourceInPythonModel(object, false, false);
}
/**
* @see PythonModelProvider#getResourceInPythonModel(IResource, boolean, boolean)
*/
protected Object getResourceInPythonModel(IResource object, boolean returnNullIfNotFound) {
return getResourceInPythonModel(object, false, returnNullIfNotFound);
}
/**
* Given some IResource in the filesystem, return the representation for it in the python model
* or the resource itself if it could not be found in the python model.
*
* Note that this method only returns some resource already created (it does not
* create some resource if it still does not exist)
*/
protected Object getResourceInPythonModel(IResource object, boolean removeFoundResource, boolean returnNullIfNotFound) {
if(DEBUG){
System.out.println("Getting resource in python model:"+object);
}
Set<PythonSourceFolder> sourceFolders = getProjectSourceFolders(object.getProject());
Object f = null;
PythonSourceFolder sourceFolder = null;
for (Iterator<PythonSourceFolder> it = sourceFolders.iterator();f == null && it.hasNext();) {
sourceFolder = it.next();
if(sourceFolder.getActualObject().equals(object)){
f = sourceFolder;
}else{
f = sourceFolder.getChild(object);
}
}
if(f == null){
if(returnNullIfNotFound){
return null;
}else{
return object;
}
}else{
if(removeFoundResource){
if(f == sourceFolder){
sourceFolders.remove(f);
}else{
sourceFolder.removeChild(object);
}
}
}
return f;
}
/**
* @param object: the resource we're interested in
* @return a set with the PythonSourceFolder that exist in the project that contains it
*/
protected Set<PythonSourceFolder> getProjectSourceFolders(IProject project) {
Map<IProject, Set<PythonSourceFolder>> p = projectToSourceFolders;
//may be already disposed
if(p != null){
Set<PythonSourceFolder> sourceFolder = p.get(project);
if(sourceFolder == null){
sourceFolder = new HashSet<PythonSourceFolder>();
p.put(project, sourceFolder);
}
return sourceFolder;
}
return new HashSet<PythonSourceFolder>();
}
/**
* @return whether there are children for the given element. Note that there is
* an optimization in this method, so that it works correctly for elements that
* are not python files, and returns true if it is a python file with any content
* (even if that content does not actually map to a node.
*
* @see org.eclipse.ui.model.BaseWorkbenchContentProvider#hasChildren(java.lang.Object)
*/
public boolean hasChildren(Object element) {
if(element instanceof PythonFile){
PythonFile f = (PythonFile) element;
if(PythonPathHelper.isValidSourceFile(f.getActualObject())){
try {
InputStream contents = f.getContents();
try{
if(contents.read() == -1){
return false; //if there is no content in the file, it has no children
}else{
return true; //if it has any content, it has children (performance reasons)
}
}finally{
contents.close();
}
} catch (Exception e) {
PydevPlugin.log(e);
return false;
}
}
return false;
}
return getChildren(element).length > 0;
}
/**
* The inputs for this method are:
*
* IWorkingSet (in which case it will return the projects -- IResource -- that are a part of the working set)
* IResource (in which case it will return IWrappedResource or IResources)
* IWrappedResource (in which case it will return IWrappedResources)
*
* @return the children for some element (IWrappedResource or IResource)
*/
public Object[] getChildren(Object parentElement) {
//------------------------------------------- for the working set, just return the children directly
// if(parentElement instanceof IWorkingSet){
// IWorkingSet set = (IWorkingSet) parentElement;
// return set.getElements();
// }
Object[] childrenToReturn = null;
if(parentElement instanceof IWrappedResource){
// we're below some python model
childrenToReturn = getChildrenForIWrappedResource((IWrappedResource) parentElement);
} else if(parentElement instanceof IResource || parentElement instanceof IWorkingSet){
// now, this happens if we're not below a python model(so, we may only find a source folder here)
childrenToReturn = getChildrenForIResourceOrWorkingSet(parentElement);
}
if(childrenToReturn == null){
return EMPTY;
}
return childrenToReturn;
}
/**
* @param parentElement an IResource from where we want to get the children (or a working set)
*
* @return as we're not below a source folder here, we have still not entered the 'python' domain,
* and as the starting point for the 'python' domain is always a source folder, the things
* that can be returned are IResources and PythonSourceFolders.
*/
private Object[] getChildrenForIResourceOrWorkingSet(Object parentElement) {
PythonNature nature = null;
IProject project = null;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -