package hudson.plugins.violations.generate;
import java.io.PrintWriter;
import java.io.IOException;
import java.io.FileReader;
import java.io.Reader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.FileInputStream;
import java.io.BufferedReader;
import java.util.Set;
import java.util.Map;
import java.util.TreeSet;
import java.util.HashMap;
import java.util.SortedMap;
import java.util.TreeMap;
import hudson.plugins.violations.util.CloseUtil;
import hudson.plugins.violations.model.Violation;
import hudson.plugins.violations.model.FullFileModel;
import hudson.plugins.violations.ViolationsConfig;
/**
* Class to output a file model.
*/
public class OutputFileModel implements Execute {
private static final int NEAR = 10;
private static class LimitedType {
private int number = 0;
private Set<Violation> violations = new TreeSet<Violation>();
}
private final FullFileModel fileModel;
private final ViolationsConfig config;
private PrintWriter w;
private BufferedReader sourceReader = null;
private HashMap<Integer, Set<Violation>> vMap
= new HashMap<Integer, Set<Violation>>();
private SortedMap<String, LimitedType> limitedMap
= new TreeMap<String, LimitedType>();
/**
* Create a class to output a file model.
* @param fileModel the model to output.
* @param config the configuration - used to suppress outputing
* violations.
*/
public OutputFileModel(FullFileModel fileModel,
ViolationsConfig config) {
this.fileModel = fileModel;
this.config = config;
}
/**
* Output the file model.
* @param w the output writer.
* @throws IOException if there is a problem.
*/
public void execute(PrintWriter w) throws IOException {
this.w = w;
w.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
w.println("<file");
w.print(" ");
w.println(XMLUtil.toAttribute("name", fileModel.getDisplayName()));
w.print(" ");
w.println(XMLUtil.toAttribute(
"last-modified",
"" + fileModel.getLastModified()));
if (fileModel.getSourceFile() != null) {
w.print(" ");
w.println(XMLUtil.toAttribute(
"file", fileModel.getSourceFile().getAbsolutePath()));
}
w.println(">");
createLimited();
if (fileModel.getSourceFile() != null
&& fileModel.getSourceFile().exists()) {
try {
outputContents();
} finally {
closeSourceFile();
}
}
outputSummary();
w.println("</file>");
}
private void addToVMap(Violation v) {
if (vMap.get(v.getLine()) == null) {
vMap.put(v.getLine(), new TreeSet<Violation>());
}
vMap.get(v.getLine()).add(v);
}
private void createLimited() throws IOException {
for (Map.Entry<String, TreeSet<Violation>> e
: fileModel.getTypeMap().entrySet()) {
String type = e.getKey();
Set<Violation> violations = e.getValue();
LimitedType limitedType = new LimitedType();
limitedType.number = violations.size();
int c = 0;
for (Violation v: violations) {
limitedType.violations.add(v);
addToVMap(v);
doViolation(v);
c++;
if (c >= config.getLimit()) {
break;
}
}
limitedMap.put(type, limitedType);
}
}
private void closeSourceFile() {
CloseUtil.close(sourceReader);
sourceReader = null;
}
private boolean near(int lineNumber) {
for (int i = lineNumber - NEAR; i < lineNumber + NEAR; ++i) {
if (vMap.get(i) != null) {
return true;
}
}
return false;
}
private void outputSummary() throws IOException {
for (Map.Entry<String, LimitedType> e: limitedMap.entrySet()) {
String type = e.getKey();
LimitedType t = e.getValue();
w.println(" <type type='" + type + "' number='" + t.number
+ "' suppressed='"
+ ((t.number <= config.getLimit()) ? 0
: t.number - config.getLimit())
+ "'/>");
}
}
private Reader open() throws IOException {
if ("default".equals(config.getEncoding())) {
return new FileReader(fileModel.getSourceFile());
}
InputStream is = new FileInputStream(fileModel.getSourceFile());
try {
return new InputStreamReader(is, config.getEncoding());
} catch (IOException ex) {
CloseUtil.close(is);
throw ex;
}
}
private void outputContents() throws IOException {
sourceReader = new BufferedReader(open());
int lineNumber = 1;
while (true) {
String line = sourceReader.readLine();
if (line == null) {
break;
}
if (!near(lineNumber)) {
lineNumber++;
continue;
}
w.print(" <line number='" + lineNumber + "'>");
w.print(XMLUtil.escapeContent(line));
w.println("</line>");
lineNumber++;
}
}
private void printAttr(String name, Object value)
throws IOException {
if (value == null) {
return;
}
w.print (" ");
w.println(XMLUtil.toAttribute(name, value.toString()));
}
private void doViolation(Violation v)
throws IOException {
w.println(" <violation");
printAttr("line", v.getLine());
printAttr("source", v.getSource());
printAttr("severity", v.getSeverity());
printAttr("type", v.getType());
printAttr("message", v.getMessage().trim());
printAttr("severity-level", v.getSeverityLevel());
if (v.getPopupMessage() != null) {
printAttr("popup-message", v.getPopupMessage().trim());
}
w.println(" />");
}
}