001package arcommon.net; 002 003import java.awt.Color; 004import java.io.BufferedWriter; 005import java.io.File; 006import java.io.IOException; 007import java.io.OutputStream; 008import java.io.OutputStreamWriter; 009import java.io.UnsupportedEncodingException; 010import java.util.ArrayList; 011import java.util.Arrays; 012import java.util.HashSet; 013import java.util.List; 014 015import arcommon.FileUtil; 016import arcommon.StringUtil; 017import arcommon.xml.XmlUtil; 018 019public class HtmlWriter { 020 021 public static final String DEFAULT_ENCODING = "UTF-8"; 022 public static final String DEFAULT_LANGUAGE = "en"; 023 024 public static final String HTTP_METHOD_GET = "GET"; 025 public static final String HTTP_METHOD_PUT = "PUT"; 026 public static final String HTTP_METHOD_POST = "POST"; 027 028 public static final String INPUT_TYPE_CHECKBOX = "checkbox"; 029 public static final String INPUT_TYPE_DATE = "date"; 030 public static final String INPUT_TYPE_DATETIME_LOCAL = "datetime-local"; 031 public static final String INPUT_TYPE_FILE = "file"; 032 public static final String INPUT_TYPE_HIDDEN = "hidden"; 033 public static final String INPUT_TYPE_PASSWORD = "password"; 034 public static final String INPUT_TYPE_NUMBER = "number"; 035 public static final String INPUT_TYPE_RADIO = "radio"; 036 public static final String INPUT_TYPE_SUBMIT = "submit"; 037 public static final String INPUT_TYPE_TEXT = "text"; 038 public static final String INPUT_TYPE_TIME = "time"; 039 040 public static final String FORM_ENCTYPE_MULTIPART = "multipart/form-data"; 041 042 protected enum HtmlMode {BEFORE_START, 043 BODY, APPLET, APPLET_AFTER_ALT, IMAGE, SOURCE, 044 TABLE, ROW, CELL, 045 CENTER, BOLD, HEADER, FONT_SIZE, STRONG, TEXT_COLOR, 046 SELECT, FORM, FIELDSET, 047 CLOSED 048 }; 049 050 private BufferedWriter outWriter; 051 052 private String encoding; 053 private String language; 054 055 private int indent = 0; 056 private boolean startOfLine = true; 057 private HtmlMode mode; 058 private ArrayList<HtmlMode> nestedModes; 059 private int headerActiveLevel; 060 private HashSet<String> usedId; 061 062 public HtmlWriter(String fileName) { 063 this(fileName, null); 064 } 065 066 public HtmlWriter(String fileName, String pencoding) { 067 this(fileName, pencoding, null); 068 } 069 070 public HtmlWriter(String fileName, String pencoding, String planguage) { 071 encoding = pencoding; 072 if (encoding == null) { 073 encoding = DEFAULT_ENCODING; 074 } 075 language = planguage; 076 if (language == null) { 077 language = DEFAULT_LANGUAGE; 078 } 079 080 File file = new File(fileName).getParentFile(); 081 if (file != null) { 082 FileUtil.mkdirs(file); 083 } 084 085 outWriter = FileUtil.createBufferedWriter(fileName, false, encoding); 086 mode = HtmlMode.BEFORE_START; 087 088 usedId = new HashSet<String>(); 089 } 090 091 public HtmlWriter(OutputStream poutStream) { 092 language = DEFAULT_LANGUAGE; 093 mode = HtmlMode.BEFORE_START; 094 usedId = new HashSet<String>(); 095 setEncoding(poutStream, DEFAULT_ENCODING); 096 } 097 098 public void writeTitle(String title) { 099 writeTitleAndIcon(title, null); 100 } 101 102 public void writeTitleAndIcon(String title, String icon) { 103 validateBeforeStart(); 104 writeHtmlLine("<!DOCTYPE html>"); 105 if (language == null) { 106 writeHtmlLine("<html>"); 107 } else { 108 writeHtmlLine("<html lang=\"" + language + "\">"); 109 } 110 writeHtmlLine("<head>"); 111 if (icon != null) { 112 writeHtmlLine("<link rel=\"icon\" href=\"" + NetUtil.encodeUrlMyVariant(icon) + "\">"); 113 } 114 writeHtmlLine("<title>" + title + "</title>"); 115 if (encoding != null) { 116 writeHtmlLine("<meta charset=\"" + encoding + "\">"); 117 } 118 writeHtmlLine("<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">"); 119 writeHtmlLine("</head>"); 120 writeHtmlLine("<body>"); 121 mode = HtmlMode.BODY; 122 } 123 124 public void writeIFrame(String source) { 125 writeIFrame(source, null, null, null); 126 } 127 128 public void writeIFrame(String source, int width, int height) { 129 writeIFrame(source, width, height, null); 130 } 131 132 public void writeIFrame(String source, int width, int height, String style) { 133 writeIFrame(source, "" + width, "" + height, style); 134 } 135 136 public void writeIFrame(String source, String width, String height, String style) { 137 // "<iframe src=\"" + link + "\" width=\"400\" height=\"500\" style=\"border:none;\"></iframe>"); 138 validateNormalMode(); 139 if (source == null) { 140 throw new RuntimeException("source == null"); 141 } 142 143 writeHtml("<iframe src=\"" + source + "\""); 144 if (width != null) { 145 writeHtml(" width=\"" + width + "\""); 146 } 147 if (height != null) { 148 writeHtml(" height=\"" + height + "\""); 149 } 150 if (style != null) { 151 writeHtml(" style=\"" + style + "\""); 152 } 153 154 writeHtml("></iframe>"); 155 writeNewlineInternal(); 156 } 157 158 public void writeHeader(String header, int headerLevel) { 159 writeHeader(header, headerLevel, true); 160 } 161 162 public void writeHeader(String header, int headerLevel, String id) { 163 writeHeader(header, headerLevel, id, true); 164 } 165 166 public void writeHeader(String header, int headerLevel, boolean center) { 167 writeHeader(header, headerLevel, null, center); 168 } 169 170 public void writeHeader(String header, int headerLevel, String id, boolean center) { 171 startHeader(headerLevel, id, center); 172 writeText(header); 173 endHeader(); 174 } 175 176 public void writeHorizontalHeader() { 177 validateNormalMode(); 178 writeHtmlLine("<hr>"); 179 } 180 181 public static class HtmlImageMap { 182 private String id; 183 private String name; 184 private List<HtmlImageArea> areas; 185 186 public HtmlImageMap(String pname) { 187 name = pname; 188 } 189 190 public HtmlImageMap(String pid, String pname) { 191 id = pid; 192 name = pname; 193 validateId(id); 194 } 195 196 public void add(HtmlImageArea htmlImageArea) { 197 if (areas == null) { 198 areas = new ArrayList<HtmlImageArea>(); 199 } 200 areas.add(htmlImageArea); 201 } 202 } 203 204 public static class HtmlImageArea { 205 private String shape; 206 private String coords; 207 private String href; 208 private String alt; 209 private String title; 210 private boolean openInNewWindow; 211 212 public HtmlImageArea(String pshape, String pcoords, String phref, String palt, String ptitle, boolean popenInNewWindow) { 213 shape = pshape; 214 coords = pcoords; 215 href = phref; 216 alt = palt; 217 title = ptitle; 218 openInNewWindow = popenInNewWindow; 219 } 220 } 221 222 public void writeImageCenter(String fileName) { 223 writeImageCenter(fileName, null); 224 } 225 226 public void writeImageCenter(String fileName, int width, int height) { 227 writeImage(fileName, new IdAndStyle(null, null, "display", "block", "margin", "auto"), width, height, null, null); 228 } 229 230 public void writeImageCenter(String fileName, HtmlImageMap htmlImageMap) { 231 writeImage(fileName, new IdAndStyle(null, null, "display", "block", "margin", "auto"), null, null, null, htmlImageMap); 232 } 233 234 public void writeImageCenter(String fileName, int width, int height, HtmlImageMap htmlImageMap) { 235 writeImage(fileName, new IdAndStyle(null, null, "display", "block", "margin", "auto"), width, height, null, htmlImageMap); 236 } 237 238 public void writeImage(String fileName) { 239 writeImage(fileName, null, null); 240 } 241 242 public void writeImage(String fileName, String alt, HtmlImageMap htmlImageMap) { 243 writeImage(fileName, null, null, null, alt, htmlImageMap); 244 } 245 246 public void writeImage(String fileName, int width, int height, String alt, HtmlImageMap htmlImageMap) { 247 writeImage(fileName, null, width, height, alt, htmlImageMap); 248 } 249 250 private String adjustLink(String url) { 251 if (url.startsWith("data:")) { 252 return url; 253 } 254 255 url = url.replace('\\', '/'); 256 url = NetUtil.encodeUrlMyVariant(url); 257 return url; 258 } 259 260 protected void writeImage(String fileName, IdAndStyle idAndStyle, Integer width, Integer height, String alt, HtmlImageMap htmlImageMap) { 261 String attributesString = ""; 262 if (idAndStyle != null) { 263 attributesString += idAndStyle.toString(); 264 } 265 if (width != null) { 266 if (width.intValue() <= 0) { 267 throw new RuntimeException("width.intValue() <= 0 " + width.intValue()); 268 } 269 attributesString += " width=\"" + width + "\""; 270 } 271 if (height != null) { 272 if (height.intValue() <= 0) { 273 throw new RuntimeException("height.intValue() <= 0 " + height.intValue()); 274 } 275 attributesString += " height=\"" + height + "\""; 276 } 277 if (alt != null && alt.length() > 0) { 278 attributesString += " alt=\"" + alt + "\""; 279 } 280 if (htmlImageMap != null) { 281 attributesString += " usemap=\"#" + htmlImageMap.name + "\""; 282 } 283 fileName = adjustLink(fileName); 284 writeHtml("<img" + attributesString + " src=\"" + fileName + "\" />"); 285 writeNewlineInternal(); 286 287 if (htmlImageMap != null) { 288 writeHtml("<map" + idToStringWithValidation(htmlImageMap.id) + " name=\"" + htmlImageMap.name + "\">"); 289 writeNewlineInternal(); 290 for (HtmlImageArea htmlImageArea : htmlImageMap.areas) { 291 String href = htmlImageArea.href; 292 if (href != null) { 293 href = adjustLink(href); 294 } 295 writeHtml("<area shape=\"" + htmlImageArea.shape + "\"" 296 + " coords=\"" + htmlImageArea.coords + "\"" 297 + (htmlImageArea.openInNewWindow ? " target=\"_blank\"" : "") 298 + (href != null ? " href=\"" + href + "\"" : "") 299 + " alt=\"" + htmlImageArea.alt + "\"" 300 + " title=\"" + htmlImageArea.title + "\" />"); 301 writeNewlineInternal(); 302 } 303 writeHtml("</map>"); 304 writeNewlineInternal(); 305 } 306 } 307 308 public static class HtmlMediaSource { 309 private String src; 310 private String type; 311 312 public HtmlMediaSource(String psrc, String ptype) { 313 src = psrc; 314 type = ptype; 315 if (src == null || src.length() == 0) { 316 throw new RuntimeException("src == null || src.length() == 0"); 317 } 318 if (type == null || type.length() == 0) { 319 throw new RuntimeException("type == null || type.length() == 0"); 320 } 321 } 322 323 @Override 324 public String toString() { 325 return "<source src=\"" + src + "\" type=\"" + type + "\" />"; 326 } 327 } 328 329 public void writeVideo(int width, int height, boolean autoplay, boolean controls, boolean muted, boolean loop, String preload, HtmlMediaSource... htmlMediaSources) { 330 if (htmlMediaSources.length == 0) { 331 throw new RuntimeException("HtmlMediaSource(s) must be specified (htmlMediaSources.length == 0)"); 332 } 333 writeVideo(width, height, autoplay, controls, muted, loop, null, null, preload, htmlMediaSources); 334 } 335 336 public void writeVideo(int width, int height, boolean autoplay, boolean controls, boolean muted, boolean loop, String src, String alt, String preload, HtmlMediaSource... htmlMediaSources) { 337 writeMedia("video", null, width, height, autoplay, controls, muted, loop, src, alt, preload, htmlMediaSources); 338 } 339 340 public void writeVideo(IdAndStyle idAndStyle, int width, int height, boolean autoplay, boolean controls, boolean muted, boolean loop, String preload, HtmlMediaSource... htmlMediaSources) { 341 if (htmlMediaSources.length == 0) { 342 throw new RuntimeException("HtmlMediaSource(s) must be specified (htmlMediaSources.length == 0)"); 343 } 344 writeVideo(idAndStyle, width, height, autoplay, controls, muted, loop, null, null, preload, htmlMediaSources); 345 } 346 347 public void writeVideo(IdAndStyle idAndStyle, int width, int height, boolean autoplay, boolean controls, boolean muted, boolean loop, String src, String alt, String preload, HtmlMediaSource... htmlMediaSources) { 348 writeMedia("video", idAndStyle, width, height, autoplay, controls, muted, loop, src, alt, preload, htmlMediaSources); 349 } 350 351 public void writeAudio(boolean autoplay, boolean controls, boolean muted, boolean loop, String preload, HtmlMediaSource... htmlMediaSources) { 352 if (htmlMediaSources.length == 0) { 353 throw new RuntimeException("HtmlMediaSource(s) must be specified (htmlMediaSources.length == 0)"); 354 } 355 writeAudio(autoplay, controls, muted, loop, null, null, preload, htmlMediaSources); 356 } 357 358 public void writeAudio(boolean autoplay, boolean controls, boolean muted, boolean loop, String src, String alt, String preload, HtmlMediaSource... htmlMediaSources) { 359 writeMedia("audio", null, -1, -1, autoplay, controls, muted, loop, src, alt, preload, htmlMediaSources); 360 } 361 362 public void writeAudio(IdAndStyle idAndStyle, boolean autoplay, boolean controls, boolean muted, boolean loop, String preload, HtmlMediaSource... htmlMediaSources) { 363 if (htmlMediaSources.length == 0) { 364 throw new RuntimeException("HtmlMediaSource(s) must be specified (htmlMediaSources.length == 0)"); 365 } 366 writeAudio(idAndStyle, autoplay, controls, muted, loop, null, null, preload, htmlMediaSources); 367 } 368 369 public void writeAudio(IdAndStyle idAndStyle, boolean autoplay, boolean controls, boolean muted, boolean loop, String src, String alt, String preload, HtmlMediaSource... htmlMediaSources) { 370 writeMedia("audio", idAndStyle, -1, -1, autoplay, controls, muted, loop, src, alt, preload, htmlMediaSources); 371 } 372 373 private void writeMedia(String tagName, IdAndStyle idAndStyle, int width, int height, boolean autoplay, boolean controls, boolean muted, boolean loop, String src, String alt, String preload, HtmlMediaSource... htmlMediaSources) { 374 if (htmlMediaSources.length == 0 && src == null) { 375 throw new RuntimeException("htmlMediaSources.length == 0 && src == null"); 376 } 377 writeHtml("<" + tagName); 378 writeIdAndStyle(idAndStyle); 379 if (width > 0) { 380 writeHtml(" width=\"" + width + "\""); 381 } 382 if (height > 0) { 383 writeHtml(" height=\"" + height + "\""); 384 } 385 if (autoplay) { 386 writeHtml(" autoplay"); 387 } 388 if (controls) { 389 writeHtml(" controls"); 390 } 391 if (muted) { 392 writeHtml(" muted"); 393 } 394 if (loop) { 395 writeHtml(" loop"); 396 } 397 if (preload != null) { 398 if (!preload.equals("auto") && !preload.equals("metadata") && !preload.equals("none")) { 399 throw new RuntimeException("Invalid preload: \"" + preload + "\". Must be one of \"auto\", \"metadata\" or \"none\"."); 400 } 401 writeHtml(" preload=\"" + preload + "\""); 402 } 403 if (src != null) { 404 writeHtml(" src=\"" + src + "\""); 405 } 406 407/* Breaks stuff, odd 408 if (htmlMediaSources.length == 0 && alt == null) { 409 writeHtml(" />"); 410 writeNewlineInternal(); 411 return ; 412 }*/ 413 414 writeHtml(">"); 415 writeNewlineInternal(); 416 417 levelUp(HtmlMode.SOURCE); 418 for (HtmlMediaSource htmlMediaSource : htmlMediaSources) { 419 if (htmlMediaSource == null) { 420 throw new RuntimeException("htmlMediaSource == null " + Arrays.toString(htmlMediaSources)); 421 } 422 writeHtml("<source src=\"" + htmlMediaSource.src + "\" type=\"" + htmlMediaSource.type + "\" />"); 423 writeNewlineInternal(); 424 } 425 levelDown(HtmlMode.SOURCE); 426 427 if (alt != null) { 428 writeText(alt); 429 writeNewlineInternal(); 430 } 431 432 writeHtml("</" + tagName + ">"); 433 writeNewlineInternal(); 434 } 435 436 public void startTable() { 437 startTable(false, false); 438 } 439 440 public void startTable(boolean border, boolean width100) { 441 startTable(border, width100, null); 442 } 443 444 public void startTable(boolean border, boolean width100, String backGroundColor) { 445 validateNormalMode(); 446 validateModeNotInHierarki(HtmlMode.HEADER); 447 String backGroundColorString = ""; 448 if (backGroundColor != null) { 449 backGroundColorString = " bgcolor=\"" + backGroundColor + "\""; 450 } 451 if (border) { 452 if (width100) { 453 writeHtmlLine("<table border=\"1\" cellpadding=\"3\" cellspacing=\"0\" width=\"100%\"" + backGroundColorString + ">"); 454 } else { 455 writeHtmlLine("<table border=\"1\" cellpadding=\"3\" cellspacing=\"0\"" + backGroundColorString + ">"); 456 } 457 } else { 458 if (width100) { 459 writeHtmlLine("<table width=\"100%\"" + backGroundColorString + ">"); 460 } else { 461 writeHtmlLine("<table" + backGroundColorString + ">"); 462 } 463 } 464 levelUp(HtmlMode.TABLE, false); 465 } 466 467 public void endTable() { 468 if (mode == HtmlMode.CELL) { 469 endCell(); 470 } 471 if (mode == HtmlMode.ROW) { 472 endRow(); 473 } 474 levelDown(HtmlMode.TABLE); 475 writeHtmlLine("</table>"); 476 } 477 478 public void startRow() { 479 if (mode != HtmlMode.TABLE) { 480 throw new RuntimeException("mode != HtmlMode.TABLE " + mode); 481 } 482 writeHtmlLine("<tr>"); 483 levelUp(HtmlMode.ROW, false); 484 } 485 486 public void endRow() { 487 levelDown(HtmlMode.ROW); 488 writeHtmlLine("</tr>"); 489 } 490 491 public void nextRow() { 492 nextRow(null, 0, false); 493 } 494 495 public void nextRow(String width, int colspan, boolean noWrap) { 496 nextRow(width, colspan, noWrap, null); 497 } 498 499 public void nextRow(String width, int colspan, boolean noWrap, String align) { 500 if (mode == HtmlMode.CELL) { 501 endCell(); 502 } 503 if (mode == HtmlMode.ROW) { 504 endRow(); 505 } 506 startRow(); 507 startCell(width, colspan, noWrap, align); 508 } 509 510 public void writeCell(String content) { 511 writeCell(content, null, 0, false); 512 } 513 514 public void writeCell(String content, String width, int colspan, boolean noWrap) { 515 writeCell(content, width, colspan, noWrap, null); 516 } 517 518 public void writeCell(String content, String width, int colspan, boolean noWrap, String align) { 519 writeCell(content, null, width, colspan, noWrap, align); 520 } 521 522 public void writeCell(String content, IdAndStyle idAndStyle, String width, int colspan, boolean noWrap, String align) { 523 if (content == null || content == "") { 524 writeEmptyCell(width, colspan, noWrap, align); 525 return ; 526 } 527 if (mode != HtmlMode.CELL) { 528 startCell(idAndStyle, width, colspan, noWrap, align); 529 } else { 530 if (width != null || (colspan != 1 && colspan != 0) || align != null) { 531 throw new RuntimeException("mode == HtmlMode.CELL and width/colspan/align set (width != null || (colspan != 1 && colspan != 0) || align != null)" + width + " " + colspan + " " + align); 532 } 533 } 534 writeTextWithNewlines(content); 535 endCell(); 536 } 537 538 public void writeEmptyCell() { 539 writeEmptyCell(null, 0, false); 540 } 541 542 public void writeEmptyCell(String width, int colspan, boolean noWrap) { 543 writeEmptyCell(width, colspan, noWrap, null); 544 } 545 546 public void writeEmptyCell(String width, int colspan, boolean noWrap, String align) { 547 if (mode != HtmlMode.CELL) { 548 startCell(width, colspan, noWrap, align); 549 } else { 550 if (width != null || (colspan != 1 && colspan != 0) || align != null) { 551 throw new RuntimeException("mode == HtmlMode.CELL and width/colspan/align set (width != null || (colspan != 1 && colspan != 0) || align != null)" + width + " " + colspan + " " + align); 552 } 553 } 554 writeHtmlLine(" "); 555 endCell(); 556 } 557 558 public void startCell() { 559 startCell(null, 0, false); 560 } 561 562 public void startCell(String width, int colspan, boolean noWrap) { 563 startCell(width, colspan, noWrap, null); 564 } 565 566 public void startCell(String width, int colspan, boolean noWrap, String align) { 567 startCell(null, width, colspan, noWrap, null); 568 } 569 570 public void startCell(IdAndStyle idAndStyle, String width, int colspan, boolean noWrap, String align) { 571 if (mode == HtmlMode.TABLE) { 572 startRow(); 573 } 574 if (mode != HtmlMode.ROW) { 575 throw new RuntimeException("mode != HtmlMode.ROW " + mode); 576 } 577 writeEnsureNewline(); 578 579 String text = "<td"; 580 581 if (width != null) { 582 String widthNumberPart = width; 583 if (width.endsWith("%")) { 584 widthNumberPart = width.substring(0, width.length() - 1); 585 } 586 if (width.endsWith("px")) { 587 widthNumberPart = width.substring(0, width.length() - 2); 588 } 589 if (!StringUtil.isStringAnInt(widthNumberPart)) { 590 throw new RuntimeException("Width is not a number \"" + width + "\" (" + widthNumberPart + ")"); 591 } 592 int widthNumber = StringUtil.stringToInt(widthNumberPart); 593 if (width.endsWith("%")) { 594 if (widthNumber <= 0 || widthNumber > 100) { 595 throw new RuntimeException("widthNumber <= 0 || widthNumber > 100 \"" + width + "\""); 596 } 597 } else { 598 if (widthNumber <= 0) { 599 throw new RuntimeException("widthNumber <= 0 \"" + width + "\""); 600 } 601 } 602 text += " width=\"" + width + "\""; 603 } 604 605 if (align != null) { 606 if (!align.equals("right") && !align.equals("center") && !align.equals("left")) { 607 throw new RuntimeException("Invalid align \"" + align + "\""); 608 } 609 text += " align=\"" + align + "\""; 610 } 611 612 if (colspan > 1) { 613 text += " colspan=" + colspan; 614 } 615 616 if (idAndStyle != null) { 617 text += idAndStyle.toString(); 618 } 619 620 if (noWrap) { 621 text += " nowrap"; 622 } 623 text = text + ">"; 624 writeHtmlLine(text); 625 levelUp(HtmlMode.CELL, false); 626 } 627 628 public void endCell() { 629 levelDown(HtmlMode.CELL); 630 writeEnsureNewline(); 631 writeHtmlLine("</td>"); 632 } 633 634 public void nextCell() { 635 nextCell(null, 0, false); 636 } 637 638 public void nextCell(String width, int colspan, boolean noWrap) { 639 nextCell(width, colspan, noWrap, null); 640 } 641 642 public void nextCell(String width, int colspan, boolean noWrap, String align) { 643 if (mode == HtmlMode.CELL) { 644 endCell(); 645 } 646 startCell(width, colspan, noWrap, align); 647 } 648 649 public void startCenter() { 650 validateNormalMode(); 651 writeHtml("<center>"); 652 levelUp(HtmlMode.CENTER, false); 653 } 654 655 public void endCenter() { 656 levelDown(HtmlMode.CENTER); 657 writeHtml("</center>"); 658 } 659 660 public void startBold() { 661 validateNormalMode(); 662 writeHtml("<b>"); 663 levelUp(HtmlMode.BOLD); 664 } 665 666 public void endBold() { 667 levelDown(HtmlMode.BOLD); 668 writeHtml("</b>"); 669 } 670 671 public void startHeader(int headerLevel, String id, boolean center) { 672 validateNormalMode(); 673 validateModeNotInHierarki(HtmlMode.CENTER); 674 if (headerLevel <= 0) { 675 throw new RuntimeException("header <= 0"); 676 } 677 if (headerActiveLevel > 0) { 678 throw new RuntimeException("headerActiveLevel > 0"); 679 } 680 writeHtml("<h" + headerLevel + idToStringWithValidation(id) + (center ? " align=\"center\"" : "") + ">"); 681 headerActiveLevel = headerLevel; 682 levelUp(HtmlMode.HEADER); 683 } 684 685 public void endHeader() { 686 if (headerActiveLevel == 0) { 687 throw new RuntimeException("headerActiveLevel == 0"); 688 } 689 levelDown(HtmlMode.HEADER); 690 writeHtml("</h" + headerActiveLevel + ">"); 691 writeNewlineInternal(); 692 headerActiveLevel = 0; 693 } 694 695 public void startFontSize(int size) { 696 validateNormalMode(); 697 if (size <= 0) { 698 throw new RuntimeException("size <= 0 " + size); 699 } 700 writeHtml("<span style=\"font-size: " + size + "px\">"); 701 levelUp(HtmlMode.FONT_SIZE); 702 } 703 704 public void endFontSize() { 705 levelDown(HtmlMode.FONT_SIZE); 706 writeHtml("</span>"); 707 } 708 709 public void startTextColor(Color color) { 710 validateNormalMode(); 711 if (color == null) { 712 throw new RuntimeException("color == null"); 713 } 714 String colorString = "#"; 715 colorString += StringUtil.toHex(color.getRed(), 2); 716 colorString += StringUtil.toHex(color.getGreen(), 2); 717 colorString += StringUtil.toHex(color.getBlue(), 2); 718 writeHtml("<span style=\"color: " + colorString + "\">"); 719 levelUp(HtmlMode.TEXT_COLOR); 720 } 721 722 public void endTextColor() { 723 levelDown(HtmlMode.TEXT_COLOR); 724 writeHtml("</span>"); 725 } 726 727 public void startStrong() { 728 validateNormalMode(); 729 writeHtml("<strong>"); 730 levelUp(HtmlMode.STRONG); 731 } 732 733 public void endStrong() { 734 levelDown(HtmlMode.STRONG); 735 writeHtml("</strong>"); 736 } 737 738 public void startFieldset(String legend) { 739 validateNormalMode(); 740 validateModeNotInHierarki(HtmlMode.HEADER); 741 writeHtml("<fieldset>"); 742 writeNewlineInternal(); 743 levelUp(HtmlMode.FIELDSET); 744 if (legend != null) { 745 writeHtmlLine("<legend>" + legend + "</legend>"); 746 } 747 } 748 749 public void endFieldset() { 750 levelDown(HtmlMode.FIELDSET); 751 writeHtml("</fieldset>"); 752 } 753 754 protected void startForm(String action, String method) { 755 startForm(action, method, null); 756 } 757 758 protected void startForm(String id, String action, String method) { 759 startForm(null, action, method, null); 760 } 761 762 protected void startForm(String id, String action, String method, String enctype) { 763 validateNormalMode(); 764 validateModeNotInHierarki(HtmlMode.HEADER); 765 writeHtml("<form" + idToStringWithValidation(id) + " action=\"" + action + "\" method=\"" + method + "\"" + (enctype != null && enctype.length() > 0 ? " enctype=\"" + enctype + "\"" : "") + ">"); 766 writeNewlineInternal(); 767 levelUp(HtmlMode.FORM); 768 } 769 770 protected void endForm() { 771 levelDown(HtmlMode.FORM); 772 writeHtml("</form>"); 773 writeNewlineInternal(); 774 } 775 776 public void writeLink(String link, String text) { 777 writeLink(link, text, null, null); 778 } 779 780 public void writeLinkNewWindow(String link, String text, String title) { 781 writeLink(link, text, "_blank", title); 782 } 783 784 public void writeLink(String link, String text, String target, String title) { 785 validateNormalMode(); 786 String targetString = ""; 787 if (target != null && !target.equals("")) { 788 targetString = " target=\"" + target + "\""; 789 } 790 String titleString = ""; 791 if (title != null && !title.equals("")) { 792 titleString = " title=\"" + XmlUtil.encodeXmlString(title) + "\""; 793 } 794 link = adjustLink(link); 795 writeHtml("<a href=\"" + link + "\"" + targetString + titleString + ">"); 796 if (text == null) { 797 text = link; 798 } 799 String[] split = text.split(" "); 800 for (int i = 0; i < split.length; i++) { 801 if (i > 0) { 802 writeNbsp(); 803 } 804 writeText(split[i]); 805 } 806 writeHtml("</a>"); 807 } 808 809 public void writeImageLink(String link, String imageSrc) { 810 writeImageLink(link, imageSrc, null, null); 811 } 812 813 public void writeImageLinkNewWindow(String link, String imageSrc, String title) { 814 writeImageLink(link, imageSrc, "_blank", title); 815 } 816 817 public void writeImageLink(String link, String imageSrc, String target, String title) { 818 writeImageLink(link, imageSrc, target, title, null, null); 819 } 820 821 public void writeImageLink(String link, String imageSrc, String target, String title, Integer width, Integer height) { 822 validateNormalMode(); 823 String targetString = ""; 824 if (target != null && !target.equals("")) { 825 targetString = " target=\"" + target + "\""; 826 } 827 String titleString = ""; 828 if (title != null && !title.equals("")) { 829 titleString = " title=\"" + XmlUtil.encodeXmlString(title) + "\""; 830 } 831 link = adjustLink(link); 832 writeHtml("<a href=\"" + link + "\"" + targetString + titleString + ">"); 833 levelUp(HtmlMode.IMAGE); 834 writeNewlineInternal(); 835 writeImage(imageSrc, null, width, height, title, null); 836 levelDown(HtmlMode.IMAGE); 837 writeHtml("</a>"); 838 } 839 840 public void writeBreakLine() { 841 validateNormalMode(); 842 validateModeNotInHierarki(HtmlMode.HEADER); 843 writeHtml("<br>"); 844 writeNewlineInternal(); 845 } 846 847 public void writeNbsp() { 848 writeHtml(" "); 849 } 850 851 public void startAppletTag(String code, int width, int height) { 852 startAppletTag(code, width, height, null); 853 } 854 855 public void startAppletTag(String code, int width, int height, String codebase) { 856 validateNormalMode(); 857 validateModeNotInHierarki(HtmlMode.HEADER); 858 String codebaseString = ""; 859 if (codebase != null && !codebase.equals("")) { 860 codebaseString = " codebase=\"" + codebase + "\""; 861 } 862 writeHtmlLine("<APPLET" + codebaseString + " CODE=\"" + code + "\" WIDTH=" + width + " HEIGHT=" + height + ">"); 863 levelUp(HtmlMode.APPLET); 864 } 865 866 public void writeAppletParam(String name, String value) { 867 if (mode != HtmlMode.APPLET) { 868 throw new RuntimeException("mode != HtmlMode.APPLET " + mode); 869 } 870 writeHtmlLine("<param name=\"" + name + "\" value=\"" + value + "\">"); 871 } 872 873 public void writeAppletAlt() { 874 writeAppletAlt("Your browser understands the <APPLET> tag but isn't running the applet, for some reason.", "Your browser is ignoring the <APPLET> tag!"); 875 } 876 877 public void writeAppletAlt(String altText, String text) { 878 if (mode != HtmlMode.APPLET) { 879 throw new RuntimeException("mode != HtmlMode.APPLET " + mode); 880 } 881 writeHtml("ALT=\""); 882 writeText(altText); 883 writeHtml("\""); 884 writeNewlineInternal(); 885 writeTextLine(text); 886 mode = HtmlMode.APPLET_AFTER_ALT; 887 } 888 889 public void endAppletTag() { 890 if (mode != HtmlMode.APPLET && mode != HtmlMode.APPLET_AFTER_ALT) { 891 throw new RuntimeException("mode != HtmlMode.APPLET && mode != HtmlMode.APPLET_AFTER_ALT " + mode); 892 } 893 levelDown(); 894 writeHtmlLine("</APPLET>"); 895 } 896 897 private void writeEnsureNewline() { 898 if (!startOfLine) { 899 writeNewlineInternal(); 900 } 901 } 902 903 public void writeTextWithNewlines(String text) { 904 writeTextWithNewlines(text, 0); 905 } 906 907 public void writeTextWithNewlines(String text, int indentCount) { 908 String[] split = text.split("\n"); 909 for (int i = 0; i < split.length; i++) { 910 if (i > 0) { 911 writeBreakLine(); 912 for (int j = 0; j < indentCount; j++) { 913 writeNbsp(); 914 } 915 } 916 writeText(split[i]); 917 } 918 } 919 920 public void writeTextLine(String line) { 921 writeText(line); 922 writeNewlineInternal(); 923 } 924 925 /** 926 * Writes the text with escaping / XML encoding. 927 * <br> 928 * E.g. ">" is changed to "&gt;"<br> 929 * E.g. "&gt;" is changed to "&amp;gt;" 930 * @see #writeHtml(String) 931 */ 932 public void writeText(String text) { 933 validateNormalMode(); 934 String html = XmlUtil.encodeXmlString(text); 935 html = html.replace("'", "'"); // ' doesn't work in old HTML and in Internet Explorer 936 writeHtml(html); 937 } 938 939 public void writeHtmlLine(String line) { 940 writeHtml(line); 941 writeNewlineInternal(); 942 } 943 944 /** 945 * Writes the HTML as is e.g. no escaping / XML encoding. 946 * <br> 947 * E.g. ">" is not changed 948 * @see #writeText(String) 949 */ 950 public void writeHtml(String html) { 951 if (startOfLine && indent > 0) { 952 String indentString = StringUtil.stringWithNumberOfChar(indent * 2, ' '); 953 writeString(indentString); 954 } 955 startOfLine = false; 956 writeString(html); 957 } 958 959 protected void writeNewlineInternal() { 960 writeString("\n"); 961 startOfLine = true; 962 } 963 964 protected void writeString(String text) { 965 try { 966 outWriter.write(text); 967 } catch (Exception e) { 968 throw new RuntimeException(e); 969 } 970 } 971 972 protected void forceClose() { 973 try { 974 outWriter.close(); 975 mode = HtmlMode.CLOSED; 976 } catch (IOException e) { 977 throw new RuntimeException(e); 978 } 979 } 980 981 public boolean isEmpty() { 982 return mode == HtmlMode.BEFORE_START; 983 } 984 985 public boolean isOpenAndNotNestedTags() { 986 return mode == HtmlMode.BODY; 987 } 988 989 public boolean isClosed() { 990 return mode == HtmlMode.CLOSED; 991 } 992 993 public void close() { 994 try { 995 if (isEmpty()) { 996 if (isClosed()) { 997 throw new RuntimeException("isClosed()"); 998 } 999 } else { 1000 if (!isOpenAndNotNestedTags()) { 1001 throw new RuntimeException("!isOpenAndNotNestedTags() " + mode); 1002 } 1003 writeNewlineInternal(); 1004 if (mode == HtmlMode.BODY) { 1005 writeHtmlLine("</body>"); 1006 } 1007 writeHtmlLine("</html>"); 1008 } 1009 outWriter.close(); 1010 mode = HtmlMode.CLOSED; 1011 } catch (Exception e) { 1012 throw new RuntimeException(e); 1013 } 1014 } 1015 1016 public String getLanguage() { 1017 return language; 1018 } 1019 1020 public void setLanguage(String planguage) { 1021 validateBeforeStart(); 1022 if (planguage == "dk") { 1023 throw new RuntimeException("planguage == \"dk\". Should be \"da\""); 1024 } 1025 language = planguage; 1026 } 1027 1028 public String getEncoding() { 1029 return encoding; 1030 } 1031 1032 protected void setEncoding(OutputStream poutStream, String pencoding) { 1033 validateBeforeStart(); 1034 encoding = pencoding; 1035 try { 1036 outWriter = new BufferedWriter(new OutputStreamWriter(poutStream, encoding)); 1037 } catch (UnsupportedEncodingException e) { 1038 throw new RuntimeException(e); 1039 } 1040 } 1041 1042 protected void levelUp(HtmlMode pmode) { 1043 levelUp(pmode, true); 1044 } 1045 1046 private void levelUp(HtmlMode pmode, boolean checkForUniqueness) { 1047 storeCurrentMode(); 1048 if (checkForUniqueness) { 1049 checkNestedModeForUniqueness(pmode); 1050 } 1051 if (mode == pmode) { 1052 throw new RuntimeException("Mode nested in same mode (mode == pmode) " + mode + " " + pmode); 1053 } 1054 mode = pmode; 1055 indent++; 1056 } 1057 1058 protected void levelDown() { 1059 levelDown(null); 1060 } 1061 1062 protected void levelDown(HtmlMode pmode) { 1063 if (pmode != null && mode != pmode) { 1064 throw new RuntimeException("mode != HtmlMode." + pmode + ", mode: HtmlMode." + mode); 1065 } 1066 restoredMode(); 1067 indent--; 1068 } 1069 1070 protected void validateNormalMode() { 1071 if (mode != HtmlMode.BODY && mode != HtmlMode.CELL && mode != HtmlMode.CENTER && mode != HtmlMode.BOLD && mode != HtmlMode.HEADER && mode != HtmlMode.FONT_SIZE && mode != HtmlMode.STRONG && mode != HtmlMode.FIELDSET && mode != HtmlMode.FORM && mode != HtmlMode.TEXT_COLOR) { 1072 throw new RuntimeException("mode != HtmlMode.BODY && mode != HtmlMode.NO_FRAME && mode != HtmlMode.CELL && mode != HtmlMode.CENTER && mode != HtmlMode.BOLD && mode != HtmlMode.HEADER && mode != HtmlMode.FONT && mode != HtmlMode.STRONG && mode != HtmlMode.FIELDSET && mode != HtmlMode.FORM && mode != HtmlMode.COLOR " + mode); 1073 } 1074 } 1075 1076 protected void validateBeforeStart() { 1077 if (mode != HtmlMode.BEFORE_START) { 1078 throw new RuntimeException("mode != HtmlMode.BEFORE_START " + mode); 1079 } 1080 } 1081 1082 protected void validateModeNotInHierarki(HtmlMode searchMode) { 1083 if (inHierarki(searchMode)) { 1084 if (searchMode == HtmlMode.FORM) { 1085 throw new RuntimeException("Already in form"); 1086 } 1087 1088 throw new RuntimeException("Mode HtmlMode." + searchMode + " is in hierarki (mode: " + mode + ", nestedModes: " + nestedModes + ")"); 1089 } 1090 } 1091 1092 protected void validateInForm() { 1093 validateNormalMode(); 1094 if (!inHierarki(HtmlMode.FORM)) { 1095 throw new RuntimeException("Not in form (" + mode + " " + nestedModes + ")"); 1096 } 1097 } 1098 1099 protected void writeIdAndStyle(IdAndStyle idAndStyle) { 1100 if (idAndStyle == null) { 1101 return ; 1102 } 1103 1104 validateAndAddId(idAndStyle.getId()); 1105 1106 writeHtml(idAndStyle.toString()); 1107 } 1108 1109 protected void writeId(String id) { 1110 if (id == null) { 1111 return ; 1112 } 1113 1114 writeHtml(idToStringWithValidation(id)); 1115 } 1116 1117 public String idToStringWithValidation(String id) { 1118 if (id == null) { 1119 return ""; 1120 } 1121 1122 validateAndAddId(id); 1123 return idToStringNoValidation(id); 1124 } 1125 1126 public static String idToStringNoValidation(String id) { 1127 if (id == null) { 1128 return ""; 1129 } 1130 1131 return " id=\"" + XmlUtil.encodeXmlString(id) + "\""; 1132 } 1133 1134 public static void validateId(String id) { 1135 if (id == null) { 1136 return ; 1137 } 1138 1139 if (id.contains(" ")) { 1140 throw new RuntimeException("id.contains(\" \") \"" + id + "\""); 1141 } 1142 if (id.length() == 0) { 1143 throw new RuntimeException("id.length() == 0 \"" + id + "\""); 1144 } 1145 } 1146 1147 protected void validateAndAddId(String id) { 1148 if (id == null) { 1149 return ; 1150 } 1151 1152 validateId(id); 1153 if (usedId.contains(id)) { 1154 throw new RuntimeException("Id \"" + id + "\" is not unique " + usedId); 1155 } 1156 usedId.add(id); 1157 } 1158 1159 protected boolean inHierarki(HtmlMode searchMode) { 1160 if (mode == searchMode) { 1161 return true; 1162 } 1163 1164 if (nestedModes == null) { 1165 return false; 1166 } 1167 1168 for (HtmlMode htmlMode : nestedModes) { 1169 if (htmlMode == searchMode) { 1170 return true; 1171 } 1172 } 1173 1174 return false; 1175 } 1176 1177 private void checkNestedModeForUniqueness(HtmlMode searchMode) { 1178/* if (mode == searchMode) { 1179 throw new RuntimeException("Mode is not unique " + searchMode + " (mode == searchMode)"); 1180 } 1181 if (nestedModes == null) { 1182 return ; 1183 } 1184 for (int i = 0; i < nestedModes.size();i++) { 1185 if (searchMode == nestedModes.get(i)) { 1186 throw new RuntimeException("Mode is not unique " + searchMode + " " + i + " " + nestedModes); 1187 } 1188 }*/ 1189 if (inHierarki(searchMode)) { 1190 throw new RuntimeException("Mode is not unique! searchMode: " + searchMode + ", mode: " + mode + ", nestedModes: " + nestedModes); 1191 } 1192 } 1193 1194 private void storeCurrentMode() { 1195 if (nestedModes == null) { 1196 nestedModes = new ArrayList<HtmlMode>(); 1197 } 1198 nestedModes.add(mode); 1199 } 1200 1201 private void restoredMode() { 1202 mode = nestedModes.remove(nestedModes.size() - 1); 1203 } 1204}