adds footer to the HTML report.
[cfb.git] / prod / net / jaekl / cfb / analyze / HtmlReport.java
1 package net.jaekl.cfb.analyze;
2
3 import java.io.File;
4 import java.io.IOException;
5 import java.io.PrintWriter;
6
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;
15
16 public class HtmlReport {
17         private static final String NBSP = " ";    // " " isn't valid XML, but " " is.
18         
19         CfbBundle m_bundle;
20         Delta m_delta;
21         MessageCollection m_msgColl;
22         
23         public HtmlReport(CfbBundle bundle, MessageCollection msgColl, Delta delta)
24         {
25                 m_bundle = bundle;
26                 m_delta = delta;
27                 m_msgColl = msgColl;
28         }
29         
30         public Delta getDelta() { return m_delta; }
31         
32         public void write(File output) throws IOException
33         {
34                 try ( PrintWriter pw = new PrintWriter(output, Command.UTF_8) )
35                 {
36                         write(pw);
37                         pw.flush();
38                 }
39         }
40         
41         void write(PrintWriter pw) 
42         {
43                 startPage(pw);
44         }
45         
46         void startPage(PrintWriter pw) 
47         {
48                 writeHeader(pw);
49                 pw.println("  <BODY>");
50                 writeBody(pw);
51                 pw.println("  </BODY>");
52                 pw.println("</HTML>");
53         }
54         
55         String trans(String key, Object... arguments) {
56                 return m_bundle.get(key, arguments);
57         }
58         
59         void writeBody(PrintWriter pw)
60         {
61                 writeSummary(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());
65                 writeFooter(pw);
66         }
67         
68         void writeBugLocations(PrintWriter pw, BugInstance bug)
69         {
70                 for (Location loc : bug.getLocations()) {
71                         StringBuffer sb = new StringBuffer();
72                         
73                         if (null != loc) {
74                                 if (null != loc.getClassName()) {
75                                         sb.append(loc.getClassName());
76                                 }
77                                 if ((null != loc.getMethodName()) && (loc.getMethodName().length() > 0)) {
78                                         sb.append(".").append(loc.getMethodName()).append("()");
79                                 }
80                                 int start = loc.getStart();
81                                 int end = loc.getEnd();
82                                 if (start > 0) {
83                                         sb.append(":").append("" + start);
84                                         if ((end > 0) && (end > start)) {
85                                                 sb.append("-").append("" + end);
86                                         }
87                                 }
88                                 
89                                 if (null != loc.getMethodRole()) {
90                                         sb.append(" (" + loc.getMethodRole() + ")");
91                                 }
92                         }
93                         
94                         String displayText = XmlEscape.toEscaped(sb.toString());
95                         
96                         pw.println("        <TR><TD COLSPAN=\"2\" CLASS=\"Loc\">" + displayText + "</TD></TR>");                                        
97                 }
98         }
99         
100         void writeBugs(PrintWriter pw, String key, BugInstance[] bugs)
101         {
102                 if (null == bugs || bugs.length < 1) {
103                         return;
104                 }
105                 
106                 writeSectionHeading(pw, trans(key));
107                 
108                 for (BugInstance bug : bugs) {
109                         BugPattern pattern = m_msgColl.getPattern(bug.getType());
110                         
111                         pw.println("    <P>");
112                         pw.println("      <TABLE CLASS=\"Bug\">");
113                         pw.println("        <TR>");
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>");
120                         pw.println("        <TR>");
121                         pw.println("          <TD COLSPAN=\"2\">" + pattern.getDetails() + "</TD>");
122                         pw.println("        </TR>");
123                         pw.println("      </TABLE>");
124                         pw.println("    </P>");
125                         pw.println("    <HR/>");
126                 }
127         }
128         
129         void writeBugVariables(PrintWriter pw, BugInstance bug)
130         {
131                 for (Variable var : bug.getVariables()) {
132                         StringBuffer sb = new StringBuffer();
133                         
134                         if (null != var) {
135                                 sb.append(var.getDescription() + "<BR/>");
136                         }
137                         
138                         pw.println("        <TR><TD COLSPAN=\"2\" CLASS=\"Var\">" + sb.toString() + "</TD></TR>");                                      
139                 }
140         }
141         
142         void writeFooter(PrintWriter pw)
143         {
144                 pw.println("    <DIV CLASS=\"Footer\">Report generated by <A HREF=\"https://jaekl.net/cfb/\">CFB</A></DIV>");
145         }
146         
147         void writeHeader(PrintWriter pw)
148         {
149                 String title = trans(CfbBundle.CFB_REPORT);
150                 
151                 pw.println("<HTML>");
152                 pw.println("  <HEAD>");
153                 pw.println("    <META CHARSET=\"UTF-8\"/>");
154                 pw.println("    <TITLE>" + title + "</TITLE>");
155                 writeStyle(pw);
156                 pw.println("  </HEAD>");
157         }
158         
159         void writeSectionHeading(PrintWriter pw, String heading)
160         {
161                 pw.println("    <P CLASS=\"SectionHead\">");
162                 pw.println("      <TABLE WIDTH=\"100%\"><TR><TD>" + heading + "</TD></TR></TABLE>");
163                 pw.println("    </P>");
164         }
165         
166         void writeSummary(PrintWriter pw)
167         {
168                 final String SEP = ":" + NBSP + NBSP;
169                 
170                 String earlierVersion = "";
171                 if (null != m_delta.getEarlier()) {
172                         earlierVersion = m_delta.getEarlier().constructVersionText(m_bundle);
173                 }
174                 
175                 writeSectionHeading(pw, trans(CfbBundle.CFB_REPORT));
176                 pw.println("    <P>");
177                 pw.println("      <TABLE>");
178                 pw.println("        <TR>");
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>");
182                 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>");
187                 pw.println("        <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>");
191                 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>");
195                 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>");
200                 pw.println("    </P>");
201         }
202         
203         void writeStyle(PrintWriter pw)
204         {
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>");
215         }
216 }