2 * Copyright 2003-2008 Dave Griffith, Bas Leijdekkers
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.
18 import com
.intellij
.codeInsight
.daemon
.GroupNames
;
19 import com
.intellij
.codeInspection
.BaseJavaLocalInspectionTool
;
20 import com
.intellij
.codeInspection
.LocalInspectionToolSession
;
21 import com
.intellij
.codeInspection
.ProblemsHolder
;
22 import com
.intellij
.openapi
.application
.Application
;
23 import com
.intellij
.openapi
.application
.ApplicationManager
;
24 import com
.intellij
.openapi
.diagnostic
.Logger
;
25 import com
.intellij
.openapi
.util
.text
.StringUtil
;
26 import com
.intellij
.psi
.PsiElementVisitor
;
27 import com
.intellij
.ui
.DocumentAdapter
;
28 import com
.siyeh
.ig
.ui
.FormattedTextFieldMacFix
;
29 import org
.jetbrains
.annotations
.Nls
;
30 import org
.jetbrains
.annotations
.NonNls
;
31 import org
.jetbrains
.annotations
.NotNull
;
32 import org
.jetbrains
.annotations
.Nullable
;
35 import javax
.swing
.event
.DocumentEvent
;
36 import javax
.swing
.text
.Document
;
37 import java
.lang
.reflect
.Field
;
38 import java
.lang
.reflect
.Method
;
39 import java
.text
.NumberFormat
;
40 import java
.text
.ParseException
;
41 import java
.util
.HashMap
;
42 import java
.util
.List
;
45 public abstract class BaseInspection
extends BaseJavaLocalInspectionTool
{
47 private static final Logger LOG
= Logger
.getInstance("#com.siyeh.ig.BaseInspection");
49 private InspectionRunListener listener
= null;
50 @NonNls private static final String INSPECTION_GADGETS_COMPONENT_NAME
=
52 @NonNls private static final String INSPECTION
= "Inspection";
53 @NonNls private static final Map
<String
, String
> packageGroupDisplayNameMap
= new HashMap
<String
, String
>();
55 packageGroupDisplayNameMap
.put("abstraction", GroupNames
.ABSTRACTION_GROUP_NAME
);
56 packageGroupDisplayNameMap
.put("assignment", GroupNames
.ASSIGNMENT_GROUP_NAME
);
57 packageGroupDisplayNameMap
.put("bitwise", GroupNames
.BITWISE_GROUP_NAME
);
58 packageGroupDisplayNameMap
.put("bugs", GroupNames
.BUGS_GROUP_NAME
);
59 packageGroupDisplayNameMap
.put("classlayout", GroupNames
.CLASSLAYOUT_GROUP_NAME
);
60 packageGroupDisplayNameMap
.put("classmetrics", GroupNames
.CLASSMETRICS_GROUP_NAME
);
61 packageGroupDisplayNameMap
.put("cloneable", GroupNames
.CLONEABLE_GROUP_NAME
);
62 packageGroupDisplayNameMap
.put("controlflow", GroupNames
.CONTROL_FLOW_GROUP_NAME
);
63 packageGroupDisplayNameMap
.put("dataflow", GroupNames
.DATA_FLOW_ISSUES
);
64 packageGroupDisplayNameMap
.put("dependency", GroupNames
.DEPENDENCY_GROUP_NAME
);
65 packageGroupDisplayNameMap
.put("encapsulation", GroupNames
.ENCAPSULATION_GROUP_NAME
);
66 packageGroupDisplayNameMap
.put("errorhandling", GroupNames
.ERRORHANDLING_GROUP_NAME
);
67 packageGroupDisplayNameMap
.put("finalization", GroupNames
.FINALIZATION_GROUP_NAME
);
68 packageGroupDisplayNameMap
.put("imports", GroupNames
.IMPORTS_GROUP_NAME
);
69 packageGroupDisplayNameMap
.put("inheritance", GroupNames
.INHERITANCE_GROUP_NAME
);
70 packageGroupDisplayNameMap
.put("initialization", GroupNames
.INITIALIZATION_GROUP_NAME
);
71 packageGroupDisplayNameMap
.put("internationalization", GroupNames
.INTERNATIONALIZATION_GROUP_NAME
);
72 packageGroupDisplayNameMap
.put("j2me", GroupNames
.J2ME_GROUP_NAME
);
73 packageGroupDisplayNameMap
.put("javabeans", GroupNames
.JAVABEANS_GROUP_NAME
);
74 packageGroupDisplayNameMap
.put("jdk", GroupNames
.JDK_GROUP_NAME
);
75 packageGroupDisplayNameMap
.put("jdk15", GroupNames
.JDK15_SPECIFIC_GROUP_NAME
);
76 packageGroupDisplayNameMap
.put("junit", GroupNames
.JUNIT_GROUP_NAME
);
77 packageGroupDisplayNameMap
.put("logging", GroupNames
.LOGGING_GROUP_NAME
);
78 packageGroupDisplayNameMap
.put("maturity", GroupNames
.MATURITY_GROUP_NAME
);
79 packageGroupDisplayNameMap
.put("memory", GroupNames
.MEMORY_GROUP_NAME
);
80 packageGroupDisplayNameMap
.put("methodmetrics", GroupNames
.METHODMETRICS_GROUP_NAME
);
81 packageGroupDisplayNameMap
.put("modularization", GroupNames
.MODULARIZATION_GROUP_NAME
);
82 packageGroupDisplayNameMap
.put("naming", GroupNames
.NAMING_CONVENTIONS_GROUP_NAME
);
83 packageGroupDisplayNameMap
.put("numeric", GroupNames
.NUMERIC_GROUP_NAME
);
84 packageGroupDisplayNameMap
.put("packaging", GroupNames
.PACKAGING_GROUP_NAME
);
85 packageGroupDisplayNameMap
.put("performance", GroupNames
.PERFORMANCE_GROUP_NAME
);
86 packageGroupDisplayNameMap
.put("portability", GroupNames
.PORTABILITY_GROUP_NAME
);
87 packageGroupDisplayNameMap
.put("resources", GroupNames
.RESOURCE_GROUP_NAME
);
88 packageGroupDisplayNameMap
.put("security", GroupNames
.SECURITY_GROUP_NAME
);
89 packageGroupDisplayNameMap
.put("serialization", GroupNames
.SERIALIZATION_GROUP_NAME
);
90 packageGroupDisplayNameMap
.put("style", GroupNames
.STYLE_GROUP_NAME
);
91 packageGroupDisplayNameMap
.put("threading", GroupNames
.THREADING_GROUP_NAME
);
92 packageGroupDisplayNameMap
.put("visibility", GroupNames
.VISIBILITY_GROUP_NAME
);
95 private String m_shortName
= null;
96 private long timeStamp
= -1;
99 public final String
getShortName() {
100 if (m_shortName
== null) {
101 final Class
<?
extends BaseInspection
> aClass
= getClass();
102 final String name
= aClass
.getName();
103 assert name
.endsWith(INSPECTION
) :
104 "class name must end with 'Inspection' to correctly" +
105 " calculate the short name: " + name
;
106 m_shortName
= name
.substring(name
.lastIndexOf((int)'.') + 1,
107 name
.length() - INSPECTION
.length());
113 @Override @Nls @NotNull
114 public final String
getGroupDisplayName() {
115 final Class
<?
extends BaseInspection
> thisClass
= getClass();
116 final Package thisPackage
= thisClass
.getPackage();
117 assert thisPackage
!= null : "need package to determine group display name";
118 final String name
= thisPackage
.getName();
119 assert name
!= null :
120 "inspection has default package, group display name cannot be determined";
121 final int index
= name
.lastIndexOf('.');
122 final String key
= name
.substring(index
+ 1);
123 final String groupDisplayName
= packageGroupDisplayNameMap
.get(key
);
124 assert groupDisplayName
!= null : "No display name found for " + key
;
125 return groupDisplayName
;
129 protected abstract String
buildErrorString(Object
... infos
);
131 protected boolean buildQuickFixesOnlyForOnTheFlyErrors() {
136 protected InspectionGadgetsFix
buildFix(Object
... infos
) {
141 protected InspectionGadgetsFix
[] buildFixes(Object
... infos
) {
142 return InspectionGadgetsFix
.EMPTY_ARRAY
;
145 public boolean hasQuickFix() {
146 final Class
<?
extends BaseInspection
> aClass
= getClass();
147 final Method
[] methods
= aClass
.getDeclaredMethods();
148 for (final Method method
: methods
) {
149 @NonNls final String methodName
= method
.getName();
150 if ("buildFix".equals(methodName
)) {
157 public abstract BaseInspectionVisitor
buildVisitor();
160 public PsiElementVisitor
buildVisitor(@NotNull ProblemsHolder holder
,
161 boolean isOnTheFly
) {
162 final BaseInspectionVisitor visitor
= buildVisitor();
163 visitor
.setProblemsHolder(holder
);
164 visitor
.setOnTheFly(isOnTheFly
);
165 visitor
.setInspection(this);
170 protected JFormattedTextField
prepareNumberEditor(@NonNls String fieldName
) {
172 final NumberFormat formatter
= NumberFormat
.getIntegerInstance();
173 formatter
.setParseIntegerOnly(true);
174 final JFormattedTextField valueField
= new JFormattedTextField(formatter
);
175 final Field field
= getClass().getField(fieldName
);
176 valueField
.setValue(field
.get(this));
177 valueField
.setColumns(4);
178 FormattedTextFieldMacFix
.apply(valueField
);
179 final Document document
= valueField
.getDocument();
180 document
.addDocumentListener(new DocumentAdapter() {
182 public void textChanged(DocumentEvent evt
) {
184 valueField
.commitEdit();
185 field
.set(BaseInspection
.this, ((Number
) valueField
.getValue()).intValue());
186 } catch (IllegalAccessException e
) {
188 } catch (ParseException e
) {
189 // No luck this time. Will update the field when correct value is entered.
194 } catch (NoSuchFieldException e
) {
196 } catch (IllegalAccessException e
) {
202 protected static void parseString(String string
, List
<String
>... outs
){
203 final List
<String
> strings
= StringUtil
.split(string
, ",");
204 for (List
<String
> out
: outs
) {
207 for (int i
= 0, iMax
= strings
.size(); i
< iMax
; i
+= outs
.length
) {
208 for (int j
= 0; j
< outs
.length
; j
++) {
209 final List
<String
> out
= outs
[j
];
213 out
.add(strings
.get(i
+ j
));
219 protected static String
formatString(List
<String
>... strings
){
220 final StringBuilder buffer
= new StringBuilder();
221 final int size
= strings
[0].size();
223 formatString(strings
, 0, buffer
);
224 for (int i
= 1; i
< size
; i
++) {
226 formatString(strings
, i
, buffer
);
229 return buffer
.toString();
232 private static void formatString(List
<String
>[] strings
, int index
,
234 out
.append(strings
[0].get(index
));
235 for (int i
= 1; i
< strings
.length
; i
++) {
237 out
.append(strings
[i
].get(index
));
241 private void initializeTelemetryIfNecessary() {
242 if (InspectionGadgetsPlugin
.TELEMETRY_ENABLED
&& listener
== null) {
243 final Application application
= ApplicationManager
.getApplication();
244 final InspectionGadgetsPlugin plugin
= (InspectionGadgetsPlugin
)
245 application
.getComponent(INSPECTION_GADGETS_COMPONENT_NAME
);
246 listener
= plugin
.getTelemetry();
251 public void inspectionStarted(LocalInspectionToolSession session
) {
252 super.inspectionStarted(session
);
253 if (InspectionGadgetsPlugin
.TELEMETRY_ENABLED
) {
255 System
.out
.println("start reported without corresponding finish");
257 initializeTelemetryIfNecessary();
258 timeStamp
= System
.currentTimeMillis();
263 public void inspectionFinished(LocalInspectionToolSession session
) {
264 super.inspectionFinished(session
);
265 if (InspectionGadgetsPlugin
.TELEMETRY_ENABLED
) {
267 System
.out
.println("finish reported without corresponding start");
270 final long end
= System
.currentTimeMillis();
271 final String displayName
= getDisplayName();
272 listener
.reportRun(displayName
, end
- timeStamp
);