[groovy] extract transformation-renaming logic to helper extension

This commit is contained in:
Daniil Ovchinnikov
2016-10-31 18:31:30 +03:00
parent cc1ff82436
commit 85a54e5bfc
6 changed files with 175 additions and 138 deletions
+3
View File
@@ -32,6 +32,7 @@
<extensionPoint name="membersContributor" interface="org.jetbrains.plugins.groovy.lang.resolve.NonCodeMembersContributor"/>
<extensionPoint name="importContributor" interface="org.jetbrains.plugins.groovy.lang.resolve.GrImportContributor"/>
<extensionPoint name="astTransformationSupport" interface="org.jetbrains.plugins.groovy.transformations.AstTransformationSupport"/>
<extensionPoint name="astTransformationRenameHelper" interface="org.jetbrains.plugins.groovy.transformations.AstTransformationRenameHelper"/>
<extensionPoint name="closureMissingMethodContributor"
interface="org.jetbrains.plugins.groovy.lang.resolve.ClosureMissingMethodContributor"/>
<extensionPoint name="variableEnhancer" interface="org.jetbrains.plugins.groovy.lang.psi.typeEnhancers.GrVariableEnhancer"/>
@@ -132,12 +133,14 @@
<astTransformationSupport implementation="org.jetbrains.plugins.groovy.transformations.impl.BaseScriptTransformationSupport"/>
<astTransformationSupport id="defaultSupport"
implementation="org.jetbrains.plugins.groovy.transformations.impl.DefaultTransformationSupport"/>
<astTransformationRenameHelper implementation="org.jetbrains.plugins.groovy.transformations.impl.DefaultRenameHelper"/>
<astTransformationSupport order="after defaultSupport"
implementation="org.jetbrains.plugins.groovy.transformations.impl.FieldScriptTransformationSupport"/>
<astTransformationSupport id="delegateSupport" order="after defaultSupport"
implementation="org.jetbrains.plugins.groovy.transformations.impl.DelegateTransformationSupport"/>
<astTransformationSupport id="traitSupport" order="last"
implementation="org.jetbrains.plugins.groovy.transformations.impl.TraitTransformationSupport"/>
<astTransformationRenameHelper implementation="org.jetbrains.plugins.groovy.transformations.impl.TraitRenameHelper"/>
<positionManagerDelegate implementation="org.jetbrains.plugins.groovy.gant.GantPositionManagerHelper"/>
@@ -15,7 +15,6 @@
*/
package org.jetbrains.plugins.groovy.refactoring.rename;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.search.GlobalSearchScope;
@@ -36,13 +35,10 @@ import com.intellij.util.containers.MultiMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory;
import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrField;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrCall;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrAccessorMethod;
import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GrTraitField;
import org.jetbrains.plugins.groovy.lang.psi.util.GrTraitUtil;
import org.jetbrains.plugins.groovy.lang.psi.util.GroovyPropertyUtils;
import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil;
import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil;
@@ -54,13 +50,13 @@ import java.util.Collection;
import java.util.List;
import java.util.Map;
import static org.jetbrains.plugins.groovy.transformations.RenameHelperKt.getNewNameFromTransformations;
/**
* @author ilyas
*/
public class RenameGrFieldProcessor extends RenameJavaVariableProcessor {
private static final Logger LOG = Logger.getInstance(RenameGrFieldProcessor.class);
@NotNull
@Override
public Collection<PsiReference> findReferences(PsiElement element) {
@@ -87,145 +83,34 @@ public class RenameGrFieldProcessor extends RenameJavaVariableProcessor {
String newName,
final UsageInfo[] usages,
@Nullable RefactoringElementListener listener) throws IncorrectOperationException {
final GrField field = (GrField)psiElement;
String fieldName = field.getName();
NameProvider nameProvider = new NameProvider(field, newName);
MultiMap<PsiNamedElement, UsageInfo> propertyUsages = MultiMap.createLinked();
MultiMap<PsiNamedElement, UsageInfo> simpleUsages = MultiMap.createLinked();
List<PsiReference> unknownUsages = new ArrayList<>();
GrField field = (GrField)psiElement;
Map<GrReferenceExpression, PsiElement> handled = ContainerUtil.newHashMap();
for (UsageInfo usage : usages) {
final PsiReference ref = usage.getReference();
if (ref instanceof GrReferenceExpression) {
final GroovyResolveResult resolveResult = ((GrReferenceExpression)ref).advancedResolve();
final PsiElement element = resolveResult.getElement();
if (resolveResult.isInvokedOnProperty()) {
propertyUsages.putValue((PsiNamedElement)element, usage);
}
else {
simpleUsages.putValue((PsiNamedElement)element, usage);
}
PsiElement resolved = ref.resolve();
ref.handleElementRename(getNewNameFromTransformations(resolved, newName));
handled.put((GrReferenceExpression)ref, resolved);
}
else if (ref != null) {
unknownUsages.add(ref);
handleElementRename(newName, ref, field.getName());
}
}
for (PsiReference ref : unknownUsages) {
handleElementRename(newName, ref, fieldName);
}
field.setName(newName);
nameProvider.putNewElements(field);
for (GrReferenceExpression expression : handled.keySet()) {
PsiElement oldResolved = handled.get(expression);
if (oldResolved == null) continue;
PsiElement resolved = expression.resolve();
if (resolved == null) continue;
if (expression.getManager().areElementsEquivalent(oldResolved, resolved)) continue;
qualify(field, expression);
}
PsiManager manager = field.getManager();
for (PsiNamedElement element : simpleUsages.keySet()) {
for (UsageInfo info : simpleUsages.get(element)) {
final String name = nameProvider.getNewName(element);
rename(nameProvider.getNewElement(element), info, name == null ? newName : name, name != null, manager);
}
}
for (PsiNamedElement element : propertyUsages.keySet()) {
for (UsageInfo info : propertyUsages.get(element)) {
rename(element, info, newName, true, manager);
}
}
if (listener != null) {
listener.elementRenamed(field);
}
}
private static class NameProvider {
private final Map<PsiElement, String> myNameMap = ContainerUtil.newHashMap();
private final Map<String, PsiNamedElement> myNewElements = ContainerUtil.newHashMap();
public NameProvider(GrField field, String newName) {
myNameMap.put(field, newName);
if (field.isProperty()) {
for (GrAccessorMethod getter : field.getGetters()) {
myNameMap.put(getter, RenamePropertyUtil.getGetterNameByOldName(newName, getter.getName()));
}
final GrAccessorMethod setter = field.getSetter();
if (setter != null) {
myNameMap.put(setter, GroovyPropertyUtils.getSetterName(newName));
}
}
}
String getNewName(@NotNull PsiElement element) {
String name = myNameMap.get(element);
if (name != null) {
return name;
}
if (element instanceof GrTraitField) {
PsiField prototype = ((GrTraitField)element).getPrototype();
String newPrototypeName = getNewName(prototype);
return GrTraitUtil.getTraitFieldPrefix(prototype.getContainingClass()) + newPrototypeName;
}
return null;
}
public void putNewElements(@NotNull GrField field) {
myNewElements.put(field.getName(), field);
if (field.isProperty()) {
for (GrAccessorMethod newGetter : field.getGetters()) {
myNewElements.put(newGetter.getName(), newGetter);
}
final GrAccessorMethod newSetter = field.getSetter();
if (newSetter != null) {
myNewElements.put(newSetter.getName(), newSetter);
}
}
}
public PsiNamedElement getNewElement(PsiNamedElement element) {
String newName = getNewName(element);
PsiNamedElement newElement = myNewElements.get(newName);
if (newElement != null) {
return newElement;
}
if (element instanceof GrTraitField) {
PsiField prototype = ((GrTraitField)element).getPrototype();
return getNewElement(prototype);
}
return null;
}
}
private static void rename(PsiNamedElement element,
UsageInfo info,
String nameToUse,
boolean shouldCheckForCorrectResolve,
PsiManager manager) {
final PsiReference ref = info.getReference();
assert ref != null;
final PsiElement renamed = ref.handleElementRename(nameToUse);
PsiElement newly_resolved = ref.resolve();
if (shouldCheckForCorrectResolve) {
if (element instanceof GrAccessorMethod && newly_resolved instanceof GrAccessorMethod) {
final GrAccessorMethod oldAccessor = (GrAccessorMethod)element;
final GrAccessorMethod newAccessor = (GrAccessorMethod)newly_resolved;
if (!manager.areElementsEquivalent(oldAccessor.getProperty(), newAccessor.getProperty()) &&
oldAccessor.isSetter() == newAccessor.isSetter()) {
qualify(oldAccessor, renamed, nameToUse);
}
}
else if (!manager.areElementsEquivalent(element, newly_resolved)) {
qualify((PsiMember)element, renamed, nameToUse);
}
listener.elementRenamed(psiElement);
}
}
@@ -258,13 +143,11 @@ public class RenameGrFieldProcessor extends RenameJavaVariableProcessor {
ref.handleElementRename(toRename);
}
private static void qualify(PsiMember member, PsiElement renamed, String name) {
if (!(renamed instanceof GrReferenceExpression)) return;
private static void qualify(PsiMember member, GrReferenceExpression refExpr) {
String name = refExpr.getReferenceName();
final PsiClass clazz = member.getContainingClass();
if (clazz == null) return;
final GrReferenceExpression refExpr = (GrReferenceExpression)renamed;
if (refExpr.getQualifierExpression() != null) return;
final PsiElement replaced;
@@ -274,7 +157,7 @@ public class RenameGrFieldProcessor extends RenameJavaVariableProcessor {
replaced = refExpr.replace(newRefExpr);
}
else {
final PsiClass containingClass = PsiTreeUtil.getParentOfType(renamed, PsiClass.class);
final PsiClass containingClass = PsiTreeUtil.getParentOfType(refExpr, PsiClass.class);
if (member.getManager().areElementsEquivalent(containingClass, clazz)) {
final GrReferenceExpression newRefExpr = GroovyPsiElementFactory.getInstance(member.getProject())
.createReferenceExpressionFromText("this." + name);
@@ -291,7 +174,7 @@ public class RenameGrFieldProcessor extends RenameJavaVariableProcessor {
@Override
public boolean canProcessElement(@NotNull final PsiElement element) {
return element instanceof GrField /*&& ((GrField)element).isProperty()*/ || element instanceof GrAccessorMethod;
return element instanceof GrField;
}
@Override
@@ -0,0 +1,53 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
*
* 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 org.jetbrains.plugins.groovy.transformations;
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.psi.PsiMember;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.transformations.impl.DefaultRenameHelper;
public interface AstTransformationRenameHelper {
ExtensionPointName<AstTransformationRenameHelper> EP_NAME = ExtensionPointName.create(
"org.intellij.groovy.astTransformationRenameHelper"
);
/**
* GrReferenceExpression may resolve to member generated by some AST Transformation from original member.
* <p>
* Example:
* <pre>
* class A {
* def foo
* }
* new A().getFoo()
* </pre>
* {@code getFoo} references accessor method generated from original <i>foo</i> field.
* Suppose we are renaming the field to <i>bar</i>.
* Then {@code member} will be the referenced accessor method and {@code newOriginalName} is the new name of the field, i.e. <i>bar</i>.
* Given this information {@link DefaultRenameHelper} returns the name of the new accessor
* that will be generated after rename, i.e. <i>getBar</i>.
*
* @param member currently resolved member.
* @param newOriginalName new name of original member.
* @return name of new member which will be generated after original member changes its name to {@code newOriginalName}.
* @see DefaultRenameHelper
*/
@Nullable
String getNewMemberName(@NotNull PsiMember member, @NotNull String newOriginalName);
}
@@ -0,0 +1,35 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
*
* 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 org.jetbrains.plugins.groovy.transformations.impl
import com.intellij.psi.PsiMember
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrAccessorMethod
import org.jetbrains.plugins.groovy.lang.psi.util.GroovyPropertyUtils.getAccessorName
import org.jetbrains.plugins.groovy.lang.psi.util.GroovyPropertyUtils.getSetterName
import org.jetbrains.plugins.groovy.transformations.AstTransformationRenameHelper
class DefaultRenameHelper : AstTransformationRenameHelper {
override fun getNewMemberName(member: PsiMember, newOriginalName: String): String? {
if (member !is GrAccessorMethod) return null
return if (member.isSetter) {
getSetterName(newOriginalName)
}
else {
getAccessorName(if (member.name.startsWith("is")) "is" else "get", newOriginalName)
}
}
}
@@ -0,0 +1,31 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
*
* 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 org.jetbrains.plugins.groovy.transformations.impl
import com.intellij.psi.PsiMember
import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GrTraitField
import org.jetbrains.plugins.groovy.lang.psi.util.GrTraitUtil
import org.jetbrains.plugins.groovy.transformations.AstTransformationRenameHelper
class TraitRenameHelper : AstTransformationRenameHelper {
override fun getNewMemberName(member: PsiMember, newOriginalName: String): String? {
if (member !is GrTraitField) return null
val prototype = member.prototype
val containingClass = prototype.containingClass ?: return null
return GrTraitUtil.getTraitFieldPrefix(containingClass) + newOriginalName
}
}
@@ -0,0 +1,32 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
*
* 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 org.jetbrains.plugins.groovy.transformations
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiMember
fun PsiElement?.getNewNameFromTransformations(newName: String) = (this as? PsiMember)?.let {
doGetNewNameFromTransformations(it, newName)
} ?: newName
private fun doGetNewNameFromTransformations(member: PsiMember, newName: String): String? {
@Suppress("LoopToCallChain")
for (helper in AstTransformationRenameHelper.EP_NAME.extensions) {
val newMemberName = helper.getNewMemberName(member, newName)
if (newMemberName != null) return newMemberName
}
return null
}