/** * 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 Dec 21, 2004 * * @author Fabio Zadrozny */ package org.python.pydev.editor.codecompletion.revisited.visitors; import java.util.List; import java.util.Map; import java.util.Stack; import org.python.pydev.core.ICompletionState; import org.python.pydev.core.docutils.StringUtils; import org.python.pydev.editor.codecompletion.revisited.modules.SourceToken; import org.python.pydev.parser.jython.SimpleNode; import org.python.pydev.parser.jython.ast.Assign; import org.python.pydev.parser.jython.ast.Attribute; import org.python.pydev.parser.jython.ast.Call; import org.python.pydev.parser.jython.ast.ClassDef; import org.python.pydev.parser.jython.ast.FunctionDef; import org.python.pydev.parser.jython.ast.If; import org.python.pydev.parser.jython.ast.Name; import org.python.pydev.parser.jython.ast.NameTok; import org.python.pydev.parser.jython.ast.Tuple; import org.python.pydev.parser.jython.ast.exprType; import org.python.pydev.parser.visitors.NodeUtils; /** * This class defines how we should find attributes. * * Heuristics provided allow someone to find an attr inside a function definition (IN_INIT or IN_ANY) * or inside a method call (e.g. a method called properties.create(x=0) - that's what I use, so, that's specific). * Other uses may be customized later, once we know which other uses are done. * * @author Fabio Zadrozny */ public class HeuristicFindAttrs extends AbstractVisitor { /** * Whether we should add the attributes that are added as 'self.xxx = 10' */ private boolean discoverSelfAttrs = true; private final Map<String, SourceToken> repToTokenWithArgs; /** * @param where * @param how * @param methodCall * @param state */ public HeuristicFindAttrs(int where, int how, String methodCall, String moduleName, ICompletionState state, Map<String, SourceToken> repToTokenWithArgs) { this.where = where; this.how = how; this.methodCall = methodCall; this.moduleName = moduleName; this.repToTokenWithArgs = repToTokenWithArgs; if (state != null) { if (state.getLookingFor() == ICompletionState.LOOKING_FOR_CLASSMETHOD_VARIABLE) { this.discoverSelfAttrs = false; } } } public Stack<SimpleNode> stack = new Stack<SimpleNode>(); public static final int WHITIN_METHOD_CALL = 0; public static final int WHITIN_INIT = 1; public static final int WHITIN_ANY = 2; public int where = -1; public static final int IN_ASSIGN = 0; public static final int IN_KEYWORDS = 1; public int how = -1; private boolean entryPointCorrect = false; private boolean inAssing = false; private boolean inFuncDef = false; /** * This is the method that can be used to declare them (e.g. properties.create) * It's only used it it is a method call. */ public String methodCall = ""; /** * @see org.python.pydev.parser.jython.ast.VisitorBase#unhandled_node(org.python.pydev.parser.jython.SimpleNode) */ protected Object unhandled_node(SimpleNode node) throws Exception { return null; } /** * @see org.python.pydev.parser.jython.ast.VisitorBase#traverse(org.python.pydev.parser.jython.SimpleNode) */ public void traverse(SimpleNode node) throws Exception { } @Override protected SourceToken addToken(SimpleNode node) { SourceToken tok = super.addToken(node); if (tok.getArgs().length() > 0) { this.repToTokenWithArgs.put(tok.getRepresentation(), tok); } return tok; } //ENTRY POINTS /** * @see org.python.pydev.parser.jython.ast.VisitorBase#visitCall(org.python.pydev.parser.jython.ast.Call) */ public Object visitCall(Call node) throws Exception { if (entryPointCorrect == false && methodCall.length() > 0) { entryPointCorrect = true; if (node.func instanceof Attribute) { List<String> c = StringUtils.dotSplit(methodCall); Attribute func = (Attribute) node.func; if (((NameTok) func.attr).id.equals(c.get(1))) { if (func.value instanceof Name) { Name name = (Name) func.value; if (name.id.equals(c.get(0))) { for (int i = 0; i < node.keywords.length; i++) { addToken(node.keywords[i]); } } } } } entryPointCorrect = false; } return null; } /** * @see org.python.pydev.parser.jython.ast.VisitorBase#visitFunctionDef(org.python.pydev.parser.jython.ast.FunctionDef) */ public Object visitFunctionDef(FunctionDef node) throws Exception { stack.push(node); if (entryPointCorrect == false) { entryPointCorrect = true; inFuncDef = true; if (where == WHITIN_ANY) { node.traverse(this); } else if (where == WHITIN_INIT && node.name != null && ((NameTok) node.name).id.equals("__init__")) { node.traverse(this); } entryPointCorrect = false; inFuncDef = false; } stack.pop(); return null; } //END ENTRY POINTS @Override public Object visitClassDef(ClassDef node) throws Exception { stack.push(node); Object r = super.visitClassDef(node); stack.pop(); return r; } /** * Name should be within assign. * * @see org.python.pydev.parser.jython.ast.VisitorIF#visitAssign(org.python.pydev.parser.jython.ast.Assign) */ public Object visitAssign(Assign node) throws Exception { if (how == IN_ASSIGN) { inAssing = true; exprType value = node.value; String rep = NodeUtils.getRepresentationString(value); SourceToken methodTok = null; if (rep != null) { methodTok = repToTokenWithArgs.get(rep); //The use case is the following: we have a method and an assign to it: //def method(a, b): // ... //other = method // //and later on, we want the arguments for 'other' to be the same arguments for 'method'. } for (int i = 0; i < node.targets.length; i++) { if (node.targets[i] instanceof Attribute) { visitAttribute((Attribute) node.targets[i]); } else if (node.targets[i] instanceof Name && inFuncDef == false) { String id = ((Name) node.targets[i]).id; if (id != null) { SourceToken added = addToken(node.targets[i]); if (methodTok != null) { added.updateAliasToken(methodTok); } } } else if (node.targets[i] instanceof Tuple && inFuncDef == false) { //that's for finding the definition: a,b,c = range(3) inside a class definition Tuple tuple = (Tuple) node.targets[i]; for (exprType t : tuple.elts) { if (t instanceof Name) { String id = ((Name) t).id; if (id != null) { addToken(t); } } } } } inAssing = false; } return null; } /** * @see org.python.pydev.parser.jython.ast.VisitorBase#visitAttribute(org.python.pydev.parser.jython.ast.Attribute) */ public Object visitAttribute(Attribute node) throws Exception { if (how == IN_ASSIGN && inAssing) { if (node.value instanceof Name) { String id = ((Name) node.value).id; if (id != null) { if (this.discoverSelfAttrs) { if (id.equals("self")) { addToken(node); } } else { if (id.equals("cls")) { addToken(node); } } } } } return null; } /** * @see org.python.pydev.parser.jython.ast.VisitorIF#visitIf(org.python.pydev.parser.jython.ast.If) */ public Object visitIf(If node) throws Exception { node.traverse(this); return null; } }