1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.codehaus.mojo.cobertura;
21
22 import net.sourceforge.cobertura.coveragedata.CoverageDataFileHandler;
23 import net.sourceforge.cobertura.coveragedata.ProjectData;
24 import org.apache.maven.artifact.Artifact;
25 import org.apache.maven.doxia.siterenderer.Renderer;
26 import org.apache.maven.plugin.MojoExecutionException;
27 import org.apache.maven.project.MavenProject;
28 import org.apache.maven.reporting.AbstractMavenReport;
29 import org.apache.maven.reporting.MavenReportException;
30 import org.codehaus.mojo.cobertura.configuration.MaxHeapSizeUtil;
31 import org.codehaus.mojo.cobertura.tasks.CommandLineArguments;
32 import org.codehaus.mojo.cobertura.tasks.ReportTask;
33
34 import java.io.File;
35 import java.net.URI;
36 import java.util.ArrayList;
37 import java.util.Collections;
38 import java.util.HashMap;
39 import java.util.List;
40 import java.util.Locale;
41 import java.util.Map;
42 import java.util.ResourceBundle;
43
44
45
46
47
48
49
50
51
52
53 public class CoberturaReportMojo
54 extends AbstractMavenReport
55 {
56
57
58
59
60
61
62 private String format;
63
64
65
66
67
68
69 private String[] formats = new String[]{ "html" };
70
71
72
73
74
75
76
77 private String encoding;
78
79
80
81
82
83
84 private String maxmem = "64m";
85
86
87
88
89
90
91
92
93
94
95 private File dataFile;
96
97
98
99
100
101
102
103
104 private List<Artifact> pluginClasspathList;
105
106
107
108
109
110
111
112 private File outputDirectory;
113
114
115
116
117
118
119
120 private boolean quiet;
121
122
123
124
125
126
127
128 private boolean aggregate;
129
130
131
132
133
134
135
136
137
138
139 private boolean omitGplFiles;
140
141
142
143
144
145
146 private Renderer siteRenderer;
147
148
149
150
151
152
153
154
155 private List<MavenProject> reactorProjects;
156
157
158
159
160
161
162
163
164 private MavenProject project;
165
166 private Map<MavenProject, List<MavenProject>> projectChildren;
167
168 private String relDataFileName;
169
170 private String relAggregateOutputDir;
171
172
173
174
175
176
177 public CoberturaReportMojo()
178 {
179 if ( MaxHeapSizeUtil.getInstance().envHasMavenMaxMemSetting() )
180 {
181 maxmem = MaxHeapSizeUtil.getInstance().getMavenMaxMemSetting();
182 }
183 }
184
185
186
187
188
189
190 public String getName( Locale locale )
191 {
192 return getBundle( locale ).getString( "report.cobertura.name" );
193 }
194
195
196
197
198
199
200 public String getDescription( Locale locale )
201 {
202 return getBundle( locale ).getString( "report.cobertura.description" );
203 }
204
205 @Override
206 protected String getOutputDirectory()
207 {
208 return outputDirectory.getAbsolutePath();
209 }
210
211 @Override
212 protected MavenProject getProject()
213 {
214 return project;
215 }
216
217 @Override
218 protected Renderer getSiteRenderer()
219 {
220 return siteRenderer;
221 }
222
223
224
225
226
227
228
229
230 private void executeReportTask( ReportTask task, String outputFormat )
231 throws MavenReportException
232 {
233 task.setOutputFormat( outputFormat );
234
235
236 try
237 {
238 task.execute();
239 }
240 catch ( MojoExecutionException e )
241 {
242
243
244 getLog().error( "Error in Cobertura Report generation: " + e.getMessage(), e );
245 }
246 }
247
248
249
250
251 @Override
252 protected void executeReport( Locale locale )
253 throws MavenReportException
254 {
255 if ( canGenerateSimpleReport() )
256 {
257 executeReport( getDataFile(), outputDirectory, getCompileSourceRoots() );
258 }
259
260 if ( canGenerateAggregateReports() )
261 {
262 executeAggregateReport( locale );
263 }
264 }
265
266
267
268
269 private void executeAggregateReport( Locale locale )
270 throws MavenReportException
271 {
272 for ( MavenProject proj : reactorProjects )
273 {
274 if ( !isMultiModule( proj ) )
275 {
276 continue;
277 }
278 executeAggregateReport( locale, proj );
279 }
280 }
281
282
283
284
285 private void executeAggregateReport( Locale locale, MavenProject curProject )
286 throws MavenReportException
287 {
288 List<MavenProject> children = getAllChildren( curProject );
289
290 if ( children.isEmpty() )
291 {
292 return;
293 }
294
295 List<File> serFiles = getOutputFiles( children );
296 if ( serFiles.isEmpty() )
297 {
298 getLog().info( "Not executing aggregate cobertura:report for " + curProject.getName()
299 + " as no child cobertura data files could not be found" );
300 return;
301 }
302
303 getLog().info( "Executing aggregate cobertura:report for " + curProject.getName() );
304
305 ProjectData aggProjectData = new ProjectData();
306 for ( File serFile : serFiles )
307 {
308 ProjectData data = CoverageDataFileHandler.loadCoverageData( serFile );
309 aggProjectData.merge( data );
310 }
311
312 File aggSerFile = new File( curProject.getBasedir(), relDataFileName );
313 aggSerFile.getAbsoluteFile().getParentFile().mkdirs();
314 getLog().info( "Saving aggregate cobertura information in " + aggSerFile.getAbsolutePath() );
315 CoverageDataFileHandler.saveCoverageData( aggProjectData, aggSerFile );
316
317
318 List<String> aggCompileSourceRoots = new ArrayList<String>();
319 for ( MavenProject child : children )
320 {
321 aggCompileSourceRoots.addAll( child.getCompileSourceRoots() );
322 }
323
324 File reportDir = new File( curProject.getBasedir(), relAggregateOutputDir );
325 reportDir.mkdirs();
326 executeReport( aggSerFile, reportDir, aggCompileSourceRoots );
327 }
328
329
330
331
332 private void executeReport( File curDataFile, File curOutputDirectory, List<String> curCompileSourceRoots )
333 throws MavenReportException
334 {
335 ReportTask task = new ReportTask();
336
337
338 task.setLog( getLog() );
339 task.setPluginClasspathList( pluginClasspathList );
340 task.setQuiet( quiet );
341
342
343 task.setMaxmem( maxmem );
344 task.setDataFile( curDataFile );
345 task.setOutputDirectory( curOutputDirectory );
346 task.setCompileSourceRoots( curCompileSourceRoots );
347 task.setSourceEncoding( encoding );
348
349 CommandLineArguments cmdLineArgs;
350 cmdLineArgs = new CommandLineArguments();
351 cmdLineArgs.setUseCommandsFile( true );
352 task.setCmdLineArgs( cmdLineArgs );
353
354 if ( format != null )
355 {
356 formats = new String[]{ format };
357 }
358
359 for ( int i = 0; i < formats.length; i++ )
360 {
361 executeReportTask( task, formats[i] );
362 }
363
364 removeGplFiles();
365 }
366
367
368
369
370
371 private void removeGplFiles()
372 throws MavenReportException
373 {
374 if ( omitGplFiles )
375 {
376 final String[] files =
377 new String[]{ "js/customsorttypes.js", "js/sortabletable.js", "js/stringbuilder.js" };
378 for ( int i = 0; i < files.length; i++ )
379 {
380 final File f = new File( outputDirectory, files[i] );
381 if ( f.exists() )
382 {
383 if ( f.delete() )
384 {
385 getLog().debug( "Removed GPL licensed file " + f.getPath() );
386 }
387 else
388 {
389 throw new MavenReportException( "Unable to remove GPL licensed file " + f.getPath() );
390 }
391 }
392 else
393 {
394 getLog().info( "GPL licensed file " + f.getPath() + " not found." );
395 }
396 }
397 }
398 }
399
400
401
402
403 public String getOutputName()
404 {
405 return "cobertura/index";
406 }
407
408 @Override
409 public boolean isExternalReport()
410 {
411 return true;
412 }
413
414 @Override
415 public boolean canGenerateReport()
416 {
417 if ( canGenerateSimpleReport() )
418 {
419 return true;
420 }
421 else
422 {
423 getLog().info( "Not executing cobertura:report as the cobertura data file (" + getDataFile()
424 + ") could not be found" );
425 }
426
427 if ( canGenerateAggregateReports() )
428 {
429 return true;
430 }
431
432 if ( aggregate && isMultiModule( project ) )
433 {
434
435
436
437 return true;
438 }
439 return false;
440 }
441
442
443
444
445
446
447 private boolean canGenerateSimpleReport()
448 {
449
450
451
452
453 return getDataFile().exists() && getDataFile().isFile();
454 }
455
456
457
458
459 private boolean canGenerateAggregateReports()
460 {
461
462 if ( aggregate && isLastProject( project, reactorProjects ) )
463 {
464 buildAggregateInfo();
465
466 if ( !getOutputFiles( reactorProjects ).isEmpty() )
467 {
468 return true;
469 }
470 }
471 return false;
472 }
473
474
475
476
477 @SuppressWarnings( "unchecked" )
478 private List<String> getCompileSourceRoots()
479 {
480 return project.getExecutionProject().getCompileSourceRoots();
481 }
482
483 @Override
484 public void setReportOutputDirectory( File reportOutputDirectory )
485 {
486 if ( ( reportOutputDirectory != null ) && ( !reportOutputDirectory.getAbsolutePath().endsWith( "cobertura" ) ) )
487 {
488 this.outputDirectory = new File( reportOutputDirectory, "cobertura" );
489 }
490 else
491 {
492 this.outputDirectory = reportOutputDirectory;
493 }
494 }
495
496
497
498
499
500
501
502 private ResourceBundle getBundle( Locale locale )
503 {
504 return ResourceBundle.getBundle( "cobertura-report", locale );
505 }
506
507
508
509
510
511
512
513
514 private boolean isLastProject( MavenProject project, List<MavenProject> mavenProjectList )
515 {
516 return project.equals( mavenProjectList.get( mavenProjectList.size() - 1 ) );
517 }
518
519
520
521
522
523
524
525 private boolean isMultiModule( MavenProject mavenProject )
526 {
527 return "pom".equals( mavenProject.getPackaging() );
528 }
529
530
531
532
533 private void buildAggregateInfo()
534 {
535 if ( projectChildren != null )
536 {
537
538 return;
539 }
540
541
542 projectChildren = new HashMap<MavenProject, List<MavenProject>>();
543 for ( MavenProject proj : reactorProjects )
544 {
545 List<MavenProject> depList = projectChildren.get( proj.getParent() );
546 if ( depList == null )
547 {
548 depList = new ArrayList<MavenProject>();
549 projectChildren.put( proj.getParent(), depList );
550 }
551 depList.add( proj );
552 }
553
554
555 relDataFileName = relativize( project.getBasedir(), getDataFile() );
556 if ( relDataFileName == null )
557 {
558 getLog().warn( "Could not determine relative data file name, defaulting to 'cobertura/cobertura.ser'" );
559 relDataFileName = "cobertura/cobertura.ser";
560 }
561 relAggregateOutputDir = relativize( project.getBasedir(), outputDirectory );
562 if ( relAggregateOutputDir == null )
563 {
564 getLog().warn( "Could not determine relative output dir name, defaulting to 'cobertura'" );
565 relAggregateOutputDir = "cobertura";
566 }
567 }
568
569
570
571
572 private List<MavenProject> getAllChildren( MavenProject parentProject )
573 {
574 List<MavenProject> children = projectChildren.get( parentProject );
575 if ( children == null )
576 {
577 return Collections.emptyList();
578 }
579
580 List<MavenProject> result = new ArrayList<MavenProject>();
581 for ( MavenProject child : children )
582 {
583 if ( isMultiModule( child ) )
584 {
585 result.addAll( getAllChildren( child ) );
586 }
587 else
588 {
589 result.add( child );
590 }
591 }
592 return result;
593 }
594
595
596
597
598 private List<File> getOutputFiles( List<MavenProject> projects )
599 {
600 List<File> files = new ArrayList<File>();
601 for ( MavenProject proj : projects )
602 {
603 if ( isMultiModule( proj ) )
604 {
605 continue;
606 }
607 File outputFile = new File( proj.getBasedir(), relDataFileName );
608 if ( outputFile.exists() )
609 {
610 files.add( outputFile );
611 }
612 }
613 return files;
614 }
615
616
617
618
619 private String relativize( File parentFile, File childFile )
620 {
621 try
622 {
623 URI parentURI = parentFile.getCanonicalFile().toURI().normalize();
624 URI childURI = childFile.getCanonicalFile().toURI().normalize();
625
626 URI relativeURI = parentURI.relativize( childURI );
627 if ( relativeURI.isAbsolute() )
628 {
629
630 return null;
631 }
632 String relativePath = relativeURI.getPath();
633 if ( File.separatorChar != '/' )
634 {
635 relativePath = relativePath.replace( '/', File.separatorChar );
636 }
637 return relativePath;
638 }
639 catch ( Exception e )
640 {
641 getLog().warn( "Failed relativizing " + childFile + " to " + parentFile, e );
642 }
643 return null;
644 }
645
646
647
648
649
650
651 private File getDataFile()
652 {
653 return dataFile;
654 }
655 }