1 package com.intellij.refactoring.inline;
3 import com.intellij.codeInsight.TargetElementUtilBase;
4 import com.intellij.openapi.editor.Editor;
5 import com.intellij.openapi.project.Project;
6 import com.intellij.openapi.util.Ref;
7 import com.intellij.psi.*;
8 import com.intellij.psi.search.searches.ClassInheritorsSearch;
9 import com.intellij.psi.search.searches.ReferencesSearch;
10 import com.intellij.psi.util.PsiTreeUtil;
11 import com.intellij.refactoring.RefactoringBundle;
12 import com.intellij.refactoring.util.CommonRefactoringUtil;
13 import com.intellij.refactoring.util.RefactoringUtil;
14 import com.intellij.util.Processor;
15 import org.jetbrains.annotations.Nullable;
17 import java.util.Collection;
22 public class InlineToAnonymousClassHandler extends JavaInlineActionHandler {
24 public boolean isEnabledOnElement(PsiElement element) {
25 return element instanceof PsiMethod || element instanceof PsiClass;
28 public boolean canInlineElement(PsiElement element) {
29 if (element instanceof PsiMethod) {
30 PsiMethod method = (PsiMethod)element;
31 if (method.isConstructor() && !InlineMethodHandler.isChainingConstructor(method)) {
35 if (!(element instanceof PsiClass)) return false;
36 Collection<PsiClass> inheritors = ClassInheritorsSearch.search((PsiClass)element).findAll();
37 return inheritors.size() == 0;
40 public boolean canInlineElementInEditor(PsiElement element) {
41 return canInlineElement(element);
44 public void inlineElement(final Project project, final Editor editor, final PsiElement psiElement) {
45 final PsiClass psiClass = psiElement instanceof PsiMethod ? ((PsiMethod) psiElement).getContainingClass() : (PsiClass) psiElement;
46 PsiCall callToInline = findCallToInline(editor);
48 String errorMessage = getCannotInlineMessage(psiClass);
49 if (errorMessage != null) {
50 CommonRefactoringUtil.showErrorHint(project, editor, errorMessage, RefactoringBundle.message("inline.to.anonymous.refactoring"), null);
54 InlineToAnonymousClassDialog dlg = new InlineToAnonymousClassDialog(project, psiClass, callToInline);
59 public static PsiCall findCallToInline(final Editor editor) {
60 PsiCall callToInline = null;
61 PsiReference reference = editor != null ? TargetElementUtilBase.findReference(editor) : null;
62 if (reference != null) {
63 final PsiElement element = reference.getElement();
64 if (element instanceof PsiJavaCodeReferenceElement) {
65 callToInline = RefactoringUtil.getEnclosingConstructorCall((PsiJavaCodeReferenceElement)element);
72 public static String getCannotInlineMessage(final PsiClass psiClass) {
73 if (psiClass.isAnnotationType()) {
74 return "Annotation types cannot be inlined";
76 if (psiClass.isInterface()) {
77 return "Interfaces cannot be inlined";
79 if (psiClass.isEnum()) {
80 return "Enums cannot be inlined";
82 if (psiClass.hasModifierProperty(PsiModifier.ABSTRACT)) {
83 return RefactoringBundle.message("inline.to.anonymous.no.abstract");
85 if (!psiClass.getManager().isInProject(psiClass)) {
86 return "Library classes cannot be inlined";
89 PsiClassType[] classTypes = psiClass.getExtendsListTypes();
90 for(PsiClassType classType: classTypes) {
91 PsiClass superClass = classType.resolve();
92 if (superClass == null) {
93 return "Class cannot be inlined because its superclass cannot be resolved";
97 final PsiClassType[] interfaces = psiClass.getImplementsListTypes();
98 if (interfaces.length > 1) {
99 return RefactoringBundle.message("inline.to.anonymous.no.multiple.interfaces");
101 if (interfaces.length == 1) {
102 if (interfaces [0].resolve() == null) {
103 return "Class cannot be inlined because an interface implemented by it cannot be resolved";
105 final PsiClass superClass = psiClass.getSuperClass();
106 if (superClass != null && !CommonClassNames.JAVA_LANG_OBJECT.equals(superClass.getQualifiedName())) {
107 PsiClassType interfaceType = interfaces[0];
108 if (!isRedundantImplements(superClass, interfaceType)) {
109 return RefactoringBundle.message("inline.to.anonymous.no.superclass.and.interface");
114 final PsiMethod[] methods = psiClass.getMethods();
115 for(PsiMethod method: methods) {
116 if (method.isConstructor()) {
117 PsiReturnStatement stmt = findReturnStatement(method);
119 return "Class cannot be inlined because its constructor contains 'return' statements";
122 else if (method.findSuperMethods().length == 0) {
123 if (!ReferencesSearch.search(method).forEach(new AllowedUsagesProcessor(psiClass))) {
124 return "Class cannot be inlined because it has usages of methods not inherited from its superclass or interface";
127 if (method.hasModifierProperty(PsiModifier.STATIC)) {
128 return "Class cannot be inlined because it has static methods";
132 final PsiClass[] innerClasses = psiClass.getInnerClasses();
133 for(PsiClass innerClass: innerClasses) {
134 PsiModifierList classModifiers = innerClass.getModifierList();
135 if (classModifiers.hasModifierProperty(PsiModifier.STATIC)) {
136 return "Class cannot be inlined because it has static inner classes";
138 if (!ReferencesSearch.search(innerClass).forEach(new AllowedUsagesProcessor(psiClass))) {
139 return "Class cannot be inlined because it has usages of its inner classes";
143 final PsiField[] fields = psiClass.getFields();
144 for(PsiField field: fields) {
145 final PsiModifierList fieldModifiers = field.getModifierList();
146 if (fieldModifiers != null && fieldModifiers.hasModifierProperty(PsiModifier.STATIC)) {
147 if (!fieldModifiers.hasModifierProperty(PsiModifier.FINAL)) {
148 return "Class cannot be inlined because it has static non-final fields";
150 Object initValue = null;
151 final PsiExpression initializer = field.getInitializer();
152 if (initializer != null) {
153 initValue = JavaPsiFacade.getInstance(psiClass.getProject()).getConstantEvaluationHelper().computeConstantExpression(initializer);
155 if (initValue == null) {
156 return "Class cannot be inlined because it has static fields with non-constant initializers";
159 if (!ReferencesSearch.search(field).forEach(new AllowedUsagesProcessor(psiClass))) {
160 return "Class cannot be inlined because it has usages of fields not inherited from its superclass";
164 final PsiClassInitializer[] initializers = psiClass.getInitializers();
165 for(PsiClassInitializer initializer: initializers) {
166 final PsiModifierList modifiers = initializer.getModifierList();
167 if (modifiers != null && modifiers.hasModifierProperty(PsiModifier.STATIC)) {
168 return "Class cannot be inlined because it has static initializers";
175 static boolean isRedundantImplements(final PsiClass superClass, final PsiClassType interfaceType) {
176 boolean redundantImplements = false;
177 PsiClassType[] superClassInterfaces = superClass.getImplementsListTypes();
178 for(PsiClassType superClassInterface: superClassInterfaces) {
179 if (superClassInterface.equals(interfaceType)) {
180 redundantImplements = true;
184 return redundantImplements;
187 private static PsiReturnStatement findReturnStatement(final PsiMethod method) {
188 final Ref<PsiReturnStatement> stmt = Ref.create(null);
189 method.accept(new JavaRecursiveElementWalkingVisitor() {
190 @Override public void visitReturnStatement(final PsiReturnStatement statement) {
191 super.visitReturnStatement(statement);
198 private static class AllowedUsagesProcessor implements Processor<PsiReference> {
199 private final PsiElement myPsiElement;
201 public AllowedUsagesProcessor(final PsiElement psiElement) {
202 myPsiElement = psiElement;
205 public boolean process(final PsiReference psiReference) {
206 if (PsiTreeUtil.isAncestor(myPsiElement, psiReference.getElement(), false)) {
209 PsiElement element = psiReference.getElement();
210 if (element instanceof PsiReferenceExpression) {
211 PsiExpression qualifier = ((PsiReferenceExpression)element).getQualifierExpression();
212 while (qualifier instanceof PsiParenthesizedExpression) {
213 qualifier = ((PsiParenthesizedExpression) qualifier).getExpression();
215 if (qualifier instanceof PsiNewExpression) {
216 PsiNewExpression newExpr = (PsiNewExpression) qualifier;
217 PsiJavaCodeReferenceElement classRef = newExpr.getClassReference();
218 if (classRef != null && myPsiElement.equals(classRef.resolve())) {