001: ////////////////////////////////////////////////////////////////////////////////
002: // checkstyle: Checks Java source code for adherence to a set of rules.
003: // Copyright (C) 2001-2007 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: ////////////////////////////////////////////////////////////////////////////////
019: package com.puppycrawl.tools.checkstyle;
020:
021: import java.io.File;
022: import java.io.FileInputStream;
023: import java.io.FileNotFoundException;
024: import java.io.FileOutputStream;
025: import java.io.IOException;
026: import java.io.OutputStream;
027: import java.util.List;
028: import java.util.Properties;
029: import java.util.LinkedList;
030:
031: import org.apache.commons.cli.CommandLine;
032: import org.apache.commons.cli.CommandLineParser;
033: import org.apache.commons.cli.HelpFormatter;
034: import org.apache.commons.cli.Options;
035: import org.apache.commons.cli.ParseException;
036: import org.apache.commons.cli.PosixParser;
037:
038: import com.puppycrawl.tools.checkstyle.api.AuditListener;
039: import com.puppycrawl.tools.checkstyle.api.Configuration;
040:
041: import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
042:
043: /**
044: * Wrapper command line program for the Checker.
045: * @author Oliver Burn
046: **/
047: public final class Main {
048: /** the options to the command line */
049: private static final Options OPTS = new Options();
050: static {
051: OPTS.addOption("c", true,
052: "The check configuration file to use.");
053: OPTS.addOption("r", true,
054: "Traverse the directory for source files");
055: OPTS.addOption("o", true,
056: "Sets the output file. Defaults to stdout");
057: OPTS.addOption("p", true, "Loads the properties file");
058: OPTS.addOption("n", true, "Loads the package names file");
059: OPTS
060: .addOption("f", true,
061: "Sets the output format. (plain|xml). Defaults to plain");
062: }
063:
064: /**
065: * Loops over the files specified checking them for errors. The exit code
066: * is the number of errors found in all the files.
067: * @param aArgs the command line arguments
068: **/
069: public static void main(String[] aArgs) {
070: // parse the parameters
071: final CommandLineParser clp = new PosixParser();
072: CommandLine line = null;
073: try {
074: line = clp.parse(OPTS, aArgs);
075: } catch (final ParseException e) {
076: e.printStackTrace();
077: usage();
078: }
079: assert line != null;
080:
081: // setup the properties
082: final Properties props = line.hasOption("p") ? loadProperties(new File(
083: line.getOptionValue("p")))
084: : System.getProperties();
085:
086: // ensure a config file is specified
087: if (!line.hasOption("c")) {
088: System.out.println("Must specify a config XML file.");
089: usage();
090: }
091:
092: final Configuration config = loadConfig(line, props);
093:
094: //Load the set of package names
095: ModuleFactory moduleFactory = null;
096: if (line.hasOption("n")) {
097: moduleFactory = loadPackages(line);
098: }
099:
100: // setup the output stream
101: OutputStream out = null;
102: boolean closeOut = false;
103: if (line.hasOption("o")) {
104: final String fname = line.getOptionValue("o");
105: try {
106: out = new FileOutputStream(fname);
107: closeOut = true;
108: } catch (final FileNotFoundException e) {
109: System.out.println("Could not find file: '" + fname
110: + "'");
111: System.exit(1);
112: }
113: } else {
114: out = System.out;
115: closeOut = false;
116: }
117:
118: final AuditListener listener = createListener(line, out,
119: closeOut);
120: final List files = getFilesToProcess(line);
121: final Checker c = createChecker(config, moduleFactory, listener);
122:
123: final File[] processedFiles = new File[files.size()];
124: files.toArray(processedFiles);
125: final int numErrs = c.process(processedFiles);
126: c.destroy();
127: System.exit(numErrs);
128: }
129:
130: /**
131: * Creates the Checker object.
132: *
133: * @param aConfig the configuration to use
134: * @param aFactory the module factor to use
135: * @param aNosy the sticky beak to track what happens
136: * @return a nice new fresh Checker
137: */
138: private static Checker createChecker(Configuration aConfig,
139: ModuleFactory aFactory, AuditListener aNosy) {
140: Checker c = null;
141: try {
142: c = new Checker();
143: c.setModuleFactory(aFactory);
144: c.configure(aConfig);
145: c.addListener(aNosy);
146: } catch (final Exception e) {
147: System.out.println("Unable to create Checker: "
148: + e.getMessage());
149: e.printStackTrace(System.out);
150: System.exit(1);
151: }
152: return c;
153: }
154:
155: /**
156: * Determines the files to process.
157: *
158: * @param aLine the command line options specifying what files to process
159: * @return list of files to process
160: */
161: private static List getFilesToProcess(CommandLine aLine) {
162: final List files = new LinkedList();
163: if (aLine.hasOption("r")) {
164: final String[] values = aLine.getOptionValues("r");
165: for (int i = 0; i < values.length; i++) {
166: traverse(new File(values[i]), files);
167: }
168: }
169:
170: final String[] remainingArgs = aLine.getArgs();
171: for (int i = 0; i < remainingArgs.length; i++) {
172: files.add(new File(remainingArgs[i]));
173: }
174:
175: if (files.isEmpty()) {
176: System.out.println("Must specify files to process");
177: usage();
178: }
179: return files;
180: }
181:
182: /**
183: * Create the audit listener
184: *
185: * @param aLine command line options supplied
186: * @param aOut the stream to log to
187: * @param aCloseOut whether the stream should be closed
188: * @return a fresh new <code>AuditListener</code>
189: */
190: private static AuditListener createListener(CommandLine aLine,
191: OutputStream aOut, boolean aCloseOut) {
192: final String format = aLine.hasOption("f") ? aLine
193: .getOptionValue("f") : "plain";
194:
195: AuditListener listener = null;
196: if ("xml".equals(format)) {
197: listener = new XMLLogger(aOut, aCloseOut);
198: } else if ("plain".equals(format)) {
199: listener = new DefaultLogger(aOut, aCloseOut);
200: } else {
201: System.out.println("Invalid format: (" + format
202: + "). Must be 'plain' or 'xml'.");
203: usage();
204: }
205: return listener;
206: }
207:
208: /**
209: * Loads the packages, or exists if unable to.
210: *
211: * @param aLine the supplied command line options
212: * @return a fresh new <code>ModuleFactory</code>
213: */
214: private static ModuleFactory loadPackages(CommandLine aLine) {
215: try {
216: return PackageNamesLoader.loadModuleFactory(aLine
217: .getOptionValue("n"));
218: } catch (final CheckstyleException e) {
219: System.out.println("Error loading package names file");
220: e.printStackTrace(System.out);
221: System.exit(1);
222: return null; // never get here
223: }
224: }
225:
226: /**
227: * Loads the configuration file. Will exit if unable to load.
228: *
229: * @param aLine specifies the location of the configuration
230: * @param aProps the properties to resolve with the configuration
231: * @return a fresh new configuration
232: */
233: private static Configuration loadConfig(CommandLine aLine,
234: Properties aProps) {
235: try {
236: return ConfigurationLoader.loadConfiguration(aLine
237: .getOptionValue("c"),
238: new PropertiesExpander(aProps));
239: } catch (final CheckstyleException e) {
240: System.out.println("Error loading configuration file");
241: e.printStackTrace(System.out);
242: System.exit(1);
243: return null; // can never get here
244: }
245: }
246:
247: /** Prints the usage information. **/
248: private static void usage() {
249: final HelpFormatter hf = new HelpFormatter();
250: hf.printHelp("java " + Main.class.getName()
251: + " [options] -c <config.xml> file...", OPTS);
252: System.exit(1);
253: }
254:
255: /**
256: * Traverses a specified node looking for files to check. Found
257: * files are added to a specified list. Subdirectories are also
258: * traversed.
259: *
260: * @param aNode the node to process
261: * @param aFiles list to add found files to
262: */
263: private static void traverse(File aNode, List aFiles) {
264: if (aNode.canRead()) {
265: if (aNode.isDirectory()) {
266: final File[] nodes = aNode.listFiles();
267: for (int i = 0; i < nodes.length; i++) {
268: traverse(nodes[i], aFiles);
269: }
270: } else if (aNode.isFile()) {
271: aFiles.add(aNode);
272: }
273: }
274: }
275:
276: /**
277: * Loads properties from a File.
278: * @param aFile the properties file
279: * @return the properties in aFile
280: */
281: private static Properties loadProperties(File aFile) {
282: final Properties properties = new Properties();
283: try {
284: FileInputStream fis = null;
285: fis = new FileInputStream(aFile);
286: properties.load(fis);
287: fis.close();
288: } catch (final IOException ex) {
289: System.out.println("Unable to load properties from file: "
290: + aFile.getAbsolutePath());
291: ex.printStackTrace(System.out);
292: System.exit(1);
293: }
294: return properties;
295: }
296: }
|