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
;
40 * @author Konstantin Bulenkov
42 public class InvalidPropertyKeyInspection
extends BaseJavaLocalInspectionTool
{
45 public String
getGroupDisplayName() {
46 return GroupNames
.INTERNATIONALIZATION_GROUP_NAME
;
50 public String
getDisplayName() {
51 return CodeInsightBundle
.message("inspection.unresolved.property.key.reference.name");
55 public String
getShortName() {
56 return "UnresolvedPropertyKey";
60 public HighlightDisplayLevel
getDefaultLevel() {
61 return HighlightDisplayLevel
.ERROR
;
64 public boolean isEnabledByDefault() {
70 public ProblemDescriptor
[] checkMethod(@NotNull PsiMethod method
, @NotNull InspectionManager manager
, boolean isOnTheFly
) {
71 return checkElement(method
, manager
, isOnTheFly
);
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()]);
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
);
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()]);
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) {
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
) {
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
,
157 new JavaCreatePropertyFix(expression
, key
, propertiesFiles
),
158 ProblemHighlightType
.LIKE_UNKNOWN_SYMBOL
, onTheFly
);
159 myProblems
.add(problem
);
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
,
173 ProblemHighlightType
.LIKE_UNKNOWN_SYMBOL
, onTheFly
);
174 myProblems
.add(problem
);
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
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
));
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() {