Inspections - pass onTheFly into ProblemDescriptors & use it to create LAZY refs...
[fedora-idea.git] / java / java-impl / src / com / intellij / codeInspection / defaultFileTemplateUsage / MethodBodyChecker.java
blob2a99d957e10f3607d7f77654fedd7b8f88b4d579
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.
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;
42 import java.util.Map;
44 /**
45 * @author Alexey
47 public class MethodBodyChecker {
48 private static final Logger LOG = Logger.getInstance("#com.intellij.codeInspection.defaultFileTemplateUsage.MethodBodyChecker");
50 @Nullable
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));
57 try {
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);
65 if (method == null) {
66 method = JavaPsiFacade.getInstance(project).getElementFactory().createMethod("x", returnType);
67 setupMethodBody(superSignatures, method, aClass, true);
68 cache.put(key, method);
70 return method;
72 catch (IncorrectOperationException e) {
73 return null;
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);
81 if (cache == null) {
82 cache = ((UserDataHolderEx)aClass).putUserDataIfAbsent(CACHE_KEY, new ConcurrentHashMap<String, PsiMethod>());
84 return cache;
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;
109 return 1;
111 }, true)) {
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);
121 @Nullable
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);
130 else {
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);
136 return template;
139 private static final String NEW_METHOD_BODY_TEMPLATE_NAME = FileTemplateManager.getInstance().getDefaultTemplate(JavaTemplateUtil.TEMPLATE_FROM_USAGE_METHOD_BODY).getName();
140 @Nullable
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);
150 else {
151 PsiMethod superMethod = superSignatures.get(0).getMethod();
152 OverrideImplementUtil.setupMethodBody(templateMethod, superMethod, aClass,template);
154 return template;
157 @Nullable
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;
163 try {
164 PsiMethod templateMethod = JavaPsiFacade.getInstance(method.getProject()).getElementFactory().createMethod("x", returnType);
165 template = setupMethodBody(superSignatures, templateMethod, aClass, false);
167 catch (IncorrectOperationException e) {
168 return null;
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();
181 try {
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) {
191 LOG.error(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};