/** * 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.editor.codecompletion.revisited.javaintegration; import java.io.File; import java.util.AbstractMap; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.Path; import org.eclipse.jdt.core.IClasspathEntry; 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.JavaCore; import org.eclipse.jface.text.IDocument; import org.python.pydev.core.FullRepIterable; import org.python.pydev.core.IInterpreterInfo; import org.python.pydev.core.IInterpreterManager; import org.python.pydev.core.IModule; import org.python.pydev.core.IModulesManager; import org.python.pydev.core.IProjectModulesManager; import org.python.pydev.core.IPythonNature; import org.python.pydev.core.ISystemModulesManager; import org.python.pydev.core.ModulesKey; import org.python.pydev.shared_core.callbacks.ICallback0; import org.python.pydev.shared_core.string.FastStringBuffer; import org.python.pydev.shared_core.string.StringUtils; import org.python.pydev.shared_core.structure.Tuple; /** * This class wraps a java project as we'd wrap a python project in a ProjectModulesManager, to give info on the * modules available. * * Alternative to find the package names: * SearchableEnvironment s = j.newSearchableNameEnvironment(new ICompilationUnit[]{unit}); * s.findPackages("bar".toCharArray(), new ISearchRequestor(){ * * public void acceptPackage(char[] packageName) { * System.out.println("Accept package:"+new String(packageName)); * } * * public void acceptType(char[] packageName, char[] typeName, char[][] enclosingTypeNames, int modifiers, * AccessRestriction accessRestriction) { * System.out.println("Accept type:"+new String(packageName)+" / "+new String(typeName)); * }}); * End Alternative * * Message about it: http://www.eclipse.org/newsportal/article.php?id=21742&group=eclipse.tools.jdt#21742 * * @author Fabio */ public class JavaProjectModulesManager implements IModulesManager, IProjectModulesManager { private static final String[] EMPTY_STRINTG_ARRAY = new String[0]; // DEBUG CONSTANTS private static final boolean DEBUG_GET_MODULE = false; private static final boolean DEBUG_GET_DIRECT_MODULES = false; private IJavaProject javaProject; public JavaProjectModulesManager(IJavaProject javaProject) { this.javaProject = javaProject; } @Override public IModulesManager[] getManagersInvolved(boolean checkSystemManager) { return new IModulesManager[] { this }; } /** * @return a map with the modules keys for all the available modules that start with the passed token. */ @Override public SortedMap<ModulesKey, ModulesKey> getAllDirectModulesStartingWith(final String moduleToGetTokensFrom) { if (DEBUG_GET_DIRECT_MODULES) { System.out.println("getAllDirectModulesStartingWith: " + moduleToGetTokensFrom); } final TreeMap<ModulesKey, ModulesKey> ret = new TreeMap<ModulesKey, ModulesKey>(); filterJavaPackages(new IFilter() { @Override public boolean accept(String elementName, IPackageFragmentRoot packageRoot, IJavaElement javaElement) { if (elementName.startsWith(moduleToGetTokensFrom) && elementName.length() > 0) { //we don't want the 'default' package here! if (DEBUG_GET_DIRECT_MODULES) { System.out.println("getAllDirectModulesStartingWith: found:" + elementName); } ModulesKeyForJava key = new ModulesKeyForJava(elementName, packageRoot, javaElement); ret.put(key, key); //as we care about the full module name here, we'll only try to check the classes if //the package name already starts with what we're looking for... return true; } if (elementName.length() == 0) { //or if we're in the default package. return true; } return false; } }); return ret; } /** * @return a set with all the module names contained in this modules manager (only in this modules manager, * as the addDependencies should never be true in this implementation). */ @Override public Set<String> getAllModuleNames(boolean addDependencies, final String partStartingWithLowerCase) { if (addDependencies) { throw new RuntimeException("At this point, it should never be called with dependencies " + "(because it's a java project already -- it manages that internally already)"); } final HashSet<String> ret = new HashSet<String>(); filterJavaPackages(new IFilter() { @Override public boolean accept(String elementName, IPackageFragmentRoot packageRoot, IJavaElement javaElement) { for (String mod : StringUtils.dotSplit(elementName)) { if (mod.toLowerCase().startsWith(partStartingWithLowerCase)) { ret.add(elementName); } } return true; } }); return ret; } /** * Interface to be passed to filter a java package. * * @author Fabio */ public static interface IFilter { /** * @param elementName the name of the element (same as javaElement.getElementName()) * @param packageRoot the java package where the element is contained * @param javaElement the java element * * @return true if the element should be added and false otherwise. */ public boolean accept(String elementName, IPackageFragmentRoot packageRoot, IJavaElement javaElement); } /** * This method passes through all the java packages and calls the filter callback passed * on each package found. * * If true is returned on the callback, the children of each package (classes) will also be visited, * otherwise, they'll be skipped. */ private void filterJavaPackages(IFilter filter) { IClasspathEntry[] rawClasspath; try { rawClasspath = this.javaProject.getRawClasspath(); FastStringBuffer buffer = new FastStringBuffer(); for (IClasspathEntry entry : rawClasspath) { int entryKind = entry.getEntryKind(); IClasspathEntry resolvedClasspathEntry = JavaCore.getResolvedClasspathEntry(entry); if (entryKind != IClasspathEntry.CPE_CONTAINER) { //ignore if it's in the system classpath... IPackageFragmentRoot[] roots = javaProject.findPackageFragmentRoots(resolvedClasspathEntry); //get the package roots for (IPackageFragmentRoot root : roots) { IJavaElement[] children = root.getChildren(); //get the actual packages for (IJavaElement child : children) { IPackageFragment childPackage = (IPackageFragment) child; String elementName = childPackage.getElementName(); //and if the java package is 'accepted' if (filter.accept(elementName, root, childPackage)) { buffer.clear(); buffer.append(elementName); int packageNameLen = buffer.length(); if (packageNameLen > 0) { buffer.append('.'); packageNameLen++; } //traverse its classes for (IJavaElement class_ : childPackage.getChildren()) { buffer.append(FullRepIterable.getFirstPart(class_.getElementName())); filter.accept(buffer.toString(), root, class_); buffer.setCount(packageNameLen); //leave only the package part for the next append } } } } } } } catch (Exception e) { throw new RuntimeException(e); } } @Override public String[] getBuiltins() { return EMPTY_STRINTG_ARRAY; } @Override public List<String> getCompletePythonPath(IInterpreterInfo interpreter, IInterpreterManager manager) { return new ArrayList<String>(); } @Override public IModule getModule(String name, IPythonNature nature, boolean dontSearchInit) { return this.getModuleInDirectManager(name, nature, dontSearchInit); } @Override public IModule getModule(String name, IPythonNature nature, boolean checkSystemManager, boolean dontSearchInit) { return this.getModuleInDirectManager(name, nature, dontSearchInit); } @Override public IPythonNature getNature() { return null; } @Override public boolean hasModule(ModulesKey key) { return false; } @Override public ModulesKey[] getOnlyDirectModules() { return new ModulesKey[0]; } @Override public Object getPythonPathHelper() { return null; } public void setPythonPathHelper(Object helper) { return; // noop } @Override public IModule getRelativeModule(String name, IPythonNature nature) { return this.getModuleInDirectManager(name, nature, true); } @Override public int getSize(boolean addDependenciesSize) { return 0; } @Override public ISystemModulesManager getSystemModulesManager() { return null; } @Override public Tuple<IModule, IModulesManager> getModuleAndRelatedModulesManager(String name, IPythonNature nature, boolean checkSystemManager, boolean dontSearchInit) { IModule module = this.getModule(name, nature, checkSystemManager, dontSearchInit); if (module != null) { return new Tuple<IModule, IModulesManager>(module, this); } return null; } /** * @param dontSearchInit: not applicable for this method (ignored) * @return the module that corresponds to the passed name. */ @Override public IModule getModuleInDirectManager(String name, IPythonNature nature, boolean dontSearchInit) { if (DEBUG_GET_MODULE) { System.out.println("Trying to get module in java project modules manager: " + name); } if (name.startsWith(".")) { //this happens when looking for a relative import return null; } try { IJavaElement javaElement = this.javaProject.findType(name); if (javaElement == null) { javaElement = this.javaProject.findElement(new Path(name.replace('.', '/'))); } if (DEBUG_GET_MODULE) { System.out.println("Found: " + javaElement); } if (javaElement != null) { //now, there's a catch here, we'll find any class in the project classpath, even if it's in the //global classpath (e.g.: rt.jar), and this shouldn't be treated in this project modules manager //(that's treated in the Jython system manager) IJavaElement ancestor = javaElement.getAncestor(IJavaElement.PACKAGE_FRAGMENT_ROOT); if (ancestor instanceof IPackageFragmentRoot) { IPackageFragmentRoot packageFragmentRoot = (IPackageFragmentRoot) ancestor; IClasspathEntry rawClasspathEntry = packageFragmentRoot.getRawClasspathEntry(); if (rawClasspathEntry.getEntryKind() == IClasspathEntry.CPE_CONTAINER) { return null; } } return new JavaModuleInProject(name, this.javaProject); } } catch (Exception e) { throw new RuntimeException(e); } return null; } @Override public String resolveModuleInDirectManager(IFile file) { return null; } @Override public String resolveModuleInDirectManager(String full) { return null; } //------------------------------------------------------------------------------------------------------------------ //the methods below are not actually implemented for a java project (as they aren't really applicable) //------------------------------------------------------------------------------------------------------------------ @Override public boolean isInPythonPath(IResource member, IProject container) { throw new RuntimeException("Not implemented"); } @Override public String resolveModule(IResource member, IProject container) { throw new RuntimeException("Not implemented"); } @Override public String resolveModule(String full) { throw new RuntimeException("Not implemented"); } @Override public String resolveModule(String full, boolean checkSystemManager) { throw new RuntimeException("Not implemented"); } @Override public void setPythonNature(IPythonNature nature) { throw new RuntimeException("Not implemented"); } @Override public boolean startCompletionCache() { throw new RuntimeException("Not implemented"); } @Override public void endCompletionCache() { throw new RuntimeException("Not implemented"); } @Override public void endProcessing() { throw new RuntimeException("Not implemented"); } @Override public SortedMap<ModulesKey, ModulesKey> getAllModulesStartingWith(String moduleToGetTokensFrom) { throw new RuntimeException("Not implemented"); //should never be called (this modules manager is inside another one that should handle it) } @Override public IModule addModule(ModulesKey key) { throw new RuntimeException("Not implemented"); } @Override public void changePythonPath(String pythonpath, IProject project, IProgressMonitor monitor) { throw new RuntimeException("Not implemented"); } @Override public void removeModules(Collection<ModulesKey> toRem) { throw new RuntimeException("Not implemented"); } @Override public void processDelete(ModulesKey key) { throw new RuntimeException("Not implemented"); } @Override public void processInsert(ModulesKey key) { throw new RuntimeException("Not implemented"); } @Override public void processUpdate(ModulesKey data) { throw new RuntimeException("Not implemented"); } @Override public void rebuildModule(File f, ICallback0<IDocument> doc, IProject project, IProgressMonitor monitor, IPythonNature nature) { throw new RuntimeException("Not implemented"); } @Override public void removeModule(File file, IProject project, IProgressMonitor monitor) { throw new RuntimeException("Not implemented"); } @Override public void setProject(IProject project, IPythonNature nature, boolean restoreDeltas) { throw new RuntimeException("Not implemented"); } @Override public int pushTemporaryModule(String moduleName, IModule module) { throw new RuntimeException("Not implemented"); } @Override public void popTemporaryModule(String moduleName, int handle) { throw new RuntimeException("Not implemented"); } @Override public void saveToFile(File workspaceMetadataFile) { throw new RuntimeException("Not implemented"); } @Override public AutoCloseable withNoGenerateDeltas() { throw new RuntimeException("not implemented"); } @Override public Object getCompiledModuleCreationLock(String name) { throw new RuntimeException("not implemented"); } @Override public Tuple<List<ModulesKey>, List<ModulesKey>> diffModules(AbstractMap<ModulesKey, ModulesKey> keysFound) { throw new RuntimeException("not implemented"); } }