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