blob: ae19995161f68adeb82c725827e10468dd59eb1f [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2008 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.wst.xsd.ui.internal.refactor.rename;
//import com.ibm.icu.text.Collator;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.jface.text.Position;
import org.eclipse.ltk.core.refactoring.Change;
import org.eclipse.ltk.core.refactoring.CompositeChange;
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.ParticipantManager;
import org.eclipse.ltk.core.refactoring.participants.RefactoringParticipant;
import org.eclipse.ltk.core.refactoring.participants.RenameArguments;
import org.eclipse.ltk.core.refactoring.participants.RenameParticipant;
import org.eclipse.ltk.core.refactoring.participants.RenameProcessor;
import org.eclipse.ltk.core.refactoring.participants.SharableParticipants;
import org.eclipse.text.edits.ReplaceEdit;
import org.eclipse.wst.common.core.search.SearchEngine;
import org.eclipse.wst.common.core.search.SearchMatch;
import org.eclipse.wst.common.core.search.pattern.QualifiedName;
import org.eclipse.wst.common.core.search.pattern.SearchPattern;
import org.eclipse.wst.common.core.search.scope.SearchScope;
import org.eclipse.wst.common.core.search.scope.SelectionSearchScope;
import org.eclipse.wst.common.core.search.scope.WorkspaceSearchScope;
import org.eclipse.wst.common.core.search.util.CollectingSearchRequestor;
import org.eclipse.wst.xml.core.internal.provisional.document.IDOMElement;
import org.eclipse.wst.xml.core.internal.search.XMLComponentDeclarationPattern;
import org.eclipse.wst.xml.core.internal.search.XMLComponentReferencePattern;
import org.eclipse.wst.xsd.ui.internal.refactor.Checks;
import org.eclipse.wst.xsd.ui.internal.refactor.INameUpdating;
import org.eclipse.wst.xsd.ui.internal.refactor.IReferenceUpdating;
import org.eclipse.wst.xsd.ui.internal.refactor.RefactoringComponent;
import org.eclipse.wst.xsd.ui.internal.refactor.RefactoringMessages;
import org.eclipse.wst.xsd.ui.internal.refactor.TextChangeManager;
import org.eclipse.wst.xsd.ui.internal.refactor.util.TextChangeCompatibility;
public class RenameComponentProcessor extends RenameProcessor implements INameUpdating, IReferenceUpdating {
public static final String IDENTIFIER = "org.eclipse.wst.xml.refactor.renameComponentProcessor"; //$NON-NLS-1$
private boolean singleFileOnly = false;
public static String quoteString(String value) {
value = value == null ? "" : value;
StringBuffer sb = new StringBuffer();
if (!value.startsWith("\"")) {
sb.append("\"");
}
sb.append(value);
if (!value.endsWith("\"")) {
sb.append("\"");
}
return sb.toString();
}
private TextChangeManager changeManager;
private String newName;
private RefactoringComponent selectedComponent;
private boolean updateReferences = true;
private Map references = new HashMap();
public RenameComponentProcessor(RefactoringComponent selectedComponent) {
this.selectedComponent = selectedComponent;
}
public RenameComponentProcessor(RefactoringComponent selectedComponent, String newName) {
this(selectedComponent, newName, false);
}
public RenameComponentProcessor(RefactoringComponent selectedComponent, String newName, boolean singleFileOnly) {
this.newName = newName;
this.selectedComponent = selectedComponent;
this.singleFileOnly = singleFileOnly;
}
private void addDeclarationUpdate(TextChangeManager manager) throws CoreException {
String fileStr = selectedComponent.getElement().getModel().getBaseLocation();
IFile file = ResourcesPlugin.getWorkspace().getRoot().getFile(new Path(fileStr));
addDeclarationUpdate(manager, file);
}
final void addDeclarationUpdate(TextChangeManager manager, IFile file) throws CoreException {
String componentName = selectedComponent.getName();
String componentNamespace = selectedComponent.getNamespaceURI();
QualifiedName elementQName = new QualifiedName(componentNamespace, componentName);
QualifiedName typeQName = selectedComponent.getTypeQName();
SearchScope scope = new WorkspaceSearchScope();
if (file != null) {
scope = new SelectionSearchScope(new IResource[]{file});
}
CollectingSearchRequestor requestor = new CollectingSearchRequestor();
SearchPattern pattern = new XMLComponentDeclarationPattern(file, elementQName, typeQName);
SearchEngine searchEngine = new SearchEngine();
HashMap map = new HashMap();
if (singleFileOnly)
{
map.put("searchDirtyContent", Boolean.TRUE);
}
searchEngine.search(pattern, requestor, scope, map, new NullProgressMonitor());
List results = requestor.getResults();
// more than one declaration found, so use offset as additional check
Position offsetPosition = null;
if (results.size() > 1) {
IDOMElement selectedElement = selectedComponent.getElement();
if (selectedElement != null) {
int startOffset = selectedElement.getStartOffset();
offsetPosition = new Position(startOffset, (selectedElement.getEndOffset() - startOffset));
}
}
for (Iterator iter = results.iterator(); iter.hasNext();) {
SearchMatch match = (SearchMatch) iter.next();
if (match != null) {
boolean addTextChange = true;
// additional check to verify correct declaration is changed
if (offsetPosition != null) {
addTextChange = offsetPosition.overlapsWith(match.getOffset(), match.getLength());
}
if (addTextChange) {
TextChange textChange = manager.get(match.getFile());
String newName = getNewElementName();
newName = quoteString(newName);
ReplaceEdit replaceEdit = new ReplaceEdit(match.getOffset(), match.getLength(), newName);
String editName = RefactoringMessages.getString("RenameComponentProcessor.Component_Refactoring_update_declatation");
TextChangeCompatibility.addTextEdit(textChange, editName, replaceEdit);
}
}
}
}
void addOccurrences(TextChangeManager manager, IProgressMonitor pm, RefactoringStatus status) throws CoreException {
String fileStr = selectedComponent.getElement().getModel().getBaseLocation();
IFile file = ResourcesPlugin.getWorkspace().getRoot().getFile(new Path(fileStr));
String componentName = selectedComponent.getName();
String componentNamespace = selectedComponent.getNamespaceURI();
QualifiedName elementQName = new QualifiedName(componentNamespace, componentName);
QualifiedName typeQName = selectedComponent.getTypeQName();
SearchEngine searchEngine = new SearchEngine();
SearchScope scope = null;
HashMap map = new HashMap();
if (singleFileOnly)
{
map.put("searchDirtyContent", Boolean.TRUE);
scope = new SelectionSearchScope(new IResource[]{file});
}
else
{
scope = new WorkspaceSearchScope();
}
SortingSearchRequestor requestor = new SortingSearchRequestor();
SearchPattern pattern = new XMLComponentReferencePattern(file, elementQName, typeQName);
searchEngine.search(pattern, requestor, scope, map, new NullProgressMonitor());
references = requestor.getResults();
// for (Iterator iter = references.iterator(); iter.hasNext();) {
// SearchMatch match = (SearchMatch) iter.next();
// TextChange textChange = manager.get(match.getFile());
// String newName = getNewElementName();
// if(match.getObject() instanceof Node){
// Node node = (Node)match.getObject();
// if(node instanceof IDOMAttr){
// IDOMAttr attr = (IDOMAttr)node;
// IDOMElement element = (IDOMElement)attr.getOwnerElement() ;
// newName = getNewQName(element, componentNamespace, newName);
// }
// newName = quoteString(newName);
// }
//
// ReplaceEdit replaceEdit = new ReplaceEdit(match.getOffset(),
// match.getLength(), newName );
// String editName =
// RefactoringMessages.getString("RenameComponentProcessor.Component_Refactoring_update_reference");
// TextChangeCompatibility.addTextEdit(textChange, editName,
// replaceEdit);
// }
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jdt.internal.corext.refactoring.tagging.ITextUpdating#canEnableTextUpdating()
*/
public boolean canEnableTextUpdating() {
return true;
}
public boolean canEnableUpdateReferences() {
return true;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ltk.core.refactoring.participants.RefactoringProcessor#checkFinalConditions(org.eclipse.core.runtime.IProgressMonitor,
* org.eclipse.ltk.core.refactoring.participants.CheckConditionsContext)
*/
public RefactoringStatus checkFinalConditions(IProgressMonitor monitor, CheckConditionsContext context) throws CoreException, OperationCanceledException {
Assert.isNotNull(monitor);
Assert.isNotNull(context);
final RefactoringStatus status = new RefactoringStatus();
try {
monitor.beginTask("", 2); //$NON-NLS-1$
monitor.setTaskName(RefactoringMessages.getString("RenameComponentRefactoring_checking")); //$NON-NLS-1$
status.merge(checkNewElementName(getNewElementName()));
monitor.worked(1);
monitor.setTaskName(RefactoringMessages.getString("RenameComponentRefactoring_searching")); //$NON-NLS-1$
status.merge(createRenameChanges(new SubProgressMonitor(monitor, 1)));
}
finally {
monitor.done();
}
return status;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ltk.core.refactoring.participants.RefactoringProcessor#checkInitialConditions(org.eclipse.core.runtime.IProgressMonitor)
*/
public RefactoringStatus checkInitialConditions(IProgressMonitor pm) throws CoreException, OperationCanceledException {
// TODO add code to check initial conditions for component rename
Assert.isNotNull(pm);
try {
return new RefactoringStatus();
}
finally {
pm.done();
}
}
public final RefactoringStatus checkNewElementName(final String name) {
Assert.isNotNull(name);
final RefactoringStatus result = Checks.checkName(name);
result.merge(Checks.checkComponentName(name));
if (Checks.isAlreadyNamed(selectedComponent, name))
result.addFatalError("RefactoringMessages.RenameComponentRefactoring_another_name");
return result;
}
private Object[] computeDerivedElements() {
Object[] elements = getElements();
// Object[] results = new Object[elements.length];
// for(int i=0; i< elements.length; i++){
// RefactoringComponent component = (RefactoringComponent)elements[i];
// results[i] = component.getAdaptee();
//
// }
return elements;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ltk.core.refactoring.participants.RefactoringProcessor#createChange(org.eclipse.core.runtime.IProgressMonitor)
*/
public Change createChange(IProgressMonitor pm) throws CoreException, OperationCanceledException {
// don't create any change now, all the changes are in changeManger
// variable and will be combined in postCreateChange method
return null;
}
private TextChangeManager updateChangeManager(IProgressMonitor pm, RefactoringStatus status) throws CoreException {
TextChangeManager manager = getChangeManager();
// only one declaration gets updated
addDeclarationUpdate(manager);
if (getUpdateReferences()) {
addOccurrences(manager, pm, status);
}
return manager;
}
private RefactoringStatus createRenameChanges(final IProgressMonitor monitor) throws CoreException {
Assert.isNotNull(monitor);
final RefactoringStatus status = new RefactoringStatus();
try {
monitor.beginTask(RefactoringMessages.getString("RenameComponentRefactoring_searching"), 1); //$NON-NLS-1$
updateChangeManager(new SubProgressMonitor(monitor, 1), status);
}
finally {
monitor.done();
}
return status;
}
public TextChangeManager getChangeManager() {
if (changeManager == null) {
changeManager = new TextChangeManager(false);
}
return changeManager;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.wst.xsd.internal.refactoring.rename.XSDRenameProcessor#getAffectedProjectNatures()
*/
protected String[] getAffectedProjectNatures() throws CoreException {
// TODO: find project natures of the files that are going to be
// refactored
return new String[]{"org.eclipse.jdt.core.javanature"};
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jdt.internal.corext.refactoring.tagging.ITextUpdating#getCurrentElementName()
*/
public String getCurrentElementName() {
//
return selectedComponent.getName();
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ltk.core.refactoring.participants.RefactoringProcessor#getElements()
*/
public Object[] getElements() {
Object model = selectedComponent.getModelObject();
if (model != null) {
return new Object[]{model};
}
return new Object[0];
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ltk.core.refactoring.participants.RefactoringProcessor#getIdentifier()
*/
public String getIdentifier() {
return IDENTIFIER;
}
public String getNewElementName() {
return newName;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ltk.core.refactoring.participants.RefactoringProcessor#getProcessorName()
*/
public String getProcessorName() {
return RefactoringMessages.getFormattedString("RenameComponentRefactoring.name", //$NON-NLS-1$
new String[]{selectedComponent.getNamespaceURI() + ":" + selectedComponent.getName(), newName});
}
public boolean getUpdateReferences() {
return updateReferences;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ltk.core.refactoring.participants.RefactoringProcessor#isApplicable()
*/
public boolean isApplicable() throws CoreException {
if (selectedComponent == null)
return false;
// TODO implement isApplicable logic for the named component,
// verify how it is different from other condition checks
// if (fNamedComponent.isAnonymous())
// return false;
// if (! Checks.isAvailable(fType))
// return false;
// if (isSpecialCase(fType))
// return false;
return true;
}
protected void loadDerivedParticipants(RefactoringStatus status, List result, Object[] derivedElements, ComponentRenameArguments arguments, String[] natures, SharableParticipants shared) throws CoreException {
if (derivedElements != null) {
for (int i = 0; i < derivedElements.length; i++) {
RenameParticipant[] participants = ParticipantManager.loadRenameParticipants(status, this, derivedElements[i], arguments, natures, shared);
result.addAll(Arrays.asList(participants));
}
}
}
/*
* (non-Javadoc)
*
* @see org.eclipse.wst.xsd.internal.refactoring.rename.XSDRenameProcessor#loadDerivedParticipants(org.eclipse.ltk.core.refactoring.RefactoringStatus,
* java.util.List, java.lang.String[],
* org.eclipse.ltk.core.refactoring.participants.SharableParticipants)
*/
protected void loadDerivedParticipants(RefactoringStatus status, List result, String[] natures, SharableParticipants shared) throws CoreException {
ComponentRenameArguments arguments = new ComponentRenameArguments(getNewElementName(), getUpdateReferences());
arguments.setMatches(references);
arguments.setQualifier(selectedComponent.getNamespaceURI());
// pass in changeManger to the participants so that it can collect all
// changes/per files
arguments.setChangeManager(getChangeManager());
loadDerivedParticipants(status, result, computeDerivedElements(), arguments, natures, shared);
}
protected void loadElementParticipants(RefactoringStatus status, List result, RenameArguments arguments, String[] natures, SharableParticipants shared) throws CoreException {
Object[] elements = new Object[0];// getElements();
for (int i = 0; i < elements.length; i++) {
result.addAll(Arrays.asList(ParticipantManager.loadRenameParticipants(status, this, elements[i], arguments, natures, shared)));
}
}
public final RefactoringParticipant[] loadParticipants(RefactoringStatus status, SharableParticipants sharedParticipants) throws CoreException {
RenameArguments arguments = new RenameArguments(getNewElementName(), getUpdateReferences());
String[] natures = getAffectedProjectNatures();
List result = new ArrayList();
loadElementParticipants(status, result, arguments, natures, sharedParticipants);
loadDerivedParticipants(status, result, natures, sharedParticipants);
for (Iterator i = result.iterator(); i.hasNext();) {
Object o = i.next();
if (o instanceof XMLComponentRenameParticipant) {
XMLComponentRenameParticipant p = (XMLComponentRenameParticipant) o;
// getChangeManager()
p.setChangeManager(getChangeManager());
}
}
return (RefactoringParticipant[]) result.toArray(new RefactoringParticipant[result.size()]);
}
public void setNewElementName(String newName) {
Assert.isNotNull(newName);
this.newName = newName;
}
public void setUpdateReferences(boolean update) {
updateReferences = update;
}
public Change postCreateChange(Change[] participantChanges, IProgressMonitor pm) throws CoreException, OperationCanceledException {
Assert.isNotNull(pm);
try {
String changeName = RefactoringMessages.getString("RenameComponentProcessor.Component_Refactoring_updates");
TextChange[] changes = changeManager.getAllChanges();
// Comparator c = new Comparator() {
// public int compare(Object o1, Object o2) {
// TextFileChange c1 = (TextFileChange) o1;
// TextFileChange c2 = (TextFileChange) o2;
// return Collator.getInstance().compare(c1.getFile().getFullPath(), c2.getFile().getFullPath());
// }
// };
if (changes.length > 0) {
// Arrays.sort(changes, c);
CompositeChange compositeChange = new CompositeChange("!" + changeName, changes);
compositeChange.markAsSynthetic();
return compositeChange;
}
else {
return null;
}
}
finally {
pm.done();
}
}
}