1 package net.jaekl.cfb.analyze;
4 import java.io.IOException;
5 import java.io.PrintWriter;
7 import net.jaekl.cfb.CfbBundle;
8 import net.jaekl.cfb.store.Location;
9 import net.jaekl.cfb.util.Command;
10 import net.jaekl.cfb.util.XmlEscape;
11 import net.jaekl.cfb.xml.BugInstance;
12 import net.jaekl.cfb.xml.Variable;
13 import net.jaekl.cfb.xml.messages.BugPattern;
14 import net.jaekl.cfb.xml.messages.MessageCollection;
16 public class HtmlReport {
17 private static final String NBSP = " "; // " " isn't valid XML, but " " is.
21 MessageCollection m_msgColl;
23 public HtmlReport(CfbBundle bundle, MessageCollection msgColl, Delta delta)
30 public Delta getDelta() { return m_delta; }
32 public void write(File output) throws IOException
34 try ( PrintWriter pw = new PrintWriter(output, Command.UTF_8) )
41 void write(PrintWriter pw)
46 void startPage(PrintWriter pw)
49 pw.println(" <BODY>");
51 pw.println(" </BODY>");
52 pw.println("</HTML>");
55 String trans(String key, Object... arguments) {
56 return m_bundle.get(key, arguments);
59 void writeBody(PrintWriter pw)
62 writeBugs(pw, CfbBundle.NEW_BUGS, m_delta.getNew());
63 writeBugs(pw, CfbBundle.FIXED_BUGS, m_delta.getFixed());
64 writeBugs(pw, CfbBundle.OLD_BUGS, m_delta.getCommon());
68 void writeBugLocations(PrintWriter pw, BugInstance bug)
70 for (Location loc : bug.getLocations()) {
71 StringBuffer sb = new StringBuffer();
74 if (null != loc.getClassName()) {
75 sb.append(loc.getClassName());
77 if ((null != loc.getMethodName()) && (loc.getMethodName().length() > 0)) {
78 sb.append(".").append(loc.getMethodName()).append("()");
80 int start = loc.getStart();
81 int end = loc.getEnd();
83 sb.append(":").append("" + start);
84 if ((end > 0) && (end > start)) {
85 sb.append("-").append("" + end);
89 if (null != loc.getMethodRole()) {
90 sb.append(" (" + loc.getMethodRole() + ")");
94 String displayText = XmlEscape.toEscaped(sb.toString());
96 pw.println(" <TR><TD COLSPAN=\"2\" CLASS=\"Loc\">" + displayText + "</TD></TR>");
100 void writeBugs(PrintWriter pw, String key, BugInstance[] bugs)
102 if (null == bugs || bugs.length < 1) {
106 writeSectionHeading(pw, trans(key));
108 for (BugInstance bug : bugs) {
109 BugPattern pattern = m_msgColl.getPattern(bug.getType());
112 pw.println(" <TABLE CLASS=\"Bug\">");
114 pw.println(" <TD WIDTH=\"20%\">" + bug.getCategory() + "</TD>");
115 pw.println(" <TD>" + bug.getType() + "</TD>");
116 pw.println(" </TR>");
117 writeBugLocations(pw, bug);
118 writeBugVariables(pw, bug);
119 pw.println(" <TR><TD COLSPAN=\"2\"><B>" + pattern.getShort() + "</B></TD></TR>");
121 pw.println(" <TD COLSPAN=\"2\">" + pattern.getDetails() + "</TD>");
122 pw.println(" </TR>");
123 pw.println(" </TABLE>");
125 pw.println(" <HR/>");
129 void writeBugVariables(PrintWriter pw, BugInstance bug)
131 for (Variable var : bug.getVariables()) {
132 StringBuffer sb = new StringBuffer();
135 sb.append(var.getDescription() + "<BR/>");
138 pw.println(" <TR><TD COLSPAN=\"2\" CLASS=\"Var\">" + sb.toString() + "</TD></TR>");
142 void writeFooter(PrintWriter pw)
144 pw.println(" <DIV CLASS=\"Footer\">Report generated by <A HREF=\"https://jaekl.net/cfb/\">CFB</A></DIV>");
147 void writeHeader(PrintWriter pw)
149 String title = trans(CfbBundle.CFB_REPORT);
151 pw.println("<HTML>");
152 pw.println(" <HEAD>");
153 pw.println(" <META CHARSET=\"UTF-8\"/>");
154 pw.println(" <TITLE>" + title + "</TITLE>");
156 pw.println(" </HEAD>");
159 void writeSectionHeading(PrintWriter pw, String heading)
161 pw.println(" <P CLASS=\"SectionHead\">");
162 pw.println(" <TABLE WIDTH=\"100%\"><TR><TD>" + heading + "</TD></TR></TABLE>");
166 void writeSummary(PrintWriter pw)
168 final String SEP = ":" + NBSP + NBSP;
170 String earlierVersion = "";
171 if (null != m_delta.getEarlier()) {
172 earlierVersion = m_delta.getEarlier().constructVersionText(m_bundle);
175 writeSectionHeading(pw, trans(CfbBundle.CFB_REPORT));
177 pw.println(" <TABLE>");
179 pw.println(" <TD CLASS=\"CategoryName\">" + trans(CfbBundle.NEW_VERSION) + SEP + "</TD>");
180 pw.println(" <TD CLASS=\"CategoryValue\">" + m_delta.getLater().constructVersionText(m_bundle) + "</TD>");
181 pw.println(" </TR>");
183 pw.println(" <TD CLASS=\"CategoryName\">" + trans(CfbBundle.OLD_VERSION) + SEP + "</TD>");
184 pw.println(" <TD CLASS=\"CategoryValue\">" + earlierVersion + "</TD>");
185 pw.println(" </TR>");
186 pw.println(" <TR><TD>" + NBSP + "</TD></TR>");
188 pw.println(" <TD CLASS=\"CategoryName\">" + trans(CfbBundle.NEW_BUGS) + SEP + "</TD>");
189 pw.println(" <TD CLASS=\"CategoryValue\">" + trans(CfbBundle.NUM_BUGS, m_delta.getNumNew()) + "</TD>");
190 pw.println(" </TR>");
192 pw.println(" <TD CLASS=\"CategoryName\">" + trans(CfbBundle.FIXED_BUGS) + SEP + "</TD>");
193 pw.println(" <TD CLASS=\"CategoryValue\">" + trans(CfbBundle.NUM_BUGS, m_delta.getNumFixed()) + "</TD>");
194 pw.println(" </TR>");
196 pw.println(" <TD CLASS=\"CategoryName\">" + trans(CfbBundle.OLD_BUGS) + SEP + "</TD>");
197 pw.println(" <TD CLASS=\"CategoryValue\">" + trans(CfbBundle.NUM_BUGS_OLD, m_delta.getNumCommon()) + "</TD>");
198 pw.println(" </TR>");
199 pw.println(" </TABLE>");
203 void writeStyle(PrintWriter pw)
205 pw.println(" <STYLE>");
206 pw.println(" body { background-color: #F0F0FF; }");
207 pw.println(" .CategoryName { text-align: right; }");
208 pw.println(" .CategoryValue { text-align: left; }");
209 pw.println(" div.Footer { background-color: #0000FF; color: #FFFFFF; font-size: 0.9em; text-align: right; }");
210 pw.println(" div.Footer a:link { color: #FFFFFF }");
211 pw.println(" .Loc { font-family: monospace; }");
212 pw.println(" .SectionHead td { background-color: #0000FF; color: #FFFFFF; font-size: 1.25em; font-weight: bold; }");
213 pw.println(" .Var { font-family: monospace; }");
214 pw.println(" </STYLE>");