package org.rubypeople.rdt.internal.ui.compare; import java.util.List; import java.util.Stack; import org.jruby.ast.ArrayNode; import org.jruby.ast.ClassNode; import org.jruby.ast.ClassVarAsgnNode; import org.jruby.ast.ClassVarDeclNode; import org.jruby.ast.ConstDeclNode; import org.jruby.ast.DStrNode; import org.jruby.ast.DefnNode; import org.jruby.ast.DefsNode; import org.jruby.ast.FCallNode; import org.jruby.ast.InstAsgnNode; import org.jruby.ast.ModuleNode; import org.jruby.ast.RootNode; import org.jruby.ast.SClassNode; import org.jruby.ast.StrNode; import org.rubypeople.rdt.internal.core.parser.InOrderVisitor; import org.rubypeople.rdt.internal.core.util.ASTUtil; class RubyParseTreeBuilder extends InOrderVisitor { private char[] fBuffer; private Stack<RubyNode> fStack = new Stack<RubyNode>(); private boolean fShowCU; private RubyNode fImportContainer; public RubyParseTreeBuilder(RubyNode root, char[] buffer, boolean showCU) { fBuffer = buffer; fShowCU = showCU; fStack.clear(); fStack.push(root); } /** * Closes the current Ruby node by setting its end position and pops it off * the stack. */ private void pop() { fStack.pop(); } private RubyNode getCurrentContainer() { return fStack.peek(); } /** * Adds a new RubyNode with the given type and name to the current * container. */ private void push(int type, String name, int declarationStart, int length) { while (declarationStart > 0) { char c= fBuffer[declarationStart - 1]; if (c != ' ' && c != '\t') break; declarationStart--; length++; } RubyNode node= new RubyNode(getCurrentContainer(), type, name, declarationStart, length); if (type == RubyNode.SCRIPT) node.setAppendPosition(declarationStart + length + 1); else node.setAppendPosition(declarationStart + length); fStack.push(node); } @Override public Object visitClassNode(ClassNode iVisited) { int start = iVisited.getPosition().getStartOffset(); int end = iVisited.getPosition().getEndOffset(); push(RubyNode.CLASS, ASTUtil.getFullyQualifiedName(iVisited.getCPath()), start, end - start); Object ins = super.visitClassNode(iVisited); pop(); return ins; } @Override public Object visitSClassNode(SClassNode iVisited) { int start = iVisited.getPosition().getStartOffset(); int end = iVisited.getPosition().getEndOffset(); push(RubyNode.CLASS, ASTUtil.getNameReflectively(iVisited.getReceiverNode()), start, end - start); Object ins = super.visitSClassNode(iVisited); pop(); return ins; } @Override public Object visitModuleNode(ModuleNode iVisited) { int start = iVisited.getPosition().getStartOffset(); int end = iVisited.getPosition().getEndOffset(); push(RubyNode.MODULE, ASTUtil.getFullyQualifiedName(iVisited.getCPath()), start, end - start); Object ins = super.visitModuleNode(iVisited); pop(); return ins; } @Override public Object visitRootNode(RootNode iVisited) { if (fShowCU) push(RubyNode.SCRIPT, null, iVisited.getPosition().getStartOffset(), iVisited.getPosition().getEndOffset()); Object ins = super.visitRootNode(iVisited); if (fShowCU) pop(); return ins; } @Override public Object visitDefnNode(DefnNode iVisited) { int start = iVisited.getPosition().getStartOffset(); int end = iVisited.getPosition().getEndOffset(); push(RubyNode.METHOD, iVisited.getName(), start, end - start); Object ins = super.visitDefnNode(iVisited); pop(); return ins; } @Override public Object visitDefsNode(DefsNode iVisited) { int start = iVisited.getPosition().getStartOffset(); int end = iVisited.getPosition().getEndOffset(); push(RubyNode.METHOD, iVisited.getName(), start, end - start); Object ins = super.visitDefsNode(iVisited); pop(); return ins; } public Object visitFCallNode(FCallNode iVisited) { String name = iVisited.getName(); List<String> arguments = getArgumentsFromFunctionCall(iVisited); if (name.equals("require") || name.equals("load")) { addImport(iVisited); } return super.visitFCallNode(iVisited); } private void addImport(FCallNode iVisited) { ArrayNode node = (ArrayNode) iVisited.getArgsNode(); String arg = getString(node); if (arg != null) { int s= node.getPosition().getStartOffset(); int declarationEnd= node.getPosition().getEndOffset(); int l = declarationEnd - s; if (fImportContainer == null) fImportContainer= new RubyNode(getCurrentContainer(), RubyNode.IMPORT_CONTAINER, null, s, l); new RubyNode(fImportContainer, RubyNode.IMPORT, arg, s, l); fImportContainer.setLength(declarationEnd - fImportContainer.getRange().getOffset() + 1); fImportContainer.setAppendPosition(declarationEnd + 2); // FIXME } } /** * @param node * @return */ private String getString(ArrayNode node) { Object tmp = node.childNodes().iterator().next(); if (tmp instanceof DStrNode) { DStrNode dstrNode = (DStrNode) tmp; tmp = dstrNode.childNodes().iterator().next(); } if (tmp instanceof StrNode) { StrNode strNode = (StrNode) tmp; return strNode.getValue().toString(); } return null; } @Override public Object visitInstAsgnNode(InstAsgnNode iVisited) { int start = iVisited.getPosition().getStartOffset(); int end = iVisited.getPosition().getEndOffset(); push(RubyNode.FIELD, iVisited.getName(), start, end - start); Object ins = super.visitInstAsgnNode(iVisited); pop(); return ins; } @Override public Object visitClassVarDeclNode(ClassVarDeclNode iVisited) { int start = iVisited.getPosition().getStartOffset(); int end = iVisited.getPosition().getEndOffset(); push(RubyNode.FIELD, iVisited.getName(), start, end - start); Object ins = super.visitClassVarDeclNode(iVisited); pop(); return ins; } @Override public Object visitClassVarAsgnNode(ClassVarAsgnNode iVisited) { int start = iVisited.getPosition().getStartOffset(); int end = iVisited.getPosition().getEndOffset(); push(RubyNode.FIELD, iVisited.getName(), start, end - start); Object ins = super.visitClassVarAsgnNode(iVisited); pop(); return ins; } @Override public Object visitConstDeclNode(ConstDeclNode iVisited) { int start = iVisited.getPosition().getStartOffset(); int end = iVisited.getPosition().getEndOffset(); push(RubyNode.FIELD, iVisited.getName(), start, end - start); Object ins = super.visitConstDeclNode(iVisited); pop(); return ins; } }