Inspections - pass onTheFly into ProblemDescriptors & use it to create LAZY refs...
[fedora-idea.git] / plugins / java-i18n / src / com / intellij / codeInspection / i18n / InvalidPropertyKeyInspection.java
blob0894e454a498e3b5f0145e6fc7ea9c97d9ee5975
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.i18n;
18 import com.intellij.ExtensionPoints;
19 import com.intellij.codeHighlighting.HighlightDisplayLevel;
20 import com.intellij.codeInsight.AnnotationUtil;
21 import com.intellij.codeInsight.CodeInsightBundle;
22 import com.intellij.codeInsight.daemon.GroupNames;
23 import com.intellij.codeInspection.*;
24 import com.intellij.lang.properties.PropertiesReferenceManager;
25 import com.intellij.lang.properties.psi.PropertiesFile;
26 import com.intellij.openapi.extensions.ExtensionPoint;
27 import com.intellij.openapi.extensions.Extensions;
28 import com.intellij.openapi.module.Module;
29 import com.intellij.openapi.module.ModuleUtil;
30 import com.intellij.openapi.util.Comparing;
31 import com.intellij.openapi.util.Ref;
32 import com.intellij.psi.*;
33 import org.jetbrains.annotations.NotNull;
34 import org.jetbrains.annotations.Nullable;
36 import java.util.*;
38 /**
39 * @author max
40 * @author Konstantin Bulenkov
42 public class InvalidPropertyKeyInspection extends BaseJavaLocalInspectionTool {
44 @NotNull
45 public String getGroupDisplayName() {
46 return GroupNames.INTERNATIONALIZATION_GROUP_NAME;
49 @NotNull
50 public String getDisplayName() {
51 return CodeInsightBundle.message("inspection.unresolved.property.key.reference.name");
54 @NotNull
55 public String getShortName() {
56 return "UnresolvedPropertyKey";
59 @NotNull
60 public HighlightDisplayLevel getDefaultLevel() {
61 return HighlightDisplayLevel.ERROR;
64 public boolean isEnabledByDefault() {
65 return true;
68 @Override
69 @Nullable
70 public ProblemDescriptor[] checkMethod(@NotNull PsiMethod method, @NotNull InspectionManager manager, boolean isOnTheFly) {
71 return checkElement(method, manager, isOnTheFly);
74 @Override
75 @Nullable
76 public ProblemDescriptor[] checkClass(@NotNull PsiClass aClass, @NotNull InspectionManager manager, boolean isOnTheFly) {
77 final PsiClassInitializer[] initializers = aClass.getInitializers();
78 List<ProblemDescriptor> result = new ArrayList<ProblemDescriptor>();
79 for (PsiClassInitializer initializer : initializers) {
80 final ProblemDescriptor[] descriptors = checkElement(initializer, manager, isOnTheFly);
81 if (descriptors != null) {
82 result.addAll(Arrays.asList(descriptors));
86 return result.isEmpty() ? null : result.toArray(new ProblemDescriptor[result.size()]);
89 @Override
90 @Nullable
91 public ProblemDescriptor[] checkField(@NotNull PsiField field, @NotNull InspectionManager manager, boolean isOnTheFly) {
92 final PsiExpression initializer = field.getInitializer();
93 if (initializer != null) return checkElement(initializer, manager, isOnTheFly);
95 if (field instanceof PsiEnumConstant) {
96 return checkElement(((PsiEnumConstant)field).getArgumentList(), manager, isOnTheFly);
98 return null;
101 @Nullable private static ProblemDescriptor[] checkElement(PsiElement element, final InspectionManager manager, boolean onTheFly) {
102 UnresolvedPropertyVisitor visitor = new UnresolvedPropertyVisitor(manager, onTheFly);
103 element.accept(visitor);
104 List<ProblemDescriptor> problems = visitor.getProblems();
105 return problems.isEmpty() ? null : problems.toArray(new ProblemDescriptor[problems.size()]);
108 @Override
109 @Nullable
110 public ProblemDescriptor[] checkFile(@NotNull final PsiFile file, @NotNull final InspectionManager manager, boolean isOnTheFly) {
111 ExtensionPoint<FileCheckingInspection> point = Extensions.getRootArea().getExtensionPoint(ExtensionPoints.INVALID_PROPERTY_KEY_INSPECTION_TOOL);
112 final FileCheckingInspection[] fileCheckingInspections = point.getExtensions();
113 for(FileCheckingInspection obj: fileCheckingInspections) {
114 ProblemDescriptor[] descriptors = obj.checkFile(file, manager, isOnTheFly);
115 if (descriptors != null) {
116 return descriptors;
120 return null;
123 private static class UnresolvedPropertyVisitor extends JavaRecursiveElementWalkingVisitor {
124 private final InspectionManager myManager;
125 private final List<ProblemDescriptor> myProblems = new ArrayList<ProblemDescriptor>();
126 private boolean onTheFly;
129 public UnresolvedPropertyVisitor(final InspectionManager manager, boolean onTheFly) {
130 myManager = manager;
131 this.onTheFly = onTheFly;
134 @Override public void visitAnonymousClass(PsiAnonymousClass aClass) {
135 final PsiExpressionList argList = aClass.getArgumentList();
136 if (argList != null) {
137 argList.accept(this);
141 @Override public void visitClass(PsiClass aClass) {}
143 @Override public void visitField(PsiField field) {}
145 @Override public void visitLiteralExpression(PsiLiteralExpression expression) {
146 Object value = expression.getValue();
147 if (!(value instanceof String)) return;
148 String key = (String)value;
149 if (isComputablePropertyExpression(expression)) return;
150 Ref<String> resourceBundleName = new Ref<String>();
151 if (!JavaI18nUtil.isValidPropertyReference(expression, key, resourceBundleName)) {
152 final String description = CodeInsightBundle.message("inspection.unresolved.property.key.reference.message", key);
153 final String bundleName = resourceBundleName.get();
154 final List<PropertiesFile> propertiesFiles = JavaI18nUtil.propertiesFilesByBundleName(bundleName, expression);
155 final ProblemDescriptor problem = myManager.createProblemDescriptor(expression,
156 description,
157 new JavaCreatePropertyFix(expression, key, propertiesFiles),
158 ProblemHighlightType.LIKE_UNKNOWN_SYMBOL, onTheFly);
159 myProblems.add(problem);
160 } else
161 if (expression.getParent() instanceof PsiNameValuePair) {
162 PsiNameValuePair nvp = (PsiNameValuePair) expression.getParent();
163 if (Comparing.equal(nvp.getName(), AnnotationUtil.PROPERTY_KEY_RESOURCE_BUNDLE_PARAMETER)) {
164 PropertiesReferenceManager manager = PropertiesReferenceManager.getInstance(expression.getProject());
165 Module module = ModuleUtil.findModuleForPsiElement(expression);
166 if (module != null) {
167 List<PropertiesFile> propFiles = manager.findPropertiesFiles(module, key);
168 if (propFiles.isEmpty()) {
169 final String description = CodeInsightBundle.message("inspection.invalid.resource.bundle.reference", key);
170 final ProblemDescriptor problem = myManager.createProblemDescriptor(expression,
171 description,
172 (LocalQuickFix)null,
173 ProblemHighlightType.LIKE_UNKNOWN_SYMBOL, onTheFly);
174 myProblems.add(problem);
178 } else
179 if (expression.getParent() instanceof PsiExpressionList && expression.getParent().getParent() instanceof PsiMethodCallExpression) {
180 final Map<String, Object> annotationParams = new HashMap<String, Object>();
181 annotationParams.put(AnnotationUtil.PROPERTY_KEY_RESOURCE_BUNDLE_PARAMETER, null);
182 if (! JavaI18nUtil.mustBePropertyKey(expression, annotationParams)) return;
184 final int paramsCount = JavaI18nUtil.getPropertyValueParamsMaxCount(expression);
185 if (paramsCount == -1) return;
187 final PsiExpressionList expressions = (PsiExpressionList)expression.getParent();
188 final PsiMethodCallExpression methodCall = (PsiMethodCallExpression)expressions.getParent();
189 final PsiMethod method = methodCall.resolveMethod();
190 final PsiExpression[] args = expressions.getExpressions();
191 for (int i = 0; i < args.length; i++) {
192 if (args[i] == expression) {
193 if (i + paramsCount >= args.length
194 && method != null
195 && method.getParameterList().getParametersCount() == i+2
196 && method.getParameterList().getParameters()[i+1].isVarArgs()
197 && !hasArrayTypeAt(i+1, methodCall)) {
198 myProblems.add(myManager.createProblemDescriptor(methodCall,
199 CodeInsightBundle.message("property.has.more.parameters.than.passed", key, paramsCount, args.length-i-1),
200 new LocalQuickFix[0],
201 ProblemHighlightType.GENERIC_ERROR, onTheFly));
203 break;
209 private boolean hasArrayTypeAt(int i, PsiMethodCallExpression methodCall) {
210 return methodCall != null
211 && methodCall.getArgumentList().getExpressionTypes().length > i
212 && methodCall.getArgumentList().getExpressionTypes()[i] instanceof PsiArrayType;
215 private static boolean isComputablePropertyExpression(PsiExpression expression) {
216 while (expression != null && expression.getParent() instanceof PsiParenthesizedExpression) expression = (PsiExpression)expression.getParent();
217 return expression != null && expression.getParent() instanceof PsiExpression;
220 public List<ProblemDescriptor> getProblems() {
221 return myProblems;