001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2014 Oliver Burn 004// 005// This library is free software; you can redistribute it and/or 006// modify it under the terms of the GNU Lesser General Public 007// License as published by the Free Software Foundation; either 008// version 2.1 of the License, or (at your option) any later version. 009// 010// This library is distributed in the hope that it will be useful, 011// but WITHOUT ANY WARRANTY; without even the implied warranty of 012// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013// Lesser General Public License for more details. 014// 015// You should have received a copy of the GNU Lesser General Public 016// License along with this library; if not, write to the Free Software 017// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 018//////////////////////////////////////////////////////////////////////////////// 019package com.puppycrawl.tools.checkstyle; 020 021import com.puppycrawl.tools.checkstyle.api.Utils; 022 023import com.google.common.collect.Lists; 024import com.puppycrawl.tools.checkstyle.api.AuditListener; 025import com.puppycrawl.tools.checkstyle.api.CheckstyleException; 026import com.puppycrawl.tools.checkstyle.api.Configuration; 027import java.io.File; 028import java.io.FileInputStream; 029import java.io.FileNotFoundException; 030import java.io.FileOutputStream; 031import java.io.IOException; 032import java.io.OutputStream; 033import java.util.List; 034import java.util.Properties; 035import org.apache.commons.cli.CommandLine; 036import org.apache.commons.cli.CommandLineParser; 037import org.apache.commons.cli.HelpFormatter; 038import org.apache.commons.cli.Options; 039import org.apache.commons.cli.ParseException; 040import org.apache.commons.cli.PosixParser; 041 042/** 043 * Wrapper command line program for the Checker. 044 * @author Oliver Burn 045 **/ 046public final class Main 047{ 048 /** the options to the command line */ 049 private static final Options OPTS = new Options(); 050 static { 051 OPTS.addOption("c", true, "The check configuration file to use."); 052 OPTS.addOption("o", true, "Sets the output file. Defaults to stdout"); 053 OPTS.addOption("p", true, "Loads the properties file"); 054 OPTS.addOption( 055 "f", 056 true, 057 "Sets the output format. (plain|xml). Defaults to plain"); 058 OPTS.addOption("v", false, "Print product version and exit"); 059 } 060 061 /** Stop instances being created. */ 062 private Main() 063 { 064 } 065 066 /** 067 * Loops over the files specified checking them for errors. The exit code 068 * is the number of errors found in all the files. 069 * @param args the command line arguments 070 **/ 071 public static void main(String[] args) 072 { 073 // parse the parameters 074 final CommandLineParser clp = new PosixParser(); 075 CommandLine line = null; 076 try { 077 line = clp.parse(OPTS, args); 078 } 079 catch (final ParseException e) { 080 e.printStackTrace(); 081 usage(); 082 } 083 assert line != null; 084 085 // show version and exit 086 if (line.hasOption("v")) { 087 System.out.println("Checkstyle version: " 088 + Main.class.getPackage().getImplementationVersion()); 089 System.exit(0); 090 } 091 092 // setup the properties 093 final Properties props = 094 line.hasOption("p") 095 ? loadProperties(new File(line.getOptionValue("p"))) 096 : System.getProperties(); 097 098 // ensure a config file is specified 099 if (!line.hasOption("c")) { 100 System.out.println("Must specify a config XML file."); 101 usage(); 102 } 103 104 final Configuration config = loadConfig(line, props); 105 106 // setup the output stream 107 OutputStream out = null; 108 boolean closeOut = false; 109 if (line.hasOption("o")) { 110 final String fname = line.getOptionValue("o"); 111 try { 112 out = new FileOutputStream(fname); 113 closeOut = true; 114 } 115 catch (final FileNotFoundException e) { 116 System.out.println("Could not find file: '" + fname + "'"); 117 System.exit(1); 118 } 119 } 120 else { 121 out = System.out; 122 closeOut = false; 123 } 124 125 final AuditListener listener = createListener(line, out, closeOut); 126 final List<File> files = getFilesToProcess(line); 127 final Checker c = createChecker(config, listener); 128 final int numErrs = c.process(files); 129 c.destroy(); 130 System.exit(numErrs); 131 } 132 133 /** 134 * Creates the Checker object. 135 * 136 * @param config the configuration to use 137 * @param nosy the sticky beak to track what happens 138 * @return a nice new fresh Checker 139 */ 140 private static Checker createChecker(Configuration config, 141 AuditListener nosy) 142 { 143 Checker c = null; 144 try { 145 c = new Checker(); 146 147 final ClassLoader moduleClassLoader = 148 Checker.class.getClassLoader(); 149 c.setModuleClassLoader(moduleClassLoader); 150 c.configure(config); 151 c.addListener(nosy); 152 } 153 catch (final Exception e) { 154 System.out.println("Unable to create Checker: " 155 + e.getMessage()); 156 e.printStackTrace(System.out); 157 System.exit(1); 158 } 159 return c; 160 } 161 162 /** 163 * Determines the files to process. 164 * 165 * @param line the command line options specifying what files to process 166 * @return list of files to process 167 */ 168 private static List<File> getFilesToProcess(CommandLine line) 169 { 170 final List<File> files = Lists.newLinkedList(); 171 final String[] remainingArgs = line.getArgs(); 172 for (String element : remainingArgs) { 173 traverse(new File(element), files); 174 } 175 176 if (files.isEmpty()) { 177 System.out.println("Must specify files to process"); 178 usage(); 179 } 180 return files; 181 } 182 183 /** 184 * Create the audit listener 185 * 186 * @param line command line options supplied 187 * @param out the stream to log to 188 * @param closeOut whether the stream should be closed 189 * @return a fresh new <code>AuditListener</code> 190 */ 191 private static AuditListener createListener(CommandLine line, 192 OutputStream out, 193 boolean closeOut) 194 { 195 final String format = 196 line.hasOption("f") ? line.getOptionValue("f") : "plain"; 197 198 AuditListener listener = null; 199 if ("xml".equals(format)) { 200 listener = new XMLLogger(out, closeOut); 201 } 202 else if ("plain".equals(format)) { 203 listener = new DefaultLogger(out, closeOut); 204 } 205 else { 206 System.out.println("Invalid format: (" + format 207 + "). Must be 'plain' or 'xml'."); 208 usage(); 209 } 210 return listener; 211 } 212 213 /** 214 * Loads the configuration file. Will exit if unable to load. 215 * 216 * @param line specifies the location of the configuration 217 * @param props the properties to resolve with the configuration 218 * @return a fresh new configuration 219 */ 220 private static Configuration loadConfig(CommandLine line, 221 Properties props) 222 { 223 try { 224 return ConfigurationLoader.loadConfiguration( 225 line.getOptionValue("c"), new PropertiesExpander(props)); 226 } 227 catch (final CheckstyleException e) { 228 System.out.println("Error loading configuration file"); 229 e.printStackTrace(System.out); 230 System.exit(1); 231 return null; // can never get here 232 } 233 } 234 235 /** Prints the usage information. **/ 236 private static void usage() 237 { 238 final HelpFormatter hf = new HelpFormatter(); 239 hf.printHelp( 240 "java " 241 + Main.class.getName() 242 + " [options] -c <config.xml> file...", 243 OPTS); 244 System.exit(1); 245 } 246 247 /** 248 * Traverses a specified node looking for files to check. Found 249 * files are added to a specified list. Subdirectories are also 250 * traversed. 251 * 252 * @param node the node to process 253 * @param files list to add found files to 254 */ 255 private static void traverse(File node, List<File> files) 256 { 257 if (node.canRead()) { 258 if (node.isDirectory()) { 259 final File[] nodes = node.listFiles(); 260 for (File element : nodes) { 261 traverse(element, files); 262 } 263 } 264 else if (node.isFile()) { 265 files.add(node); 266 } 267 } 268 } 269 270 /** 271 * Loads properties from a File. 272 * @param file the properties file 273 * @return the properties in file 274 */ 275 private static Properties loadProperties(File file) 276 { 277 final Properties properties = new Properties(); 278 FileInputStream fis = null; 279 try { 280 fis = new FileInputStream(file); 281 properties.load(fis); 282 } 283 catch (final IOException ex) { 284 System.out.println("Unable to load properties from file: " 285 + file.getAbsolutePath()); 286 ex.printStackTrace(System.out); 287 System.exit(1); 288 } 289 finally { 290 Utils.closeQuietly(fis); 291 } 292 293 return properties; 294 } 295}