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.
17 package com
.intellij
.codeInspection
.suspiciousNameCombination
;
19 import com
.intellij
.CommonBundle
;
20 import com
.intellij
.codeInsight
.daemon
.GroupNames
;
21 import com
.intellij
.codeInsight
.daemon
.JavaErrorMessages
;
22 import com
.intellij
.codeInspection
.InspectionsBundle
;
23 import com
.intellij
.codeInspection
.ProblemsHolder
;
24 import com
.intellij
.codeInspection
.ex
.BaseLocalInspectionTool
;
25 import com
.intellij
.openapi
.ui
.Messages
;
26 import com
.intellij
.openapi
.util
.InvalidDataException
;
27 import com
.intellij
.openapi
.util
.WriteExternalException
;
28 import com
.intellij
.openapi
.util
.text
.StringUtil
;
29 import com
.intellij
.psi
.*;
30 import com
.intellij
.psi
.codeStyle
.NameUtil
;
31 import com
.intellij
.psi
.util
.PsiTreeUtil
;
32 import com
.intellij
.ui
.AddDeleteListPanel
;
33 import org
.jdom
.Element
;
34 import org
.jetbrains
.annotations
.NonNls
;
35 import org
.jetbrains
.annotations
.NotNull
;
36 import org
.jetbrains
.annotations
.Nullable
;
39 import javax
.swing
.event
.ListDataEvent
;
40 import javax
.swing
.event
.ListDataListener
;
41 import java
.awt
.event
.ActionEvent
;
42 import java
.awt
.event
.ActionListener
;
43 import java
.awt
.event
.MouseAdapter
;
44 import java
.awt
.event
.MouseEvent
;
45 import java
.util
.ArrayList
;
46 import java
.util
.HashMap
;
47 import java
.util
.List
;
53 public class SuspiciousNameCombinationInspection
extends BaseLocalInspectionTool
{
54 private final List
<String
> myNameGroups
= new ArrayList
<String
>();
55 private final Map
<String
, String
> myWordToGroupMap
= new HashMap
<String
, String
>();
56 @NonNls private static final String ELEMENT_GROUPS
= "group";
57 @NonNls private static final String ATTRIBUTE_NAMES
= "names";
59 public SuspiciousNameCombinationInspection() {
60 addNameGroup("x,width,left,right");
61 addNameGroup("y,height,top,bottom");
64 private void clearNameGroups() {
66 myWordToGroupMap
.clear();
69 private void addNameGroup(@NonNls final String group
) {
70 myNameGroups
.add(group
);
71 List
<String
> words
= StringUtil
.split(group
, ",");
72 for(String word
: words
) {
73 myWordToGroupMap
.put(word
.trim().toLowerCase(), group
);
78 public String
getGroupDisplayName() {
79 return GroupNames
.BUGS_GROUP_NAME
;
83 public String
getDisplayName() {
84 return InspectionsBundle
.message("suspicious.name.combination.display.name");
89 public String
getShortName() {
90 return "SuspiciousNameCombination";
94 public PsiElementVisitor
buildVisitor(@NotNull ProblemsHolder holder
, boolean isOnTheFly
) {
95 return new MyVisitor(holder
);
99 public JComponent
createOptionsPanel() {
100 return new MyOptionsPanel();
103 @Override public void readSettings(Element node
) throws InvalidDataException
{
105 for(Object o
: node
.getChildren(ELEMENT_GROUPS
)) {
106 Element e
= (Element
) o
;
107 addNameGroup(e
.getAttributeValue(ATTRIBUTE_NAMES
));
111 @Override public void writeSettings(Element node
) throws WriteExternalException
{
112 for(String group
: myNameGroups
) {
113 Element e
= new Element(ELEMENT_GROUPS
);
115 e
.setAttribute(ATTRIBUTE_NAMES
, group
);
119 private class MyVisitor
extends JavaElementVisitor
{
120 private final ProblemsHolder myProblemsHolder
;
122 public MyVisitor(final ProblemsHolder problemsHolder
) {
123 myProblemsHolder
= problemsHolder
;
126 @Override public void visitReferenceExpression(PsiReferenceExpression expression
) {
129 @Override public void visitVariable(PsiVariable variable
) {
130 if (variable
.hasInitializer()) {
131 PsiExpression expr
= variable
.getInitializer();
132 if (expr
instanceof PsiReferenceExpression
) {
133 PsiReferenceExpression refExpr
= (PsiReferenceExpression
) expr
;
134 checkCombination(variable
, variable
.getName(), refExpr
.getReferenceName(), "suspicious.name.assignment");
139 @Override public void visitAssignmentExpression(PsiAssignmentExpression expression
) {
140 PsiExpression lhs
= expression
.getLExpression();
141 PsiExpression rhs
= expression
.getRExpression();
142 if (lhs
instanceof PsiReferenceExpression
&& rhs
instanceof PsiReferenceExpression
) {
143 PsiReferenceExpression lhsExpr
= (PsiReferenceExpression
) lhs
;
144 PsiReferenceExpression rhsExpr
= (PsiReferenceExpression
) rhs
;
145 checkCombination(lhsExpr
, lhsExpr
.getReferenceName(), rhsExpr
.getReferenceName(), "suspicious.name.assignment");
149 @Override public void visitCallExpression(PsiCallExpression expression
) {
150 final PsiMethod psiMethod
= expression
.resolveMethod();
151 final PsiExpressionList argList
= expression
.getArgumentList();
152 if (psiMethod
!= null && argList
!= null) {
153 final PsiExpression
[] args
= argList
.getExpressions();
154 final PsiParameter
[] parameters
= psiMethod
.getParameterList().getParameters();
155 for(int i
=0; i
<parameters
.length
; i
++) {
156 if (i
>= args
.length
) break;
157 if (args
[i
] instanceof PsiReferenceExpression
) {
158 // PsiParameter.getName() can be expensive for compiled class files, so check reference name before
159 // fetching parameter name
160 final String refName
= ((PsiReferenceExpression
)args
[i
]).getReferenceName();
161 if (findNameGroup(refName
) != null) {
162 checkCombination(args
[i
], parameters
[i
].getName(), refName
, "suspicious.name.parameter");
170 public void visitReturnStatement(final PsiReturnStatement statement
) {
171 final PsiExpression returnValue
= statement
.getReturnValue();
172 PsiMethod containingMethod
= PsiTreeUtil
.getParentOfType(returnValue
, PsiMethod
.class);
173 if (returnValue
instanceof PsiReferenceExpression
&& containingMethod
!= null) {
174 final String refName
= ((PsiReferenceExpression
)returnValue
).getReferenceName();
175 checkCombination(returnValue
, containingMethod
.getName(), refName
, "suspicious.name.return");
179 private void checkCombination(final PsiElement location
,
180 @Nullable final String name
,
181 @Nullable final String referenceName
,
183 String nameGroup1
= findNameGroup(name
);
184 String nameGroup2
= findNameGroup(referenceName
);
185 if (nameGroup1
!= null && nameGroup2
!= null && !nameGroup1
.equals(nameGroup2
)) {
186 myProblemsHolder
.registerProblem(location
, JavaErrorMessages
.message(key
, referenceName
, name
));
190 @Nullable private String
findNameGroup(@Nullable final String name
) {
194 String
[] words
= NameUtil
.splitNameIntoWords(name
);
195 String result
= null;
196 for(String word
: words
) {
197 String group
= myWordToGroupMap
.get(word
.toLowerCase());
199 if (result
== null) {
202 else if (!result
.equals(group
)) {
212 private class MyOptionsPanel
extends AddDeleteListPanel
{
213 private JButton myEditButton
;
215 public MyOptionsPanel() {
216 super(InspectionsBundle
.message("suspicious.name.combination.options.title"), myNameGroups
);
217 myEditButton
.addActionListener(new ActionListener() {
218 public void actionPerformed(ActionEvent e
) {
222 myList
.addMouseListener(new MouseAdapter() {
223 public void mouseClicked(MouseEvent e
) {
224 if (e
.getClickCount() == 2 && e
.getButton() == MouseEvent
.BUTTON1
) {
229 myListModel
.addListDataListener(new ListDataListener() {
230 public void intervalAdded(ListDataEvent e
) {
234 public void intervalRemoved(ListDataEvent e
) {
238 public void contentsChanged(ListDataEvent e
) {
244 @Override protected JButton
[] createButtons() {
245 myEditButton
= new JButton(CommonBundle
.message("button.edit"));
246 return new JButton
[] { myAddButton
, myEditButton
, myDeleteButton
};
249 protected Object
findItemToAdd() {
250 return Messages
.showInputDialog(this,
251 InspectionsBundle
.message("suspicious.name.combination.options.prompt"),
252 InspectionsBundle
.message("suspicious.name.combination.add.titile"),
253 Messages
.getQuestionIcon(), "", null);
256 private void editSelectedItem() {
257 int index
= myList
.getSelectedIndex();
259 String inputValue
= (String
) myListModel
.get(index
);
260 String newValue
= Messages
.showInputDialog(this,
261 InspectionsBundle
.message("suspicious.name.combination.options.prompt"),
262 InspectionsBundle
.message("suspicious.name.combination.edit.title"),
263 Messages
.getQuestionIcon(),
265 if (newValue
!= null) {
266 myListModel
.set(index
, newValue
);
271 private void saveChanges() {
273 for(int i
=0; i
<myListModel
.getSize(); i
++) {
274 addNameGroup((String
) myListModel
.getElementAt(i
));