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