Coverage Report - com.puppycrawl.tools.checkstyle.ant.CheckstyleAntTask
 
Classes in this File Line Coverage Branch Coverage Complexity
CheckstyleAntTask
100%
199/199
100%
54/54
0
CheckstyleAntTask$Formatter
100%
22/22
100%
12/12
0
CheckstyleAntTask$FormatterType
100%
3/3
N/A
0
CheckstyleAntTask$Listener
100%
4/4
N/A
0
CheckstyleAntTask$Property
100%
9/9
N/A
0
 
 1  
 ////////////////////////////////////////////////////////////////////////////////
 2  
 // checkstyle: Checks Java source code for adherence to a set of rules.
 3  
 // Copyright (C) 2001-2017 the original author or authors.
 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  
 
 20  
 package com.puppycrawl.tools.checkstyle.ant;
 21  
 
 22  
 import java.io.File;
 23  
 import java.io.FileInputStream;
 24  
 import java.io.FileOutputStream;
 25  
 import java.io.IOException;
 26  
 import java.io.OutputStream;
 27  
 import java.util.ArrayList;
 28  
 import java.util.Arrays;
 29  
 import java.util.List;
 30  
 import java.util.Locale;
 31  
 import java.util.Map;
 32  
 import java.util.Properties;
 33  
 import java.util.ResourceBundle;
 34  
 import java.util.stream.Collectors;
 35  
 
 36  
 import org.apache.tools.ant.AntClassLoader;
 37  
 import org.apache.tools.ant.BuildException;
 38  
 import org.apache.tools.ant.DirectoryScanner;
 39  
 import org.apache.tools.ant.Project;
 40  
 import org.apache.tools.ant.Task;
 41  
 import org.apache.tools.ant.taskdefs.LogOutputStream;
 42  
 import org.apache.tools.ant.types.EnumeratedAttribute;
 43  
 import org.apache.tools.ant.types.FileSet;
 44  
 import org.apache.tools.ant.types.Path;
 45  
 import org.apache.tools.ant.types.Reference;
 46  
 
 47  
 import com.google.common.io.Closeables;
 48  
 import com.puppycrawl.tools.checkstyle.Checker;
 49  
 import com.puppycrawl.tools.checkstyle.ConfigurationLoader;
 50  
 import com.puppycrawl.tools.checkstyle.DefaultLogger;
 51  
 import com.puppycrawl.tools.checkstyle.ModuleFactory;
 52  
 import com.puppycrawl.tools.checkstyle.PackageObjectFactory;
 53  
 import com.puppycrawl.tools.checkstyle.PropertiesExpander;
 54  
 import com.puppycrawl.tools.checkstyle.ThreadModeSettings;
 55  
 import com.puppycrawl.tools.checkstyle.XMLLogger;
 56  
 import com.puppycrawl.tools.checkstyle.api.AuditListener;
 57  
 import com.puppycrawl.tools.checkstyle.api.AutomaticBean;
 58  
 import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
 59  
 import com.puppycrawl.tools.checkstyle.api.Configuration;
 60  
 import com.puppycrawl.tools.checkstyle.api.RootModule;
 61  
 import com.puppycrawl.tools.checkstyle.api.SeverityLevel;
 62  
 import com.puppycrawl.tools.checkstyle.api.SeverityLevelCounter;
 63  
 
 64  
 /**
 65  
  * An implementation of a ANT task for calling checkstyle. See the documentation
 66  
  * of the task for usage.
 67  
  * @author Oliver Burn
 68  
  * @noinspection ClassLoaderInstantiation
 69  
  */
 70  32
 public class CheckstyleAntTask extends Task {
 71  
     /** Poor man's enum for an xml formatter. */
 72  
     private static final String E_XML = "xml";
 73  
     /** Poor man's enum for an plain formatter. */
 74  
     private static final String E_PLAIN = "plain";
 75  
 
 76  
     /** Suffix for time string. */
 77  
     private static final String TIME_SUFFIX = " ms.";
 78  
 
 79  
     /** Contains the paths to process. */
 80  32
     private final List<Path> paths = new ArrayList<>();
 81  
 
 82  
     /** Contains the filesets to process. */
 83  32
     private final List<FileSet> fileSets = new ArrayList<>();
 84  
 
 85  
     /** Contains the formatters to log to. */
 86  32
     private final List<Formatter> formatters = new ArrayList<>();
 87  
 
 88  
     /** Contains the Properties to override. */
 89  32
     private final List<Property> overrideProps = new ArrayList<>();
 90  
 
 91  
     /** Class path to locate class files. */
 92  
     private Path classpath;
 93  
 
 94  
     /** Name of file to check. */
 95  
     private String fileName;
 96  
 
 97  
     /** Config file containing configuration. */
 98  
     private String config;
 99  
 
 100  
     /** Whether to fail build on violations. */
 101  32
     private boolean failOnViolation = true;
 102  
 
 103  
     /** Property to set on violations. */
 104  
     private String failureProperty;
 105  
 
 106  
     /** The name of the properties file. */
 107  
     private File properties;
 108  
 
 109  
     /** The maximum number of errors that are tolerated. */
 110  
     private int maxErrors;
 111  
 
 112  
     /** The maximum number of warnings that are tolerated. */
 113  32
     private int maxWarnings = Integer.MAX_VALUE;
 114  
 
 115  
     /**
 116  
      * Whether to execute ignored modules - some modules may log above
 117  
      * their severity depending on their configuration (e.g. WriteTag) so
 118  
      * need to be included
 119  
      */
 120  
     private boolean executeIgnoredModules;
 121  
 
 122  
     ////////////////////////////////////////////////////////////////////////////
 123  
     // Setters for ANT specific attributes
 124  
     ////////////////////////////////////////////////////////////////////////////
 125  
 
 126  
     /**
 127  
      * Tells this task to write failure message to the named property when there
 128  
      * is a violation.
 129  
      * @param propertyName the name of the property to set
 130  
      *                      in the event of an failure.
 131  
      */
 132  
     public void setFailureProperty(String propertyName) {
 133  1
         failureProperty = propertyName;
 134  1
     }
 135  
 
 136  
     /**
 137  
      * Sets flag - whether to fail if a violation is found.
 138  
      * @param fail whether to fail if a violation is found
 139  
      */
 140  
     public void setFailOnViolation(boolean fail) {
 141  2
         failOnViolation = fail;
 142  2
     }
 143  
 
 144  
     /**
 145  
      * Sets the maximum number of errors allowed. Default is 0.
 146  
      * @param maxErrors the maximum number of errors allowed.
 147  
      */
 148  
     public void setMaxErrors(int maxErrors) {
 149  1
         this.maxErrors = maxErrors;
 150  1
     }
 151  
 
 152  
     /**
 153  
      * Sets the maximum number of warnings allowed. Default is
 154  
      * {@link Integer#MAX_VALUE}.
 155  
      * @param maxWarnings the maximum number of warnings allowed.
 156  
      */
 157  
     public void setMaxWarnings(int maxWarnings) {
 158  3
         this.maxWarnings = maxWarnings;
 159  3
     }
 160  
 
 161  
     /**
 162  
      * Adds a path.
 163  
      * @param path the path to add.
 164  
      */
 165  
     public void addPath(Path path) {
 166  4
         paths.add(path);
 167  4
     }
 168  
 
 169  
     /**
 170  
      * Adds set of files (nested fileset attribute).
 171  
      * @param fileSet the file set to add
 172  
      */
 173  
     public void addFileset(FileSet fileSet) {
 174  1
         fileSets.add(fileSet);
 175  1
     }
 176  
 
 177  
     /**
 178  
      * Add a formatter.
 179  
      * @param formatter the formatter to add for logging.
 180  
      */
 181  
     public void addFormatter(Formatter formatter) {
 182  6
         formatters.add(formatter);
 183  6
     }
 184  
 
 185  
     /**
 186  
      * Add an override property.
 187  
      * @param property the property to add
 188  
      */
 189  
     public void addProperty(Property property) {
 190  1
         overrideProps.add(property);
 191  1
     }
 192  
 
 193  
     /**
 194  
      * Set the class path.
 195  
      * @param classpath the path to locate classes
 196  
      */
 197  
     public void setClasspath(Path classpath) {
 198  4
         if (this.classpath == null) {
 199  2
             this.classpath = classpath;
 200  
         }
 201  
         else {
 202  2
             this.classpath.append(classpath);
 203  
         }
 204  4
     }
 205  
 
 206  
     /**
 207  
      * Set the class path from a reference defined elsewhere.
 208  
      * @param classpathRef the reference to an instance defining the classpath
 209  
      */
 210  
     public void setClasspathRef(Reference classpathRef) {
 211  2
         createClasspath().setRefid(classpathRef);
 212  2
     }
 213  
 
 214  
     /**
 215  
      * Creates classpath.
 216  
      * @return a created path for locating classes
 217  
      */
 218  
     public Path createClasspath() {
 219  4
         if (classpath == null) {
 220  2
             classpath = new Path(getProject());
 221  
         }
 222  4
         return classpath.createPath();
 223  
     }
 224  
 
 225  
     /**
 226  
      * Sets file to be checked.
 227  
      * @param file the file to be checked
 228  
      */
 229  
     public void setFile(File file) {
 230  22
         fileName = file.getAbsolutePath();
 231  22
     }
 232  
 
 233  
     /**
 234  
      * Sets configuration file.
 235  
      * @param configuration the configuration file, URL, or resource to use
 236  
      */
 237  
     public void setConfig(String configuration) {
 238  28
         if (config != null) {
 239  1
             throw new BuildException("Attribute 'config' has already been set");
 240  
         }
 241  27
         config = configuration;
 242  27
     }
 243  
 
 244  
     /**
 245  
      * Sets flag - whether to execute ignored modules.
 246  
      * @param omit whether to execute ignored modules
 247  
      */
 248  
     public void setExecuteIgnoredModules(boolean omit) {
 249  1
         executeIgnoredModules = omit;
 250  1
     }
 251  
 
 252  
     ////////////////////////////////////////////////////////////////////////////
 253  
     // Setters for Root Module's configuration attributes
 254  
     ////////////////////////////////////////////////////////////////////////////
 255  
 
 256  
     /**
 257  
      * Sets a properties file for use instead
 258  
      * of individually setting them.
 259  
      * @param props the properties File to use
 260  
      */
 261  
     public void setProperties(File props) {
 262  2
         properties = props;
 263  2
     }
 264  
 
 265  
     ////////////////////////////////////////////////////////////////////////////
 266  
     // The doers
 267  
     ////////////////////////////////////////////////////////////////////////////
 268  
 
 269  
     @Override
 270  
     public void execute() {
 271  27
         final long startTime = System.currentTimeMillis();
 272  
 
 273  
         try {
 274  
             // output version info in debug mode
 275  27
             final ResourceBundle compilationProperties = ResourceBundle
 276  27
                     .getBundle("checkstylecompilation", Locale.ROOT);
 277  27
             final String version = compilationProperties
 278  27
                     .getString("checkstyle.compile.version");
 279  27
             final String compileTimestamp = compilationProperties
 280  27
                     .getString("checkstyle.compile.timestamp");
 281  27
             log("checkstyle version " + version, Project.MSG_VERBOSE);
 282  27
             log("compiled on " + compileTimestamp, Project.MSG_VERBOSE);
 283  
 
 284  
             // Check for no arguments
 285  27
             if (fileName == null
 286  5
                     && fileSets.isEmpty()
 287  4
                     && paths.isEmpty()) {
 288  1
                 throw new BuildException(
 289  
                         "Must specify at least one of 'file' or nested 'fileset' or 'path'.",
 290  1
                         getLocation());
 291  
             }
 292  26
             if (config == null) {
 293  1
                 throw new BuildException("Must specify 'config'.", getLocation());
 294  
             }
 295  25
             realExecute(version);
 296  
         }
 297  
         finally {
 298  27
             final long endTime = System.currentTimeMillis();
 299  27
             log("Total execution took " + (endTime - startTime) + TIME_SUFFIX,
 300  
                 Project.MSG_VERBOSE);
 301  27
         }
 302  17
     }
 303  
 
 304  
     /**
 305  
      * Helper implementation to perform execution.
 306  
      * @param checkstyleVersion Checkstyle compile version.
 307  
      */
 308  
     private void realExecute(String checkstyleVersion) {
 309  
         // Create the root module
 310  25
         RootModule rootModule = null;
 311  
         try {
 312  25
             rootModule = createRootModule();
 313  
 
 314  
             // setup the listeners
 315  22
             final AuditListener[] listeners = getListeners();
 316  40
             for (AuditListener element : listeners) {
 317  20
                 rootModule.addListener(element);
 318  
             }
 319  20
             final SeverityLevelCounter warningCounter =
 320  
                 new SeverityLevelCounter(SeverityLevel.WARNING);
 321  20
             rootModule.addListener(warningCounter);
 322  
 
 323  20
             processFiles(rootModule, warningCounter, checkstyleVersion);
 324  
         }
 325  
         finally {
 326  25
             destroyRootModule(rootModule);
 327  17
         }
 328  17
     }
 329  
 
 330  
     /**
 331  
      * Destroy root module. This method exists only due to bug in cobertura library
 332  
      * https://github.com/cobertura/cobertura/issues/170
 333  
      * @param rootModule Root module that was used to process files
 334  
      */
 335  
     private static void destroyRootModule(RootModule rootModule) {
 336  25
         if (rootModule != null) {
 337  22
             rootModule.destroy();
 338  
         }
 339  25
     }
 340  
 
 341  
     /**
 342  
      * Scans and processes files by means given root module.
 343  
      * @param rootModule Root module to process files
 344  
      * @param warningCounter Root Module's counter of warnings
 345  
      * @param checkstyleVersion Checkstyle compile version
 346  
      */
 347  
     private void processFiles(RootModule rootModule, final SeverityLevelCounter warningCounter,
 348  
             final String checkstyleVersion) {
 349  20
         final long startTime = System.currentTimeMillis();
 350  20
         final List<File> files = getFilesToCheck();
 351  20
         final long endTime = System.currentTimeMillis();
 352  20
         log("To locate the files took " + (endTime - startTime) + TIME_SUFFIX,
 353  
             Project.MSG_VERBOSE);
 354  
 
 355  20
         log("Running Checkstyle " + checkstyleVersion + " on " + files.size()
 356  
                 + " files", Project.MSG_INFO);
 357  20
         log("Using configuration " + config, Project.MSG_VERBOSE);
 358  
 
 359  
         final int numErrs;
 360  
 
 361  
         try {
 362  20
             final long processingStartTime = System.currentTimeMillis();
 363  20
             numErrs = rootModule.process(files);
 364  19
             final long processingEndTime = System.currentTimeMillis();
 365  19
             log("To process the files took " + (processingEndTime - processingStartTime)
 366  
                 + TIME_SUFFIX, Project.MSG_VERBOSE);
 367  
         }
 368  1
         catch (CheckstyleException ex) {
 369  1
             throw new BuildException("Unable to process files: " + files, ex);
 370  19
         }
 371  19
         final int numWarnings = warningCounter.getCount();
 372  19
         final boolean okStatus = numErrs <= maxErrors && numWarnings <= maxWarnings;
 373  
 
 374  
         // Handle the return status
 375  19
         if (!okStatus) {
 376  4
             final String failureMsg =
 377  
                     "Got " + numErrs + " errors and " + numWarnings
 378  
                             + " warnings.";
 379  4
             if (failureProperty != null) {
 380  1
                 getProject().setProperty(failureProperty, failureMsg);
 381  
             }
 382  
 
 383  4
             if (failOnViolation) {
 384  2
                 throw new BuildException(failureMsg, getLocation());
 385  
             }
 386  
         }
 387  17
     }
 388  
 
 389  
     /**
 390  
      * Creates new instance of the root module.
 391  
      * @return new instance of the root module
 392  
      */
 393  
     private RootModule createRootModule() {
 394  
         final RootModule rootModule;
 395  
         try {
 396  25
             final Properties props = createOverridingProperties();
 397  24
             final ThreadModeSettings threadModeSettings =
 398  
                     ThreadModeSettings.SINGLE_THREAD_MODE_INSTANCE;
 399  
             final ConfigurationLoader.IgnoredModulesOptions ignoredModulesOptions;
 400  24
             if (executeIgnoredModules) {
 401  1
                 ignoredModulesOptions = ConfigurationLoader.IgnoredModulesOptions.EXECUTE;
 402  
             }
 403  
             else {
 404  23
                 ignoredModulesOptions = ConfigurationLoader.IgnoredModulesOptions.OMIT;
 405  
             }
 406  
 
 407  24
             final Configuration configuration = ConfigurationLoader.loadConfiguration(config,
 408  
                     new PropertiesExpander(props), ignoredModulesOptions, threadModeSettings);
 409  
 
 410  22
             final ClassLoader moduleClassLoader =
 411  22
                 Checker.class.getClassLoader();
 412  
 
 413  22
             final ModuleFactory factory = new PackageObjectFactory(
 414  22
                     Checker.class.getPackage().getName() + ".", moduleClassLoader);
 415  
 
 416  22
             rootModule = (RootModule) factory.createModule(configuration.getName());
 417  22
             rootModule.setModuleClassLoader(moduleClassLoader);
 418  
 
 419  22
             if (rootModule instanceof Checker) {
 420  11
                 final ClassLoader loader = new AntClassLoader(getProject(),
 421  
                         classpath);
 422  
 
 423  11
                 ((Checker) rootModule).setClassLoader(loader);
 424  
             }
 425  
 
 426  22
             rootModule.configure(configuration);
 427  
         }
 428  2
         catch (final CheckstyleException ex) {
 429  2
             throw new BuildException(String.format(Locale.ROOT, "Unable to create Root Module: "
 430  
                     + "config {%s}, classpath {%s}.", config, classpath), ex);
 431  22
         }
 432  22
         return rootModule;
 433  
     }
 434  
 
 435  
     /**
 436  
      * Create the Properties object based on the arguments specified
 437  
      * to the ANT task.
 438  
      * @return the properties for property expansion expansion
 439  
      * @throws BuildException if an error occurs
 440  
      */
 441  
     private Properties createOverridingProperties() {
 442  25
         final Properties returnValue = new Properties();
 443  
 
 444  
         // Load the properties file if specified
 445  25
         if (properties != null) {
 446  2
             FileInputStream inStream = null;
 447  
             try {
 448  2
                 inStream = new FileInputStream(properties);
 449  1
                 returnValue.load(inStream);
 450  
             }
 451  1
             catch (final IOException ex) {
 452  1
                 throw new BuildException("Error loading Properties file '"
 453  1
                         + properties + "'", ex, getLocation());
 454  
             }
 455  
             finally {
 456  2
                 Closeables.closeQuietly(inStream);
 457  1
             }
 458  
         }
 459  
 
 460  
         // override with Ant properties like ${basedir}
 461  24
         final Map<String, Object> antProps = getProject().getProperties();
 462  24
         for (Map.Entry<String, Object> entry : antProps.entrySet()) {
 463  1
             final String value = String.valueOf(entry.getValue());
 464  1
             returnValue.setProperty(entry.getKey(), value);
 465  1
         }
 466  
 
 467  
         // override with properties specified in subelements
 468  24
         for (Property p : overrideProps) {
 469  1
             returnValue.setProperty(p.getKey(), p.getValue());
 470  1
         }
 471  
 
 472  24
         return returnValue;
 473  
     }
 474  
 
 475  
     /**
 476  
      * Return the list of listeners set in this task.
 477  
      * @return the list of listeners.
 478  
      */
 479  
     private AuditListener[] getListeners() {
 480  22
         final int formatterCount = Math.max(1, formatters.size());
 481  
 
 482  22
         final AuditListener[] listeners = new AuditListener[formatterCount];
 483  
 
 484  
         // formatters
 485  
         try {
 486  22
             if (formatters.isEmpty()) {
 487  16
                 final OutputStream debug = new LogOutputStream(this, Project.MSG_DEBUG);
 488  16
                 final OutputStream err = new LogOutputStream(this, Project.MSG_ERR);
 489  16
                 listeners[0] = new DefaultLogger(debug, AutomaticBean.OutputStreamOptions.CLOSE,
 490  
                         err, AutomaticBean.OutputStreamOptions.CLOSE);
 491  16
             }
 492  
             else {
 493  10
                 for (int i = 0; i < formatterCount; i++) {
 494  6
                     final Formatter formatter = formatters.get(i);
 495  6
                     listeners[i] = formatter.createListener(this);
 496  
                 }
 497  
             }
 498  
         }
 499  2
         catch (IOException ex) {
 500  2
             throw new BuildException(String.format(Locale.ROOT, "Unable to create listeners: "
 501  
                     + "formatters {%s}.", formatters), ex);
 502  20
         }
 503  20
         return listeners;
 504  
     }
 505  
 
 506  
     /**
 507  
      * Returns the list of files (full path name) to process.
 508  
      * @return the list of files included via the fileName, filesets and paths.
 509  
      */
 510  
     private List<File> getFilesToCheck() {
 511  20
         final List<File> allFiles = new ArrayList<>();
 512  20
         if (fileName != null) {
 513  
             // oops we've got an additional one to process, don't
 514  
             // forget it. No sweat, it's fully resolved via the setter.
 515  16
             log("Adding standalone file for audit", Project.MSG_VERBOSE);
 516  16
             allFiles.add(new File(fileName));
 517  
         }
 518  
 
 519  20
         final List<File> filesFromFileSets = scanFileSets();
 520  20
         allFiles.addAll(filesFromFileSets);
 521  
 
 522  20
         final List<File> filesFromPaths = scanPaths();
 523  20
         allFiles.addAll(filesFromPaths);
 524  
 
 525  20
         return allFiles;
 526  
     }
 527  
 
 528  
     /**
 529  
      * Retrieves all files from the defined paths.
 530  
      * @return a list of files defined via paths.
 531  
      */
 532  
     private List<File> scanPaths() {
 533  20
         final List<File> allFiles = new ArrayList<>();
 534  
 
 535  24
         for (int i = 0; i < paths.size(); i++) {
 536  4
             final Path currentPath = paths.get(i);
 537  4
             final List<File> pathFiles = scanPath(currentPath, i + 1);
 538  4
             allFiles.addAll(pathFiles);
 539  
         }
 540  
 
 541  20
         return allFiles;
 542  
     }
 543  
 
 544  
     /**
 545  
      * Scans the given path and retrieves all files for the given path.
 546  
      *
 547  
      * @param path      A path to scan.
 548  
      * @param pathIndex The index of the given path. Used in log messages only.
 549  
      * @return A list of files, extracted from the given path.
 550  
      */
 551  
     private List<File> scanPath(Path path, int pathIndex) {
 552  4
         final String[] resources = path.list();
 553  4
         log(pathIndex + ") Scanning path " + path, Project.MSG_VERBOSE);
 554  4
         final List<File> allFiles = new ArrayList<>();
 555  4
         int concreteFilesCount = 0;
 556  
 
 557  7
         for (String resource : resources) {
 558  3
             final File file = new File(resource);
 559  3
             if (file.isFile()) {
 560  2
                 concreteFilesCount++;
 561  2
                 allFiles.add(file);
 562  
             }
 563  
             else {
 564  1
                 final DirectoryScanner scanner = new DirectoryScanner();
 565  1
                 scanner.setBasedir(file);
 566  1
                 scanner.scan();
 567  1
                 final List<File> scannedFiles = retrieveAllScannedFiles(scanner, pathIndex);
 568  1
                 allFiles.addAll(scannedFiles);
 569  
             }
 570  
         }
 571  
 
 572  4
         if (concreteFilesCount > 0) {
 573  4
             log(String.format(Locale.ROOT, "%d) Adding %d files from path %s",
 574  2
                 pathIndex, concreteFilesCount, path), Project.MSG_VERBOSE);
 575  
         }
 576  
 
 577  4
         return allFiles;
 578  
     }
 579  
 
 580  
     /**
 581  
      * Returns the list of files (full path name) to process.
 582  
      * @return the list of files included via the filesets.
 583  
      */
 584  
     protected List<File> scanFileSets() {
 585  19
         final List<File> allFiles = new ArrayList<>();
 586  
 
 587  20
         for (int i = 0; i < fileSets.size(); i++) {
 588  1
             final FileSet fileSet = fileSets.get(i);
 589  1
             final DirectoryScanner scanner = fileSet.getDirectoryScanner(getProject());
 590  1
             final List<File> scannedFiles = retrieveAllScannedFiles(scanner, i);
 591  1
             allFiles.addAll(scannedFiles);
 592  
         }
 593  
 
 594  19
         return allFiles;
 595  
     }
 596  
 
 597  
     /**
 598  
      * Retrieves all matched files from the given scanner.
 599  
      *
 600  
      * @param scanner  A directory scanner. Note, that {@link DirectoryScanner#scan()}
 601  
      *                 must be called before calling this method.
 602  
      * @param logIndex A log entry index. Used only for log messages.
 603  
      * @return A list of files, retrieved from the given scanner.
 604  
      */
 605  
     private List<File> retrieveAllScannedFiles(DirectoryScanner scanner, int logIndex) {
 606  2
         final String[] fileNames = scanner.getIncludedFiles();
 607  4
         log(String.format(Locale.ROOT, "%d) Adding %d files from directory %s",
 608  2
             logIndex, fileNames.length, scanner.getBasedir()), Project.MSG_VERBOSE);
 609  
 
 610  4
         return Arrays.stream(fileNames)
 611  12
             .map(name -> scanner.getBasedir() + File.separator + name)
 612  2
             .map(File::new)
 613  2
             .collect(Collectors.toList());
 614  
     }
 615  
 
 616  
     /**
 617  
      * Poor mans enumeration for the formatter types.
 618  
      * @author Oliver Burn
 619  
      */
 620  8
     public static class FormatterType extends EnumeratedAttribute {
 621  
         /** My possible values. */
 622  1
         private static final String[] VALUES = {E_XML, E_PLAIN};
 623  
 
 624  
         @Override
 625  
         public String[] getValues() {
 626  8
             return VALUES.clone();
 627  
         }
 628  
     }
 629  
 
 630  
     /**
 631  
      * Details about a formatter to be used.
 632  
      * @author Oliver Burn
 633  
      */
 634  10
     public static class Formatter {
 635  
         /** The formatter type. */
 636  
         private FormatterType type;
 637  
         /** The file to output to. */
 638  
         private File toFile;
 639  
         /** Whether or not the write to the named file. */
 640  10
         private boolean useFile = true;
 641  
 
 642  
         /**
 643  
          * Set the type of the formatter.
 644  
          * @param type the type
 645  
          */
 646  
         public void setType(FormatterType type) {
 647  7
             this.type = type;
 648  7
         }
 649  
 
 650  
         /**
 651  
          * Set the file to output to.
 652  
          * @param destination destination the file to output to
 653  
          */
 654  
         public void setTofile(File destination) {
 655  8
             toFile = destination;
 656  8
         }
 657  
 
 658  
         /**
 659  
          * Sets whether or not we write to a file if it is provided.
 660  
          * @param use whether not not to use provided file.
 661  
          */
 662  
         public void setUseFile(boolean use) {
 663  4
             useFile = use;
 664  4
         }
 665  
 
 666  
         /**
 667  
          * Creates a listener for the formatter.
 668  
          * @param task the task running
 669  
          * @return a listener
 670  
          * @throws IOException if an error occurs
 671  
          */
 672  
         public AuditListener createListener(Task task) throws IOException {
 673  
             final AuditListener listener;
 674  13
             if (type != null
 675  10
                     && E_XML.equals(type.getValue())) {
 676  4
                 listener = createXmlLogger(task);
 677  
             }
 678  
             else {
 679  9
                 listener = createDefaultLogger(task);
 680  
             }
 681  11
             return listener;
 682  
         }
 683  
 
 684  
         /**
 685  
          * Creates default logger.
 686  
          * @param task the task to possibly log to
 687  
          * @return a DefaultLogger instance
 688  
          * @throws IOException if an error occurs
 689  
          */
 690  
         private AuditListener createDefaultLogger(Task task)
 691  
                 throws IOException {
 692  
             final AuditListener defaultLogger;
 693  9
             if (toFile == null || !useFile) {
 694  2
                 defaultLogger = new DefaultLogger(
 695  
                     new LogOutputStream(task, Project.MSG_DEBUG),
 696  
                         AutomaticBean.OutputStreamOptions.CLOSE,
 697  
                         new LogOutputStream(task, Project.MSG_ERR),
 698  
                         AutomaticBean.OutputStreamOptions.CLOSE
 699  
                 );
 700  
             }
 701  
             else {
 702  7
                 final FileOutputStream infoStream = new FileOutputStream(toFile);
 703  6
                 defaultLogger =
 704  
                         new DefaultLogger(infoStream, AutomaticBean.OutputStreamOptions.CLOSE,
 705  
                                 infoStream, AutomaticBean.OutputStreamOptions.NONE);
 706  
             }
 707  8
             return defaultLogger;
 708  
         }
 709  
 
 710  
         /**
 711  
          * Creates XML logger.
 712  
          * @param task the task to possibly log to
 713  
          * @return an XMLLogger instance
 714  
          * @throws IOException if an error occurs
 715  
          */
 716  
         private AuditListener createXmlLogger(Task task) throws IOException {
 717  
             final AuditListener xmlLogger;
 718  4
             if (toFile == null || !useFile) {
 719  2
                 xmlLogger = new XMLLogger(new LogOutputStream(task, Project.MSG_INFO),
 720  
                         AutomaticBean.OutputStreamOptions.CLOSE);
 721  
             }
 722  
             else {
 723  2
                 xmlLogger = new XMLLogger(new FileOutputStream(toFile),
 724  
                         AutomaticBean.OutputStreamOptions.CLOSE);
 725  
             }
 726  3
             return xmlLogger;
 727  
         }
 728  
     }
 729  
 
 730  
     /**
 731  
      * Represents a property that consists of a key and value.
 732  
      */
 733  2
     public static class Property {
 734  
         /** The property key. */
 735  
         private String key;
 736  
         /** The property value. */
 737  
         private String value;
 738  
 
 739  
         /**
 740  
          * Gets key.
 741  
          * @return the property key
 742  
          */
 743  
         public String getKey() {
 744  1
             return key;
 745  
         }
 746  
 
 747  
         /**
 748  
          * Sets key.
 749  
          * @param key sets the property key
 750  
          */
 751  
         public void setKey(String key) {
 752  1
             this.key = key;
 753  1
         }
 754  
 
 755  
         /**
 756  
          * Gets value.
 757  
          * @return the property value
 758  
          */
 759  
         public String getValue() {
 760  2
             return value;
 761  
         }
 762  
 
 763  
         /**
 764  
          * Sets value.
 765  
          * @param value set the property value
 766  
          */
 767  
         public void setValue(String value) {
 768  1
             this.value = value;
 769  1
         }
 770  
 
 771  
         /**
 772  
          * Sets the property value from a File.
 773  
          * @param file set the property value from a File
 774  
          */
 775  
         public void setFile(File file) {
 776  1
             value = file.getAbsolutePath();
 777  1
         }
 778  
     }
 779  
 
 780  
     /** Represents a custom listener. */
 781  1
     public static class Listener {
 782  
         /** Class name of the listener class. */
 783  
         private String className;
 784  
 
 785  
         /**
 786  
          * Gets class name.
 787  
          * @return the class name
 788  
          */
 789  
         public String getClassname() {
 790  1
             return className;
 791  
         }
 792  
 
 793  
         /**
 794  
          * Sets class name.
 795  
          * @param name set the class name
 796  
          */
 797  
         public void setClassname(String name) {
 798  1
             className = name;
 799  1
         }
 800  
     }
 801  
 }