View Javadoc

1   /*
2    * Copyright 2007-2013 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.maven.exceptions;
17  
18  import java.io.File;
19  import java.util.ArrayList;
20  import java.util.List;
21  import java.util.Locale;
22  import java.util.ResourceBundle;
23  
24  import org.apache.maven.artifact.DependencyResolutionRequiredException;
25  import org.apache.maven.doxia.sink.Sink;
26  import org.apache.maven.plugin.logging.Log;
27  import org.apache.maven.reporting.MavenReportException;
28  
29  import de.smartics.exceptions.report.ReportBuilder;
30  import de.smartics.exceptions.report.ReportConfiguration;
31  import de.smartics.exceptions.report.data.InMemoryExceptionCodesReport;
32  import de.smartics.exceptions.report.data.ProjectConfiguration;
33  import de.smartics.exceptions.report.data.ReportProblem;
34  import de.smartics.exceptions.report.generator.ReportGenerator;
35  import de.smartics.exceptions.report.utils.StringFunction;
36  import de.smartics.maven.exceptions.conf.ConfigUtils;
37  import de.smartics.maven.exceptions.conf.DefaultProjectConfiguration;
38  import de.smartics.maven.exceptions.runtime.ProjectClassLoader;
39  import de.smartics.maven.util.PathUtils;
40  
41  /**
42   * Generates exception code documentation.
43   *
44   * @goal report
45   * @phase site
46   * @requiresProject
47   * @requiresDependencyResolution
48   * @description Generates documentation for the exception codes used in the
49   *              project.
50   */
51  public class ExceptionCodeReport extends AbstractElementReport
52  {
53    // ********************************* Fields *********************************
54  
55    // --- constants ------------------------------------------------------------
56  
57    // --- members --------------------------------------------------------------
58  
59    // ... plugin infrastructure ................................................
60  
61    // ... references to artifacts of other plugins .............................
62  
63    // ... own configuration parameters .........................................
64  
65    // ... basics
66  
67    /**
68     * Specifies the name of the file to save the properties report (without
69     * extension).
70     *
71     * @parameter expression="${properties.report.fileName}" default-value=
72     *            "exception-codes-report"
73     */
74    private String outputName;
75  
76    // ... sources
77  
78    /**
79     * Specifies additional root directories of source files to consider. Please
80     * note that source file archives found at the same location as the class file
81     * archives are automatically visible. The source archive is required to have
82     * the suffix <code>-sources</code> in front of the extension and only jar-
83     * and zip-archives are supported.
84     *
85     * @parameter expression="${exceptioncodes.additionalSources}"
86     */
87    private String additionalSources;
88  
89    /**
90     * Specifies the encoding the sources will be read.
91     *
92     * @parameter expression="${exceptioncodes.build.sourceEncoding}"
93     *            default-value="UTF-8"
94     */
95    private String sourceEncoding;
96  
97    /**
98     * Specifies the Java version of the sources.
99     *
100    * @parameter expression="${exceptioncodes.build.sourceVersion}"
101    *            default-value="1.5"
102    */
103   private String sourceVersion;
104 
105   /**
106    * Specifies the names filter of the source and class files to be included in
107    * the reports. The path starts at the source path root, not at the
108    * project root.
109    *
110    * @parameter expression="${exceptioncodes.includes}"
111    * @since 1.0
112    */
113   @SuppressWarnings("rawtypes")
114   private ArrayList includes; // NOPMD
115 
116   /**
117    * Specifies the names filter of the source and class files to be excluded
118    * from the reports. The path starts at the source path root, not at the
119    * project root.
120    *
121    * @parameter expression="${exceptioncodes.excludes}"
122    * @since 1.0
123    */
124   @SuppressWarnings("rawtypes")
125   private ArrayList excludes; // NOPMD
126 
127   // ... report
128 
129   /**
130    * Specifies the name of the report generator instance. This class has to
131    * implement <code>de.smartics.exceptions.report.ReportGenerator</code>. <br/>
132    * Currently three implementations are provided:
133    * <code>de.smartics.maven.exceptions.report.PackageSortedSinkReportGenerator</code>,
134    * <code>de.smartics.maven.exceptions.report.CodeSortedSinkReportGenerator</code>,
135    * and <code>de.smartics.maven.exceptions.report.ComponentCodeSortedSinkReportGenerator</code>. <br/>
136    *
137    * @parameter expression="${exceptioncodes.report.generator}" default-value=
138    *            "de.smartics.maven.exceptions.report.PackageSortedSinkReportGenerator"
139    */
140   private String reportGenerator;
141 
142   /**
143    * Specifies the encoding the report will be written.
144    *
145    * @parameter expression="${exceptioncodes.report.encoding}"
146    *            default-value="UTF-8"
147    */
148   private String reportEncoding;
149 
150   /**
151    * A simple flag to skip the generation of the report. If set on the command
152    * line use <code>-Dexceptioncodes.skip</code>.
153    *
154    * @parameter expression="${exceptioncodes.skip}" default-value="false"
155    * @since 1.0
156    */
157   private boolean skip;
158 
159   // ****************************** Initializer *******************************
160 
161   // ****************************** Constructors ******************************
162 
163   // ****************************** Inner Classes *****************************
164 
165   // ********************************* Methods ********************************
166 
167   // --- init -----------------------------------------------------------------
168 
169   // --- get&set --------------------------------------------------------------
170 
171   // ... plugin metadata ......................................................
172 
173   // ... plugin infrastructure ................................................
174 
175   /**
176    * {@inheritDoc}
177    *
178    * @see org.apache.maven.reporting.AbstractMavenReport#getOutputName()
179    */
180   public String getOutputName()
181   {
182     return outputName;
183   }
184 
185   // --- business -------------------------------------------------------------
186 
187   @Override
188   public void executeReport(final Locale locale) throws MavenReportException
189   {
190     if (skip)
191     {
192       final Log log = getLog();
193       if (log.isInfoEnabled())
194       {
195         log.info("Report '" + getName(locale) + "' skipped.");
196       }
197       return;
198     }
199 
200     try
201     {
202       final ProjectConfiguration<Sink> projectConfig =
203           createProjectConfiguration(locale);
204       final ReportConfiguration reportConfig =
205           createReportConfiguration(projectConfig);
206 
207       final ReportBuilder builder = ReportBuilder.create(reportConfig);
208       final InMemoryExceptionCodesReport report =
209           new InMemoryExceptionCodesReport();
210       builder.reportTo(report);
211 
212       if (report.hasProblems())
213       {
214         final String message = toString(report.getProblems());
215         getLog().error(message);
216         throw new MavenReportException(
217             "Problems encountered while generating report.\n"
218                 + " Please refer to error log for details.");
219       }
220 
221       final ReportGenerator<Sink> generator =
222           projectConfig.getReporterInstance();
223       generator.writeReport(projectConfig, report);
224     }
225     catch (final Exception e)
226     {
227       e.printStackTrace();
228       throw new MavenReportException(
229           "Cannot generate " + getName(locale) + ".", e);
230     }
231   }
232 
233   private static String toString(final List<ReportProblem> problems)
234   {
235     final StringBuilder buffer = new StringBuilder(512);
236 
237     for (final ReportProblem problem : problems)
238     {
239       buffer.append(problem).append('\n');
240     }
241 
242     return buffer.toString();
243   }
244 
245   @SuppressWarnings("unchecked")
246   private ProjectConfiguration<Sink> createProjectConfiguration(
247       final Locale locale) throws DependencyResolutionRequiredException
248   {
249     final List<String> classRootDirectoryNames =
250         project.getCompileClasspathElements();
251 
252     final List<String> sourceRootDirectoryNames = new ArrayList<String>();
253     sourceRootDirectoryNames.addAll(StringFunction.split(
254         this.additionalSources, ","));
255     sourceRootDirectoryNames.addAll(project.getCompileSourceRoots());
256     sourceRootDirectoryNames.addAll(ConfigUtils
257         .discoverSourceJars(classRootDirectoryNames));
258     final List<String> toolClassPath = PathUtils.toClassPath(pluginArtifacts);
259 
260     final Log log = getLog();
261     if (log.isDebugEnabled())
262     {
263       log.debug("Tool class path: " + toolClassPath);
264       log.debug("Class roots    : " + classRootDirectoryNames);
265       log.debug("Source roots   : " + sourceRootDirectoryNames);
266     }
267 
268     final DefaultProjectConfiguration.Builder<Sink> builder =
269         new DefaultProjectConfiguration.Builder<Sink>(project.getName());
270 
271     builder.setIncludes(includes);
272     builder.setExcludes(excludes);
273     builder.setSourceEncoding(this.sourceEncoding);
274     builder.setSourceVersion(this.sourceVersion);
275     builder.setToolClassPath(toolClassPath);
276     builder.setClassRootDirectoryNames(classRootDirectoryNames);
277     builder.setSourceRootDirectoryNames(sourceRootDirectoryNames);
278     builder.setJavadocDir(javadocDir);
279 
280     builder.setReportEncoding(this.reportEncoding);
281     builder.setReporter(this.reportGenerator);
282     builder.setReport(new File(this.outputDirectory.getAbsolutePath(),
283         outputName).getPath());
284 
285     final ResourceBundle bundle = getBundle(locale);
286     builder.setBundle(bundle);
287 
288     final ProjectConfiguration<Sink> config = builder.build();
289 
290     final Sink sink = getSink();
291     final ReportGenerator<Sink> generator = config.getReporterInstance();
292     generator.setOutput(sink);
293 
294     return config;
295   }
296 
297   private ReportConfiguration createReportConfiguration(
298       final ProjectConfiguration<Sink> projectConfig)
299   {
300     final ReportConfiguration reportConfig = new ReportConfiguration();
301     reportConfig.setEncoding(this.sourceEncoding);
302     final ProjectClassLoader classLoader =
303         new ProjectClassLoader(Thread.currentThread().getContextClassLoader(),
304             projectConfig.getClassRootDirectoryNames());
305     reportConfig.setProjectClassLoader(classLoader);
306     for (final String name : projectConfig.getSourceRootDirectoryNames())
307     {
308       final File file = new File(name);
309       reportConfig.addSourceTree(file);
310     }
311 
312     reportConfig.addIncludes(projectConfig.getIncludes());
313     reportConfig.addExcludes(projectConfig.getExcludes());
314 
315     return reportConfig;
316   }
317 
318   @Override
319   protected String getBundleName()
320   {
321     return "de/smartics/exceptions/report/ExceptionCodeReportBundle";
322   }
323 
324   // --- object basics --------------------------------------------------------
325 
326 }