package hudson.plugins.cobertura.renderers;
import hudson.FilePath;
import hudson.model.BuildListener;
import hudson.plugins.cobertura.targets.CoveragePaint;
import hudson.remoting.VirtualChannel;
import java.io.*;
import java.util.*;
/**
* TODO javadoc.
*
* @author Stephen Connolly
* @since 31-Aug-2007 16:52:25
*/
public class SourceCodePainter implements FilePath.FileCallable<Boolean>, Serializable {
private final Set<String> sourcePaths;
private final Map<String, CoveragePaint> paint;
private final FilePath destination;
private final BuildListener listener;
public SourceCodePainter(FilePath destination, Set<String> sourcePaths,
Map<String, CoveragePaint> paint, BuildListener listener) {
this.destination = destination;
this.sourcePaths = sourcePaths;
this.paint = paint;
this.listener = listener;
}
public void paintSourceCode(File source, CoveragePaint paint, FilePath canvas) throws IOException, InterruptedException {
OutputStream os = null;
Reader reader = null;
BufferedReader input = null;
BufferedOutputStream bos = null;
PrintStream output = null;
int line = 0;
try {
canvas.getParent().mkdirs();
os = canvas.write();
reader = new FileReader(source);
input = new BufferedReader(reader);
bos = new BufferedOutputStream(os);
output = new PrintStream(bos);
String content;
while ((content = input.readLine()) != null) {
line++;
if (paint.isPainted(line)) {
final int hits = paint.getHits(line);
final int branchCoverage = paint.getBranchCoverage(line);
final int branchTotal = paint.getBranchTotal(line);
final int coveragePercent = (hits == 0) ? 0 : (int) (branchCoverage * 100.0 / branchTotal);
if (paint.getHits(line) > 0) {
if (branchTotal == branchCoverage) {
output.println("<tr class=\"coverFull\">");
} else {
output.println("<tr class=\"coverPart\" title=\"Line "
+ line + ": Conditional coverage " + coveragePercent + "% ("
+ branchCoverage + "/" + branchTotal + ")\">");
}
} else {
output.println("<tr class=\"coverNone\">");
}
output.println("<td class=\"line\">" + line + "</td>");
output.println("<td class=\"hits\">" + hits + "</td>");
} else {
output.println("<tr class=\"noCover\">");
output.println("<td class=\"line\">" + line + "</td>");
output.println("<td class=\"hits\"/>");
}
output.println("<td class=\"code\">" + content.replace("&", "&").replace("<", "<")
.replace(">", ">").replace("\n", "").replace("\r", "").replace(" ", " ")
.replace("\t", " ")
+ "</td>");
output.println("</tr>");
}
} finally {
if (output != null) {
output.close();
}
if (bos != null) {
bos.close();
}
if (input != null) {
input.close();
}
if (os != null) {
os.close();
}
if (reader != null) {
reader.close();
}
}
}
/**
* {@inheritDoc}
*/
public Boolean invoke(File workspaceDir, VirtualChannel channel) throws IOException {
final List<File> trialPaths = new ArrayList<File>(sourcePaths.size());
for (String sourcePath : sourcePaths) {
final File trialPath = new File(sourcePath);
if (trialPath.exists()) {
trialPaths.add(trialPath);
}
final File trialPath2 = new File(workspaceDir, sourcePath);
if (trialPath2.exists() && !trialPath2.equals(trialPath)) {
trialPaths.add(trialPath2);
}
}
for (Map.Entry<String, CoveragePaint> entry : paint.entrySet()) {
// first see if we can find the file
File source = new File(workspaceDir, entry.getKey());
final Iterator<File> possiblePath = trialPaths.iterator();
while (!source.exists() && possiblePath.hasNext()) {
source = new File(possiblePath.next(), entry.getKey());
}
if (source.isFile()) {
try {
paintSourceCode(source, entry.getValue(), destination.child(entry.getKey()));
} catch (IOException e) {
// We made our best shot at generating painted source code,
// but alas, we failed. Log the error and continue. We
// should not fail the build just because we cannot paint
// one file.
e.printStackTrace(
listener.error(
"ERROR: Failure to paint " + source +
" to "+ destination));
} catch (InterruptedException e) {
return Boolean.FALSE;
}
}
}
return Boolean.TRUE;
}
}