View Javadoc

1   /*
2    * Copyright 2008-2009 smartics, Kronseder & Reiner GmbH
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package de.smartics.exceptions.cli;
17  
18  import java.io.BufferedWriter;
19  import java.io.FileOutputStream;
20  import java.io.IOException;
21  import java.io.OutputStreamWriter;
22  import java.io.Writer;
23  import java.util.List;
24  import java.util.Locale;
25  import java.util.ResourceBundle;
26  
27  import javax.xml.stream.XMLStreamWriter;
28  
29  import org.apache.commons.cli.CommandLine;
30  import org.apache.commons.cli.CommandLineParser;
31  import org.apache.commons.cli.HelpFormatter;
32  import org.apache.commons.cli.OptionBuilder;
33  import org.apache.commons.cli.Options;
34  import org.apache.commons.cli.ParseException;
35  import org.apache.commons.cli.PosixParser;
36  import org.apache.commons.io.IOUtils;
37  import org.apache.commons.lang.StringUtils;
38  
39  import de.smartics.analysis.javadoc.Filter;
40  import de.smartics.analysis.javadoc.filter.ImplementsInterfaceFilter;
41  import de.smartics.analysis.javadoc.util.StringFunction;
42  import de.smartics.exceptions.core.Code;
43  import de.smartics.report.ReportException;
44  import de.smartics.report.conf.DefaultProjectConfiguration;
45  import de.smartics.report.conf.ProjectConfiguration;
46  import de.smartics.report.generator.AbstractXmlReportGenerator;
47  import de.smartics.report.launch.ReportLauncher;
48  
49  /**
50   * Runs the report generator.
51   * 
52   * @author <a href="mailto:robert.reiner@smartics.de">Robert Reiner</a>
53   * @version $Revision:591 $
54   */
55  public class Main
56  {
57    // ********************************* Fields *********************************
58  
59    // --- constants ------------------------------------------------------------
60  
61    /**
62     * The name of the project to collect information for.
63     * <p>
64     * The value of this constant is {@value}.
65     */
66    public static final String PROJECT_NAME = "project-name";
67  
68    /**
69     * The name of the report file to write.
70     * <p>
71     * The value of this constant is {@value}.
72     */
73    public static final String REPORT_FILE = "report-file";
74  
75    /**
76     * The class name of the report generator instance to use.
77     * <p>
78     * The value of this constant is {@value}.
79     */
80    public static final String REPORT_GENERATOR = "report-generator";
81  
82    /**
83     * The encoding of the report documentation.
84     * <p>
85     * The value of this constant is {@value}.
86     */
87    public static final String REPORT_ENCODING = "report-encoding";
88  
89    /**
90     * The semicolon separated excluded directories. Use Ant syntax.
91     * <p>
92     * The value of this constant is {@value}.
93     */
94    public static final String REPORT_EXCLUDES = "report-excludes";
95  
96    /**
97     * The semicolon separated included directories. Use Ant syntax.
98     * <p>
99     * The value of this constant is {@value}.
100    */
101   public static final String REPORT_INCLUDES = "report-includes";
102 
103   /**
104    * The encoding of the Java source files.
105    * <p>
106    * The value of this constant is {@value}.
107    */
108   public static final String JAVA_SOURCE_ENCODING = "java-source-encoding";
109 
110   /**
111    * The semicolon separated source root directories.
112    * <p>
113    * The value of this constant is {@value}.
114    */
115   public static final String JAVA_SOURCE_PATH = "java-source-path";
116 
117   /**
118    * The semicolon separated class root directories. If this is set, the default
119    * class path specified by <code>java.class.path</code> will not be used. The
120    * default class path is used if this property is not specified.
121    * <p>
122    * The value of this constant is {@value}.
123    */
124   public static final String JAVA_CLASS_PATH = "java-class-path";
125 
126   /**
127    * The link reference to the CSS file to add to the head element of the
128    * generated HTML document.
129    * <p>
130    * The value of this constant is {@value}.
131    */
132   public static final String CSS_FILE = "css-file";
133 
134   // --- members --------------------------------------------------------------
135 
136   // ****************************** Initializer *******************************
137 
138   // ****************************** Constructors ******************************
139 
140   /**
141    * Default constructor.
142    */
143   public Main()
144   {
145   }
146 
147   // ****************************** Inner Classes *****************************
148 
149   // ********************************* Methods ********************************
150 
151   // --- init -----------------------------------------------------------------
152 
153   // --- get&set --------------------------------------------------------------
154 
155   // --- business -------------------------------------------------------------
156 
157   /**
158    * Runs with system properties. See constants in this class for options.
159    * 
160    * @throws ReportException ReportException on any problem generating the
161    *           report.
162    * @throws ParseException
163    * @see de.smartics.exceptions.report.Main#run(de.smartics.exceptions.report.core.ExceptionCodeProjectConfiguration)
164    */
165   public int run(final String... args) throws ReportException
166   {
167     final Options options = createOptions();
168     try
169     {
170       final CommandLine commandLine = createCommandLine(options, args);
171       final ProjectConfiguration<XMLStreamWriter> config =
172           createConfig(commandLine);
173       run(config);
174       System.out.println("Report '" + commandLine.getOptionValue(REPORT_FILE)
175           + "' written successfully.");
176       return 0;
177     }
178     catch (final ParseException e)
179     {
180       final HelpFormatter formatter = new HelpFormatter();
181       System.err.println(e.getLocalizedMessage());
182       formatter.printHelp("java " + getClass().getName(), options);
183       return -1;
184     }
185   }
186 
187   /**
188    * Creates the report configuration to control the report generation from the
189    * information provided by the command line.
190    * 
191    * @param commandLine the user's command line arguments to control the
192    *          generation of the report.
193    * @return the project configuration to control the report generation.
194    */
195   private static ProjectConfiguration<XMLStreamWriter> createConfig(
196       final CommandLine commandLine)
197   {
198     final List<String> classRootDirectoryNames =
199         readClassRootDirectoryNames(commandLine);
200     final String sourcePath = commandLine.getOptionValue(JAVA_SOURCE_PATH);
201     final List<String> sourceRootDirectoryNames =
202         StringFunction.split(sourcePath, ";");
203 
204     final String includesString =
205         commandLine.getOptionValue(REPORT_INCLUDES, "**");
206     final List<String> includes = StringFunction.split(includesString, ",");
207     final String excludesString =
208         commandLine.getOptionValue(REPORT_EXCLUDES);
209     final List<String> excludes = StringFunction.split(excludesString, ",");
210 
211     final DefaultProjectConfiguration.Builder<XMLStreamWriter> builder =
212         new DefaultProjectConfiguration.Builder<XMLStreamWriter>(commandLine
213             .getOptionValue(PROJECT_NAME));
214 
215     builder.setIncludes(includes);
216     builder.setExcludes(excludes);
217     builder.setSourceEncoding(commandLine.getOptionValue(JAVA_SOURCE_ENCODING,
218         "UTF-8"));
219     builder.setClassRootDirectoryNames(classRootDirectoryNames);
220     builder.setSourceRootDirectoryNames(sourceRootDirectoryNames);
221 
222     builder.setReportEncoding(commandLine.getOptionValue(REPORT_ENCODING,
223         "UTF-8"));
224     builder.setReporter(commandLine.getOptionValue(REPORT_GENERATOR,
225         "de.smartics.exceptions.report.generator.HtmlReportGenerator"));
226     builder.setReport(commandLine.getOptionValue(REPORT_FILE));
227     builder.setStyleSheet(commandLine.getOptionValue(CSS_FILE));
228 
229     final Filter filter = new ImplementsInterfaceFilter(Code.class);
230     builder.setFilter(filter);
231 
232     final ResourceBundle bundle =
233         ResourceBundle.getBundle(
234             "de/smartics/exceptions/report/ExceptionCodeReportBundle",
235             Locale.ENGLISH, Main.class.getClassLoader()); // NOPMD
236     builder.setBundle(bundle);
237 
238     final ProjectConfiguration<XMLStreamWriter> config = builder.build();
239     return config;
240   }
241 
242   /**
243    * Reads the collection of class path root directories.
244    * 
245    * @return the collection of class path root directories.
246    */
247   private static List<String> readClassRootDirectoryNames(
248       final CommandLine commandLine)
249   {
250     String classPath = commandLine.getOptionValue(JAVA_CLASS_PATH);
251     if (StringUtils.isBlank(classPath))
252     {
253       classPath = System.getProperty("java.class.path");
254     }
255     final List<String> classRootDirectoryNames =
256         StringFunction.split(classPath, ";");
257     return classRootDirectoryNames;
258   }
259 
260   /**
261    * Delegates call to {@link #run(ProjectConfiguration,XMLStreamWriter)} .
262    * 
263    * @param config the configuration to control the discovery process.
264    * @throws ReportException on any problem generating the report.
265    * @see #run(ProjectConfiguration,XMLStreamWriter)
266    */
267   public void run(final ProjectConfiguration<XMLStreamWriter> config)
268       throws ReportException
269   {
270     final String reportEncoding = config.getReportEncoding();
271     final String report = config.getReport();
272 
273     Writer writer = null;
274     try
275     {
276       writer =
277           new BufferedWriter(new OutputStreamWriter(
278               new FileOutputStream(report), reportEncoding));
279       final XMLStreamWriter output =
280           AbstractXmlReportGenerator.createOutput(writer);
281       run(config, output);
282     }
283     catch (final ReportException e) // NOPMD
284     {
285       throw e;
286     }
287     catch (final Exception e)
288     {
289       throw new ReportException(e);
290     }
291     finally
292     {
293       IOUtils.closeQuietly(writer);
294     }
295   }
296 
297   /**
298    * Runs the reporting with the given configuration and writer.
299    * 
300    * @param configuration the configuration to control the discovery process.
301    * @param output the writer to write to. This overrules the report file in the
302    *          configuration. If the writer is <code>null</code> the report file
303    *          is required to be set.
304    * @throws ReportException on any problem generating the report.
305    */
306   public void run(
307       final ProjectConfiguration<XMLStreamWriter> configuration,
308       final XMLStreamWriter output) throws ReportException
309   {
310     final ReportLauncher<XMLStreamWriter> launcher =
311         new ReportLauncher<XMLStreamWriter>("exceptioncodes-cli");
312     launcher.launch(configuration, output);
313   }
314 
315   // ... helper ...............................................................
316 
317   /**
318    * Parses the command line arguments and returns the parsed result.
319    * 
320    * @param args the command line arguments to parse.
321    * @return the command line information.
322    * @throws ParseException if the command line cannot be parsed.
323    */
324   private static CommandLine createCommandLine(
325       final Options options,
326       final String[] args) throws ParseException
327   {
328     final CommandLineParser parser = new PosixParser();
329     final CommandLine cmd = parser.parse(options, args);
330     return cmd;
331   }
332 
333   /**
334    * Creates the options for the CLI.
335    * 
336    * @return the requested options.
337    */
338   private static Options createOptions()
339   {
340     final Options options = new Options();
341 
342     options.addOption(OptionBuilder.withLongOpt(JAVA_SOURCE_ENCODING)
343         .withArgName("encoding").hasArg().withDescription(
344             "the encoding of the Java source files.").create("se"));
345     options.addOption(OptionBuilder.withLongOpt(JAVA_SOURCE_PATH).withArgName(
346         "path1;path2").hasArg().withDescription(
347         "the semicolon separated source root directories.").isRequired()
348         .create("path"));
349     options.addOption(OptionBuilder.withLongOpt(JAVA_CLASS_PATH).withArgName(
350         "path1;path2").hasArg().withDescription(
351         "the semicolon separated classpath root directories.").create("cp"));
352     options.addOption(OptionBuilder.withLongOpt(REPORT_INCLUDES).withArgName(
353         "path;path").hasArg().withDescription(
354         "the semicolon separated included directories. Use Ant syntax.")
355         .create("i"));
356     options.addOption(OptionBuilder.withLongOpt(REPORT_EXCLUDES).withArgName(
357         "path;path").hasArg().withDescription(
358         "the semicolon separated excluded directories. Use Ant syntax.")
359         .create("e"));
360     options.addOption(OptionBuilder.withLongOpt(REPORT_ENCODING).withArgName(
361         "encoding").hasArg().withDescription(
362         "the encoding of the report documentation.").create("c"));
363     options.addOption(OptionBuilder.withLongOpt(REPORT_GENERATOR).withArgName(
364         "class").hasArg().withDescription(
365         "the class name of the report generator instance to use.").create("g"));
366     options.addOption(OptionBuilder.withLongOpt(REPORT_FILE)
367         .withArgName("file").hasArg().withDescription(
368             "the name of the report file to write.").isRequired().create("f"));
369     options.addOption(OptionBuilder.withLongOpt(PROJECT_NAME).withArgName(
370         "project-name").hasArg().withDescription(
371         "the name of the project to collect information for.").isRequired()
372         .create("p"));
373     options
374         .addOption(OptionBuilder
375             .withLongOpt(CSS_FILE)
376             .withArgName("css-file")
377             .hasArg()
378             .withDescription(
379                 "the link reference to the CSS file to add to the head element of the generated HTML document.")
380             .isRequired().create("s"));
381     return options;
382   }
383 
384   // --- object basics --------------------------------------------------------
385 
386   /**
387    * Launches the program.
388    * 
389    * @param args the program arguments.
390    * @throws IOException on any problem reading from or writing to streams.
391    * @throws ReportException on any problem generating the report.
392    */
393   public static void main(final String... args) throws Exception,
394       ReportException
395   {
396     final Main main = new Main();
397     final int returnCode = main.run(args);
398     System.exit(returnCode);
399   }
400 }