/*
* Copyright 2012 Kantega AS
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.kantega.revoc.report;
import org.kantega.revoc.registry.BranchPoint;
import org.kantega.revoc.registry.CoverageData;
import org.kantega.revoc.source.SourceSource;
import org.apache.commons.io.IOUtils;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.List;
/**
*
*/
public class HtmlReport {
public void run(CoverageData coverageData, String selectedClass, Writer writer, SourceSource sourceSource) {
long now = System.currentTimeMillis();
coverageData = new CoverageFolder().fold(coverageData);
PrintWriter pw = new PrintWriter(writer);
pw.println(" <!DOCTYPE html><html>");
pw.println("<head>");
pw.println("<style type=\"text/css\">");
try {
IOUtils.copy(new InputStreamReader(HtmlReport.class.getResourceAsStream("revoc.css"), "utf-8"), writer);
} catch (IOException e) {
throw new RuntimeException(e);
}
pw.println("</style>");
pw.println("</head>");
pw.println("<body>");
pw.println("<h1>Revoc Coverage Report</h1>");
printMenu(pw);
pw.println("<div id=\"classlist\">");
String[] classNames = coverageData.getClassNames();
for (int i = 0; i < classNames.length; i++) {
String className = coverageData.getClassNames()[i];
if(selectedClass != null && !selectedClass.equals(className)) {
continue;
}
pw.println("<h2>Class " + className.replace('/', '.') +"</h2>");
String sourceFile = coverageData.getSourceFiles()[i];
long[] linesVisited = coverageData.getLinesVisited(i);
String sourceClassname = className;
if(sourceFile != null && !(className +".java").endsWith(sourceFile)) {
sourceClassname = className.substring(0, className.lastIndexOf("/")+1) + sourceFile.substring(0, sourceFile.length()-".java".length());
}
String[] sourceLines = sourceSource.getSource(sourceClassname, coverageData.getClassLoader(i));
if (sourceLines == null) {
pw.println("<p>Found no source for class " + className +"</p>");
} else {
pw.println("<table>");
for (int l = 0; l < sourceLines.length; l++) {
// noline, nonvisited, visited
// singlebranched, branched
StringBuffer conditionals = new StringBuffer();
StringBuffer conditionalExpl = new StringBuffer();
List<BranchPoint> singleBranchPoints = new ArrayList<BranchPoint>();
// Row with classes
pw.print("<tr class=\"");
if(l >= linesVisited.length || linesVisited[l] < 0) {
pw.print("noline");
} else if(linesVisited[l] == 0) {
pw.print("nonvisited");
} else {
pw.print("visited");
}
if(l < linesVisited.length ) {
BranchPoint[] branchPointsForLine = coverageData.getBranchPointsForLine(i, l);
if(branchPointsForLine.length > 0) {
pw.print(" branched");
}
for(int b = 0; b < branchPointsForLine.length; b++) {
BranchPoint branchPoint = branchPointsForLine[b];
conditionals.append((branchPoint.getBefore()-branchPoint.getAfter())+ "/" + branchPoint.getAfter());
if(b < branchPointsForLine.length-1) {
conditionals.append(", ");
}
if(branchPoint.isNeverBranched() || branchPoint.isAlwaysBranched()) {
singleBranchPoints.add(branchPoint);
}
}
if(!singleBranchPoints.isEmpty() && linesVisited[l] != 0) {
pw.print(" singlebranched");
}
for(int b = 0; b < singleBranchPoints.size(); b++) {
BranchPoint branchPoint = singleBranchPoints.get(b);
conditionalExpl.append("Condition " + (b+1) +" " +(branchPoint.isNeverBranched() ? "never" : "always") +" branched. (" + (branchPoint.getBefore()-branchPoint.getAfter()) + " of " + branchPoint.getBefore() +" times)");
if(b < singleBranchPoints.size()-1) {
conditionalExpl.append("\n");
}
}
}
pw.println("\">");
// Line number
pw.println("<td class=linenumber id=\"line-" +(l+1) +"\"><a name=\"" +(l + 1) +"\">" +(l + 1) +"</a></td>");
// Num visits
pw.print("<td class=numvisits>");
if (l >= linesVisited.length) {
} else {
long numvisits = linesVisited[l];
if (numvisits == 0) {
pw.print("0");
} else if(numvisits > 0) {
pw.print(numvisits);
}
}
pw.println("</td>");
//
pw.print("<td class=\"conditional\">");
if(conditionals.length() > 0 && linesVisited[l] != 0) {
pw.print("<a title=\"" + conditionalExpl.toString() +"\">" +conditionals.toString() +"</a>");
}
pw.println("</td>");
String time = "";
if(l < linesVisited.length && coverageData.getLinesVisitTimes(i)[l] >0) {
final long secondsSince = (now - coverageData.getLinesVisitTimes(i)[l]) / 1000;
if(secondsSince <= 60) {
time += secondsSince +"s";
} else if(secondsSince < 3600) {
time += (secondsSince/60) +"m";
} else if(secondsSince < 86400) {
time += (secondsSince/3600) +"h";
} else {
time += (secondsSince/86400) +"d";
}
}
pw.println("<td class=\"lastvisit\">" + time +"</td>");
pw.println("<td class=sourceline><span><pre>");
String formattedSource = sourceLines[l].replaceAll("<", "<").replaceAll(">", ">");
if(conditionals.length() > 0) {
pw.print("<a title=\"" + conditionalExpl.toString() +"\">");
}
pw.print(formattedSource);
if(conditionals.length() > 0) {
pw.println("</a>");
}
pw.println("</pre></span>");
pw.println("</td>");
pw.println("</tr>");
}
pw.println("</table>");
}
}
pw.println("</div>");
pw.println("</body></html>");
}
protected void printMenu(PrintWriter pw) {
}
}