find type parameters for class
[fedora-idea.git] / java / java-impl / src / com / intellij / refactoring / inlineSuperClass / InlineSuperClassRefactoringProcessor.java
blob112b86ba4ed947ac274d4799e5aad49c3214e5db
1 /*
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.
18 * User: anna
19 * Date: 27-Aug-2008
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;
48 import java.util.Map;
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) {
58 super(project);
59 mySuperClass = superClass;
60 myTargetClasses = targetClasses;
61 MemberInfoStorage memberInfoStorage = new MemberInfoStorage(mySuperClass, new MemberInfo.Filter<PsiMember>() {
62 public boolean includeMember(PsiMember element) {
63 return true;
65 });
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));
90 } else {
91 final PsiImportStatement importStatement = PsiTreeUtil.getParentOfType(element, PsiImportStatement.class);
92 if (importStatement != null) {
93 usages.add(new RemoveImportUsageInfo(importStatement));
95 else {
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));
105 } else {
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,
119 targetClass));
120 usages.add(new ReplaceConstructorUsageInfo(((PsiNewExpression)parent), newType, myTargetClasses));
122 else if (parent instanceof PsiJavaCodeReferenceElement) {
123 usages.add(new ReplaceReferenceUsageInfo(((PsiJavaCodeReferenceElement)parent).getQualifier(), myTargetClasses));
129 return true;
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));
160 continue;
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));
181 break;
188 @Override
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
210 @Override
211 protected boolean showConflicts(MultiMap<PsiElement, String> conflicts) {
212 return true;
214 }.run();
216 RefactoringUtil.sortDepthFirstRightLeftOrder(usages);
217 for (UsageInfo usageInfo : usages) {
218 if (!(usageInfo instanceof ReplaceExtendsListUsageInfo)) {
219 try {
220 ((FixableUsageInfo)usageInfo).fixUsage();
222 catch (IncorrectOperationException e) {
223 LOG.info(e);
227 replaceInnerTypeUsages();
229 //postpone broken hierarchy
230 for (UsageInfo usage : usages) {
231 if (usage instanceof ReplaceExtendsListUsageInfo) {
232 ((ReplaceExtendsListUsageInfo)usage).fixUsage();
235 try {
236 mySuperClass.delete();
238 catch (IncorrectOperationException e) {
239 LOG.error(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() {
253 @Override
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)));
263 @Override
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);
269 try {
270 replacementMap.put(new UsageInfo(expression), elementFactory.createExpressionFromText("new " + elementFactory.createType(
271 targetClass, subst).getCanonicalText() + expression.getArgumentList().getText(), expression));
273 catch (IncorrectOperationException e) {
274 LOG.error(e);
280 try {
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) {
289 LOG.error(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)));
300 return subst;
303 protected String getCommandName() {
304 return InlineSuperClassRefactoringHandler.REFACTORING_NAME;