KickJava   Java API By Example, From Geeks To Geeks.

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


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 import java.io.FileInputStream JavaDoc;
23 import java.io.FileNotFoundException JavaDoc;
24 import java.io.FileOutputStream JavaDoc;
25 import java.io.IOException JavaDoc;
26 import java.io.OutputStream JavaDoc;
27 import java.util.ArrayList JavaDoc;
28 import java.util.Iterator JavaDoc;
29 import java.util.List JavaDoc;
30 import java.util.Properties JavaDoc;
31 import java.util.Hashtable JavaDoc;
32 import java.util.ResourceBundle JavaDoc;
33 import java.net.URL JavaDoc;
34
35 import com.puppycrawl.tools.checkstyle.api.AuditListener;
36 import com.puppycrawl.tools.checkstyle.api.Configuration;
37 import com.puppycrawl.tools.checkstyle.api.SeverityLevelCounter;
38 import com.puppycrawl.tools.checkstyle.api.SeverityLevel;
39 import org.apache.tools.ant.AntClassLoader;
40 import org.apache.tools.ant.BuildException;
41 import org.apache.tools.ant.DirectoryScanner;
42 import org.apache.tools.ant.Project;
43 import org.apache.tools.ant.Task;
44 import org.apache.tools.ant.taskdefs.LogOutputStream;
45 import org.apache.tools.ant.types.EnumeratedAttribute;
46 import org.apache.tools.ant.types.FileSet;
47 import org.apache.tools.ant.types.Path;
48 import org.apache.tools.ant.types.Reference;
49
50 /**
51  * An implementation of a ANT task for calling checkstyle. See the documentation
52  * of the task for usage.
53  * @author Oliver Burn
54  */

