1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package com.puppycrawl.tools.checkstyle;
20
21 import com.google.common.collect.Lists;
22 import com.google.common.collect.Sets;
23 import com.puppycrawl.tools.checkstyle.api.AuditEvent;
24 import com.puppycrawl.tools.checkstyle.api.AuditListener;
25 import com.puppycrawl.tools.checkstyle.api.AutomaticBean;
26 import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
27 import com.puppycrawl.tools.checkstyle.api.Configuration;
28 import com.puppycrawl.tools.checkstyle.api.Context;
29 import com.puppycrawl.tools.checkstyle.api.FastStack;
30 import com.puppycrawl.tools.checkstyle.api.FileSetCheck;
31 import com.puppycrawl.tools.checkstyle.api.FileText;
32 import com.puppycrawl.tools.checkstyle.api.Filter;
33 import com.puppycrawl.tools.checkstyle.api.FilterSet;
34 import com.puppycrawl.tools.checkstyle.api.LocalizedMessage;
35 import com.puppycrawl.tools.checkstyle.api.MessageDispatcher;
36 import com.puppycrawl.tools.checkstyle.api.SeverityLevel;
37 import com.puppycrawl.tools.checkstyle.api.SeverityLevelCounter;
38 import com.puppycrawl.tools.checkstyle.api.Utils;
39
40 import java.io.File;
41 import java.io.FileNotFoundException;
42 import java.io.IOException;
43 import java.io.UnsupportedEncodingException;
44 import java.nio.charset.Charset;
45 import java.util.List;
46 import java.util.Locale;
47 import java.util.Set;
48 import java.util.SortedSet;
49 import java.util.StringTokenizer;
50
51 import static com.puppycrawl.tools.checkstyle.Utils.fileExtensionMatches;
52
53
54
55
56
57
58
59 public class Checker extends AutomaticBean implements MessageDispatcher
60 {
61
62 private final SeverityLevelCounter counter = new SeverityLevelCounter(
63 SeverityLevel.ERROR);
64
65
66 private final List<AuditListener> listeners = Lists.newArrayList();
67
68
69 private final List<FileSetCheck> fileSetChecks = Lists.newArrayList();
70
71
72 private ClassLoader loader = Thread.currentThread()
73 .getContextClassLoader();
74
75
76 private String basedir;
77
78
79 private String localeCountry = Locale.getDefault().getCountry();
80
81 private String localeLanguage = Locale.getDefault().getLanguage();
82
83
84 private ModuleFactory moduleFactory;
85
86
87 private ClassLoader moduleClassLoader;
88
89
90 private Context childContext;
91
92
93 private final FilterSet filters = new FilterSet();
94
95
96 private String[] fileExtensions = {};
97
98
99
100
101
102
103
104
105
106
107
108 private SeverityLevel severityLevel = SeverityLevel.ERROR;
109
110
111 private String charset = System.getProperty("file.encoding", "UTF-8");
112
113
114
115
116
117
118
119 public Checker() throws CheckstyleException
120 {
121 addListener(counter);
122 }
123
124 @Override
125 public void finishLocalSetup() throws CheckstyleException
126 {
127 final Locale locale = new Locale(localeLanguage, localeCountry);
128 LocalizedMessage.setLocale(locale);
129
130 if (moduleFactory == null) {
131
132 if (moduleClassLoader == null) {
133 throw new CheckstyleException(
134 "if no custom moduleFactory is set, "
135 + "moduleClassLoader must be specified");
136 }
137
138 final Set<String> packageNames = PackageNamesLoader
139 .getPackageNames(moduleClassLoader);
140 moduleFactory = new PackageObjectFactory(packageNames,
141 moduleClassLoader);
142 }
143
144 final DefaultContext context = new DefaultContext();
145 context.add("charset", charset);
146 context.add("classLoader", loader);
147 context.add("moduleFactory", moduleFactory);
148 context.add("severity", severityLevel.getName());
149 context.add("basedir", basedir);
150 childContext = context;
151 }
152
153 @Override
154 protected void setupChild(Configuration childConf)
155 throws CheckstyleException
156 {
157 final String name = childConf.getName();
158 try {
159 final Object child = moduleFactory.createModule(name);
160 if (child instanceof AutomaticBean) {
161 final AutomaticBean bean = (AutomaticBean) child;
162 bean.contextualize(childContext);
163 bean.configure(childConf);
164 }
165 if (child instanceof FileSetCheck) {
166 final FileSetCheck fsc = (FileSetCheck) child;
167 addFileSetCheck(fsc);
168 }
169 else if (child instanceof Filter) {
170 final Filter filter = (Filter) child;
171 addFilter(filter);
172 }
173 else if (child instanceof AuditListener) {
174 final AuditListener listener = (AuditListener) child;
175 addListener(listener);
176 }
177 else {
178 throw new CheckstyleException(name
179 + " is not allowed as a child in Checker");
180 }
181 }
182 catch (final Exception ex) {
183
184 throw new CheckstyleException("cannot initialize module " + name
185 + " - " + ex.getMessage(), ex);
186 }
187 }
188
189
190
191
192
193
194 public void addFileSetCheck(FileSetCheck fileSetCheck)
195 {
196 fileSetCheck.setMessageDispatcher(this);
197 fileSetChecks.add(fileSetCheck);
198 }
199
200
201
202
203
204 public void addFilter(Filter filter)
205 {
206 filters.addFilter(filter);
207 }
208
209
210
211
212
213 public void removeFilter(Filter filter)
214 {
215 filters.removeFilter(filter);
216 }
217
218
219 public void destroy()
220 {
221 listeners.clear();
222 filters.clear();
223 }
224
225
226
227
228
229 public final void addListener(AuditListener listener)
230 {
231 listeners.add(listener);
232 }
233
234
235
236
237
238 public void removeListener(AuditListener listener)
239 {
240 listeners.remove(listener);
241 }
242
243
244
245
246
247
248
249
250
251 public int process(List<File> files)
252 {
253
254 fireAuditStarted();
255 for (final FileSetCheck fsc : fileSetChecks) {
256 fsc.beginProcessing(charset);
257 }
258
259
260 for (final File f : files) {
261 if (!fileExtensionMatches(f, fileExtensions)) {
262 continue;
263 }
264 final String fileName = f.getAbsolutePath();
265 fireFileStarted(fileName);
266 final SortedSet<LocalizedMessage> fileMessages = Sets.newTreeSet();
267 try {
268 final FileText theText = new FileText(f.getAbsoluteFile(),
269 charset);
270 for (final FileSetCheck fsc : fileSetChecks) {
271 fileMessages.addAll(fsc.process(f, theText));
272 }
273 }
274 catch (final FileNotFoundException fnfe) {
275 Utils.getExceptionLogger().debug(
276 "FileNotFoundException occured.", fnfe);
277 fileMessages.add(new LocalizedMessage(0,
278 Defn.CHECKSTYLE_BUNDLE, "general.fileNotFound", null,
279 null, this.getClass(), null));
280 }
281 catch (final IOException ioe) {
282 Utils.getExceptionLogger().debug("IOException occured.", ioe);
283 fileMessages.add(new LocalizedMessage(0,
284 Defn.CHECKSTYLE_BUNDLE, "general.exception",
285 new String[] {ioe.getMessage()}, null, this.getClass(),
286 null));
287 }
288 fireErrors(fileName, fileMessages);
289 fireFileFinished(fileName);
290 }
291
292
293 for (final FileSetCheck fsc : fileSetChecks) {
294
295 fsc.finishProcessing();
296 fsc.destroy();
297 }
298
299 final int errorCount = counter.getCount();
300 fireAuditFinished();
301 return errorCount;
302 }
303
304
305
306
307
308
309 private String getStrippedFileName(final String fileName)
310 {
311 return Utils.getStrippedFileName(basedir, fileName);
312 }
313
314
315 public void setBasedir(String basedir)
316 {
317
318
319
320 this.basedir = normalize(basedir);
321 }
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341 public String normalize(String normalizingPath)
342 {
343
344 if (normalizingPath == null) {
345 return normalizingPath;
346 }
347
348 final String osName = System.getProperty("os.name").toLowerCase(
349 Locale.US);
350 final boolean onNetWare = osName.indexOf("netware") > -1;
351
352 String path = normalizingPath.replace('/', File.separatorChar).replace('\\',
353 File.separatorChar);
354
355
356 final int colon = path.indexOf(":");
357
358 if (!onNetWare) {
359 if (!path.startsWith(File.separator)
360 && !(path.length() >= 2
361 && Character.isLetter(path.charAt(0)) && colon == 1))
362 {
363 final String msg = path + " is not an absolute path";
364 throw new IllegalArgumentException(msg);
365 }
366 }
367 else {
368 if (!path.startsWith(File.separator) && colon == -1) {
369 final String msg = path + " is not an absolute path";
370 throw new IllegalArgumentException(msg);
371 }
372 }
373
374 boolean dosWithDrive = false;
375 String root = null;
376
377 if (!onNetWare && path.length() >= 2
378 && Character.isLetter(path.charAt(0)) && path.charAt(1) == ':'
379 || onNetWare && colon > -1)
380 {
381
382 dosWithDrive = true;
383
384 final char[] ca = path.replace('/', '\\').toCharArray();
385 final StringBuilder sbRoot = new StringBuilder();
386 for (int i = 0; i < colon; i++) {
387 sbRoot.append(Character.toUpperCase(ca[i]));
388 }
389 sbRoot.append(':');
390 if (colon + 1 < path.length()) {
391 sbRoot.append(File.separatorChar);
392 }
393 root = sbRoot.toString();
394
395
396 final StringBuilder sbPath = new StringBuilder();
397 for (int i = colon + 1; i < ca.length; i++) {
398 if (ca[i] != '\\' || ca[i] == '\\' && ca[i - 1] != '\\') {
399 sbPath.append(ca[i]);
400 }
401 }
402 path = sbPath.toString().replace('\\', File.separatorChar);
403
404 }
405 else {
406 if (path.length() == 1) {
407 root = File.separator;
408 path = "";
409 }
410 else if (path.charAt(1) == File.separatorChar) {
411
412 root = File.separator + File.separator;
413 path = path.substring(2);
414 }
415 else {
416 root = File.separator;
417 path = path.substring(1);
418 }
419 }
420
421 final FastStack<String> s = FastStack.newInstance();
422 s.push(root);
423 final StringTokenizer tok = new StringTokenizer(path, File.separator);
424 while (tok.hasMoreTokens()) {
425 final String thisToken = tok.nextToken();
426 if (".".equals(thisToken)) {
427 continue;
428 }
429 else if ("..".equals(thisToken)) {
430 if (s.size() < 2) {
431 throw new IllegalArgumentException("Cannot resolve path "
432 + path);
433 }
434 s.pop();
435 }
436 else {
437 s.push(thisToken);
438 }
439 }
440
441 final StringBuilder sb = new StringBuilder();
442 for (int i = 0; i < s.size(); i++) {
443 if (i > 1) {
444
445
446 sb.append(File.separatorChar);
447 }
448 sb.append(s.peek(i));
449 }
450
451 path = sb.toString();
452 if (dosWithDrive) {
453 path = path.replace('/', '\\');
454 }
455 return path;
456 }
457
458
459 public final String getBasedir()
460 {
461 return basedir;
462 }
463
464
465 protected void fireAuditStarted()
466 {
467 final AuditEvent evt = new AuditEvent(this);
468 for (final AuditListener listener : listeners) {
469 listener.auditStarted(evt);
470 }
471 }
472
473
474 protected void fireAuditFinished()
475 {
476 final AuditEvent evt = new AuditEvent(this);
477 for (final AuditListener listener : listeners) {
478 listener.auditFinished(evt);
479 }
480 }
481
482
483
484
485
486
487
488 @Override
489 public void fireFileStarted(String fileName)
490 {
491 final String stripped = getStrippedFileName(fileName);
492 final AuditEvent evt = new AuditEvent(this, stripped);
493 for (final AuditListener listener : listeners) {
494 listener.fileStarted(evt);
495 }
496 }
497
498
499
500
501
502
503
504 @Override
505 public void fireFileFinished(String fileName)
506 {
507 final String stripped = getStrippedFileName(fileName);
508 final AuditEvent evt = new AuditEvent(this, stripped);
509 for (final AuditListener listener : listeners) {
510 listener.fileFinished(evt);
511 }
512 }
513
514
515
516
517
518
519
520 @Override
521 public void fireErrors(String fileName,
522 SortedSet<LocalizedMessage> errors)
523 {
524 final String stripped = getStrippedFileName(fileName);
525 for (final LocalizedMessage element : errors) {
526 final AuditEvent evt = new AuditEvent(this, stripped, element);
527 if (filters.accept(evt)) {
528 for (final AuditListener listener : listeners) {
529 listener.addError(evt);
530 }
531 }
532 }
533 }
534
535
536
537
538
539
540
541 public final void setFileExtensions(String[] extensions)
542 {
543 if (extensions == null) {
544 fileExtensions = null;
545 return;
546 }
547
548 fileExtensions = new String[extensions.length];
549 for (int i = 0; i < extensions.length; i++) {
550 final String extension = extensions[i];
551 if (extension.startsWith(".")) {
552 fileExtensions[i] = extension;
553 }
554 else {
555 fileExtensions[i] = "." + extension;
556 }
557 }
558 }
559
560
561
562
563
564
565 public void setModuleFactory(ModuleFactory moduleFactory)
566 {
567 this.moduleFactory = moduleFactory;
568 }
569
570
571 public void setLocaleCountry(String localeCountry)
572 {
573 this.localeCountry = localeCountry;
574 }
575
576
577 public void setLocaleLanguage(String localeLanguage)
578 {
579 this.localeLanguage = localeLanguage;
580 }
581
582
583
584
585
586
587
588
589 public final void setSeverity(String severity)
590 {
591 severityLevel = SeverityLevel.getInstance(severity);
592 }
593
594
595
596
597
598
599
600
601 public final void setClassloader(ClassLoader loader)
602 {
603 this.loader = loader;
604 }
605
606
607
608
609
610
611
612
613 public final void setModuleClassLoader(ClassLoader moduleClassLoader)
614 {
615 this.moduleClassLoader = moduleClassLoader;
616 }
617
618
619
620
621
622
623 public void setCharset(String charset)
624 throws UnsupportedEncodingException
625 {
626 if (!Charset.isSupported(charset)) {
627 final String message = "unsupported charset: '" + charset + "'";
628 throw new UnsupportedEncodingException(message);
629 }
630 this.charset = charset;
631 }
632 }