2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
21 package com
.intellij
.refactoring
.inlineSuperClass
;
23 import com
.intellij
.openapi
.diagnostic
.Logger
;
24 import com
.intellij
.openapi
.project
.Project
;
25 import com
.intellij
.openapi
.util
.Ref
;
26 import com
.intellij
.psi
.*;
27 import com
.intellij
.psi
.search
.searches
.ReferencesSearch
;
28 import com
.intellij
.psi
.util
.PsiTreeUtil
;
29 import com
.intellij
.psi
.util
.PsiUtil
;
30 import com
.intellij
.psi
.util
.TypeConversionUtil
;
31 import com
.intellij
.refactoring
.inlineSuperClass
.usageInfo
.*;
32 import com
.intellij
.refactoring
.memberPushDown
.PushDownConflicts
;
33 import com
.intellij
.refactoring
.memberPushDown
.PushDownProcessor
;
34 import com
.intellij
.refactoring
.util
.DocCommentPolicy
;
35 import com
.intellij
.refactoring
.util
.FixableUsageInfo
;
36 import com
.intellij
.refactoring
.util
.FixableUsagesRefactoringProcessor
;
37 import com
.intellij
.refactoring
.util
.RefactoringUtil
;
38 import com
.intellij
.refactoring
.util
.classMembers
.MemberInfo
;
39 import com
.intellij
.refactoring
.util
.classMembers
.MemberInfoStorage
;
40 import com
.intellij
.usageView
.UsageInfo
;
41 import com
.intellij
.usageView
.UsageViewDescriptor
;
42 import com
.intellij
.util
.IncorrectOperationException
;
43 import com
.intellij
.util
.Processor
;import com
.intellij
.util
.containers
.HashMap
;
44 import com
.intellij
.util
.containers
.MultiMap
;
45 import org
.jetbrains
.annotations
.NotNull
;
47 import java
.util
.List
;
50 public class InlineSuperClassRefactoringProcessor
extends FixableUsagesRefactoringProcessor
{
51 public static final Logger LOG
= Logger
.getInstance("#" + InlineSuperClassRefactoringProcessor
.class.getName());
53 private final PsiClass mySuperClass
;
54 private final PsiClass
[] myTargetClasses
;
55 private final MemberInfo
[] myMemberInfos
;
57 public InlineSuperClassRefactoringProcessor(Project project
, PsiClass superClass
, final PsiClass
... targetClasses
) {
59 mySuperClass
= superClass
;
60 myTargetClasses
= targetClasses
;
61 MemberInfoStorage memberInfoStorage
= new MemberInfoStorage(mySuperClass
, new MemberInfo
.Filter
<PsiMember
>() {
62 public boolean includeMember(PsiMember element
) {
66 final List
<MemberInfo
> members
= memberInfoStorage
.getClassMemberInfos(mySuperClass
);
67 for (MemberInfo member
: members
) {
68 member
.setChecked(true);
70 myMemberInfos
= members
.toArray(new MemberInfo
[members
.size()]);
73 protected UsageViewDescriptor
createUsageViewDescriptor(final UsageInfo
[] usages
) {
74 return new InlineSuperClassUsageViewDescriptor(mySuperClass
);
78 protected void findUsages(@NotNull final List
<FixableUsageInfo
> usages
) {
79 final JavaPsiFacade facade
= JavaPsiFacade
.getInstance(myProject
);
80 final PsiElementFactory elementFactory
= facade
.getElementFactory();
81 final PsiResolveHelper resolveHelper
= facade
.getResolveHelper();
83 ReferencesSearch
.search(mySuperClass
).forEach(new Processor
<PsiReference
>() {
84 public boolean process(final PsiReference reference
) {
85 final PsiElement element
= reference
.getElement();
86 if (element
instanceof PsiJavaCodeReferenceElement
) {
87 final PsiImportStaticStatement staticImportStatement
= PsiTreeUtil
.getParentOfType(element
, PsiImportStaticStatement
.class);
88 if (staticImportStatement
!= null) {
89 usages
.add(new ReplaceStaticImportUsageInfo(staticImportStatement
, myTargetClasses
));
91 final PsiImportStatement importStatement
= PsiTreeUtil
.getParentOfType(element
, PsiImportStatement
.class);
92 if (importStatement
!= null) {
93 usages
.add(new RemoveImportUsageInfo(importStatement
));
96 final PsiElement parent
= element
.getParent();
97 if (parent
instanceof PsiReferenceList
) {
98 final PsiElement pparent
= parent
.getParent();
99 if (pparent
instanceof PsiClass
) {
100 final PsiClass inheritor
= (PsiClass
)pparent
;
101 if (parent
.equals(inheritor
.getExtendsList()) || parent
.equals(inheritor
.getImplementsList())) {
102 usages
.add(new ReplaceExtendsListUsageInfo((PsiJavaCodeReferenceElement
)element
, mySuperClass
, inheritor
));
106 final PsiClass targetClass
= myTargetClasses
[0];
107 final PsiClassType targetClassType
= elementFactory
108 .createType(targetClass
, TypeConversionUtil
.getSuperClassSubstitutor(mySuperClass
, targetClass
, PsiSubstitutor
.EMPTY
));
110 if (parent
instanceof PsiTypeElement
) {
111 final PsiType superClassType
= ((PsiTypeElement
)parent
).getType();
112 PsiSubstitutor subst
= getSuperClassSubstitutor(superClassType
, targetClassType
, resolveHelper
, targetClass
);
113 usages
.add(new ReplaceWithSubtypeUsageInfo(((PsiTypeElement
)parent
), elementFactory
.createType(targetClass
, subst
), myTargetClasses
));
115 else if (parent
instanceof PsiNewExpression
) {
116 final PsiClassType newType
= elementFactory
.createType(targetClass
,
117 getSuperClassSubstitutor(((PsiNewExpression
)parent
).getType(),
118 targetClassType
, resolveHelper
,
120 usages
.add(new ReplaceConstructorUsageInfo(((PsiNewExpression
)parent
), newType
, myTargetClasses
));
122 else if (parent
instanceof PsiJavaCodeReferenceElement
) {
123 usages
.add(new ReplaceReferenceUsageInfo(((PsiJavaCodeReferenceElement
)parent
).getQualifier(), myTargetClasses
));
132 for (PsiClass targetClass
: myTargetClasses
) {
133 for (MemberInfo memberInfo
: myMemberInfos
) {
134 final PsiMember member
= memberInfo
.getMember();
135 for (PsiReference reference
: ReferencesSearch
.search(member
, member
.getUseScope(), true)) {
136 final PsiElement element
= reference
.getElement();
137 if (element
instanceof PsiReferenceExpression
&&
138 ((PsiReferenceExpression
)element
).getQualifierExpression() instanceof PsiSuperExpression
&&
139 PsiTreeUtil
.isAncestor(targetClass
, element
, false)) {
140 usages
.add(new RemoveQualifierUsageInfo((PsiReferenceExpression
)element
));
145 final PsiMethod
[] superConstructors
= mySuperClass
.getConstructors();
146 for (PsiMethod constructor
: targetClass
.getConstructors()) {
147 final PsiCodeBlock constrBody
= constructor
.getBody();
148 LOG
.assertTrue(constrBody
!= null);
149 final PsiStatement
[] statements
= constrBody
.getStatements();
150 if (statements
.length
> 0) {
151 final PsiStatement firstConstrStatement
= statements
[0];
152 if (firstConstrStatement
instanceof PsiExpressionStatement
) {
153 final PsiExpression expression
= ((PsiExpressionStatement
)firstConstrStatement
).getExpression();
154 if (expression
instanceof PsiMethodCallExpression
) {
155 final PsiReferenceExpression methodExpression
= ((PsiMethodCallExpression
)expression
).getMethodExpression();
156 if (methodExpression
.getText().equals(PsiKeyword
.SUPER
)) {
157 final PsiMethod superConstructor
= ((PsiMethodCallExpression
)expression
).resolveMethod();
158 if (superConstructor
!= null && superConstructor
.getBody() != null) {
159 usages
.add(new InlineSuperCallUsageInfo((PsiMethodCallExpression
)expression
));
167 //insert implicit call to super
168 for (PsiMethod superConstructor
: superConstructors
) {
169 if (superConstructor
.getParameterList().getParametersCount() == 0) {
170 final PsiExpression expression
= JavaPsiFacade
.getElementFactory(myProject
).createExpressionFromText("super()", constructor
);
171 usages
.add(new InlineSuperCallUsageInfo((PsiMethodCallExpression
)expression
, constrBody
));
176 if (targetClass
.getConstructors().length
== 0) {
177 //copy default constructor
178 for (PsiMethod superConstructor
: superConstructors
) {
179 if (superConstructor
.getParameterList().getParametersCount() == 0) {
180 usages
.add(new CopyDefaultConstructorUsageInfo(targetClass
, superConstructor
));
189 protected boolean preprocessUsages(final Ref
<UsageInfo
[]> refUsages
) {
190 final MultiMap
<PsiElement
, String
> conflicts
= new MultiMap
<PsiElement
, String
>();
191 final PushDownConflicts pushDownConflicts
= new PushDownConflicts(mySuperClass
, myMemberInfos
);
192 for (PsiClass targetClass
: myTargetClasses
) {
193 for (MemberInfo info
: myMemberInfos
) {
194 final PsiMember member
= info
.getMember();
195 pushDownConflicts
.checkMemberPlacementInTargetClassConflict(targetClass
, member
);
197 //todo check accessibility conflicts
199 final MultiMap
<PsiElement
, String
> conflictsMap
= pushDownConflicts
.getConflicts();
200 for (PsiElement element
: conflictsMap
.keySet()) {
201 conflicts
.put(element
, conflictsMap
.get(element
));
203 checkConflicts(refUsages
, conflicts
);
204 return showConflicts(conflicts
);
207 protected void performRefactoring(final UsageInfo
[] usages
) {
208 new PushDownProcessor(mySuperClass
.getProject(), myMemberInfos
, mySuperClass
, new DocCommentPolicy(DocCommentPolicy
.ASIS
)){
209 //push down conflicts are already collected
211 protected boolean showConflicts(MultiMap
<PsiElement
, String
> conflicts
) {
216 RefactoringUtil
.sortDepthFirstRightLeftOrder(usages
);
217 for (UsageInfo usageInfo
: usages
) {
218 if (!(usageInfo
instanceof ReplaceExtendsListUsageInfo
)) {
220 ((FixableUsageInfo
)usageInfo
).fixUsage();
222 catch (IncorrectOperationException e
) {
227 replaceInnerTypeUsages();
229 //postpone broken hierarchy
230 for (UsageInfo usage
: usages
) {
231 if (usage
instanceof ReplaceExtendsListUsageInfo
) {
232 ((ReplaceExtendsListUsageInfo
)usage
).fixUsage();
236 mySuperClass
.delete();
238 catch (IncorrectOperationException e
) {
243 private void replaceInnerTypeUsages() {
244 final JavaPsiFacade facade
= JavaPsiFacade
.getInstance(myProject
);
245 final PsiElementFactory elementFactory
= facade
.getElementFactory();
246 final PsiResolveHelper resolveHelper
= facade
.getResolveHelper();
247 final Map
<UsageInfo
, PsiElement
> replacementMap
= new HashMap
<UsageInfo
, PsiElement
>();
248 for (final PsiClass targetClass
: myTargetClasses
) {
249 final PsiSubstitutor superClassSubstitutor
=
250 TypeConversionUtil
.getSuperClassSubstitutor(mySuperClass
, targetClass
, PsiSubstitutor
.EMPTY
);
251 final PsiClassType targetClassType
= elementFactory
.createType(targetClass
, superClassSubstitutor
);
252 targetClass
.accept(new JavaRecursiveElementWalkingVisitor() {
254 public void visitTypeElement(final PsiTypeElement typeElement
) {
255 super.visitTypeElement(typeElement
);
256 final PsiType superClassType
= typeElement
.getType();
257 if (PsiUtil
.resolveClassInType(superClassType
) == mySuperClass
) {
258 PsiSubstitutor subst
= getSuperClassSubstitutor(superClassType
, targetClassType
, resolveHelper
, targetClass
);
259 replacementMap
.put(new UsageInfo(typeElement
), elementFactory
.createTypeElement(elementFactory
.createType(targetClass
, subst
)));
264 public void visitNewExpression(final PsiNewExpression expression
) {
265 super.visitNewExpression(expression
);
266 final PsiType superClassType
= expression
.getType();
267 if (PsiUtil
.resolveClassInType(superClassType
) == mySuperClass
) {
268 PsiSubstitutor subst
= getSuperClassSubstitutor(superClassType
, targetClassType
, resolveHelper
, targetClass
);
270 replacementMap
.put(new UsageInfo(expression
), elementFactory
.createExpressionFromText("new " + elementFactory
.createType(
271 targetClass
, subst
).getCanonicalText() + expression
.getArgumentList().getText(), expression
));
273 catch (IncorrectOperationException e
) {
281 for (Map
.Entry
<UsageInfo
,PsiElement
> elementEntry
: replacementMap
.entrySet()) {
282 final PsiElement element
= elementEntry
.getKey().getElement();
283 if (element
!= null) {
284 element
.replace(elementEntry
.getValue());
288 catch (IncorrectOperationException e
) {
293 private static PsiSubstitutor
getSuperClassSubstitutor(final PsiType superClassType
, final PsiClassType targetClassType
, final PsiResolveHelper resolveHelper
, PsiClass targetClass
) {
294 PsiSubstitutor subst
= PsiSubstitutor
.EMPTY
;
295 for (PsiTypeParameter typeParameter
: PsiUtil
.typeParametersIterable(targetClass
)) {
296 subst
= subst
.put(typeParameter
,
297 resolveHelper
.getSubstitutionForTypeParameter(typeParameter
, targetClassType
, superClassType
, false,
298 PsiUtil
.getLanguageLevel(targetClass
)));
303 protected String
getCommandName() {
304 return InlineSuperClassRefactoringHandler
.REFACTORING_NAME
;