/***** 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> * Copyright (C) 2006 Mirko Stocker <me@misto.ch> * Copyright (C) 2006 Thomas Corbat <tcorbat@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.extractmethod; import java.util.ArrayList; import java.util.Collection; import java.util.LinkedHashMap; import java.util.Map; import java.util.Observable; import org.jruby.ast.BlockNode; import org.jruby.ast.DAsgnNode; import org.jruby.ast.DVarNode; import org.jruby.ast.DefsNode; import org.jruby.ast.LocalAsgnNode; import org.jruby.ast.Node; import org.rubypeople.rdt.refactoring.core.NodeFactory; import org.rubypeople.rdt.refactoring.core.NodeProvider; import org.rubypeople.rdt.refactoring.nodewrapper.LocalNodeWrapper; import org.rubypeople.rdt.refactoring.nodewrapper.VisibilityNodeWrapper; import org.rubypeople.rdt.refactoring.util.NodeUtil; public class ExtractedMethodHelper extends Observable { public static final VisibilityNodeWrapper.METHOD_VISIBILITY DEFAULT_VISIBILITY = VisibilityNodeWrapper.METHOD_VISIBILITY.PRIVATE; private VisibilityNodeWrapper.METHOD_VISIBILITY visibility; private Node selectedNodes; private Collection<LocalNodeWrapper> localNodesNeededAsReturnValues; private Map<Integer, LocalNodeWrapper> afterSelectionNodes; private ArrayList<ExtractedArgument> argsOrdered; private String methodName = ""; //$NON-NLS-1$ private final boolean isStaticMethod; public ExtractedMethodHelper(ExtractMethodConfig config) { selectedNodes = config.getSelectedNodes(); visibility = initVisibility(config.hasEnclosingClassNode()); isStaticMethod = config.getEnclosingMethodNode() instanceof DefsNode; initAfterSelectionNodes(config.getEnclosingScopeNode()); initNeededLocalNodes(); } public Node getSelectedNodes() { return selectedNodes; } private VisibilityNodeWrapper.METHOD_VISIBILITY initVisibility(boolean isDefnNodeInClassNode) { if (isDefnNodeInClassNode) { return DEFAULT_VISIBILITY; } return VisibilityNodeWrapper.METHOD_VISIBILITY.NONE; } private void initAfterSelectionNodes(Node enclosingScopeNode) { Collection<Node> allNodes = NodeProvider.getAllNodes(enclosingScopeNode); afterSelectionNodes = new LinkedHashMap<Integer, LocalNodeWrapper>(); int endPostOfLastSelectedNode = selectedNodes.getPosition().getEndOffset(); boolean isWrongScopeNode = false; int endOfOtherScope = 0; for (Node aktNode : allNodes) { if (NodeUtil.hasScope(aktNode)) { if (aktNode.getPosition().getEndOffset() > endOfOtherScope) { isWrongScopeNode = false; endOfOtherScope = 0; } if (!containsSameNodes(selectedNodes, aktNode)) { isWrongScopeNode = true; int endOfAktNode = aktNode.getPosition().getEndOffset(); if (endOfAktNode > endOfOtherScope) { endOfOtherScope = endOfAktNode; } } } if (isLocalNodeOfEnclosingScope(isWrongScopeNode, aktNode)) { int aktStartOffset = aktNode.getPosition().getStartOffset(); if (aktStartOffset > endPostOfLastSelectedNode) { LocalNodeWrapper localNode = new LocalNodeWrapper(aktNode); afterSelectionNodes.put(Integer.valueOf(localNode.getId()), localNode); } } } } private boolean containsSameNodes(Node selectionScopeNode, Node aktScopeNode) { if(selectionScopeNode.childNodes().isEmpty()) { return false; } Object nodeToFind = selectionScopeNode.childNodes().toArray()[0]; for (Object aktNode : NodeProvider.getAllNodes(aktScopeNode)) { if (aktNode.equals(nodeToFind)) { return true; } } return false; } private boolean isLocalNodeOfEnclosingScope(boolean isWrongScopeNode, Node aktNode) { return !isWrongScopeNode && (NodeUtil.nodeAssignableFrom(aktNode, LocalNodeWrapper.LOCAL_NODES_CLASSES)); } private void initNeededLocalNodes() { Collection<LocalNodeWrapper> allLocalNodes = LocalNodeWrapper.gatherLocalNodes(selectedNodes); Map<String, LocalNodeWrapper> firstOccurrenceIsNotDefinitionLocalNodes = new LinkedHashMap<String, LocalNodeWrapper>(); Map<String, LocalNodeWrapper> localNodesNeededAsReturnValues = new LinkedHashMap<String, LocalNodeWrapper>(); Map<String, LocalNodeWrapper> firstOccurrenceIsDefinitionLocalNodes = new LinkedHashMap<String, LocalNodeWrapper>(); for (LocalNodeWrapper aktLocalNode : allLocalNodes) { String nodeName = getLocalNodeName(aktLocalNode); if (aktLocalNode.isAsgnNode()) { if (!firstOccurrenceIsNotDefinitionLocalNodes.containsKey(nodeName) && !containsOccurrencesOfItself(aktLocalNode)) { firstOccurrenceIsDefinitionLocalNodes.put(nodeName, aktLocalNode); } if (localNodeNeededAfterSelectedNodes(aktLocalNode)) { localNodesNeededAsReturnValues.put(nodeName, aktLocalNode); } } else { if (!firstOccurrenceIsDefinitionLocalNodes.containsKey(nodeName)) { firstOccurrenceIsNotDefinitionLocalNodes.put(nodeName, aktLocalNode); } } } this.localNodesNeededAsReturnValues = localNodesNeededAsReturnValues.values(); argsOrdered = new ArrayList<ExtractedArgument>(); for (LocalNodeWrapper aktArgNode : firstOccurrenceIsNotDefinitionLocalNodes.values()) { argsOrdered.add(new ExtractedArgument(aktArgNode.getId(), getLocalNodeName(aktArgNode))); } } private boolean containsOccurrencesOfItself(LocalNodeWrapper localNode) { String name = getLocalNodeName(localNode); Collection<LocalNodeWrapper> subNodes = LocalNodeWrapper.gatherLocalNodes(localNode.getWrappedNode()); for (LocalNodeWrapper aktSubNode : subNodes) { if (!aktSubNode.equals(localNode) && getLocalNodeName(aktSubNode).equals(name)) { return true; } } return false; } private boolean localNodeNeededAfterSelectedNodes(LocalNodeWrapper localNode) { return afterSelectionNodes.containsKey(Integer.valueOf(localNode.getId())); } public Node getMethodNode(boolean needsNewLineAtBeginOfBlock, boolean needsNewLineAtEndOfBlock) { updateLocalNamesInNamedNodes(selectedNodes); BlockNode blockNode = (BlockNode) (selectedNodes instanceof BlockNode ? selectedNodes : NodeFactory.createBlockNode(NodeFactory.createNewLineNode(selectedNodes))); if (localNodesNeededAsReturnValues.size() > 0) { blockNode.add(getReturnNode()); } Node methodDefinitionNode = null; if(isStaticMethod) { methodDefinitionNode = NodeFactory.createStaticMethodNode(methodName, getInMethodStringMethodArgs(), null, blockNode); } else { methodDefinitionNode = NodeFactory.createMethodNodeWithoutNewline(methodName, NodeFactory.createArgsNode(getInMethodStringMethodArgs()), blockNode); } methodDefinitionNode = NodeFactory.createNewLineNode(methodDefinitionNode); if (visibility.equals(VisibilityNodeWrapper.METHOD_VISIBILITY.NONE)) { return NodeFactory.createBlockNode(needsNewLineAtBeginOfBlock, needsNewLineAtEndOfBlock, methodDefinitionNode); } Node visibilityNode = NodeFactory.createVisibilityNode(visibility, methodName); return NodeFactory.createBlockNode(needsNewLineAtBeginOfBlock, needsNewLineAtEndOfBlock, methodDefinitionNode, visibilityNode); } private void updateLocalNamesInNamedNodes(Node scopeNode) { Collection<LocalNodeWrapper> allLocalNodes = LocalNodeWrapper.gatherLocalNodes(scopeNode); for (LocalNodeWrapper aktLocalNode : allLocalNodes) { updateLocalNameInNamedNode(aktLocalNode); } updateArgsOrderedNames(); } private void updateArgsOrderedNames() { for (ExtractedArgument aktArg : argsOrdered) { aktArg.setOldInExtractedMethodArgName(aktArg.getNewInExtractedMethodArgName()); } } private void updateLocalNameInNamedNode(LocalNodeWrapper aktNode) { String oldName = aktNode.getName(); for (ExtractedArgument aktArg : argsOrdered) { if (aktArg.getOldInExtractedMethodArgName().equals(oldName)) { String newName = aktArg.getNewInExtractedMethodArgName(); aktNode.setName(newName); } } } public ArrayList<String> getLocalOnlyVariables() { ArrayList<String> arguments = new ArrayList<String>(); for (ExtractedArgument arg : argsOrdered) { arguments.add(arg.getOriginalName()); } ArrayList<String> local = new ArrayList<String>(); for(LocalNodeWrapper varNode : LocalNodeWrapper.gatherLocalNodes(getMethodCallNode())) { if (!arguments.contains(varNode.getName())) { local.add(varNode.getName()); } } for (DAsgnNode n : NodeProvider.gatherLocalDAsgnNodes(selectedNodes)) { local.add(n.getName()); } return local; } private Node getReturnNode() { if (localNodesNeededAsReturnValues.size() == 1) { LocalNodeWrapper node = localNodesNeededAsReturnValues.toArray(new LocalNodeWrapper[localNodesNeededAsReturnValues.size()])[0]; DVarNode localNode = NodeFactory.createDVarNode(getLocalNodeName(node)); return NodeFactory.createNewLineNode(localNode); } Collection<Node> localVarNodes = getLocalVarNodes(localNodesNeededAsReturnValues); return NodeFactory.createNewLineNode(NodeFactory.createArrayNode(localVarNodes)); } private Collection<String> getInMethodStringMethodArgs() { Collection<String> args = new ArrayList<String>(); for (ExtractedArgument arg : argsOrdered) { args.add(arg.getNewInExtractedMethodArgName()); } return args; } private Collection<Node> getCallArgs() { Collection<Node> args = new ArrayList<Node>(); for (ExtractedArgument aktArg : argsOrdered) { args.add(NodeFactory.createDVarNode(aktArg.getOriginalName())); } return args; } private Collection<Node> getLocalVarNodes(Collection<LocalNodeWrapper> localNodes) { Collection<Node> arguments = new ArrayList<Node>(); for (LocalNodeWrapper aktLocalNode : localNodes) { if (aktLocalNode.isDVarNode()) { arguments.add(NodeFactory.createDVarNode(aktLocalNode.getName())); } else { arguments.add(NodeFactory.createLocalVarNode(aktLocalNode.getName())); } } return arguments; } public Node getMethodCallNode() { Node methodCallNode = NodeFactory.createMethodCallNode(methodName, getCallArgs()); if (localNodesNeededAsReturnValues.size() > 0) { return getAsgnNode(methodCallNode); } return methodCallNode; } private Node getAsgnNode(Node methodCallNode) { if (localNodesNeededAsReturnValues.size() == 1) { LocalNodeWrapper firstReturnNode = localNodesNeededAsReturnValues.toArray(new LocalNodeWrapper[localNodesNeededAsReturnValues.size()])[0]; return getLocalAsgnNode(firstReturnNode, methodCallNode); } Collection<Node> localAsgnNodes = getLocalAsgnNodesForMultipleAsgnNode(); return NodeFactory.createMultipleAsgnNode(localAsgnNodes, methodCallNode); } private LocalAsgnNode getLocalAsgnNode(LocalNodeWrapper localNode, Node valueNode) { String name = getLocalNodeName(localNode); return NodeFactory.createLocalAsgnNode(name, localNode.getId(), valueNode); } private Collection<Node> getLocalAsgnNodesForMultipleAsgnNode() { Collection<Node> result = new ArrayList<Node>(); for (LocalNodeWrapper aktNode : localNodesNeededAsReturnValues) { result.add(NodeFactory.createLocalAsgnNode(getLocalNodeName(aktNode), aktNode.getId(), null)); } return result; } private String getLocalNodeName(LocalNodeWrapper node) { return LocalNodeWrapper.getLocalNodeName(node); } public Collection<String> getArguments() { return getInMethodStringMethodArgs(); } public boolean hasArguments() { return !argsOrdered.isEmpty(); } public void changeParameter(int fromId, int toId) { ExtractedArgument aktArg = argsOrdered.remove(fromId); argsOrdered.add(toId, aktArg); setChanged(); notifyObservers(); } public void changeParameter(int id, String name) { ExtractedArgument aktArg = argsOrdered.get(id); aktArg.setNewInExtractedMethodArgName(name); setChanged(); notifyObservers(); } public void setVisibility(VisibilityNodeWrapper.METHOD_VISIBILITY v) { visibility = v; } public VisibilityNodeWrapper.METHOD_VISIBILITY getVisibility() { return visibility; } public String getMethodName() { return methodName; } public void setMethodName(String methodName) { this.methodName = methodName; setChanged(); notifyObservers(); } }