Coverage Report - net.sourceforge.cobertura.reporting.html.HTMLReport
 
Classes in this File Line Coverage Branch Coverage Complexity
HTMLReport
94%
351/375
82%
67/82
3.045
 
 1  
 /*
 2  
  * Cobertura - http://cobertura.sourceforge.net/
 3  
  *
 4  
  * Copyright (C) 2005 Mark Doliner
 5  
  * Copyright (C) 2005 Grzegorz Lukasik
 6  
  * Copyright (C) 2005 Jeremy Thomerson
 7  
  * Copyright (C) 2006 Naoki Iwami
 8  
  *
 9  
  * Cobertura is free software; you can redistribute it and/or modify
 10  
  * it under the terms of the GNU General Public License as published
 11  
  * by the Free Software Foundation; either version 2 of the License,
 12  
  * or (at your option) any later version.
 13  
  *
 14  
  * Cobertura is distributed in the hope that it will be useful, but
 15  
  * WITHOUT ANY WARRANTY; without even the implied warranty of
 16  
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 17  
  * General Public License for more details.
 18  
  *
 19  
  * You should have received a copy of the GNU General Public License
 20  
  * along with Cobertura; if not, write to the Free Software
 21  
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 22  
  * USA
 23  
  */
 24  
 
 25  
 package net.sourceforge.cobertura.reporting.html;
 26  
 
 27  
 import java.io.BufferedReader;
 28  
 import java.io.File;
 29  
 import java.io.FileInputStream;
 30  
 import java.io.FileNotFoundException;
 31  
 import java.io.IOException;
 32  
 import java.io.InputStreamReader;
 33  
 import java.io.PrintWriter;
 34  
 import java.io.UnsupportedEncodingException;
 35  
 import java.text.DateFormat;
 36  
 import java.text.DecimalFormat;
 37  
 import java.text.NumberFormat;
 38  
 import java.util.Collection;
 39  
 import java.util.Collections;
 40  
 import java.util.Date;
 41  
 import java.util.Iterator;
 42  
 import java.util.SortedSet;
 43  
 import java.util.TreeSet;
 44  
 import java.util.Vector;
 45  
 
 46  
 import net.sourceforge.cobertura.coveragedata.ClassData;
 47  
 import net.sourceforge.cobertura.coveragedata.CoverageData;
 48  
 import net.sourceforge.cobertura.coveragedata.LineData;
 49  
 import net.sourceforge.cobertura.coveragedata.PackageData;
 50  
 import net.sourceforge.cobertura.coveragedata.ProjectData;
 51  
 import net.sourceforge.cobertura.coveragedata.SourceFileData;
 52  
 import net.sourceforge.cobertura.reporting.ComplexityCalculator;
 53  
 import net.sourceforge.cobertura.reporting.html.files.CopyFiles;
 54  
 import net.sourceforge.cobertura.util.FileFinder;
 55  
 import net.sourceforge.cobertura.util.Header;
 56  
 import net.sourceforge.cobertura.util.IOUtil;
 57  
 import net.sourceforge.cobertura.util.StringUtil;
 58  
 
 59  
 import org.apache.log4j.Logger;
 60  
 
 61  
 public class HTMLReport
 62  
 {
 63  
 
 64  4
         private static final Logger LOGGER = Logger.getLogger(HTMLReport.class);
 65  
 
 66  
         private File destinationDir;
 67  
 
 68  
         private FileFinder finder;
 69  
 
 70  
         private ComplexityCalculator complexity;
 71  
 
 72  
         private ProjectData projectData;
 73  
 
 74  
         /**
 75  
          * Create a coverage report
 76  
          */
 77  
         public HTMLReport(ProjectData projectData, File outputDir,
 78  
                         FileFinder finder, ComplexityCalculator complexity)
 79  
                         throws Exception
 80  4
         {
 81  4
                 this.destinationDir = outputDir;
 82  4
                 this.finder = finder;
 83  4
                 this.complexity = complexity;
 84  4
                 this.projectData = projectData;
 85  
 
 86  4
                 CopyFiles.copy(outputDir);
 87  4
                 generatePackageList();
 88  4
                 generateSourceFileLists();
 89  4
                 generateOverviews();
 90  4
                 generateSourceFiles();
 91  4
         }
 92  
 
 93  
         private String generatePackageName(PackageData packageData)
 94  
         {
 95  35
                 if (packageData.getName().equals(""))
 96  0
                         return "(default)";
 97  35
                 return packageData.getName();
 98  
         }
 99  
 
 100  
         private void generatePackageList() throws IOException
 101  
         {
 102  4
                 File file = new File(destinationDir, "frame-packages.html");
 103  4
                 PrintWriter out = null;
 104  
 
 105  
                 try
 106  
                 {
 107  4
                         out = IOUtil.getPrintWriter(file);
 108  
 
 109  4
                         out
 110  
                                         .println("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"");
 111  4
                         out
 112  
                                         .println("           \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">");
 113  
 
 114  4
                         out
 115  
                                         .println("<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">");
 116  4
                         out.println("<head>");
 117  4
                         out
 118  
                                         .println("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />");
 119  4
                         out.println("<title>Coverage Report</title>");
 120  4
                         out
 121  
                                         .println("<link title=\"Style\" type=\"text/css\" rel=\"stylesheet\" href=\"css/main.css\" />");
 122  4
                         out.println("</head>");
 123  4
                         out.println("<body>");
 124  4
                         out.println("<h5>Packages</h5>");
 125  4
                         out.println("<table width=\"100%\">");
 126  4
                         out.println("<tr>");
 127  4
                         out
 128  
                                         .println("<td nowrap=\"nowrap\"><a href=\"frame-summary.html\" onclick='parent.sourceFileList.location.href=\"frame-sourcefiles.html\"' target=\"summary\">All</a></td>");
 129  4
                         out.println("</tr>");
 130  
 
 131  4
                         Iterator iter = projectData.getPackages().iterator();
 132  11
                         while (iter.hasNext())
 133  
                         {
 134  7
                                 PackageData packageData = (PackageData)iter.next();
 135  7
                                 String url1 = "frame-summary-" + packageData.getName()
 136  
                                                 + ".html";
 137  7
                                 String url2 = "frame-sourcefiles-" + packageData.getName()
 138  
                                                 + ".html";
 139  7
                                 out.println("<tr>");
 140  7
                                 out.println("<td nowrap=\"nowrap\"><a href=\"" + url1
 141  
                                                 + "\" onclick='parent.sourceFileList.location.href=\""
 142  
                                                 + url2 + "\"' target=\"summary\">"
 143  
                                                 + generatePackageName(packageData) + "</a></td>");
 144  7
                                 out.println("</tr>");
 145  7
                         }
 146  4
                         out.println("</table>");
 147  4
                         out.println("</body>");
 148  4
                         out.println("</html>");
 149  
                 }
 150  
                 finally
 151  
                 {
 152  4
                         if (out != null)
 153  
                         {
 154  4
                                 out.close();
 155  4
                         }
 156  0
                 }
 157  4
         }
 158  
 
 159  
         private void generateSourceFileLists() throws IOException
 160  
         {
 161  4
                 generateSourceFileList(null);
 162  4
                 Iterator iter = projectData.getPackages().iterator();
 163  11
                 while (iter.hasNext())
 164  
                 {
 165  7
                         PackageData packageData = (PackageData)iter.next();
 166  7
                         generateSourceFileList(packageData);
 167  7
                 }
 168  4
         }
 169  
 
 170  
         private void generateSourceFileList(PackageData packageData)
 171  
                         throws IOException
 172  
         {
 173  
                 String filename;
 174  
                 Collection sourceFiles;
 175  11
                 if (packageData == null)
 176  
                 {
 177  4
                         filename = "frame-sourcefiles.html";
 178  4
                         sourceFiles = projectData.getSourceFiles();
 179  4
                 }
 180  
                 else
 181  
                 {
 182  7
                         filename = "frame-sourcefiles-" + packageData.getName() + ".html";
 183  7
                         sourceFiles = packageData.getSourceFiles();
 184  
                 }
 185  
 
 186  
                 // sourceFiles may be sorted, but if so it's sorted by
 187  
                 // the full path to the file, and we only want to sort
 188  
                 // based on the file's basename.
 189  11
                 Vector sortedSourceFiles = new Vector();
 190  11
                 sortedSourceFiles.addAll(sourceFiles);
 191  11
                 Collections.sort(sortedSourceFiles,
 192  
                                 new SourceFileDataBaseNameComparator());
 193  
 
 194  11
                 File file = new File(destinationDir, filename);
 195  11
                 PrintWriter out = null;
 196  
                 try
 197  
                 {
 198  11
                         out = IOUtil.getPrintWriter(file);
 199  
 
 200  11
                         out
 201  
                                         .println("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"");
 202  11
                         out
 203  
                                         .println("           \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">");
 204  
 
 205  11
                         out.println("<html>");
 206  11
                         out.println("<head>");
 207  11
                         out
 208  
                                         .println("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\"/>");
 209  11
                         out.println("<title>Coverage Report Classes</title>");
 210  11
                         out
 211  
                                         .println("<link title=\"Style\" type=\"text/css\" rel=\"stylesheet\" href=\"css/main.css\"/>");
 212  11
                         out.println("</head>");
 213  11
                         out.println("<body>");
 214  11
                         out.println("<h5>");
 215  11
                         out.println(packageData == null ? "All Packages"
 216  
                                         : generatePackageName(packageData));
 217  11
                         out.println("</h5>");
 218  11
                         out.println("<div class=\"separator\">&nbsp;</div>");
 219  11
                         out.println("<h5>Classes</h5>");
 220  11
                         if (!sortedSourceFiles.isEmpty())
 221  
                         {
 222  11
                                 out.println("<table width=\"100%\">");
 223  11
                                 out.println("<tbody>");
 224  
 
 225  11
                                 for (Iterator iter = sortedSourceFiles.iterator(); iter
 226  39
                                                 .hasNext();)
 227  
                                 {
 228  28
                                         SourceFileData sourceFileData = (SourceFileData)iter.next();
 229  28
                                         out.println("<tr>");
 230  
                                         String percentCovered;
 231  28
                                         if (sourceFileData.getNumberOfValidLines() > 0)
 232  20
                                                 percentCovered = getPercentValue(sourceFileData
 233  
                                                                 .getLineCoverageRate());
 234  
                                         else
 235  8
                                                 percentCovered = "N/A";
 236  28
                                         out
 237  
                                                         .println("<td nowrap=\"nowrap\"><a target=\"summary\" href=\""
 238  
                                                                         + sourceFileData.getNormalizedName()
 239  
                                                                         + ".html\">"
 240  
                                                                         + sourceFileData.getBaseName()
 241  
                                                                         + "</a> <i>("
 242  
                                                                         + percentCovered
 243  
                                                                         + ")</i></td>");
 244  28
                                         out.println("</tr>");
 245  28
                                 }
 246  11
                                 out.println("</tbody>");
 247  11
                                 out.println("</table>");
 248  
                         }
 249  
 
 250  11
                         out.println("</body>");
 251  11
                         out.println("</html>");
 252  
                 }
 253  
                 finally
 254  
                 {
 255  11
                         if (out != null)
 256  
                         {
 257  11
                                 out.close();
 258  11
                         }
 259  0
                 }
 260  11
         }
 261  
 
 262  
         private void generateOverviews() throws IOException
 263  
         {
 264  4
                 generateOverview(null);
 265  4
                 Iterator iter = projectData.getPackages().iterator();
 266  11
                 while (iter.hasNext())
 267  
                 {
 268  7
                         PackageData packageData = (PackageData)iter.next();
 269  7
                         generateOverview(packageData);
 270  7
                 }
 271  4
         }
 272  
 
 273  
         private void generateOverview(PackageData packageData) throws IOException
 274  
         {
 275  
                 Iterator iter;
 276  
 
 277  
                 String filename;
 278  11
                 if (packageData == null)
 279  
                 {
 280  4
                         filename = "frame-summary.html";
 281  4
                 }
 282  
                 else
 283  
                 {
 284  7
                         filename = "frame-summary-" + packageData.getName() + ".html";
 285  
                 }
 286  11
                 File file = new File(destinationDir, filename);
 287  11
                 PrintWriter out = null;
 288  
 
 289  
                 try
 290  
                 {
 291  11
                         out = IOUtil.getPrintWriter(file);;
 292  
 
 293  11
                         out
 294  
                                         .println("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"");
 295  11
                         out
 296  
                                         .println("           \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">");
 297  
 
 298  11
                         out.println("<html>");
 299  11
                         out.println("<head>");
 300  11
                         out
 301  
                                         .println("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\"/>");
 302  11
                         out.println("<title>Coverage Report</title>");
 303  11
                         out
 304  
                                         .println("<link title=\"Style\" type=\"text/css\" rel=\"stylesheet\" href=\"css/main.css\"/>");
 305  11
                         out
 306  
                                         .println("<link title=\"Style\" type=\"text/css\" rel=\"stylesheet\" href=\"css/sortabletable.css\"/>");
 307  11
                         out
 308  
                                         .println("<script type=\"text/javascript\" src=\"js/popup.js\"></script>");
 309  11
                         out
 310  
                                         .println("<script type=\"text/javascript\" src=\"js/sortabletable.js\"></script>");
 311  11
                         out
 312  
                                         .println("<script type=\"text/javascript\" src=\"js/customsorttypes.js\"></script>");
 313  11
                         out.println("</head>");
 314  11
                         out.println("<body>");
 315  
 
 316  11
                         out.print("<h5>Coverage Report - ");
 317  11
                         out.print(packageData == null ? "All Packages"
 318  
                                         : generatePackageName(packageData));
 319  11
                         out.println("</h5>");
 320  11
                         out.println("<div class=\"separator\">&nbsp;</div>");
 321  11
                         out.println("<table class=\"report\" id=\"packageResults\">");
 322  11
                         out.println(generateTableHeader("Package", true));
 323  11
                         out.println("<tbody>");
 324  
 
 325  
                         SortedSet packages;
 326  11
                         if (packageData == null)
 327  
                         {
 328  
                                 // Output a summary line for all packages
 329  4
                                 out.println(generateTableRowForTotal());
 330  
 
 331  
                                 // Get packages
 332  4
                                 packages = projectData.getPackages();
 333  4
                         }
 334  
                         else
 335  
                         {
 336  
                                 // Get subpackages
 337  7
                                 packages = projectData.getSubPackages(packageData.getName());
 338  
                         }
 339  
 
 340  
                         // Output a line for each package or subpackage
 341  11
                         iter = packages.iterator();
 342  25
                         while (iter.hasNext())
 343  
                         {
 344  14
                                 PackageData subPackageData = (PackageData)iter.next();
 345  14
                                 out.println(generateTableRowForPackage(subPackageData));
 346  14
                         }
 347  
 
 348  11
                         out.println("</tbody>");
 349  11
                         out.println("</table>");
 350  11
                         out.println("<script type=\"text/javascript\">");
 351  11
                         out
 352  
                                         .println("var packageTable = new SortableTable(document.getElementById(\"packageResults\"),");
 353  11
                         out
 354  
                                         .println("    [\"String\", \"Number\", \"Percentage\", \"Percentage\", \"FormattedNumber\"]);");
 355  11
                         out.println("packageTable.sort(0);");
 356  11
                         out.println("</script>");
 357  
 
 358  
                         // Get the list of source files in this package
 359  
                         Collection sourceFiles;
 360  11
                         if (packageData == null)
 361  
                         {
 362  4
                                 PackageData defaultPackage = (PackageData)projectData
 363  
                                                 .getChild("");
 364  4
                                 if (defaultPackage != null)
 365  
                                 {
 366  0
                                         sourceFiles = defaultPackage.getSourceFiles();
 367  0
                                 }
 368  
                                 else
 369  
                                 {
 370  4
                                         sourceFiles = new TreeSet();
 371  
                                 }
 372  4
                         }
 373  
                         else
 374  
                         {
 375  7
                                 sourceFiles = packageData.getSourceFiles();
 376  
                         }
 377  
 
 378  
                         // Output a line for each source file
 379  11
                         if (sourceFiles.size() > 0)
 380  
                         {
 381  7
                                 out.println("<div class=\"separator\">&nbsp;</div>");
 382  7
                                 out.println("<table class=\"report\" id=\"classResults\">");
 383  7
                                 out.println(generateTableHeader("Classes in this Package",
 384  
                                                 false));
 385  7
                                 out.println("<tbody>");
 386  
 
 387  7
                                 iter = sourceFiles.iterator();
 388  21
                                 while (iter.hasNext())
 389  
                                 {
 390  14
                                         SourceFileData sourceFileData = (SourceFileData)iter.next();
 391  14
                                         out.println(generateTableRowsForSourceFile(sourceFileData));
 392  14
                                 }
 393  
 
 394  7
                                 out.println("</tbody>");
 395  7
                                 out.println("</table>");
 396  7
                                 out.println("<script type=\"text/javascript\">");
 397  7
                                 out
 398  
                                                 .println("var classTable = new SortableTable(document.getElementById(\"classResults\"),");
 399  7
                                 out
 400  
                                                 .println("    [\"String\", \"Percentage\", \"Percentage\", \"FormattedNumber\"]);");
 401  7
                                 out.println("classTable.sort(0);");
 402  7
                                 out.println("</script>");
 403  
                         }
 404  
 
 405  11
                         out.println(generateFooter());
 406  
 
 407  11
                         out.println("</body>");
 408  11
                         out.println("</html>");
 409  
                 }
 410  
                 finally
 411  
                 {
 412  11
                         if (out != null)
 413  
                         {
 414  11
                                 out.close();
 415  11
                         }
 416  0
                 }
 417  11
         }
 418  
 
 419  
         private void generateSourceFiles()
 420  
         {
 421  4
                 Iterator iter = projectData.getSourceFiles().iterator();
 422  18
                 while (iter.hasNext())
 423  
                 {
 424  14
                         SourceFileData sourceFileData = (SourceFileData)iter.next();
 425  
                         try
 426  
                         {
 427  14
                                 generateSourceFile(sourceFileData);
 428  
                         }
 429  0
                         catch (IOException e)
 430  
                         {
 431  0
                                 LOGGER.info("Could not generate HTML file for source file "
 432  
                                                 + sourceFileData.getName() + ": "
 433  
                                                 + e.getLocalizedMessage());
 434  14
                         }
 435  14
                 }
 436  4
         }
 437  
 
 438  
         private void generateSourceFile(SourceFileData sourceFileData)
 439  
                         throws IOException
 440  
         {
 441  14
                 if (!sourceFileData.containsInstrumentationInfo())
 442  
                 {
 443  0
                         LOGGER.info("Data file does not contain instrumentation "
 444  
                                         + "information for the file " + sourceFileData.getName()
 445  
                                         + ".  Ensure this class was instrumented, and this "
 446  
                                         + "data file contains the instrumentation information.");
 447  
                 }
 448  
 
 449  14
                 String filename = sourceFileData.getNormalizedName() + ".html";
 450  14
                 File file = new File(destinationDir, filename);
 451  14
                 PrintWriter out = null;
 452  
 
 453  
                 try
 454  
                 {
 455  14
                         out = IOUtil.getPrintWriter(file);
 456  
 
 457  14
                         out
 458  
                                         .println("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"");
 459  14
                         out
 460  
                                         .println("           \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">");
 461  
 
 462  14
                         out.println("<html>");
 463  14
                         out.println("<head>");
 464  14
                         out
 465  
                                         .println("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\"/>");
 466  14
                         out.println("<title>Coverage Report</title>");
 467  14
                         out
 468  
                                         .println("<link title=\"Style\" type=\"text/css\" rel=\"stylesheet\" href=\"css/main.css\"/>");
 469  14
                         out
 470  
                                         .println("<script type=\"text/javascript\" src=\"js/popup.js\"></script>");
 471  14
                         out.println("</head>");
 472  14
                         out.println("<body>");
 473  14
                         out.print("<h5>Coverage Report - ");
 474  14
                         String classPackageName = sourceFileData.getPackageName();
 475  14
                         if ((classPackageName != null) && classPackageName.length() > 0)
 476  
                         {
 477  14
                                 out.print(classPackageName + ".");
 478  
                         }
 479  14
                         out.print(sourceFileData.getBaseName());
 480  14
                         out.println("</h5>");
 481  
 
 482  
                         // Output the coverage summary for this class
 483  14
                         out.println("<div class=\"separator\">&nbsp;</div>");
 484  14
                         out.println("<table class=\"report\">");
 485  14
                         out.println(generateTableHeader("Classes in this File", false));
 486  14
                         out.println(generateTableRowsForSourceFile(sourceFileData));
 487  14
                         out.println("</table>");
 488  
 
 489  
                         // Output the coverage summary for methods in this class
 490  
                         // TODO
 491  
 
 492  
                         // Output this class's source code with syntax and coverage highlighting
 493  14
                         out.println("<div class=\"separator\">&nbsp;</div>");
 494  14
                         out.println(generateHtmlizedJavaSource(sourceFileData));
 495  
 
 496  14
                         out.println(generateFooter());
 497  
 
 498  14
                         out.println("</body>");
 499  14
                         out.println("</html>");
 500  
                 }
 501  
                 finally
 502  
                 {
 503  14
                         if (out != null)
 504  
                         {
 505  14
                                 out.close();
 506  14
                         }
 507  0
                 }
 508  14
         }
 509  
    
 510  
         private String generateBranchInfo(LineData lineData, String content) {
 511  210
                 boolean hasBranch = (lineData != null) ? lineData.hasBranch() : false;
 512  210
                 if (hasBranch) 
 513  
                 {
 514  10
                         StringBuffer ret = new StringBuffer();
 515  10
                         ret.append("<a title=\"Line ").append(lineData.getLineNumber()).append(": Conditional coverage ")
 516  
                            .append(lineData.getConditionCoverage());
 517  10
                         if (lineData.getConditionSize() > 1)
 518  
                         {
 519  4
                                 ret.append(" [each condition: ");
 520  16
                                 for (int i = 0; i < lineData.getConditionSize(); i++)
 521  
                                 {
 522  12
                                         if (i > 0)
 523  8
                                                 ret.append(", ");
 524  12
                                         ret.append(lineData.getConditionCoverage(i));
 525  
                                 }
 526  4
                                 ret.append("]");
 527  
                         }
 528  10
                         ret.append(".\">").append(content).append("</a>");
 529  10
                         return ret.toString();
 530  
                 }
 531  
                 else
 532  
                 {
 533  200
                         return content;
 534  
                 }
 535  
         }
 536  
 
 537  
         private String generateHtmlizedJavaSource(SourceFileData sourceFileData)
 538  
         {
 539  14
                 File sourceFile = null;
 540  
                 try
 541  
                 {
 542  14
                         sourceFile = finder.getFileForSource(sourceFileData.getName());
 543  
                 }
 544  3
                 catch (IOException e)
 545  
                 {
 546  3
                         return "<p>Unable to locate " + sourceFileData.getName()
 547  
                                         + ".  Have you specified the source directory?</p>";
 548  11
                 }
 549  
 
 550  11
                 BufferedReader br = null;
 551  
                 try
 552  
                 {
 553  11
                         br = new BufferedReader(new InputStreamReader(new FileInputStream(sourceFile), "UTF-8"));
 554  
                 }
 555  0
                 catch (UnsupportedEncodingException e)
 556  
                 {
 557  0
                         return "<p>Unable to open " + sourceFile.getAbsolutePath()
 558  
                                         + ": The encoding 'UTF-8' is not supported by your JVM.</p>";
 559  
                 }
 560  0
                 catch (FileNotFoundException e)
 561  
                 {
 562  0
                         return "<p>Unable to open " + sourceFile.getAbsolutePath() + "</p>";
 563  11
                 }
 564  
 
 565  11
                 StringBuffer ret = new StringBuffer();
 566  11
                 ret
 567  
                                 .append("<table cellspacing=\"0\" cellpadding=\"0\" class=\"src\">\n");
 568  
                 try
 569  
                 {
 570  
                         String lineStr;
 571  11
                         JavaToHtml javaToHtml = new JavaToHtml();
 572  11
                         int lineNumber = 1;
 573  500
                         while ((lineStr = br.readLine()) != null)
 574  
                         {
 575  489
                                 ret.append("<tr>");
 576  489
                                 if (sourceFileData.isValidSourceLineNumber(lineNumber))
 577  
                                 {
 578  105
                                         LineData lineData = sourceFileData.getLineCoverage(lineNumber);
 579  105
                                         ret.append("  <td class=\"numLineCover\">&nbsp;"
 580  
                                                         + lineNumber + "</td>");
 581  105
                                         if ((lineData != null) && (lineData.isCovered()))
 582  
                                         {
 583  69
                                                 ret.append("  <td class=\"nbHitsCovered\">" 
 584  
                                                                 + generateBranchInfo(lineData, "&nbsp;" + ((lineData != null) ? lineData.getHits() : 0)) 
 585  
                                                                 + "</td>");
 586  69
                                                 ret
 587  
                                                         .append("  <td class=\"src\"><pre class=\"src\">&nbsp;"
 588  
                                                                         + generateBranchInfo(lineData, javaToHtml.process(lineStr))
 589  
                                                                         + "</pre></td>");
 590  69
                                         }
 591  
                                         else
 592  
                                         {
 593  36
                                                 ret.append("  <td class=\"nbHitsUncovered\">"
 594  
                                                                 + generateBranchInfo(lineData, "&nbsp;" + ((lineData != null) ? lineData.getHits() : 0))
 595  
                                                                 + "</td>");
 596  36
                                                 ret
 597  
                                                         .append("  <td class=\"src\"><pre class=\"src\"><span class=\"srcUncovered\">&nbsp;"
 598  
                                                                         + generateBranchInfo(lineData, javaToHtml.process(lineStr))
 599  
                                                                         + "</span></pre></td>");
 600  
                                         }
 601  105
                                 }
 602  
                                 else
 603  
                                 {
 604  384
                                         ret.append("  <td class=\"numLine\">&nbsp;" + lineNumber
 605  
                                                         + "</td>");
 606  384
                                         ret.append("  <td class=\"nbHits\">&nbsp;</td>\n");
 607  384
                                         ret.append("  <td class=\"src\"><pre class=\"src\">&nbsp;"
 608  
                                                         + javaToHtml.process(lineStr) + "</pre></td>");
 609  
                                 }
 610  489
                                 ret.append("</tr>\n");
 611  489
                                 lineNumber++;
 612  489
                         }
 613  
                 }
 614  0
                 catch (IOException e)
 615  
                 {
 616  0
                         ret.append("<tr><td>Error reading from file "
 617  
                                         + sourceFile.getAbsolutePath() + ": "
 618  
                                         + e.getLocalizedMessage() + "</td></tr>\n");
 619  
                 }
 620  
                 finally
 621  
                 {
 622  0
                         try
 623  
                         {
 624  11
                                 br.close();
 625  
                         }
 626  0
                         catch (IOException e)
 627  
                         {
 628  11
                         }
 629  0
                 }
 630  
 
 631  11
                 ret.append("</table>\n");
 632  
 
 633  11
                 return ret.toString();
 634  
         }
 635  
 
 636  
         private static String generateFooter()
 637  
         {
 638  25
                 return "<div class=\"footer\">Report generated by "
 639  
                                 + "<a href=\"http://cobertura.sourceforge.net/\" target=\"_top\">Cobertura</a> "
 640  
                                 + Header.version() + " on "
 641  
                                 + DateFormat.getInstance().format(new Date()) + ".</div>";
 642  
         }
 643  
 
 644  
         private static String generateTableHeader(String title,
 645  
                         boolean showColumnForNumberOfClasses)
 646  
         {
 647  32
                 StringBuffer ret = new StringBuffer();
 648  32
                 ret.append("<thead>");
 649  32
                 ret.append("<tr>");
 650  32
                 ret.append("  <td class=\"heading\">" + title + "</td>");
 651  32
                 if (showColumnForNumberOfClasses)
 652  
                 {
 653  11
                         ret.append("  <td class=\"heading\"># Classes</td>");
 654  
                 }
 655  32
                 ret.append("  <td class=\"heading\">"
 656  
                                 + generateHelpURL("Line Coverage",
 657  
                                                 "The percent of lines executed by this test run.")
 658  
                                 + "</td>");
 659  32
                 ret.append("  <td class=\"heading\">"
 660  
                                 + generateHelpURL("Branch Coverage",
 661  
                                                 "The percent of branches executed by this test run.")
 662  
                                 + "</td>");
 663  32
                 ret
 664  
                                 .append("  <td class=\"heading\">"
 665  
                                                 + generateHelpURL(
 666  
                                                                 "Complexity",
 667  
                                                                 "Average McCabe's cyclomatic code complexity for all methods.  This is basically a count of the number of different code paths in a method (incremented by 1 for each if statement, while loop, etc.)")
 668  
                                                 + "</td>");
 669  32
                 ret.append("</tr>");
 670  32
                 ret.append("</thead>");
 671  32
                 return ret.toString();
 672  
         }
 673  
 
 674  
         private static String generateHelpURL(String text, String description)
 675  
         {
 676  194
                 StringBuffer ret = new StringBuffer();
 677  194
                 boolean popupTooltips = false;
 678  194
                 if (popupTooltips)
 679  
                 {
 680  0
                         ret
 681  
                                         .append("<a class=\"hastooltip\" href=\"help.html\" onclick=\"popupwindow('help.html'); return false;\">");
 682  0
                         ret.append(text);
 683  0
                         ret.append("<span>" + description + "</span>");
 684  0
                         ret.append("</a>");
 685  0
                 }
 686  
                 else
 687  
                 {
 688  194
                         ret
 689  
                                         .append("<a class=\"dfn\" href=\"help.html\" onclick=\"popupwindow('help.html'); return false;\">");
 690  194
                         ret.append(text);
 691  194
                         ret.append("</a>");
 692  
                 }
 693  194
                 return ret.toString();
 694  
         }
 695  
 
 696  
         private String generateTableRowForTotal()
 697  
         {
 698  4
                 StringBuffer ret = new StringBuffer();
 699  4
                 double ccn = complexity.getCCNForProject(projectData);
 700  
 
 701  4
                 ret.append("  <tr>");
 702  4
                 ret.append("<td><b>All Packages</b></td>");
 703  4
                 ret.append("<td class=\"value\">"
 704  
                                 + projectData.getNumberOfSourceFiles() + "</td>");
 705  4
                 ret.append(generateTableColumnsFromData(projectData, ccn));
 706  4
                 ret.append("</tr>");
 707  4
                 return ret.toString();
 708  
         }
 709  
 
 710  
         private String generateTableRowForPackage(PackageData packageData)
 711  
         {
 712  14
                 StringBuffer ret = new StringBuffer();
 713  14
                 String url1 = "frame-summary-" + packageData.getName() + ".html";
 714  14
                 String url2 = "frame-sourcefiles-" + packageData.getName() + ".html";
 715  14
                 double ccn = complexity.getCCNForPackage(packageData);
 716  
 
 717  14
                 ret.append("  <tr>");
 718  14
                 ret.append("<td><a href=\"" + url1
 719  
                                 + "\" onclick='parent.sourceFileList.location.href=\"" + url2
 720  
                                 + "\"'>" + generatePackageName(packageData) + "</a></td>");
 721  14
                 ret.append("<td class=\"value\">" + packageData.getNumberOfChildren()
 722  
                                 + "</td>");
 723  14
                 ret.append(generateTableColumnsFromData(packageData, ccn));
 724  14
                 ret.append("</tr>");
 725  14
                 return ret.toString();
 726  
         }
 727  
 
 728  
         private String generateTableRowsForSourceFile(SourceFileData sourceFileData)
 729  
         {
 730  28
                 StringBuffer ret = new StringBuffer();
 731  28
                 String sourceFileName = sourceFileData.getNormalizedName();
 732  
                 // TODO: ccn should be calculated per-class, not per-file
 733  28
                 double ccn = complexity.getCCNForSourceFile(sourceFileData);
 734  
 
 735  28
                 Iterator iter = sourceFileData.getClasses().iterator();
 736  56
                 while (iter.hasNext())
 737  
                 {
 738  28
                         ClassData classData = (ClassData)iter.next();
 739  28
                         ret
 740  
                                         .append(generateTableRowForClass(classData, sourceFileName,
 741  
                                                         ccn));
 742  28
                 }
 743  
 
 744  28
                 return ret.toString();
 745  
         }
 746  
 
 747  
         private String generateTableRowForClass(ClassData classData,
 748  
                         String sourceFileName, double ccn)
 749  
         {
 750  28
                 StringBuffer ret = new StringBuffer();
 751  
 
 752  28
                 ret.append("  <tr>");
 753  
                 // TODO: URL should jump straight to the class (only for inner classes?)
 754  28
                 ret.append("<td><a href=\"" + sourceFileName
 755  
                                 + ".html\">" + classData.getBaseName() + "</a></td>");
 756  28
                 ret.append(generateTableColumnsFromData(classData, ccn));
 757  28
                 ret.append("</tr>\n");
 758  28
                 return ret.toString();
 759  
         }
 760  
 
 761  
         /**
 762  
          * Return a string containing three HTML table cells.  The first
 763  
          * cell contains a graph showing the line coverage, the second
 764  
          * cell contains a graph showing the branch coverage, and the
 765  
          * third cell contains the code complexity.
 766  
          *
 767  
          * @param ccn The code complexity to display.  This should be greater
 768  
          *        than 1.
 769  
          * @return A string containing the HTML for three table cells.
 770  
          */
 771  
         private static String generateTableColumnsFromData(CoverageData coverageData,
 772  
                         double ccn)
 773  
         {
 774  46
                 int numLinesCovered = coverageData.getNumberOfCoveredLines();
 775  46
                 int numLinesValid = coverageData.getNumberOfValidLines();
 776  46
                 int numBranchesCovered = coverageData.getNumberOfCoveredBranches();
 777  46
                 int numBranchesValid = coverageData.getNumberOfValidBranches();
 778  
 
 779  
                 // The "hidden" CSS class is used below to write the ccn without
 780  
                 // any formatting so that the table column can be sorted correctly
 781  46
                 return "<td>" + generatePercentResult(numLinesCovered, numLinesValid)
 782  
                                 +"</td><td>"
 783  
                                 + generatePercentResult(numBranchesCovered, numBranchesValid)
 784  
                                 + "</td><td class=\"value\"><span class=\"hidden\">"
 785  
                                 + ccn + ";</span>" + getDoubleValue(ccn) + "</td>";
 786  
         }
 787  
 
 788  
         /**
 789  
          * This is crazy complicated, and took me a while to figure out,
 790  
          * but it works.  It creates a dandy little percentage meter, from
 791  
          * 0 to 100.
 792  
          * @param dividend The number of covered lines or branches.
 793  
          * @param divisor  The number of valid lines or branches.
 794  
          * @return A percentage meter.
 795  
          */
 796  
         private static String generatePercentResult(int dividend, int divisor)
 797  
         {
 798  92
                 StringBuffer sb = new StringBuffer();
 799  
 
 800  92
                 sb.append("<table cellpadding=\"0px\" cellspacing=\"0px\" class=\"percentgraph\"><tr class=\"percentgraph\"><td align=\"right\" class=\"percentgraph\" width=\"40\">");
 801  92
                 if (divisor > 0)
 802  43
                         sb.append(getPercentValue((double)dividend / divisor));
 803  
                 else
 804  49
                         sb.append(generateHelpURL(
 805  
                                         "N/A",
 806  
                                         "Line coverage and branch coverage will appear as \"Not Applicable\" when Cobertura can not find line number information in the .class file.  This happens for stub and skeleton classes, interfaces, or when the class was not compiled with \"debug=true.\""));
 807  92
                 sb.append("</td><td class=\"percentgraph\"><div class=\"percentgraph\">");
 808  92
                 if (divisor > 0)
 809  
                 {
 810  43
                         sb.append("<div class=\"greenbar\" style=\"width:"
 811  
                                         + (dividend * 100 / divisor) + "px\">");
 812  43
                         sb.append("<span class=\"text\">");
 813  43
                         sb.append(dividend);
 814  43
                         sb.append("/");
 815  43
                         sb.append(divisor);
 816  43
                 }
 817  
                 else
 818  
                 {
 819  49
                         sb.append("<div class=\"na\" style=\"width:100px\">");
 820  49
                         sb.append("<span class=\"text\">");
 821  49
                         sb.append(generateHelpURL(
 822  
                                         "N/A",
 823  
                                         "Line coverage and branch coverage will appear as \"Not Applicable\" when Cobertura can not find line number information in the .class file.  This happens for stub and skeleton classes, interfaces, or when the class was not compiled with \"debug=true.\""));
 824  
                 }
 825  92
                 sb.append("</span></div></div></td></tr></table>");
 826  
 
 827  92
                 return sb.toString();
 828  
         }
 829  
 
 830  
         private static String getDoubleValue(double value)
 831  
         {
 832  
                 NumberFormat formatter;
 833  46
                 formatter = new DecimalFormat();
 834  46
                 return formatter.format(value);
 835  
         }
 836  
 
 837  
         private static String getPercentValue(double value)
 838  
         {
 839  63
                 return StringUtil.getPercentValue(value);
 840  
         }
 841  
 
 842  
 }