1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39 package net.objectlab.qalab.m2.report;
40
41 import java.io.File;
42 import java.io.IOException;
43 import java.util.Locale;
44 import java.util.Properties;
45 import java.util.ResourceBundle;
46 import java.io.FileInputStream;
47 import java.io.InputStream;
48
49 import net.objectlab.qalab.m2.BuildStatChartMojo;
50 import net.objectlab.qalab.m2.QALabStatAllMergeMojo;
51 import net.objectlab.qalab.m2.util.Utils;
52 import net.objectlab.qalab.m2.util.XmlTransformer;
53
54 import org.apache.maven.plugin.MojoExecutionException;
55 import org.apache.maven.project.MavenProject;
56 import org.apache.maven.reporting.AbstractMavenReport;
57 import org.apache.maven.reporting.MavenReportException;
58
59 import org.codehaus.doxia.site.renderer.SiteRenderer;
60 import javax.xml.transform.TransformerException;
61
62 /**
63 * *** IMPORTANT USE this Report as part of your reporting section, it merges the data in qalab.xml,
64 * creates the charts and generates the chart report, ensure that this report is run AFTER Checkstyle,
65 * pmd, Findbugs, Cobertura, etc.
66 *
67 * @author Benoit Xhenseval.
68 * @goal report-merge-chart
69 * @phase deploy
70 */
71 public class MergeAndChartReport extends AbstractMavenReport {
72
73
74 /**
75 * The input file generated by Checkstyle.
76 *
77 * @parameter expression="${project.build.directory}/checkstyle-result.xml"
78 */
79 private File checkstyleInputFile = null;
80
81 /**
82 * The fully qualified class name for the handler for Checkstyle.
83 *
84 * @parameter default-value="net.objectlab.qalab.parser.CheckstyleStatMerge"
85 */
86 private String checkstyleHandler;
87
88 /**
89 * The input file generated by PMD.
90 *
91 * @parameter expression="${project.build.directory}/pmd.xml"
92 */
93 private File pmdInputFile = null;
94
95 /**
96 * The fully qualified class name for the handler for PMD.
97 *
98 * @parameter default-value="net.objectlab.qalab.parser.PMDStatMerge"
99 */
100 private String pmdHandler;
101
102 /**
103 * The input file generated by PMD CPD.
104 *
105 * @parameter expression="${project.build.directory}/cpd.xml"
106 */
107 private File pmdCpdInputFile = null;
108
109 /**
110 * The fully qualified class name for the handler for PMD CPD.
111 *
112 * @parameter default-value="net.objectlab.qalab.parser.PMDCPDStatMerge"
113 */
114 private String pmdCpdHandler;
115
116 /**
117 * The input file generated by FindBugs.
118 *
119 * @parameter expression="${project.build.directory}/findbugs.xml"
120 */
121 private File findbugsInputFile = null;
122
123 /**
124 * The fully qualified class name for the handler for FindBugs.
125 *
126 * @parameter default-value="net.objectlab.qalab.parser.FindBugsStatMerge"
127 */
128 private String findbugsHandler;
129
130 /**
131 * The input file generated by Simian.
132 *
133 * @parameter expression="${project.build.directory}/simian-raw-report.xml"
134 */
135 private File simianInputFile = null;
136
137 /**
138 * The fully qualified class name for the handler for Simian.
139 *
140 * @parameter default-value="net.objectlab.qalab.parser.SimianStatMerge"
141 */
142 private String simianHandler;
143
144 /**
145 * The input file generated by Cobertura.
146 *
147 * @parameter expression="${project.reporting.outputDirectory}/cobertura/coverage.xml"
148 */
149 private File coberturaInputFile = null;
150
151 /**
152 * The fully qualified class name for the handler for Cobertura Line.
153 *
154 * @parameter default-value="net.objectlab.qalab.parser.CoberturaLineStatMerge"
155 */
156 private String coberturaLineHandler;
157
158 /**
159 * The fully qualified class name for the handler for Cobertura Branch.
160 *
161 * @parameter default-value="net.objectlab.qalab.parser.CoberturaBranchStatMerge"
162 */
163 private String coberturaBranchHandler;
164
165 /**
166 * If true then any debug logging output will be suppressed.
167 *
168 * @parameter default-value=false
169 */
170 private boolean quiet = false;
171
172 /**
173 * If true then use ONLY DATE for timestamp (use in conjunction action
174 * replace).
175 *
176 * @parameter default-value=true
177 */
178 private boolean dateOnly = true;
179
180 /**
181 * The directory where the source code is.
182 *
183 * @parameter expression="${project.build.sourceDirectory}"
184 */
185 private String srcDir;
186
187 /**
188 * The timestamp for the stats.
189 *
190 * @parameter
191 */
192 private String mergerTimeStamp;
193
194 /**
195 * An exporter class name.
196 *
197 * @parameter default-value="net.objectlab.qalab.exporter.QALabXMLExporter"
198 */
199 private String exporterClassName = "net.objectlab.qalab.exporter.QALabXMLExporter";
200
201 /**
202 * The properties file to use instead of setting them one by one.
203 *
204 * @parameter
205 */
206 private File propertiesFile;
207
208 /**
209 * the loaded properties from the properties file declared above.
210 */
211 private Properties theProperties = null;
212
213
214
215 /**
216 * The directory where the generated html report will go.
217 *
218 * @parameter expression="${project.reporting.outputDirectory}/qalab"
219 * @required
220 */
221 private File chartOutputDirectory;
222
223 /**
224 * The Chart chartWidth.
225 *
226 * @parameter default-value=750
227 */
228 private int chartWidth;
229
230 /**
231 * The Chart chartHeight.
232 *
233 * @parameter default-value=500
234 */
235 private int chartHeight;
236
237 /**
238 * If the movingAverage is <= 0 then there is no moving average, otherwise
239 * it shows the average based on the last n points, where n is the value of
240 * this field.
241 *
242 * @parameter default-value=0
243 */
244 private int movingAverage = 0;
245
246 /**
247 * If true then generate a summary chart only.
248 *
249 * @parameter default-value=false
250 */
251 private boolean chartSummaryOnly = false;
252
253 /**
254 * Statistic type to appear on summary chart, defaulted to
255 * 'checkstyle,pmd,findbugs,simian,pmd-cpd'.
256 *
257 * @parameter default-value="checkstyle,pmd,findbugs,simian,pmd-cpd"
258 */
259 private String summaryTypes = "checkstyle,pmd,findbugs,simian,pmd-cpd";
260
261 /**
262 * File prefix for the charts (e.g. cobertura-) Default empty.
263 *
264 * @parameter default-value=""
265 */
266 private String chartFilePrefix = "";
267
268 /**
269 * X Axis Title
270 *
271 * @parameter default-value="Date"
272 */
273 private String xAxisTitle = "Date";
274
275 /**
276 * Y Axis Title
277 *
278 * @parameter default-value="Violation Count / Coverage Percent"
279 */
280 private String yAxisTitle = "Violation Count / Coverage Percent";
281
282 /**
283 * X Axis Title for Summary
284 *
285 * @parameter default-value="Date"
286 */
287 private String xAxisSummaryTitle = "Date";
288
289 /**
290 * Y Axis Title for Summary
291 *
292 * @parameter default-value="Violation Count"
293 */
294 private String yAxisSummaryTitle = "Violation Count";
295
296
297
298 /**
299 * The generated qalab.xml file.
300 *
301 * @parameter expression="${project.basedir}/qalab.xml"
302 */
303 private File qalabFile = null;
304
305 /**
306 * The xslt stylesheet bundled, either qalab-chart-xdoc.xsl or
307 * qalab-chart-html.xsl.
308 *
309 * @parameter default-value="qalab-chart-xdoc.xsl";
310 */
311 private String chartBundledXsl = null;
312
313 /**
314 * The directory where the generated xdoc/html report will go.
315 *
316 * @parameter expression="${project.build.directory}/generated-site/xdoc/qalab/"
317 * @required
318 */
319 private String outputDirectory;
320
321 /**
322 * Statistic types, defaulted to
323 * 'checkstyle,pmd,pmd-cpd,findbugs,simian,cobertura-line,cobertura-branch'.
324 *
325 * @parameter default-value="checkstyle,pmd,pmd-cpd,findbugs,simian,cobertura-line,cobertura-branch"
326 */
327 private String types = "checkstyle,pmd,pmd-cpd,findbugs,simian,cobertura-line,cobertura-branch";
328
329 /**
330 * The xml input stream.
331 */
332 private InputStream theXmlStream = null;
333
334 /**
335 * The xslt style sheet.
336 *
337 * @parameter
338 */
339 private File styleSheet = null;
340
341 /**
342 * The xslt style sheet input stream.
343 */
344 private InputStream theStyleSheetStream = null;
345
346 /**
347 * The number of hours to define the time window from now.
348 *
349 * @parameter default-value="48"
350 */
351 private String startTimeHoursOffset = "48";
352
353 /**
354 * The string version of the actual date computed by the offset set above.
355 */
356 private String theOffset;
357
358 /**
359 * Not sure what this is.
360 *
361 * @component
362 */
363 private SiteRenderer siteRenderer;
364
365 /**
366 * The maven project.
367 *
368 * @parameter expression="${project}"
369 * @required
370 * @readonly
371 */
372 private MavenProject project;
373
374 /**
375 * generate the actual report.
376 *
377 * @param aLocale
378 * ignored.
379 * @throws MavenReportException
380 * if anything goes wrong.
381 */
382 protected final void executeReport(final Locale aLocale) throws MavenReportException {
383
384 getLog().info("QALab: Merge and Chart Report");
385
386 try {
387 doMerge();
388 } catch (MojoExecutionException e) {
389 throw new MavenReportException("Issue during Merge", e);
390 }
391
392 try {
393 doChart();
394 } catch (MojoExecutionException e) {
395 throw new MavenReportException("Issue during Chart", e);
396 }
397
398 validate();
399
400 getLog().info("Output Directory: " + getOutputDirectory());
401
402 final File outdir = new File(getOutputDirectory());
403 if (!outdir.exists()) {
404 outdir.mkdirs();
405 }
406 assert outdir.isDirectory() : "the output directory was not a directory";
407
408 final File output;
409 try {
410 output = File.createTempFile("qalab", null, outdir);
411 assert output != null : "the temp file is null.";
412
413 } catch (IOException ioex) {
414 throw new MavenReportException("could not create temp file.", ioex);
415 }
416
417 final XmlTransformer t = new XmlTransformer(theXmlStream, theStyleSheetStream, output);
418 t.addParameter("targetdir", getOutputDirectory());
419 t.addParameter("type", types);
420 t.addParameter("offset", theOffset);
421 try {
422 t.transform();
423 } catch (TransformerException tex) {
424 throw new MavenReportException("transformation failed.", tex);
425 }
426
427
428 output.deleteOnExit();
429 }
430
431 private void doMerge() throws MojoExecutionException {
432 QALabStatAllMergeMojo mojo = new QALabStatAllMergeMojo();
433 mojo.setCheckstyleHandler(checkstyleHandler);
434 mojo.setCheckstyleInputFile(checkstyleInputFile);
435 mojo.setPmdCpdHandler(pmdCpdHandler);
436 mojo.setPmdCpdInputFile(pmdCpdInputFile);
437 mojo.setPmdHandler(pmdHandler);
438 mojo.setPmdInputFile(pmdInputFile);
439 mojo.setSimianHandler(simianHandler);
440 mojo.setSimianInputFile(simianInputFile);
441 mojo.setCoberturaBranchHandler(coberturaBranchHandler);
442 mojo.setCoberturaInputFile(coberturaInputFile);
443 mojo.setCoberturaLineHandler(coberturaLineHandler);
444 mojo.setDateOnly(dateOnly);
445 mojo.setExporterClassName(exporterClassName);
446 mojo.setFindbugsHandler(findbugsHandler);
447 mojo.setFindbugsInputFile(findbugsInputFile);
448 mojo.setMergerTimeStamp(mergerTimeStamp);
449 mojo.setOutputFile(qalabFile);
450 mojo.setPropertiesFile(propertiesFile);
451 mojo.setQuiet(quiet);
452 mojo.setSrcDir(srcDir);
453 mojo.setTheProperties(theProperties);
454 mojo.setPluginContext(getPluginContext());
455
456 mojo.execute();
457 }
458
459 private void doChart() throws MojoExecutionException {
460 BuildStatChartMojo mojo = new BuildStatChartMojo();
461 mojo.setFilePrefix(chartFilePrefix);
462 mojo.setSummaryOnly(chartSummaryOnly);
463 mojo.setSummaryTypes(summaryTypes);
464 mojo.setHeight(chartHeight);
465 mojo.setWidth(chartWidth);
466 mojo.setMovingAverage(movingAverage);
467 mojo.setQalabFile(qalabFile);
468 mojo.setQuiet(quiet);
469 mojo.setToDir(chartOutputDirectory);
470 mojo.setTypes(types);
471 mojo.setPluginContext(getPluginContext());
472 mojo.setXAxisSummaryTitle(xAxisSummaryTitle);
473 mojo.setXAxisTitle(xAxisTitle);
474 mojo.setYAxisSummaryTitle(yAxisSummaryTitle);
475 mojo.setYAxisTitle(yAxisTitle);
476
477 mojo.execute();
478
479
480 mojo.setFilePrefix("cobertura-" + chartFilePrefix);
481 mojo.setSummaryOnly(true);
482 mojo.setSummaryTypes("cobertura-line,cobertura-branch");
483 mojo.setYAxisSummaryTitle("Coverage Percent");
484 mojo.execute();
485 }
486
487 /**
488 * @return "qalab/index"
489 * @see org.apache.maven.reporting.MavenReport#getOutputName()
490 */
491 public final String getOutputName() {
492 return "qalab/index";
493 }
494
495 /**
496 * @return The output directory as configured in your <code>pom.xml</code>.
497 * @see org.apache.maven.reporting.AbstractMavenReport#getOutputDirectory()
498 */
499 protected final String getOutputDirectory() {
500 return outputDirectory;
501 }
502
503 /**
504 * @return The Maven Project itself. Used internally to get access to Config
505 * params etc.
506 * @see org.apache.maven.reporting.AbstractMavenReport#getProject()
507 */
508 protected final MavenProject getProject() {
509 return project;
510 }
511
512 /**
513 * @return a direct reference to the site renderer.
514 * @see org.apache.maven.reporting.AbstractMavenReport#getSiteRenderer()
515 */
516 protected final SiteRenderer getSiteRenderer() {
517 return siteRenderer;
518 }
519
520 /**
521 * @param aLocale
522 * The locale.
523 * @return The locale specific report name.
524 * @see org.apache.maven.reporting.MavenReport#getName(java.util.Locale)
525 */
526 public final String getName(final Locale aLocale) {
527 return getBundle(aLocale).getString("report.qalab.name");
528 }
529
530 /**
531 * @param aLocale
532 * The locale.
533 * @return The locale specific report description.
534 * @see org.apache.maven.reporting.MavenReport#getDescription(java.util.Locale)
535 */
536 public final String getDescription(final Locale aLocale) {
537 return getBundle(aLocale).getString("report.qalab.description");
538 }
539
540 /**
541 * @return true.
542 * @see org.apache.maven.reporting.MavenReport#isExternalReport
543 */
544 public final boolean isExternalReport() {
545 return true;
546 }
547
548 /**
549 * @param aLocale
550 * The locale.
551 * @return the resource bundle for this report.
552 */
553 private static ResourceBundle getBundle(final Locale aLocale) {
554 return ResourceBundle.getBundle("qalab-report", aLocale, MergeAndChartReport.class.getClassLoader());
555 }
556
557 public void setXAxisSummaryTitle(String xAxisSummaryTitle) {
558 this.xAxisSummaryTitle = xAxisSummaryTitle;
559 }
560
561 public void setXAxisTitle(String xAxisTitle) {
562 this.xAxisTitle = xAxisTitle;
563 }
564
565 public void setYAxisSummaryTitle(String yAxisSummaryTitle) {
566 this.yAxisSummaryTitle = yAxisSummaryTitle;
567 }
568
569 public void setYAxisTitle(String yAxisTitle) {
570 this.yAxisTitle = yAxisTitle;
571 }
572
573 /**
574 * Validates the parameters supplied by maven 2.
575 *
576 * @throws MavenReportException
577 * if any supplied params are wrong.
578 */
579 private void validate() throws MavenReportException {
580 try {
581 Utils.checkFile(qalabFile, "qalabFile");
582 theXmlStream = new FileInputStream(qalabFile);
583 } catch (IOException ioex) {
584 throw new MavenReportException(ioex.getMessage(), ioex);
585 }
586
587 if (styleSheet == null) {
588 try {
589 theStyleSheetStream = Utils.extractAsInputStream(chartBundledXsl);
590 } catch (IOException ioex) {
591 throw new MavenReportException("Could not find the default stylesheet. " + ioex.getMessage(), ioex);
592 }
593 } else {
594 try {
595 Utils.checkFile(styleSheet, "styleSheet");
596 theStyleSheetStream = new FileInputStream(styleSheet);
597 } catch (IOException ioex) {
598 throw new MavenReportException(ioex.getMessage(), ioex);
599 }
600 }
601
602 theOffset = Utils.formatDateBasedOnOffset(startTimeHoursOffset);
603 }
604 }
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620