Coverage Report - com.puppycrawl.tools.checkstyle.Main
 
Classes in this File Line Coverage Branch Coverage Complexity
Main
100%
232/232
100%
102/102
0
Main$1
100%
3/3
N/A
0
Main$CliOptions
100%
1/1
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;
 21  
 
 22  
 import java.io.File;
 23  
 import java.io.FileInputStream;
 24  
 import java.io.FileNotFoundException;
 25  
 import java.io.FileOutputStream;
 26  
 import java.io.IOException;
 27  
 import java.io.OutputStream;
 28  
 import java.util.ArrayList;
 29  
 import java.util.LinkedList;
 30  
 import java.util.List;
 31  
 import java.util.Properties;
 32  
 import java.util.logging.ConsoleHandler;
 33  
 import java.util.logging.Filter;
 34  
 import java.util.logging.Level;
 35  
 import java.util.logging.LogRecord;
 36  
 import java.util.logging.Logger;
 37  
 import java.util.regex.Pattern;
 38  
 
 39  
 import org.apache.commons.cli.CommandLine;
 40  
 import org.apache.commons.cli.CommandLineParser;
 41  
 import org.apache.commons.cli.DefaultParser;
 42  
 import org.apache.commons.cli.HelpFormatter;
 43  
 import org.apache.commons.cli.Options;
 44  
 import org.apache.commons.cli.ParseException;
 45  
 import org.apache.commons.logging.Log;
 46  
 import org.apache.commons.logging.LogFactory;
 47  
 
 48  
 import com.google.common.io.Closeables;
 49  
 import com.puppycrawl.tools.checkstyle.api.AuditListener;
 50  
 import com.puppycrawl.tools.checkstyle.api.AutomaticBean;
 51  
 import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
 52  
 import com.puppycrawl.tools.checkstyle.api.Configuration;
 53  
 import com.puppycrawl.tools.checkstyle.api.LocalizedMessage;
 54  
 import com.puppycrawl.tools.checkstyle.api.RootModule;
 55  
 import com.puppycrawl.tools.checkstyle.utils.CommonUtils;
 56  
 
 57  
 /**
 58  
  * Wrapper command line program for the Checker.
 59  
  * @author the original author or authors.
 60  
  * @noinspection UseOfSystemOutOrSystemErr
 61  
  **/
 62  
 public final class Main {
 63  
     /**
 64  
      * A key pointing to the error counter
 65  
      * message in the "messages.properties" file.
 66  
      */
 67  
     public static final String ERROR_COUNTER = "Main.errorCounter";
 68  
     /**
 69  
      * A key pointing to the load properties exception
 70  
      * message in the "messages.properties" file.
 71  
      */
 72  
     public static final String LOAD_PROPERTIES_EXCEPTION = "Main.loadProperties";
 73  
     /**
 74  
      * A key pointing to the create listener exception
 75  
      * message in the "messages.properties" file.
 76  
      */
 77  
     public static final String CREATE_LISTENER_EXCEPTION = "Main.createListener";
 78  
     /** Logger for Main. */
 79  1
     private static final Log LOG = LogFactory.getLog(Main.class);
 80  
 
 81  
     /** Width of CLI help option. */
 82  
     private static final int HELP_WIDTH = 100;
 83  
 
 84  
     /** Exit code returned when execution finishes with {@link CheckstyleException}. */
 85  
     private static final int EXIT_WITH_CHECKSTYLE_EXCEPTION_CODE = -2;
 86  
 
 87  
     /** Name for the option 'v'. */
 88  
     private static final String OPTION_V_NAME = "v";
 89  
 
 90  
     /** Name for the option 'c'. */
 91  
     private static final String OPTION_C_NAME = "c";
 92  
 
 93  
     /** Name for the option 'f'. */
 94  
     private static final String OPTION_F_NAME = "f";
 95  
 
 96  
     /** Name for the option 'p'. */
 97  
     private static final String OPTION_P_NAME = "p";
 98  
 
 99  
     /** Name for the option 'o'. */
 100  
     private static final String OPTION_O_NAME = "o";
 101  
 
 102  
     /** Name for the option 't'. */
 103  
     private static final String OPTION_T_NAME = "t";
 104  
 
 105  
     /** Name for the option '--tree'. */
 106  
     private static final String OPTION_TREE_NAME = "tree";
 107  
 
 108  
     /** Name for the option '-T'. */
 109  
     private static final String OPTION_CAPITAL_T_NAME = "T";
 110  
 
 111  
     /** Name for the option '--treeWithComments'. */
 112  
     private static final String OPTION_TREE_COMMENT_NAME = "treeWithComments";
 113  
 
 114  
     /** Name for the option '-j'. */
 115  
     private static final String OPTION_J_NAME = "j";
 116  
 
 117  
     /** Name for the option '--javadocTree'. */
 118  
     private static final String OPTION_JAVADOC_TREE_NAME = "javadocTree";
 119  
 
 120  
     /** Name for the option '-J'. */
 121  
     private static final String OPTION_CAPITAL_J_NAME = "J";
 122  
 
 123  
     /** Name for the option '--treeWithJavadoc'. */
 124  
     private static final String OPTION_TREE_JAVADOC_NAME = "treeWithJavadoc";
 125  
 
 126  
     /** Name for the option '-d'. */
 127  
     private static final String OPTION_D_NAME = "d";
 128  
 
 129  
     /** Name for the option '--debug'. */
 130  
     private static final String OPTION_DEBUG_NAME = "debug";
 131  
 
 132  
     /** Name for the option 'e'. */
 133  
     private static final String OPTION_E_NAME = "e";
 134  
 
 135  
     /** Name for the option '--exclude'. */
 136  
     private static final String OPTION_EXCLUDE_NAME = "exclude";
 137  
 
 138  
     /** Name for the option '--executeIgnoredModules'. */
 139  
     private static final String OPTION_EXECUTE_IGNORED_MODULES_NAME = "executeIgnoredModules";
 140  
 
 141  
     /** Name for the option 'x'. */
 142  
     private static final String OPTION_X_NAME = "x";
 143  
 
 144  
     /** Name for the option '--exclude-regexp'. */
 145  
     private static final String OPTION_EXCLUDE_REGEXP_NAME = "exclude-regexp";
 146  
 
 147  
     /** Name for the option '-C'. */
 148  
     private static final String OPTION_CAPITAL_C_NAME = "C";
 149  
 
 150  
     /** Name for the option '--checker-threads-number'. */
 151  
     private static final String OPTION_CHECKER_THREADS_NUMBER_NAME = "checker-threads-number";
 152  
 
 153  
     /** Name for the option '-W'. */
 154  
     private static final String OPTION_CAPITAL_W_NAME = "W";
 155  
 
 156  
     /** Name for the option '--tree-walker-threads-number'. */
 157  
     private static final String OPTION_TREE_WALKER_THREADS_NUMBER_NAME =
 158  
         "tree-walker-threads-number";
 159  
 
 160  
     /** Name for 'xml' format. */
 161  
     private static final String XML_FORMAT_NAME = "xml";
 162  
 
 163  
     /** Name for 'plain' format. */
 164  
     private static final String PLAIN_FORMAT_NAME = "plain";
 165  
 
 166  
     /** A string value of 1. */
 167  
     private static final String ONE_STRING_VALUE = "1";
 168  
 
 169  
     /** Don't create instance of this class, use {@link #main(String[])} method instead. */
 170  1
     private Main() {
 171  1
     }
 172  
 
 173  
     /**
 174  
      * Loops over the files specified checking them for errors. The exit code
 175  
      * is the number of errors found in all the files.
 176  
      * @param args the command line arguments.
 177  
      * @throws IOException if there is a problem with files access
 178  
      * @noinspection CallToPrintStackTrace, CallToSystemExit
 179  
      **/
 180  
     public static void main(String... args) throws IOException {
 181  45
         int errorCounter = 0;
 182  45
         boolean cliViolations = false;
 183  
         // provide proper exit code based on results.
 184  45
         final int exitWithCliViolation = -1;
 185  45
         int exitStatus = 0;
 186  
 
 187  
         try {
 188  
             //parse CLI arguments
 189  45
             final CommandLine commandLine = parseCli(args);
 190  
 
 191  
             // show version and exit if it is requested
 192  44
             if (commandLine.hasOption(OPTION_V_NAME)) {
 193  2
                 System.out.println("Checkstyle version: "
 194  1
                         + Main.class.getPackage().getImplementationVersion());
 195  1
                 exitStatus = 0;
 196  
             }
 197  
             else {
 198  86
                 final List<File> filesToProcess = getFilesToProcess(getExclusions(commandLine),
 199  43
                         commandLine.getArgs());
 200  
 
 201  
                 // return error if something is wrong in arguments
 202  43
                 final List<String> messages = validateCli(commandLine, filesToProcess);
 203  43
                 cliViolations = !messages.isEmpty();
 204  43
                 if (cliViolations) {
 205  16
                     exitStatus = exitWithCliViolation;
 206  16
                     errorCounter = 1;
 207  16
                     messages.forEach(System.out::println);
 208  
                 }
 209  
                 else {
 210  27
                     errorCounter = runCli(commandLine, filesToProcess);
 211  19
                     exitStatus = errorCounter;
 212  
                 }
 213  
             }
 214  
         }
 215  1
         catch (ParseException pex) {
 216  
             // something wrong with arguments - print error and manual
 217  1
             cliViolations = true;
 218  1
             exitStatus = exitWithCliViolation;
 219  1
             errorCounter = 1;
 220  1
             System.out.println(pex.getMessage());
 221  1
             printUsage();
 222  
         }
 223  7
         catch (CheckstyleException ex) {
 224  7
             exitStatus = EXIT_WITH_CHECKSTYLE_EXCEPTION_CODE;
 225  7
             errorCounter = 1;
 226  7
             ex.printStackTrace();
 227  
         }
 228  
         finally {
 229  
             // return exit code base on validation of Checker
 230  
             // two ifs exist till https://github.com/hcoles/pitest/issues/377
 231  45
             if (errorCounter != 0) {
 232  25
                 if (!cliViolations) {
 233  8
                     final LocalizedMessage errorCounterMessage = new LocalizedMessage(0,
 234  
                             Definitions.CHECKSTYLE_BUNDLE, ERROR_COUNTER,
 235  8
                             new String[] {String.valueOf(errorCounter)}, null, Main.class, null);
 236  8
                     System.out.println(errorCounterMessage.getMessage());
 237  
                 }
 238  
             }
 239  45
             if (exitStatus != 0) {
 240  25
                 System.exit(exitStatus);
 241  
             }
 242  
         }
 243  19
     }
 244  
 
 245  
     /**
 246  
      * Parses and executes Checkstyle based on passed arguments.
 247  
      * @param args
 248  
      *        command line parameters
 249  
      * @return parsed information about passed parameters
 250  
      * @throws ParseException
 251  
      *         when passed arguments are not valid
 252  
      */
 253  
     private static CommandLine parseCli(String... args)
 254  
             throws ParseException {
 255  
         // parse the parameters
 256  45
         final CommandLineParser clp = new DefaultParser();
 257  
         // always returns not null value
 258  45
         return clp.parse(buildOptions(), args);
 259  
     }
 260  
 
 261  
     /**
 262  
      * Gets the list of exclusions provided through the command line argument.
 263  
      * @param commandLine command line object
 264  
      * @return List of exclusion patterns.
 265  
      */
 266  
     private static List<Pattern> getExclusions(CommandLine commandLine) {
 267  43
         final List<Pattern> result = new ArrayList<>();
 268  
 
 269  43
         if (commandLine.hasOption(OPTION_E_NAME)) {
 270  2
             for (String value : commandLine.getOptionValues(OPTION_E_NAME)) {
 271  1
                 result.add(Pattern.compile("^" + Pattern.quote(new File(value).getAbsolutePath())
 272  
                         + "$"));
 273  
             }
 274  
         }
 275  43
         if (commandLine.hasOption(OPTION_X_NAME)) {
 276  2
             for (String value : commandLine.getOptionValues(OPTION_X_NAME)) {
 277  1
                 result.add(Pattern.compile(value));
 278  
             }
 279  
         }
 280  
 
 281  43
         return result;
 282  
     }
 283  
 
 284  
     /**
 285  
      * Do validation of Command line options.
 286  
      * @param cmdLine command line object
 287  
      * @param filesToProcess List of files to process found from the command line.
 288  
      * @return list of violations
 289  
      */
 290  
     // -@cs[CyclomaticComplexity] Breaking apart will damage encapsulation
 291  
     private static List<String> validateCli(CommandLine cmdLine, List<File> filesToProcess) {
 292  43
         final List<String> result = new ArrayList<>();
 293  
 
 294  43
         if (filesToProcess.isEmpty()) {
 295  3
             result.add("Files to process must be specified, found 0.");
 296  
         }
 297  
         // ensure there is no conflicting options
 298  40
         else if (cmdLine.hasOption(OPTION_T_NAME) || cmdLine.hasOption(OPTION_CAPITAL_T_NAME)
 299  33
                 || cmdLine.hasOption(OPTION_J_NAME) || cmdLine.hasOption(OPTION_CAPITAL_J_NAME)) {
 300  9
             if (cmdLine.hasOption(OPTION_C_NAME) || cmdLine.hasOption(OPTION_P_NAME)
 301  7
                     || cmdLine.hasOption(OPTION_F_NAME) || cmdLine.hasOption(OPTION_O_NAME)) {
 302  4
                 result.add("Option '-t' cannot be used with other options.");
 303  
             }
 304  5
             else if (filesToProcess.size() > 1) {
 305  1
                 result.add("Printing AST is allowed for only one file.");
 306  
             }
 307  
         }
 308  
         // ensure a configuration file is specified
 309  31
         else if (cmdLine.hasOption(OPTION_C_NAME)) {
 310  30
             final String configLocation = cmdLine.getOptionValue(OPTION_C_NAME);
 311  
             try {
 312  
                 // test location only
 313  30
                 CommonUtils.getUriByFilename(configLocation);
 314  
             }
 315  1
             catch (CheckstyleException ignored) {
 316  1
                 result.add(String.format("Could not find config XML file '%s'.", configLocation));
 317  29
             }
 318  
 
 319  
             // validate optional parameters
 320  30
             if (cmdLine.hasOption(OPTION_F_NAME)) {
 321  6
                 final String format = cmdLine.getOptionValue(OPTION_F_NAME);
 322  6
                 if (!PLAIN_FORMAT_NAME.equals(format) && !XML_FORMAT_NAME.equals(format)) {
 323  1
                     result.add(String.format("Invalid output format."
 324  
                             + " Found '%s' but expected '%s' or '%s'.",
 325  
                             format, PLAIN_FORMAT_NAME, XML_FORMAT_NAME));
 326  
                 }
 327  
             }
 328  30
             if (cmdLine.hasOption(OPTION_P_NAME)) {
 329  2
                 final String propertiesLocation = cmdLine.getOptionValue(OPTION_P_NAME);
 330  2
                 final File file = new File(propertiesLocation);
 331  2
                 if (!file.exists()) {
 332  1
                     result.add(String.format("Could not find file '%s'.", propertiesLocation));
 333  
                 }
 334  
             }
 335  30
             verifyThreadsNumberParameter(cmdLine, result, OPTION_CAPITAL_C_NAME,
 336  
                 "Checker threads number must be greater than zero",
 337  
                 "Invalid Checker threads number");
 338  30
             verifyThreadsNumberParameter(cmdLine, result, OPTION_CAPITAL_W_NAME,
 339  
                 "TreeWalker threads number must be greater than zero",
 340  
                 "Invalid TreeWalker threads number");
 341  30
         }
 342  
         else {
 343  1
             result.add("Must specify a config XML file.");
 344  
         }
 345  
 
 346  43
         return result;
 347  
     }
 348  
 
 349  
     /**
 350  
      * Verifies threads number CLI parameter value.
 351  
      * @param cmdLine a command line
 352  
      * @param result a resulting list of errors
 353  
      * @param cliParameterName a CLI parameter name
 354  
      * @param mustBeGreaterThanZeroMessage a message which should be reported
 355  
      *                                     if the number of threads is less than or equal to zero
 356  
      * @param invalidNumberMessage a message which should be reported if the passed value
 357  
      *                             is not a valid number
 358  
      */
 359  
     private static void verifyThreadsNumberParameter(CommandLine cmdLine, List<String> result,
 360  
         String cliParameterName, String mustBeGreaterThanZeroMessage,
 361  
         String invalidNumberMessage) {
 362  60
         if (cmdLine.hasOption(cliParameterName)) {
 363  10
             final String checkerThreadsNumberStr =
 364  10
                 cmdLine.getOptionValue(cliParameterName);
 365  10
             if (CommonUtils.isInt(checkerThreadsNumberStr)) {
 366  8
                 final int checkerThreadsNumber = Integer.parseInt(checkerThreadsNumberStr);
 367  8
                 if (checkerThreadsNumber < 1) {
 368  2
                     result.add(mustBeGreaterThanZeroMessage);
 369  
                 }
 370  8
             }
 371  
             else {
 372  2
                 result.add(invalidNumberMessage);
 373  
             }
 374  
         }
 375  60
     }
 376  
 
 377  
     /**
 378  
      * Do execution of CheckStyle based on Command line options.
 379  
      * @param commandLine command line object
 380  
      * @param filesToProcess List of files to process found from the command line.
 381  
      * @return number of violations
 382  
      * @throws IOException if a file could not be read.
 383  
      * @throws CheckstyleException if something happens processing the files.
 384  
      */
 385  
     private static int runCli(CommandLine commandLine, List<File> filesToProcess)
 386  
             throws IOException, CheckstyleException {
 387  27
         int result = 0;
 388  
 
 389  
         // create config helper object
 390  27
         final CliOptions config = convertCliToPojo(commandLine, filesToProcess);
 391  27
         if (commandLine.hasOption(OPTION_T_NAME)) {
 392  
             // print AST
 393  1
             final File file = config.files.get(0);
 394  1
             final String stringAst = AstTreeStringPrinter.printFileAst(file,
 395  
                     AstTreeStringPrinter.PrintOptions.WITHOUT_COMMENTS);
 396  1
             System.out.print(stringAst);
 397  1
         }
 398  26
         else if (commandLine.hasOption(OPTION_CAPITAL_T_NAME)) {
 399  1
             final File file = config.files.get(0);
 400  1
             final String stringAst = AstTreeStringPrinter.printFileAst(file,
 401  
                     AstTreeStringPrinter.PrintOptions.WITH_COMMENTS);
 402  1
             System.out.print(stringAst);
 403  1
         }
 404  25
         else if (commandLine.hasOption(OPTION_J_NAME)) {
 405  1
             final File file = config.files.get(0);
 406  1
             final String stringAst = DetailNodeTreeStringPrinter.printFileAst(file);
 407  1
             System.out.print(stringAst);
 408  1
         }
 409  24
         else if (commandLine.hasOption(OPTION_CAPITAL_J_NAME)) {
 410  1
             final File file = config.files.get(0);
 411  1
             final String stringAst = AstTreeStringPrinter.printJavaAndJavadocTree(file);
 412  1
             System.out.print(stringAst);
 413  1
         }
 414  
         else {
 415  23
             if (commandLine.hasOption(OPTION_D_NAME)) {
 416  1
                 final Logger parentLogger = Logger.getLogger(Main.class.getName()).getParent();
 417  1
                 final ConsoleHandler handler = new ConsoleHandler();
 418  1
                 handler.setLevel(Level.FINEST);
 419  1
                 handler.setFilter(new Filter() {
 420  1
                     private final String packageName = Main.class.getPackage().getName();
 421  
 
 422  
                     @Override
 423  
                     public boolean isLoggable(LogRecord record) {
 424  39839
                         return record.getLoggerName().startsWith(packageName);
 425  
                     }
 426  
                 });
 427  1
                 parentLogger.addHandler(handler);
 428  1
                 parentLogger.setLevel(Level.FINEST);
 429  
             }
 430  23
             if (LOG.isDebugEnabled()) {
 431  1
                 LOG.debug("Checkstyle debug logging enabled");
 432  2
                 LOG.debug("Running Checkstyle with version: "
 433  1
                         + Main.class.getPackage().getImplementationVersion());
 434  
             }
 435  
 
 436  
             // run Checker
 437  23
             result = runCheckstyle(config);
 438  
         }
 439  
 
 440  19
         return result;
 441  
     }
 442  
 
 443  
     /**
 444  
      * Util method to convert CommandLine type to POJO object.
 445  
      * @param cmdLine command line object
 446  
      * @param filesToProcess List of files to process found from the command line.
 447  
      * @return command line option as POJO object
 448  
      */
 449  
     private static CliOptions convertCliToPojo(CommandLine cmdLine, List<File> filesToProcess) {
 450  27
         final CliOptions conf = new CliOptions();
 451  27
         conf.format = cmdLine.getOptionValue(OPTION_F_NAME);
 452  27
         if (conf.format == null) {
 453  22
             conf.format = PLAIN_FORMAT_NAME;
 454  
         }
 455  27
         conf.outputLocation = cmdLine.getOptionValue(OPTION_O_NAME);
 456  27
         conf.configLocation = cmdLine.getOptionValue(OPTION_C_NAME);
 457  27
         conf.propertiesLocation = cmdLine.getOptionValue(OPTION_P_NAME);
 458  27
         conf.files = filesToProcess;
 459  27
         conf.executeIgnoredModules = cmdLine.hasOption(OPTION_EXECUTE_IGNORED_MODULES_NAME);
 460  27
         final String checkerThreadsNumber = cmdLine.getOptionValue(
 461  
                 OPTION_CAPITAL_C_NAME, ONE_STRING_VALUE);
 462  27
         conf.checkerThreadsNumber = Integer.parseInt(checkerThreadsNumber);
 463  27
         final String treeWalkerThreadsNumber = cmdLine.getOptionValue(
 464  
                 OPTION_CAPITAL_W_NAME, ONE_STRING_VALUE);
 465  27
         conf.treeWalkerThreadsNumber = Integer.parseInt(treeWalkerThreadsNumber);
 466  27
         return conf;
 467  
     }
 468  
 
 469  
     /**
 470  
      * Executes required Checkstyle actions based on passed parameters.
 471  
      * @param cliOptions
 472  
      *        pojo object that contains all options
 473  
      * @return number of violations of ERROR level
 474  
      * @throws FileNotFoundException
 475  
      *         when output file could not be found
 476  
      * @throws CheckstyleException
 477  
      *         when properties file could not be loaded
 478  
      */
 479  
     private static int runCheckstyle(CliOptions cliOptions)
 480  
             throws CheckstyleException, FileNotFoundException {
 481  
         // setup the properties
 482  
         final Properties props;
 483  
 
 484  23
         if (cliOptions.propertiesLocation == null) {
 485  22
             props = System.getProperties();
 486  
         }
 487  
         else {
 488  1
             props = loadProperties(new File(cliOptions.propertiesLocation));
 489  
         }
 490  
 
 491  
         // create a configuration
 492  23
         final ThreadModeSettings multiThreadModeSettings =
 493  
                 new ThreadModeSettings(
 494  23
                         cliOptions.checkerThreadsNumber, cliOptions.treeWalkerThreadsNumber);
 495  
 
 496  
         final ConfigurationLoader.IgnoredModulesOptions ignoredModulesOptions;
 497  23
         if (cliOptions.executeIgnoredModules) {
 498  1
             ignoredModulesOptions = ConfigurationLoader.IgnoredModulesOptions.EXECUTE;
 499  
         }
 500  
         else {
 501  22
             ignoredModulesOptions = ConfigurationLoader.IgnoredModulesOptions.OMIT;
 502  
         }
 503  
 
 504  46
         final Configuration config = ConfigurationLoader.loadConfiguration(
 505  23
                 cliOptions.configLocation, new PropertiesExpander(props),
 506  
                 ignoredModulesOptions, multiThreadModeSettings);
 507  
 
 508  
         // create a listener for output
 509  21
         final AuditListener listener = createListener(cliOptions.format, cliOptions.outputLocation);
 510  
 
 511  
         // create RootModule object and run it
 512  
         final int errorCounter;
 513  21
         final ClassLoader moduleClassLoader = Checker.class.getClassLoader();
 514  21
         final RootModule rootModule = getRootModule(config.getName(), moduleClassLoader);
 515  
 
 516  
         try {
 517  
 
 518  20
             rootModule.setModuleClassLoader(moduleClassLoader);
 519  20
             rootModule.configure(config);
 520  16
             rootModule.addListener(listener);
 521  
 
 522  
             // run RootModule
 523  16
             errorCounter = rootModule.process(cliOptions.files);
 524  
 
 525  
         }
 526  
         finally {
 527  20
             rootModule.destroy();
 528  15
         }
 529  
 
 530  15
         return errorCounter;
 531  
     }
 532  
 
 533  
     /**
 534  
      * Creates a new instance of the root module that will control and run
 535  
      * Checkstyle.
 536  
      * @param name The name of the module. This will either be a short name that
 537  
      *        will have to be found or the complete package name.
 538  
      * @param moduleClassLoader Class loader used to load the root module.
 539  
      * @return The new instance of the root module.
 540  
      * @throws CheckstyleException if no module can be instantiated from name
 541  
      */
 542  
     private static RootModule getRootModule(String name, ClassLoader moduleClassLoader)
 543  
             throws CheckstyleException {
 544  21
         final ModuleFactory factory = new PackageObjectFactory(
 545  21
                 Checker.class.getPackage().getName(), moduleClassLoader);
 546  
 
 547  21
         return (RootModule) factory.createModule(name);
 548  
     }
 549  
 
 550  
     /**
 551  
      * Loads properties from a File.
 552  
      * @param file
 553  
      *        the properties file
 554  
      * @return the properties in file
 555  
      * @throws CheckstyleException
 556  
      *         when could not load properties file
 557  
      */
 558  
     private static Properties loadProperties(File file)
 559  
             throws CheckstyleException {
 560  2
         final Properties properties = new Properties();
 561  
 
 562  2
         FileInputStream fis = null;
 563  
         try {
 564  2
             fis = new FileInputStream(file);
 565  1
             properties.load(fis);
 566  
         }
 567  1
         catch (final IOException ex) {
 568  1
             final LocalizedMessage loadPropertiesExceptionMessage = new LocalizedMessage(0,
 569  
                     Definitions.CHECKSTYLE_BUNDLE, LOAD_PROPERTIES_EXCEPTION,
 570  1
                     new String[] {file.getAbsolutePath()}, null, Main.class, null);
 571  1
             throw new CheckstyleException(loadPropertiesExceptionMessage.getMessage(), ex);
 572  
         }
 573  
         finally {
 574  2
             Closeables.closeQuietly(fis);
 575  1
         }
 576  
 
 577  1
         return properties;
 578  
     }
 579  
 
 580  
     /**
 581  
      * Creates the audit listener.
 582  
      *
 583  
      * @param format format of the audit listener
 584  
      * @param outputLocation the location of output
 585  
      * @return a fresh new {@code AuditListener}
 586  
      * @exception FileNotFoundException when provided output location is not found
 587  
      * @noinspection IOResourceOpenedButNotSafelyClosed
 588  
      */
 589  
     private static AuditListener createListener(String format,
 590  
                                                 String outputLocation)
 591  
             throws FileNotFoundException {
 592  
 
 593  
         // setup the output stream
 594  
         final OutputStream out;
 595  
         final AutomaticBean.OutputStreamOptions closeOutputStream;
 596  23
         if (outputLocation == null) {
 597  19
             out = System.out;
 598  19
             closeOutputStream = AutomaticBean.OutputStreamOptions.NONE;
 599  
         }
 600  
         else {
 601  4
             out = new FileOutputStream(outputLocation);
 602  4
             closeOutputStream = AutomaticBean.OutputStreamOptions.CLOSE;
 603  
         }
 604  
 
 605  
         // setup a listener
 606  
         final AuditListener listener;
 607  23
         if (XML_FORMAT_NAME.equals(format)) {
 608  1
             listener = new XMLLogger(out, closeOutputStream);
 609  
 
 610  
         }
 611  22
         else if (PLAIN_FORMAT_NAME.equals(format)) {
 612  20
             listener = new DefaultLogger(out, closeOutputStream, out,
 613  
                     AutomaticBean.OutputStreamOptions.NONE);
 614  
 
 615  
         }
 616  
         else {
 617  2
             if (closeOutputStream == AutomaticBean.OutputStreamOptions.CLOSE) {
 618  1
                 CommonUtils.close(out);
 619  
             }
 620  2
             final LocalizedMessage outputFormatExceptionMessage = new LocalizedMessage(0,
 621  
                     Definitions.CHECKSTYLE_BUNDLE, CREATE_LISTENER_EXCEPTION,
 622  
                     new String[] {format, PLAIN_FORMAT_NAME, XML_FORMAT_NAME}, null,
 623  
                     Main.class, null);
 624  2
             throw new IllegalStateException(outputFormatExceptionMessage.getMessage());
 625  
         }
 626  
 
 627  21
         return listener;
 628  
     }
 629  
 
 630  
     /**
 631  
      * Determines the files to process.
 632  
      * @param patternsToExclude The list of directory patterns to exclude from searching.
 633  
      * @param filesToProcess
 634  
      *        arguments that were not processed yet but shall be
 635  
      * @return list of files to process
 636  
      */
 637  
     private static List<File> getFilesToProcess(List<Pattern> patternsToExclude,
 638  
             String... filesToProcess) {
 639  43
         final List<File> files = new LinkedList<>();
 640  86
         for (String element : filesToProcess) {
 641  43
             files.addAll(listFiles(new File(element), patternsToExclude));
 642  
         }
 643  
 
 644  43
         return files;
 645  
     }
 646  
 
 647  
     /**
 648  
      * Traverses a specified node looking for files to check. Found files are added to a specified
 649  
      * list. Subdirectories are also traversed.
 650  
      * @param node
 651  
      *        the node to process
 652  
      * @param patternsToExclude The list of directory patterns to exclude from searching.
 653  
      * @return found files
 654  
      */
 655  
     private static List<File> listFiles(File node, List<Pattern> patternsToExclude) {
 656  
         // could be replaced with org.apache.commons.io.FileUtils.list() method
 657  
         // if only we add commons-io library
 658  193
         final List<File> result = new LinkedList<>();
 659  
 
 660  193
         if (node.canRead()) {
 661  192
             if (node.isDirectory()) {
 662  10
                 if (!isDirectoryExcluded(node.getAbsolutePath(), patternsToExclude)) {
 663  8
                     final File[] files = node.listFiles();
 664  
                     // listFiles() can return null, so we need to check it
 665  8
                     if (files != null) {
 666  154
                         for (File element : files) {
 667  147
                             result.addAll(listFiles(element, patternsToExclude));
 668  
                         }
 669  
                     }
 670  8
                 }
 671  
             }
 672  182
             else if (node.isFile()) {
 673  181
                 result.add(node);
 674  
             }
 675  
         }
 676  193
         return result;
 677  
     }
 678  
 
 679  
     /**
 680  
      * Checks if a directory {@code path} should be excluded based on if it matches one of the
 681  
      * patterns supplied.
 682  
      * @param path The path of the directory to check
 683  
      * @param patternsToExclude The list of directory patterns to exclude from searching.
 684  
      * @return True if the directory matches one of the patterns.
 685  
      */
 686  
     private static boolean isDirectoryExcluded(String path, List<Pattern> patternsToExclude) {
 687  10
         boolean result = false;
 688  
 
 689  10
         for (Pattern pattern : patternsToExclude) {
 690  3
             if (pattern.matcher(path).find()) {
 691  2
                 result = true;
 692  2
                 break;
 693  
             }
 694  1
         }
 695  
 
 696  10
         return result;
 697  
     }
 698  
 
 699  
     /** Prints the usage information. **/
 700  
     private static void printUsage() {
 701  1
         final HelpFormatter formatter = new HelpFormatter();
 702  1
         formatter.setWidth(HELP_WIDTH);
 703  3
         formatter.printHelp(String.format("java %s [options] -c <config.xml> file...",
 704  2
                 Main.class.getName()), buildOptions());
 705  1
     }
 706  
 
 707  
     /**
 708  
      * Builds and returns list of parameters supported by cli Checkstyle.
 709  
      * @return available options
 710  
      */
 711  
     private static Options buildOptions() {
 712  46
         final Options options = new Options();
 713  46
         options.addOption(OPTION_C_NAME, true, "Sets the check configuration file to use.");
 714  46
         options.addOption(OPTION_O_NAME, true, "Sets the output file. Defaults to stdout");
 715  46
         options.addOption(OPTION_P_NAME, true, "Loads the properties file");
 716  46
         options.addOption(OPTION_F_NAME, true, String.format(
 717  
                 "Sets the output format. (%s|%s). Defaults to %s",
 718  
                 PLAIN_FORMAT_NAME, XML_FORMAT_NAME, PLAIN_FORMAT_NAME));
 719  46
         options.addOption(OPTION_V_NAME, false, "Print product version and exit");
 720  46
         options.addOption(OPTION_T_NAME, OPTION_TREE_NAME, false,
 721  
                 "Print Abstract Syntax Tree(AST) of the file");
 722  46
         options.addOption(OPTION_CAPITAL_T_NAME, OPTION_TREE_COMMENT_NAME, false,
 723  
                 "Print Abstract Syntax Tree(AST) of the file including comments");
 724  46
         options.addOption(OPTION_J_NAME, OPTION_JAVADOC_TREE_NAME, false,
 725  
                 "Print Parse tree of the Javadoc comment");
 726  46
         options.addOption(OPTION_CAPITAL_J_NAME, OPTION_TREE_JAVADOC_NAME, false,
 727  
                 "Print full Abstract Syntax Tree of the file");
 728  46
         options.addOption(OPTION_D_NAME, OPTION_DEBUG_NAME, false,
 729  
                 "Print all debug logging of CheckStyle utility");
 730  46
         options.addOption(OPTION_E_NAME, OPTION_EXCLUDE_NAME, true,
 731  
                 "Directory path to exclude from CheckStyle");
 732  46
         options.addOption(OPTION_X_NAME, OPTION_EXCLUDE_REGEXP_NAME, true,
 733  
                 "Regular expression of directory to exclude from CheckStyle");
 734  46
         options.addOption(OPTION_EXECUTE_IGNORED_MODULES_NAME, false,
 735  
                 "Allows ignored modules to be run.");
 736  46
         options.addOption(OPTION_CAPITAL_C_NAME, OPTION_CHECKER_THREADS_NUMBER_NAME, true,
 737  
                 "(experimental) The number of Checker threads (must be greater than zero)");
 738  46
         options.addOption(OPTION_CAPITAL_W_NAME, OPTION_TREE_WALKER_THREADS_NUMBER_NAME, true,
 739  
                 "(experimental) The number of TreeWalker threads (must be greater than zero)");
 740  46
         return options;
 741  
     }
 742  
 
 743  
     /** Helper structure to clear show what is required for Checker to run. **/
 744  497
     private static class CliOptions {
 745  
         /** Properties file location. */
 746  
         private String propertiesLocation;
 747  
         /** Config file location. */
 748  
         private String configLocation;
 749  
         /** Output format. */
 750  
         private String format;
 751  
         /** Output file location. */
 752  
         private String outputLocation;
 753  
         /** List of file to validate. */
 754  
         private List<File> files;
 755  
         /** Switch whether to execute ignored modules or not. */
 756  
         private boolean executeIgnoredModules;
 757  
         /** The checker threads number. */
 758  
         private int checkerThreadsNumber;
 759  
         /** The tree walker threads number. */
 760  
         private int treeWalkerThreadsNumber;
 761  
     }
 762  
 }