From 85a54e5bfc898c40b030e01f09f5d4c20e29fbfc Mon Sep 17 00:00:00 2001 From: Daniil Ovchinnikov Date: Mon, 31 Oct 2016 18:02:56 +0300 Subject: [PATCH] [groovy] extract transformation-renaming logic to helper extension --- plugins/groovy/src/META-INF/plugin.xml | 3 + .../rename/RenameGrFieldProcessor.java | 159 +++--------------- .../AstTransformationRenameHelper.java | 53 ++++++ .../impl/DefaultRenameHelper.kt | 35 ++++ .../transformations/impl/TraitRenameHelper.kt | 31 ++++ .../groovy/transformations/renameHelper.kt | 32 ++++ 6 files changed, 175 insertions(+), 138 deletions(-) create mode 100644 plugins/groovy/src/org/jetbrains/plugins/groovy/transformations/AstTransformationRenameHelper.java create mode 100644 plugins/groovy/src/org/jetbrains/plugins/groovy/transformations/impl/DefaultRenameHelper.kt create mode 100644 plugins/groovy/src/org/jetbrains/plugins/groovy/transformations/impl/TraitRenameHelper.kt create mode 100644 plugins/groovy/src/org/jetbrains/plugins/groovy/transformations/renameHelper.kt diff --git a/plugins/groovy/src/META-INF/plugin.xml b/plugins/groovy/src/META-INF/plugin.xml index e3be1c0a5756..dd8cc4d42332 100644 --- a/plugins/groovy/src/META-INF/plugin.xml +++ b/plugins/groovy/src/META-INF/plugin.xml @@ -32,6 +32,7 @@ + @@ -132,12 +133,14 @@ + + diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/rename/RenameGrFieldProcessor.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/rename/RenameGrFieldProcessor.java index dc3b9c42320b..4ab4e1ee639d 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/rename/RenameGrFieldProcessor.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/rename/RenameGrFieldProcessor.java @@ -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 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 propertyUsages = MultiMap.createLinked(); - MultiMap simpleUsages = MultiMap.createLinked(); - - List unknownUsages = new ArrayList<>(); + GrField field = (GrField)psiElement; + Map 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 myNameMap = ContainerUtil.newHashMap(); - private final Map 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 diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/transformations/AstTransformationRenameHelper.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/transformations/AstTransformationRenameHelper.java new file mode 100644 index 000000000000..cb6dc0f92c36 --- /dev/null +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/transformations/AstTransformationRenameHelper.java @@ -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 EP_NAME = ExtensionPointName.create( + "org.intellij.groovy.astTransformationRenameHelper" + ); + + /** + * GrReferenceExpression may resolve to member generated by some AST Transformation from original member. + *

+ * Example: + *

+   *   class A {
+   *     def foo
+   *   }
+   *   new A().getFoo()
+   * 
+ * {@code getFoo} references accessor method generated from original foo field. + * Suppose we are renaming the field to bar. + * Then {@code member} will be the referenced accessor method and {@code newOriginalName} is the new name of the field, i.e. bar. + * Given this information {@link DefaultRenameHelper} returns the name of the new accessor + * that will be generated after rename, i.e. getBar. + * + * @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); +} diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/transformations/impl/DefaultRenameHelper.kt b/plugins/groovy/src/org/jetbrains/plugins/groovy/transformations/impl/DefaultRenameHelper.kt new file mode 100644 index 000000000000..d4d94b0ad122 --- /dev/null +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/transformations/impl/DefaultRenameHelper.kt @@ -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) + } + } +} \ No newline at end of file diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/transformations/impl/TraitRenameHelper.kt b/plugins/groovy/src/org/jetbrains/plugins/groovy/transformations/impl/TraitRenameHelper.kt new file mode 100644 index 000000000000..15c2a078b92b --- /dev/null +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/transformations/impl/TraitRenameHelper.kt @@ -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 + } +} \ No newline at end of file diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/transformations/renameHelper.kt b/plugins/groovy/src/org/jetbrains/plugins/groovy/transformations/renameHelper.kt new file mode 100644 index 000000000000..075b65c2b2f2 --- /dev/null +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/transformations/renameHelper.kt @@ -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 +} \ No newline at end of file