/* * 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.rename.renameLocal; import java.util.List; import org.codehaus.groovy.ast.ASTNode; import org.codehaus.groovy.ast.Variable; import org.codehaus.groovy.eclipse.codebrowsing.requestor.ASTNodeFinder; import org.codehaus.groovy.eclipse.codebrowsing.requestor.Region; import org.codehaus.groovy.eclipse.refactoring.Activator; import org.codehaus.jdt.groovy.model.GroovyCompilationUnit; import org.codehaus.jdt.groovy.model.GroovyNature; import org.eclipse.core.resources.IFile; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; 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.IField; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.ILocalVariable; import org.eclipse.jdt.core.IMethod; import org.eclipse.jdt.core.ISourceRange; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.JavaConventions; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.refactoring.CompilationUnitChange; import org.eclipse.jdt.groovy.search.LocalVariableReferenceRequestor; import org.eclipse.jdt.groovy.search.TypeInferencingVisitorFactory; import org.eclipse.jdt.groovy.search.TypeInferencingVisitorWithRequestor; import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; import org.eclipse.jdt.internal.core.JavaElement; import org.eclipse.jdt.internal.core.LocalVariable; import org.eclipse.jdt.internal.corext.refactoring.RefactoringCoreMessages; import org.eclipse.jdt.internal.corext.refactoring.base.JavaStatusContext; import org.eclipse.jdt.internal.corext.refactoring.rename.JavaRenameProcessor; import org.eclipse.jdt.internal.corext.refactoring.rename.RenameModifications; import org.eclipse.jdt.internal.corext.refactoring.util.ResourceUtil; import org.eclipse.jdt.ui.refactoring.RefactoringSaveHelper; import org.eclipse.jface.text.IRegion; import org.eclipse.ltk.core.refactoring.Change; import org.eclipse.ltk.core.refactoring.RefactoringStatus; import org.eclipse.ltk.core.refactoring.participants.CheckConditionsContext; import org.eclipse.ltk.core.refactoring.participants.RenameArguments; import org.eclipse.text.edits.MultiTextEdit; import org.eclipse.text.edits.ReplaceEdit; import org.eclipse.text.edits.TextEdit; import org.eclipse.text.edits.TextEditGroup; public class GroovyRenameLocalVariableProcessor extends JavaRenameProcessor { Variable variable; GroovyCompilationUnit unit; CompilationUnitChange change; ILocalVariable localVariable; GroovyRenameLocalVariableProcessor(ILocalVariable localVariable, String newName, RefactoringStatus status) { initialize(localVariable, newName, status); } private void initialize(ILocalVariable localVariable, String newName, RefactoringStatus status) { this.localVariable = localVariable; ICompilationUnit unit = (ICompilationUnit) localVariable.getAncestor(IJavaElement.COMPILATION_UNIT); if (unit instanceof GroovyCompilationUnit) { this.unit = (GroovyCompilationUnit) unit; } else { status.merge(RefactoringStatus.createErrorStatus("Expecting a Groovy compilation unit, but instead found " + unit.getElementName())); } if (newName != null && newName.length() > 0) { if (newName.equals(localVariable.getElementName())) { status.merge(RefactoringStatus.createErrorStatus("New name is the same as the old name")); } setNewElementName(newName); } else { status.merge(RefactoringStatus.createErrorStatus("Invalid new name")); } } @Override protected RenameModifications computeRenameModifications() throws CoreException { RenameModifications mods = new RenameModifications(); mods.rename(localVariable, new RenameArguments(getNewElementName(), true)); return mods; } @Override protected RefactoringStatus doCheckFinalConditions(IProgressMonitor pm, CheckConditionsContext context) throws CoreException, OperationCanceledException { // ensure that we are working on a working copy so that we can use == for testing nodes. boolean wasWorkingCopy = true; try { if (!unit.isWorkingCopy()) { unit.becomeWorkingCopy(new SubProgressMonitor(pm, 10)); wasWorkingCopy = false; } List<IRegion> references = findReferences(); change = createEdits(references); RefactoringStatus result = checkShadowing(); return result; } catch (Exception e) { return RefactoringStatus.create(new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Exception creating edits for rename refactoring", e)); } finally { if (!wasWorkingCopy) { unit.discardWorkingCopy(); } } } /** * Checks to see if the new name shadows an existing name. */ private RefactoringStatus checkShadowing() { IType type = (IType) localVariable.getAncestor(IJavaElement.TYPE); if (type != null) { IField maybeShadows = type.getField(getNewElementName()); if (maybeShadows.exists()) { return RefactoringStatus.createWarningStatus("Warning: new variable name " + getNewElementName() + " shadows a field in " + type.getElementName()); } } LocalVariableNameCheckerRequestor requestor = new LocalVariableNameCheckerRequestor(variable, getNewElementName()); TypeInferencingVisitorWithRequestor visitor = new TypeInferencingVisitorFactory().createVisitor(unit); visitor.visitCompilationUnit(requestor); if (requestor.isShadowing()) { IJavaElement parent = localVariable.getParent(); if (parent instanceof IMethod) { return RefactoringStatus.createWarningStatus("Warning: new variable name " + getNewElementName() + " shadows a variable in " + localVariable.getParent().getElementName(), JavaStatusContext.create((IMethod) parent)); } else { return RefactoringStatus.createWarningStatus("Warning: new variable name " + getNewElementName() + " shadows a variable in " + localVariable.getParent().getElementName()); } } return new RefactoringStatus(); } @Override protected String[] getAffectedProjectNatures() throws CoreException { return new String[] {JavaCore.NATURE_ID, GroovyNature.GROOVY_NATURE}; } @Override protected IFile[] getChangedFiles() throws CoreException { return new IFile[] {ResourceUtil.getFile(unit)}; } @Override public int getSaveMode() { return RefactoringSaveHelper.SAVE_NOTHING; } public RefactoringStatus checkNewElementName(String newName) throws CoreException { if (localVariable.getElementName().equals(newName)) { return RefactoringStatus.createErrorStatus("New name must be different from old name"); } else { return RefactoringStatus.create(JavaConventions.validateFieldName( newName, CompilerOptions.VERSION_1_6, CompilerOptions.VERSION_1_6)); } } public String getCurrentElementName() { return localVariable.getElementName(); } public Object getNewElement() throws CoreException { int start = localVariable.getNameRange().getOffset(), until = localVariable.getNameRange().getOffset() + getNewElementName().length() - 1; return new LocalVariable((JavaElement) localVariable.getParent(), getNewElementName(), start, until, start, until, localVariable.getTypeSignature(), null, 0, false); } @Override public RefactoringStatus checkInitialConditions(IProgressMonitor pm) throws CoreException, OperationCanceledException { Variable variable = findVariable(); if (variable == null) { return RefactoringStatus.createErrorStatus("Cannot find local variable " + localVariable.getElementName()); } return new RefactoringStatus(); } @Override public Change createChange(IProgressMonitor pm) throws CoreException, OperationCanceledException { return change; } @Override public Object[] getElements() { return new Object[] { localVariable }; } @Override public String getIdentifier() { return RenameLocalGroovyVariableContribution.ID; } @Override public String getProcessorName() { return "Rename Local Variable (Groovy)"; } @Override public boolean isApplicable() throws CoreException { if (unit == null) { return false; } return true; } private List<IRegion> findReferences() { if (variable == null) { try { variable = findVariable(); } catch (JavaModelException e) { throw new RuntimeException(e); } } LocalVariableReferenceRequestor requestor = new LocalVariableReferenceRequestor(variable, localVariable.getParent()); TypeInferencingVisitorWithRequestor visitor = new TypeInferencingVisitorFactory().createVisitor(unit); visitor.visitCompilationUnit(requestor); return requestor.getReferences(); } private Variable findVariable() throws JavaModelException { ISourceRange sourceRange = localVariable.getSourceRange(); ASTNodeFinder findLocalVar = new ASTNodeFinder(new Region(sourceRange.getOffset(), sourceRange.getLength())); ASTNode node = findLocalVar.doVisit(unit.getModuleNode()); return node instanceof Variable ? (Variable) node : null; } private CompilationUnitChange createEdits(List<IRegion> references) { TextEdit[] allEdits= new TextEdit[references.size()]; int index = 0; for (IRegion region : references) { allEdits[index] = new ReplaceEdit(region.getOffset(), region.getLength(), getNewElementName()); index++; } CompilationUnitChange change = new CompilationUnitChange(RefactoringCoreMessages.RenameTempRefactoring_rename, unit); MultiTextEdit rootEdit= new MultiTextEdit(); change.setEdit(rootEdit); change.setKeepPreviewEdits(true); for (TextEdit edit : allEdits) { rootEdit.addChild(edit); change.addTextEditGroup(new TextEditGroup(RefactoringCoreMessages.RenameTempRefactoring_changeName, edit)); } return change; } }