Display LocalVariables along with Locations in 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.LocalVariable;
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         }
66         
67         void writeBugLocations(PrintWriter pw, BugInstance bug)
68         {
69                 for (Location loc : bug.getLocations()) {
70                         StringBuffer sb = new StringBuffer();
71                         
72                         if (null != loc) {
73                                 if (null != loc.getClassName()) {
74                                         sb.append(loc.getClassName());
75                                 }
76                                 if ((null != loc.getMethodName()) && (loc.getMethodName().length() > 0)) {
77                                         sb.append(".").append(loc.getMethodName()).append("()");
78                                 }
79                                 int start = loc.getStart();
80                                 int end = loc.getEnd();
81                                 if (start > 0) {
82                                         sb.append(":").append("" + start);
83                                         if ((end > 0) && (end > start)) {
84                                                 sb.append("-").append("" + end);
85                                         }
86                                 }
87                                 
88                                 if (null != loc.getMethodRole()) {
89                                         sb.append(" (" + loc.getMethodRole() + ")");
90                                 }
91                         }
92                         
93                         String displayText = XmlEscape.toEscaped(sb.toString());
94                         
95                         pw.println("        <TR><TD COLSPAN=\"2\" CLASS=\"Loc\">" + displayText + "</TD></TR>");                                        
96                 }
97         }
98         
99         void writeBugs(PrintWriter pw, String key, BugInstance[] bugs)
100         {
101                 if (null == bugs || bugs.length < 1) {
102                         return;
103                 }
104                 
105                 writeSectionHeading(pw, trans(key));
106                 
107                 for (BugInstance bug : bugs) {
108                         BugPattern pattern = m_msgColl.getPattern(bug.getType());
109                         
110                         pw.println("    <P>");
111                         pw.println("      <TABLE CLASS=\"Bug\">");
112                         pw.println("        <TR>");
113                         pw.println("          <TD WIDTH=\"20%\">" + bug.getCategory() + "</TD>");
114                         pw.println("          <TD>" + bug.getType() + "</TD>");
115                         pw.println("        </TR>");
116                         writeBugLocations(pw, bug);
117                         writeBugVariables(pw, bug);
118                         pw.println("        <TR><TD COLSPAN=\"2\"><B>" + pattern.getShort() + "</B></TD></TR>");
119                         pw.println("        <TR>");
120                         pw.println("          <TD COLSPAN=\"2\">" + pattern.getDetails() + "</TD>");
121                         pw.println("        </TR>");
122                         pw.println("      </TABLE>");
123                         pw.println("    </P>");
124                         pw.println("    <HR/>");
125                 }
126         }
127         
128         void writeBugVariables(PrintWriter pw, BugInstance bug)
129         {
130                 for (LocalVariable var : bug.getVariables()) {
131                         StringBuffer sb = new StringBuffer();
132                         
133                         if (null != var) {
134                                 String name = var.getName();
135                                 String role = var.getRole();
136                                 sb.append(name);
137                                 if (null != role) {
138                                         sb.append(" (").append(role).append(")");
139                                 }
140                         }
141                         
142                         pw.println("        <TR><TD COLSPAN=\"2\" CLASS=\"Var\">" + sb.toString() + "</TD></TR>");                                      
143                 }
144         }
145         
146         void writeHeader(PrintWriter pw)
147         {
148                 String title = trans(CfbBundle.CFB_REPORT);
149                 
150                 pw.println("<HTML>");
151                 pw.println("  <HEAD>");
152                 pw.println("    <META CHARSET=\"UTF-8\"/>");
153                 pw.println("    <TITLE>" + title + "</TITLE>");
154                 writeStyle(pw);
155                 pw.println("  </HEAD>");
156         }
157         
158         void writeSectionHeading(PrintWriter pw, String heading)
159         {
160                 pw.println("    <P CLASS=\"SectionHead\">");
161                 pw.println("      <TABLE WIDTH=\"100%\"><TR><TD>" + heading + "</TD></TR></TABLE>");
162                 pw.println("    </P>");
163         }
164         
165         void writeSummary(PrintWriter pw)
166         {
167                 final String SEP = ":" + NBSP + NBSP;
168                 
169                 String earlierVersion = "";
170                 if (null != m_delta.getEarlier()) {
171                         earlierVersion = m_delta.getEarlier().constructVersionText(m_bundle);
172                 }
173                 
174                 writeSectionHeading(pw, trans(CfbBundle.CFB_REPORT));
175                 pw.println("    <P>");
176                 pw.println("      <TABLE>");
177                 pw.println("        <TR>");
178                 pw.println("          <TD CLASS=\"CategoryName\">" + trans(CfbBundle.NEW_VERSION) + SEP + "</TD>");
179                 pw.println("          <TD CLASS=\"CategoryValue\">" + m_delta.getLater().constructVersionText(m_bundle) + "</TD>");
180                 pw.println("        </TR>");
181                 pw.println("        <TR>");
182                 pw.println("          <TD CLASS=\"CategoryName\">" + trans(CfbBundle.OLD_VERSION) + SEP + "</TD>");
183                 pw.println("          <TD CLASS=\"CategoryValue\">" + earlierVersion + "</TD>");
184                 pw.println("        </TR>");
185                 pw.println("        <TR><TD>" + NBSP + "</TD></TR>");
186                 pw.println("        <TR>");
187                 pw.println("          <TD CLASS=\"CategoryName\">" + trans(CfbBundle.NEW_BUGS) + SEP + "</TD>");
188                 pw.println("          <TD CLASS=\"CategoryValue\">" + trans(CfbBundle.NUM_BUGS, m_delta.getNumNew()) + "</TD>");
189                 pw.println("        </TR>");
190                 pw.println("        <TR>");
191                 pw.println("          <TD CLASS=\"CategoryName\">" + trans(CfbBundle.FIXED_BUGS) + SEP + "</TD>");
192                 pw.println("          <TD CLASS=\"CategoryValue\">" + trans(CfbBundle.NUM_BUGS, m_delta.getNumFixed()) + "</TD>");
193                 pw.println("        </TR>");
194                 pw.println("        <TR>");
195                 pw.println("          <TD CLASS=\"CategoryName\">" + trans(CfbBundle.OLD_BUGS) + SEP + "</TD>");
196                 pw.println("          <TD CLASS=\"CategoryValue\">" + trans(CfbBundle.NUM_BUGS_OLD, m_delta.getNumCommon()) + "</TD>");
197                 pw.println("        </TR>");
198                 pw.println("      </TABLE>");
199                 pw.println("    </P>");
200         }
201         
202         void writeStyle(PrintWriter pw)
203         {
204                 pw.println("    <STYLE>");
205                 pw.println("                   body { background-color: #F0F0FF; }");
206             pw.println("    .CategoryName  { text-align: right; }");
207             pw.println("    .CategoryValue { text-align: left; }");
208                 pw.println("    .Loc           { font-family: monospace; }");
209             pw.println("    .SectionHead   td { background-color: #0000FF; color: #FFFFFF; font-size: 1.25em; font-weight: bold; }");
210                 pw.println("    .Var           { font-family: monospace; }");
211                 pw.println("    </STYLE>");
212         }
213 }