package hudson.plugins.cobertura.renderers;
import static hudson.plugins.cobertura.IOUtils.closeQuietly;
import hudson.FilePath;
import hudson.model.TaskListener;
import hudson.plugins.cobertura.targets.CoveragePaint;
import hudson.remoting.VirtualChannel;
import jenkins.MasterToSlaveFileCallable;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* TODO javadoc.
*
* @author Stephen Connolly
* @since 31-Aug-2007 16:52:25
*/
public class SourceCodePainter extends MasterToSlaveFileCallable<Boolean> implements Serializable {
private final Set<String> sourcePaths;
private final Map<String, CoveragePaint> paint;
private final FilePath destination;
private final TaskListener listener;
private final SourceEncoding sourceEncoding;
public SourceCodePainter(FilePath destination, Set<String> sourcePaths, Map<String, CoveragePaint> paint, TaskListener listener,
SourceEncoding sourceEncoding) {
this.destination = destination;
this.sourcePaths = sourcePaths;
this.paint = paint;
this.listener = listener;
this.sourceEncoding = sourceEncoding;
}
public void paintSourceCode(File source, CoveragePaint paint, FilePath canvas) throws IOException, InterruptedException {
OutputStream os = null;
FileInputStream is = null;
InputStreamReader reader = null;
BufferedReader input = null;
OutputStreamWriter bos = null;
BufferedWriter output = null;
int line = 0;
try {
canvas.getParent().mkdirs();
os = canvas.write();
is = new FileInputStream(source);
reader = new InputStreamReader(is, getSourceEncoding().getEncodingName());
input = new BufferedReader(reader);
bos = new OutputStreamWriter(os, "UTF-8");
output = new BufferedWriter(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.write("<tr class=\"coverFull\">\n");
} else {
output.write("<tr class=\"coverPart\" title=\"Line " + line + ": Conditional coverage " + coveragePercent + "% ("
+ branchCoverage + "/" + branchTotal + ")\">\n");
}
} else {
output.write("<tr class=\"coverNone\">\n");
}
output.write("<td class=\"line\"><a name='" + line + "'/>" + line + "</td>\n");
output.write("<td class=\"hits\">" + hits + "</td>\n");
} else {
output.write("<tr class=\"noCover\">\n");
output.write("<td class=\"line\"><a name='" + line + "'/>" + line + "</td>\n");
output.write("<td class=\"hits\"/>\n");
}
output.write("<td class=\"code\">"
+ content.replace("&", "&").replace("<", "<").replace(">", ">").replace("\n", "").replace("\r", "").replace(" ",
" ").replace("\t", " ") + "</td>\n");
output.write("</tr>\n");
}
} finally {
closeQuietly(output);
closeQuietly(bos);
closeQuietly(input);
closeQuietly(is);
closeQuietly(os);
closeQuietly(reader);
}
}
/**
* {@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;
}
public SourceEncoding getSourceEncoding() {
if (sourceEncoding == null) {
return SourceEncoding.UTF_8;
}
return sourceEncoding;
}
}