/* * Copyright 2009-2017 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.extract; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.codehaus.groovy.ast.ASTNode; import org.codehaus.groovy.ast.AnnotatedNode; import org.codehaus.groovy.ast.ClassNode; import org.codehaus.groovy.ast.FieldNode; import org.codehaus.groovy.ast.MethodNode; 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.CastExpression; import org.codehaus.groovy.ast.expr.ConstantExpression; import org.codehaus.groovy.ast.expr.Expression; 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.BlockStatement; import org.codehaus.groovy.ast.stmt.Statement; import org.codehaus.groovy.eclipse.codebrowsing.fragments.ASTFragmentKind; import org.codehaus.groovy.eclipse.codebrowsing.fragments.BinaryExpressionFragment; import org.codehaus.groovy.eclipse.codebrowsing.fragments.FragmentVisitor; import org.codehaus.groovy.eclipse.codebrowsing.fragments.IASTFragment; import org.codehaus.groovy.eclipse.codebrowsing.fragments.MethodCallFragment; import org.codehaus.groovy.eclipse.codebrowsing.fragments.PropertyExpressionFragment; import org.codehaus.groovy.eclipse.codebrowsing.fragments.SimpleExpressionASTFragment; import org.codehaus.groovy.eclipse.codebrowsing.requestor.Region; import org.codehaus.groovy.eclipse.codebrowsing.selection.FindAllOccurrencesVisitor; import org.codehaus.groovy.eclipse.codebrowsing.selection.FindSurroundingNode; import org.codehaus.groovy.eclipse.codebrowsing.selection.FindSurroundingNode.VisitKind; import org.codehaus.groovy.eclipse.core.GroovyCore; import org.codehaus.groovy.eclipse.refactoring.core.utils.ASTTools; import org.codehaus.groovy.eclipse.refactoring.formatter.DefaultGroovyFormatter; import org.codehaus.groovy.eclipse.refactoring.formatter.FormatterPreferences; import org.codehaus.groovy.syntax.Types; import org.codehaus.jdt.groovy.model.GroovyCompilationUnit; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.SubProgressMonitor; import org.eclipse.jdt.core.ICompilationUnit; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IMember; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.NamingConventions; import org.eclipse.jdt.core.SourceRange; import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.core.refactoring.CompilationUnitChange; import org.eclipse.jdt.core.refactoring.descriptors.ExtractLocalDescriptor; import org.eclipse.jdt.internal.core.refactoring.descriptors.RefactoringSignatureDescriptorFactory; import org.eclipse.jdt.internal.corext.refactoring.Checks; import org.eclipse.jdt.internal.corext.refactoring.JDTRefactoringDescriptorComment; import org.eclipse.jdt.internal.corext.refactoring.JavaRefactoringArguments; import org.eclipse.jdt.internal.corext.refactoring.JavaRefactoringDescriptorUtil; import org.eclipse.jdt.internal.corext.refactoring.RefactoringCoreMessages; import org.eclipse.jdt.internal.corext.refactoring.base.JavaStatusContext; import org.eclipse.jdt.internal.corext.refactoring.util.ResourceUtil; import org.eclipse.jdt.internal.corext.util.JavaConventionsUtil; import org.eclipse.jdt.internal.corext.util.Messages; import org.eclipse.jdt.internal.ui.viewsupport.BasicElementLabels; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.Document; import org.eclipse.jface.text.IDocument; import org.eclipse.ltk.core.refactoring.Change; import org.eclipse.ltk.core.refactoring.Refactoring; import org.eclipse.ltk.core.refactoring.RefactoringChangeDescriptor; import org.eclipse.ltk.core.refactoring.RefactoringDescriptor; import org.eclipse.ltk.core.refactoring.RefactoringStatus; import org.eclipse.ltk.core.refactoring.RefactoringStatusContext; import org.eclipse.ltk.core.refactoring.TextEditChangeGroup; import org.eclipse.text.edits.InsertEdit; import org.eclipse.text.edits.MalformedTreeException; import org.eclipse.text.edits.MultiTextEdit; import org.eclipse.text.edits.ReplaceEdit; import org.eclipse.text.edits.TextEditGroup; /** * @author Andrew Eisenberg */ public class ExtractGroovyLocalRefactoring extends Refactoring { private static final String[] KNOWN_METHOD_NAME_PREFIXES= {"get", "is", "to", "set"}; private static final String ATTRIBUTE_REPLACE = "replace"; private IASTFragment selectedExpression; private GroovyCompilationUnit unit; private ModuleNode module; private int start = -1, length = -1; private String localName; private boolean replaceAllOccurrences; private CompilationUnitChange change; private Map<IASTFragment, List<IASTFragment>> allParentStack; private List<IASTFragment> matchingFragments; public ExtractGroovyLocalRefactoring(JavaRefactoringArguments arguments, RefactoringStatus status) { } public ExtractGroovyLocalRefactoring( GroovyCompilationUnit unit, int offset, int length) { this.unit = unit; this.start = offset; this.length = length; this.module = unit.getModuleNode(); } @Override public RefactoringStatus checkInitialConditions(IProgressMonitor pm) throws CoreException { try { pm.beginTask("", 6); IASTFragment expr = getSelectedFragment(); if (expr == null) { return RefactoringStatus.createFatalErrorStatus("Must select a full expression", JavaStatusContext.create(unit, new SourceRange(start, length))); } int trimmedLength = expr.getTrimmedLength(unit); int exprLength = expr.getLength(); // problem is that some expressions include whitespace in the end of // their sloc. // need to handle this case. // the selected length must be somewhere >= the trimmed (no // whitespace) length and <= the non-trimeed (w/ whitespace) length if (expr.getStart() != start || length > exprLength || length < trimmedLength) { return RefactoringStatus.createFatalErrorStatus("Must select a full expression", JavaStatusContext.create(unit, new SourceRange(start, length))); } RefactoringStatus result = Checks.validateModifiesFiles(ResourceUtil.getFiles(new ICompilationUnit[] { unit }), getValidationContext()); if (result.hasFatalError()) { return result; } if (module == null) { result.addFatalError("Cannot build module node for file. Possible syntax error."); return result; } if (getSelectedFragment() == null) { result.addFatalError("Illegal expression selected"); } result.merge(checkSelection(new SubProgressMonitor(pm, 3))); // if (!result.hasFatalError() && isLiteralNodeSelected()) { // replaceAllOccurrences = false; // } return result; } finally { pm.done(); } } @Override public RefactoringStatus checkFinalConditions(IProgressMonitor pm) throws CoreException { try { pm.beginTask(RefactoringCoreMessages.ExtractTempRefactoring_checking_preconditions, 4); RefactoringStatus result = new RefactoringStatus(); change = doCreateChange(result, new SubProgressMonitor(pm, 2)); if (getExcludedVariableNames().contains(localName)) { result.addWarning(Messages.format(RefactoringCoreMessages.ExtractTempRefactoring_another_variable, BasicElementLabels.getJavaElementName(localName))); } result.merge(checkMatchingFragments()); change.setKeepPreviewEdits(true); return result; } finally { pm.done(); } } /** * Finds all variable names that are currently in use * in the scope */ private Set<String> getExcludedVariableNames() { Set<String> usedNames = new HashSet<String>(); if (getSelectedFragment() != null) { Set<Variable> vars = ASTTools.getVariablesInScope(module, getSelectedFragment().getAssociatedExpression()); for (Variable v : vars) { usedNames.add(v.getName()); } // now check to see if the selected expression itself is a keyword String selectedText = getTextAt(getSelectedFragment().getStart(), getSelectedFragment().getEnd()); if (JavaConventionsUtil.validateIdentifier(selectedText, null) == Status.OK_STATUS) { usedNames.add(selectedText); } } return usedNames; } /** * Ensure that the matching expressions are not LHS of an assignment * * @return * @throws JavaModelException */ private RefactoringStatus checkMatchingFragments() { RefactoringStatus result = new RefactoringStatus(); List<IASTFragment> matchingExprs = getMatchingExpressions(); for (IASTFragment matchingExpr : matchingExprs) { if (isDeclaration(matchingExpr)) { String msg = "The selected expression is a declaration. Extracting may cause an error."; result.addError(msg, JavaStatusContext.create(unit, new SourceRange(matchingExpr.getStart(), matchingExpr .getLength()))); } if (isLeftValue(matchingExpr)) { String msg = RefactoringCoreMessages.ExtractTempRefactoring_assigned_to; result.addWarning(msg, JavaStatusContext.create(unit, new SourceRange(matchingExpr.getStart(), matchingExpr .getLength()))); } } return result; } private boolean isDeclaration(IASTFragment matchingExpr) { Expression associatedExpression = matchingExpr.getAssociatedExpression(); if (associatedExpression instanceof VariableExpression) { if (((VariableExpression) associatedExpression).getAccessedVariable() == associatedExpression) { return true; } } return false; } private boolean isLeftValue(IASTFragment frag) { return frag.kind() == ASTFragmentKind.BINARY && ((BinaryExpressionFragment) frag).getToken().getType() == Types.ASSIGN; } private List<IASTFragment> getMatchingExpressions() { if (matchingFragments != null) { return matchingFragments; } IASTFragment origFragment = getSelectedFragment(); List<IASTFragment> parentStack = getParentStack(origFragment); AnnotatedNode limitTo = null; for (IASTFragment fragment : parentStack) { ASTNode astNode = fragment.getAssociatedNode(); if (astNode instanceof FieldNode) { limitTo = (FieldNode) astNode; break; } if (astNode instanceof MethodNode) { limitTo = (MethodNode) astNode; break; } if (astNode instanceof ClassNode) { limitTo = (ClassNode) astNode; break; } if (astNode instanceof ModuleNode) { limitTo = ((ModuleNode) astNode).getScriptClassDummy(); } } if (replaceAllOccurrences) { FindAllOccurrencesVisitor v = new FindAllOccurrencesVisitor(unit.getModuleNode(), limitTo); matchingFragments = v.findOccurrences(origFragment); } else { matchingFragments = Collections.singletonList(origFragment); } return matchingFragments; } private CompilationUnitChange doCreateChange(RefactoringStatus status, IProgressMonitor pm) throws CoreException { CompilationUnitChange newChange = new CompilationUnitChange("Extract Local Variable", unit); try { pm.beginTask(RefactoringCoreMessages.ExtractTempRefactoring_checking_preconditions, 1); newChange.setEdit(new MultiTextEdit()); createTempDeclaration(newChange, status); if (!status.hasFatalError()) { addReplaceExpressionWithTemp(newChange); } } finally { pm.done(); } return newChange; } /** * Replace all occurrences with the new variable name * * @param newChange */ private void addReplaceExpressionWithTemp(CompilationUnitChange newChange) { List<IASTFragment> matchingExpressions; if (replaceAllOccurrences) { matchingExpressions = getMatchingExpressions(); } else { matchingExpressions = Collections.singletonList(getSelectedFragment()); } for (IASTFragment matchingExpr : matchingExpressions) { TextEditGroup group = new TextEditGroup(RefactoringCoreMessages.ExtractTempRefactoring_replace_occurrences); ReplaceEdit edit = new ReplaceEdit(matchingExpr.getStart(), matchingExpr.getLength(), localName); group.addTextEdit(edit); newChange.addChangeGroup(new TextEditChangeGroup(newChange, group)); newChange.addEdit(edit); } } private void createTempDeclaration(CompilationUnitChange newChange, RefactoringStatus status) { List<IASTFragment> matchingExpressions = replaceAllOccurrences ? getMatchingExpressions() : Collections .singletonList(getSelectedFragment()); int insertLoc = insertAt(matchingExpressions, status); if (insertLoc == -1 && !status.hasFatalError()) { status.addFatalError("Could not find a suitable extraction location", createContext()); } if (status.hasFatalError()) { return; } int lineStart = findLineStart(insertLoc); // prefix the declaration with the same whitespace as the next line String prefix = getTextAt(lineStart, insertLoc); TextEditGroup group = new TextEditGroup(RefactoringCoreMessages.ExtractTempRefactoring_declare_local_variable); InsertEdit edit = new InsertEdit(lineStart, createExpressionText(prefix, status)); group.addTextEdit(edit); newChange.addChangeGroup(new TextEditChangeGroup(newChange, group)); newChange.addEdit(edit); } private String getTextAt(int start, int end) { return String.valueOf(CharOperation.subarray(unit.getContents(), start, end)); } /** * Based on all of the matching expressions, determine where to insert the * declaration. * * @param matchingExpressions * @param decl * @param status * @return */ private int insertAt(List<IASTFragment> matchingExpressions, RefactoringStatus status) { // find the first matching expression. // determine the start of its associated statement // must ensure that it is in the correct code block. uggh // insert at position before statement // find the longest matching parent prefix @SuppressWarnings("unchecked") List<IASTFragment>[] parentsStack = new List[matchingExpressions.size()]; int i = 0; IASTFragment firstExpression = null; for (IASTFragment matchingExpr : matchingExpressions) { if (firstExpression == null || matchingExpr.getStart() < firstExpression.getStart()) { firstExpression = matchingExpr; } parentsStack[i++] = getParentStack(matchingExpr); } IASTFragment[] commonPrefix = getLongestStackPrefix(parentsStack); if (commonPrefix.length == 0) { status.addFatalError("Could not find a common root for extracted occurrences.", createContext()); return -1; } // now we have the common prefix and the first expression. // need to find the first statement in the array. // if it is not a block statement, then we know to insert immediately // before // if it is a block statement, then take the parent stack of the first // statement // and walk up it to find the statement immediately under the block and // insert there Statement firstCommonStatement = null; // it's possible there is no statement here, like if inside a field or // parameter initializer. // in this case, we should fail. for (IASTFragment fragment : commonPrefix) { if (fragment.getAssociatedNode() instanceof Statement) { firstCommonStatement = (Statement) fragment.getAssociatedNode(); break; } } int insertLoc; if (firstCommonStatement instanceof BlockStatement) { // if the first common statement is a block statement, // then we actually want to place the declaration inside the // block. To figure out where, we look at the firstParentStack // and walk to determine where its statement is inside the // containing block statement. insertLoc = -1; List<IASTFragment> firstParentStack = getParentStack(firstExpression); for (int j = 1; j < firstParentStack.size(); j++) { if (firstParentStack.get(j).getAssociatedNode() == firstCommonStatement) { insertLoc = firstParentStack.get(j - 1).getStart(); break; } } } else if (firstCommonStatement != null) { insertLoc = firstCommonStatement.getStart(); } else { // is null insertLoc = -1; } return insertLoc; } private int findLineStart(int insertLoc) { char[] contents = unit.getContents(); while (insertLoc >= 0 && contents[insertLoc] != '\n' && contents[insertLoc] != '\r') { insertLoc--; } return insertLoc; } private IASTFragment[] getLongestStackPrefix(List<IASTFragment>[] parentsStack) { int length = -1; if (parentsStack.length == 0) { return new IASTFragment[0]; // } else if (parentsStack.length == 1) { // return (IASTFragment[]) parentsStack[0].toArray(new IASTFragment[0]); } int minArrayLength = parentsStack[0].size(); for (int i = 1; i < parentsStack.length; i++) { minArrayLength = Math.min(minArrayLength, parentsStack[i].size()); } for (int i = 0; i < minArrayLength; i++) { if (!allStacksEqual(parentsStack, i)) { break; } length++; } if (length <= -1) { return new IASTFragment[0]; } return getStackPrefix(parentsStack[0], length); } /** * Creates a sub-array of <code>length</code> elements */ private IASTFragment[] getStackPrefix(List<IASTFragment> stack, int length) { IASTFragment[] array = new IASTFragment[length + 1]; int i = 0; while (i <= length) { array[length - i] = stack.get(stack.size() - 1 - i); i++; } return array; } /** * Checks to see if the ith element of all stacks are == * * @param parentsStack * @param i * @return true iff all elements are equal in the ith position */ private boolean allStacksEqual(List<IASTFragment>[] parentsStack, int i) { IASTFragment candidate = parentsStack[0].get(parentsStack[0].size() - 1 - i); for (List<IASTFragment> stack : parentsStack) { if (stack.get(stack.size() - 1 - i).getAssociatedNode() != candidate.getAssociatedNode()) { return false; } } return true; } private List<IASTFragment> getParentStack(IASTFragment expr) { if (allParentStack == null) { allParentStack = new HashMap<IASTFragment, List<IASTFragment>>(); } else if (allParentStack.containsKey(expr)) { return allParentStack.get(expr); } FindSurroundingNode find = new FindSurroundingNode(new Region(expr), VisitKind.PARENT_STACK); find.doVisitSurroundingNode(module); List<IASTFragment> parentStack = new ArrayList<IASTFragment>(find.getParentStack()); Collections.reverse(parentStack); allParentStack.put(expr, parentStack); return parentStack; } // private boolean isLiteralNodeSelected() throws JavaModelException { // ASTNode expr = getSelectedFragment().getAssociatedExpression(); // if (expr == null) { // return false; // } // if (expr instanceof ConstantExpression) { // ConstantExpression constExpr = (ConstantExpression) expr; // return constExpr.isEmptyStringExpression() || constExpr.isFalseExpression() || constExpr.isNullExpression() // || constExpr.isTrueExpression() || constExpr.getValue() instanceof Number // || unit.getContents()[constExpr.getStart()] == '"'; // } else { // return false; // } // } private String createExpressionText(String prefix, RefactoringStatus status) { StringBuilder sb = new StringBuilder(); sb.append("def ").append(localName).append(" = ").append( getTextAt(getSelectedFragment().getStart(), getSelectedFragment().getEnd())); IDocument doc = new Document(sb.toString()); DefaultGroovyFormatter formatter = new DefaultGroovyFormatter(doc, new FormatterPreferences(unit), 0); try { formatter.format().apply(doc); } catch (MalformedTreeException e) { GroovyCore.logException("Exception during extract local variable refactoring", e); status.addFatalError(e.getMessage(), createContext()); } catch (BadLocationException e) { GroovyCore.logException("Exception during extract local variable refactoring", e); status.addFatalError(e.getMessage(), createContext()); } return prefix + doc.get(); } private RefactoringStatus checkSelection(IProgressMonitor pm) throws JavaModelException { try { pm.beginTask("", 2); IASTFragment selectedExpression = getSelectedFragment(); if (selectedExpression == null) { String message = RefactoringCoreMessages.ExtractTempRefactoring_select_expression; return RefactoringStatus.createFatalErrorStatus(message, createContext()); } pm.worked(1); RefactoringStatus result= new RefactoringStatus(); result.merge(checkExpression()); if (result.hasFatalError()) return result; pm.worked(1); return result; } finally { pm.done(); } } private RefactoringStatus checkExpression() throws JavaModelException { RefactoringStatus result= new RefactoringStatus(); IASTFragment selectedExpression = getSelectedFragment(); result.merge(checkExpressionFragmentIsRValue(selectedExpression)); if(result.hasFatalError()) return result; if ((selectedExpression instanceof ConstantExpression) && ((ConstantExpression) selectedExpression).isNullExpression()) { result.merge(RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.ExtractTempRefactoring_null_literals)); } return result; } private RefactoringStatus checkExpressionFragmentIsRValue(IASTFragment fragment) throws JavaModelException { if (isDeclaration(fragment)) { return RefactoringStatus.createFatalErrorStatus("Target expression is a variable declaration. Cannot extract."); } if (isLeftValue(fragment)) { return RefactoringStatus.createFatalErrorStatus("Target expression is target of an assignment. Cannot extract."); } return new RefactoringStatus(); } private RefactoringStatusContext createContext() { IJavaElement elt; try { elt = unit.getElementAt(start); if (elt instanceof IMember) { return JavaStatusContext.create((IMember) elt); } } catch (JavaModelException e) { GroovyCore.logException("Error finding refactoring context", e); } return null; } private IASTFragment getSelectedFragment() { if(selectedExpression != null) { return selectedExpression; } FindSurroundingNode finder = new FindSurroundingNode(new Region(start, length), VisitKind.SURROUNDING_NODE); IASTFragment fragment = finder.doVisitSurroundingNode(unit.getModuleNode()); if (ASTFragmentKind.isExpressionKind(fragment)) { selectedExpression = fragment; } return selectedExpression; } // !! similar to ExtractTempRefactoring equivalent public String getSignaturePreview() throws JavaModelException { String space= " "; return "def" + space + localName; } public void setReplaceAllOccurrences(boolean replaceAllOccurrences) { this.replaceAllOccurrences = replaceAllOccurrences; } public boolean isReplaceAllOccurrences() { return replaceAllOccurrences; } public RefactoringStatus checkLocalNameOnChange(String newName) { Assert.isTrue(newName.equals(getLocalName())); String selectedText = getTextAt(getSelectedFragment().getStart(), getSelectedFragment().getEnd()); if (newName.equals(selectedText)) { return RefactoringStatus.createFatalErrorStatus("Extracted variable name must be different from original text"); } return Checks.checkTempName(newName, unit); } public String[] guessLocalNames() { String text = getBaseNameFromExpression(getSelectedFragment()); String[] excludedNames = getExcludedVariableNames().toArray(new String[0]); return NamingConventions.suggestVariableNames(NamingConventions.VK_LOCAL, NamingConventions.BK_NAME, text, unit.getJavaProject(), 0, excludedNames, true); } class GuessBaseNameVisitor extends FragmentVisitor { StringBuilder sb = new StringBuilder(); String getGuessedName() { String name = sb.toString(); if (name.length() == 0) { name = "local"; } else { for (int i = 0; i < KNOWN_METHOD_NAME_PREFIXES.length; i++) { String curr = KNOWN_METHOD_NAME_PREFIXES[i]; if (name.startsWith(curr)) { if (name.equals(curr)) { return "local"; // don't suggest 'get' as variable // name } else if (Character.isUpperCase(name.charAt(curr.length()))) { return name.substring(curr.length()); } } } try { Integer.parseInt(name); name = "_" + name; } catch (NumberFormatException e) { // ignore } } // make first char lower case if (name.length() > 0) { name = "" + Character.toLowerCase(name.charAt(0)) + name.substring(1); } return name; } @Override public boolean visit(BinaryExpressionFragment fragment) { String nextPart = nameFromExpression(fragment.getAssociatedExpression()); if (nextPart != null) { sb.append(nextPart); } return true; } @Override public boolean visit(MethodCallFragment fragment) { String nextPart = nameFromExpression(fragment.getAssociatedExpression()); if (nextPart != null) { sb.append(nextPart); } return true; } @Override public boolean visit(PropertyExpressionFragment fragment) { String nextPart = nameFromExpression(fragment.getAssociatedExpression()); if (nextPart != null) { sb.append(nextPart); } return true; } @Override public boolean visit(SimpleExpressionASTFragment fragment) { String nextPart = nameFromExpression(fragment.getAssociatedExpression()); if (nextPart != null) { sb.append(nextPart); } return true; } private String nameFromExpression(Expression expr) { String name = null; if (expr instanceof org.codehaus.groovy.ast.expr.CastExpression) { expr = ((CastExpression) expr).getExpression(); } if (expr instanceof ConstantExpression) { name = ((ConstantExpression) expr).getText(); } else if (expr instanceof MethodCallExpression) { name = ((MethodCallExpression) expr).getMethodAsString(); } else if (expr instanceof StaticMethodCallExpression) { name = ((StaticMethodCallExpression) expr).getMethod(); } else if (expr instanceof BinaryExpression) { name = nameFromExpression(((BinaryExpression) expr).getLeftExpression()) + nameFromExpression(((BinaryExpression) expr).getRightExpression()); } else if (expr instanceof Variable) { name = ((Variable) expr).getName(); } // always capitalize // the first character will be made lower case later if (name != null && name.length() > 0) { name = "" + Character.toUpperCase(name.charAt(0)) + name.substring(1); } return name; } } private String getBaseNameFromExpression(IASTFragment assignedFragment) { if (assignedFragment == null) { return "local"; } GuessBaseNameVisitor visitor = new GuessBaseNameVisitor(); assignedFragment.accept(visitor); return visitor.getGuessedName(); } public void setLocalName(String newName) { Assert.isNotNull(newName); localName = newName; } public String getLocalName() { return localName; } @Override public String getName() { return "Extract to local variable" + (isReplaceAllOccurrences() ? " (all occurrences)" : ""); } @Override public Change createChange(IProgressMonitor pm) throws CoreException, OperationCanceledException { try { pm.beginTask(RefactoringCoreMessages.ExtractTempRefactoring_checking_preconditions, 1); ExtractLocalDescriptor descriptor = createRefactoringDescriptor(); change.setDescriptor(new RefactoringChangeDescriptor(descriptor)); return change; } finally { pm.done(); } } /** * @return */ private ExtractLocalDescriptor createRefactoringDescriptor() { final Map<String, String> arguments = new HashMap<String, String>(); String project = null; IJavaProject javaProject = unit.getJavaProject(); if (javaProject != null) project = javaProject.getElementName(); final String description = Messages.format(RefactoringCoreMessages.ExtractTempRefactoring_descriptor_description_short, BasicElementLabels.getJavaElementName(localName)); final String expression = getTextAt(getSelectedFragment().getStart(), getSelectedFragment().getEnd()); final String header = Messages.format(RefactoringCoreMessages.ExtractTempRefactoring_descriptor_description, new String[] { BasicElementLabels.getJavaElementName(localName), BasicElementLabels.getJavaCodeString(expression) }); final JDTRefactoringDescriptorComment comment = new JDTRefactoringDescriptorComment(project, this, header); comment.addSetting(Messages.format(RefactoringCoreMessages.ExtractTempRefactoring_name_pattern, BasicElementLabels .getJavaElementName(localName))); comment.addSetting(Messages.format(RefactoringCoreMessages.ExtractTempRefactoring_expression_pattern, BasicElementLabels .getJavaCodeString(expression))); if (replaceAllOccurrences) comment.addSetting(RefactoringCoreMessages.ExtractTempRefactoring_replace_occurrences); final ExtractLocalDescriptor descriptor = RefactoringSignatureDescriptorFactory.createExtractLocalDescriptor(project, description, comment.asString(), arguments, RefactoringDescriptor.NONE); arguments.put(JavaRefactoringDescriptorUtil.ATTRIBUTE_INPUT, JavaRefactoringDescriptorUtil.elementToHandle(project, unit)); arguments.put(JavaRefactoringDescriptorUtil.ATTRIBUTE_NAME, localName); arguments.put(JavaRefactoringDescriptorUtil.ATTRIBUTE_SELECTION, start + " " + length); arguments.put(ATTRIBUTE_REPLACE, Boolean.valueOf(replaceAllOccurrences).toString()); return descriptor; } }