| /******************************************************************************* |
| * 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.getString("RenameComponentRefactoring_another_name")); //$NON-NLS-1$ |
| 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(); |
| } |
| } |
| |
| |
| |
| } |