/** * Copyright (c) 2005-2011 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. */ /* * Created on Nov 12, 2004 * * @author Fabio Zadrozny */ package org.python.pydev.editor.codecompletion.revisited.modules; import java.io.File; import java.io.IOException; import java.util.HashMap; import java.util.Map; import org.eclipse.jface.text.IDocument; import org.python.pydev.core.FileUtilsFileBuffer; import org.python.pydev.core.FullRepIterable; import org.python.pydev.core.ICodeCompletionASTManager; import org.python.pydev.core.ICompletionCache; import org.python.pydev.core.ICompletionState; import org.python.pydev.core.IGrammarVersionProvider; import org.python.pydev.core.ILocalScope; import org.python.pydev.core.IModule; import org.python.pydev.core.IModulesManager; import org.python.pydev.core.IPythonNature; import org.python.pydev.core.IToken; import org.python.pydev.core.MisconfigurationException; import org.python.pydev.core.ModulesKey; import org.python.pydev.core.ModulesKeyForZip; import org.python.pydev.core.TupleN; import org.python.pydev.core.structure.CompletionRecursionException; import org.python.pydev.editor.codecompletion.revisited.CompletionStateFactory; import org.python.pydev.editor.codecompletion.revisited.PythonPathHelper; import org.python.pydev.editor.codecompletion.revisited.visitors.Definition; import org.python.pydev.parser.PyParser; import org.python.pydev.parser.jython.SimpleNode; import com.aptana.shared_core.io.FileUtils; import com.aptana.shared_core.structure.Tuple; /** * @author Fabio Zadrozny */ public abstract class AbstractModule implements IModule { private static final IToken[] EMPTY_TOKEN_ARRAY = new IToken[0]; /** * May be changed for tests */ public static String MODULE_NAME_WHEN_FILE_IS_UNDEFINED = ""; /** * @see org.python.pydev.core.IModule#getWildImportedModules() */ public abstract IToken[] getWildImportedModules(); /** * @see org.python.pydev.core.IModule#getFile() */ public abstract File getFile(); /** * @see org.python.pydev.core.IModule#getTokenImportedModules() */ public abstract IToken[] getTokenImportedModules(); /** * @see org.python.pydev.core.IModule#getGlobalTokens() */ public abstract IToken[] getGlobalTokens(); /** * Don't deal with zip files unless specifically specified */ public String getZipFilePath() { return null; } /** * @see org.python.pydev.core.IModule#getLocalTokens(int, int, ILocalScope) */ public IToken[] getLocalTokens(int line, int col, ILocalScope scope) { return EMPTY_TOKEN_ARRAY; } /** * Checks if it is in the global tokens that were created in this module * @param tok the token we are looking for * @return true if it was found and false otherwise */ public abstract boolean isInDirectGlobalTokens(String tok, ICompletionCache completionCache); /** * @throws CompletionRecursionException * @see org.python.pydev.core.IModule#isInGlobalTokens(java.lang.String, org.python.pydev.plugin.nature.PythonNature) */ public boolean isInGlobalTokens(String tok, IPythonNature nature, ICompletionCache completionCache) throws CompletionRecursionException { return isInGlobalTokens(tok, nature, true, completionCache); } /** * @throws CompletionRecursionException * @see org.python.pydev.core.IModule#isInGlobalTokens(java.lang.String, org.python.pydev.plugin.nature.PythonNature, boolean) */ public boolean isInGlobalTokens(String tok, IPythonNature nature, boolean searchSameLevelMods, ICompletionCache completionCache) throws CompletionRecursionException { return isInGlobalTokens(tok, nature, searchSameLevelMods, false, completionCache) != IModule.NOT_FOUND; } public int isInGlobalTokens(String tok, IPythonNature nature, boolean searchSameLevelMods, boolean ifHasGetAttributeConsiderInTokens, ICompletionCache completionCache) throws CompletionRecursionException { //it's worth checking it if it is not dotted... (much faster as it's in a map already) if (tok.indexOf(".") == -1) { if (isInDirectGlobalTokens(tok, completionCache)) { return IModule.FOUND_TOKEN; } } String[] headAndTail = FullRepIterable.headAndTail(tok); String head = headAndTail[1]; String generateTokensFor = headAndTail[0]; Map<String, IToken> cachedTokens = getCachedCompletions(tok, nature, searchSameLevelMods, completionCache, generateTokensFor); if (cachedTokens.containsKey(head)) { return IModule.FOUND_TOKEN; } if (ifHasGetAttributeConsiderInTokens) { IToken token = cachedTokens.get("__getattribute__"); if (token == null || isTokenFromBuiltins(token)) { token = cachedTokens.get("__getattr__"); } if (token != null && !isTokenFromBuiltins(token)) { return IModule.FOUND_BECAUSE_OF_GETATTR; } //Try to determine if the user specified it has a @DynamicAttrs. String[] parentsHeadAndTail = FullRepIterable.headAndTail(generateTokensFor); cachedTokens = getCachedCompletions(tok, nature, searchSameLevelMods, completionCache, parentsHeadAndTail[0]); IToken parentToken = cachedTokens.get(parentsHeadAndTail[1]); if (parentToken != null) { String docString = parentToken.getDocStr(); if (docString != null) { if (docString.indexOf("@DynamicAttrs") != -1) { //class that has things dynamically defined. return IModule.FOUND_BECAUSE_OF_GETATTR; } } } } //if not found until now, it is not defined return IModule.NOT_FOUND; } @SuppressWarnings("unchecked") protected Map<String, IToken> getCachedCompletions(String tok, IPythonNature nature, boolean searchSameLevelMods, ICompletionCache completionCache, String generateTokensFor) throws CompletionRecursionException { //now, check if it's cached in a way we can use it (we cache it not as raw tokens, but as representation --> token) //to help in later searches. String name = this.getName(); Object key = new TupleN("isInGlobalTokens", name != null ? name : "", generateTokensFor, tok, searchSameLevelMods); Map<String, IToken> cachedTokens = (Map<String, IToken>) completionCache.getObj(key); if (cachedTokens == null) { cachedTokens = internalGenerateCachedTokens(nature, completionCache, generateTokensFor, searchSameLevelMods); completionCache.add(key, cachedTokens); } return cachedTokens; } private boolean isTokenFromBuiltins(IToken token) { String parentPackage = token.getParentPackage(); return parentPackage.equals("__builtin__") || parentPackage.startsWith("__builtin__.") || parentPackage.equals("builtins") || parentPackage.startsWith("builtins."); } /** * Generates the cached tokens in the needed structure for a 'fast' search given a token representation * (creates a map with the name of the token --> token). */ private Map<String, IToken> internalGenerateCachedTokens(IPythonNature nature, ICompletionCache completionCache, String activationToken, boolean searchSameLevelMods) throws CompletionRecursionException { Map<String, IToken> cachedTokens = new HashMap<String, IToken>(); //if still not found, we have to get all the tokens, including regular and wild imports ICompletionState state = CompletionStateFactory.getEmptyCompletionState(nature, completionCache); ICodeCompletionASTManager astManager = nature.getAstManager(); state.setActivationToken(activationToken); //we don't want to gather builtins in this case. state.setBuiltinsGotten(true); IToken[] globalTokens = astManager.getCompletionsForModule(this, state, searchSameLevelMods); for (IToken token : globalTokens) { String rep = token.getRepresentation(); IToken t = cachedTokens.get(rep); if (t != null) { //only override tokens if it's a getattr that's not defined in the builtin module if (rep.equals("__getattribute__") || rep.equals("__getattr__")) { if (!isTokenFromBuiltins(token)) { cachedTokens.put(rep, token); } } } else { cachedTokens.put(rep, token); } } return cachedTokens; } /** * The token we're looking for must be the state activation token */ public abstract Definition[] findDefinition(ICompletionState state, int line, int col, IPythonNature nature) throws Exception; /** * @see org.python.pydev.core.IModule#getGlobalTokens(org.python.pydev.editor.codecompletion.revisited.CompletionState, org.python.pydev.core.ICodeCompletionASTManager) */ public abstract IToken[] getGlobalTokens(ICompletionState state, ICodeCompletionASTManager manager); /** * @see org.python.pydev.core.IModule#getDocString() */ public abstract String getDocString(); /** * Name of the module */ protected String name; /** * @see org.python.pydev.core.IModule#getName() */ public String getName() { return name; } /** * Constructor * * @param name - name of the module */ protected AbstractModule(String name) { this.name = name; } /** * This method creates a source module from a file. * * @return * @throws IOException * @throws MisconfigurationException */ public static AbstractModule createModule(String name, File f, IPythonNature nature, boolean checkForPath) throws IOException, MisconfigurationException { if (PythonPathHelper.isValidFileMod(f.getName())) { if (PythonPathHelper.isValidSourceFile(f.getName())) { return createModuleFromDoc(name, f, FileUtilsFileBuffer.getDocFromFile(f), nature, checkForPath); } else { //this should be a compiled extension... we have to get completions from the python shell. return new CompiledModule(name, nature.getAstManager().getModulesManager()); } } //if we are here, return null... return null; } /** * This function creates the module given that you have a document (that will be parsed) * @throws MisconfigurationException */ public static SourceModule createModuleFromDoc(String name, File f, IDocument doc, IGrammarVersionProvider nature, boolean checkForPath) throws MisconfigurationException { //for doc, we are only interested in python files. if (f != null) { if (!checkForPath || PythonPathHelper.isValidSourceFile(f.getName())) { Tuple<SimpleNode, Throwable> obj = PyParser.reparseDocument(new PyParser.ParserInfo(doc, nature, name, f)); return new SourceModule(name, f, obj.o1, obj.o2); } } else { Tuple<SimpleNode, Throwable> obj = PyParser.reparseDocument(new PyParser.ParserInfo(doc, nature, name, f)); return new SourceModule(name, f, obj.o1, obj.o2); } return null; } /** * This function creates a module and resolves the module name (use this function if only the file is available). * @throws MisconfigurationException */ public static IModule createModuleFromDoc(File file, IDocument doc, IPythonNature pythonNature) throws MisconfigurationException { IModulesManager projModulesManager = pythonNature.getAstManager().getModulesManager(); String moduleName = null; if (file != null) { moduleName = projModulesManager.resolveModule(FileUtils.getFileAbsolutePath(file)); } if (moduleName == null) { moduleName = MODULE_NAME_WHEN_FILE_IS_UNDEFINED; } IModule module = createModuleFromDoc(moduleName, file, doc, pythonNature, false); return module; } /** * Creates a source file generated only from an ast. * @param n the ast root * @return the module */ public static IModule createModule(SimpleNode n) { return new SourceModule(null, null, n, null); } /** * Creates a source file generated only from an ast. * * @param n the ast root * @param file the module file * @param moduleName the name of the module * * @return the module */ public static IModule createModule(SimpleNode n, File file, String moduleName) { return new SourceModule(moduleName, file, n, null); } /** * @return an empty module representing the key passed. */ public static AbstractModule createEmptyModule(ModulesKey key) { if (key instanceof ModulesKeyForZip) { ModulesKeyForZip e = ((ModulesKeyForZip) key); return new EmptyModuleForZip(key.name, key.file, e.zipModulePath, e.isFile); } else { return new EmptyModule(key.name, key.file); } } public ILocalScope getLocalScope(int line, int col) { return null; } /** * @see org.python.pydev.core.IModule#toString() */ @Override public String toString() { String n2 = this.getClass().getName(); String n = n2.substring(n2.lastIndexOf('.') + 1); return this.getName() + " (" + n + ")"; } /** * @return true if the name we have ends with .__init__ (default for packages -- others are modules) */ public boolean isPackage() { return this.name != null && this.name.endsWith(".__init__"); } public String getPackageFolderName() { return FullRepIterable.getParentModule(this.name); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((name == null) ? 0 : name.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (!(obj instanceof AbstractModule)) return false; AbstractModule other = (AbstractModule) obj; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; return true; } }