/** * 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 21/08/2005 */ package com.python.pydev.analysis.visitors; import org.python.pydev.core.ICodeCompletionASTManager; import org.python.pydev.core.ICompletionCache; import org.python.pydev.core.ICompletionState; import org.python.pydev.core.IDefinition; import org.python.pydev.core.IModule; import org.python.pydev.core.IPythonNature; import org.python.pydev.core.IToken; import org.python.pydev.core.Tuple3; import org.python.pydev.core.structure.CompletionRecursionException; import org.python.pydev.editor.codecompletion.revisited.AbstractASTManager; import org.python.pydev.editor.codecompletion.revisited.CompletionStateFactory; import org.python.pydev.editor.codecompletion.revisited.modules.SourceToken; import org.python.pydev.editor.codecompletion.revisited.visitors.Definition; import com.aptana.shared_core.string.FastStringBuffer; import com.python.pydev.analysis.scopeanalysis.AbstractScopeAnalyzerVisitor; /** * The import checker not only generates information on errors for unresolved modules, but also gathers * dependency information so that we can do incremental building of dependent modules. * * @author Fabio */ public final class ImportChecker { /** * This is the nature we are analyzing */ private final IPythonNature nature; /** * this is the name of the module that we are analyzing */ private final String moduleName; private final AbstractScopeAnalyzerVisitor visitor; /** * This is the information stored about some import: * Contains the actual module, the representation in the current module and whether it was resolved or not. */ public static class ImportInfo { /** * This is the module where this info was found */ public final IModule mod; /** * This is the token that relates to this import info (in the module it was found) */ public final IToken token; /** * This is the representation where it was found */ public final String rep; /** * Determines whether it was resolved or not (if not resolved, the other attributes may be null) */ public final boolean wasResolved; /** * Should we use the definition cache? */ private boolean useActualDefinitionCache = false; /** * This is the cache for the definitions. */ private IDefinition[] definitionCache; public ImportInfo(IModule mod, String rep, IToken token, boolean wasResolved) { this.mod = mod; this.rep = rep; this.token = token; this.wasResolved = wasResolved; } @Override public String toString() { FastStringBuffer buffer = new FastStringBuffer(); buffer.append("ImportInfo("); buffer.append(" Resolved:"); buffer.append(wasResolved); if (wasResolved) { buffer.append(" Rep:"); buffer.append(rep); buffer.append(" Mod:"); buffer.append(mod != null ? mod.getName() : "null"); } buffer.append(")"); return buffer.toString(); } public IDefinition[] getDefinitions(IPythonNature nature, ICompletionCache completionCache) throws Exception { if (useActualDefinitionCache) { return definitionCache; } useActualDefinitionCache = true; if (this.mod != null) { definitionCache = this.mod.findDefinition( CompletionStateFactory.getEmptyCompletionState(this.rep, nature, completionCache), -1, -1, nature); } else { definitionCache = new IDefinition[0]; } return definitionCache; } /** * @return the definition that matches this import info. */ public Definition getModuleDefinitionFromImportInfo(IPythonNature nature, ICompletionCache completionCache) { try { IDefinition[] definitions = getDefinitions(nature, completionCache); int len = definitions.length; for (int i = 0; i < len; i++) { IDefinition definition = definitions[i]; if (definition instanceof Definition) { Definition d = (Definition) definition; if (d.module != null && d.value.length() == 0 && d.ast == null) { return d; } } } } catch (Exception e) { throw new RuntimeException(e); } return null; } } /** * constructor - will remove all dependency info on the project that we will start to analyze */ public ImportChecker(AbstractScopeAnalyzerVisitor visitor, IPythonNature nature, String moduleName) { this.nature = nature; this.moduleName = moduleName; this.visitor = visitor; } /** * @param token MUST be an import token * @param reportUndefinedImports * * @return the module where the token was found and a String representing the way it was found * in the module. * * Note: it may return information even if the token was not found in the representation required. This is useful * to get dependency info, because it is actually dependent on the module, event though it does not have the * token we were looking for. */ public ImportInfo visitImportToken(IToken token, boolean reportUndefinedImports, ICompletionCache completionCache) { return visitImportToken(reportUndefinedImports, token, moduleName, nature, visitor, completionCache); } /** * This is so that we can use it without actually being in some visit. */ public static ImportInfo visitImportToken(boolean reportUndefinedImports, IToken token, String moduleName, IPythonNature nature, AbstractScopeAnalyzerVisitor visitor, ICompletionCache completionCache) { //try to find it as a relative import boolean wasResolved = false; Tuple3<IModule, String, IToken> modTok = null; String checkForToken = ""; if (token instanceof SourceToken) { ICodeCompletionASTManager astManager = nature.getAstManager(); ICompletionState state = CompletionStateFactory.getEmptyCompletionState(token.getRepresentation(), nature, completionCache); try { modTok = astManager.findOnImportedMods(new IToken[] { token }, state, moduleName, visitor.current); } catch (CompletionRecursionException e1) { modTok = null;//unable to resolve it } if (modTok != null && modTok.o1 != null) { checkForToken = modTok.o2; if (modTok.o2.length() == 0) { wasResolved = true; } else { try { checkForToken = AbstractASTManager.getTokToSearchInOtherModule(modTok); if (astManager.getRepInModule(modTok.o1, checkForToken, nature) != null) { wasResolved = true; } } catch (CompletionRecursionException e) { //not resolved... } } } //if it got here, it was not resolved if (!wasResolved && reportUndefinedImports) { visitor.onAddUnresolvedImport(token); } } //might still return a modTok, even if the token we were looking for was not found. if (modTok != null) { return new ImportInfo(modTok.o1, checkForToken, modTok.o3, wasResolved); } else { return new ImportInfo(null, null, null, wasResolved); } } }