1
2
3
4 package net.sourceforge.pmd;
5
6 import java.io.File;
7 import java.util.ArrayList;
8 import java.util.Arrays;
9 import java.util.Collection;
10 import java.util.HashSet;
11 import java.util.Iterator;
12 import java.util.List;
13 import java.util.logging.Level;
14 import java.util.logging.Logger;
15
16 import net.sourceforge.pmd.benchmark.Benchmark;
17 import net.sourceforge.pmd.benchmark.Benchmarker;
18 import net.sourceforge.pmd.lang.Language;
19 import net.sourceforge.pmd.lang.LanguageVersion;
20 import net.sourceforge.pmd.lang.ast.Node;
21 import net.sourceforge.pmd.lang.rule.RuleReference;
22 import net.sourceforge.pmd.util.CollectionUtil;
23 import net.sourceforge.pmd.util.StringUtil;
24 import net.sourceforge.pmd.util.filter.Filter;
25 import net.sourceforge.pmd.util.filter.Filters;
26
27
28
29
30
31
32
33
34 public class RuleSet {
35
36 private static final Logger LOG = Logger.getLogger(RuleSet.class.getName());
37
38 private List<Rule> rules = new ArrayList<Rule>();
39 private String fileName;
40 private String name = "";
41 private String description = "";
42
43
44 private List<String> excludePatterns = new ArrayList<String>(0);
45 private List<String> includePatterns = new ArrayList<String>(0);
46
47 private Filter<File> filter;
48
49
50
51
52
53
54
55
56 public static RuleSet createFor(String name, Rule... theRules) {
57
58 RuleSet rs = new RuleSet();
59 rs.setName(name);
60 for (Rule rule : theRules) {
61 rs.addRule(rule);
62 }
63 return rs;
64 }
65
66
67
68
69
70
71 public int size() {
72 return rules.size();
73 }
74
75
76
77
78
79
80
81 public void addRule(Rule rule) {
82 if (rule == null) {
83 throw new IllegalArgumentException("Missing rule");
84 }
85 rules.add(rule);
86 }
87
88
89
90
91
92
93
94
95
96
97 public boolean addRuleReplaceIfExists(Rule rule) {
98 if (rule == null) {
99 throw new IllegalArgumentException("Missing rule");
100 }
101
102 boolean replaced = false;
103 for (Iterator<Rule> it = rules.iterator(); it.hasNext();) {
104 Rule r = it.next();
105 if (r.getName().equals(rule.getName()) && r.getLanguage() == rule.getLanguage()) {
106 it.remove();
107 replaced = true;
108 }
109 }
110 addRule(rule);
111 return replaced;
112 }
113
114
115
116
117
118
119
120
121
122
123 public boolean addRuleIfNotExists(Rule rule) {
124 if (rule == null) {
125 throw new IllegalArgumentException("Missing rule");
126 }
127
128 boolean exists = false;
129 for (Rule r : rules) {
130 if (r.getName().equals(rule.getName()) && r.getLanguage() == rule.getLanguage()) {
131 exists = true;
132 break;
133 }
134 }
135 if (!exists) {
136 addRule(rule);
137 }
138 return !exists;
139 }
140
141
142
143
144
145
146
147 public void addRuleByReference(String ruleSetFileName, Rule rule) {
148 if (StringUtil.isEmpty(ruleSetFileName)) {
149 throw new RuntimeException("Adding a rule by reference is not allowed with an empty rule set file name.");
150 }
151 if (rule == null) {
152 throw new IllegalArgumentException("Cannot add a null rule reference to a RuleSet");
153 }
154 RuleReference ruleReference;
155 if (rule instanceof RuleReference) {
156 ruleReference = (RuleReference) rule;
157 } else {
158 RuleSetReference ruleSetReference = new RuleSetReference();
159 ruleSetReference.setRuleSetFileName(ruleSetFileName);
160 ruleReference = new RuleReference();
161 ruleReference.setRule(rule);
162 ruleReference.setRuleSetReference(ruleSetReference);
163 }
164 rules.add(ruleReference);
165 }
166
167
168
169
170
171
172 public Collection<Rule> getRules() {
173 return rules;
174 }
175
176
177
178
179
180
181
182
183 public boolean usesDFA(Language language) {
184 for (Rule r : rules) {
185 if (r.getLanguage().equals(language)) {
186 if (r.usesDFA()) {
187 return true;
188 }
189 }
190 }
191 return false;
192 }
193
194
195
196
197
198
199
200
201
202
203 public Rule getRuleByName(String ruleName) {
204
205 for (Rule r : rules) {
206 if (r.getName().equals(ruleName)) {
207 return r;
208 }
209 }
210 return null;
211 }
212
213
214
215
216
217
218 public void addRuleSet(RuleSet ruleSet) {
219 rules.addAll(rules.size(), ruleSet.getRules());
220 }
221
222
223
224
225
226
227
228
229
230
231
232 public void addRuleSetByReference(RuleSet ruleSet, boolean allRules) {
233 addRuleSetByReference(ruleSet, allRules, (String[]) null);
234 }
235
236
237
238
239
240
241
242
243
244
245
246
247 public void addRuleSetByReference(RuleSet ruleSet, boolean allRules, String... excludes) {
248 if (StringUtil.isEmpty(ruleSet.getFileName())) {
249 throw new RuntimeException("Adding a rule by reference is not allowed with an empty rule set file name.");
250 }
251 RuleSetReference ruleSetReference = new RuleSetReference(ruleSet.getFileName());
252 ruleSetReference.setAllRules(allRules);
253 if (excludes != null) {
254 ruleSetReference.setExcludes(new HashSet<String>(Arrays.asList(excludes)));
255 }
256 for (Rule rule : ruleSet.getRules()) {
257 RuleReference ruleReference = new RuleReference(rule, ruleSetReference);
258 rules.add(ruleReference);
259 }
260 }
261
262
263
264
265
266
267
268
269
270
271
272
273 public boolean applies(File file) {
274
275 if (filter == null) {
276 Filter<String> regexFilter = Filters.buildRegexFilterIncludeOverExclude(includePatterns, excludePatterns);
277 filter = Filters.toNormalizedFileFilter(regexFilter);
278 }
279
280 return file != null ? filter.filter(file) : true;
281 }
282
283
284
285
286
287
288
289 public void start(RuleContext ctx) {
290 for (Rule rule : rules) {
291 rule.start(ctx);
292 }
293 }
294
295
296
297
298
299
300
301
302 public void apply(List<? extends Node> acuList, RuleContext ctx) {
303 long start = System.nanoTime();
304 for (Rule rule : rules) {
305 try {
306 if (!rule.usesRuleChain() && applies(rule, ctx.getLanguageVersion())) {
307 rule.apply(acuList, ctx);
308 long end = System.nanoTime();
309 Benchmarker.mark(Benchmark.Rule, rule.getName(), end - start, 1);
310 start = end;
311 }
312 } catch (RuntimeException e) {
313 if (ctx.isIgnoreExceptions()) {
314 if (LOG.isLoggable(Level.WARNING)) {
315 LOG.log(Level.WARNING, "Exception applying rule " + rule.getName()
316 + " on file " + ctx.getSourceCodeFilename() + ", continuing with next rule",
317 e);
318 }
319 } else {
320 throw e;
321 }
322 }
323 }
324 }
325
326
327
328
329
330
331
332
333
334
335
336
337 public static boolean applies(Rule rule, LanguageVersion languageVersion) {
338 final LanguageVersion min = rule.getMinimumLanguageVersion();
339 final LanguageVersion max = rule.getMaximumLanguageVersion();
340 return rule.getLanguage().equals(languageVersion.getLanguage())
341 && (min == null || min.compareTo(languageVersion) <= 0)
342 && (max == null || max.compareTo(languageVersion) >= 0);
343 }
344
345
346
347
348
349
350
351 public void end(RuleContext ctx) {
352 for (Rule rule : rules) {
353 rule.end(ctx);
354 }
355 }
356
357
358
359
360
361
362
363
364
365 @Override
366 public boolean equals(Object o) {
367 if (!(o instanceof RuleSet)) {
368 return false;
369 }
370
371 if (this == o) {
372 return true;
373 }
374
375 RuleSet ruleSet = (RuleSet) o;
376 return getName().equals(ruleSet.getName()) && getRules().equals(ruleSet.getRules());
377 }
378
379 @Override
380 public int hashCode() {
381 return getName().hashCode() + 13 * getRules().hashCode();
382 }
383
384 public String getFileName() {
385 return fileName;
386 }
387
388 public void setFileName(String fileName) {
389 this.fileName = fileName;
390 }
391
392 public String getName() {
393 return name;
394 }
395
396 public void setName(String name) {
397 this.name = name;
398 }
399
400 public String getDescription() {
401 return description;
402 }
403
404 public void setDescription(String description) {
405 this.description = description;
406 }
407
408 public List<String> getExcludePatterns() {
409 return excludePatterns;
410 }
411
412
413
414
415
416
417 public void addExcludePattern(String aPattern) {
418 if (excludePatterns.contains(aPattern)) {
419 return;
420 }
421
422 excludePatterns.add(aPattern);
423 patternsChanged();
424 }
425
426
427
428
429
430
431 public void addExcludePatterns(Collection<String> someExcludePatterns) {
432 int added = CollectionUtil.addWithoutDuplicates(someExcludePatterns, excludePatterns);
433 if (added > 0) {
434 patternsChanged();
435 }
436 }
437
438
439
440
441
442
443 public void setExcludePatterns(Collection<String> theExcludePatterns) {
444 if (excludePatterns.equals(theExcludePatterns)) {
445 return;
446 }
447
448 excludePatterns.clear();
449 CollectionUtil.addWithoutDuplicates(theExcludePatterns, excludePatterns);
450 patternsChanged();
451 }
452
453 public List<String> getIncludePatterns() {
454 return includePatterns;
455 }
456
457
458
459
460
461
462 public void addIncludePattern(String aPattern) {
463 if (includePatterns.contains(aPattern)) {
464 return;
465 }
466
467 includePatterns.add(aPattern);
468 patternsChanged();
469 }
470
471
472
473
474
475
476 public void addIncludePatterns(Collection<String> someIncludePatterns) {
477 int added = CollectionUtil.addWithoutDuplicates(someIncludePatterns, includePatterns);
478 if (added > 0) {
479 patternsChanged();
480 }
481 }
482
483
484
485
486
487
488 public void setIncludePatterns(Collection<String> theIncludePatterns) {
489 if (includePatterns.equals(theIncludePatterns)) {
490 return;
491 }
492
493 includePatterns.clear();
494 CollectionUtil.addWithoutDuplicates(theIncludePatterns, includePatterns);
495 patternsChanged();
496 }
497
498 private void patternsChanged() {
499 filter = null;
500
501 }
502
503
504
505
506
507
508
509
510 public boolean usesTypeResolution(Language language) {
511 for (Rule r : rules) {
512 if (r.getLanguage().equals(language)) {
513 if (r.usesTypeResolution()) {
514 return true;
515 }
516 }
517 }
518 return false;
519 }
520
521
522
523
524
525
526 public void removeDysfunctionalRules(Collection<Rule> collector) {
527 Iterator<Rule> iter = rules.iterator();
528
529 while (iter.hasNext()) {
530 Rule rule = iter.next();
531 if (rule.dysfunctionReason() != null) {
532 iter.remove();
533 collector.add(rule);
534 }
535 }
536 }
537 }