diff --git a/plugins/groovy/src/META-INF/plugin.xml b/plugins/groovy/src/META-INF/plugin.xml index 8bc87714a4cd..bc60ac50d75f 100644 --- a/plugins/groovy/src/META-INF/plugin.xml +++ b/plugins/groovy/src/META-INF/plugin.xml @@ -1444,6 +1444,8 @@ implementation="org.jetbrains.plugins.groovy.springloaded.SpringLoadedPositionManagerFactory"/> + diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/GroovyChangeContextUtil.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/GroovyChangeContextUtil.java index 422b5573c00c..0dd6b2c8b9f9 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/GroovyChangeContextUtil.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/GroovyChangeContextUtil.java @@ -18,6 +18,7 @@ package org.jetbrains.plugins.groovy.refactoring; import com.intellij.openapi.util.Key; import com.intellij.psi.*; +import com.intellij.psi.codeStyle.JavaCodeStyleManager; import com.intellij.psi.util.PsiTreeUtil; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement; @@ -114,6 +115,7 @@ public class GroovyChangeContextUtil { final PsiElement qualifier = refExpr.getQualifier(); if (!(qualifier instanceof GrReferenceExpression)) { refExpr.setQualifier(factory.createReferenceExpressionFromText(memberClass.getQualifiedName())); + JavaCodeStyleManager.getInstance(manager.getProject()).shortenClassReferences(refExpr.getQualifier()); return; } } @@ -140,4 +142,13 @@ public class GroovyChangeContextUtil { } } } + + public static void clearContextInfo(PsiElement scope) { + scope.putCopyableUserData(QUALIFIER_CLASS_KEY, null); + scope.putCopyableUserData(REF_TO_CLASS, null); + scope.putCopyableUserData(REF_TO_MEMBER, null); + for (PsiElement child = scope.getFirstChild(); child != null; child = child.getNextSibling()) { + clearContextInfo(child); + } + } } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/GroovyRefactoringSupportProvider.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/GroovyRefactoringSupportProvider.java index 5e2e2af4210c..b8b2857fb551 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/GroovyRefactoringSupportProvider.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/GroovyRefactoringSupportProvider.java @@ -41,6 +41,7 @@ import org.jetbrains.plugins.groovy.refactoring.introduce.constant.GrIntroduceCo import org.jetbrains.plugins.groovy.refactoring.introduce.field.GrIntroduceFieldHandler; import org.jetbrains.plugins.groovy.refactoring.introduce.parameter.GrIntroduceParameterHandler; import org.jetbrains.plugins.groovy.refactoring.introduce.variable.GrIntroduceVariableHandler; +import org.jetbrains.plugins.groovy.refactoring.memberPullUp.GrPullUpHandler; /** * @author ilyas @@ -129,4 +130,17 @@ public class GroovyRefactoringSupportProvider extends RefactoringSupportProvider public RefactoringActionHandler getIntroduceConstantHandler() { return new GrIntroduceConstantHandler(); } + + @Nullable + @Override + public RefactoringActionHandler getPullUpHandler() { + return new GrPullUpHandler(); + } + + @Nullable + @Override + public RefactoringActionHandler getExtractInterfaceHandler() { + //return new ExtractInterfaceHandler(); + return null; + } } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/classMembers/GrClassMemberReferenceVisitor.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/classMembers/GrClassMemberReferenceVisitor.java new file mode 100644 index 000000000000..92a073521bc2 --- /dev/null +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/classMembers/GrClassMemberReferenceVisitor.java @@ -0,0 +1,81 @@ +/* + * Copyright 2000-2013 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.refactoring.classMembers; + +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiElement; +import com.intellij.psi.util.PsiTreeUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.plugins.groovy.lang.psi.GrReferenceElement; +import org.jetbrains.plugins.groovy.lang.psi.GroovyRecursiveElementVisitor; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMember; +import org.jetbrains.plugins.groovy.lang.psi.api.types.GrCodeReferenceElement; +import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil; + +/** + * @author Max Medvedev + */ +public abstract class GrClassMemberReferenceVisitor extends GroovyRecursiveElementVisitor { + private final PsiClass myClass; + + public GrClassMemberReferenceVisitor(@NotNull PsiClass aClass) { + myClass = aClass; + } + + @Override + public void visitReferenceExpression(GrReferenceExpression ref) { + GrExpression qualifier = ref.getQualifier(); + + if (qualifier != null && !(PsiUtil.isThisOrSuperRef(qualifier))) { + qualifier.accept(this); + + if (!(qualifier instanceof GrReferenceExpression) || !(((GrReferenceExpression) qualifier).resolve() instanceof PsiClass)) { + return; + } + } + + PsiElement resolved = ref.resolve(); + if (resolved instanceof GrMember) { + PsiClass containingClass = ((GrMember)resolved).getContainingClass(); + if (isPartOf(myClass, containingClass)) { + visitClassMemberReferenceElement((GrMember)resolved, ref); + } + } + } + + @Override + public void visitCodeReferenceElement(GrCodeReferenceElement reference) { + PsiElement referencedElement = reference.resolve(); + if (referencedElement instanceof GrTypeDefinition) { + final GrTypeDefinition referencedClass = (GrTypeDefinition)referencedElement; + if (PsiTreeUtil.isAncestor(myClass, referencedElement, true) || + isPartOf(myClass, referencedClass.getContainingClass())) { + visitClassMemberReferenceElement((GrMember)referencedElement, reference); + } + } + } + + private static boolean isPartOf(@NotNull PsiClass aClass, @Nullable PsiClass containingClass) { + if (containingClass == null) return false; + return aClass.equals(containingClass) || aClass.isInheritor(containingClass, true); + } + + protected abstract void visitClassMemberReferenceElement(GrMember resolved, GrReferenceElement ref); +} diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/classMembers/GrDependantMembersCollector.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/classMembers/GrDependantMembersCollector.java new file mode 100644 index 000000000000..1159e2e809f2 --- /dev/null +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/classMembers/GrDependantMembersCollector.java @@ -0,0 +1,52 @@ +/* + * Copyright 2000-2013 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.refactoring.classMembers; + +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiMember; +import com.intellij.psi.PsiMethod; +import com.intellij.refactoring.classMembers.DependentMembersCollectorBase; +import org.jetbrains.plugins.groovy.lang.psi.GrReferenceElement; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMember; + +/** + * @author Max Medvedev + */ +public class GrDependantMembersCollector extends DependentMembersCollectorBase { + public GrDependantMembersCollector(PsiClass clazz, PsiClass superClass) { + super(clazz, superClass); + } + + @Override + public void collect(GrMember member) { + member.accept(new GrClassMemberReferenceVisitor(getClazz()) { + @Override + protected void visitClassMemberReferenceElement(GrMember classMember, GrReferenceElement ref) { + if (!existsInSuperClass(classMember)) { + myCollection.add(classMember); + } + } + }); + } + + private boolean existsInSuperClass(PsiMember classMember) { + if (getSuperClass() == null) return false; + if (!(classMember instanceof PsiMethod)) return false; + final PsiMethod method = ((PsiMethod)classMember); + final PsiMethod methodBySignature = (getSuperClass()).findMethodBySignature(method, true); + return methodBySignature != null; + } +} diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/classMembers/GrMemberInfo.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/classMembers/GrMemberInfo.java new file mode 100644 index 000000000000..e1caae834ace --- /dev/null +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/classMembers/GrMemberInfo.java @@ -0,0 +1,186 @@ +/* + * Copyright 2000-2013 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.refactoring.classMembers; + +import com.intellij.psi.*; +import com.intellij.psi.util.PsiFormatUtil; +import com.intellij.refactoring.RefactoringBundle; +import com.intellij.refactoring.classMembers.MemberInfoBase; +import com.intellij.util.containers.ContainerUtil; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrField; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrReferenceList; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMember; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +/** + * @author Max Medvedev + */ +public class GrMemberInfo extends MemberInfoBase { + public GrMemberInfo(GrMember member) { + this(member, false, null); + } + + public GrMemberInfo(GrMember member, boolean isSuperClass, GrReferenceList sourceReferenceList) { + super(member); + LOG.assertTrue(member.isValid()); + mySourceReferenceList = sourceReferenceList; + if (member instanceof GrMethod) { + GrMethod method = (GrMethod)member; + displayName = PsiFormatUtil.formatMethod(method, PsiSubstitutor.EMPTY, PsiFormatUtil.SHOW_NAME | PsiFormatUtil.SHOW_TYPE | PsiFormatUtil.TYPE_AFTER | PsiFormatUtil.SHOW_PARAMETERS, + PsiFormatUtil.SHOW_NAME | PsiFormatUtil.SHOW_TYPE | PsiFormatUtil.TYPE_AFTER); + PsiMethod[] superMethods = method.findSuperMethods(); + if (superMethods.length > 0) { + overrides = !superMethods[0].hasModifierProperty(PsiModifier.ABSTRACT) ? Boolean.TRUE : Boolean.FALSE; + } + else { + overrides = null; + } + + isStatic = method.hasModifierProperty(PsiModifier.STATIC); + } + else if (member instanceof GrField) { + GrField field = (GrField)member; + displayName = PsiFormatUtil + .formatVariable(field, PsiFormatUtil.SHOW_NAME | PsiFormatUtil.SHOW_TYPE | PsiFormatUtil.TYPE_AFTER, PsiSubstitutor.EMPTY); + isStatic = field.hasModifierProperty(PsiModifier.STATIC); + overrides = null; + } + else if (member instanceof GrTypeDefinition) { + GrTypeDefinition aClass = (GrTypeDefinition)member; + + if (isSuperClass) { + if (aClass.isInterface()) { + displayName = RefactoringBundle.message("member.info.implements.0", aClass.getName()); + overrides = Boolean.FALSE; + } + else { + displayName = RefactoringBundle.message("member.info.extends.0", aClass.getName()); + overrides = Boolean.TRUE; + } + } + else { + displayName = aClass.getName(); + overrides = null; + } + + isStatic = aClass.hasModifierProperty(PsiModifier.STATIC); + } + else { + LOG.assertTrue(false); + isStatic = false; + displayName = ""; + overrides = null; + } + } + + public GrReferenceList getSourceReferenceList() { + return mySourceReferenceList; + } + + public static List extractClassMembers(GrTypeDefinition subclass, Filter filter, boolean extractInterfacesDeep) { + List members = new ArrayList(); + extractClassMembers(subclass, members, filter, extractInterfacesDeep); + return members; + } + + public static void extractClassMembers(PsiClass subclass, + List result, + Filter filter, + final boolean extractInterfacesDeep) { + + if (!(subclass instanceof GrTypeDefinition)) return; + + + if (extractInterfacesDeep) { + extractSuperInterfaces(subclass, filter, result, ContainerUtil.newHashSet()); + } + else { + PsiClass[] interfaces = subclass.getInterfaces(); + GrReferenceList sourceRefList = subclass.isInterface() + ? ((GrTypeDefinition)subclass).getExtendsClause() + : ((GrTypeDefinition)subclass).getImplementsClause(); + for (PsiClass anInterface : interfaces) { + if (anInterface instanceof GrTypeDefinition && filter.includeMember((GrMember)anInterface)) { + result.add(new GrMemberInfo((GrMember)anInterface, true, sourceRefList)); + } + } + } + + + PsiClass[] innerClasses = subclass.getInnerClasses(); + for (PsiClass innerClass : innerClasses) { + if (innerClass instanceof GrTypeDefinition && filter.includeMember((GrMember)innerClass)) { + result.add(new GrMemberInfo((GrMember)innerClass)); + } + } + + GrMethod[] methods = ((GrTypeDefinition)subclass).getCodeMethods(); + for (GrMethod method : methods) { + if (!method.isConstructor() && filter.includeMember(method)) { + result.add(new GrMemberInfo(method)); + } + } + + GrField[] fields = ((GrTypeDefinition)subclass).getCodeFields(); + for (final GrField field : fields) { + if (filter.includeMember(field)) { + result.add(new GrMemberInfo(field)); + } + } + } + + private static void extractSuperInterfaces(final PsiClass subclass, + final Filter filter, + final List result, + Set processed) { + if (!processed.contains(subclass)) { + processed.add(subclass); + if (subclass instanceof GrTypeDefinition) { + extractSuperInterfacesFromReferenceList(((GrTypeDefinition)subclass).getExtendsClause(), filter, result, processed); + extractSuperInterfacesFromReferenceList(((GrTypeDefinition)subclass).getImplementsClause(), filter, result, processed); + } + } + } + + private static void extractSuperInterfacesFromReferenceList(final GrReferenceList referenceList, + final Filter filter, + final List result, + final Set processed) { + if (referenceList != null) { + final PsiClassType[] extendsListTypes = referenceList.getReferenceTypes(); + for (PsiClassType extendsListType : extendsListTypes) { + final PsiClass aSuper = extendsListType.resolve(); + if (aSuper instanceof GrTypeDefinition) { + if (aSuper.isInterface()) { + if (filter.includeMember((GrMember)aSuper)) { + result.add(new GrMemberInfo((GrMember)aSuper, true, referenceList)); + } + } + else { + extractSuperInterfaces(aSuper, filter, result, processed); + } + } + } + } + } + + private final GrReferenceList mySourceReferenceList; +} diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/classMembers/GrMemberInfoStorage.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/classMembers/GrMemberInfoStorage.java new file mode 100644 index 000000000000..e2ad5864e81a --- /dev/null +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/classMembers/GrMemberInfoStorage.java @@ -0,0 +1,89 @@ +/* + * Copyright 2000-2013 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.refactoring.classMembers; + +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiClassType; +import com.intellij.psi.util.MethodSignatureUtil; +import com.intellij.refactoring.classMembers.AbstractMemberInfoStorage; +import com.intellij.refactoring.classMembers.MemberInfoBase; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrField; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrExtendsClause; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrImplementsClause; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMember; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod; + +import java.util.ArrayList; + +/** + * @author Max Medvedev + */ +public class GrMemberInfoStorage extends AbstractMemberInfoStorage { + public GrMemberInfoStorage(GrTypeDefinition aClass, MemberInfoBase.Filter memberInfoFilter) { + super(aClass, memberInfoFilter); + } + + @Override + protected boolean isInheritor(PsiClass baseClass, PsiClass aClass) { + return aClass.isInheritor(baseClass, true); + } + + @Override + protected void extractClassMembers(PsiClass aClass, ArrayList temp) { + GrMemberInfo.extractClassMembers(aClass, temp, myFilter, false); + } + + @Override + protected boolean memberConflict(GrMember member1, GrMember member) { + if (member instanceof GrMethod && member1 instanceof GrMethod) { + return MethodSignatureUtil.areSignaturesEqual((GrMethod)member, (GrMethod)member1); + } + else if (member instanceof GrField && member1 instanceof GrField || + member instanceof GrTypeDefinition && member1 instanceof GrTypeDefinition) { + return member.getName().equals(member1.getName()); + } + + return false; + } + + @Override + protected void buildSubClassesMap(PsiClass aClass) { + if (aClass instanceof GrTypeDefinition) { + final GrExtendsClause extendsList = ((GrTypeDefinition)aClass).getExtendsClause(); + if (extendsList != null) { + buildSubClassesMapForList(extendsList.getReferenceTypes(), (GrTypeDefinition)aClass); + } + + final GrImplementsClause implementsList = ((GrTypeDefinition)aClass).getImplementsClause(); + if (implementsList != null) { + buildSubClassesMapForList(implementsList.getReferenceTypes(), (GrTypeDefinition)aClass); + } + } + } + + private void buildSubClassesMapForList(final PsiClassType[] classesList, GrTypeDefinition aClass) { + for (int i = 0; i < classesList.length; i++) { + PsiClassType element = classesList[i]; + PsiClass resolved = element.resolve(); + if (resolved instanceof GrTypeDefinition) { + GrTypeDefinition superClass = (GrTypeDefinition)resolved; + getSubclasses(superClass).add(aClass); + buildSubClassesMap(superClass); + } + } + } +} diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/classMembers/GrMemberSelectionTable.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/classMembers/GrMemberSelectionTable.java new file mode 100644 index 000000000000..e50828468907 --- /dev/null +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/classMembers/GrMemberSelectionTable.java @@ -0,0 +1,114 @@ +/* + * Copyright 2000-2013 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.refactoring.classMembers; + +import com.intellij.icons.AllIcons; +import com.intellij.psi.PsiMember; +import com.intellij.psi.PsiMethod; +import com.intellij.psi.PsiModifier; +import com.intellij.psi.PsiModifierList; +import com.intellij.refactoring.classMembers.MemberInfoModel; +import com.intellij.refactoring.ui.AbstractMemberSelectionTable; +import com.intellij.ui.RowIcon; +import com.intellij.util.IconUtil; +import com.intellij.util.VisibilityIcons; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMember; + +import javax.swing.*; +import java.util.List; + +/** + * @author Max Medvedev + */ +public class GrMemberSelectionTable extends AbstractMemberSelectionTable { + + public GrMemberSelectionTable(final List memberInfos, String abstractColumnHeader) { + this(memberInfos, null, abstractColumnHeader); + } + + public GrMemberSelectionTable(final List memberInfos, MemberInfoModel memberInfoModel, String abstractColumnHeader) { + super(memberInfos, memberInfoModel, abstractColumnHeader); + } + + @Nullable + @Override + protected Object getAbstractColumnValue(GrMemberInfo memberInfo) { + if (!(memberInfo.getMember() instanceof PsiMethod)) return null; + if (memberInfo.isStatic()) return null; + + PsiMethod method = (PsiMethod)memberInfo.getMember(); + if (method.hasModifierProperty(PsiModifier.ABSTRACT)) { + final Boolean fixedAbstract = myMemberInfoModel.isFixedAbstract(memberInfo); + if (fixedAbstract != null) return fixedAbstract; + } + + if (!myMemberInfoModel.isAbstractEnabled(memberInfo)) { + return myMemberInfoModel.isAbstractWhenDisabled(memberInfo); + } + else { + return memberInfo.isToAbstract() ? Boolean.TRUE : Boolean.FALSE; + } + } + + @Override + protected boolean isAbstractColumnEditable(int rowIndex) { + GrMemberInfo info = myMemberInfos.get(rowIndex); + if (!(info.getMember() instanceof PsiMethod)) return false; + if (info.isStatic()) return false; + + PsiMethod method = (PsiMethod)info.getMember(); + if (method.hasModifierProperty(PsiModifier.ABSTRACT)) { + if (myMemberInfoModel.isFixedAbstract(info) != null) { + return false; + } + } + + return info.isChecked() && myMemberInfoModel.isAbstractEnabled(info); + } + + + @Override + protected void setVisibilityIcon(GrMemberInfo memberInfo, RowIcon icon) { + PsiMember member = memberInfo.getMember(); + PsiModifierList modifiers = member != null ? member.getModifierList() : null; + if (modifiers != null) { + VisibilityIcons.setVisibilityIcon(modifiers, icon); + } + else { + icon.setIcon(IconUtil.getEmptyIcon(true), VISIBILITY_ICON_POSITION); + } + } + + @Override + protected Icon getOverrideIcon(GrMemberInfo memberInfo) { + PsiMember member = memberInfo.getMember(); + Icon overrideIcon = EMPTY_OVERRIDE_ICON; + if (member instanceof PsiMethod) { + if (Boolean.TRUE.equals(memberInfo.getOverrides())) { + overrideIcon = AllIcons.General.OverridingMethod; + } + else if (Boolean.FALSE.equals(memberInfo.getOverrides())) { + overrideIcon = AllIcons.General.ImplementingMethod; + } + else { + overrideIcon = EMPTY_OVERRIDE_ICON; + } + } + return overrideIcon; + } +} + diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/classMembers/GroovyClassMembersRefactoringSupport.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/classMembers/GroovyClassMembersRefactoringSupport.java new file mode 100644 index 000000000000..46d9b72ff67a --- /dev/null +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/classMembers/GroovyClassMembersRefactoringSupport.java @@ -0,0 +1,36 @@ +/* + * Copyright 2000-2013 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.refactoring.classMembers; + +import com.intellij.psi.PsiClass; +import com.intellij.refactoring.classMembers.ClassMembersRefactoringSupport; +import com.intellij.refactoring.classMembers.DependentMembersCollectorBase; +import com.intellij.refactoring.classMembers.MemberInfoBase; + +/** + * @author Max Medvedev + */ +public class GroovyClassMembersRefactoringSupport implements ClassMembersRefactoringSupport { + @Override + public DependentMembersCollectorBase createDependentMembersCollector(Object clazz, Object superClass) { + return new GrDependantMembersCollector((PsiClass)clazz, (PsiClass)superClass); + } + + @Override + public boolean isProperMember(MemberInfoBase member) { + return member instanceof GrMemberInfo; + } +} diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/extractInterface/GrExtractInterfaceHandler.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/extractInterface/GrExtractInterfaceHandler.java new file mode 100644 index 000000000000..1b7a112e49f5 --- /dev/null +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/extractInterface/GrExtractInterfaceHandler.java @@ -0,0 +1,164 @@ +/* + * Copyright 2000-2013 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.refactoring.extractInterface; + +import com.intellij.history.LocalHistory; +import com.intellij.history.LocalHistoryAction; +import com.intellij.lang.findUsages.DescriptiveNameUtil; +import com.intellij.openapi.actionSystem.DataContext; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.editor.ScrollType; +import com.intellij.openapi.project.Project; +import com.intellij.psi.*; +import com.intellij.refactoring.HelpID; +import com.intellij.refactoring.RefactoringActionHandler; +import com.intellij.refactoring.RefactoringBundle; +import com.intellij.refactoring.extractInterface.ExtractClassUtil; +import com.intellij.refactoring.extractSuperclass.ExtractSuperClassUtil; +import com.intellij.refactoring.lang.ElementsHandler; +import com.intellij.refactoring.memberPullUp.PullUpHelper; +import com.intellij.refactoring.util.CommonRefactoringUtil; +import com.intellij.refactoring.util.DocCommentPolicy; +import com.intellij.refactoring.util.classMembers.MemberInfo; +import com.intellij.util.IncorrectOperationException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrAnonymousClassDefinition; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition; + +import javax.swing.*; + +/** + * @author Max Medvedev + */ +public class GrExtractInterfaceHandler implements RefactoringActionHandler, ElementsHandler { + public void invoke(@NotNull Project project, Editor editor, PsiFile file, DataContext dataContext) { + int offset = editor.getCaretModel().getOffset(); + editor.getScrollingModel().scrollToCaret(ScrollType.MAKE_VISIBLE); + PsiElement element = file.findElementAt(offset); + while (true) { + if (element == null || element instanceof PsiFile) { + String message = RefactoringBundle.getCannotRefactorMessage(RefactoringBundle.message("error.wrong.caret.position.class")); + CommonRefactoringUtil.showErrorHint(project, editor, message, REFACTORING_NAME, HelpID.EXTRACT_INTERFACE); + return; + } + + if (element instanceof GrTypeDefinition && !(element instanceof GrAnonymousClassDefinition)) { + invoke(project, new PsiElement[]{element}, dataContext); + return; + } + + element = element.getParent(); + } + } + + public void invoke(@NotNull final Project project, @NotNull PsiElement[] elements, DataContext dataContext) { + if (elements.length != 1) return; + + + myProject = project; + myClass = (PsiClass)elements[0]; + + + if (!CommonRefactoringUtil.checkReadOnlyStatus(project, myClass)) return; + +/* todo + final JavaExtractSuperBaseDialog dialog = new ExtractInterfaceDialog(myProject, myClass); + dialog.show(); + if (!dialog.isOK() || !dialog.isExtractSuperclass()) return; + + final MultiMap conflicts = new MultiMap(); + ExtractSuperClassUtil.checkSuperAccessible(dialog.getTargetDirectory(), conflicts, myClass); + if (!ExtractSuperClassUtil.showConflicts(dialog, conflicts, myProject)) return; + + CommandProcessor.getInstance().executeCommand(myProject, new Runnable() { + public void run() { + ApplicationManager.getApplication().runWriteAction(new Runnable() { + public void run() { + myInterfaceName = dialog.getExtractedSuperName(); + mySelectedMembers = ArrayUtil.toObjectArray(dialog.getSelectedMemberInfos(), MemberInfo.class); + myTargetDir = dialog.getTargetDirectory(); + myJavaDocPolicy = new DocCommentPolicy(dialog.getDocCommentPolicy()); + try { + doRefactoring(); + } + catch (IncorrectOperationException e) { + LOG.error(e); + } + } + }); + } + }, REFACTORING_NAME, null);*/ + } + + private void doRefactoring() throws IncorrectOperationException { + LocalHistoryAction a = LocalHistory.getInstance().startAction(getCommandName()); + final PsiClass anInterface; + try { + anInterface = extractInterface(myTargetDir, myClass, myInterfaceName, mySelectedMembers, myJavaDocPolicy); + } + finally { + a.finish(); + } + + + if (anInterface != null) { + final SmartPsiElementPointer classPointer = + SmartPointerManager.getInstance(myProject).createSmartPsiElementPointer(myClass); + final SmartPsiElementPointer interfacePointer = + SmartPointerManager.getInstance(myProject).createSmartPsiElementPointer(anInterface); + final Runnable turnRefsToSuperRunnable = new Runnable() { + @Override + public void run() { + ExtractClassUtil.askAndTurnRefsToSuper(myProject, classPointer, interfacePointer); + } + }; + SwingUtilities.invokeLater(turnRefsToSuperRunnable); + } + } + + public static PsiClass extractInterface(PsiDirectory targetDir, + PsiClass aClass, + String interfaceName, + MemberInfo[] selectedMembers, + DocCommentPolicy javaDocPolicy) throws IncorrectOperationException { + PsiClass anInterface = JavaDirectoryService.getInstance().createInterface(targetDir, interfaceName); + PsiJavaCodeReferenceElement ref = ExtractSuperClassUtil.createExtendingReference(anInterface, aClass, selectedMembers); + final PsiReferenceList referenceList = aClass.isInterface() ? aClass.getExtendsList() : aClass.getImplementsList(); + assert referenceList != null; + referenceList.add(ref); + PullUpHelper pullUpHelper = new PullUpHelper(aClass, anInterface, selectedMembers, javaDocPolicy); + pullUpHelper.moveMembersToBase(); + return anInterface; + } + + private String getCommandName() { + return RefactoringBundle.message("extract.interface.command.name", myInterfaceName, DescriptiveNameUtil.getDescriptiveName(myClass)); + } + + public boolean isEnabledOnElements(PsiElement[] elements) { + return elements.length == 1 && elements[0] instanceof PsiClass; + } + + private static final Logger LOG = Logger.getInstance(GrExtractInterfaceHandler.class); + public static final String REFACTORING_NAME = RefactoringBundle.message("extract.interface.title"); + private Project myProject; + private PsiClass myClass; + private String myInterfaceName; + private MemberInfo[] mySelectedMembers; + private PsiDirectory myTargetDir; + private DocCommentPolicy myJavaDocPolicy; +} diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/memberPullUp/GrPullUpDialog.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/memberPullUp/GrPullUpDialog.java new file mode 100644 index 000000000000..5fc3aa9dbf80 --- /dev/null +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/memberPullUp/GrPullUpDialog.java @@ -0,0 +1,196 @@ +/* + * Copyright 2000-2013 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.refactoring.memberPullUp; + +import com.intellij.openapi.help.HelpManager; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Comparing; +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiDocCommentOwner; +import com.intellij.psi.PsiMember; +import com.intellij.psi.PsiMethod; +import com.intellij.psi.statistics.StatisticsInfo; +import com.intellij.psi.statistics.StatisticsManager; +import com.intellij.refactoring.HelpID; +import com.intellij.refactoring.JavaRefactoringSettings; +import com.intellij.refactoring.RefactoringBundle; +import com.intellij.refactoring.classMembers.AbstractUsesDependencyMemberInfoModel; +import com.intellij.refactoring.classMembers.MemberInfoModel; +import com.intellij.refactoring.memberPullUp.PullUpDialogBase; +import com.intellij.refactoring.memberPullUp.PullUpHelper; +import com.intellij.refactoring.ui.AbstractMemberSelectionTable; +import com.intellij.refactoring.ui.ClassCellRenderer; +import com.intellij.refactoring.ui.DocCommentPanel; +import com.intellij.refactoring.util.DocCommentPolicy; +import com.intellij.refactoring.util.RefactoringHierarchyUtil; +import com.intellij.refactoring.util.classMembers.InterfaceContainmentVerifier; +import com.intellij.util.ui.UIUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMember; +import org.jetbrains.plugins.groovy.refactoring.classMembers.GrMemberInfo; +import org.jetbrains.plugins.groovy.refactoring.classMembers.GrMemberInfoStorage; +import org.jetbrains.plugins.groovy.refactoring.classMembers.GrMemberSelectionTable; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; +import java.util.List; + +/** + * @author Max Medvedev + */ +class GrPullUpDialog extends PullUpDialogBase { + private final Callback myCallback; + private DocCommentPanel myJavaDocPanel; + + private final InterfaceContainmentVerifier myInterfaceContainmentVerifier = new InterfaceContainmentVerifier() { + public boolean checkedInterfacesContain(PsiMethod psiMethod) { + return PullUpHelper.checkedInterfacesContain(myMemberInfos, psiMethod); + } + }; + + private static final String PULL_UP_STATISTICS_KEY = "pull.up##"; + + public interface Callback { + boolean checkConflicts(GrPullUpDialog dialog); + } + + public GrPullUpDialog(Project project, + PsiClass typeDefinition, + List superClasses, + GrMemberInfoStorage storage, + GrPullUpHandler handler) { + super(project, typeDefinition, superClasses, storage, GrPullUpHandler.REFACTORING_NAME); + + myCallback = handler; + + init(); + } + + public int getJavaDocPolicy() { + return myJavaDocPanel.getPolicy(); + } + + protected String getDimensionServiceKey() { + return "#com.intellij.refactoring.memberPullUp.PullUpDialog"; + } + + InterfaceContainmentVerifier getContainmentVerifier() { + return myInterfaceContainmentVerifier; + } + + @Override + protected void initClassCombo(JComboBox classCombo) { + classCombo.setRenderer(new ClassCellRenderer(classCombo.getRenderer())); + classCombo.addItemListener(new ItemListener() { + public void itemStateChanged(ItemEvent e) { + if (e.getStateChange() == ItemEvent.SELECTED) { + if (myMemberSelectionPanel != null) { + // ((MyMemberInfoModel)myMemberInfoModel).setSuperClass(getSuperClass()); + myMemberSelectionPanel.getTable().setMemberInfos(myMemberInfos); + myMemberSelectionPanel.getTable().fireExternalDataChange(); + } + } + } + }); + } + + protected PsiClass getPreselection() { + PsiClass preselection = RefactoringHierarchyUtil.getNearestBaseClass(myClass, false); + + final String statKey = PULL_UP_STATISTICS_KEY + myClass.getQualifiedName(); + for (StatisticsInfo info : StatisticsManager.getInstance().getAllValues(statKey)) { + final String superClassName = info.getValue(); + PsiClass superClass = null; + for (PsiClass aClass : mySuperClasses) { + if (Comparing.strEqual(superClassName, aClass.getQualifiedName())) { + superClass = aClass; + break; + } + } + if (superClass != null && StatisticsManager.getInstance().getUseCount(info) > 0) { + preselection = superClass; + break; + } + } + return preselection; + } + + protected void doHelpAction() { + HelpManager.getInstance().invokeHelp(HelpID.MEMBERS_PULL_UP); + } + + protected void doAction() { + if (!myCallback.checkConflicts(this)) return; + JavaRefactoringSettings.getInstance().PULL_UP_MEMBERS_JAVADOC = myJavaDocPanel.getPolicy(); + final PsiClass superClass = getSuperClass(); + String name = superClass.getQualifiedName(); + if (name != null) { + StatisticsManager + .getInstance().incUseCount(new StatisticsInfo(PULL_UP_STATISTICS_KEY + myClass.getQualifiedName(), name)); + } + + List infos = getSelectedMemberInfos(); + GrPullUpHelper processor = + new GrPullUpHelper(myClass, superClass, infos.toArray(new GrMemberInfo[infos.size()]), new DocCommentPolicy(getJavaDocPolicy())); + invokeRefactoring(processor); + close(OK_EXIT_CODE); + } + + @Override + protected void addCustomElementsToCentralPanel(JPanel panel) { + myJavaDocPanel = new DocCommentPanel(RefactoringBundle.message("javadoc.for.abstracts")); + myJavaDocPanel.setPolicy(JavaRefactoringSettings.getInstance().PULL_UP_MEMBERS_JAVADOC); + boolean hasJavadoc = false; + for (GrMemberInfo info : myMemberInfos) { + final PsiMember member = info.getMember(); + if (myMemberInfoModel.isAbstractEnabled(info) && + member instanceof PsiDocCommentOwner && + ((PsiDocCommentOwner)member).getDocComment() != null) { + hasJavadoc = true; + break; + } + } + UIUtil.setEnabled(myJavaDocPanel, hasJavadoc, true); + panel.add(myJavaDocPanel, BorderLayout.EAST); + } + + @Override + protected AbstractMemberSelectionTable createMemberSelectionTable(List infos) { + return new GrMemberSelectionTable(infos, RefactoringBundle.message("make.abstract")); + } + + @Override + protected MemberInfoModel createMemberInfoModel() { + //return new UsedByDependencyMemberInfoModel(myClass); + return new MyMemberInfoModel(myClass, getSuperClass(), false); + } + + private static class MyMemberInfoModel extends AbstractUsesDependencyMemberInfoModel { + public MyMemberInfoModel(PsiClass aClass, PsiClass superClass, boolean recursive) { + super(aClass, superClass, recursive); + } + + @Override + protected int doCheck(@NotNull GrMemberInfo memberInfo, int problem) { + if (problem == ERROR && memberInfo.isStatic()) { + return WARNING; + } + return problem; + } + } +} diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/memberPullUp/GrPullUpHandler.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/memberPullUp/GrPullUpHandler.java new file mode 100644 index 000000000000..533615048f0a --- /dev/null +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/memberPullUp/GrPullUpHandler.java @@ -0,0 +1,201 @@ +/* + * Copyright 2000-2013 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.refactoring.memberPullUp; + +import com.intellij.openapi.actionSystem.DataContext; +import com.intellij.openapi.actionSystem.PlatformDataKeys; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.editor.ScrollType; +import com.intellij.openapi.progress.ProgressManager; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.DialogWrapper; +import com.intellij.psi.*; +import com.intellij.refactoring.HelpID; +import com.intellij.refactoring.RefactoringActionHandler; +import com.intellij.refactoring.RefactoringBundle; +import com.intellij.refactoring.classMembers.MemberInfoBase; +import com.intellij.refactoring.lang.ElementsHandler; +import com.intellij.refactoring.memberPullUp.PullUpConflictsUtil; +import com.intellij.refactoring.ui.ConflictsDialog; +import com.intellij.refactoring.util.CommonRefactoringUtil; +import com.intellij.refactoring.util.RefactoringHierarchyUtil; +import com.intellij.util.containers.MultiMap; +import org.codehaus.groovy.runtime.DefaultGroovyMethods; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrField; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMember; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod; +import org.jetbrains.plugins.groovy.refactoring.classMembers.GrMemberInfo; +import org.jetbrains.plugins.groovy.refactoring.classMembers.GrMemberInfoStorage; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author Max Medvedev + */ +public class GrPullUpHandler implements RefactoringActionHandler, GrPullUpDialog.Callback, ElementsHandler { + private static final Logger LOG = Logger.getInstance(GrPullUpHandler.class); + public static final String REFACTORING_NAME = RefactoringBundle.message("pull.members.up.title"); + + private PsiClass mySubclass; + private Project myProject; + + public void invoke(@NotNull Project project, Editor editor, PsiFile file, DataContext dataContext) { + int offset = editor.getCaretModel().getOffset(); + editor.getScrollingModel().scrollToCaret(ScrollType.MAKE_VISIBLE); + PsiElement element = file.findElementAt(offset); + + while (true) { + if (element == null || element instanceof PsiFile) { + String message = RefactoringBundle + .getCannotRefactorMessage(RefactoringBundle.message("the.caret.should.be.positioned.inside.a.class.to.pull.members.from")); + CommonRefactoringUtil.showErrorHint(project, editor, message, REFACTORING_NAME, HelpID.MEMBERS_PULL_UP); + return; + } + + + if (!CommonRefactoringUtil.checkReadOnlyStatus(project, element)) return; + + + if (element instanceof GrTypeDefinition || element instanceof GrField || element instanceof GrMethod) { + invoke(project, new PsiElement[]{element}, dataContext); + return; + } + + element = element.getParent(); + } + } + + public void invoke(@NotNull final Project project, @NotNull PsiElement[] elements, DataContext dataContext) { + if (elements.length != 1) return; + + myProject = project; + + PsiElement element = elements[0]; + GrTypeDefinition aClass; + PsiElement aMember = null; + + if (element instanceof GrTypeDefinition) { + aClass = (GrTypeDefinition)element; + } + else if (element instanceof GrMethod) { + aClass = DefaultGroovyMethods.asType(((GrMethod)element).getContainingClass(), GrTypeDefinition.class); + aMember = element; + } + else if (element instanceof GrField) { + aClass = DefaultGroovyMethods.asType(((GrField)element).getContainingClass(), GrTypeDefinition.class); + aMember = element; + } + else { + return; + } + + + invokeImpl(project, dataContext, aClass, aMember); + } + + private void invokeImpl(Project project, DataContext dataContext, GrTypeDefinition aClass, PsiElement aMember) { + final Editor editor = dataContext != null ? PlatformDataKeys.EDITOR.getData(dataContext) : null; + if (aClass == null) { + String message = + RefactoringBundle.getCannotRefactorMessage(RefactoringBundle.message("is.not.supported.in.the.current.context", REFACTORING_NAME)); + CommonRefactoringUtil.showErrorHint(project, editor, message, REFACTORING_NAME, HelpID.MEMBERS_PULL_UP); + return; + } + + + ArrayList bases = RefactoringHierarchyUtil.createBasesList(aClass, false, true); + + if (bases.isEmpty()) { + final GrTypeDefinition containingClass = DefaultGroovyMethods.asType(aClass.getContainingClass(), GrTypeDefinition.class); + if (containingClass != null) { + invokeImpl(project, dataContext, containingClass, aClass); + return; + } + + String message = RefactoringBundle.getCannotRefactorMessage( + RefactoringBundle.message("class.does.not.have.base.classes.interfaces.in.current.project", aClass.getQualifiedName())); + CommonRefactoringUtil.showErrorHint(project, editor, message, REFACTORING_NAME, HelpID.MEMBERS_PULL_UP); + return; + } + + + mySubclass = aClass; + GrMemberInfoStorage memberInfoStorage = new GrMemberInfoStorage((GrTypeDefinition)mySubclass, new MemberInfoBase.Filter() { + public boolean includeMember(GrMember element) { + return true; + } + }); + List members = memberInfoStorage.getClassMemberInfos(mySubclass); + PsiManager manager = mySubclass.getManager(); + + for (GrMemberInfo member : members) { + if (manager.areElementsEquivalent(member.getMember(), aMember)) { + member.setChecked(true); + break; + } + } + + + final GrPullUpDialog dialog = new GrPullUpDialog(project, aClass, bases, memberInfoStorage, this); + dialog.show(); + } + + public boolean checkConflicts(final GrPullUpDialog dialog) { + /* todo */ + List _infos = dialog.getSelectedMemberInfos(); + final GrMemberInfo[] infos = _infos.toArray(new GrMemberInfo[_infos.size()]); + final PsiClass superClass = dialog.getSuperClass(); + if (!checkWritable(superClass, infos)) return false; + final MultiMap conflicts = new MultiMap(); + if (!ProgressManager.getInstance().runProcessWithProgressSynchronously(new Runnable() { + public void run() { + final PsiDirectory targetDirectory = superClass.getContainingFile().getContainingDirectory(); + final PsiPackage targetPackage = targetDirectory != null ? JavaDirectoryService.getInstance().getPackage(targetDirectory) : null; + conflicts.putAllValues(PullUpConflictsUtil.checkConflicts(infos, mySubclass, superClass, targetPackage, targetDirectory, + dialog.getContainmentVerifier())); + } + }, RefactoringBundle.message("detecting.possible.conflicts"), true, myProject)) { + return false; + } + if (!conflicts.isEmpty()) { + ConflictsDialog conflictsDialog = new ConflictsDialog(myProject, conflicts); + conflictsDialog.show(); + final boolean ok = conflictsDialog.isOK(); + if (!ok && conflictsDialog.isShowConflicts()) dialog.close(DialogWrapper.CANCEL_EXIT_CODE); + return ok; + } + + return true; + } + + private boolean checkWritable(PsiClass superClass, GrMemberInfo[] infos) { + if (!CommonRefactoringUtil.checkReadOnlyStatus(myProject, superClass)) return false; + for (GrMemberInfo info : infos) { + if (info.getMember() instanceof PsiClass && info.getOverrides() != null) continue; + if (!CommonRefactoringUtil.checkReadOnlyStatus(myProject, info.getMember())) return false; + } + + return true; + } + + public boolean isEnabledOnElements(PsiElement[] elements) { + return elements.length == 1 && elements[0] instanceof PsiClass; + } +} diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/memberPullUp/GrPullUpHelper.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/memberPullUp/GrPullUpHelper.java new file mode 100644 index 000000000000..0a9972ca276d --- /dev/null +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/memberPullUp/GrPullUpHelper.java @@ -0,0 +1,711 @@ +/* + * Copyright 2000-2013 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.refactoring.memberPullUp; + +import com.intellij.codeInsight.AnnotationUtil; +import com.intellij.codeInsight.intention.AddAnnotationFix; +import com.intellij.lang.findUsages.DescriptiveNameUtil; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.util.Key; +import com.intellij.psi.*; +import com.intellij.psi.codeStyle.CodeStyleSettings; +import com.intellij.psi.codeStyle.CodeStyleSettingsManager; +import com.intellij.psi.codeStyle.JavaCodeStyleManager; +import com.intellij.psi.search.LocalSearchScope; +import com.intellij.psi.search.searches.OverridingMethodsSearch; +import com.intellij.psi.search.searches.ReferencesSearch; +import com.intellij.psi.util.MethodSignatureUtil; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.util.PsiUtil; +import com.intellij.psi.util.TypeConversionUtil; +import com.intellij.refactoring.BaseRefactoringProcessor; +import com.intellij.refactoring.RefactoringBundle; +import com.intellij.refactoring.listeners.JavaRefactoringListenerManager; +import com.intellij.refactoring.listeners.impl.JavaRefactoringListenerManagerImpl; +import com.intellij.refactoring.util.DocCommentPolicy; +import com.intellij.refactoring.util.RefactoringHierarchyUtil; +import com.intellij.refactoring.util.RefactoringUIUtil; +import com.intellij.refactoring.util.RefactoringUtil; +import com.intellij.usageView.UsageInfo; +import com.intellij.usageView.UsageViewDescriptor; +import com.intellij.util.IncorrectOperationException; +import com.intellij.util.containers.ContainerUtil; +import org.codehaus.groovy.runtime.DefaultGroovyMethods; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.plugins.groovy.lang.psi.GrReferenceElement; +import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement; +import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory; +import org.jetbrains.plugins.groovy.lang.psi.GroovyRecursiveElementVisitor; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrField; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrExtendsClause; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrImplementsClause; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrReferenceList; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMember; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod; +import org.jetbrains.plugins.groovy.lang.psi.api.types.GrCodeReferenceElement; +import org.jetbrains.plugins.groovy.refactoring.GroovyChangeContextUtil; +import org.jetbrains.plugins.groovy.refactoring.classMembers.GrClassMemberReferenceVisitor; +import org.jetbrains.plugins.groovy.refactoring.classMembers.GrMemberInfo; + +import java.util.*; + +/** + * @author Max Medvedev + */ +public class GrPullUpHelper extends BaseRefactoringProcessor { + private static final Logger LOG = Logger.getInstance(GrPullUpHelper.class); + private static final Key SUPER_REF = Key.create("SUPER_REF"); + private static final Key THIS_REF = Key.create("THIS_REF"); + private static final Key PRESERVE_QUALIFIER = Key.create("PRESERVE_QUALIFIER"); + + private PsiClass mySourceClass; + private GrTypeDefinition myTargetSuperClass; + private GrMemberInfo[] myMembersToMove; + private DocCommentPolicy myDocCommentPolicy; + private Set myMembersAfterMove; + + + public GrPullUpHelper(PsiClass aClass, PsiClass superClass, GrMemberInfo[] infos, DocCommentPolicy policy) { + super(aClass.getProject()); + + mySourceClass = aClass; + myTargetSuperClass = (GrTypeDefinition)superClass; + myMembersToMove = infos; + myDocCommentPolicy = policy; + } + + @NotNull + @Override + protected UsageViewDescriptor createUsageViewDescriptor(UsageInfo[] usages) { + return new UsageViewDescriptor() { + public String getProcessedElementsHeader() { + return "Pull up members from"; + } + + @NotNull + public PsiElement[] getElements() { + return new PsiClass[]{mySourceClass}; + } + + public String getCodeReferencesText(int usagesCount, int filesCount) { + return "Class to pull up members to \"" + RefactoringUIUtil.getDescription(myTargetSuperClass, true) + "\""; + } + + public String getCommentReferencesText(int usagesCount, int filesCount) { + return null; + } + }; + } + + @NotNull + @Override + protected UsageInfo[] findUsages() { + final List result = new ArrayList(); + for (GrMemberInfo info : myMembersToMove) { + final PsiMember member = info.getMember(); + if (member.hasModifierProperty(PsiModifier.STATIC)) { + for (PsiReference reference : ReferencesSearch.search(member)) { + result.add(new UsageInfo(reference)); + } + } + } + + return DefaultGroovyMethods.asType(result, UsageInfo[].class); + } + + @Override + protected void performRefactoring(UsageInfo[] usages) { + moveMembersToBase(); + //moveFieldInitializations(); todo + for (UsageInfo usage : usages) { + PsiElement element = usage.getElement(); + if (element instanceof GrReferenceExpression) { + GrExpression qualifier = ((GrReferenceExpression)element).getQualifier(); + if (qualifier instanceof GrReferenceExpression && ((GrReferenceExpression)qualifier).resolve().equals(mySourceClass)) { + ((GrReferenceExpression)qualifier).bindToElement(myTargetSuperClass); + } + } + } + + /* + todo + ApplicationManager.application.invokeLater(new Runnable() { + @Override + public void run() { + processMethodsDuplicates(); + } + }, ModalityState.NON_MODAL, myProject.getDisposed());*/ + + } + + @Override + protected String getCommandName() { + return RefactoringBundle.message("pullUp.command", DescriptiveNameUtil.getDescriptiveName(mySourceClass)); + } + + public void moveMembersToBase() throws IncorrectOperationException { + final HashSet movedMembers = ContainerUtil.newHashSet(); + myMembersAfterMove = ContainerUtil.newHashSet(); + + // build aux sets + for (GrMemberInfo info : myMembersToMove) { + movedMembers.add(info.getMember()); + } + + + // correct private member visibility + for (GrMemberInfo info : myMembersToMove) { + if (info.getMember() instanceof PsiClass && info.getOverrides() != null) continue; + setCorrectVisibility(movedMembers, info); + GroovyChangeContextUtil.encodeContextInfo(info.getMember()); + info.getMember().accept(new QualifiedThisSuperSearcher()); + fixReferencesToStatic(info.getMember(), movedMembers); + } + + + + + final PsiSubstitutor substitutor = upDownSuperClassSubstitutor(); + + // do actual move + for (GrMemberInfo info : myMembersToMove) { + if (info.getMember() instanceof PsiMethod) { + doMoveMethod(movedMembers, substitutor, info); + } + else if (info.getMember() instanceof GrField) { + doMoveField(movedMembers, substitutor, info); + } + else if (info.getMember() instanceof PsiClass) { + doMoveClass(movedMembers, substitutor, info); + } + } + + + ExplicitSuperDeleter explicitSuperDeleter = new ExplicitSuperDeleter(); + for (PsiMember member : myMembersAfterMove) { + ((GrMember)member).accept(explicitSuperDeleter); + } + explicitSuperDeleter.fixSupers(); + + final QualifiedThisSuperAdjuster qualifiedThisSuperAdjuster = new QualifiedThisSuperAdjuster(); + for (PsiMember member : myMembersAfterMove) { + ((GrMember)member).accept(qualifiedThisSuperAdjuster); + } + + for (PsiMember member : myMembersAfterMove) { + GroovyChangeContextUtil.decodeContextInfo(member, null, null); + } + + + final JavaRefactoringListenerManagerImpl listenerManager = (JavaRefactoringListenerManagerImpl)JavaRefactoringListenerManager.getInstance(myProject); + for (final PsiMember movedMember : myMembersAfterMove) { + ((GroovyPsiElement)movedMember).accept(new GroovyRecursiveElementVisitor() { + @Override + public void visitReferenceExpression(GrReferenceExpression referenceExpression) { + if (processRef(referenceExpression)) return; + super.visitReferenceExpression(referenceExpression); + } + + @Override + public void visitCodeReferenceElement(GrCodeReferenceElement refElement) { + if (processRef(refElement)) return; + super.visitCodeReferenceElement(refElement); + } + + private boolean processRef(@NotNull GrReferenceElement refElement) { + final PsiElement qualifier = refElement.getQualifier(); + if (qualifier != null) { + final Boolean preserveQualifier = qualifier.getCopyableUserData(PRESERVE_QUALIFIER); + if (preserveQualifier != null && !preserveQualifier) { + refElement.setQualifier(null); + return true; + } + } + return false; + } + }); + listenerManager.fireMemberMoved(mySourceClass, movedMember); + } + } + + private PsiSubstitutor upDownSuperClassSubstitutor() { + PsiSubstitutor substitutor = PsiSubstitutor.EMPTY; + for (PsiTypeParameter parameter : PsiUtil.typeParametersIterable(mySourceClass)) { + substitutor = substitutor.put(parameter, null); + } + + final Map substitutionMap = + TypeConversionUtil.getSuperClassSubstitutor(myTargetSuperClass, mySourceClass, PsiSubstitutor.EMPTY).getSubstitutionMap(); + for (PsiTypeParameter parameter : substitutionMap.keySet()) { + final PsiType type = substitutionMap.get(parameter); + final PsiClass resolvedClass = PsiUtil.resolveClassInType(type); + if (resolvedClass instanceof PsiTypeParameter) { + substitutor = substitutor.put((PsiTypeParameter)resolvedClass, JavaPsiFacade.getElementFactory(myProject).createType(parameter)); + } + } + + return substitutor; + } + + public void setCorrectVisibility(final HashSet movedMembers, GrMemberInfo info) { + PsiModifierListOwner modifierListOwner = info.getMember(); + if (myTargetSuperClass.isInterface()) { + PsiUtil.setModifierProperty(modifierListOwner, PsiModifier.PUBLIC, true); + } + else if (modifierListOwner.hasModifierProperty(PsiModifier.PRIVATE)) { + if (info.isToAbstract() || willBeUsedInSubclass(modifierListOwner, movedMembers, myTargetSuperClass, mySourceClass)) { + PsiUtil.setModifierProperty(modifierListOwner, PsiModifier.PROTECTED, true); + } + + if (modifierListOwner instanceof GrTypeDefinition) { + ((GrTypeDefinition)modifierListOwner).accept(new GroovyRecursiveElementVisitor() { + @Override + public void visitMethod(GrMethod method) { + check(method); + } + + @Override + public void visitField(GrField field) { + check(field); + } + + @Override + public void visitTypeDefinition(GrTypeDefinition typeDefinition) { + check(typeDefinition); + super.visitTypeDefinition(typeDefinition); + } + + private void check(PsiMember member) { + if (member.hasModifierProperty(PsiModifier.PRIVATE)) { + if (willBeUsedInSubclass(member, movedMembers, myTargetSuperClass, mySourceClass)) { + PsiUtil.setModifierProperty(member, PsiModifier.PROTECTED, true); + } + } + } + }); + } + } + } + + private static boolean willBeUsedInSubclass(PsiElement member, Set movedMembers, PsiClass superclass, PsiClass subclass) { + for (PsiReference ref : ReferencesSearch.search(member, new LocalSearchScope(subclass), false)) { + PsiElement element = ref.getElement(); + if (!RefactoringHierarchyUtil.willBeInTargetClass(element, movedMembers, superclass, false)) { + return true; + } + } + + return false; + } + + private void doMoveMethod(Set movedMembers, PsiSubstitutor substitutor, GrMemberInfo info) { + GroovyPsiElementFactory elementFactory = GroovyPsiElementFactory.getInstance(myProject); + GrMethod method = (GrMethod)info.getMember(); + PsiMethod sibling = method; + PsiMethod anchor = null; + while (sibling != null) { + sibling = PsiTreeUtil.getNextSiblingOfType(sibling, PsiMethod.class); + if (sibling != null) { + anchor = MethodSignatureUtil.findMethodInSuperClassBySignatureInDerived(method.getContainingClass(), myTargetSuperClass, + sibling.getSignature(PsiSubstitutor.EMPTY), false); + if (anchor != null) { + break; + } + } + } + + GrMethod methodCopy = (GrMethod)method.copy(); + if (method.findSuperMethods(myTargetSuperClass).length == 0) { + deleteOverrideAnnotationIfFound(methodCopy); + } + + final boolean isOriginalMethodAbstract = + method.hasModifierProperty(PsiModifier.ABSTRACT) || method.hasModifierProperty(PsiModifier.DEFAULT); + if (myTargetSuperClass.isInterface() || info.isToAbstract()) { + GroovyChangeContextUtil.clearContextInfo(method); + RefactoringUtil.makeMethodAbstract(myTargetSuperClass, methodCopy); + replaceMovedMemberTypeParameters(methodCopy, PsiUtil.typeParametersIterable(mySourceClass), substitutor, elementFactory); + + myDocCommentPolicy.processCopiedJavaDoc(methodCopy.getDocComment(), method.getDocComment(), isOriginalMethodAbstract); + + final PsiMember movedElement = + anchor != null ? (PsiMember)myTargetSuperClass.addBefore(methodCopy, anchor) : (PsiMember)myTargetSuperClass.add(methodCopy); + CodeStyleSettings styleSettings = CodeStyleSettingsManager.getSettings(method.getProject()); + if (styleSettings.INSERT_OVERRIDE_ANNOTATION) { + if (PsiUtil.isLanguageLevel5OrHigher(mySourceClass) && !myTargetSuperClass.isInterface() || + PsiUtil.isLanguageLevel6OrHigher(mySourceClass)) { + new AddAnnotationFix(CommonClassNames.JAVA_LANG_OVERRIDE, method) + .invoke(method.getProject(), null, mySourceClass.getContainingFile()); + } + } + + if (!PsiUtil.isLanguageLevel6OrHigher(mySourceClass) && myTargetSuperClass.isInterface()) { + if (isOriginalMethodAbstract) { + for (PsiMethod oMethod : OverridingMethodsSearch.search(method)) { + deleteOverrideAnnotationIfFound(oMethod); + } + } + + deleteOverrideAnnotationIfFound(method); + } + + myMembersAfterMove.add(movedElement); + if (isOriginalMethodAbstract) { + method.delete(); + } + } + else { + if (isOriginalMethodAbstract) { + PsiUtil.setModifierProperty(myTargetSuperClass, PsiModifier.ABSTRACT, true); + } + + //fixReferencesToStatic(methodCopy, movedMembers); + replaceMovedMemberTypeParameters(methodCopy, PsiUtil.typeParametersIterable(mySourceClass), substitutor, elementFactory); + final PsiMethod superClassMethod = myTargetSuperClass.findMethodBySignature(methodCopy, false); + if (superClassMethod != null && superClassMethod.hasModifierProperty(PsiModifier.ABSTRACT)) { + superClassMethod.replace(methodCopy); + } + else { + final PsiMember movedElement = + anchor != null ? (PsiMember)myTargetSuperClass.addBefore(methodCopy, anchor) : (PsiMember)myTargetSuperClass.add(methodCopy); + myMembersAfterMove.add(movedElement); + } + + method.delete(); + } + } + + private static void deleteOverrideAnnotationIfFound(PsiMethod oMethod) { + final PsiAnnotation annotation = AnnotationUtil.findAnnotation(oMethod, CommonClassNames.JAVA_LANG_OVERRIDE); + if (annotation != null) { + annotation.delete(); + } + } + + public static void replaceMovedMemberTypeParameters(final PsiElement member, + final Iterable parametersIterable, + final PsiSubstitutor substitutor, + final GroovyPsiElementFactory factory) { + final Map replacement = new LinkedHashMap(); + for (PsiTypeParameter parameter : parametersIterable) { + PsiType substitutedType = substitutor.substitute(parameter); + if (substitutedType == null) { + substitutedType = TypeConversionUtil.erasure(factory.createType(parameter)); + } + + PsiElement scopeElement = member instanceof GrField ? member.getParent() : member; + for (PsiReference reference : ReferencesSearch.search(parameter, new LocalSearchScope(scopeElement))) { + final PsiElement element = reference.getElement(); + final PsiElement parent = element.getParent(); + if (parent instanceof PsiTypeElement) { + replacement.put(parent, factory.createTypeElement(substitutedType)); + } + else if (element instanceof GrCodeReferenceElement && substitutedType instanceof PsiClassType) { + replacement.put(element, factory.createReferenceElementByType((PsiClassType)substitutedType)); + } + } + } + + for (PsiElement element : replacement.keySet()) { + if (element.isValid()) { + element.replace(replacement.get(element)); + } + } + } + + private void fixReferencesToStatic(GroovyPsiElement classMember, Set movedMembers) throws IncorrectOperationException { + final StaticReferencesCollector collector = new StaticReferencesCollector(movedMembers); + classMember.accept(collector); + ArrayList refs = collector.getReferences(); + ArrayList members = collector.getReferees(); + ArrayList classes = collector.getRefereeClasses(); + GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(myProject); + + for (int i = 0; i < refs.size(); i++) { + GrReferenceElement ref = refs.get(i); + PsiElement namedElement = members.get(i); + PsiClass aClass = classes.get(i); + + if (namedElement instanceof PsiNamedElement) { + GrReferenceExpression newRef = (GrReferenceExpression)factory.createExpressionFromText("a." + ((PsiNamedElement)namedElement).getName(), null); + GrExpression qualifier = newRef.getQualifierExpression(); + assert qualifier != null; + qualifier = (GrExpression)qualifier.replace(factory.createReferenceExpressionFromText(aClass.getQualifiedName())); + qualifier.putCopyableUserData(PRESERVE_QUALIFIER, ref.isQualified()); + PsiElement replaced = ref.replace(newRef); + JavaCodeStyleManager.getInstance(myProject).shortenClassReferences(replaced); + } + } + } + + + private class StaticReferencesCollector extends GrClassMemberReferenceVisitor { + private final ArrayList myReferences = new ArrayList(); + private final ArrayList myReferees = new ArrayList(); + private final ArrayList myRefereeClasses = new ArrayList(); + private final Set myMovedMembers; + + private StaticReferencesCollector(Set movedMembers) { + super(mySourceClass); + myMovedMembers = movedMembers; + } + + public ArrayList getReferees() { + return myReferees; + } + + public ArrayList getRefereeClasses() { + return myRefereeClasses; + } + + public ArrayList getReferences() { + return myReferences; + } + + @Override + protected void visitClassMemberReferenceElement(GrMember classMember, GrReferenceElement classMemberReference) { + if (classMember.hasModifierProperty(PsiModifier.STATIC) /*&& classMemberReference.isQualified()*/) { + if (!myMovedMembers.contains(classMember) && + RefactoringHierarchyUtil.isMemberBetween(myTargetSuperClass, mySourceClass, classMember)) { + myReferences.add(classMemberReference); + myReferees.add(classMember); + myRefereeClasses.add(classMember.getContainingClass()); + } + else if (myMovedMembers.contains(classMember) || myMembersAfterMove.contains(classMember)) { + myReferences.add(classMemberReference); + myReferees.add(classMember); + myRefereeClasses.add(myTargetSuperClass); + } + } + } + } + + private class ExplicitSuperDeleter extends GroovyRecursiveElementVisitor { + private final ArrayList mySupersToDelete = ContainerUtil.newArrayList(); + private final ArrayList mySupersToChangeToThis = ContainerUtil.newArrayList(); + + @Override + public void visitReferenceExpression(GrReferenceExpression expression) { + super.visitReferenceExpression(expression); + if(org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil.isSuperReference(expression.getQualifierExpression())) { + PsiElement resolved = expression.resolve(); + if (resolved == null || resolved instanceof PsiMethod && shouldFixSuper((PsiMethod) resolved)) { + mySupersToDelete.add(expression.getQualifierExpression()); + } + } + else if (org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil.isSuperReference(expression)) { + mySupersToChangeToThis.add(expression); + } + } + + @Override + public void visitTypeDefinition(GrTypeDefinition typeDefinition) { + //do nothing + } + + private boolean shouldFixSuper(PsiMethod method) { + for (PsiMember element : myMembersAfterMove) { + if (element instanceof PsiMethod) { + PsiMethod member = (PsiMethod)element; + // if there is such member among moved members, super qualifier + // should not be removed + final PsiManager manager = method.getManager(); + if (manager.areElementsEquivalent(member.getContainingClass(), method.getContainingClass()) && + MethodSignatureUtil.areSignaturesEqual(member, method)) { + return false; + } + } + } + + final PsiMethod methodFromSuper = myTargetSuperClass.findMethodBySignature(method, false); + return methodFromSuper == null; + } + + public void fixSupers() throws IncorrectOperationException { + final GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(myProject); + GrReferenceExpression thisExpression = (GrReferenceExpression) factory.createExpressionFromText("this", null); + for (GrExpression expression : mySupersToDelete) { + expression.delete(); + } + + for (GrReferenceExpression superExpression : mySupersToChangeToThis) { + superExpression.replace(thisExpression); + } + } + } + + private class QualifiedThisSuperAdjuster extends GroovyRecursiveElementVisitor { + @Override + public void visitReferenceExpression(GrReferenceExpression expression) { + super.visitReferenceExpression(expression); + if (expression.getCopyableUserData(SUPER_REF) != null) { + expression.putCopyableUserData(SUPER_REF, null); + final GrExpression qualifier = expression.getQualifier(); + if (qualifier instanceof GrReferenceExpression && ((GrReferenceExpression)qualifier).isReferenceTo(mySourceClass)) { + try { + GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(myProject); + GrExpression newExpr = factory.createExpressionFromText(myTargetSuperClass.getName() + ".this", null); + expression.replace(newExpr); + } + catch (IncorrectOperationException e) { + LOG.error(e); + } + } + } + else if (expression.getCopyableUserData(THIS_REF) != null) { + expression.putCopyableUserData(THIS_REF, null); + final GrExpression qualifier = expression.getQualifier(); + if (qualifier instanceof GrReferenceExpression && ((GrReferenceExpression)qualifier).isReferenceTo(mySourceClass)) { + try { + ((GrReferenceExpression)qualifier).bindToElement(myTargetSuperClass); + GroovyChangeContextUtil.clearContextInfo(qualifier); + } + catch (IncorrectOperationException e) { + LOG.error(e); + } + } + } + } + } + + private void doMoveField(Set movedMembers, PsiSubstitutor substitutor, GrMemberInfo info) { + GroovyPsiElementFactory elementFactory = GroovyPsiElementFactory.getInstance(myProject); + GrField field = (GrField)info.getMember(); + field.normalizeDeclaration(); + replaceMovedMemberTypeParameters(field, PsiUtil.typeParametersIterable(mySourceClass), substitutor, elementFactory); + //fixReferencesToStatic(field, movedMembers); + if (myTargetSuperClass.isInterface()) { + PsiUtil.setModifierProperty(field, PsiModifier.PUBLIC, true); + } + final PsiMember movedElement = (PsiMember)myTargetSuperClass.add(field); + myMembersAfterMove.add(movedElement); + field.delete(); + } + + private void doMoveClass(Set movedMembers, PsiSubstitutor substitutor, GrMemberInfo info) { + GroovyPsiElementFactory elementFactory = GroovyPsiElementFactory.getInstance(myProject); + GrTypeDefinition aClass = (GrTypeDefinition)info.getMember(); + if (Boolean.FALSE.equals(info.getOverrides())) { + final GrReferenceList sourceReferenceList = info.getSourceReferenceList(); + LOG.assertTrue(sourceReferenceList != null); + GrCodeReferenceElement ref = mySourceClass.equals(sourceReferenceList.getParent()) ? + removeFromReferenceList(sourceReferenceList, aClass) : + findReferenceToClass(sourceReferenceList, aClass); + if (ref != null && !myTargetSuperClass.isInheritor(aClass, false)) { + replaceMovedMemberTypeParameters(ref, PsiUtil.typeParametersIterable(mySourceClass), substitutor, elementFactory); + GrReferenceList referenceList; + if (myTargetSuperClass.isInterface()) { + referenceList = myTargetSuperClass.getExtendsClause(); + if (referenceList == null) { + GrExtendsClause newClause = GroovyPsiElementFactory.getInstance(myProject).createExtendsClause(); + PsiElement anchor = myTargetSuperClass.getTypeParameterList() != null ? myTargetSuperClass.getTypeParameterList(): + myTargetSuperClass.getNameIdentifierGroovy(); + referenceList = (GrReferenceList)myTargetSuperClass.addAfter(newClause, anchor); + //CodeStyleManager.getInstance(myProject).reformat(referenceList); + //myTargetSuperClass.getNode().addLeaf(TokenType.WHITE_SPACE, " ", referenceList.getNode()); + } + } + else { + referenceList = myTargetSuperClass.getImplementsClause(); + + if (referenceList == null) { + GrImplementsClause newClause = GroovyPsiElementFactory.getInstance(myProject).createImplementsClause(); + PsiElement anchor = myTargetSuperClass.getExtendsClause() != null ? myTargetSuperClass.getExtendsClause() : + myTargetSuperClass.getTypeParameterList() != null ? myTargetSuperClass.getTypeParameterList() : + myTargetSuperClass.getNameIdentifierGroovy(); + referenceList = (GrReferenceList)myTargetSuperClass.addAfter(newClause, anchor); + //CodeStyleManager.getInstance(myProject).reformat(referenceList); + //myTargetSuperClass.getNode().addLeaf(TokenType.WHITE_SPACE, " ", referenceList.getNode()); + } + + } + + assert referenceList != null; + referenceList.add(ref); + } + } + else { + replaceMovedMemberTypeParameters(aClass, PsiUtil.typeParametersIterable(mySourceClass), substitutor, elementFactory); + //fixReferencesToStatic(aClass, movedMembers); + PsiMember movedElement = (PsiMember)myTargetSuperClass.addAfter(aClass, null); + //movedElement = (PsiMember)CodeStyleManager.getInstance(myProject).reformat(movedElement); + myMembersAfterMove.add(movedElement); + aClass.delete(); + } + } + + public static GrCodeReferenceElement findReferenceToClass(GrReferenceList refList, PsiClass aClass) { + GrCodeReferenceElement[] refs = refList.getReferenceElements(); + for (GrCodeReferenceElement ref : refs) { + if (ref.isReferenceTo(aClass)) { + return ref; + } + } + return null; + } + + + /** + * removes a reference to the specified class from the reference list given + * + * @return if removed - a reference to the class or null if there were no references to this class in the reference list + */ + public static GrCodeReferenceElement removeFromReferenceList(GrReferenceList refList, PsiClass aClass) + throws IncorrectOperationException { + GrCodeReferenceElement[] refs = refList.getReferenceElements(); + for (GrCodeReferenceElement ref : refs) { + if (ref.isReferenceTo(aClass)) { + GrCodeReferenceElement refCopy = (GrCodeReferenceElement)ref.copy(); + ref.delete(); + return refCopy; + } + } + return null; + } + + private class QualifiedThisSuperSearcher extends GroovyRecursiveElementVisitor { + @Override + public void visitReferenceExpression(GrReferenceExpression expression) { + super.visitReferenceExpression(expression); + if (org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil.isSuperReference(expression)) { + final GrExpression qualifier = expression.getQualifier(); + if (qualifier instanceof GrReferenceExpression && ((GrReferenceExpression)qualifier).isReferenceTo(mySourceClass)) { + try { + expression.putCopyableUserData(SUPER_REF, Boolean.TRUE); + } + catch (IncorrectOperationException e) { + LOG.error(e); + } + } + } + else if (org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil.isThisReference(expression)) { + final GrExpression qualifier = expression.getQualifier(); + if (qualifier instanceof GrReferenceExpression && ((GrReferenceExpression)qualifier).isReferenceTo(mySourceClass)) { + try { + expression.putCopyableUserData(THIS_REF, Boolean.TRUE); + } + catch (IncorrectOperationException e) { + LOG.error(e); + } + } + } + + } + } +} diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/refactoring/memberPullUp/GrPullUpTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/refactoring/memberPullUp/GrPullUpTest.groovy new file mode 100644 index 000000000000..e510154844f9 --- /dev/null +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/refactoring/memberPullUp/GrPullUpTest.groovy @@ -0,0 +1,243 @@ +/* + * Copyright 2000-2013 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.refactoring.memberPullUp + +import com.intellij.psi.* +import com.intellij.psi.util.PsiTreeUtil +import com.intellij.refactoring.listeners.JavaRefactoringListenerManager +import com.intellij.refactoring.listeners.MoveMemberListener +import com.intellij.refactoring.util.DocCommentPolicy +import com.intellij.util.ui.UIUtil +import junit.framework.Assert +import org.jetbrains.plugins.groovy.LightGroovyTestCase +import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrReferenceList +import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition +import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMember +import org.jetbrains.plugins.groovy.refactoring.classMembers.GrMemberInfo +import org.jetbrains.plugins.groovy.util.TestUtils +/** + * Created by Max Medvedev on 8/17/13 + */ +class GrPullUpTest extends LightGroovyTestCase { + @Override + protected String getBasePath() { + TestUtils.testDataPath + 'refactoring/pullUp' + } + + public void testQualifiedThis() { + doTest(new MemberDescriptor("Inner", PsiClass)); + } + + public void testQualifiedSuper() { + doTest(new MemberDescriptor("Inner", PsiClass)); + } + + public void testQualifiedReference() { // IDEADEV-25008 + doTest(new MemberDescriptor("x", PsiField), + new MemberDescriptor("getX", PsiMethod), + new MemberDescriptor("setX", PsiMethod)); + } + + public void testPullUpInheritedStaticClasses() { + doTest(new MemberDescriptor("C", PsiClass), + new MemberDescriptor("D", PsiClass)); + } + + public void testPullUpPrivateInnerClassWithPrivateConstructor() { + doTest(new MemberDescriptor("C", PsiClass)); + } + + public void testPullUpAndMakeAbstract() { + doTest(new MemberDescriptor("a", PsiMethod), + new MemberDescriptor("b", PsiMethod, true)); + } + + public void _testTryCatchFieldInitializer() { + doTest(new MemberDescriptor("field", PsiField)); + } + + public void testIfFieldInitializationWithNonMovedField() { + doTest(new MemberDescriptor("f", PsiField)); + } + + public void _testIfFieldMovedInitialization() { + doTest(new MemberDescriptor("f", PsiField)); + } + + public void _testMultipleConstructorsFieldInitialization() { + doTest(new MemberDescriptor("f", PsiField)); + } + + public void _testMultipleConstructorsFieldInitializationNoGood() { + doTest(new MemberDescriptor("f", PsiField)); + } + + public void _testRemoveOverride() { + doTest(new MemberDescriptor("get", PsiMethod)); + } + + public void testTypeParamErasure() { + doTest(new MemberDescriptor("f", PsiField)); + } + + public void testTypeParamSubst() { + doTest(new MemberDescriptor("f", PsiField)); + } + + public void testTypeArgument() { + doTest(new MemberDescriptor("f", PsiField)); + } + + public void testGenericsInAbstractMethod() { + doTest(new MemberDescriptor("method", PsiMethod, true)); + } + + public void _testReplaceDuplicatesInInheritors() { + doTest(new MemberDescriptor("foo", PsiMethod, false)); + } + + public void testGenericsInImplements() { + doTest(false, new MemberDescriptor("I", PsiClass)); + } + + public void testUpdateStaticRefs() { + doTest(false, new MemberDescriptor("foo", PsiMethod)); + } + + public void testRemoveOverrideFromPulledMethod() { + doTest(false, new MemberDescriptor("foo", PsiMethod)); + } + + public void testPreserveOverrideInPulledMethod() { + doTest(false, new MemberDescriptor("foo", PsiMethod)); + } + + public void testMergeInterfaces() { + doTest(false, new MemberDescriptor("I", PsiClass)); + } + + public void testTypeParamsConflictingNames() { + doTest(false, new MemberDescriptor("foo", PsiMethod)); + } + + public void testEscalateVisibility() { + doTest(false, new MemberDescriptor("foo", PsiMethod)); + } + + public void testExtensionMethod() { + doTest(false, new MemberDescriptor("foo", PsiMethod)); + } + + public void testPreserveOverride() { + doTest(false, new MemberDescriptor("foo", PsiMethod)); + } + + private void doTest(MemberDescriptor... membersToFind) { + doTest(true, membersToFind); + } + + private void doTest(final boolean checkMembersMovedCount, MemberDescriptor... membersToFind) { + myFixture.configureByFile(getTestName(false) + ".groovy"); + PsiElement elementAt = myFixture.getFile().findElementAt(myFixture.getEditor().getCaretModel().getOffset()); + final PsiClass sourceClass = PsiTreeUtil.getParentOfType(elementAt, PsiClass); + assertNotNull(sourceClass); + + PsiClass targetClass = sourceClass.getSuperClass(); + assertNotNull(targetClass); + if (!targetClass.isWritable()) { + final PsiClass[] interfaces = sourceClass.getInterfaces(); + assertEquals(1, interfaces.length); + assertTrue(interfaces[0].isWritable()); + targetClass = interfaces[0]; + } + GrMemberInfo[] infos = findMembers(sourceClass, membersToFind); + + final int[] countMoved = [0]; + final MoveMemberListener listener = new MoveMemberListener() { + @Override + public void memberMoved(PsiClass aClass, PsiMember member) { + assertEquals(sourceClass, aClass); + countMoved[0]++; + } + }; + JavaRefactoringListenerManager.getInstance(getProject()).addMoveMembersListener(listener); + final GrPullUpHelper helper = new GrPullUpHelper(sourceClass, targetClass, infos, new DocCommentPolicy(DocCommentPolicy.ASIS)); + helper.run(); + UIUtil.dispatchAllInvocationEvents(); + JavaRefactoringListenerManager.getInstance(getProject()).removeMoveMembersListener(listener); + if (checkMembersMovedCount) { + assertEquals(countMoved[0], membersToFind.length); + } + myFixture.checkResultByFile(getTestName(false) + "_after.groovy"); + } + + public static class MemberDescriptor { + private String myName; + private Class myClass; + private boolean myAbstract; + + public MemberDescriptor(String name, Class aClass, boolean isAbstract) { + myName = name; + myClass = aClass; + myAbstract = isAbstract; + } + + + public MemberDescriptor(String name, Class aClass) { + this(name, aClass, false); + } + } + + public static GrMemberInfo[] findMembers(final PsiClass sourceClass, final MemberDescriptor... membersToFind) { + GrMemberInfo[] infos = new GrMemberInfo[membersToFind.length] + for (int i = 0; i < membersToFind.length; i++) { + final Class clazz = membersToFind[i].myClass + final String name = membersToFind[i].myName + PsiMember member = null + boolean overrides = false + GrReferenceList refList = null + if (PsiClass.isAssignableFrom(clazz)) { + member = sourceClass.findInnerClassByName(name, false) + if (member == null) { + final PsiClass[] supers = sourceClass.getSupers() + for (PsiClass superTypeClass : supers) { + if (superTypeClass.getName().equals(name)) { + member = superTypeClass + overrides = true + refList = superTypeClass.isInterface() ? + (sourceClass as GrTypeDefinition).getImplementsClause() : + (sourceClass as GrTypeDefinition).getExtendsClause() + break + } + } + } + + } else if (PsiMethod.class.isAssignableFrom(clazz)) { + final PsiMethod[] methods = sourceClass.findMethodsByName(name, false) + Assert.assertEquals(1, methods.length) + member = methods[0] + } else if (PsiField.class.isAssignableFrom(clazz)) { + member = sourceClass.findFieldByName(name, false) + } + + assertNotNull(member) + assertInstanceOf(member, GrMember) + infos[i] = new GrMemberInfo(member as GrMember, overrides, refList) + infos[i].setToAbstract(membersToFind[i].myAbstract) + } + return infos + } +} diff --git a/plugins/groovy/testdata/refactoring/pullUp/EscalateVisibility.groovy b/plugins/groovy/testdata/refactoring/pullUp/EscalateVisibility.groovy new file mode 100644 index 000000000000..6f50634df5a2 --- /dev/null +++ b/plugins/groovy/testdata/refactoring/pullUp/EscalateVisibility.groovy @@ -0,0 +1,10 @@ +class Foo { + +} + +class FooImpl extends Foo { + private void foo(){} + void bar() { + foo(); + } +} diff --git a/plugins/groovy/testdata/refactoring/pullUp/EscalateVisibility_after.groovy b/plugins/groovy/testdata/refactoring/pullUp/EscalateVisibility_after.groovy new file mode 100644 index 000000000000..725e5c5a2b12 --- /dev/null +++ b/plugins/groovy/testdata/refactoring/pullUp/EscalateVisibility_after.groovy @@ -0,0 +1,10 @@ +class Foo { + + protected void foo(){} +} + +class FooImpl extends Foo { + void bar() { + foo(); + } +} diff --git a/plugins/groovy/testdata/refactoring/pullUp/ExtensionMethod.groovy b/plugins/groovy/testdata/refactoring/pullUp/ExtensionMethod.groovy new file mode 100644 index 000000000000..4a59e1eef8c8 --- /dev/null +++ b/plugins/groovy/testdata/refactoring/pullUp/ExtensionMethod.groovy @@ -0,0 +1,8 @@ +interface Base { +} + +interface I2 extends Base { + default void foo() { + System.out.println("Hi there."); + } +} diff --git a/plugins/groovy/testdata/refactoring/pullUp/ExtensionMethod_after.groovy b/plugins/groovy/testdata/refactoring/pullUp/ExtensionMethod_after.groovy new file mode 100644 index 000000000000..469ee86ef62a --- /dev/null +++ b/plugins/groovy/testdata/refactoring/pullUp/ExtensionMethod_after.groovy @@ -0,0 +1,8 @@ +interface Base { + default void foo() { + System.out.println("Hi there."); + } +} + +interface I2 extends Base { +} diff --git a/plugins/groovy/testdata/refactoring/pullUp/GenericsInAbstractMethod.groovy b/plugins/groovy/testdata/refactoring/pullUp/GenericsInAbstractMethod.groovy new file mode 100644 index 000000000000..bd2f4991dff6 --- /dev/null +++ b/plugins/groovy/testdata/refactoring/pullUp/GenericsInAbstractMethod.groovy @@ -0,0 +1,5 @@ +public abstract class Parent {} + +class Child extends Parent { + void method(T t){} +} diff --git a/plugins/groovy/testdata/refactoring/pullUp/GenericsInAbstractMethod_after.groovy b/plugins/groovy/testdata/refactoring/pullUp/GenericsInAbstractMethod_after.groovy new file mode 100644 index 000000000000..353201fac27c --- /dev/null +++ b/plugins/groovy/testdata/refactoring/pullUp/GenericsInAbstractMethod_after.groovy @@ -0,0 +1,8 @@ +public abstract class Parent { + abstract void method(S t) +} + +class Child extends Parent { + @Override + void method(T t){} +} diff --git a/plugins/groovy/testdata/refactoring/pullUp/GenericsInImplements.groovy b/plugins/groovy/testdata/refactoring/pullUp/GenericsInImplements.groovy new file mode 100644 index 000000000000..07e1f89a50c9 --- /dev/null +++ b/plugins/groovy/testdata/refactoring/pullUp/GenericsInImplements.groovy @@ -0,0 +1,10 @@ +public class Parent {} + +interface I { + void method(IT t); +} + +class Child extends Parent implements I{ + + public void method(T t){} +} diff --git a/plugins/groovy/testdata/refactoring/pullUp/GenericsInImplements_after.groovy b/plugins/groovy/testdata/refactoring/pullUp/GenericsInImplements_after.groovy new file mode 100644 index 000000000000..3aca9f833a81 --- /dev/null +++ b/plugins/groovy/testdata/refactoring/pullUp/GenericsInImplements_after.groovy @@ -0,0 +1,10 @@ +public class Parent implements I {} + +interface I { + void method(IT t); +} + +class Child extends Parent { + + public void method(T t){} +} diff --git a/plugins/groovy/testdata/refactoring/pullUp/IfFieldInitializationWithNonMovedField.groovy b/plugins/groovy/testdata/refactoring/pullUp/IfFieldInitializationWithNonMovedField.groovy new file mode 100644 index 000000000000..ba3609d7238c --- /dev/null +++ b/plugins/groovy/testdata/refactoring/pullUp/IfFieldInitializationWithNonMovedField.groovy @@ -0,0 +1,18 @@ +public class A { +} + +class B extends A { + final String f; + final String foo; + + B(String fi, String foo) { + this.foo = foo; + if (fi == this.foo) { + f = foo; + } else { + f = ""; + } + } + + +} diff --git a/plugins/groovy/testdata/refactoring/pullUp/IfFieldInitializationWithNonMovedField_after.groovy b/plugins/groovy/testdata/refactoring/pullUp/IfFieldInitializationWithNonMovedField_after.groovy new file mode 100644 index 000000000000..8d52600bc079 --- /dev/null +++ b/plugins/groovy/testdata/refactoring/pullUp/IfFieldInitializationWithNonMovedField_after.groovy @@ -0,0 +1,18 @@ +public class A { + protected final String f +} + +class B extends A { + final String foo; + + B(String fi, String foo) { + this.foo = foo; + if (fi == this.foo) { + f = foo; + } else { + f = ""; + } + } + + +} diff --git a/plugins/groovy/testdata/refactoring/pullUp/IfFieldMovedInitialization.groovy b/plugins/groovy/testdata/refactoring/pullUp/IfFieldMovedInitialization.groovy new file mode 100644 index 000000000000..f541f0ef1118 --- /dev/null +++ b/plugins/groovy/testdata/refactoring/pullUp/IfFieldMovedInitialization.groovy @@ -0,0 +1,18 @@ +public class A { +} + +class B extends A { + final String f; + final String foo; + + B(String fi, String foo) { + this.foo = foo; + if (fi == foo) { + f = foo; + } else { + f = ""; + } + } + + +} diff --git a/plugins/groovy/testdata/refactoring/pullUp/IfFieldMovedInitialization_after.groovy b/plugins/groovy/testdata/refactoring/pullUp/IfFieldMovedInitialization_after.groovy new file mode 100644 index 000000000000..4000fdc4e154 --- /dev/null +++ b/plugins/groovy/testdata/refactoring/pullUp/IfFieldMovedInitialization_after.groovy @@ -0,0 +1,22 @@ +public class A { + final String f; + + public A(String fi, String foo) { + if (fi == foo) { + f = foo; + } else { + f = ""; + } + } +} + +class B extends A { + final String foo; + + B(String fi, String foo) { + super(fi, foo); + this.foo = foo; + } + + +} diff --git a/plugins/groovy/testdata/refactoring/pullUp/MergeInterfaces.groovy b/plugins/groovy/testdata/refactoring/pullUp/MergeInterfaces.groovy new file mode 100644 index 000000000000..5376e86b6b42 --- /dev/null +++ b/plugins/groovy/testdata/refactoring/pullUp/MergeInterfaces.groovy @@ -0,0 +1,3 @@ +class Base implements I {} +class Test extends Base implements I {} +interface I{} diff --git a/plugins/groovy/testdata/refactoring/pullUp/MergeInterfaces_after.groovy b/plugins/groovy/testdata/refactoring/pullUp/MergeInterfaces_after.groovy new file mode 100644 index 000000000000..ea7fffc75bf2 --- /dev/null +++ b/plugins/groovy/testdata/refactoring/pullUp/MergeInterfaces_after.groovy @@ -0,0 +1,3 @@ +class Base implements I {} +class Test extends Base {} +interface I{} diff --git a/plugins/groovy/testdata/refactoring/pullUp/MultipleConstructorsFieldInitialization.groovy b/plugins/groovy/testdata/refactoring/pullUp/MultipleConstructorsFieldInitialization.groovy new file mode 100644 index 000000000000..f884f48399c2 --- /dev/null +++ b/plugins/groovy/testdata/refactoring/pullUp/MultipleConstructorsFieldInitialization.groovy @@ -0,0 +1,17 @@ +public class A { +} + +class B extends A { + final String f; + final String foo; + + B(String fi, String foo) { + this.foo = foo; + f = ""; + } + + B(String foo) { + this.foo = foo; + f = ""; + } +} diff --git a/plugins/groovy/testdata/refactoring/pullUp/MultipleConstructorsFieldInitializationNoGood.groovy b/plugins/groovy/testdata/refactoring/pullUp/MultipleConstructorsFieldInitializationNoGood.groovy new file mode 100644 index 000000000000..25117fc692c8 --- /dev/null +++ b/plugins/groovy/testdata/refactoring/pullUp/MultipleConstructorsFieldInitializationNoGood.groovy @@ -0,0 +1,21 @@ +public class A { +} + +class B extends A { + final String f; + final String foo; + + B(String fi, String foo) { + this.foo = foo; + if (fi == this.foo) { + f = foo; + } else { + f = ""; + } + } + + B(String f) { + this.f = f; + foo = ""; + } +} diff --git a/plugins/groovy/testdata/refactoring/pullUp/MultipleConstructorsFieldInitializationNoGood_after.groovy b/plugins/groovy/testdata/refactoring/pullUp/MultipleConstructorsFieldInitializationNoGood_after.groovy new file mode 100644 index 000000000000..b66ba0b55967 --- /dev/null +++ b/plugins/groovy/testdata/refactoring/pullUp/MultipleConstructorsFieldInitializationNoGood_after.groovy @@ -0,0 +1,21 @@ +public class A { + final String f; +} + +class B extends A { + final String foo; + + B(String fi, String foo) { + this.foo = foo; + if (fi == this.foo) { + f = foo; + } else { + f = ""; + } + } + + B(String f) { + this.f = f; + foo = ""; + } +} diff --git a/plugins/groovy/testdata/refactoring/pullUp/MultipleConstructorsFieldInitialization_after.groovy b/plugins/groovy/testdata/refactoring/pullUp/MultipleConstructorsFieldInitialization_after.groovy new file mode 100644 index 000000000000..7c19de1ed075 --- /dev/null +++ b/plugins/groovy/testdata/refactoring/pullUp/MultipleConstructorsFieldInitialization_after.groovy @@ -0,0 +1,21 @@ +public class A { + final String f; + + public A() { + f = ""; + } +} + +class B extends A { + final String foo; + + B(String fi, String foo) { + super(); + this.foo = foo; + } + + B(String foo) { + super(); + this.foo = foo; + } +} diff --git a/plugins/groovy/testdata/refactoring/pullUp/PreserveOverride.groovy b/plugins/groovy/testdata/refactoring/pullUp/PreserveOverride.groovy new file mode 100644 index 000000000000..84f2f18bad76 --- /dev/null +++ b/plugins/groovy/testdata/refactoring/pullUp/PreserveOverride.groovy @@ -0,0 +1,12 @@ +abstract class Bazz { + public abstract void foo(); +} + +abstract class Foo extends Bazz {} + +class Bar extends Foo { + @Override + public void foo() { + } +} + diff --git a/plugins/groovy/testdata/refactoring/pullUp/PreserveOverrideInPulledMethod.groovy b/plugins/groovy/testdata/refactoring/pullUp/PreserveOverrideInPulledMethod.groovy new file mode 100644 index 000000000000..c814a6825a69 --- /dev/null +++ b/plugins/groovy/testdata/refactoring/pullUp/PreserveOverrideInPulledMethod.groovy @@ -0,0 +1,12 @@ +public class Test { + abstract class Base extends IntImpl { + @Override + public abstract String foo(); + } + + class IntImpl extends Int {} + + class Int { + public abstract String foo(); + } +} diff --git a/plugins/groovy/testdata/refactoring/pullUp/PreserveOverrideInPulledMethod_after.groovy b/plugins/groovy/testdata/refactoring/pullUp/PreserveOverrideInPulledMethod_after.groovy new file mode 100644 index 000000000000..3c8eea40c9bb --- /dev/null +++ b/plugins/groovy/testdata/refactoring/pullUp/PreserveOverrideInPulledMethod_after.groovy @@ -0,0 +1,13 @@ +public class Test { + abstract class Base extends IntImpl { + } + + abstract class IntImpl extends Int { + @Override + public abstract String foo(); + } + + class Int { + public abstract String foo(); + } +} diff --git a/plugins/groovy/testdata/refactoring/pullUp/PreserveOverride_after.groovy b/plugins/groovy/testdata/refactoring/pullUp/PreserveOverride_after.groovy new file mode 100644 index 000000000000..ea553c109132 --- /dev/null +++ b/plugins/groovy/testdata/refactoring/pullUp/PreserveOverride_after.groovy @@ -0,0 +1,13 @@ +abstract class Bazz { + public abstract void foo(); +} + +abstract class Foo extends Bazz { + @Override + public void foo() { + } +} + +class Bar extends Foo { +} + diff --git a/plugins/groovy/testdata/refactoring/pullUp/PullUpAndMakeAbstract.groovy b/plugins/groovy/testdata/refactoring/pullUp/PullUpAndMakeAbstract.groovy new file mode 100644 index 000000000000..111cfbe2f1c7 --- /dev/null +++ b/plugins/groovy/testdata/refactoring/pullUp/PullUpAndMakeAbstract.groovy @@ -0,0 +1,11 @@ +public class A2 { +} + +class B2 extends A2 { + public void a() { + b(); + } + + private void b() { + } +} diff --git a/plugins/groovy/testdata/refactoring/pullUp/PullUpAndMakeAbstract_after.groovy b/plugins/groovy/testdata/refactoring/pullUp/PullUpAndMakeAbstract_after.groovy new file mode 100644 index 000000000000..312788c07d78 --- /dev/null +++ b/plugins/groovy/testdata/refactoring/pullUp/PullUpAndMakeAbstract_after.groovy @@ -0,0 +1,14 @@ +public abstract class A2 { + public void a() { + b(); + } + + protected abstract void b() +} + +class B2 extends A2 { + + @Override + protected void b() { + } +} diff --git a/plugins/groovy/testdata/refactoring/pullUp/PullUpInheritedStaticClasses.groovy b/plugins/groovy/testdata/refactoring/pullUp/PullUpInheritedStaticClasses.groovy new file mode 100644 index 000000000000..e0faaba55617 --- /dev/null +++ b/plugins/groovy/testdata/refactoring/pullUp/PullUpInheritedStaticClasses.groovy @@ -0,0 +1,8 @@ +public class A extends AA { + + static class C extends D {} + static class D extends B {} + static class B {} +} + +class AA {} diff --git a/plugins/groovy/testdata/refactoring/pullUp/PullUpInheritedStaticClasses_after.groovy b/plugins/groovy/testdata/refactoring/pullUp/PullUpInheritedStaticClasses_after.groovy new file mode 100644 index 000000000000..cc948e7632ea --- /dev/null +++ b/plugins/groovy/testdata/refactoring/pullUp/PullUpInheritedStaticClasses_after.groovy @@ -0,0 +1,10 @@ +public class A extends AA { + + static class B {} +} + +class AA { + static class C extends D {} + + static class D extends A.B {} +} diff --git a/plugins/groovy/testdata/refactoring/pullUp/PullUpPrivateInnerClassWithPrivateConstructor.groovy b/plugins/groovy/testdata/refactoring/pullUp/PullUpPrivateInnerClassWithPrivateConstructor.groovy new file mode 100644 index 000000000000..a5fbc567cd6b --- /dev/null +++ b/plugins/groovy/testdata/refactoring/pullUp/PullUpPrivateInnerClassWithPrivateConstructor.groovy @@ -0,0 +1,15 @@ +public class B extends A { + private void f(){ + new C(); + } + + private static class C{ + private C(){ + + } + } +} + +//A.java +class A { +} diff --git a/plugins/groovy/testdata/refactoring/pullUp/PullUpPrivateInnerClassWithPrivateConstructor_after.groovy b/plugins/groovy/testdata/refactoring/pullUp/PullUpPrivateInnerClassWithPrivateConstructor_after.groovy new file mode 100644 index 000000000000..a1cc2b848007 --- /dev/null +++ b/plugins/groovy/testdata/refactoring/pullUp/PullUpPrivateInnerClassWithPrivateConstructor_after.groovy @@ -0,0 +1,15 @@ +public class B extends A { + private void f(){ + new C(); + } + +} + +//A.java +class A { + protected static class C{ + protected C(){ + + } + } +} diff --git a/plugins/groovy/testdata/refactoring/pullUp/QualifiedReference.groovy b/plugins/groovy/testdata/refactoring/pullUp/QualifiedReference.groovy new file mode 100644 index 000000000000..cf557ddd7033 --- /dev/null +++ b/plugins/groovy/testdata/refactoring/pullUp/QualifiedReference.groovy @@ -0,0 +1,13 @@ +class X {} + +class Y extends X { + private static int x = 0; + + public static int getX() { + return x; + } + + public static void setX(int x) { + Y.x = x; + } +} diff --git a/plugins/groovy/testdata/refactoring/pullUp/QualifiedReference_after.groovy b/plugins/groovy/testdata/refactoring/pullUp/QualifiedReference_after.groovy new file mode 100644 index 000000000000..2ee952609dcd --- /dev/null +++ b/plugins/groovy/testdata/refactoring/pullUp/QualifiedReference_after.groovy @@ -0,0 +1,16 @@ +class X { + private static int x = 0 + + public static int getX() { + return x; + } + + public static void setX(int x) { + X.x = x; + } +} + +class Y extends X { + + +} diff --git a/plugins/groovy/testdata/refactoring/pullUp/QualifiedSuper.groovy b/plugins/groovy/testdata/refactoring/pullUp/QualifiedSuper.groovy new file mode 100644 index 000000000000..6c586617fb01 --- /dev/null +++ b/plugins/groovy/testdata/refactoring/pullUp/QualifiedSuper.groovy @@ -0,0 +1,12 @@ +class QualifiedSuper extends Base { + class Inner { + void goo() { + QualifiedSuper.super.toString(); + } + } +} + +class Base { + + +} diff --git a/plugins/groovy/testdata/refactoring/pullUp/QualifiedSuper_after.groovy b/plugins/groovy/testdata/refactoring/pullUp/QualifiedSuper_after.groovy new file mode 100644 index 000000000000..08e72509da9e --- /dev/null +++ b/plugins/groovy/testdata/refactoring/pullUp/QualifiedSuper_after.groovy @@ -0,0 +1,12 @@ +class QualifiedSuper extends Base { +} + +class Base { + + + class Inner { + void goo() { + Base.this.toString(); + } + } +} diff --git a/plugins/groovy/testdata/refactoring/pullUp/QualifiedThis.groovy b/plugins/groovy/testdata/refactoring/pullUp/QualifiedThis.groovy new file mode 100644 index 000000000000..ae042886bafb --- /dev/null +++ b/plugins/groovy/testdata/refactoring/pullUp/QualifiedThis.groovy @@ -0,0 +1,15 @@ +class Base { + void foo() { + } +} + +public class QualifiedThis extends Base { + void foo() { + } + + class Inner { + void bar() { + QualifiedThis.this.foo(); + } + } +} diff --git a/plugins/groovy/testdata/refactoring/pullUp/QualifiedThis_after.groovy b/plugins/groovy/testdata/refactoring/pullUp/QualifiedThis_after.groovy new file mode 100644 index 000000000000..c097c66b5f82 --- /dev/null +++ b/plugins/groovy/testdata/refactoring/pullUp/QualifiedThis_after.groovy @@ -0,0 +1,16 @@ +class Base { + void foo() { + } + + class Inner { + void bar() { + Base.this.foo(); + } + } +} + +public class QualifiedThis extends Base { + void foo() { + } + +} diff --git a/plugins/groovy/testdata/refactoring/pullUp/RemoveOverride.groovy b/plugins/groovy/testdata/refactoring/pullUp/RemoveOverride.groovy new file mode 100644 index 000000000000..8c3d05feb402 --- /dev/null +++ b/plugins/groovy/testdata/refactoring/pullUp/RemoveOverride.groovy @@ -0,0 +1,16 @@ +public class Test { + class Impl extends Base { + @Override + public String get() { + return "239"; + } + } + + abstract class Base implements Int { + public abstract String get(); + + } + + interface Int { + } +} diff --git a/plugins/groovy/testdata/refactoring/pullUp/RemoveOverrideFromPulledMethod.groovy b/plugins/groovy/testdata/refactoring/pullUp/RemoveOverrideFromPulledMethod.groovy new file mode 100644 index 000000000000..2c14369982ae --- /dev/null +++ b/plugins/groovy/testdata/refactoring/pullUp/RemoveOverrideFromPulledMethod.groovy @@ -0,0 +1,9 @@ +public class Test { + abstract class Base extends Int { + @Override + public abstract String foo(); + } + + class Int { + } +} diff --git a/plugins/groovy/testdata/refactoring/pullUp/RemoveOverrideFromPulledMethod_after.groovy b/plugins/groovy/testdata/refactoring/pullUp/RemoveOverrideFromPulledMethod_after.groovy new file mode 100644 index 000000000000..15c02bbec11c --- /dev/null +++ b/plugins/groovy/testdata/refactoring/pullUp/RemoveOverrideFromPulledMethod_after.groovy @@ -0,0 +1,8 @@ +public class Test { + abstract class Base extends Int { + } + + abstract class Int { + public abstract String foo(); + } +} diff --git a/plugins/groovy/testdata/refactoring/pullUp/RemoveOverride_after.groovy b/plugins/groovy/testdata/refactoring/pullUp/RemoveOverride_after.groovy new file mode 100644 index 000000000000..94aef6d3d359 --- /dev/null +++ b/plugins/groovy/testdata/refactoring/pullUp/RemoveOverride_after.groovy @@ -0,0 +1,15 @@ +public class Test { + class Impl extends Base { + public String get() { + return "239"; + } + } + + abstract class Base implements Int { + + } + + interface Int { + String get(); + } +} diff --git a/plugins/groovy/testdata/refactoring/pullUp/ReplaceDuplicatesInInheritors.groovy b/plugins/groovy/testdata/refactoring/pullUp/ReplaceDuplicatesInInheritors.groovy new file mode 100644 index 000000000000..0f8c6a110fc8 --- /dev/null +++ b/plugins/groovy/testdata/refactoring/pullUp/ReplaceDuplicatesInInheritors.groovy @@ -0,0 +1,18 @@ +class A {} +class AImpl1 extends A{ + void foo() { + System.out.println("hello"); + System.out.println("hello"); + System.out.println("hello"); + System.out.println("hello"); + } +} + +class AImpl2 extends A { + void bar() { + System.out.println("hello"); + System.out.println("hello"); + System.out.println("hello"); + System.out.println("hello"); + } +} diff --git a/plugins/groovy/testdata/refactoring/pullUp/ReplaceDuplicatesInInheritors_after.groovy b/plugins/groovy/testdata/refactoring/pullUp/ReplaceDuplicatesInInheritors_after.groovy new file mode 100644 index 000000000000..47b655ce3c89 --- /dev/null +++ b/plugins/groovy/testdata/refactoring/pullUp/ReplaceDuplicatesInInheritors_after.groovy @@ -0,0 +1,16 @@ +class A { + void foo() { + System.out.println("hello"); + System.out.println("hello"); + System.out.println("hello"); + System.out.println("hello"); + } +} +class AImpl1 extends A{ +} + +class AImpl2 extends A { + void bar() { + foo(); + } +} diff --git a/plugins/groovy/testdata/refactoring/pullUp/TryCatchFieldInitializer.groovy b/plugins/groovy/testdata/refactoring/pullUp/TryCatchFieldInitializer.groovy new file mode 100644 index 000000000000..64675e489afa --- /dev/null +++ b/plugins/groovy/testdata/refactoring/pullUp/TryCatchFieldInitializer.groovy @@ -0,0 +1,19 @@ +public class Sup { + +} + +class ExtractSuperClass extends Sup { + + private final String field; + + public ExtractSuperClass() { + + + try { + field = (String)"text"; + } + catch (RuntimeException e) { + throw new RuntimeException(); + } + } +} diff --git a/plugins/groovy/testdata/refactoring/pullUp/TryCatchFieldInitializer_after.groovy b/plugins/groovy/testdata/refactoring/pullUp/TryCatchFieldInitializer_after.groovy new file mode 100644 index 000000000000..600247f2d6a1 --- /dev/null +++ b/plugins/groovy/testdata/refactoring/pullUp/TryCatchFieldInitializer_after.groovy @@ -0,0 +1,22 @@ +public class Sup { + + protected final String field + + public Sup() { + try { + field = (String)"text"; + } + catch (RuntimeException e) { + throw new RuntimeException(); + } + } +} + +class ExtractSuperClass extends Sup { + + public ExtractSuperClass() { + super(); + + + } +} diff --git a/plugins/groovy/testdata/refactoring/pullUp/TypeArgument.groovy b/plugins/groovy/testdata/refactoring/pullUp/TypeArgument.groovy new file mode 100644 index 000000000000..21140c87bb61 --- /dev/null +++ b/plugins/groovy/testdata/refactoring/pullUp/TypeArgument.groovy @@ -0,0 +1,5 @@ +public class Parent {} + +class Child extends Parent { + String f; +} diff --git a/plugins/groovy/testdata/refactoring/pullUp/TypeArgument_after.groovy b/plugins/groovy/testdata/refactoring/pullUp/TypeArgument_after.groovy new file mode 100644 index 000000000000..3eb901010ca6 --- /dev/null +++ b/plugins/groovy/testdata/refactoring/pullUp/TypeArgument_after.groovy @@ -0,0 +1,6 @@ +public class Parent { + String f +} + +class Child extends Parent { +} diff --git a/plugins/groovy/testdata/refactoring/pullUp/TypeParamErasure.groovy b/plugins/groovy/testdata/refactoring/pullUp/TypeParamErasure.groovy new file mode 100644 index 000000000000..0dbc24fce32d --- /dev/null +++ b/plugins/groovy/testdata/refactoring/pullUp/TypeParamErasure.groovy @@ -0,0 +1,5 @@ +public class Parent {} + +class Child extends Parent { + T f; +} diff --git a/plugins/groovy/testdata/refactoring/pullUp/TypeParamErasure_after.groovy b/plugins/groovy/testdata/refactoring/pullUp/TypeParamErasure_after.groovy new file mode 100644 index 000000000000..ab94d54beee3 --- /dev/null +++ b/plugins/groovy/testdata/refactoring/pullUp/TypeParamErasure_after.groovy @@ -0,0 +1,6 @@ +public class Parent { + Object f +} + +class Child extends Parent { +} diff --git a/plugins/groovy/testdata/refactoring/pullUp/TypeParamSubst.groovy b/plugins/groovy/testdata/refactoring/pullUp/TypeParamSubst.groovy new file mode 100644 index 000000000000..f2ad0c7e8dae --- /dev/null +++ b/plugins/groovy/testdata/refactoring/pullUp/TypeParamSubst.groovy @@ -0,0 +1,5 @@ +public class Parent {} + +class Child extends Parent { + T f; +} diff --git a/plugins/groovy/testdata/refactoring/pullUp/TypeParamSubst_after.groovy b/plugins/groovy/testdata/refactoring/pullUp/TypeParamSubst_after.groovy new file mode 100644 index 000000000000..703445143419 --- /dev/null +++ b/plugins/groovy/testdata/refactoring/pullUp/TypeParamSubst_after.groovy @@ -0,0 +1,6 @@ +public class Parent { + S f +} + +class Child extends Parent { +} diff --git a/plugins/groovy/testdata/refactoring/pullUp/TypeParamsConflictingNames.groovy b/plugins/groovy/testdata/refactoring/pullUp/TypeParamsConflictingNames.groovy new file mode 100644 index 000000000000..b0d736631ae4 --- /dev/null +++ b/plugins/groovy/testdata/refactoring/pullUp/TypeParamsConflictingNames.groovy @@ -0,0 +1,7 @@ +interface Bar { } + +interface Base { } + +class Foo implements Base { + void foo(Bar bar) { } +} diff --git a/plugins/groovy/testdata/refactoring/pullUp/TypeParamsConflictingNames_after.groovy b/plugins/groovy/testdata/refactoring/pullUp/TypeParamsConflictingNames_after.groovy new file mode 100644 index 000000000000..8c29df338869 --- /dev/null +++ b/plugins/groovy/testdata/refactoring/pullUp/TypeParamsConflictingNames_after.groovy @@ -0,0 +1,10 @@ +interface Bar { } + +interface Base { + void foo(Bar bar); +} + +class Foo implements Base { + @Override + public void foo(Bar bar) { } +} diff --git a/plugins/groovy/testdata/refactoring/pullUp/UpdateStaticRefs.groovy b/plugins/groovy/testdata/refactoring/pullUp/UpdateStaticRefs.groovy new file mode 100644 index 000000000000..7ad6fb62eaf6 --- /dev/null +++ b/plugins/groovy/testdata/refactoring/pullUp/UpdateStaticRefs.groovy @@ -0,0 +1,10 @@ +class Foo {} +class FooImpl extends Foo { + public static void foo(){} + +} +class U { + public static void main(String[] args) { + FooImpl.foo(); + } +} diff --git a/plugins/groovy/testdata/refactoring/pullUp/UpdateStaticRefs_after.groovy b/plugins/groovy/testdata/refactoring/pullUp/UpdateStaticRefs_after.groovy new file mode 100644 index 000000000000..dc797f702d0d --- /dev/null +++ b/plugins/groovy/testdata/refactoring/pullUp/UpdateStaticRefs_after.groovy @@ -0,0 +1,11 @@ +class Foo { + public static void foo(){} +} +class FooImpl extends Foo { + +} +class U { + public static void main(String[] args) { + Foo.foo(); + } +}