/* * Copyright 2003-2014 the original author or authors. * * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.codehaus.groovy.eclipse.refactoring.core.convert; import java.util.List; import java.util.Set; import org.codehaus.groovy.ast.ClassCodeVisitorSupport; import org.codehaus.groovy.ast.ClassNode; import org.codehaus.groovy.ast.ModuleNode; import org.codehaus.groovy.ast.Variable; import org.codehaus.groovy.ast.expr.BinaryExpression; import org.codehaus.groovy.ast.expr.ClassExpression; import org.codehaus.groovy.ast.expr.ClosureExpression; import org.codehaus.groovy.ast.expr.ConstantExpression; import org.codehaus.groovy.ast.expr.Expression; import org.codehaus.groovy.ast.expr.ListExpression; import org.codehaus.groovy.ast.expr.MapExpression; import org.codehaus.groovy.ast.expr.MethodCallExpression; import org.codehaus.groovy.ast.expr.StaticMethodCallExpression; import org.codehaus.groovy.ast.expr.VariableExpression; import org.codehaus.groovy.ast.stmt.ExpressionStatement; import org.codehaus.groovy.ast.stmt.ReturnStatement; import org.codehaus.groovy.eclipse.codebrowsing.requestor.Region; import org.codehaus.groovy.eclipse.core.GroovyCore; import org.codehaus.groovy.eclipse.core.util.VisitCompleteException; import org.codehaus.groovy.eclipse.refactoring.core.utils.ASTTools; import org.codehaus.jdt.groovy.model.GroovyCompilationUnit; import org.eclipse.jdt.core.NamingConventions; import org.eclipse.jface.text.IDocument; import org.eclipse.swt.graphics.Point; import org.eclipse.text.edits.InsertEdit; import org.eclipse.text.edits.MultiTextEdit; import org.eclipse.text.edits.TextEdit; /** * Common class to process the assign to new local variable refactoring. * Used by both the completion proposal and the refactor menu option. * * @author Stephanie Van Dyk sevandyk@gmail.com * @created April 15, 2012 */ // FIXGWD: This class should be converted into a proper refactoring class which // extends Refactoring. public class AssignStatementToNewLocalRefactoring { private final GroovyCompilationUnit unit; private final int length; private final int offset; // for setting new selection private int newLength; private int newOffset; private boolean atExpressionStatement; private Region region; private Expression expression; public AssignStatementToNewLocalRefactoring(GroovyCompilationUnit unit, int offset) { this.unit = unit; length = 0; this.offset = offset; } public void applyRefactoring(IDocument document) { if (atExpressionStatement) { TextEdit thisEdit = findReplacement(document); try { if (thisEdit != null) { thisEdit.apply(document); } } catch (Exception e) { GroovyCore.logException("Oops.", e); } } } public boolean isApplicable() { if (unit == null) { return false; } return this.atExpressionStatement(); } private boolean atExpressionStatement() { region = new Region(offset, length); atExpressionStatement = false; ClassCodeVisitorSupport visitor = new ClassCodeVisitorSupport() { private void processExpression(Expression statementExpression) { if (statementExpression instanceof org.codehaus.groovy.ast.expr.BinaryExpression) { BinaryExpression bexp = (BinaryExpression) statementExpression; if (!bexp.getOperation().getText().equals("=")) { expression = statementExpression; atExpressionStatement = true; } else if (bexp.getRightExpression() instanceof ClosureExpression) { return; } else { throw new VisitCompleteException(); } } else { expression = statementExpression; atExpressionStatement = true; } } @Override public void visitExpressionStatement(ExpressionStatement statement) { if (region.regionIsCoveredByNode(statement)) { processExpression(statement.getExpression()); } super.visitExpressionStatement(statement); } @Override public void visitReturnStatement(ReturnStatement statement) { if (region.regionIsCoveredByNode(statement)) { processExpression(statement.getExpression()); } super.visitReturnStatement(statement); } }; ModuleNode moduleNode = unit.getModuleNode(); List<ClassNode> classes = moduleNode.getClasses(); if (classes.isEmpty()) { classes.add(moduleNode.getScriptClassDummy()); } for (ClassNode classNode : classes) { try { visitor.visitClass(classNode); } catch (VisitCompleteException expected) { break; } } return atExpressionStatement; } private TextEdit findReplacement(IDocument doc) { try { return createEdit(doc, expression); } catch (Exception e) { GroovyCore.logException("Exception during assign statement to local variable.", e); return null; } } private TextEdit createEdit(IDocument doc, Expression expression) { TextEdit edit = new MultiTextEdit(); String candidate; if (expression instanceof ConstantExpression) { candidate = ((ConstantExpression) expression).getText(); } else if (expression instanceof VariableExpression) { candidate = ((VariableExpression) expression).getName(); } else if (expression instanceof ClassExpression) { candidate = ((ClassExpression) expression).getType().getNameWithoutPackage(); } else if (expression instanceof MethodCallExpression) { candidate = ((MethodCallExpression) expression).getMethodAsString(); } else if (expression instanceof StaticMethodCallExpression) { candidate = ((StaticMethodCallExpression) expression).getMethod(); } else if (expression instanceof MapExpression) { candidate = "map"; } else if (expression instanceof ListExpression) { candidate = "list"; } else { candidate = "temp"; } Set<Variable> vars = ASTTools.getVariablesInScope(unit.getModuleNode(), expression); String[] variableNames = new String[vars.size()]; int i = 0; for (Variable v : vars) { variableNames[i] = v.getName(); i++; } String[] names = NamingConventions.suggestVariableNames(NamingConventions.VK_LOCAL, NamingConventions.BK_NAME, candidate, null, 0, variableNames, true); edit.addChild(new InsertEdit(expression.getStart(), "def " + names[0] + " = ")); // add 4 for "def ". newOffset = expression.getStart() + 4; newLength = names[0].length(); return edit; } public Point getNewSelection() { return new Point(newOffset, newLength); } }