| /* |
| * Copyright (c) 2013, the Dart project authors. |
| * |
| * Licensed under the Eclipse Public License v1.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.eclipse.org/legal/epl-v10.html |
| * |
| * 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 com.google.dart.engine.services.internal.refactoring; |
| |
| import com.google.common.collect.Lists; |
| import com.google.dart.engine.element.ClassElement; |
| import com.google.dart.engine.element.Element; |
| import com.google.dart.engine.element.FieldElement; |
| import com.google.dart.engine.element.LibraryElement; |
| import com.google.dart.engine.element.MethodElement; |
| import com.google.dart.engine.element.PropertyAccessorElement; |
| import com.google.dart.engine.element.TypeParameterElement; |
| import com.google.dart.engine.search.MatchKind; |
| import com.google.dart.engine.search.SearchEngine; |
| import com.google.dart.engine.search.SearchFilter; |
| import com.google.dart.engine.search.SearchMatch; |
| import com.google.dart.engine.services.change.Change; |
| import com.google.dart.engine.services.change.CompositeChange; |
| import com.google.dart.engine.services.change.Edit; |
| import com.google.dart.engine.services.change.MergeCompositeChange; |
| import com.google.dart.engine.services.change.SourceChange; |
| import com.google.dart.engine.services.change.SourceChangeManager; |
| import com.google.dart.engine.services.refactoring.NamingConventions; |
| import com.google.dart.engine.services.refactoring.ProgressMonitor; |
| import com.google.dart.engine.services.refactoring.Refactoring; |
| import com.google.dart.engine.services.refactoring.SubProgressMonitor; |
| import com.google.dart.engine.services.status.RefactoringStatus; |
| import com.google.dart.engine.services.util.HierarchyUtils; |
| import com.google.dart.engine.source.Source; |
| import com.google.dart.engine.source.SourceFactory; |
| |
| import java.util.Iterator; |
| import java.util.List; |
| |
| /** |
| * {@link Refactoring} for renaming {@link FieldElement} and {@link MethodElement}. |
| */ |
| public class RenameClassMemberRefactoringImpl extends RenameRefactoringImpl { |
| private final Element element; |
| private RenameClassMemberValidator validator; |
| private List<SearchMatch> nameReferences; |
| |
| private boolean hasIgnoredElements = false; |
| |
| public RenameClassMemberRefactoringImpl(SearchEngine searchEngine, Element element) { |
| super(searchEngine, element); |
| this.element = element; |
| } |
| |
| @Override |
| public RefactoringStatus checkFinalConditions(ProgressMonitor pm) throws Exception { |
| pm = checkProgressMonitor(pm); |
| pm.beginTask("Checking final conditions", 1); |
| try { |
| RefactoringStatus result = new RefactoringStatus(); |
| validator = new RenameClassMemberValidator( |
| searchEngine, |
| element.getKind(), |
| (ClassElement) element.getEnclosingElement(), |
| element.getDisplayName(), |
| newName); |
| result.merge(validator.validate(new SubProgressMonitor(pm, 1), true)); |
| // add warning if some elements/reference are ignored |
| hasIgnoredElements = validator.hasIgnoredElements; |
| removeReferencesIfCannotUpdateSource(nameReferences); |
| removeReferencesIfCannotUpdateSource(validator.renameElementsReferences); |
| if (hasIgnoredElements) { |
| result.addWarning("Elements and references in SDK and external packages will not be renamed."); |
| } |
| // done |
| return result; |
| } finally { |
| pm.done(); |
| } |
| } |
| |
| @Override |
| public RefactoringStatus checkInitialConditions(ProgressMonitor pm) throws Exception { |
| if (element instanceof MethodElement && ((MethodElement) element).isOperator()) { |
| return RefactoringStatus.createFatalErrorStatus("Cannot rename operator."); |
| } |
| preparePotentialMatchers(); |
| return new RefactoringStatus(); |
| } |
| |
| @Override |
| public RefactoringStatus checkNewName(String newName) { |
| RefactoringStatus result = new RefactoringStatus(); |
| result.merge(super.checkNewName(newName)); |
| if (element instanceof FieldElement) { |
| result.merge(NamingConventions.validateFieldName(newName)); |
| } |
| if (element instanceof MethodElement) { |
| result.merge(NamingConventions.validateMethodName(newName)); |
| } |
| return result; |
| } |
| |
| @Override |
| public Change createChange(ProgressMonitor pm) throws Exception { |
| pm = checkProgressMonitor(pm); |
| try { |
| SourceChangeManager exactManager = new SourceChangeManager(); |
| // update declaration |
| for (Element renameElement : validator.renameElements) { |
| if (!renameElement.isSynthetic()) { |
| Source elementSource = renameElement.getSource(); |
| SourceChange elementChange = exactManager.get(elementSource); |
| addDeclarationEdit(elementChange, renameElement); |
| } |
| } |
| // update references |
| List<SourceReference> elementRefs = getSourceReferences(validator.renameElementsReferences); |
| for (SourceReference reference : elementRefs) { |
| SourceChange refChange = exactManager.get(reference.source); |
| addReferenceEdit(refChange, reference); |
| } |
| // potential matches |
| SourceChangeManager previewManager = new SourceChangeManager(); |
| List<SourceReference> nameSourceReferences = getSourceReferences(nameReferences); |
| for (SourceReference reference : nameSourceReferences) { |
| // check that element is accessible |
| boolean accessible = false; |
| for (Element refElement : reference.elements) { |
| LibraryElement whereLibrary = refElement.getLibrary(); |
| accessible |= element.isAccessibleIn(whereLibrary); |
| } |
| if (!accessible) { |
| continue; |
| } |
| // add edit |
| SourceChange refChange = previewManager.get(reference.source); |
| Edit edit = createReferenceEdit(reference, newName); |
| addEdit(refChange, "Update reference @" + reference.range.getOffset(), edit); |
| } |
| // return CompositeChange |
| SourceChange[] exactChanges = exactManager.getChanges(); |
| SourceChange[] previewChanges = previewManager.getChanges(); |
| if (previewChanges.length == 0) { |
| CompositeChange compositeChange = new CompositeChange(getRefactoringName()); |
| compositeChange.add(exactChanges); |
| return compositeChange; |
| } else { |
| CompositeChange exactChange = new CompositeChange("Exact changes"); |
| CompositeChange previewChange = new CompositeChange("Potential matches"); |
| exactChange.add(exactChanges); |
| previewChange.add(previewChanges); |
| return new MergeCompositeChange(getRefactoringName(), previewChange, exactChange); |
| } |
| } finally { |
| pm.done(); |
| } |
| } |
| |
| @Override |
| public String getRefactoringName() { |
| if (element instanceof TypeParameterElement) { |
| return "Rename Type Parameter"; |
| } |
| if (element instanceof FieldElement) { |
| return "Rename Field"; |
| } |
| return "Rename Method"; |
| } |
| |
| @Override |
| public boolean requiresPreview() { |
| return nameReferences != null && !nameReferences.isEmpty(); |
| } |
| |
| private void preparePotentialMatchers() { |
| if (element instanceof FieldElement || element instanceof MethodElement |
| || element instanceof PropertyAccessorElement) { |
| nameReferences = searchEngine.searchQualifiedMemberReferences( |
| element.getDisplayName(), |
| null, |
| new SearchFilter() { |
| @Override |
| public boolean passes(SearchMatch match) { |
| return match.getKind() == MatchKind.NAME_REFERENCE_UNRESOLVED; |
| } |
| }); |
| } else { |
| nameReferences = Lists.newArrayList(); |
| } |
| nameReferences = HierarchyUtils.getAccessibleMatches(element, nameReferences); |
| } |
| |
| private void removeReferencesIfCannotUpdateSource(List<SearchMatch> references) { |
| SourceFactory sourceFactory = element.getContext().getSourceFactory(); |
| for (Iterator<SearchMatch> iter = references.iterator(); iter.hasNext();) { |
| SearchMatch reference = iter.next(); |
| Source refSource = reference.getElement().getSource(); |
| if (!sourceFactory.isLocalSource(refSource)) { |
| iter.remove(); |
| hasIgnoredElements = true; |
| } |
| } |
| } |
| } |