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.
16 package com
.intellij
.codeInspection
.defaultFileTemplateUsage
;
18 import com
.intellij
.codeInsight
.CodeInsightUtil
;
19 import com
.intellij
.codeInsight
.PsiEquivalenceUtil
;
20 import com
.intellij
.codeInsight
.daemon
.impl
.quickfix
.CreateFromUsageUtils
;
21 import com
.intellij
.codeInsight
.generation
.OverrideImplementUtil
;
22 import com
.intellij
.codeInspection
.*;
23 import com
.intellij
.ide
.fileTemplates
.FileTemplate
;
24 import com
.intellij
.ide
.fileTemplates
.FileTemplateManager
;
25 import com
.intellij
.ide
.fileTemplates
.JavaTemplateUtil
;
26 import com
.intellij
.openapi
.diagnostic
.Logger
;
27 import com
.intellij
.openapi
.project
.Project
;
28 import com
.intellij
.openapi
.util
.Key
;
29 import com
.intellij
.openapi
.util
.Pair
;
30 import com
.intellij
.openapi
.util
.UserDataHolderEx
;
31 import com
.intellij
.psi
.*;
32 import com
.intellij
.psi
.codeStyle
.CodeStyleManager
;
33 import com
.intellij
.psi
.search
.GlobalSearchScope
;
34 import com
.intellij
.util
.IncorrectOperationException
;
35 import com
.intellij
.util
.containers
.ConcurrentHashMap
;
36 import org
.jetbrains
.annotations
.NotNull
;
37 import org
.jetbrains
.annotations
.Nullable
;
39 import java
.util
.Collection
;
40 import java
.util
.Comparator
;
41 import java
.util
.List
;
47 public class MethodBodyChecker
{
48 private static final Logger LOG
= Logger
.getInstance("#com.intellij.codeInspection.defaultFileTemplateUsage.MethodBodyChecker");
51 private static PsiMethod
getTemplateMethod(PsiType returnType
, List
<HierarchicalMethodSignature
> superSignatures
, final PsiClass aClass
) {
52 Project project
= aClass
.getProject();
54 if (!(returnType
instanceof PsiPrimitiveType
)) {
55 returnType
= PsiType
.getJavaLangObject(PsiManager
.getInstance(project
), GlobalSearchScope
.allScope(project
));
58 final FileTemplate template
= getMethodFileTemplate(superSignatures
, true);
59 if (template
== null) return null;
60 final String fileTemplateName
= template
.getName();
61 String methodName
= superSignatures
.isEmpty() ?
"" : superSignatures
.get(0).getName();
62 String key
= returnType
.getCanonicalText() + "+" + methodName
+ "+"+fileTemplateName
;
63 final Map
<String
, PsiMethod
> cache
= getTemplatesCache(aClass
);
64 PsiMethod method
= cache
.get(key
);
66 method
= JavaPsiFacade
.getInstance(project
).getElementFactory().createMethod("x", returnType
);
67 setupMethodBody(superSignatures
, method
, aClass
, true);
68 cache
.put(key
, method
);
72 catch (IncorrectOperationException e
) {
77 private static final Key
<Map
<String
, PsiMethod
>> CACHE_KEY
= new Key
<Map
<String
, PsiMethod
>>("MethodBodyChecker templates cache");
79 private static Map
<String
, PsiMethod
> getTemplatesCache(PsiClass aClass
) {
80 Map
<String
, PsiMethod
> cache
= aClass
.getUserData(CACHE_KEY
);
82 cache
= ((UserDataHolderEx
)aClass
).putUserDataIfAbsent(CACHE_KEY
, new ConcurrentHashMap
<String
, PsiMethod
>());
87 static void checkMethodBody(final PsiMethod method
,
88 final InspectionManager manager
,
89 final Collection
<ProblemDescriptor
> problemDescriptors
, boolean onTheFly
) {
90 PsiType returnType
= method
.getReturnType();
91 if (method
.isConstructor() || returnType
== null) return;
92 PsiCodeBlock body
= method
.getBody();
93 if (body
== null) return;
94 PsiClass aClass
= method
.getContainingClass();
95 if (aClass
== null || aClass
.isInterface()) return;
96 List
<HierarchicalMethodSignature
> superSignatures
= method
.getHierarchicalMethodSignature().getSuperSignatures();
97 final PsiMethod superMethod
= superSignatures
.isEmpty() ?
null : superSignatures
.get(0).getMethod();
99 final PsiMethod templateMethod
= getTemplateMethod(returnType
, superSignatures
, aClass
);
100 if (templateMethod
== null) return;
102 final PsiCodeBlock templateBody
= templateMethod
.getBody();
103 if (templateBody
== null) return;
105 if (PsiEquivalenceUtil
.areElementsEquivalent(body
, templateBody
, new Comparator
<PsiElement
>(){
106 public int compare(final PsiElement element1
, final PsiElement element2
) {
107 // templates may be different on super method name
108 if (element1
== superMethod
&& (element2
== templateMethod
|| element2
== null)) return 0;
112 Pair
<?
extends PsiElement
, ?
extends PsiElement
> range
= DefaultFileTemplateUsageInspection
.getInteriorRange(body
);
113 final String description
= InspectionsBundle
.message("default.file.template.description");
114 ProblemDescriptor problem
= manager
.createProblemDescriptor(range
.first
, range
.second
, description
,
115 ProblemHighlightType
.GENERIC_ERROR_OR_WARNING
, onTheFly
,
116 createMethodBodyQuickFix(method
));
117 problemDescriptors
.add(problem
);
122 private static FileTemplate
getMethodFileTemplate(final List
<HierarchicalMethodSignature
> superSignatures
,
123 final boolean useDefaultTemplate
) {
124 FileTemplateManager templateManager
= FileTemplateManager
.getInstance();
125 FileTemplate template
;
126 if (superSignatures
.isEmpty()) {
127 String name
= JavaTemplateUtil
.TEMPLATE_FROM_USAGE_METHOD_BODY
;
128 template
= useDefaultTemplate ? templateManager
.getDefaultTemplate(name
) : templateManager
.getCodeTemplate(name
);
131 PsiMethod superMethod
= superSignatures
.get(0).getMethod();
132 String name
= superMethod
.hasModifierProperty(PsiModifier
.ABSTRACT
) ?
133 JavaTemplateUtil
.TEMPLATE_IMPLEMENTED_METHOD_BODY
: JavaTemplateUtil
.TEMPLATE_OVERRIDDEN_METHOD_BODY
;
134 template
= useDefaultTemplate ? templateManager
.getDefaultTemplate(name
) : templateManager
.getCodeTemplate(name
);
139 private static final String NEW_METHOD_BODY_TEMPLATE_NAME
= FileTemplateManager
.getInstance().getDefaultTemplate(JavaTemplateUtil
.TEMPLATE_FROM_USAGE_METHOD_BODY
).getName();
141 private static FileTemplate
setupMethodBody(final List
<HierarchicalMethodSignature
> superSignatures
,
142 final PsiMethod templateMethod
,
143 final PsiClass aClass
,
144 final boolean useDefaultTemplate
) throws IncorrectOperationException
{
145 FileTemplate template
= getMethodFileTemplate(superSignatures
, useDefaultTemplate
);
146 if (template
== null) return null;
147 if (NEW_METHOD_BODY_TEMPLATE_NAME
.equals(template
.getName())) {
148 CreateFromUsageUtils
.setupMethodBody(templateMethod
, aClass
, template
);
151 PsiMethod superMethod
= superSignatures
.get(0).getMethod();
152 OverrideImplementUtil
.setupMethodBody(templateMethod
, superMethod
, aClass
,template
);
158 private static LocalQuickFix
[] createMethodBodyQuickFix(final PsiMethod method
) {
159 PsiType returnType
= method
.getReturnType();
160 PsiClass aClass
= method
.getContainingClass();
161 List
<HierarchicalMethodSignature
> superSignatures
= method
.getHierarchicalMethodSignature().getSuperSignatures();
162 FileTemplate template
;
164 PsiMethod templateMethod
= JavaPsiFacade
.getInstance(method
.getProject()).getElementFactory().createMethod("x", returnType
);
165 template
= setupMethodBody(superSignatures
, templateMethod
, aClass
, false);
167 catch (IncorrectOperationException e
) {
171 final ReplaceWithFileTemplateFix replaceWithFileTemplateFix
= new ReplaceWithFileTemplateFix() {
172 public void applyFix(@NotNull Project project
, @NotNull ProblemDescriptor descriptor
) {
173 PsiType returnType
= method
.getReturnType();
174 if (method
.isConstructor() || returnType
== null) return;
175 PsiCodeBlock body
= method
.getBody();
176 if (body
== null) return;
177 if (!CodeInsightUtil
.preparePsiElementsForWrite(body
)) return;
178 PsiClass aClass
= method
.getContainingClass();
179 if (aClass
== null) return;
180 List
<HierarchicalMethodSignature
> superSignatures
= method
.getHierarchicalMethodSignature().getSuperSignatures();
182 PsiMethod templateMethod
= JavaPsiFacade
.getInstance(method
.getProject()).getElementFactory().createMethod("x", returnType
);
183 setupMethodBody(superSignatures
, templateMethod
, aClass
, false);
184 final PsiCodeBlock templateBody
= templateMethod
.getBody();
185 if (templateBody
== null) return;
187 PsiElement newBody
= body
.replace(templateBody
);
188 CodeStyleManager
.getInstance(project
).reformat(newBody
);
190 catch (IncorrectOperationException e
) {
195 LocalQuickFix editFileTemplateFix
= DefaultFileTemplateUsageInspection
.createEditFileTemplateFix(template
, replaceWithFileTemplateFix
);
196 if (template
!= null && template
.isDefault()) {
197 return new LocalQuickFix
[]{editFileTemplateFix
};
199 return new LocalQuickFix
[]{replaceWithFileTemplateFix
, editFileTemplateFix
};