View Javadoc

1   /*
2    * Copyright 2007-2011 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) // NOPMD
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()); // NOPMD
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 = commandLine.getOptionValue(REPORT_EXCLUDES);
208     final List<String> excludes = StringFunction.split(excludesString, ",");
209 
210     final DefaultProjectConfiguration.Builder<XMLStreamWriter> builder =
211         new DefaultProjectConfiguration.Builder<XMLStreamWriter>(
212             commandLine.getOptionValue(PROJECT_NAME));
213 
214     builder.setIncludes(includes);
215     builder.setExcludes(excludes);
216     builder.setSourceEncoding(commandLine.getOptionValue(JAVA_SOURCE_ENCODING,
217         "UTF-8"));
218     builder.setClassRootDirectoryNames(classRootDirectoryNames);
219     builder.setSourceRootDirectoryNames(sourceRootDirectoryNames);
220 
221     builder.setReportEncoding(commandLine.getOptionValue(REPORT_ENCODING,
222         "UTF-8"));
223     builder.setReporter(commandLine.getOptionValue(REPORT_GENERATOR,
224         "de.smartics.exceptions.report.generator.HtmlReportGenerator"));
225     builder.setReport(commandLine.getOptionValue(REPORT_FILE));
226     builder.setStyleSheet(commandLine.getOptionValue(CSS_FILE));
227 
228     final Filter filter = new ImplementsInterfaceFilter(Code.class);
229     builder.setFilter(filter);
230 
231     final ResourceBundle bundle =
232         ResourceBundle.getBundle(
233             "de/smartics/exceptions/report/ExceptionCodeReportBundle",
234             Locale.ENGLISH, Main.class.getClassLoader()); // NOPMD
235     builder.setBundle(bundle);
236 
237     final ProjectConfiguration<XMLStreamWriter> config = builder.build();
238     return config;
239   }
240 
241   /**
242    * Reads the collection of class path root directories.
243    *
244    * @return the collection of class path root directories.
245    */
246   private static List<String> readClassRootDirectoryNames(
247       final CommandLine commandLine)
248   {
249     String classPath = commandLine.getOptionValue(JAVA_CLASS_PATH);
250     if (StringUtils.isBlank(classPath))
251     {
252       classPath = System.getProperty("java.class.path");
253     }
254     final List<String> classRootDirectoryNames =
255         StringFunction.split(classPath, ";");
256     return classRootDirectoryNames;
257   }
258 
259   /**
260    * Delegates call to {@link #run(ProjectConfiguration,XMLStreamWriter)} .
261    *
262    * @param config the configuration to control the discovery process.
263    * @throws ReportException on any problem generating the report.
264    * @see #run(ProjectConfiguration,XMLStreamWriter)
265    */
266   public void run(final ProjectConfiguration<XMLStreamWriter> config)
267     throws ReportException
268   {
269     final String reportEncoding = config.getReportEncoding();
270     final String report = config.getReport();
271 
272     Writer writer = null;
273     try
274     {
275       writer =
276           new BufferedWriter(new OutputStreamWriter(
277               new FileOutputStream(report), reportEncoding));
278       final XMLStreamWriter output =
279           AbstractXmlReportGenerator.createOutput(writer);
280       run(config, output);
281     }
282     catch (final ReportException e) // NOPMD
283     {
284       throw e;
285     }
286     catch (final Exception e)
287     {
288       throw new ReportException(e);
289     }
290     finally
291     {
292       IOUtils.closeQuietly(writer);
293     }
294   }
295 
296   /**
297    * Runs the reporting with the given configuration and writer.
298    *
299    * @param configuration the configuration to control the discovery process.
300    * @param output the writer to write to. This overrules the report file in the
301    *          configuration. If the writer is <code>null</code> the report file
302    *          is required to be set.
303    * @throws ReportException on any problem generating the report.
304    */
305   public void run(final ProjectConfiguration<XMLStreamWriter> configuration,
306       final XMLStreamWriter output) throws ReportException
307   {
308     final ReportLauncher<XMLStreamWriter> launcher =
309         new ReportLauncher<XMLStreamWriter>("exceptioncodes-cli");
310     launcher.launch(configuration, output);
311   }
312 
313   // ... helper ...............................................................
314 
315   /**
316    * Parses the command line arguments and returns the parsed result.
317    *
318    * @param args the command line arguments to parse.
319    * @return the command line information.
320    * @throws ParseException if the command line cannot be parsed.
321    */
322   private static CommandLine createCommandLine(final Options options,
323       final String[] args) throws ParseException
324   {
325     final CommandLineParser parser = new PosixParser();
326     final CommandLine cmd = parser.parse(options, args);
327     return cmd;
328   }
329 
330   /**
331    * Creates the options for the CLI.
332    *
333    * @return the requested options.
334    */
335   private static Options createOptions()
336   {
337     final Options options = new Options();
338 
339     options
340         .addOption(OptionBuilder.withLongOpt(JAVA_SOURCE_ENCODING)
341             .withArgName("encoding").hasArg()
342             .withDescription("the encoding of the Java source files.")
343             .create("se"));
344     options.addOption(OptionBuilder.withLongOpt(JAVA_SOURCE_PATH)
345         .withArgName("path1;path2").hasArg()
346         .withDescription("the semicolon separated source root directories.")
347         .isRequired().create("path"));
348     options.addOption(OptionBuilder.withLongOpt(JAVA_CLASS_PATH)
349         .withArgName("path1;path2").hasArg()
350         .withDescription("the semicolon separated classpath root directories.")
351         .create("cp"));
352     options.addOption(OptionBuilder
353         .withLongOpt(REPORT_INCLUDES)
354         .withArgName("path;path")
355         .hasArg()
356         .withDescription(
357             "the semicolon separated included directories. Use Ant syntax.")
358         .create("i"));
359     options.addOption(OptionBuilder
360         .withLongOpt(REPORT_EXCLUDES)
361         .withArgName("path;path")
362         .hasArg()
363         .withDescription(
364             "the semicolon separated excluded directories. Use Ant syntax.")
365         .create("e"));
366     options.addOption(OptionBuilder.withLongOpt(REPORT_ENCODING)
367         .withArgName("encoding").hasArg()
368         .withDescription("the encoding of the report documentation.")
369         .create("c"));
370     options.addOption(OptionBuilder
371         .withLongOpt(REPORT_GENERATOR)
372         .withArgName("class")
373         .hasArg()
374         .withDescription(
375             "the class name of the report generator instance to use.")
376         .create("g"));
377     options.addOption(OptionBuilder.withLongOpt(REPORT_FILE)
378         .withArgName("file").hasArg()
379         .withDescription("the name of the report file to write.").isRequired()
380         .create("f"));
381     options.addOption(OptionBuilder.withLongOpt(PROJECT_NAME)
382         .withArgName("project-name").hasArg()
383         .withDescription("the name of the project to collect information for.")
384         .isRequired().create("p"));
385     options
386         .addOption(OptionBuilder
387             .withLongOpt(CSS_FILE)
388             .withArgName("css-file")
389             .hasArg()
390             .withDescription(
391                 "the link reference to the CSS file to add to the head element of the generated HTML document.")
392             .isRequired().create("s"));
393     return options;
394   }
395 
396   // --- object basics --------------------------------------------------------
397 
398   /**
399    * Launches the program.
400    *
401    * @param args the program arguments.
402    * @throws IOException on any problem reading from or writing to streams.
403    * @throws ReportException on any problem generating the report.
404    */
405   public static void main(final String... args) throws Exception,
406     ReportException
407   {
408     final Main main = new Main();
409     final int returnCode = main.run(args);
410     System.exit(returnCode); // NOPMD
411   }
412 }