1 package net.sf.statcvs.pages;
2
3 import java.io.FileWriter;
4 import java.io.IOException;
5 import java.text.DecimalFormat;
6 import java.text.NumberFormat;
7 import java.util.ArrayList;
8 import java.util.Collections;
9 import java.util.Date;
10 import java.util.Iterator;
11 import java.util.List;
12 import java.util.logging.Logger;
13
14 import net.sf.statcvs.Messages;
15 import net.sf.statcvs.charts.ChartImage;
16 import net.sf.statcvs.model.Directory;
17 import net.sf.statcvs.output.ReportConfig;
18 import net.sf.statcvs.renderer.TableRenderer;
19 import net.sf.statcvs.reports.TableReport;
20
21 public class Page implements NavigationNode {
22 private final static NumberFormat[] DOUBLE_FORMATS = { new DecimalFormat("0"), new DecimalFormat("0.0"), new DecimalFormat("0.00"),
23 new DecimalFormat("0.000"), new DecimalFormat("0.0000") };
24 private final static Logger logger = Logger.getLogger("sf.net.statcvs");
25
26 private final ReportConfig config;
27 private final String fileName;
28 private final String shortTitle;
29 private final String fullTitle;
30 private final MarkupSyntax outputFormat;
31 private StringBuffer contents = new StringBuffer();
32 private NavigationNode parent = null;
33 private String siblingsTitle = null;
34 private List siblings = Collections.EMPTY_LIST;
35 private final List children = new ArrayList(0);
36 private final List attributeKeys = new ArrayList(0);
37 private final List attributeValues = new ArrayList(0);
38 private boolean showLinkToPreviousSibling = false;
39 private boolean inSection = false;
40 private boolean written = false;
41
42 /**
43 * Creates a new page.
44 * @param config The configuration to use
45 * @param fileName File name for the page, <em>without</em> file extension
46 * @param shortTitle A short navigation title
47 * @param fullTitle A full headline title
48 */
49 public Page(final ReportConfig config, final String fileName, final String shortTitle, final String fullTitle) {
50 this.config = config;
51 this.fileName = fileName;
52 this.shortTitle = shortTitle;
53 this.fullTitle = fullTitle;
54 this.outputFormat = config.getMarkup();
55 }
56
57
58
59
60 public void setParent(final NavigationNode parent) {
61 this.parent = parent;
62 }
63
64 /**
65 * Sets a list of {@link Page}s that are siblings of this page.
66 * The generated page will contain a navigation list that links
67 * to all siblings. The sibling list may contain the page
68 * itself.
69 * @param siblingsTitle Title for navigation list, e.g. "Monthly Reports"
70 * @param sibling A list of {@link Page}s
71 */
72 public void setSiblings(final String siblingsTitle, final List siblingPages) {
73 this.siblingsTitle = siblingsTitle;
74 this.siblings = siblingPages;
75 }
76
77 public void addChild(final NavigationNode child) {
78 this.children.add(child);
79 child.setParent(this);
80 }
81
82
83
84
85 public String getURL() {
86 return this.fileName + ".html";
87 }
88
89
90
91
92 public String getShortTitle() {
93 return this.shortTitle;
94 }
95
96
97
98
99 public String getFullTitle() {
100 return this.fullTitle;
101 }
102
103 public void setShowLinkToPreviousSibling(final boolean showLink) {
104 this.showLinkToPreviousSibling = showLink;
105 }
106
107 public void addAttribute(final String key, final int value) {
108 addAttribute(key, Integer.toString(value));
109 }
110
111 public void addAttribute(final String key, final int value, final String unit) {
112 addAttribute(key, Integer.toString(value) + " " + unit);
113 }
114
115 public void addAttribute(final String key, final Date value) {
116 addRawAttribute(key, HTML.getDateAndTime(value));
117 }
118
119 public void addAttribute(final String key, final String value) {
120 addRawAttribute(key, HTML.escape(value));
121 }
122
123 public void addAttribute(final String key, final double value, final int decimalPlaces) {
124 addAttribute(key, DOUBLE_FORMATS[decimalPlaces].format(value));
125 }
126
127 public void addAttribute(final String key, final double value, final int decimalPlaces, final String unit) {
128 addAttribute(key, DOUBLE_FORMATS[decimalPlaces].format(value) + " " + unit);
129 }
130
131 public void addRawAttribute(final String key, final String rawValue) {
132 this.attributeKeys.add(key);
133 this.attributeValues.add(rawValue);
134 }
135
136 public void addRawContent(final String s) {
137 this.contents.append(s);
138 }
139
140 public void addSection(final String title) {
141 if (this.inSection) {
142 this.contents.append(this.outputFormat.endSection2());
143 }
144 this.contents.append(this.outputFormat.startSection2(title));
145 this.inSection = true;
146 }
147
148 public void addLink(final String url, final String text) {
149 this.addRawContent("<p>" + HTML.getLink(url, text) + "</p>\n");
150 }
151
152 public void add(final ChartImage chart) {
153 if (chart == null) {
154 return;
155 }
156 addRawContent("<p class=\"chart\"><img src=\"" + HTML.escape(chart.getURL()) + "\" alt=\"" + HTML.escape(chart.getFullTitle()) + "\" width=\""
157 + chart.getWidth() + "\" height=\"" + chart.getHeight() + "\" /></p>");
158 chart.write();
159 }
160
161 public void add(final ChartImage chart, final String linkURL) {
162 if (chart == null) {
163 return;
164 }
165 addRawContent("<p class=\"chart\"><a href=\"" + HTML.escape(linkURL) + "\"><img src=\"" + HTML.escape(chart.getURL()) + "\" alt=\""
166 + HTML.escape(chart.getFullTitle()) + "\" width=\"" + chart.getWidth() + "\" height=\"" + chart.getHeight() + "\" /></a></p>");
167 chart.write();
168 }
169
170 public void add(final TableReport table) {
171 table.calculate();
172 addRawContent(new TableRenderer(table.getTable(), this.outputFormat).getRenderedTable());
173 }
174
175 public void add(final Directory directory, final boolean withRootLinks) {
176 addRawContent(new DirectoryTreeFormatter(directory, withRootLinks).getFormatted());
177 }
178
179 public void add(final PageGroup pages) {
180 addRawContent(pages.asLinkList());
181 addChild(pages);
182 }
183
184
185
186
187 public void write() {
188 if (this.written) {
189 return;
190 }
191 if (this.inSection) {
192 this.contents.append(this.outputFormat.endSection2());
193 }
194 final Iterator it = this.children.iterator();
195 while (it.hasNext()) {
196 final NavigationNode page = (NavigationNode) it.next();
197 page.setParent(this);
198 page.write();
199 }
200 final String fileWithExtension = this.fileName + "." + this.config.getMarkup().getExtension();
201 logger.info("writing page '" + this.fullTitle + "' to " + fileWithExtension);
202 FileWriter w = null;
203 try {
204 w = new FileWriter(this.config.getRootDirectory() + fileWithExtension);
205 w.write(this.outputFormat.getHeader(this.fullTitle, this.config.getCssHandler().getLink(), config.getCharSet()));
206 w.write(this.outputFormat.startSection1(this.fullTitle));
207 w.write(getLinkToParent());
208 w.write(getNavigationLinks());
209 w.write(getAttributes());
210 w.write(this.contents.toString());
211 w.write(getLinkToPreviousSibling());
212 w.write(this.outputFormat.endSection1());
213 w.write(getGeneratedByBlock());
214 w.write(this.outputFormat.getEndOfPage());
215 } catch (final IOException ex) {
216 logger.warning(ex.getMessage());
217 } finally {
218 if (w != null) {
219 try {
220 w.close();
221 } catch (final IOException e) {
222 logger.warning(e.getMessage());
223 }
224 }
225 }
226 this.written = true;
227
228
229 this.contents = null;
230 }
231
232 public String asParentLink() {
233 String result = "« " + HTML.getLink(getURL(), getShortTitle());
234 if (this.parent != null) {
235 result = this.parent.asParentLink() + " " + result;
236 }
237 return result;
238 }
239
240 private String getLinkToParent() {
241 if (this.parent == null) {
242 return "";
243 }
244 return "<div id=\"parentlink\">" + this.parent.asParentLink() + "</div>\n";
245 }
246
247 private String getNavigationLinks() {
248 if (this.siblingsTitle == null || this.siblings.isEmpty()) {
249 return "";
250 }
251 final StringBuffer s = new StringBuffer();
252 s.append(this.outputFormat.startSection2(this.siblingsTitle, "nav"));
253 s.append("<ul>\n");
254 final Iterator it = this.siblings.iterator();
255 while (it.hasNext()) {
256 final NavigationNode sibling = (NavigationNode) it.next();
257 s.append(" <li>");
258 if (sibling == this) {
259 s.append("<span class=\"here\">" + HTML.escape(sibling.getShortTitle()) + "</span>");
260 } else {
261 s.append(HTML.getLink(sibling.getURL(), sibling.getShortTitle()));
262 }
263 s.append("</li>\n");
264 }
265 s.append("</ul>\n");
266 s.append(this.outputFormat.endSection2());
267 return s.toString();
268 }
269
270 private String getAttributes() {
271 if (this.attributeKeys.isEmpty()) {
272 return "";
273 }
274 final StringBuffer s = new StringBuffer();
275 s.append("<dl class=\"attributes\">\n");
276 for (int i = 0; i < this.attributeKeys.size(); i++) {
277 final String key = (String) this.attributeKeys.get(i);
278 final String value = (String) this.attributeValues.get(i);
279 s.append(" <dt>" + HTML.escape(key) + ":</dt>\n");
280 s.append(" <dd>" + value + "</dd>\n");
281 }
282 s.append("</dl>\n");
283 return s.toString();
284 }
285
286 private String getGeneratedByBlock() {
287 final StringBuffer s = new StringBuffer();
288 s.append("<div id=\"generatedby\">");
289 s.append(Messages.getString("PAGE_GENERATED_BY"));
290 s.append(" ");
291 s.append(HTML.getLink(Messages.getString("PROJECT_URL"), Messages.getString("PROJECT_SHORTNAME")) + " " + Messages.getString("PROJECT_VERSION"));
292 s.append("</div>\n");
293 return s.toString();
294 }
295
296 private String getLinkToPreviousSibling() {
297 if (!this.showLinkToPreviousSibling) {
298 return "";
299 }
300 final NavigationNode sibling = findPreviousSibling();
301 if (sibling == null) {
302 return "";
303 }
304 return "<p class=\"previous\">" + HTML.getLink(sibling.getURL(), sibling.getShortTitle()) + " » </p>\n";
305 }
306
307 private NavigationNode findPreviousSibling() {
308 final Iterator it = this.siblings.iterator();
309 while (it.hasNext()) {
310 final NavigationNode sibling = (NavigationNode) it.next();
311 if (sibling != this) {
312 continue;
313 }
314 if (!it.hasNext()) {
315 return null;
316 }
317 return (NavigationNode) it.next();
318 }
319 return null;
320 }
321 }