Fri, 24 Apr 2020 03:58:51 +0100
Merge
1 /*
2 * Copyright (c) 1996,2010, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
26 import java.io.BufferedWriter;
27 import java.io.File;
28 import java.io.IOException;
29 import java.io.Writer;
30 import java.net.URL;
31 import java.text.MessageFormat;
32 import java.util.ResourceBundle;
34 /**
35 * A class to facilitate writing HTML via a stream.
36 */
37 public class HTMLWriter
38 {
39 /**
40 * Create an HTMLWriter object, using a default doctype for HTML 3.2.
41 * @param out a Writer to which to write the generated HTML
42 * @throws IOException if there is a problem writing to the underlying stream
43 */
44 public HTMLWriter(Writer out) throws IOException {
45 this(out, "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2//EN\">");
46 }
48 /**
49 * Create an HTMLWriter object, using a specifed doctype header.
50 * @param out a Writer to which to write the generated HTML
51 * @param docType a string containing a doctype header for the HTML to be generetaed
52 * @throws IOException if there is a problem writing to the underlying stream
53 */
54 public HTMLWriter(Writer out, String docType) throws IOException {
55 if (out instanceof BufferedWriter)
56 this.out = (BufferedWriter) out;
57 else
58 this.out = new BufferedWriter(out);
59 this.out.write(docType);
60 this.out.newLine();
61 }
63 /**
64 * Create an HTMLWriter object, using a specified bundle for localizing messages.
65 * @param out a Writer to which to write the generated HTML
66 * @param i18n a resource bundle to use to localize messages
67 * @throws IOException if there is a problem writing to the underlying stream
68 */
69 public HTMLWriter(Writer out, ResourceBundle i18n) throws IOException {
70 this(out);
71 this.i18n = i18n;
72 }
75 /**
76 * Create an HTMLWriter object, using a specifed doctype header and
77 * using a specified bundle for l0calizing messages.
78 * @param out a Writer to which to write the generated HTML
79 * @param docType a string containing a doctype header for the HTML to be generetaed
80 * @param i18n a resource bundle to use to localize messages
81 * @throws IOException if there is a problem writing to the underlying stream
82 */
83 public HTMLWriter(Writer out, String docType, ResourceBundle i18n) throws IOException {
84 this(out, docType);
85 this.i18n = i18n;
86 }
88 /**
89 * Set the reource bundle to be used for localizing messages.
90 * @param i18n the resource bundle to be used for localizing messages
91 */
92 public void setResourceBundle(ResourceBundle i18n) {
93 this.i18n = i18n;
94 }
96 /**
97 * Flush the stream, and the underlying output stream.
98 * @throws IOException if there is a problem writing to the underlying stream
99 */
100 public void flush() throws IOException {
101 out.flush();
102 }
104 /**
105 * Close the stream, and the underlying output stream.
106 * @throws IOException if there is a problem closing the underlying stream
107 */
108 public void close() throws IOException {
109 out.close();
110 }
112 /**
113 * Write a newline to the underlying output stream.
114 * @throws IOException if there is a problem writing to the underlying stream
115 */
116 public void newLine() throws IOException {
117 out.newLine();
118 }
120 /**
121 * Start an HTML tag. If a prior tag has been started, it will
122 * be closed first. Once a tag has been opened, attributes for the
123 * tag may be written out, followed by body content before finally
124 * ending the tag.
125 * @param tag the tag to be started
126 * @throws IOException if there is a problem writing to the underlying stream
127 * @see #writeAttr
128 * @see #write
129 * @see #endTag
130 */
131 public void startTag(String tag) throws IOException {
132 if (state == IN_TAG) {
133 out.write(">");
134 state = IN_BODY;
135 }
136 //newLine();
137 out.write("<");
138 out.write(tag);
139 state = IN_TAG;
140 }
142 /**
143 * Finish an HTML tag. It is expected that a call to endTag will match
144 * a corresponding earlier call to startTag, but there is no formal check
145 * for this.
146 * @param tag the tag to be closed.
147 * @throws IOException if there is a problem writing to the underlying stream
148 */
149 public void endTag(String tag) throws IOException {
150 if (state == IN_TAG) {
151 out.write(">");
152 state = IN_BODY;
153 out.newLine();
154 }
155 out.write("</");
156 out.write(tag);
157 out.write(">");
158 //out.newLine(); // PATCHED, jjg
159 state = IN_BODY;
160 }
162 /**
163 * Finish an empty element tag, such as a META, BASE or LINK tag.
164 * This is expected to correspond with a startTag.
165 * @param tag the tag which is being closed. this is only useful for
166 * validation, it is not written out
167 * @throws IllegalStateException if this call does not follow startTag
168 * (stream is not currently inside a tag)
169 * @throws IOException if there is a problem writing to the underlying stream
170 */
171 public void endEmptyTag(String tag) throws IOException {
172 if (state != IN_TAG)
173 throw new IllegalStateException();
175 out.write(">");
176 state = IN_BODY;
177 out.newLine();
178 }
180 /**
181 * Write an attribute for a tag. A tag must previously have been started.
182 * All tag attributes must be written before any body text is written.
183 * The value will be quoted if necessary when writing it to the underlying
184 * stream. No check is made that the attribute is valid for the current tag.
185 * @param name the name of the attribute to be written
186 * @param value the value of the attribute to be written
187 * @throws IllegalStateException if the stream is not in a state to
188 * write attributes -- e.g. if this call does not follow startTag or other
189 * calls of writteAttr
190 * @throws IOException if there is a problem writing to the underlying stream
191 */
192 public void writeAttr(String name, String value) throws IOException {
193 if (state != IN_TAG)
194 throw new IllegalStateException();
196 out.write(" ");
197 out.write(name);
198 out.write("=");
199 boolean alpha = true;
200 for (int i = 0; i < value.length() && alpha; i++)
201 alpha = Character.isLetter(value.charAt(i));
202 if (!alpha)
203 out.write("\"");
204 out.write(value);
205 if (!alpha)
206 out.write("\"");
207 }
209 /**
210 * Write an attribute for a tag. A tag must previously have been started.
211 * All tag attributes must be written before any body text is written.
212 * The value will be quoted if necessary when writing it to the underlying
213 * stream. No check is made that the attribute is valid for the current tag.
214 * @param name the name of the attribute to be written
215 * @param value the value of the attribute to be written
216 * @throws IllegalStateException if the stream is not in a state to
217 * write attributes -- e.g. if this call does not follow startTag or other
218 * calls of writteAttr
219 * @throws IOException if there is a problem writing to the underlying stream
220 */
221 public void writeAttr(String name, int value) throws IOException {
222 writeAttr(name, Integer.toString(value));
223 }
225 /**
226 * Write a line of text, followed by a newline.
227 * The text will be escaped as necessary.
228 * @param text the text to be written.
229 * @throws IOException if there is a problem closing the underlying stream
230 */
231 public void writeLine(String text) throws IOException {
232 write(text);
233 out.newLine();
234 }
236 /**
237 * Write body text, escaping it as necessary.
238 * If this call follows a call of startTag, the open tag will be
239 * closed -- meaning that no more attributes can be written until another
240 * tag is started. If the text value is null, the current tag will still
241 * be closed, but no other text will be written.
242 * @param text the text to be written, may be null or zero length.
243 * @throws IOException if there is a problem writing to the underlying stream
244 */
245 public void write(String text) throws IOException {
246 if (state == IN_TAG) {
247 out.write(">");
248 state = IN_BODY;
249 }
251 if (text == null)
252 return;
254 // check to see if there are any special characters
255 boolean specialChars = false;
256 for (int i = 0; i < text.length() && !specialChars; i++) {
257 switch (text.charAt(i)) {
258 case '<': case '>': case '&':
259 specialChars = true;
260 }
261 }
263 // if there are special characters write the string character at a time;
264 // otherwise, write it out as is
265 if (specialChars) {
266 for (int i = 0; i < text.length(); i++) {
267 char c = text.charAt(i);
268 switch (c) {
269 case '<': out.write("<"); break;
270 case '>': out.write(">"); break;
271 case '&': out.write("&"); break;
272 default: out.write(c);
273 }
274 }
275 }
276 else
277 out.write(text);
278 }
280 /**
281 * Write a basic HTML entity, such as or { .
282 * @param entity the entity to write
283 * @throws IOException if there is a problem writing to the underlying stream
284 */
285 public void writeEntity(String entity) throws IOException {
286 if (state == IN_TAG) {
287 out.write(">");
288 state = IN_BODY;
289 }
290 out.write(entity);
291 }
293 /**
294 * Write an image tag, using a specified path for the image source attribute.
295 * @param imagePath the path for the image source
296 * @throws IOException if there is a problem closing the underlying stream
297 */
298 public void writeImage(String imagePath) throws IOException {
299 startTag(IMAGE);
300 writeAttr(SRC, imagePath);
301 }
303 /**
304 * Write an image tag, using a specified path for the image source attribute.
305 * @param imageURL the url for the image source
306 * @throws IOException if there is a problem closing the underlying stream
307 */
308 public void writeImage(URL imageURL) throws IOException {
309 writeImage(imageURL.toString());
310 }
312 /**
313 * Write a hypertext link.
314 * @param anchor the target for the link
315 * @param body the body text for the link
316 * @throws IOException if there is a problem closing the underlying stream
317 */
318 public void writeLink(String anchor, String body) throws IOException {
319 startTag(A);
320 writeAttr(HREF, anchor);
321 write(body);
322 endTag(A);
323 }
325 /**
326 * Write a hypertext link.
327 * @param file the target for the link
328 * @param body the body text for the link
329 * @throws IOException if there is a problem closing the underlying stream
330 */
331 public void writeLink(File file, String body) throws IOException {
332 startTag(A);
333 StringBuffer sb = new StringBuffer();
334 String path = file.getPath().replace(File.separatorChar, '/');
335 if (file.isAbsolute() && !path.startsWith("/"))
336 sb.append('/');
337 sb.append(path);
338 writeAttr(HREF, sb.toString());
339 write(body);
340 endTag(A);
341 }
343 /**
344 * Write a hypertext link.
345 * @param file the target and body for the link
346 * @throws IOException if there is a problem closing the underlying stream
347 */
348 public void writeLink(File file) throws IOException {
349 writeLink(file, file.getPath());
350 }
352 /**
353 * Write a hypertext link.
354 * @param url the target for the link
355 * @param body the body text for the link
356 * @throws IOException if there is a problem closing the underlying stream
357 */
358 public void writeLink(URL url, String body) throws IOException {
359 startTag(A);
360 writeAttr(HREF, url.toString());
361 write(body);
362 endTag(A);
363 }
365 /**
366 * Write the destination marker for a hypertext link.
367 * @param anchor the destination marker for hypertext links
368 * @param body the body text for the marker
369 * @throws IOException if there is a problem closing the underlying stream
370 */
371 public void writeLinkDestination(String anchor, String body) throws IOException {
372 startTag(A);
373 writeAttr(NAME, anchor);
374 write(body);
375 endTag(A);
376 }
378 /**
379 * Write a parameter tag.
380 * @param name the name of the parameter
381 * @param value the value of the parameter
382 * @throws IOException if there is a problem closing the underlying stream
383 */
384 public void writeParam(String name, String value) throws IOException {
385 startTag(PARAM);
386 writeAttr(NAME, name);
387 writeAttr(VALUE, value);
388 }
390 /**
391 * Write a style attribute.
392 * @param value the value for the style atrtribute
393 * @throws IOException if there is a problem closing the underlying stream
394 */
395 public void writeStyleAttr(String value) throws IOException {
396 writeAttr(STYLE, value);
397 }
399 /**
400 * Write a localized message, using a specified resource bundle.
401 * @param i18n the resource bundle used to localize the message
402 * @param key the key for the message to be localized
403 * @throws IOException if there is a problem closing the underlying stream
404 */
405 public void write(ResourceBundle i18n, String key) throws IOException {
406 write(getString(i18n, key));
407 }
409 /**
410 * Write a localized message, using a specified resource bundle.
411 * @param i18n the resource bundle used to localize the message
412 * @param key the key for the message to be localized
413 * @param arg an argument to be formatted into the localized message
414 * @throws IOException if there is a problem closing the underlying stream
415 */
416 public void write(ResourceBundle i18n, String key, Object arg) throws IOException {
417 write(getString(i18n, key, arg));
418 }
420 /**
421 * Write a localized message, using a specified resource bundle.
422 * @param i18n the resource bundle used to localize the message
423 * @param key the key for the message to be localized
424 * @param args arguments to be formatted into the localized message
425 * @throws IOException if there is a problem closing the underlying stream
426 */
427 public void write(ResourceBundle i18n, String key, Object[] args) throws IOException {
428 write(getString(i18n, key, args));
429 }
431 /**
432 * Write a localized message, using the default resource bundle.
433 * @param key the key for the message to be localized
434 * @throws IOException if there is a problem closing the underlying stream
435 */
436 public void writeI18N(String key) throws IOException {
437 write(getString(i18n, key));
438 }
440 /**
441 * Write a localized message, using the default resource bundle.
442 * @param key the key for the message to be localized
443 * @param arg an argument to be formatted into the localized message
444 * @throws IOException if there is a problem closing the underlying stream
445 */
446 public void writeI18N(String key, Object arg) throws IOException {
447 write(getString(i18n, key, arg));
448 }
450 /**
451 * Write a localized message, using the default resource bundle.
452 * @param key the key for the message to be localized
453 * @param args arguments to be formatted into the localized message
454 * @throws IOException if there is a problem closing the underlying stream
455 */
456 public void writeI18N(String key, Object[] args) throws IOException {
457 write(getString(i18n, key, args));
458 }
460 private String getString(ResourceBundle rb, String key, Object... args) {
461 String s = rb.getString(key);
462 return MessageFormat.format(s, args);
463 }
465 /** The HTML "a" tag. */
466 public static final String A = "a";
467 /** The HTML "align" attribute. */
468 public static final String ALIGN = "align";
469 /** The HTML "b" tag. */
470 public static final String B = "b";
471 /** The HTML "body" tag. */
472 public static final String BODY = "body";
473 /** The HTML "border" attribute. */
474 public static final String BORDER = "border";
475 /** The HTML "br" tag. */
476 public static final String BR = "br";
477 /** The HTML "class" attribute. */
478 public static final String CLASS = "class";
479 /** The HTML "classid" attribute. */
480 public static final String CLASSID = "classid";
481 /** The HTML "code" tag. */
482 public static final String CODE = "code";
483 /** The HTML "color" attribte. */
484 public static final String COLOR = "color";
485 /** The HTML "col" attribute value. */
486 public static final String COL = "col";
487 /** The HTML "dd" tag. */
488 public static final String DD = "dd";
489 /** The HTML "div" tag. */
490 public static final String DIV = "div";
491 /** The HTML "dl" tag. */
492 public static final String DL = "dl";
493 /** The HTML "dt" tag. */
494 public static final String DT = "dt";
495 /** The HTML "font" tag. */
496 public static final String FONT = "font";
497 /** The HTML "h1" tag. */
498 public static final String H1 = "h1";
499 /** The HTML "h2" tag. */
500 public static final String H2 = "h2";
501 /** The HTML "h3" tag. */
502 public static final String H3 = "h3";
503 /** The HTML "h4" tag. */
504 public static final String H4 = "h4";
505 /** The HTML "h5" tag. */
506 public static final String H5 = "h5";
507 /** The HTML "head" tag. */
508 public static final String HEAD = "head";
509 /** The HTML "href" attribute. */
510 public static final String HREF = "href";
511 /** The HTML "html" tag. */
512 public static final String HTML = "html";
513 /** The HTML "hr" tag. */
514 public static final String HR = "hr";
515 /** The HTML "i" tag. */
516 public static final String I = "i";
517 /** The HTML "id" tag. */
518 public static final String ID = "id";
519 /** The HTML "image" tag. */
520 public static final String IMAGE = "image";
521 /** The HTML "left" attribute value. */
522 public static final String LEFT = "left";
523 /** The HTML "li" tag. */
524 public static final String LI = "li";
525 /** The HTML "link" tag. */
526 public static final String LINK = "link";
527 /** The HTML "name" attribute. */
528 public static final String NAME = "name";
529 /** The HTML "object" tag. */
530 public static final String OBJECT = "object";
531 /** The HTML "p" tag. */
532 public static final String PARAM = "param";
533 /** The HTML "param" tag. */
534 public static final String P = "p";
535 /** The HTML "rel" attribute value. */
536 public static final String REL = "rel";
537 /** The HTML "right" attribute value. */
538 public static final String RIGHT = "right";
539 /** The HTML "row" attribute value. */
540 public static final String ROW = "row";
541 /** The HTML "script" tag. */
542 public static final String SCRIPT = "script";
543 /** The HTML "small" tag. */
544 public static final String SMALL = "small";
545 /** The HTML "span" tag. */
546 public static final String SPAN = "span";
547 /** The HTML "src" attribute. */
548 public static final String SRC = "src";
549 /** The HTML "scope" attribute. */
550 public static final String SCOPE = "scope";
551 /** The HTML "style" attribute. */
552 public static final String STYLE = "style";
553 /** The HTML "table" tag. */
554 public static final String TABLE = "table";
555 /** The HTML "td" tag. */
556 public static final String TD = "td";
557 /** The HTML type for JavaScript. */
558 public static final String TEXT_JAVASCRIPT = "text/javascript";
559 /** The HTML "title"attribute. */
560 public static final String TITLE = "title";
561 /** The HTML "th" tag. */
562 public static final String TH = "th";
563 /** The HTML "top" attribute value. */
564 public static final String TOP = "top";
565 /** The HTML "tr" tag. */
566 public static final String TR = "tr";
567 /** The HTML "type" attribute. */
568 public static final String TYPE = "type";
569 /** The HTML "ul" tag. */
570 public static final String UL = "ul";
571 /** The HTML "valign" attribute. */
572 public static final String VALIGN = "valign";
573 /** The HTML "value" attribute. */
574 public static final String VALUE = "value";
577 private BufferedWriter out;
578 private int state;
579 private ResourceBundle i18n;
580 private static final int IN_TAG = 1;
581 private static final int IN_BODY = 2;
582 }