/***** BEGIN LICENSE BLOCK ***** * Version: CPL 1.0/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Common Public * License Version 1.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.eclipse.org/legal/cpl-v10.html * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * Copyright (C) 2006 Lukas Felber <lfelber@hsr.ch> * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the CPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the CPL, the GPL or the LGPL. ***** END LICENSE BLOCK *****/ package org.rubypeople.rdt.refactoring.core; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Comparator; import java.util.Iterator; import java.util.List; import java.util.TreeSet; import org.jruby.CompatVersion; import org.jruby.ast.ArgsNode; import org.jruby.ast.ArrayNode; import org.jruby.ast.BlockNode; import org.jruby.ast.ClassVarAsgnNode; import org.jruby.ast.ClassVarNode; import org.jruby.ast.CommentNode; import org.jruby.ast.DAsgnNode; import org.jruby.ast.DefnNode; import org.jruby.ast.DefsNode; import org.jruby.ast.FCallNode; import org.jruby.ast.InstAsgnNode; import org.jruby.ast.InstVarNode; import org.jruby.ast.IterNode; import org.jruby.ast.LocalAsgnNode; import org.jruby.ast.MethodDefNode; import org.jruby.ast.NewlineNode; import org.jruby.ast.NilImplicitNode; import org.jruby.ast.Node; import org.jruby.ast.RootNode; import org.jruby.ast.SymbolNode; import org.jruby.ast.WhileNode; import org.jruby.ast.types.INameNode; import org.jruby.lexer.yacc.SyntaxException; import org.jruby.parser.ParserConfiguration; import org.jruby.util.KCode; import org.rubypeople.rdt.internal.core.parser.RubyParser; import org.rubypeople.rdt.internal.core.parser.RubyParserWithComments; import org.rubypeople.rdt.refactoring.nodewrapper.AttrAccessorNodeWrapper; import org.rubypeople.rdt.refactoring.nodewrapper.FieldNodeWrapper; import org.rubypeople.rdt.refactoring.nodewrapper.MethodCallNodeWrapper; import org.rubypeople.rdt.refactoring.util.NodeUtil; /** * This class would make more sense integrated into the Class org.jruby.ast.Node. It is not (yet) implemented there * because we like to keep our code seperated from JRuby. */ public class NodeProvider { private static final Class[] EMPTY_NODES = { NewlineNode.class, BlockNode.class, ArrayNode.class, ArgsNode.class, IterNode.class, WhileNode.class }; public static Collection<Node> getChildren(Node enclosingNode) { Iterator<Node> it = enclosingNode.childNodes().iterator(); Collection<Node> children = new ArrayList<Node>(); while (it.hasNext()) children.add(it.next()); return children; } public static boolean hasSyntaxErrors(String fileName, String fileContent) { try { parseFile(fileName, fileContent); return false; } catch (SyntaxException e) { return true; } } private static RootNode parseFile(String fileName, String fileContent) { RubyParser parser = new RubyParserWithComments() { @Override protected ParserConfiguration getParserConfig() { // Refactoring code expects line numbers to be 1 based, no 0-based return new ParserConfiguration(KCode.NIL, 1, true, false, CompatVersion.RUBY1_8); } }; return (RootNode) parser.parse(fileName, fileContent, true /* bypass cache */).getAST(); } public static RootNode getRootNode(String fileName, String fileContent) { try { return (fileContent != null) ? parseFile(fileName, fileContent) : null; } catch (SyntaxException e) { return null; } } public static Collection<Node> getAttributeNodes(Node parent) { Collection<Node> attrNodes = getSubNodes(parent, InstAsgnNode.class, InstVarNode.class); attrNodes.addAll(getAttrListNodes(parent)); TreeSet<Node> attrNodesNoDuplicates = new TreeSet<Node>(new Comparator<Node>() { public int compare(Node node0, Node node1) { return getName(node0).compareTo(getName(node1)); } private String getName(Node node) { return ((INameNode) node).getName(); } }); attrNodesNoDuplicates.addAll(attrNodes); return attrNodesNoDuplicates; } private static Collection<Node> getAttrListNodes(Node parent) { Collection<Node> result = new ArrayList<Node>(); Collection<Node> fCallNodes = getSubNodes(parent, FCallNode.class); for (Node node : fCallNodes) { FCallNode fCallNode = (FCallNode) node; if (fCallNode.getName().equals("attr")) { //$NON-NLS-1$ result.addAll(getSubNodes(fCallNode.getArgsNode(), SymbolNode.class)); } } return result; } public static Collection<AttrAccessorNodeWrapper> getAccessorNodes(Node parent) { Collection<Node> callNodes = getSubNodes(parent, FCallNode.class); Collection<AttrAccessorNodeWrapper> accessorNodes = new ArrayList<AttrAccessorNodeWrapper>(); for (Node node : callNodes) { FCallNode fCallNode = (FCallNode) node; if (isAccessorNode(fCallNode)) { addAccessorNodes(accessorNodes, fCallNode); } } return accessorNodes; } private static void addAccessorNodes(Collection<AttrAccessorNodeWrapper> accessorNodes, FCallNode callNode) { if (NodeUtil.nodeAssignableFrom(callNode.getArgsNode(), ArrayNode.class)) { for (Object o : callNode.getArgsNode().childNodes()) { Node aktNode = (Node) o; if (NodeUtil.nodeAssignableFrom(aktNode, SymbolNode.class)) { SymbolNode symbolNode = ((SymbolNode) aktNode); accessorNodes.add(new AttrAccessorNodeWrapper(callNode, symbolNode)); } } } } public static boolean isAccessorNode(FCallNode fCallNode) { if (!hasAccessorName(fCallNode)) { return false; } if (NodeUtil.nodeAssignableFrom(fCallNode.getArgsNode(), ArrayNode.class)) { ArrayNode arrayNode = (ArrayNode) fCallNode.getArgsNode(); for (Object o : arrayNode.childNodes()) { Node aktNode = (Node) o; if (!NodeUtil.nodeAssignableFrom(aktNode, SymbolNode.class)) { return false; } } } else { return false; } return true; } private static boolean hasAccessorName(FCallNode callNode) { String name = callNode.getName(); return (name.equals(AttrAccessorNodeWrapper.ATTR_ACCESSOR) || name.equals(AttrAccessorNodeWrapper.ATTR_READER) || name .equals(AttrAccessorNodeWrapper.ATTR_WRITER)); } public static Node getLastChildNode(Node parent) { return getLastChildNode(parent, Node.class); } public static Node getLastChildNode(Node parent, Class<? extends Node> childClass) { Node lastMatch = null; Collection<Node> childList = getChildren(parent); for (Node o : childList) { Node node = unwrap(o); if (childClass.isAssignableFrom(node.getClass())) lastMatch = node; } return lastMatch; } public static boolean hasChildNode(Node parent, Class<? extends Node> childClass) { return getFirstChildNode(parent, childClass) != null; } public static Node getFirstChildNode(Node parent, Class<? extends Node> childClass) { Collection<Node> childList = getChildren(parent); for (Node o : childList) { Node node = unwrap(o); if (childClass.isAssignableFrom(node.getClass())) return node; } return null; } /** * Returns the parent of a given node. */ public static Node findParentNode(Node rootNode, Node child) { if (child == null) return null; Collection<Node> allNodes = getAllNodes(rootNode); for (Node node : allNodes) { if (containsNode(node.childNodes(), child)) { return node; } } return null; } public static Node findParentNode(Node rootNode, Node child, Class type) { while ((child = findParentNode(rootNode, child)) != null && !(child instanceof RootNode)) { if (child.getClass().isAssignableFrom(type)) { return child; } } return null; } private static boolean containsNode(List<Node> list, Node node) { for (Node child : list) { if (child.equals(NilImplicitNode.NIL)) continue; if (child.getPosition().getStartOffset() == node.getPosition().getStartOffset() && child.getPosition().getEndOffset() == node.getPosition().getEndOffset()) { return true; } } return false; } public static Node getNextNode(Node parentNode, Node node) { boolean match = false; Collection<Node> childList = getChildren(parentNode); for (Node o : childList) { Node aktNode = unwrap(o); if (match) return aktNode; if (node.equals(aktNode)) match = true; } return null; } public static Node getNodeBefore(Node parentNode, Node node) { Node nodeBefore = null; Collection<Node> childList = getChildren(parentNode); for (Node o : childList) { Node aktNode = unwrap(o); if (aktNode.equals(node) && !(aktNode instanceof CommentNode)) return nodeBefore; else if (!(aktNode instanceof CommentNode)) nodeBefore = aktNode; } return null; } public static Node unwrap(Node node) { if (node instanceof NewlineNode) node = ((NewlineNode) node).getNextNode(); return node; } public static boolean hasNodeBefore(Node parentNode, Node node) { return getNodeBefore(parentNode, node) != null; } public static Collection<Node> getAllNodes(Node parentNode) { Collection<Node> allNodes = new ArrayList<Node>(); if (parentNode != null) { allNodes.add(parentNode); for (Object o : parentNode.childNodes()) { Node node = (Node) o; allNodes.addAll(getAllNodes(node)); } } return allNodes; } public static Collection<MethodDefNode> getMethodNodes(Node parentNode) { Collection<Node> subNodes = getSubNodes(parentNode, DefnNode.class, DefsNode.class); return Arrays.asList(subNodes.toArray(new MethodDefNode[subNodes.size()])); } public static Collection<FCallNode> getLoadAndRequireNodes(Node rootNode) { Collection<FCallNode> loadAndRequireNodes = new ArrayList<FCallNode>(); Collection<Node> fCallNodes = NodeProvider.getSubNodes(rootNode, FCallNode.class); for (Node node : fCallNodes) { FCallNode fCallNode = (FCallNode) node; if (isLoadOrRequireNode(fCallNode)) loadAndRequireNodes.add(fCallNode); } return loadAndRequireNodes; } private static boolean isLoadOrRequireNode(FCallNode fCallNode) { return fCallNode.getName().equalsIgnoreCase("load") || fCallNode.getName().equalsIgnoreCase("require"); //$NON-NLS-1$ //$NON-NLS-2$ } public static Collection<LocalAsgnNode> gatherLocalAsgnNodes(Node baseNode) { Collection<Node> nodes = gatherNodesOfTypeInAktScopeNode(baseNode, LocalAsgnNode.class); LocalAsgnNode[] asgnNodes = nodes.toArray(new LocalAsgnNode[nodes.size()]); Collection<LocalAsgnNode> localAsgnNodes = Arrays.asList(asgnNodes); return localAsgnNodes; } public static Collection<DAsgnNode> gatherLocalDAsgnNodes(Node baseNode) { Collection<Node> nodes = gatherNodesOfTypeInAktScopeNode(baseNode, DAsgnNode.class); Collection<DAsgnNode> dAsgnNodes = Arrays.asList(nodes.toArray(new DAsgnNode[nodes.size()])); return dAsgnNodes; } public static Collection<Node> gatherNodesOfTypeInAktScopeNode(Node baseNode, Class... klasses) { ArrayList<Node> candidates = new ArrayList<Node>(); if (NodeUtil.nodeAssignableFrom(baseNode, klasses)) { candidates.add(baseNode); } if (baseNode != null && !NodeUtil.hasScope(baseNode)) { for (Object o : baseNode.childNodes()) { Node n = (Node) o; candidates.addAll(gatherNodesOfTypeInAktScopeNode(n, klasses)); } } return candidates; } public static Collection<Node> getSubNodes(Node baseNode, Class... klasses) { Collection<Node> allNodes = getAllNodes(baseNode); Collection<Node> resultNodes = new ArrayList<Node>(); for (Node aktNode : allNodes) { if (NodeUtil.nodeAssignableFrom(aktNode, klasses)) { resultNodes.add(aktNode); } } return resultNodes; } public static boolean hasSubNodes(Node baseNode, Class... klasses) { return !getSubNodes(baseNode, klasses).isEmpty(); } public static Node getEnclosingNodeOfType(Node baseNode, Node enclosedNode, Class... klasses) { return SelectionNodeProvider.getSelectedNodeOfType(baseNode, enclosedNode.getPosition().getStartOffset(), klasses); } public static Collection<MethodDefNode> gatherMethodDefinitionNodes(Node enclosingScopeNode) { Collection<Node> nodes = gatherNodesOfTypeInAktScopeNode(enclosingScopeNode, DefnNode.class, DefsNode.class); return Arrays.asList(nodes.toArray(new MethodDefNode[nodes.size()])); } public static Collection<Node> getInstFieldOccurences(Node node) { Collection<Node> allOccurences = getSubNodes(node, InstAsgnNode.class, InstVarNode.class); allOccurences.addAll(getAttrListNodes(node)); return allOccurences; } public static Collection<Node> getClassFieldOccurences(Node decoratedNode) { return getSubNodes(decoratedNode, ClassVarAsgnNode.class, ClassVarNode.class); } public static boolean isEmptyNode(Node node) { if (node == null) { return true; } if (!NodeUtil.nodeAssignableFrom(node, EMPTY_NODES)) { return false; } for (Object o : node.childNodes()) { Node aktChild = (Node) o; if (!isEmptyNode(aktChild)) { return false; } } return true; } public static Collection<MethodCallNodeWrapper> getMethodCallNodes(Node baseNode) { Collection<Node> callNodes = getSubNodes(baseNode, MethodCallNodeWrapper.METHOD_CALL_NODE_CLASSES()); Collection<MethodCallNodeWrapper> callNode = new ArrayList<MethodCallNodeWrapper>(); for (Node aktCallNode : callNodes) { callNode.add(new MethodCallNodeWrapper(aktCallNode)); } return callNode; } public static Collection<FieldNodeWrapper> getFieldNodes(Node baseNode) { Collection<Node> fieldNodes = getSubNodes(baseNode, FieldNodeWrapper.fieldNodeClasses()); Collection<FieldNodeWrapper> fields = new ArrayList<FieldNodeWrapper>(); for (Node aktFieldNode : fieldNodes) { fields.add(new FieldNodeWrapper(aktFieldNode)); } return fields; } }