package org.rubypeople.rdt.internal.core.util; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.jruby.ast.ArgsNode; import org.jruby.ast.ArgumentNode; import org.jruby.ast.ArrayNode; import org.jruby.ast.AttrAssignNode; import org.jruby.ast.BignumNode; import org.jruby.ast.CallNode; import org.jruby.ast.ClassNode; import org.jruby.ast.ClassVarAsgnNode; import org.jruby.ast.ClassVarDeclNode; import org.jruby.ast.ClassVarNode; import org.jruby.ast.Colon2Node; import org.jruby.ast.ConstDeclNode; import org.jruby.ast.ConstNode; import org.jruby.ast.DAsgnNode; import org.jruby.ast.DStrNode; import org.jruby.ast.DefnNode; import org.jruby.ast.DefsNode; import org.jruby.ast.FCallNode; import org.jruby.ast.FalseNode; import org.jruby.ast.FixnumNode; import org.jruby.ast.GlobalAsgnNode; import org.jruby.ast.GlobalVarNode; import org.jruby.ast.HashNode; import org.jruby.ast.IArgumentNode; import org.jruby.ast.IScopingNode; import org.jruby.ast.InstAsgnNode; import org.jruby.ast.InstVarNode; import org.jruby.ast.IterNode; import org.jruby.ast.ListNode; import org.jruby.ast.LocalAsgnNode; import org.jruby.ast.ModuleNode; import org.jruby.ast.MultipleAsgnNode; import org.jruby.ast.NilNode; import org.jruby.ast.Node; import org.jruby.ast.SClassNode; import org.jruby.ast.SelfNode; import org.jruby.ast.SplatNode; import org.jruby.ast.StrNode; import org.jruby.ast.SymbolNode; import org.jruby.ast.TrueNode; import org.jruby.ast.ZArrayNode; import org.jruby.ast.types.INameNode; import org.jruby.lexer.yacc.ISourcePosition; import org.jruby.parser.StaticScope; import org.rubypeople.rdt.internal.ti.util.ClosestSpanningNodeLocator; import org.rubypeople.rdt.internal.ti.util.INodeAcceptor; public abstract class ASTUtil { private static final boolean VERBOSE = false; private static final String NAMESPACE_DELIMETER = "::"; private static final String OBJECT = "Object"; private static final String EMPTY_STRING = ""; /** * @param argsNode * @param scope * @return */ public static String[] getArgs(Node argsNode, StaticScope scope) { if (argsNode == null) return new String[0]; ArgsNode args = (ArgsNode) argsNode; boolean hasRest = false; if (args.getRestArg() != -1) hasRest = true; boolean hasBlock = false; if (args.getBlock() != null) hasBlock = true; int optArgCount = 0; if (args.getOptArgs() != null) optArgCount = args.getOptArgs().size(); List<String> arguments = getArguments(args.getPre()); if (optArgCount > 0) { arguments.addAll(getArguments(args.getOptArgs())); } if (hasRest) { String restName = "*"; if (args.getRestArg() != -2) { restName += scope.getVariables()[args.getRestArg()]; } arguments.add(restName); } if (hasBlock) arguments.add("&" + scope.getVariables()[args.getBlock().getCount()]); return stringListToArray(arguments); } private static String[] stringListToArray(List<String> list) { String[] array = new String[list.size()]; for (int i = 0; i < list.size(); i++) { array[i] = list.get(i); } return array; } public static List<String> getArguments(ListNode argList) { if (argList == null) return new ArrayList<String>(); List<String> arguments = new ArrayList<String>(); for (Node node : argList.childNodes()) { if (node instanceof ArgumentNode) { arguments.add(((ArgumentNode) node).getName()); } else if (node instanceof LocalAsgnNode) { LocalAsgnNode local = (LocalAsgnNode) node; String argString = local.getName(); argString += " = "; argString += stringRepresentation(local.getValueNode()); arguments.add(argString); } else { System.err.println("Reached argument node type we can't handle: " + node.getClass().getSimpleName()); } } return arguments; } public static String stringRepresentation(Node node) { if (node == null) return ""; if (node instanceof HashNode) return "{}"; if (node instanceof SelfNode) return "self"; if (node instanceof NilNode) return "nil"; if (node instanceof TrueNode) return "true"; if (node instanceof FalseNode) return "false"; if (node instanceof SymbolNode) return ':' + ((SymbolNode) node).getName(); if (node instanceof INameNode) return ((INameNode) node).getName(); if (node instanceof ZArrayNode) return "[]"; if (node instanceof FixnumNode) return "" + ((FixnumNode) node).getValue(); if (node instanceof DStrNode) return stringRepresentation((DStrNode) node); if (node instanceof StrNode) return '"' + ((StrNode) node).getValue().toString() + '"'; log("Reached node type we don't know how to represent: " + node.getClass().getName()); return node.toString(); } private static void log(String string) { if (VERBOSE) System.out.println(string); } private static String stringRepresentation(DStrNode node) { List children = node.childNodes(); StringBuffer buffer = new StringBuffer(); buffer.append("\""); for (Iterator iter = children.iterator(); iter.hasNext();) { Node child = (Node) iter.next(); buffer.append(stringRepresentation(child)); } buffer.append("\""); return buffer.toString(); } /** * Gets the name of a node by reflectively invoking "getName()" on it; helper method just to cut many * "instanceof/cast" pairs. * * @param node * @return name or null */ public static String getNameReflectively(Node node) { if (node == null) return ""; if (node instanceof ClassNode) { ClassNode classNode = (ClassNode) node; return getNameReflectively(classNode.getCPath()); } if (node instanceof ModuleNode) { ModuleNode moduleNode = (ModuleNode) node; return getNameReflectively(moduleNode.getCPath()); } if (node instanceof INameNode) { return ((INameNode) node).getName(); } try { Method getNameMethod = node.getClass().getMethod("getName", new Class[] {}); Object name = getNameMethod.invoke(node, new Object[0]); return (String) name; } catch (Exception e) { return null; } } public static String getFullyQualifiedName(Colon2Node node) { StringBuffer name = new StringBuffer(); Node left = node.getLeftNode(); if (left instanceof Colon2Node) { name.append(getFullyQualifiedName((Colon2Node) left)); } else if (left instanceof ConstNode) { name.append(((ConstNode) left).getName()); } name.append(NAMESPACE_DELIMETER); name.append(node.getName()); return name.toString(); } public static boolean isAssignment(Node node) { return (node instanceof LocalAsgnNode) || (node instanceof ClassVarAsgnNode) || (node instanceof InstAsgnNode) || (node instanceof GlobalAsgnNode) || (node instanceof AttrAssignNode); } public static boolean isTypeDefinition(Node node) { return node instanceof ClassNode || node instanceof ModuleNode || node instanceof SClassNode; } public static boolean isMethodDefinition(Node node) { return node instanceof DefnNode || node instanceof DefsNode; } public static boolean isMethodCall(Node node) { return node instanceof FCallNode || node instanceof CallNode; } public static String getSource(String contents, Node node) { if (node == null || contents == null) return null; ISourcePosition pos = node.getPosition(); if (pos == null) return null; if (pos.getStartOffset() >= contents.length()) return null; // position is past end of our source if (pos.getEndOffset() > contents.length()) return null; // end is past end of source return new String(contents.substring(pos.getStartOffset(), pos.getEndOffset())); } public static boolean isVariable(Node node) { return (node instanceof GlobalAsgnNode) || (node instanceof GlobalVarNode) || (node instanceof InstAsgnNode) || (node instanceof InstVarNode) || (node instanceof ConstDeclNode) || (node instanceof ConstNode) || (node instanceof ClassVarAsgnNode) || (node instanceof ClassVarDeclNode) || (node instanceof ClassVarNode); } public static List<String> getArgumentsFromFunctionCall(IArgumentNode iVisited) { List<String> arguments = new ArrayList<String>(); List<Node> nodes = getArgumentNodesFromFunctionCall(iVisited); for (Node node : nodes) { if (node instanceof DAsgnNode) { DAsgnNode dasgn = (DAsgnNode) node; arguments.add(dasgn.getName()); } else { arguments.add(stringRepresentation(node)); } } return arguments; } public static List<Node> getArgumentNodesFromFunctionCall(IArgumentNode iVisited) { List<Node> arguments = new ArrayList<Node>(); Node argsNode = iVisited.getArgsNode(); Iterator iter = null; if (argsNode instanceof SplatNode) { SplatNode splat = (SplatNode) argsNode; iter = splat.childNodes().iterator(); } else if (argsNode instanceof ArrayNode) { ArrayNode arrayNode = (ArrayNode) iVisited.getArgsNode(); iter = arrayNode.childNodes().iterator(); } else if (argsNode == null) { // Block? Node iterNode = null; if (iVisited instanceof FCallNode) { FCallNode fcall = (FCallNode) iVisited; iterNode = fcall.getIterNode(); } else if (iVisited instanceof CallNode) { CallNode call = (CallNode) iVisited; iterNode = call.getIterNode(); } if (iterNode == null) return arguments; if (iterNode instanceof IterNode) { // yup, it has a block IterNode yeah = (IterNode) iterNode; Node varNode = yeah.getVarNode(); if (varNode instanceof DAsgnNode) { // single variable in block DAsgnNode dassgn = (DAsgnNode) varNode; arguments.add(dassgn); } else if (varNode instanceof MultipleAsgnNode) { // multiple variables in block MultipleAsgnNode multi = (MultipleAsgnNode) varNode; ListNode list = multi.getHeadNode(); if (list != null) iter = list.childNodes().iterator(); else { Node multiArgsNode = multi.getArgsNode(); if (multiArgsNode instanceof DAsgnNode) { // single variable in block DAsgnNode dassgn = (DAsgnNode) multiArgsNode; arguments.add(dassgn); } } } } } if (iter == null) return arguments; for (; iter.hasNext();) { Node argument = (Node) iter.next(); arguments.add(argument); } return arguments; } /** * Build up the fully qualified name of the super class for a class declaration * * @param superNode * @return */ public static String getSuperClassName(Node superNode) { if (superNode == null) return OBJECT; return getFullyQualifiedName(superNode); } public static String getFullyQualifiedName(Node node) { if (node == null) return EMPTY_STRING; if (node instanceof ConstNode) { ConstNode constNode = (ConstNode) node; return constNode.getName(); } if (node instanceof Colon2Node) { Colon2Node colonNode = (Colon2Node) node; String prefix = getFullyQualifiedName(colonNode.getLeftNode()); if (prefix.length() > 0) prefix = prefix + NAMESPACE_DELIMETER; return prefix + colonNode.getName(); } return getNameReflectively(node); } public static String getFullyQualifiedTypeName(Node rootNode, Node typeNode) { String namespace = ASTUtil.getNamespace(rootNode, typeNode.getPosition().getStartOffset()); if (typeNode instanceof IScopingNode) // class or module { String typeName = getNameReflectively(typeNode); if (namespace.length() == 0) return typeName; if (!namespace.equals(typeName) && !namespace.endsWith(NAMESPACE_DELIMETER + typeName)) { namespace += NAMESPACE_DELIMETER + typeName; } } return namespace; } public static String getNamespace(Node root, int offset) { List<Node> surrounding = new ArrayList<Node>(); Node typeNode = ClosestSpanningNodeLocator.Instance().findClosestSpanner(root, offset, new INodeAcceptor() { public boolean doesAccept(Node node) { return node instanceof ModuleNode || node instanceof ClassNode; } }); if (typeNode == null) return ""; if (offset < ((IScopingNode) typeNode).getCPath().getPosition().getEndOffset()) { int newStartOffset = typeNode.getPosition().getStartOffset() - 1; if (newStartOffset < 0) newStartOffset = 0; typeNode = ClosestSpanningNodeLocator.Instance().findClosestSpanner(root, newStartOffset, new INodeAcceptor() { public boolean doesAccept(Node node) { return node instanceof ModuleNode || node instanceof ClassNode; } }); } while (typeNode != null) { surrounding.add(0, typeNode); typeNode = ClosestSpanningNodeLocator.Instance().findClosestSpanner(root, typeNode.getPosition().getStartOffset() - 1, new INodeAcceptor() { public boolean doesAccept(Node node) { return node instanceof ModuleNode || node instanceof ClassNode; } }); } StringBuffer buffer = new StringBuffer(); boolean first = true; for (Node node : surrounding) { if (!first) { buffer.append(NAMESPACE_DELIMETER); } buffer.append(getNameReflectively(node)); if (first) { first = false; } } return buffer.toString(); } /** * Used to compare the keys for hashes, ignoring difference between symbols and strings (and bignums, fixnums too) * * @param node * @return */ public static String stringValue(Node node) { if (node instanceof StrNode) { return ((StrNode) node).getValue().toString(); } if (node instanceof SymbolNode) { return ((SymbolNode) node).getName(); } if (node instanceof FixnumNode) { return Long.toString(((FixnumNode) node).getValue()); } if (node instanceof BignumNode) { return ((BignumNode) node).getValue().toString(); } return null; } }