blob: 9da5b455614c5fc628a7de30690cac86b94cd014 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2017 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.dltk.internal.corext.refactoring.rename;
import java.util.StringTokenizer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.dltk.core.IDLTKLanguageToolkit;
import org.eclipse.dltk.core.IField;
import org.eclipse.dltk.core.ILocalVariable;
import org.eclipse.dltk.core.IMember;
import org.eclipse.dltk.core.IModelElement;
import org.eclipse.dltk.core.IScriptProject;
import org.eclipse.dltk.core.ISourceModule;
import org.eclipse.dltk.core.ISourceRange;
import org.eclipse.dltk.core.ModelException;
import org.eclipse.dltk.core.manipulation.IScriptRefactorings;
import org.eclipse.dltk.core.search.IDLTKSearchConstants;
import org.eclipse.dltk.core.search.IDLTKSearchScope;
import org.eclipse.dltk.core.search.SearchEngine;
import org.eclipse.dltk.core.search.SearchMatch;
import org.eclipse.dltk.core.search.SearchParticipant;
import org.eclipse.dltk.core.search.SearchPattern;
import org.eclipse.dltk.core.search.SearchRequestor;
import org.eclipse.dltk.internal.core.manipulation.Messages;
import org.eclipse.dltk.internal.core.manipulation.ScriptManipulationPlugin;
import org.eclipse.dltk.internal.core.refactoring.descriptors.RenameModelElementDescriptor;
import org.eclipse.dltk.internal.corext.refactoring.RefactoringCoreMessages;
import org.eclipse.dltk.internal.corext.refactoring.ScriptRefactoringArguments;
import org.eclipse.dltk.internal.corext.refactoring.ScriptRefactoringDescriptor;
import org.eclipse.dltk.internal.corext.refactoring.changes.DynamicValidationRefactoringChange;
import org.eclipse.dltk.internal.corext.refactoring.code.ScriptableRefactoring;
import org.eclipse.dltk.internal.corext.refactoring.participants.ScriptProcessors;
import org.eclipse.dltk.internal.corext.refactoring.tagging.IReferenceUpdating;
import org.eclipse.dltk.internal.corext.refactoring.util.ResourceUtil;
import org.eclipse.dltk.internal.corext.refactoring.util.TextChangeManager;
import org.eclipse.dltk.internal.corext.util.SearchUtils;
import org.eclipse.ltk.core.refactoring.Change;
import org.eclipse.ltk.core.refactoring.RefactoringDescriptor;
import org.eclipse.ltk.core.refactoring.RefactoringStatus;
import org.eclipse.ltk.core.refactoring.TextChange;
import org.eclipse.ltk.core.refactoring.participants.CheckConditionsContext;
import org.eclipse.ltk.core.refactoring.participants.RefactoringArguments;
import org.eclipse.ltk.core.refactoring.participants.RenameArguments;
import org.eclipse.text.edits.MalformedTreeException;
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 abstract class RenameModelElementProcessor extends ScriptRenameProcessor implements IReferenceUpdating {
protected IModelElement fModelElement;
protected ISourceModule fCu;
// the following fields are set or modified after the construction
protected boolean fUpdateReferences;
protected String fCurrentName;
// private CompilationUnit fCompilationUnitNode;
// private VariableDeclaration fTempDeclarationNode;
// protected SourceModuleChange fChange;
// private boolean fIsComposite is always false
// private GroupCategorySet fCategorySet;
private TextChangeManager fChangeManager;
// private RenameAnalyzeUtil.LocalAnalyzePackage fLocalAnalyzePackage;
private final IDLTKLanguageToolkit fToolkit;
public RenameModelElementProcessor(IModelElement localVariable, IDLTKLanguageToolkit toolkit) {
fToolkit = toolkit;
fModelElement = localVariable;
fCu = (ISourceModule) fModelElement.getAncestor(IModelElement.SOURCE_MODULE);
fChangeManager = new TextChangeManager(true);
}
@Override
public RefactoringStatus initialize(RefactoringArguments arguments) {
if (!(arguments instanceof ScriptRefactoringArguments))
return RefactoringStatus
.createFatalErrorStatus(RefactoringCoreMessages.InitializableRefactoring_inacceptable_arguments);
final ScriptRefactoringArguments extended = (ScriptRefactoringArguments) arguments;
final String handle = extended.getAttribute(ScriptRefactoringDescriptor.ATTRIBUTE_INPUT);
if (handle != null) {
final IModelElement element = ScriptRefactoringDescriptor.handleToElement(extended.getProject(), handle,
false);
if (element != null && element.exists()) {
if (element.getElementType() == IModelElement.SOURCE_MODULE) {
fCu = (ISourceModule) element;
} else if (element.getElementType() == IModelElement.LOCAL_VARIABLE) {
fModelElement = element;
fCu = (ISourceModule) fModelElement.getAncestor(IModelElement.SOURCE_MODULE);
if (fCu == null)
return ScriptableRefactoring.createInputFatalStatus(element, getProcessorName(),
IScriptRefactorings.RENAME_LOCAL_VARIABLE);
} else
return ScriptableRefactoring.createInputFatalStatus(element, getProcessorName(),
IScriptRefactorings.RENAME_LOCAL_VARIABLE);
} else
return ScriptableRefactoring.createInputFatalStatus(element, getProcessorName(),
IScriptRefactorings.RENAME_LOCAL_VARIABLE);
} else
return RefactoringStatus.createFatalErrorStatus(
Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist,
ScriptRefactoringDescriptor.ATTRIBUTE_INPUT));
final String name = extended.getAttribute(ScriptRefactoringDescriptor.ATTRIBUTE_NAME);
if (name != null && !"".equals(name)) //$NON-NLS-1$
setNewElementName(name);
else
return RefactoringStatus.createFatalErrorStatus(
Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist,
ScriptRefactoringDescriptor.ATTRIBUTE_NAME));
if (fCu != null && fModelElement == null) {
final String selection = extended.getAttribute(ScriptRefactoringDescriptor.ATTRIBUTE_SELECTION);
if (selection != null) {
int offset = -1;
int length = -1;
final StringTokenizer tokenizer = new StringTokenizer(selection);
if (tokenizer.hasMoreTokens())
offset = Integer.valueOf(tokenizer.nextToken()).intValue();
if (tokenizer.hasMoreTokens())
length = Integer.valueOf(tokenizer.nextToken()).intValue();
if (offset >= 0 && length >= 0) {
try {
final IModelElement[] elements = fCu.codeSelect(offset, length);
if (elements != null) {
for (int index = 0; index < elements.length; index++) {
final IModelElement element = elements[index];
if (element instanceof ILocalVariable)
fModelElement = element;
}
}
if (fModelElement == null)
return ScriptableRefactoring.createInputFatalStatus(null, getProcessorName(),
IScriptRefactorings.RENAME_LOCAL_VARIABLE);
} catch (ModelException exception) {
ScriptManipulationPlugin.log(exception);
}
} else
return RefactoringStatus.createFatalErrorStatus(
Messages.format(RefactoringCoreMessages.InitializableRefactoring_illegal_argument,
new Object[] { selection, ScriptRefactoringDescriptor.ATTRIBUTE_SELECTION }));
} else
return RefactoringStatus.createFatalErrorStatus(
Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist,
ScriptRefactoringDescriptor.ATTRIBUTE_SELECTION));
}
final String references = extended.getAttribute(ScriptRefactoringDescriptor.ATTRIBUTE_REFERENCES);
if (references != null) {
fUpdateReferences = Boolean.valueOf(references).booleanValue();
} else
return RefactoringStatus.createFatalErrorStatus(
Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist,
ScriptRefactoringDescriptor.ATTRIBUTE_REFERENCES));
return new RefactoringStatus();
}
@Override
public String getCurrentElementName() {
return fCurrentName;
}
@Override
public boolean canEnableUpdateReferences() {
return true;
}
@Override
public void setUpdateReferences(boolean update) {
fUpdateReferences = update;
}
@Override
public boolean getUpdateReferences() {
return fUpdateReferences;
}
@Override
protected RenameModifications computeRenameModifications() throws CoreException {
RenameModifications result = new RenameModifications();
if (fModelElement instanceof ILocalVariable) {
result.rename((ILocalVariable) fModelElement,
new RenameArguments(getNewElementName(), getUpdateReferences()));
} else if (fModelElement instanceof IField) {
// TODO: add switching method in RenameModifications
result.rename((IField) fModelElement, new RenameArguments(getNewElementName(), getUpdateReferences()));
}
return result;
}
@Override
protected IFile[] getChangedFiles() throws CoreException {
return ResourceUtil.getFiles(fChangeManager.getAllSourceModules());
}
@Override
protected RefactoringStatus doCheckFinalConditions(IProgressMonitor pm, CheckConditionsContext context)
throws CoreException, OperationCanceledException {
try {
pm.beginTask("", 1); //$NON-NLS-1$
RefactoringStatus result = checkNewElementName(getNewElementName());
if (result.hasFatalError())
return result;
createEdits(pm);
// LocalAnalyzePackage[] localAnalyzePackages= new
// RenameAnalyzeUtil.LocalAnalyzePackage[] { fLocalAnalyzePackage };
// result.merge(RenameAnalyzeUtil.analyzeLocalRenames(localAnalyzePackages,
// fChange, fCompilationUnitNode, true));
return result;
} finally {
pm.done();
}
}
private void createEdits(IProgressMonitor pm) throws CoreException {
fChangeManager.clear();
IDLTKSearchScope scope = SearchEngine.createWorkspaceScope(fToolkit);
SearchEngine engine = new SearchEngine();
if (fUpdateReferences) {
SearchPattern pattern = SearchPattern.createPattern(fModelElement, IDLTKSearchConstants.REFERENCES,
SearchUtils.GENERICS_AGNOSTIC_MATCH_RULE, fToolkit);
IProgressMonitor monitor = new SubProgressMonitor(pm, 1000);
engine.search(pattern, new SearchParticipant[] { SearchEngine.getDefaultSearchParticipant() }, scope,
new SearchRequestor() {
@Override
public void acceptSearchMatch(SearchMatch match) throws CoreException {
if (!(match.getElement() instanceof IModelElement))
return;
IModelElement elem = (IModelElement) match.getElement();
ISourceModule cu = (ISourceModule) elem.getAncestor(IModelElement.SOURCE_MODULE);
if (cu != null) {
ReplaceEdit edit = createReplaceEdit(match);
addTextEdit(fChangeManager.get(cu), getProcessorName(), edit);
}
// if (match.getResource().equals(fCu.getCorrespondingResource()))
// edits.add(new ReplaceEdit(match.getOffset(), fCurrentName.length(),
// getNewElementName()));
}
}, monitor);
}
ISourceRange decl = null;
if (fModelElement instanceof ILocalVariable) {
decl = ((ILocalVariable) fModelElement).getNameRange();
} else if (fModelElement instanceof IMember) {
decl = ((IMember) fModelElement).getNameRange();
}
if (decl != null) {
ReplaceEdit edit = new ReplaceEdit(decl.getOffset(), fCurrentName.length(), getNewElementName());
addTextEdit(fChangeManager.get(fCu), getProcessorName(), edit);
}
// fChange= new
// SourceModuleChange(RefactoringCoreMessages.RenameTempRefactoring_rename,
// fCu);
// MultiTextEdit rootEdit= new MultiTextEdit();
// fChange.setEdit(rootEdit);
// fChange.setKeepPreviewEdits(true);
// for (TextEdit edit : edits) {
// rootEdit.addChild(edit);
// fChange.addTextEditGroup(new
// TextEditGroup(RefactoringCoreMessages.RenameTempRefactoring_changeName,
// edit));
// }
}
/**
* Creates {@link ReplaceEdit} for the specified {@link SearchMatch}
*
* @param match
* @return
* @since 5.0.0
*/
protected ReplaceEdit createReplaceEdit(SearchMatch match) {
return new ReplaceEdit(match.getOffset(), fCurrentName.length(), getNewElementName());
}
private static void addTextEdit(TextChange change, String name, TextEdit edit) throws MalformedTreeException {
TextEdit root = change.getEdit();
if (root == null) {
root = new MultiTextEdit();
change.setEdit(root);
}
root.addChild(edit);
change.addTextEditGroup(new TextEditGroup(name, edit));
}
@Override
protected String[] getAffectedProjectNatures() throws CoreException {
return ScriptProcessors.computeAffectedNatures(fCu);
}
@Override
public Object[] getElements() {
return new Object[] { fModelElement };
}
@Override
public String getNewElement() {
return getNewElementName();
}
@Override
public RefactoringStatus checkInitialConditions(IProgressMonitor pm)
throws CoreException, OperationCanceledException {
// fCompilationUnitNode = RefactoringASTParser.parseWithASTProvider(fCu, true,
// null);
// ISourceRange sourceRange= fLocalVariable.
// fLocalVariable.get
// ASTNode name= NodeFinder.perform(fCompilationUnitNode, sourceRange);
// if (name == null)
// return;
// if (name.getParent() instanceof VariableDeclaration)
// fTempDeclarationNode= (VariableDeclaration) name.getParent();
// if (fTempDeclarationNode == null || fTempDeclarationNode.resolveBinding() ==
// null)
// return
// RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.RenameTempRefactoring_must_select_local);
// if (! Checks.isDeclaredIn(fTempDeclarationNode, MethodDeclaration.class)
// && ! Checks.isDeclaredIn(fTempDeclarationNode, Initializer.class))
// return
// RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.RenameTempRefactoring_only_in_methods_and_initializers);
// fCurrentName= fTempDeclarationNode.getName().getIdentifier();
fCurrentName = fModelElement.getElementName();
return new RefactoringStatus();
}
/*
* private AccumulatingProblemReporter getAccumulatingProblemReporter() { final
* PerWorkingCopyInfo perWorkingCopyInfo = getPerWorkingCopyInfo(); if
* (perWorkingCopyInfo != null && perWorkingCopyInfo.isActive()) { final
* IScriptProject project = getScriptProject();
*
* // https://bugs.eclipse.org/bugs/show_bug.cgi?id=267008 // Script nature
* check is not enough. It's possible that the // external project created //
* has a name of ExternalScriptProject.EXTERNAL_PROJECT_NAME, but no // script
* nature. // If script nature added during //
* WorkingCopyOwner.newWorkingCopy(...), this fix is not relevant. // Does
* script nature should be added in // WorkingCopyOwner.newWorkingCopy, or just
* script name checked? if (project != null &&
* (ExternalScriptProject.EXTERNAL_PROJECT_NAME
* .equals(project.getProject().getName()) || ScriptProject
* .hasScriptNature(project.getProject()))) { return new
* AccumulatingProblemReporter(perWorkingCopyInfo); } } return null; }
*/
@Override
public Change createChange(IProgressMonitor monitor) throws CoreException {
try {
monitor.beginTask(RefactoringCoreMessages.RenameFieldRefactoring_checking, 1);
TextChange[] changes = fChangeManager.getAllChanges();
RenameModelElementDescriptor descriptor = createRefactoringDescriptor();
return new DynamicValidationRefactoringChange(descriptor, getProcessorName(), changes);
} finally {
monitor.done();
}
}
/*
* @Override public Change createChange(IProgressMonitor pm) throws
* CoreException, OperationCanceledException { try {
* pm.beginTask(RefactoringCoreMessages.RenameTypeProcessor_creating_changes,
* 1); RenameModelElementDescriptor descriptor= createRefactoringDescriptor();
* fChange.setDescriptor(new RefactoringChangeDescriptor(descriptor)); return
* fChange; } finally { pm.done(); } }
*/
private RenameModelElementDescriptor createRefactoringDescriptor() {
String project = null;
IScriptProject scriptProject = fCu.getScriptProject();
if (scriptProject != null)
project = scriptProject.getElementName();
// final String header=
// Messages.format(RefactoringCoreMessages.RenameLocalVariableProcessor_descriptor_description,
// new String[] { BasicElementLabels.getJavaElementName(fCurrentName),
// JavaElementLabels.getElementLabel(fLocalVariable.getParent(),
// JavaElementLabels.ALL_FULLY_QUALIFIED),
// BasicElementLabels.getJavaElementName(fNewName)});
// final String description=
// Messages.format(RefactoringCoreMessages.RenameLocalVariableProcessor_descriptor_description_short,
// BasicElementLabels.getJavaElementName(fCurrentName));
// final String comment= new JDTRefactoringDescriptorComment(project, this,
// header).asString();
final RenameModelElementDescriptor descriptor = new RenameModelElementDescriptor(
IScriptRefactorings.RENAME_LOCAL_VARIABLE);
descriptor.setProject(project);
// descriptor.setDescription(description);
// descriptor.setComment(comment);
descriptor.setFlags(RefactoringDescriptor.NONE);
descriptor.setModelElement(fModelElement);
descriptor.setNewName(getNewElementName());
descriptor.setUpdateReferences(fUpdateReferences);
return descriptor;
}
@Override
public boolean needsSavedEditors() {
return false;
}
}