KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > puppycrawl > tools > checkstyle > Checker


1 ////////////////////////////////////////////////////////////////////////////////
2
// checkstyle: Checks Java source code for adherence to a set of rules.
3
// Copyright (C) 2001-2005 Oliver Burn
4
//
5
// This library is free software; you can redistribute it and/or
6
// modify it under the terms of the GNU Lesser General Public
7
// License as published by the Free Software Foundation; either
8
// version 2.1 of the License, or (at your option) any later version.
9
//
10
// This library is distributed in the hope that it will be useful,
11
// but WITHOUT ANY WARRANTY; without even the implied warranty of
12
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13
// Lesser General Public License for more details.
14
//
15
// You should have received a copy of the GNU Lesser General Public
16
// License along with this library; if not, write to the Free Software
17
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
////////////////////////////////////////////////////////////////////////////////
19
package com.puppycrawl.tools.checkstyle;
20
21 import java.io.File JavaDoc;
22
23 import java.util.ArrayList JavaDoc;
24 import java.util.Iterator JavaDoc;
25 import java.util.Locale JavaDoc;
26 import java.util.Stack JavaDoc;
27 import java.util.StringTokenizer JavaDoc;
28
29 import com.puppycrawl.tools.checkstyle.api.SeverityLevelCounter;
30 import com.puppycrawl.tools.checkstyle.api.AutomaticBean;
31 import com.puppycrawl.tools.checkstyle.api.MessageDispatcher;
32 import com.puppycrawl.tools.checkstyle.api.SeverityLevel;
33 import com.puppycrawl.tools.checkstyle.api.Context;
34 import com.puppycrawl.tools.checkstyle.api.FilterSet;
35 import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
36 import com.puppycrawl.tools.checkstyle.api.LocalizedMessage;
37 import com.puppycrawl.tools.checkstyle.api.Configuration;
38 import com.puppycrawl.tools.checkstyle.api.FileSetCheck;
39 import com.puppycrawl.tools.checkstyle.api.Filter;
40 import com.puppycrawl.tools.checkstyle.api.AuditListener;
41 import com.puppycrawl.tools.checkstyle.api.Utils;
42 import com.puppycrawl.tools.checkstyle.api.AuditEvent;
43
44 /**
45  * This class provides the functionality to check a set of files.
46  * @author Oliver Burn
47  * @author <a HREF="mailto:stephane.bailliez@wanadoo.fr">Stephane Bailliez</a>
48  * @author lkuehne
49  */

