/* * 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.Sets; import com.google.dart.engine.ast.AstNode; import com.google.dart.engine.ast.FunctionDeclaration; import com.google.dart.engine.ast.MethodDeclaration; import com.google.dart.engine.context.AnalysisException; import com.google.dart.engine.element.ClassElement; import com.google.dart.engine.element.CompilationUnitElement; import com.google.dart.engine.element.Element; import com.google.dart.engine.element.MethodElement; import com.google.dart.engine.element.PropertyAccessorElement; import com.google.dart.engine.scanner.Token; import com.google.dart.engine.search.SearchEngine; 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.SourceChange; import com.google.dart.engine.services.change.SourceChangeManager; import com.google.dart.engine.services.refactoring.ConvertGetterToMethodRefactoring; import com.google.dart.engine.services.refactoring.ProgressMonitor; import com.google.dart.engine.services.status.RefactoringStatus; import com.google.dart.engine.services.util.HierarchyUtils; import com.google.dart.engine.utilities.source.SourceRange; import static com.google.dart.engine.services.internal.correction.CorrectionUtils.getChildren; import static com.google.dart.engine.utilities.source.SourceRangeFactory.rangeElementName; import static com.google.dart.engine.utilities.source.SourceRangeFactory.rangeStartEnd; import java.util.List; import java.util.Set; /** * Implementation of {@link ConvertGetterToMethodRefactoring}. */ public class ConvertGetterToMethodRefactoringImpl extends RefactoringImpl implements ConvertGetterToMethodRefactoring { private final SearchEngine searchEngine; private final PropertyAccessorElement element; private SourceChangeManager changeManager; public ConvertGetterToMethodRefactoringImpl(SearchEngine searchEngine, PropertyAccessorElement element) { this.searchEngine = searchEngine; this.element = element; } @Override public RefactoringStatus checkFinalConditions(ProgressMonitor pm) throws Exception { return new RefactoringStatus(); } @Override public RefactoringStatus checkInitialConditions(ProgressMonitor pm) throws Exception { pm = checkProgressMonitor(pm); pm.beginTask("Checking initial conditions", 1); try { RefactoringStatus result = new RefactoringStatus(); if (element.isSynthetic()) { return RefactoringStatus.createFatalErrorStatus("Only explicit getter can be converted to method."); } pm.worked(1); return result; } finally { pm.done(); } } @Override public Change createChange(ProgressMonitor pm) throws Exception { pm.beginTask("Processing...", 2); try { changeManager = new SourceChangeManager(); // FunctionDeclaration if (element.getEnclosingElement() instanceof CompilationUnitElement) { // update declaration updateElementDeclaration(element); pm.worked(1); // update references updateElementReferences(element); pm.worked(1); } // MethodDeclaration if (element.getEnclosingElement() instanceof ClassElement) { Set<PropertyAccessorElement> updateElements = getHierarchyMethods(); pm.worked(1); // update elements for (PropertyAccessorElement element : updateElements) { updateElementDeclaration(element); updateElementReferences(element); } pm.worked(1); } // done return new CompositeChange(getRefactoringName(), changeManager.getChanges()); } finally { pm.done(); changeManager = null; } } @Override public String getRefactoringName() { return "Convert Getter to Method"; } /** * When {@link #element} is {@link MethodElement}, finds all overrides in super- and sub- classes. */ private Set<PropertyAccessorElement> getHierarchyMethods() { ClassElement enclosingClass = (ClassElement) element.getEnclosingElement(); // prepare super/sub-classes Set<ClassElement> superClasses = HierarchyUtils.getSuperClasses(enclosingClass); Set<ClassElement> subClasses = HierarchyUtils.getSubClasses(searchEngine, enclosingClass); // full hierarchy Set<ClassElement> hierarchyClasses = Sets.newHashSet(); hierarchyClasses.add(enclosingClass); hierarchyClasses.addAll(superClasses); hierarchyClasses.addAll(subClasses); // prepare elements to update Set<PropertyAccessorElement> updateElements = Sets.newHashSet(); for (ClassElement superClass : hierarchyClasses) { for (Element child : getChildren(superClass, element.getDisplayName())) { if (child instanceof PropertyAccessorElement && !child.isSynthetic()) { PropertyAccessorElement accessor = (PropertyAccessorElement) child; if (accessor.isGetter()) { updateElements.add(accessor); } } } } return updateElements; } private void updateElementDeclaration(PropertyAccessorElement element) throws AnalysisException { String description = "Convert getter declaration into method"; SourceChange change = changeManager.get(element.getSource()); // prepare "get" keyword Token getKeyword = null; { AstNode node = element.getNode(); if (node instanceof MethodDeclaration) { getKeyword = ((MethodDeclaration) node).getPropertyKeyword(); } else if (node instanceof FunctionDeclaration) { getKeyword = ((FunctionDeclaration) node).getPropertyKeyword(); } } // remove "get " if (getKeyword != null) { Edit edit = new Edit(rangeStartEnd(getKeyword, element.getNameOffset()), ""); change.addEdit(edit, description); } // add parameters "()" { Edit edit = new Edit(rangeElementName(element).getEnd(), 0, "()"); change.addEdit(edit, description); } } private void updateElementReferences(Element element) throws Exception { List<SearchMatch> matches = searchEngine.searchReferences(element, null, null); List<SourceReference> references = getSourceReferences(matches); for (SourceReference reference : references) { SourceRange refRange = reference.range; SourceChange refChange = changeManager.get(reference.source); // insert "()" refChange.addEdit( new Edit(refRange.getEnd(), 0, "()"), "Replace field access with invocation"); } } }