This commit is contained in:
Max Medvedev
2013-09-25 16:11:51 +04:00
parent cbf7531f01
commit fc48bab366
66 changed files with 2732 additions and 0 deletions
+2
View File
@@ -1444,6 +1444,8 @@
implementation="org.jetbrains.plugins.groovy.springloaded.SpringLoadedPositionManagerFactory"/>
<codeStyle.ReferenceAdjuster language="Groovy" implementationClass="org.jetbrains.plugins.groovy.codeStyle.GrReferenceAdjuster"/>
<codeInsight.unresolvedReferenceQuickFixProvider implementation="org.jetbrains.plugins.groovy.jarFinder.GroovyFindJarQuickFixProvider"/>
<lang.refactoringSupport.classMembersRefactoringSupport language="Groovy"
implementationClass="org.jetbrains.plugins.groovy.refactoring.classMembers.GroovyClassMembersRefactoringSupport"/>
</extensions>
<extensions defaultExtensionNs="com.intellij.debugger">
@@ -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);
}
}
}
@@ -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;
}
}
@@ -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);
}
@@ -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<GrMember, PsiClass> {
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;
}
}
@@ -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<GrMember> {
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<GrMemberInfo> extractClassMembers(GrTypeDefinition subclass, Filter<GrMember> filter, boolean extractInterfacesDeep) {
List<GrMemberInfo> members = new ArrayList<GrMemberInfo>();
extractClassMembers(subclass, members, filter, extractInterfacesDeep);
return members;
}
public static void extractClassMembers(PsiClass subclass,
List<GrMemberInfo> result,
Filter<GrMember> filter,
final boolean extractInterfacesDeep) {
if (!(subclass instanceof GrTypeDefinition)) return;
if (extractInterfacesDeep) {
extractSuperInterfaces(subclass, filter, result, ContainerUtil.<PsiClass>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<GrMember> filter,
final List<GrMemberInfo> result,
Set<PsiClass> 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<GrMember> filter,
final List<GrMemberInfo> result,
final Set<PsiClass> 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;
}
@@ -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<GrMember, PsiClass, GrMemberInfo> {
public GrMemberInfoStorage(GrTypeDefinition aClass, MemberInfoBase.Filter<GrMember> memberInfoFilter) {
super(aClass, memberInfoFilter);
}
@Override
protected boolean isInheritor(PsiClass baseClass, PsiClass aClass) {
return aClass.isInheritor(baseClass, true);
}
@Override
protected void extractClassMembers(PsiClass aClass, ArrayList<GrMemberInfo> 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);
}
}
}
}
@@ -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<GrMember, GrMemberInfo> {
public GrMemberSelectionTable(final List<GrMemberInfo> memberInfos, String abstractColumnHeader) {
this(memberInfos, null, abstractColumnHeader);
}
public GrMemberSelectionTable(final List<GrMemberInfo> memberInfos, MemberInfoModel<GrMember, GrMemberInfo> 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;
}
}
@@ -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;
}
}
@@ -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<PsiElement, String> conflicts = new MultiMap<PsiElement, String>();
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<PsiClass> classPointer =
SmartPointerManager.getInstance(myProject).createSmartPsiElementPointer(myClass);
final SmartPsiElementPointer<PsiClass> 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;
}
@@ -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<GrMemberInfoStorage, GrMemberInfo, GrMember, PsiClass> {
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<PsiClass> 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<GrMemberInfo> 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<GrMember, GrMemberInfo> createMemberSelectionTable(List<GrMemberInfo> infos) {
return new GrMemberSelectionTable(infos, RefactoringBundle.message("make.abstract"));
}
@Override
protected MemberInfoModel<GrMember, GrMemberInfo> createMemberInfoModel() {
//return new UsedByDependencyMemberInfoModel<PyElement, PyClass, PyMemberInfo>(myClass);
return new MyMemberInfoModel(myClass, getSuperClass(), false);
}
private static class MyMemberInfoModel extends AbstractUsesDependencyMemberInfoModel<GrMember, PsiClass, GrMemberInfo> {
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;
}
}
}
@@ -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<PsiClass> 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<GrMember>() {
public boolean includeMember(GrMember element) {
return true;
}
});
List<GrMemberInfo> 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<GrMemberInfo> _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<PsiElement, String> conflicts = new MultiMap<PsiElement, String>();
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;
}
}
@@ -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<Boolean> SUPER_REF = Key.create("SUPER_REF");
private static final Key<Boolean> THIS_REF = Key.create("THIS_REF");
private static final Key<Boolean> PRESERVE_QUALIFIER = Key.create("PRESERVE_QUALIFIER");
private PsiClass mySourceClass;
private GrTypeDefinition myTargetSuperClass;
private GrMemberInfo[] myMembersToMove;
private DocCommentPolicy myDocCommentPolicy;
private Set<PsiMember> 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<UsageInfo> result = new ArrayList<UsageInfo>();
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<PsiMember> 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<? extends GroovyPsiElement> 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<PsiTypeParameter, PsiType> 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<PsiMember> 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<PsiMember> 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<PsiMember> 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<PsiTypeParameter> parametersIterable,
final PsiSubstitutor substitutor,
final GroovyPsiElementFactory factory) {
final Map<PsiElement, PsiElement> replacement = new LinkedHashMap<PsiElement, PsiElement>();
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<PsiMember> movedMembers) throws IncorrectOperationException {
final StaticReferencesCollector collector = new StaticReferencesCollector(movedMembers);
classMember.accept(collector);
ArrayList<GrReferenceElement> refs = collector.getReferences();
ArrayList<PsiElement> members = collector.getReferees();
ArrayList<PsiClass> 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<GrReferenceElement> myReferences = new ArrayList<GrReferenceElement>();
private final ArrayList<PsiElement> myReferees = new ArrayList<PsiElement>();
private final ArrayList<PsiClass> myRefereeClasses = new ArrayList<PsiClass>();
private final Set<PsiMember> myMovedMembers;
private StaticReferencesCollector(Set<PsiMember> movedMembers) {
super(mySourceClass);
myMovedMembers = movedMembers;
}
public ArrayList<PsiElement> getReferees() {
return myReferees;
}
public ArrayList<PsiClass> getRefereeClasses() {
return myRefereeClasses;
}
public ArrayList<GrReferenceElement> 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<GrExpression> mySupersToDelete = ContainerUtil.newArrayList();
private final ArrayList<GrReferenceExpression> 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<PsiMember> 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<PsiMember> 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);
}
}
}
}
}
}
@@ -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<? extends PsiMember> myClass;
private boolean myAbstract;
public MemberDescriptor(String name, Class<? extends PsiMember> aClass, boolean isAbstract) {
myName = name;
myClass = aClass;
myAbstract = isAbstract;
}
public MemberDescriptor(String name, Class<? extends PsiMember> 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<? extends PsiMember> 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
}
}
@@ -0,0 +1,10 @@
class Foo {
}
class FooImpl extends Foo {
private void fo<caret>o(){}
void bar() {
foo();
}
}
@@ -0,0 +1,10 @@
class Foo {
protected void foo(){}
}
class FooImpl extends Foo {
void bar() {
foo();
}
}
@@ -0,0 +1,8 @@
interface Base {
}
interface I2 extends Base {
default void foo<caret>() {
System.out.println("Hi there.");
}
}
@@ -0,0 +1,8 @@
interface Base {
default void foo() {
System.out.println("Hi there.");
}
}
interface I2 extends Base {
}
@@ -0,0 +1,5 @@
public abstract class Parent<S> {}
class Child<T> extends Parent<T> {
void <caret>method(T t){}
}
@@ -0,0 +1,8 @@
public abstract class Parent<S> {
abstract void method(S t)
}
class Child<T> extends Parent<T> {
@Override
void method(T t){}
}
@@ -0,0 +1,10 @@
public class Parent<S> {}
interface I<IT> {
void method(IT t);
}
class Child<T> extends Parent<T> implements I<T>{
<caret>
public void method(T t){}
}
@@ -0,0 +1,10 @@
public class Parent<S> implements I<S> {}
interface I<IT> {
void method(IT t);
}
class Child<T> extends Parent<T> {
public void method(T t){}
}
@@ -0,0 +1,18 @@
public class A {
}
class B extends A {
final String <caret>f;
final String foo;
B(String fi, String foo) {
this.foo = foo;
if (fi == this.foo) {
f = foo;
} else {
f = "";
}
}
}
@@ -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 = "";
}
}
}
@@ -0,0 +1,18 @@
public class A {
}
class B extends A {
final String <caret>f;
final String foo;
B(String fi, String foo) {
this.foo = foo;
if (fi == foo) {
f = foo;
} else {
f = "";
}
}
}
@@ -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;
}
}
@@ -0,0 +1,3 @@
class Base implements I<String> {}
class Te<caret>st extends Base implements I<String> {}
interface I<T>{}
@@ -0,0 +1,3 @@
class Base implements I<String> {}
class Test extends Base {}
interface I<T>{}
@@ -0,0 +1,17 @@
public class A {
}
class B extends A {
final String <caret>f;
final String foo;
B(String fi, String foo) {
this.foo = foo;
f = "";
}
B(String foo) {
this.foo = foo;
f = "";
}
}
@@ -0,0 +1,21 @@
public class A {
}
class B extends A {
final String <caret>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 = "";
}
}
@@ -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 = "";
}
}
@@ -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;
}
}
@@ -0,0 +1,12 @@
abstract class Bazz {
public abstract void foo();
}
abstract class Foo extends Bazz {}
class Bar extends Foo {
@Override
public void f<caret>oo() {
}
}
@@ -0,0 +1,12 @@
public class Test {
abstract class Base extends IntImpl {
@Override
public abstract String<caret> foo();
}
class IntImpl extends Int {}
class Int {
public abstract String foo();
}
}
@@ -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();
}
}
@@ -0,0 +1,13 @@
abstract class Bazz {
public abstract void foo();
}
abstract class Foo extends Bazz {
@Override
public void foo() {
}
}
class Bar extends Foo {
}
@@ -0,0 +1,11 @@
public class A2 {
}
class B2 extends A2 {
public void <caret>a() {
b();
}
private void b() {
}
}
@@ -0,0 +1,14 @@
public abstract class A2 {
public void a() {
b();
}
protected abstract void b()
}
class B2 extends A2 {
@Override
protected void b() {
}
}
@@ -0,0 +1,8 @@
public class A extends AA {
<caret>
static class C extends D {}
static class D extends B {}
static class B {}
}
class AA {}
@@ -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 {}
}
@@ -0,0 +1,15 @@
public class B extends A {
private void f(){
new C();
}
<caret>
private static class C{
private C(){
}
}
}
//A.java
class A {
}
@@ -0,0 +1,15 @@
public class B extends A {
private void f(){
new C();
}
}
//A.java
class A {
protected static class C{
protected C(){
}
}
}
@@ -0,0 +1,13 @@
class X {}
class <caret>Y extends X {
private static int x = 0;
public static int getX() {
return x;
}
public static void setX(int x) {
Y.x = x;
}
}
@@ -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 {
}
@@ -0,0 +1,12 @@
class <caret>QualifiedSuper extends Base {
class Inner {
void goo() {
QualifiedSuper.super.toString();
}
}
}
class Base {
}
@@ -0,0 +1,12 @@
class QualifiedSuper extends Base {
}
class Base {
class Inner {
void goo() {
Base.this.toString();
}
}
}
@@ -0,0 +1,15 @@
class Base {
void foo() {
}
}
public class <caret>QualifiedThis extends Base {
void foo() {
}
class Inner {
void bar() {
QualifiedThis.this.foo();
}
}
}
@@ -0,0 +1,16 @@
class Base {
void foo() {
}
class Inner {
void bar() {
Base.this.foo();
}
}
}
public class QualifiedThis extends Base {
void foo() {
}
}
@@ -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 <caret>get();
}
interface Int {
}
}
@@ -0,0 +1,9 @@
public class Test {
abstract class Base extends Int {
@Override
public abstract String<caret> foo();
}
class Int {
}
}
@@ -0,0 +1,8 @@
public class Test {
abstract class Base extends Int {
}
abstract class Int {
public abstract String foo();
}
}
@@ -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();
}
}
@@ -0,0 +1,18 @@
class A {}
class AImpl1 extends A{
void f<caret>oo() {
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");
}
}
@@ -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();
}
}
@@ -0,0 +1,19 @@
public class Sup {
}
class ExtractSuperClass extends Sup {
private final String <caret>field;
public ExtractSuperClass() {
try {
field = (String)"text";
}
catch (RuntimeException e) {
throw new RuntimeException();
}
}
}
@@ -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();
}
}
@@ -0,0 +1,5 @@
public class Parent<S> {}
class Child extends Parent<String> {
String <caret>f;
}
@@ -0,0 +1,6 @@
public class Parent<S> {
String f
}
class Child extends Parent<String> {
}
@@ -0,0 +1,5 @@
public class Parent {}
class Child<T> extends Parent {
T <caret>f;
}
@@ -0,0 +1,6 @@
public class Parent {
Object f
}
class Child<T> extends Parent {
}
@@ -0,0 +1,5 @@
public class Parent<S> {}
class Child<T> extends Parent<T> {
T <caret>f;
}
@@ -0,0 +1,6 @@
public class Parent<S> {
S f
}
class Child<T> extends Parent<T> {
}
@@ -0,0 +1,7 @@
interface Bar<T> { }
interface Base<T> { }
class Foo<T,U> implements Base<U> {
void fo<caret>o(Bar<U> bar) { }
}
@@ -0,0 +1,10 @@
interface Bar<T> { }
interface Base<T> {
void foo(Bar<T> bar);
}
class Foo<T,U> implements Base<U> {
@Override
public void foo(Bar<U> bar) { }
}
@@ -0,0 +1,10 @@
class Foo {}
class FooImpl extends Foo {
public static void foo(){}
<caret>
}
class U {
public static void main(String[] args) {
FooImpl.foo();
}
}
@@ -0,0 +1,11 @@
class Foo {
public static void foo(){}
}
class FooImpl extends Foo {
}
class U {
public static void main(String[] args) {
Foo.foo();
}
}