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 }