50 public class Checker extends AutomaticBean
51     implements MessageDispatcher
52 {
53     /** maintains error count */
54     private final SeverityLevelCounter mCounter =
55             new SeverityLevelCounter(SeverityLevel.ERROR);
56
57     /** vector of listeners */
58     private final ArrayList JavaDoc mListeners = new ArrayList JavaDoc();
59
60     /** vector of fileset checks */
61     private final ArrayList JavaDoc mFileSetChecks = new ArrayList JavaDoc();
62
63     /** class loader to resolve classes with. **/
64     private ClassLoader JavaDoc mLoader =
65             Thread.currentThread().getContextClassLoader();
66
67     /** the basedir to strip off in filenames */
68     private String JavaDoc mBasedir;
69
70     /** locale country to report messages **/
71     private String JavaDoc mLocaleCountry = Locale.getDefault().getCountry();
72     /** locale language to report messages **/
73     private String JavaDoc mLocaleLanguage = Locale.getDefault().getLanguage();
74
75     /** The factory for instantiating submodules */
76     private ModuleFactory mModuleFactory;
77
78     /** the context of all child components */
79     private Context mChildContext;
80
81     /** The audit event filters */
82     private final FilterSet mFilters = new FilterSet();
83
84     /**
85      * The severity level of any violations found by submodules.
86      * The value of this property is passed to submodules via
87      * contextualize().
88      *
89      * Note: Since the Checker is merely a container for modules
90      * it does not make sense to implement logging functionality
91      * here. Consequently Checker does not extend AbstractViolationReporter,
92      * leading to a bit of duplicated code for severity level setting.
93      */

94     private SeverityLevel mSeverityLevel = SeverityLevel.ERROR;
95
96     /**
97      * Creates a new <code>Checker</code> instance.
98      * The instance needs to be contextualized and configured.
99      *
100      * @throws CheckstyleException if an error occurs
101      */

102     public Checker()
103         throws CheckstyleException
104     {
105         addListener(mCounter);
106     }
107
108     /** {@inheritDoc} */
109     public void finishLocalSetup() throws CheckstyleException
110     {
111         final Locale JavaDoc locale = new Locale JavaDoc(mLocaleLanguage, mLocaleCountry);
112         LocalizedMessage.setLocale(locale);
113
114         if (mModuleFactory == null) {
115             mModuleFactory = PackageNamesLoader.loadModuleFactory(Thread
116                     .currentThread().getContextClassLoader());
117         }
118
119         final DefaultContext context = new DefaultContext();
120         context.add("classLoader", mLoader);
121         context.add("moduleFactory", mModuleFactory);
122         context.add("severity", mSeverityLevel.getName());
123         context.add("basedir", mBasedir);
124         mChildContext = context;
125     }
126
127     /**
128      * Instantiates, configures and registers a child AbstractFilter
129      * or FileSetCheck
130      * that is specified in the provided configuration.
131      * @param aChildConf {@inheritDoc}
132      * @throws CheckstyleException {@inheritDoc}
133      * @see com.puppycrawl.tools.checkstyle.api.AutomaticBean
134      */

135     protected void setupChild(Configuration aChildConf)
136         throws CheckstyleException
137     {
138         final String JavaDoc name = aChildConf.getName();
139         try {
140             final Object JavaDoc child = mModuleFactory.createModule(name);
141             if (child instanceof AutomaticBean) {
142                 final AutomaticBean bean = (AutomaticBean) child;
143                 bean.contextualize(mChildContext);
144                 bean.configure(aChildConf);
145             }
146             if (child instanceof FileSetCheck) {
147                 final FileSetCheck fsc = (FileSetCheck) child;
148                 addFileSetCheck(fsc);
149             }
150             else if (child instanceof Filter) {
151                 final Filter filter = (Filter) child;
152                 addFilter(filter);
153             }
154             else if (child instanceof AuditListener) {
155                 final AuditListener listener = (AuditListener) child;
156                 addListener(listener);
157             }
158             else {
159                 throw new CheckstyleException(name
160                     + " is not allowed as a child in Checker");
161             }
162         }
163         catch (final Exception JavaDoc ex) {
164             // TODO i18n
165
throw new CheckstyleException(
166                     "cannot initialize module "
167                     + name + " - " + ex.getMessage(), ex);
168         }
169     }
170
171     /**
172      * Adds a FileSetCheck to the list of FileSetChecks
173      * that is executed in process().
174      * @param aFileSetCheck the additional FileSetCheck
175      */

176     public void addFileSetCheck(FileSetCheck aFileSetCheck)
177     {
178         aFileSetCheck.setMessageDispatcher(this);
179         mFileSetChecks.add(aFileSetCheck);
180     }
181
182     /**
183      * Adds a filter to the end of the audit event filter chain.
184      * @param aFilter the additional filter
185      */

186     public void addFilter(Filter aFilter)
187     {
188         mFilters.addFilter(aFilter);
189     }
190
191     /**
192      * Removes filter.
193      * @param aFilter filter to remove.
194      */

195     public void removeFilter(Filter aFilter)
196     {
197         mFilters.removeFilter(aFilter);
198     }
199
200     /** Cleans up the object. **/
201     public void destroy()
202     {
203         mListeners.clear();
204         mFilters.clear();
205     }
206
207     /**
208      * Add the listener that will be used to receive events from the audit.
209      * @param aListener the nosy thing
210      */

211     public void addListener(AuditListener aListener)
212     {
213         mListeners.add(aListener);
214     }
215
216     /**
217      * Removes a given listener.
218      * @param aListener a listener to remove
219      */

220     public void removeListener(AuditListener aListener)
221     {
222         mListeners.remove(aListener);
223     }
224
225     /**
226      * Processes a set of files with all FileSetChecks.
227      * Once this is done, it is highly recommended to call for
228      * the destroy method to close and remove the listeners.
229      * @param aFiles the list of files to be audited.
230      * @return the total number of errors found
231      * @see #destroy()
232      */

233     public int process(File JavaDoc[] aFiles)
234     {
235         fireAuditStarted();
236         for (int i = 0; i < mFileSetChecks.size(); i++) {
237             final FileSetCheck fileSetCheck =
238                 (FileSetCheck) mFileSetChecks.get(i);
239             fileSetCheck.process(aFiles);
240             fileSetCheck.destroy();
241         }
242         final int errorCount = mCounter.getCount();
243         fireAuditFinished();
244         return errorCount;
245     }
246
247
248     /**
249      * Create a stripped down version of a filename.
250      * @param aFileName the original filename
251      * @return the filename where an initial prefix of basedir is stripped
252      */

253     private String JavaDoc getStrippedFileName(final String JavaDoc aFileName)
254     {
255         return Utils.getStrippedFileName(mBasedir, aFileName);
256     }
257
258     /** @param aBasedir the base directory to strip off in filenames */
259     public void setBasedir(String JavaDoc aBasedir)
260     {
261         // we use getAbsolutePath() instead of getCanonicalPath()
262
// because normalize() removes all . and .. so path
263
// will be canonical by default.
264
mBasedir = normalize(aBasedir);
265     }
266
267     /**
268      * &quot;normalize&quot; the given absolute path.
269      *
270      * <p>This includes:
271      * <ul>
272      * <li>Uppercase the drive letter if there is one.</li>
273      * <li>Remove redundant slashes after the drive spec.</li>
274      * <li>resolve all ./, .\, ../ and ..\ sequences.</li>
275      * <li>DOS style paths that start with a drive letter will have
276      * \ as the separator.</li>
277      * </ul>
278      *
279      * @param aPath a path for &quot;normalizing&quot;
280      * @return &quot;normalized&quot; file name
281      * @throws java.lang.NullPointerException if the file path is
282      * equal to null.
283      */

284     public String JavaDoc normalize(String JavaDoc aPath)
285     {
286         final String JavaDoc osName =
287             System.getProperty("os.name").toLowerCase(Locale.US);
288         final boolean onNetWare = (osName.indexOf("netware") > -1);
289
290         final String JavaDoc orig = aPath;
291
292         aPath = aPath.replace('/', File.separatorChar)
293             .replace('\\', File.separatorChar);
294
295         // make sure we are dealing with an absolute path
296
final int colon = aPath.indexOf(":");
297
298         if (!onNetWare) {
299             if (!aPath.startsWith(File.separator)
300                 && !((aPath.length() >= 2)
301                      && Character.isLetter(aPath.charAt(0))
302                      && (colon == 1)))
303             {
304                 final String JavaDoc msg = aPath + " is not an absolute path";
305                 throw new IllegalArgumentException JavaDoc(msg);
306             }
307         }
308         else {
309             if (!aPath.startsWith(File.separator)
310                 && (colon == -1))
311             {
312                 final String JavaDoc msg = aPath + " is not an absolute path";
313                 throw new IllegalArgumentException JavaDoc(msg);
314             }
315         }
316
317         boolean dosWithDrive = false;
318         String JavaDoc root = null;
319         // Eliminate consecutive slashes after the drive spec
320
if ((!onNetWare
321              && (aPath.length() >= 2)
322              && Character.isLetter(aPath.charAt(0))
323              && (aPath.charAt(1) == ':'))
324             || (onNetWare && (colon > -1)))
325         {
326
327             dosWithDrive = true;
328
329             final char[] ca = aPath.replace('/', '\\').toCharArray();
330             final StringBuffer JavaDoc sbRoot = new StringBuffer JavaDoc();
331             for (int i = 0; i < colon; i++) {
332                 sbRoot.append(Character.toUpperCase(ca[i]));
333             }
334             sbRoot.append(':');
335             if (colon + 1 < aPath.length()) {
336                 sbRoot.append(File.separatorChar);
337             }
338             root = sbRoot.toString();
339
340             // Eliminate consecutive slashes after the drive spec
341
final StringBuffer JavaDoc sbPath = new StringBuffer JavaDoc();
342             for (int i = colon + 1; i < ca.length; i++) {
343                 if ((ca[i] != '\\')
344                     || ((ca[i] == '\\') && (ca[i - 1] != '\\')))
345                 {
346                     sbPath.append(ca[i]);
347                 }
348             }
349             aPath = sbPath.toString().replace('\\', File.separatorChar);
350
351         }
352         else {
353             if (aPath.length() == 1) {
354                 root = File.separator;
355                 aPath = "";
356             }
357             else if (aPath.charAt(1) == File.separatorChar) {
358                 // UNC drive
359
root = File.separator + File.separator;
360                 aPath = aPath.substring(2);
361             }
362             else {
363                 root = File.separator;
364                 aPath = aPath.substring(1);
365             }
366         }
367
368         final Stack JavaDoc s = new Stack JavaDoc();
369         s.push(root);
370         final StringTokenizer JavaDoc tok = new StringTokenizer JavaDoc(aPath, File.separator);
371         while (tok.hasMoreTokens()) {
372             final String JavaDoc thisToken = tok.nextToken();
373             if (".".equals(thisToken)) {
374                 continue;
375             }
376             else if ("..".equals(thisToken)) {
377                 if (s.size() < 2) {
378                     throw new IllegalArgumentException JavaDoc("Cannot resolve path "
379                                                        + orig);
380                 }
381                 s.pop();
382             }
383             else { // plain component
384
s.push(thisToken);
385             }
386         }
387
388         final StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
389         for (int i = 0; i < s.size(); i++) {
390             if (i > 1) {
391                 // not before the filesystem root and not after it, since root
392
// already contains one
393
sb.append(File.separatorChar);
394             }
395             sb.append(s.elementAt(i));
396         }
397
398
399         aPath = sb.toString();
400         if (dosWithDrive) {
401             aPath = aPath.replace('/', '\\');
402         }
403         return aPath;
404     }
405
406     /** @return the base directory property used in unit-test. */
407     public final String JavaDoc getBasedir()
408     {
409         return mBasedir;
410     }
411
412     /** notify all listeners about the audit start */
413     protected void fireAuditStarted()
414     {
415         final AuditEvent evt = new AuditEvent(this);
416         final Iterator JavaDoc it = mListeners.iterator();
417         while (it.hasNext()) {
418             final AuditListener listener = (AuditListener) it.next();
419             listener.auditStarted(evt);
420         }
421     }
422
423     /** notify all listeners about the audit end */
424     protected void fireAuditFinished()
425     {
426         final AuditEvent evt = new AuditEvent(this);
427         final Iterator JavaDoc it = mListeners.iterator();
428         while (it.hasNext()) {
429             final AuditListener listener = (AuditListener) it.next();
430             listener.auditFinished(evt);
431         }
432     }
433
434     /**
435      * Notify all listeners about the beginning of a file audit.
436      *
437      * @param aFileName
438      * the file to be audited
439      */

440     public void fireFileStarted(String JavaDoc aFileName)
441     {
442         final String JavaDoc stripped = getStrippedFileName(aFileName);
443         final AuditEvent evt = new AuditEvent(this, stripped);
444         final Iterator JavaDoc it = mListeners.iterator();
445         while (it.hasNext()) {
446             final AuditListener listener = (AuditListener) it.next();
447             listener.fileStarted(evt);
448         }
449     }
450
451     /**
452      * Notify all listeners about the end of a file audit.
453      *
454      * @param aFileName
455      * the audited file
456      */

457     public void fireFileFinished(String JavaDoc aFileName)
458     {
459         final String JavaDoc stripped = getStrippedFileName(aFileName);
460         final AuditEvent evt = new AuditEvent(this, stripped);
461         final Iterator JavaDoc it = mListeners.iterator();
462         while (it.hasNext()) {
463             final AuditListener listener = (AuditListener) it.next();
464             listener.fileFinished(evt);
465         }
466     }
467
468     /**
469      * notify all listeners about the errors in a file.
470      *
471      * @param aFileName
472      * the audited file
473      * @param aErrors
474      * the audit errors from the file
475      */

476     public void fireErrors(String JavaDoc aFileName, LocalizedMessage[] aErrors)
477     {
478         final String JavaDoc stripped = getStrippedFileName(aFileName);
479         for (int i = 0; i < aErrors.length; i++) {
480             final AuditEvent evt = new AuditEvent(this, stripped, aErrors[i]);
481             if (mFilters.accept(evt)) {
482                 final Iterator JavaDoc it = mListeners.iterator();
483                 while (it.hasNext()) {
484                     final AuditListener listener = (AuditListener) it.next();
485                     listener.addError(evt);
486                 }
487             }
488         }
489     }
490
491     /**
492      * Sets the factory for creating submodules.
493      *
494      * @param aModuleFactory the factory for creating FileSetChecks
495      */

496     public void setModuleFactory(ModuleFactory aModuleFactory)
497     {
498         mModuleFactory = aModuleFactory;
499     }
500
501     /** @param aLocaleCountry the country to report messages **/
502     public void setLocaleCountry(String JavaDoc aLocaleCountry)
503     {
504         mLocaleCountry = aLocaleCountry;
505     }
506
507     /** @param aLocaleLanguage the language to report messages **/
508     public void setLocaleLanguage(String JavaDoc aLocaleLanguage)
509     {
510         mLocaleLanguage = aLocaleLanguage;
511     }
512
513     /**
514      * Sets the severity level. The string should be one of the names
515      * defined in the <code>SeverityLevel</code> class.
516      *
517      * @param aSeverity The new severity level
518      * @see SeverityLevel
519      */

520     public final void setSeverity(String JavaDoc aSeverity)
521     {
522         mSeverityLevel = SeverityLevel.getInstance(aSeverity);
523     }
524
525     /**
526      * Sets the classloader that is used to contextualize filesetchecks.
527      * Some Check implementations will use that classloader to improve the
528      * quality of their reports, e.g. to load a class and then analyze it via
529      * reflection.
530      * @param aLoader the new classloader
531      */

532     public final void setClassloader(ClassLoader JavaDoc aLoader)
533     {
534         mLoader = aLoader;
535     }
536 }
537
Popular Tags