55 public class CheckStyleTask extends Task
56 {
57     /** poor man's enum for an xml formatter */
58     private static final String JavaDoc E_XML = "xml";
59     /** poor man's enum for an plain formatter */
60     private static final String JavaDoc E_PLAIN = "plain";
61
62     /** class path to locate class files */
63     private Path mClasspath;
64
65     /** name of file to check */
66     private String JavaDoc mFileName;
67
68     /** config file containing configuration */
69     private String JavaDoc mConfigLocation;
70
71     /** contains package names */
72     private File JavaDoc mPackageNamesFile;
73
74     /** whether to fail build on violations */
75     private boolean mFailOnViolation = true;
76
77     /** property to set on violations */
78     private String JavaDoc mFailureProperty;
79
80     /** contains the filesets to process */
81     private final List JavaDoc mFileSets = new ArrayList JavaDoc();
82
83     /** contains the formatters to log to */
84     private final List JavaDoc mFormatters = new ArrayList JavaDoc();
85
86     /** contains the Properties to override */
87     private final List JavaDoc mOverrideProps = new ArrayList JavaDoc();
88
89     /** the name of the properties file */
90     private File JavaDoc mPropertiesFile;
91
92     /** the maximum number of errors that are tolerated. */
93     private int mMaxErrors;
94
95     /** the maximum number of warnings that are tolerated. */
96     private int mMaxWarnings = Integer.MAX_VALUE;
97
98     ////////////////////////////////////////////////////////////////////////////
99
// Setters for ANT specific attributes
100
////////////////////////////////////////////////////////////////////////////
101

102     /**
103      * Tells this task to set the named property to "true" when there
104      * is a violation.
105      * @param aPropertyName the name of the property to set
106      * in the event of an failure.
107      */

108     public void setFailureProperty(String JavaDoc aPropertyName)
109     {
110         mFailureProperty = aPropertyName;
111     }
112
113     /** @param aFail whether to fail if a violation is found */
114     public void setFailOnViolation(boolean aFail)
115     {
116         mFailOnViolation = aFail;
117     }
118
119     /**
120      * Sets the maximum number of errors allowed. Default is 0.
121      * @param aMaxErrors the maximum number of errors allowed.
122      */

123     public void setMaxErrors(int aMaxErrors)
124     {
125         mMaxErrors = aMaxErrors;
126     }
127
128     /**
129      * Sets the maximum number of warings allowed. Default is
130      * {@link Integer#MAX_VALUE}.
131      * @param aMaxWarnings the maximum number of warnings allowed.
132      */

133     public void setMaxWarnings(int aMaxWarnings)
134     {
135         mMaxWarnings = aMaxWarnings;
136     }
137
138     /**
139      * Adds a set of files (nested fileset attribute).
140      * @param aFS the file set to add
141      */

142     public void addFileset(FileSet aFS)
143     {
144         mFileSets.add(aFS);
145     }
146
147     /**
148      * Add a formatter.
149      * @param aFormatter the formatter to add for logging.
150      */

151     public void addFormatter(Formatter aFormatter)
152     {
153         mFormatters.add(aFormatter);
154     }
155
156     /**
157      * Add an override property.
158      * @param aProperty the property to add
159      */

160     public void addProperty(Property aProperty)
161     {
162         mOverrideProps.add(aProperty);
163     }
164
165     /**
166      * Set the class path.
167      * @param aClasspath the path to locate classes
168      */

169     public void setClasspath(Path aClasspath)
170     {
171         if (mClasspath == null) {
172             mClasspath = aClasspath;
173         }
174         else {
175             mClasspath.append(aClasspath);
176         }
177     }
178
179     /**
180      * Set the class path from a reference defined elsewhere.
181      * @param aClasspathRef the reference to an instance defining the classpath
182      */

183     public void setClasspathRef(Reference aClasspathRef)
184     {
185         createClasspath().setRefid(aClasspathRef);
186     }
187
188     /** @return a created path for locating classes */
189     public Path createClasspath()
190     {
191         if (mClasspath == null) {
192             mClasspath = new Path(getProject());
193         }
194         return mClasspath.createPath();
195     }
196
197     /** @param aFile the file to be checked */
198     public void setFile(File JavaDoc aFile)
199     {
200         mFileName = aFile.getAbsolutePath();
201     }
202
203     /** @param aFile the configuration file to use */
204     public void setConfig(File JavaDoc aFile)
205     {
206         setConfigLocation(aFile.getAbsolutePath());
207     }
208
209     /** @param aURL the URL of the configuration to use */
210     public void setConfigURL(URL JavaDoc aURL)
211     {
212         setConfigLocation(aURL.toExternalForm());
213     }
214
215     /**
216      * Sets the location of the configuration.
217      * @param aLocation the location, which is either a
218      */

219     private void setConfigLocation(String JavaDoc aLocation)
220     {
221         if (mConfigLocation != null) {
222             throw new BuildException("Attributes 'config' and 'configURL' "
223                     + "must not be set at the same time");
224         }
225         mConfigLocation = aLocation;
226     }
227
228     /** @param aFile the package names file to use */
229     public void setPackageNamesFile(File JavaDoc aFile)
230     {
231         mPackageNamesFile = aFile;
232     }
233
234     ////////////////////////////////////////////////////////////////////////////
235
// Setters for Checker configuration attributes
236
////////////////////////////////////////////////////////////////////////////
237

238     /**
239      * Sets a properties file for use instead
240      * of individually setting them.
241      * @param aProps the properties File to use
242      */

243     public void setProperties(File JavaDoc aProps)
244     {
245         mPropertiesFile = aProps;
246     }
247
248     ////////////////////////////////////////////////////////////////////////////
249
// The doers
250
////////////////////////////////////////////////////////////////////////////
251

252     /**
253      * Actually checks the files specified. All errors are reported to
254      * System.out. Will fail if any errors occurred.
255      * @throws BuildException an error occurred
256      */

257     public void execute() throws BuildException
258     {
259         final long startTime = System.currentTimeMillis();
260         final ClassLoader JavaDoc loader = Thread.currentThread()
261                 .getContextClassLoader();
262         try {
263             Thread.currentThread().setContextClassLoader(
264                     getClass().getClassLoader());
265             realExecute();
266         }
267         finally {
268             Thread.currentThread().setContextClassLoader(loader);
269             final long endTime = System.currentTimeMillis();
270             log("Total execution took " + (endTime - startTime) + " ms.",
271                 Project.MSG_VERBOSE);
272         }
273     }
274
275     /**
276      * Helper implementation to perform execution.
277      */

278     private void realExecute()
279     {
280         // output version info in debug mode
281
final ResourceBundle JavaDoc compilationProperties = ResourceBundle
282                 .getBundle("checkstylecompilation");
283         final String JavaDoc version = compilationProperties
284                 .getString("checkstyle.compile.version");
285         final String JavaDoc compileTimestamp = compilationProperties
286                 .getString("checkstyle.compile.timestamp");
287         log("checkstyle version " + version, Project.MSG_VERBOSE);
288         log("compiled on " + compileTimestamp, Project.MSG_VERBOSE);
289
290         // Check for no arguments
291
if ((mFileName == null) && (mFileSets.size() == 0)) {
292             throw new BuildException(
293                     "Must specify atleast one of 'file' or nested 'fileset'.",
294                     getLocation());
295         }
296
297         if (mConfigLocation == null) {
298             throw new BuildException("Must specify 'config'.", getLocation());
299         }
300
301         // Create the checker
302
Checker c = null;
303         try {
304             c = createChecker();
305
306             final SeverityLevelCounter warningCounter =
307                 new SeverityLevelCounter(SeverityLevel.WARNING);
308             c.addListener(warningCounter);
309
310             // Process the files
311
long startTime = System.currentTimeMillis();
312             final File JavaDoc[] files = scanFileSets();
313             long endTime = System.currentTimeMillis();
314             log("To locate the files took " + (endTime - startTime) + " ms.",
315                 Project.MSG_VERBOSE);
316
317             log("Running Checkstyle " + version + " on " + files.length
318                     + " files", Project.MSG_INFO);
319             log("Using configuration " + mConfigLocation, Project.MSG_VERBOSE);
320
321             startTime = System.currentTimeMillis();
322             final int numErrs = c.process(files);
323             endTime = System.currentTimeMillis();
324             log("To process the files took " + (endTime - startTime) + " ms.",
325                 Project.MSG_VERBOSE);
326             final int numWarnings = warningCounter.getCount();
327             final boolean ok = (numErrs <= mMaxErrors)
328                     && (numWarnings <= mMaxWarnings);
329
330             // Handle the return status
331
if (!ok && (mFailureProperty != null)) {
332                 getProject().setProperty(mFailureProperty, "true");
333             }
334
335             if (!ok && mFailOnViolation) {
336                 throw new BuildException("Got " + numErrs + " errors and "
337                         + numWarnings + " warnings.", getLocation());
338             }
339         }
340         finally {
341             if (c != null) {
342                 c.destroy();
343             }
344         }
345     }
346
347     /**
348      * Creates new instance of <code>Checker</code>.
349      * @return new instance of <code>Checker</code>
350      */

351     private Checker createChecker()
352     {
353         Checker c = null;
354         try {
355             final Properties JavaDoc props = createOverridingProperties();
356             final Configuration config = ConfigurationLoader.loadConfiguration(
357                     mConfigLocation, new PropertiesExpander(props), true);
358
359             final DefaultContext context = new DefaultContext();
360             final ClassLoader JavaDoc loader = new AntClassLoader(getProject(),
361                     mClasspath);
362             context.add("classloader", loader);
363
364             c = new Checker();
365
366             //load the set of package names
367
if (mPackageNamesFile != null) {
368                 final ModuleFactory moduleFactory = PackageNamesLoader
369                         .loadModuleFactory(mPackageNamesFile.getAbsolutePath());
370                 c.setModuleFactory(moduleFactory);
371             }
372             c.contextualize(context);
373             c.configure(config);
374
375             // setup the listeners
376
final AuditListener[] listeners = getListeners();
377             for (int i = 0; i < listeners.length; i++) {
378                 c.addListener(listeners[i]);
379             }
380         }
381         catch (final Exception JavaDoc e) {
382             throw new BuildException("Unable to create a Checker: "
383                     + e.getMessage(), e);
384         }
385
386         return c;
387     }
388
389     /**
390      * Create the Properties object based on the arguments specified
391      * to the ANT task.
392      * @return the properties for property expansion expansion
393      * @throws BuildException if an error occurs
394      */

395     private Properties JavaDoc createOverridingProperties()
396     {
397         final Properties JavaDoc retVal = new Properties JavaDoc();
398
399         // Load the properties file if specified
400
if (mPropertiesFile != null) {
401             FileInputStream JavaDoc inStream = null;
402             try {
403                 inStream = new FileInputStream JavaDoc(mPropertiesFile);
404                 retVal.load(inStream);
405             }
406             catch (final FileNotFoundException JavaDoc e) {
407                 throw new BuildException("Could not find Properties file '"
408                         + mPropertiesFile + "'", e, getLocation());
409             }
410             catch (final IOException JavaDoc e) {
411                 throw new BuildException("Error loading Properties file '"
412                         + mPropertiesFile + "'", e, getLocation());
413             }
414             finally {
415                 try {
416                     if (inStream != null) {
417                         inStream.close();
418                     }
419                 }
420                 catch (final IOException JavaDoc e) {
421                     throw new BuildException("Error closing Properties file '"
422                             + mPropertiesFile + "'", e, getLocation());
423                 }
424             }
425         }
426
427         // override with Ant properties like ${basedir}
428
final Hashtable JavaDoc antProps = this.getProject().getProperties();
429         for (final Iterator JavaDoc it = antProps.keySet().iterator(); it.hasNext();) {
430             final String JavaDoc key = (String JavaDoc) it.next();
431             final String JavaDoc value = String.valueOf(antProps.get(key));
432             retVal.put(key, value);
433         }
434
435         // override with properties specified in subelements
436
for (final Iterator JavaDoc it = mOverrideProps.iterator(); it.hasNext();) {
437             final Property p = (Property) it.next();
438             retVal.put(p.getKey(), p.getValue());
439         }
440
441         return retVal;
442     }
443
444     /**
445      * Return the list of listeners set in this task.
446      * @return the list of listeners.
447      * @throws ClassNotFoundException if an error occurs
448      * @throws InstantiationException if an error occurs
449      * @throws IllegalAccessException if an error occurs
450      * @throws IOException if an error occurs
451      */

452     protected AuditListener[] getListeners() throws ClassNotFoundException JavaDoc,
453             InstantiationException JavaDoc, IllegalAccessException JavaDoc, IOException JavaDoc
454     {
455         final int formatterCount = Math.max(1, mFormatters.size());
456
457         final AuditListener[] listeners = new AuditListener[formatterCount];
458
459         // formatters
460
if (mFormatters.size() == 0) {
461             final OutputStream JavaDoc debug = new LogOutputStream(this,
462                     Project.MSG_DEBUG);
463             final OutputStream JavaDoc err = new LogOutputStream(this, Project.MSG_ERR);
464             listeners[0] = new DefaultLogger(debug, true, err, true);
465         }
466         else {
467             for (int i = 0; i < formatterCount; i++) {
468                 final Formatter f = (Formatter) mFormatters.get(i);
469                 listeners[i] = f.createListener(this);
470             }
471         }
472         return listeners;
473     }
474
475     /**
476      * returns the list of files (full path name) to process.
477      * @return the list of files included via the filesets.
478      */

479     protected File JavaDoc[] scanFileSets()
480     {
481         final ArrayList JavaDoc list = new ArrayList JavaDoc();
482         if (mFileName != null) {
483             // oops we've got an additional one to process, don't
484
// forget it. No sweat, it's fully resolved via the setter.
485
log("Adding standalone file for audit", Project.MSG_VERBOSE);
486             list.add(new File JavaDoc(mFileName));
487         }
488         for (int i = 0; i < mFileSets.size(); i++) {
489             final FileSet fs = (FileSet) mFileSets.get(i);
490             final DirectoryScanner ds = fs.getDirectoryScanner(getProject());
491             ds.scan();
492
493             final String JavaDoc[] names = ds.getIncludedFiles();
494             log(i + ") Adding " + names.length + " files from directory "
495                     + ds.getBasedir(), Project.MSG_VERBOSE);
496
497             for (int j = 0; j < names.length; j++) {
498                 final String JavaDoc pathname = ds.getBasedir() + File.separator
499                         + names[j];
500                 list.add(new File JavaDoc(pathname));
501             }
502         }
503
504         return (File JavaDoc[]) list.toArray(new File JavaDoc[0]);
505     }
506
507     /**
508      * Poor mans enumeration for the formatter types.
509      * @author Oliver Burn
510      */

511     public static class FormatterType extends EnumeratedAttribute
512     {
513         /** my possible values */
514         private static final String JavaDoc[] VALUES = {E_XML, E_PLAIN};
515
516         /** {@inheritDoc} */
517         public String JavaDoc[] getValues()
518         {
519             return VALUES;
520         }
521     }
522
523     /**
524      * Details about a formatter to be used.
525      * @author Oliver Burn
526      */

527     public static class Formatter
528     {
529         /** the formatter type */
530         private FormatterType mFormatterType;
531         /** the file to output to */
532         private File JavaDoc mToFile;
533         /** Whether or not the write to the named file. */
534         private boolean mUseFile = true;
535
536         /**
537          * Set the type of the formatter.
538          * @param aType the type
539          */

540         public void setType(FormatterType aType)
541         {
542             final String JavaDoc val = aType.getValue();
543             if (!E_XML.equals(val) && !E_PLAIN.equals(val)) {
544                 throw new BuildException("Invalid formatter type: " + val);
545             }
546
547             mFormatterType = aType;
548         }
549
550         /**
551          * Set the file to output to.
552          * @param aTo the file to output to
553          */

554         public void setTofile(File JavaDoc aTo)
555         {
556             mToFile = aTo;
557         }
558
559         /**
560          * Sets whether or not we write to a file if it is provided.
561          * @param aUse whether not not to use provided file.
562          */

563         public void setUseFile(boolean aUse)
564         {
565             mUseFile = aUse;
566         }
567
568         /**
569          * Creates a listener for the formatter.
570          * @param aTask the task running
571          * @return a listener
572          * @throws IOException if an error occurs
573          */

574         public AuditListener createListener(Task aTask) throws IOException JavaDoc
575         {
576             if ((mFormatterType != null)
577                     && E_XML.equals(mFormatterType.getValue()))
578             {
579                 return createXMLLogger(aTask);
580             }
581             return createDefaultLogger(aTask);
582         }
583
584         /**
585          * @return a DefaultLogger instance
586          * @param aTask the task to possibly log to
587          * @throws IOException if an error occurs
588          */

589         private AuditListener createDefaultLogger(Task aTask)
590             throws IOException JavaDoc
591         {
592             if ((mToFile == null) || !mUseFile) {
593                 return new DefaultLogger(
594                     new LogOutputStream(aTask, Project.MSG_DEBUG),
595                     true, new LogOutputStream(aTask, Project.MSG_ERR), true);
596             }
597             return new DefaultLogger(new FileOutputStream JavaDoc(mToFile), true);
598         }
599
600         /**
601          * @return an XMLLogger instance
602          * @param aTask the task to possibly log to
603          * @throws IOException if an error occurs
604          */

605         private AuditListener createXMLLogger(Task aTask) throws IOException JavaDoc
606         {
607             if ((mToFile == null) || !mUseFile) {
608                 return new XMLLogger(new LogOutputStream(aTask,
609                         Project.MSG_INFO), true);
610             }
611             return new XMLLogger(new FileOutputStream JavaDoc(mToFile), true);
612         }
613     }
614
615     /**
616      * Represents a property that consists of a key and value.
617      */

618     public static class Property
619     {
620         /** the property key */
621         private String JavaDoc mKey;
622         /** the property value */
623         private String JavaDoc mValue;
624
625         /** @return the property key */
626         public String JavaDoc getKey()
627         {
628             return mKey;
629         }
630
631         /** @param aKey sets the property key */
632         public void setKey(String JavaDoc aKey)
633         {
634             mKey = aKey;
635         }
636
637         /** @return the property value */
638         public String JavaDoc getValue()
639         {
640             return mValue;
641         }
642
643         /** @param aValue set the property value */
644         public void setValue(String JavaDoc aValue)
645         {
646             mValue = aValue;
647         }
648
649         /** @param aValue set the property value from a File */
650         public void setFile(File JavaDoc aValue)
651         {
652             setValue(aValue.getAbsolutePath());
653         }
654     }
655
656     /** Represents a custom listener. */
657     public static class Listener
658     {
659         /** classname of the listener class */
660         private String JavaDoc mClassname;
661
662         /** @return the classname */
663         public String JavaDoc getClassname()
664         {
665             return mClassname;
666         }
667
668         /** @param aClassname set the classname */
669         public void setClassname(String JavaDoc aClassname)
670         {
671             mClassname = aClassname;
672         }
673     }
674 }
675
Popular Tags