1 package org.apache.maven.jcoveragereport;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 import java.io.BufferedReader;
21 import java.io.BufferedWriter;
22 import java.io.File;
23 import java.io.FileReader;
24 import java.io.FileWriter;
25 import java.io.IOException;
26 import java.io.PrintWriter;
27 import java.text.NumberFormat;
28 import java.util.ArrayList;
29 import java.util.Iterator;
30 import java.util.List;
31
32 import org.apache.oro.text.perl.Perl5Util;
33
34 /**
35 * @author Emmanuel Venisse
36 * @version $Id: CoverageReport.java 328198 2005-10-25 00:28:22Z aheritier $
37 */
38 public class CoverageReport
39 {
40 /** coverage data */
41 private Coverage coverage;
42
43 /**
44 * Create a coverage report
45 * @param coverage coverage data
46 */
47 public CoverageReport(Coverage coverage)
48 {
49 this.coverage = coverage;
50 }
51
52 public void generate(String dir) throws IOException
53 {
54 File directory = new File(dir);
55 directory.mkdirs();
56
57 generateSourceFiles(directory);
58 generateFrameset(directory);
59 generatePackageList(directory);
60 generateClassList(directory);
61 generateOverview(directory);
62 }
63
64 private void generateFrameset(File dir) throws IOException
65 {
66 File fsFile = new File(dir, "index.html");
67 PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter(fsFile)));
68 pw.println("<html>");
69 pw.println("<head>");
70 pw.println("<title>unit tests coverage report</title>");
71 pw.println("</head>");
72 pw.println("<FRAMESET cols=\"20%,80%\">");
73 pw.println("<FRAMESET rows=\"30%,70%\">");
74 pw.println("<FRAME src=\"overview-frame.html\" name=\"packageListFrame\" title=\"All Packages\">");
75 pw.println("<FRAME src=\"allclasses-frame.html\" name=\"packageFrame\" title=\"All classes and interfaces (except non-static nested types)\">");
76 pw.println("</FRAMESET>");
77 pw.println("<FRAME src=\"overview-summary.html\" name=\"classFrame\" title=\"Package, class and interface descriptions\" scrolling=\"yes\">");
78 pw.println("<NOFRAMES>");
79 pw.println("This document is designed to be viewed using the frames feature. If you see this message, you are using a non-frame-capable web client.");
80 pw.println("<BR>");
81 pw.println("Link to<A HREF=\"overview-summary.html\">Non-frame version.</A>");
82 pw.println("</NOFRAMES>");
83 pw.println("</FRAMESET>");
84 pw.println("</html>");
85 pw.close();
86 }
87
88 private void generatePackageList(File dir) throws IOException
89 {
90 File fsFile = new File(dir, "overview-frame.html");
91 PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter(fsFile)));
92 pw.println("<html>");
93 pw.println("<head>");
94 pw.println("<title>unit tests coverage report</title>");
95 pw.println("<link rel =\"stylesheet\" type=\"text/css\" href=\"style.css\" title=\"Style\">");
96 pw.println("</head>");
97 pw.println("<body>");
98 pw.println("<span class=\"title\">Coverage report</span>");
99 pw.println("<table>");
100 pw.println("<tr>");
101 pw.println("<td nowrap=\"nowrap\">");
102 pw.println("<a href=\"overview-summary.html\" target=\"classFrame\">Overview</a><br>");
103 pw.println("<a href=\"allclasses-frame.html\" target=\"packageFrame\">All classes</a>");
104 pw.println("</td>");
105 pw.println("</tr>");
106 pw.println("</table>");
107 pw.println("<p>");
108 pw.println("<table>");
109 pw.println("<tr>");
110 pw.println("<td nowrap=\"nowrap\"><span class=\"title2\">All packages</span></td>");
111 pw.println("</tr>");
112 pw.println("<tr>");
113 pw.println("<td nowrap=\"nowrap\">");
114
115 for (Iterator iter = coverage.getPackagesSortedByName().iterator(); iter.hasNext(); )
116 {
117 Package pkg = (Package) iter.next();
118 String url = pkg.getDirectory() + "/package-frame.html";
119 pw.println("<a href=\"" + url + "\" target=\"packageFrame\">" + pkg.getName() + "</a><br>");
120 }
121
122 pw.println("</td>");
123 pw.println("</tr>");
124 pw.println("</table>");
125 pw.println("</body>");
126 pw.println("</html>");
127 pw.close();
128
129 for (Iterator iter = coverage.getPackagesSortedByName().iterator(); iter.hasNext(); )
130 {
131 Package pkg = (Package) iter.next();
132 generateClassList(dir, pkg);
133 generateOverview(dir, pkg);
134 }
135 }
136
137 private void generateClassList(File dir) throws IOException
138 {
139 generateClassList(dir, null);
140 }
141
142 private void generateClassList(File dir, Package pkg) throws IOException
143 {
144 String filename;
145 String rootRef;
146 List classes;
147 String urlDirectory = "";
148 if (pkg == null)
149 {
150 rootRef = "";
151 filename = "allclasses-frame.html";
152 classes = coverage.getClassesSortedByName();
153 }
154 else
155 {
156 rootRef = getRelativePath(pkg.getName() + "/");
157 filename = pkg.getDirectory() + "/package-frame.html";
158 classes = pkg.getClassesSortedByName();
159 urlDirectory = ".";
160 }
161 File fsFile = new File(dir, filename);
162 PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter(fsFile)));
163 pw.println("<html>");
164 pw.println("<head>");
165 pw.println("<title>unit tests coverage report</title>");
166 pw.println("<link rel =\"stylesheet\" type=\"text/css\" href=\"" + rootRef + "style.css\" title=\"Style\">");
167 pw.println("</head>");
168 pw.println("<body>");
169 if (pkg != null)
170 {
171 pw.println("<a href=\"package-summary.html\" target=\"classFrame\">" + pkg.getName() + "</a><br>");
172 pw.println("<p>");
173 }
174 pw.println("<span class=\"title\">All classes</span>");
175 pw.println("<table>");
176 pw.println("<tr>");
177 pw.println("<td nowrap=\"nowrap\">");
178
179 for (Iterator iter = classes.iterator(); iter.hasNext(); )
180 {
181 Clazz theClass = (Clazz) iter.next();
182 if (pkg == null)
183 {
184 urlDirectory = theClass.getFile().substring(0, theClass.getFile().lastIndexOf("/"));
185 }
186 String classFilename = theClass.getName() + ".html";
187 if (theClass.getFile().endsWith("/" + theClass.getName() + ".java"))
188 {
189 pw.println("<a href=\"" + urlDirectory + "/" + classFilename + "\" target=\"classFrame\">" + theClass.getName() + "</a><span class=\"text_italic\"> (" + getPercentValue(theClass.getLineRate()) + ")</span><br>");
190 }
191 }
192
193 pw.println("</td>");
194 pw.println("</tr>");
195 pw.println("</table>");
196 pw.println("</body>");
197 pw.println("</html>");
198 pw.close();
199 }
200
201 private void generateOverview(File dir) throws IOException
202 {
203 generateOverview(dir, null);
204 }
205
206 private void generateOverview(File dir, Package thePackage) throws IOException
207 {
208 String filename = "overview-summary.html";
209 String rootRef;
210 if (thePackage != null)
211 {
212 filename = thePackage.getDirectory() + "/package-summary.html";
213 rootRef = getRelativePath(thePackage.getName() + "/");
214 }
215 else
216 {
217 rootRef = "";
218 }
219 File fsFile = new File(dir, filename);
220 PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter(fsFile)));
221 pw.println("<html>");
222 pw.println("<head>");
223 pw.println("<title>unit tests coverage report</title>");
224 pw.println("<link rel =\"stylesheet\" type=\"text/css\" href=\"" + rootRef + "style.css\" title=\"Style\">");
225 pw.println("</head>");
226 pw.println("<body>");
227 pw.println("<span class=\"title\">Coverage report</span>");
228 pw.println("<p>");
229 pw.println("<table class=\"report\" cellpadding=\"0\" cellspacing=\"0\">");
230 pw.println("<tr class=\"report\">");
231 pw.println("<th class=\"report\"> </th>");
232 pw.println("<th class=\"report\">Files</th>");
233 pw.println("<th class=\"report\">%line</th>");
234 pw.println("<th class=\"report\">%branch</th>");
235 pw.println("</tr>");
236 pw.println("<tr class=\"report\">");
237 if (thePackage != null)
238 {
239 pw.println("<td class=\"reportText\">" + thePackage.getName() + "</td>");
240 pw.println("<td class=\"reportValue\">" + thePackage.getClasses().size() + "</td>");
241 pw.println("<td class=\"reportValue\">" + generatePercentResult(getPercentValue(thePackage.getCoveredPercentLine())) + "</td>");
242 pw.println("<td class=\"reportValue\">" + generatePercentResult(getPercentValue(thePackage.getCoveredPercentBranch())) + "</td>");
243 }
244 else
245 {
246 pw.println("<td class=\"reportText\">Project</td>");
247 pw.println("<td class=\"reportValue\">" + coverage.getClasses().size() + "</td>");
248 pw.println("<td class=\"reportValue\">" + generatePercentResult(getPercentValue(coverage.getCoveredPercentLine())) + "</td>");
249 pw.println("<td class=\"reportValue\">" + generatePercentResult(getPercentValue(coverage.getCoveredPercentBranch())) + "</td>");
250 }
251 pw.println("</tr>");
252 List pkgList = null;
253 if (thePackage != null)
254 {
255 pkgList = coverage.getSubPackage(thePackage);
256 if (pkgList.size() > 0)
257 {
258 pw.println("<tr>");
259 pw.println("<td class=\"spacer\" colspan=\"4\"><span class=\"title2\">Packages</span></td>");
260 pw.println("</tr>");
261 for (Iterator iter = pkgList.iterator(); iter.hasNext(); )
262 {
263 Package pkg = (Package) iter.next();
264 pw.println("<tr class=\"report\">");
265 String subPkgDir = pkg.getDirectory().substring(thePackage.getDirectory().length() + 1);
266 pw.println("<td class=\"reportText\"><a href=\"" + subPkgDir + "/package-summary.html\">" + pkg.getName() + "</a></td>");
267 pw.println("<td class=\"reportValue\">" + pkg.getClasses().size() + "</td>");
268 pw.println("<td class=\"reportValue\">" + generatePercentResult(getPercentValue(pkg.getCoveredPercentLine())) + "</td>");
269 pw.println("<td class=\"reportValue\">" + generatePercentResult(getPercentValue(pkg.getCoveredPercentBranch())) + "</td>");
270 pw.println("</tr>");
271 }
272 }
273 if (thePackage.getClasses().size() > 0)
274 {
275 pw.println("<tr>");
276 pw.println("<td class=\"spacer\" colspan=\"4\"><span class=\"title2\">Classes</span></td>");
277 pw.println("</tr>");
278 for (Iterator it = thePackage.getClassesSortedByName().iterator(); it.hasNext(); )
279 {
280 Clazz cl = (Clazz) it.next();
281 if (cl.getFile().indexOf("[Unknown]")<0)
282 {
283 String classFilename = getShortFilename(cl);
284 pw.println("<tr class=\"report\">");
285 pw.println("<td class=\"reportText\" colspan=\"2\"><a href=\"" + classFilename + "\">" + cl.getName() + "</a></td>");
286 pw.println("<td class=\"reportValue\">" + generatePercentResult(getPercentValue(cl.getLineRate())) + "</td>");
287 pw.println("<td class=\"reportValue\">" + generatePercentResult(getPercentValue(cl.getBranchRate())) + "</td>");
288 pw.println("</tr>");
289 }
290 }
291 }
292 }
293 else
294 {
295 pkgList = coverage.getPackages();
296 if (pkgList.size() > 0)
297 {
298 pw.println("<tr>");
299 pw.println("<td class=\"spacer\" colspan=\"4\"><span class=\"title2\">Packages</span></td>");
300 pw.println("</tr>");
301 for (Iterator iter = coverage.getPackagesSortedByName().iterator(); iter.hasNext(); )
302 {
303 Package pkg = (Package) iter.next();
304 pw.println("<tr class=\"report\">");
305 pw.println("<td class=\"reportText\"><a href=\"" + pkg.getDirectory() + "/package-summary.html\">" + pkg.getName() + "</a></td>");
306 pw.println("<td class=\"reportValue\">" + pkg.getClasses().size() + "</td>");
307 pw.println("<td class=\"reportValue\">" + generatePercentResult(getPercentValue(pkg.getCoveredPercentLine())) + "</td>");
308 pw.println("<td class=\"reportValue\">" + generatePercentResult(getPercentValue(pkg.getCoveredPercentBranch())) + "</td>");
309 pw.println("</tr>");
310 }
311 }
312 if (coverage.getClasses().size() > 0)
313 {
314 List classesList = new ArrayList();
315 for (Iterator iter = coverage.getClassesSortedByName().iterator(); iter.hasNext(); )
316 {
317 Clazz cl = (Clazz) iter.next();
318 if (cl.getPackageName() == null
319 || cl.getPackageName().equals(""))
320 {
321 classesList.add(cl);
322 }
323 }
324 if (classesList.size() > 0)
325 {
326 pw.println("<tr>");
327 pw.println("<td class=\"spacer\" colspan=\"4\"><span class=\"title2\">Classes</span></td>");
328 pw.println("</tr>");
329 for (Iterator iter = classesList.iterator(); iter.hasNext(); )
330 {
331 Clazz cl = (Clazz) iter.next();
332 if (cl.getFile().indexOf("[Unknown]")<0)
333 {
334 String classFilename = getShortFilename(cl);
335 pw.println("<tr class=\"report\">");
336 pw.println("<td class=\"reportText\" colspan=\"2\"><a href=\"" + classFilename + "\">" + cl.getName() + "</a></td>");
337 pw.println("<td class=\"reportValue\">" + generatePercentResult(getPercentValue(cl.getLineRate())) + "</td>");
338 pw.println("<td class=\"reportValue\">" + generatePercentResult(getPercentValue(cl.getBranchRate())) + "</td>");
339 pw.println("</tr>");
340 }
341 }
342 }
343 }
344 }
345 pw.println("</table>");
346 pw.println(generateFooter());
347 pw.println("</body>");
348 pw.println("</html>");
349 pw.close();
350 }
351
352 /**
353 * Gets the name to output the file to.
354 *
355 * @param cl the class to get the filename for
356 * @return the filename
357 */
358 private String getShortFilename(Clazz cl) {
359 String name = cl.getFile().substring(cl.getFile().lastIndexOf("/")+1, cl.getFile().lastIndexOf("."));
360 if (cl.getName().indexOf('$') >= 0) {
361 name = name + "_" + cl.getName().substring(cl.getName().indexOf('$') + 1);
362 }
363 return name + ".html";
364 }
365
366 /**
367 * Gets the name to output the file to.
368 *
369 * @param cl the class to get the filename for
370 * @return the filename
371 */
372 private String getFullFilename(Clazz cl) {
373 String name = cl.getFile().substring(0, cl.getFile().lastIndexOf("."));
374 if (cl.getName().indexOf('$') >= 0) {
375 name = name + "_" + cl.getName().substring(cl.getName().indexOf('$') + 1);
376 }
377 return name + ".html";
378 }
379
380 private void generateSourceFiles(File dir) throws IOException
381 {
382 for (Iterator iter = coverage.getClasses().iterator(); iter.hasNext(); )
383 {
384 Clazz theClass = (Clazz) iter.next();
385 File file = new File(coverage.getSrcDirectory(), theClass.getFile());
386 if (file.exists())
387 {
388 generateSourceFile(dir, theClass);
389 }
390 }
391 }
392
393 private void generateSourceFile(File directory, Clazz theClass) throws IOException
394 {
395 String srcOutputFilename = getFullFilename(theClass);
396 File srcOutputFile = new File(directory, srcOutputFilename);
397 File dirOutputFile = srcOutputFile.getParentFile();
398 if (dirOutputFile != null)
399 {
400 dirOutputFile.mkdirs();
401 }
402
403 PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter(srcOutputFile)));
404 pw.println("<html>");
405 pw.println("<head>");
406 pw.println("<title>unit tests coverage</title>");
407 String rootRef = getRelativePath(theClass.getPackageName());
408 pw.println("<link rel =\"stylesheet\" type=\"text/css\" href=\"" + rootRef + "style.css\" title=\"Style\">");
409 pw.println("</head>");
410 pw.println("<body>");
411 pw.println("<span class=\"title\">Coverage report</span>");
412 pw.println("<p>");
413 pw.println(" <table cellspacing=\"0\" cellpadding=\"0\" class=\"report\">");
414 pw.println(" <tr class=\"report\">");
415 pw.println(" <th class=\"report\"> </th>");
416 pw.println(" <th class=\"report\">%line</th>");
417 pw.println(" <th class=\"report\">%branch</th>");
418 pw.println(" </tr>");
419 pw.println(" <tr class=\"report\">");
420 pw.println(" <td class=\"reportText\"><span class=\"text\">" + theClass.getPackageName() + "." + theClass.getName() + "</span></td>");
421 pw.println(" <td class=\"reportValue\">" + generatePercentResult(getPercentValue(theClass.getLineRate())) + "</td>");
422 pw.println(" <td class=\"reportValue\">" + generatePercentResult(getPercentValue(theClass.getBranchRate())) + "</td>");
423 pw.println(" </tr>");
424 pw.println(" </table>");
425 pw.println(" <p>");
426 pw.println(" <table cellspacing=\"0\" cellpadding=\"0\" class=\"src\">");
427
428 BufferedReader br = new BufferedReader(new FileReader(new File(coverage.getSrcDirectory(), theClass.getFile())));
429 String lineStr;
430 int numLigne = 1;
431 while ((lineStr = br.readLine()) != null)
432 {
433 Line theLine = (Line) theClass.getLinesMap().get(new Integer(numLigne));
434 int nbHits = 0;
435 if (theLine != null)
436 {
437 nbHits = theLine.getNbHits();
438 }
439 pw.println(" <tr>");
440 if (theLine != null)
441 {
442 pw.println(" <td class=\"numLineCover\"> " + numLigne + "</td>");
443 if (nbHits > 0)
444 {
445 pw.println(" <td class=\"nbHitsCovered\"> " + theLine.getNbHits() + "</td>");
446 pw.println(" <td class=\"src\"><pre class=\"src\"> " + JavaToHtml.syntaxHighlight(lineStr) + "</pre></td>");
447 }
448 else
449 {
450 pw.println(" <td class=\"nbHitsUncovered\"> " + theLine.getNbHits() + "</td>");
451 pw.println(" <td class=\"src\"><pre class=\"src\"><span class=\"srcUncovered\"> "+ JavaToHtml.syntaxHighlight(lineStr) + "</span></pre></td>");
452 }
453 }
454 else
455 {
456 pw.println(" <td class=\"numLine\"> " + numLigne + "</td>");
457 pw.println(" <td class=\"nbHits\"> </td>");
458 pw.println(" <td class=\"src\"><pre class=\"src\"> " + JavaToHtml.syntaxHighlight(lineStr) + "</pre></td>");
459 }
460 pw.println(" </tr>");
461 numLigne++;
462 }
463 pw.println(" </table>");
464 pw.println(generateFooter());
465 pw.println("</body>");
466 pw.println("</html>");
467 br.close();
468 pw.close();
469 }
470
471 private String generateFooter()
472 {
473 StringBuffer sb = new StringBuffer();
474 sb.append("<p>");
475 sb.append("<table cellpadding=\"0\" cellspacing=\"0\" class=\"report\">");
476 sb.append(" <tr class=\"report\">");
477 sb.append(" <td class=\"reportText\"><span class=\"text\">");
478 sb.append(" This report is generated by <a href=\"http://www.jcoverage.com\">jcoverage</a>, <a href=\"http://maven.apache.org\">Maven</a> and <a href=\"http://maven.apache.org/maven-1.x/reference/plugins/jcoverage/\">Maven JCoverage Plugin</a>.");
479 sb.append(" </span></td>");
480 sb.append(" </tr>");
481 sb.append("</table>");
482 return sb.toString();
483 }
484
485 private String generatePercentResult(String percentValue)
486 {
487 if (percentValue.endsWith("%"))
488 {
489 percentValue = percentValue.substring(0, percentValue.length() - 1);
490 }
491 double rest = 0;
492 try
493 {
494 rest = 100d - new Double(percentValue).doubleValue();
495 }
496 catch(NumberFormatException e)
497 {
498 rest = 0;
499 }
500 StringBuffer sb = new StringBuffer();
501 sb.append("<table class=\"percentGraph\" cellpadding=\"0\" cellspacing=\"0\" align=\"right\">");
502 sb.append("<tr>");
503 sb.append("<td><span class=\"text\">" + percentValue + "% </span></td>");
504 sb.append("<td>");
505 sb.append("<table class=\"percentGraph\" cellpadding=\"0\" cellspacing=\"0\">");
506 sb.append("<tr>");
507 sb.append("<td class=\"percentCovered\" width=\"" + percentValue + "\"></td>");
508 sb.append("<td class=\"percentUnCovered\" width=\"" + String.valueOf(rest) + "\"></td>");
509 sb.append("</tr>");
510 sb.append("</table>");
511 sb.append("</td>");
512 sb.append("</tr>");
513 sb.append("</table>");
514 return sb.toString();
515 }
516
517 private String getRelativePath(String path)
518 {
519 if (path != null && !path.equals(""))
520 {
521 return new Perl5Util().substitute("s/[^\\.]*(\\.|$)/..\\//g", path);
522 }
523 else
524 {
525 return "";
526 }
527 }
528
529 private String getPercentValue(String value)
530 {
531 if (value.endsWith("%"))
532 {
533 value = value.substring(0, value.length() - 1);
534 }
535 double percent = 0;
536 try
537 {
538 percent = new Double(value).doubleValue();
539 }
540 catch(NumberFormatException e)
541 {
542 percent = 0;
543 }
544 NumberFormat percentFormatter;
545
546 percentFormatter = NumberFormat.getPercentInstance();
547 return percentFormatter.format(percent);
548 }
549 }