001 /* HTMLWriter.java -- 002 Copyright (C) 2006 Free Software Foundation, Inc. 003 004 This file is part of GNU Classpath. 005 006 GNU Classpath is free software; you can redistribute it and/or modify 007 it under the terms of the GNU General Public License as published by 008 the Free Software Foundation; either version 2, or (at your option) 009 any later version. 010 011 GNU Classpath is distributed in the hope that it will be useful, but 012 WITHOUT ANY WARRANTY; without even the implied warranty of 013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014 General Public License for more details. 015 016 You should have received a copy of the GNU General Public License 017 along with GNU Classpath; see the file COPYING. If not, write to the 018 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 019 02110-1301 USA. 020 021 Linking this library statically or dynamically with other modules is 022 making a combined work based on this library. Thus, the terms and 023 conditions of the GNU General Public License cover the whole 024 combination. 025 026 As a special exception, the copyright holders of this library give you 027 permission to link this library with independent modules to produce an 028 executable, regardless of the license terms of these independent 029 modules, and to copy and distribute the resulting executable under 030 terms of your choice, provided that you also meet, for each linked 031 independent module, the terms and conditions of the license of that 032 module. An independent module is a module which is not derived from 033 or based on this library. If you modify this library, you may extend 034 this exception to your version of the library, but you are not 035 obligated to do so. If you do not wish to do so, delete this 036 exception statement from your version. */ 037 038 package javax.swing.text.html; 039 040 import gnu.java.lang.CPStringBuilder; 041 042 import java.io.IOException; 043 import java.io.Writer; 044 045 import java.util.Enumeration; 046 import java.util.HashSet; 047 048 import javax.swing.ComboBoxModel; 049 050 import javax.swing.text.AbstractWriter; 051 import javax.swing.text.AttributeSet; 052 import javax.swing.text.BadLocationException; 053 import javax.swing.text.Document; 054 import javax.swing.text.Element; 055 import javax.swing.text.StyleConstants; 056 057 import javax.swing.text.html.HTML; 058 import javax.swing.text.html.HTMLDocument; 059 import javax.swing.text.html.Option; 060 061 /** 062 * HTMLWriter, 063 * A Writer for HTMLDocuments. 064 * 065 * @author David Fu (fchoong at netbeans.jp) 066 */ 067 068 public class HTMLWriter 069 extends AbstractWriter 070 { 071 /** 072 * We keep a reference of the writer passed by the construct. 073 */ 074 private Writer outWriter = null; 075 076 /** 077 * We keep a reference of the HTMLDocument passed by the construct. 078 */ 079 private HTMLDocument htmlDoc = null; 080 081 /** 082 * Used to keep track of which embeded has been written out. 083 */ 084 private HashSet openEmbededTagHashSet = null; 085 086 private String new_line_str = "" + NEWLINE; 087 088 private char[] html_entity_char_arr = {'<', '>', '&', '"'}; 089 090 private String[] html_entity_escape_str_arr = {"<", ">", "&", 091 """}; 092 093 // variables used to output Html Fragment 094 private int doc_pos = -1; 095 private int doc_len = -1; 096 private int doc_offset_remaining = -1; 097 private int doc_len_remaining = -1; 098 private HashSet htmlFragmentParentHashSet = null; 099 private Element startElem = null; 100 private Element endElem = null; 101 private boolean fg_pass_start_elem = false; 102 private boolean fg_pass_end_elem = false; 103 104 /** 105 * Constructs a HTMLWriter. 106 * 107 * @param writer writer to write output to 108 * @param doc the HTMLDocument to output 109 */ 110 public HTMLWriter(Writer writer, HTMLDocument doc) 111 { 112 super(writer, doc); 113 outWriter = writer; 114 htmlDoc = doc; 115 openEmbededTagHashSet = new HashSet(); 116 } // public HTMLWriter(Writer writer, HTMLDocument doc) 117 118 /** 119 * Constructs a HTMLWriter which outputs a Html Fragment. 120 * 121 * @param writer <code>Writer</code> to write output to 122 * @param doc the <code>javax.swing.text.html.HTMLDocument</code> 123 * to output 124 * @param pos position to start outputing the document 125 * @param len amount to output the document 126 */ 127 public HTMLWriter(Writer writer, HTMLDocument doc, int pos, int len) 128 { 129 super(writer, doc, pos, len); 130 outWriter = writer; 131 htmlDoc = doc; 132 openEmbededTagHashSet = new HashSet(); 133 134 doc_pos = pos; 135 doc_offset_remaining = pos; 136 doc_len = len; 137 doc_len_remaining = len; 138 htmlFragmentParentHashSet = new HashSet(); 139 } // public HTMLWriter(Writer writer, HTMLDocument doc, int pos, int len) 140 141 /** 142 * Call this method to start outputing HTML. 143 * 144 * @throws IOException on any I/O exceptions 145 * @throws BadLocationException if a pos is not a valid position in the 146 * html doc element 147 */ 148 public void write() 149 throws IOException, BadLocationException 150 { 151 Element rootElem = htmlDoc.getDefaultRootElement(); 152 153 if (doc_pos == -1 && doc_len == -1) 154 { 155 // Normal traversal. 156 traverse(rootElem); 157 } // if(doc_pos == -1 && doc_len == -1) 158 else 159 { 160 // Html fragment traversal. 161 if (doc_pos == -1 || doc_len == -1) 162 throw new BadLocationException("Bad Location(" 163 + doc_pos + ", " + doc_len + ")", doc_pos); 164 165 startElem = htmlDoc.getCharacterElement(doc_pos); 166 167 int start_offset = startElem.getStartOffset(); 168 169 // Positions before start_offset will not be traversed, and thus 170 // will not be counted. 171 if (start_offset > 0) 172 doc_offset_remaining = doc_offset_remaining - start_offset; 173 174 Element tempParentElem = startElem; 175 176 while ((tempParentElem = tempParentElem.getParentElement()) != null) 177 { 178 if (!htmlFragmentParentHashSet.contains(tempParentElem)) 179 htmlFragmentParentHashSet.add(tempParentElem); 180 } // while((tempParentElem = tempParentElem.getParentElement()) 181 // != null) 182 183 // NOTE: 20061030 - fchoong - the last index should not be included. 184 endElem = htmlDoc.getCharacterElement(doc_pos + doc_len - 1); 185 186 tempParentElem = endElem; 187 188 while ((tempParentElem = tempParentElem.getParentElement()) != null) 189 { 190 if (!htmlFragmentParentHashSet.contains(tempParentElem)) 191 htmlFragmentParentHashSet.add(tempParentElem); 192 } // while((tempParentElem = tempParentElem.getParentElement()) 193 // != null) 194 195 traverseHtmlFragment(rootElem); 196 197 } // else 198 199 // NOTE: close out remaining open embeded tags. 200 Object[] tag_arr = openEmbededTagHashSet.toArray(); 201 202 for (int i = 0; i < tag_arr.length; i++) 203 { 204 writeRaw("</" + tag_arr[i].toString() + ">"); 205 } // for(int i = 0; i < tag_arr.length; i++) 206 207 } // public void write() throws IOException, BadLocationException 208 209 /** 210 * Writes all the attributes in the attrSet, except for attrbutes with 211 * keys of <code>javax.swing.text.html.HTML.Tag</code>, 212 * <code>javax.swing.text.StyleConstants</code> or 213 * <code>javax.swing.text.html.HTML.Attribute.ENDTAG</code>. 214 * 215 * @param attrSet attrSet to write out 216 * 217 * @throws IOException on any I/O exceptions 218 */ 219 protected void writeAttributes(AttributeSet attrSet) 220 throws IOException 221 { 222 Enumeration attrNameEnum = attrSet.getAttributeNames(); 223 224 while (attrNameEnum.hasMoreElements()) 225 { 226 Object key = attrNameEnum.nextElement(); 227 Object value = attrSet.getAttribute(key); 228 229 // HTML.Attribute.ENDTAG is an instance, not a class. 230 if (!((key instanceof HTML.Tag) || (key instanceof StyleConstants) 231 || (key == HTML.Attribute.ENDTAG))) 232 { 233 if (key == HTML.Attribute.SELECTED) 234 writeRaw(" selected"); 235 else if (key == HTML.Attribute.CHECKED) 236 writeRaw(" checked"); 237 else 238 writeRaw(" " + key + "=\"" + value + "\""); 239 } // if(!((key instanceof HTML.Tag) || (key instanceof 240 // StyleConstants) || (key == HTML.Attribute.ENDTAG))) 241 } // while(attrNameEnum.hasMoreElements()) 242 243 } // protected void writeAttributes(AttributeSet attrSet) throws IOException 244 245 /** 246 * Writes out an empty tag. i.e. a tag without any child elements. 247 * 248 * @param paramElem the element to output as an empty tag 249 * 250 * @throws IOException on any I/O exceptions 251 * @throws BadLocationException if a pos is not a valid position in the 252 * html doc element 253 */ 254 protected void emptyTag(Element paramElem) 255 throws IOException, BadLocationException 256 { 257 String elem_name = paramElem.getName(); 258 AttributeSet attrSet = paramElem.getAttributes(); 259 260 writeRaw("<" + elem_name); 261 writeAttributes(attrSet); 262 writeRaw(">"); 263 264 if (isBlockTag(attrSet)) 265 { 266 writeRaw("</" + elem_name + ">"); 267 } // if(isBlockTag(attrSet)) 268 269 } // protected void emptyTag(Element paramElem) 270 // throws IOException, BadLocationException 271 272 /** 273 * Determines if it is a block tag or not. 274 * 275 * @param attrSet the attrSet of the element 276 * 277 * @return <code>true</code> if it is a block tag 278 * <code>false</code> if it is a not block tag 279 */ 280 protected boolean isBlockTag(AttributeSet attrSet) 281 { 282 return ((HTML.Tag) 283 attrSet.getAttribute(StyleConstants.NameAttribute)).isBlock(); 284 } // protected boolean isBlockTag(AttributeSet attrSet) 285 286 /** 287 * Writes out a start tag. Synthesized elements are skipped. 288 * 289 * @param paramElem the element to output as a start tag 290 * @throws IOException on any I/O exceptions 291 * @throws BadLocationException if a pos is not a valid position in the 292 * html doc element 293 */ 294 protected void startTag(Element paramElem) 295 throws IOException, BadLocationException 296 { 297 // NOTE: Sysnthesized elements do no call this method at all. 298 String elem_name = paramElem.getName(); 299 AttributeSet attrSet = paramElem.getAttributes(); 300 301 indent(); 302 writeRaw("<" + elem_name); 303 writeAttributes(attrSet); 304 writeRaw(">"); 305 writeLineSeparator(); // Extra formatting to look more like the RI. 306 incrIndent(); 307 308 } // protected void startTag(Element paramElem) 309 // throws IOException, BadLocationException 310 311 /** 312 * Writes out the contents of a textarea. 313 * 314 * @param attrSet the attrSet of the element to output as a text area 315 * @throws IOException on any I/O exceptions 316 * @throws BadLocationException if a pos is not a valid position in the 317 * html doc element 318 */ 319 protected void textAreaContent(AttributeSet attrSet) 320 throws IOException, BadLocationException 321 { 322 writeLineSeparator(); // Extra formatting to look more like the RI. 323 indent(); 324 writeRaw("<textarea"); 325 writeAttributes(attrSet); 326 writeRaw(">"); 327 328 Document tempDocument = 329 (Document) attrSet.getAttribute(StyleConstants.ModelAttribute); 330 331 writeRaw(tempDocument.getText(0, tempDocument.getLength())); 332 indent(); 333 writeRaw("</textarea>"); 334 335 } // protected void textAreaContent(AttributeSet attrSet) 336 // throws IOException, BadLocationException 337 338 /** 339 * Writes out text, within the appropriate range if it is specified. 340 * 341 * @param paramElem the element to output as a text 342 * @throws IOException on any I/O exceptions 343 * @throws BadLocationException if a pos is not a valid position in the 344 * html doc element 345 */ 346 protected void text(Element paramElem) 347 throws IOException, BadLocationException 348 { 349 int offset = paramElem.getStartOffset(); 350 int len = paramElem.getEndOffset() - paramElem.getStartOffset(); 351 String txt_value = htmlDoc.getText(offset, len); 352 353 writeContent(txt_value); 354 355 } // protected void text(Element paramElem) 356 // throws IOException, BadLocationException 357 358 /** 359 * Writes out the contents of a select element. 360 * 361 * @param attrSet the attrSet of the element to output as a select box 362 * 363 * @throws IOException on any I/O exceptions 364 */ 365 protected void selectContent(AttributeSet attrSet) 366 throws IOException 367 { 368 writeLineSeparator(); // Extra formatting to look more like the RI. 369 indent(); 370 writeRaw("<select"); 371 writeAttributes(attrSet); 372 writeRaw(">"); 373 incrIndent(); 374 writeLineSeparator(); // extra formatting to look more like the RI. 375 376 ComboBoxModel comboBoxModel = 377 (ComboBoxModel) attrSet.getAttribute(StyleConstants.ModelAttribute); 378 379 for (int i = 0; i < comboBoxModel.getSize(); i++) 380 { 381 writeOption((Option) comboBoxModel.getElementAt(i)); 382 } // for(int i = 0; i < comboBoxModel.getSize(); i++) 383 384 decrIndent(); 385 indent(); 386 writeRaw("</select>"); 387 388 } // protected void selectContent(AttributeSet attrSet) throws IOException 389 390 /** 391 * Writes out the contents of an option element. 392 * 393 * @param option the option object to output as a select option 394 * 395 * @throws IOException on any I/O exceptions 396 */ 397 protected void writeOption(Option option) 398 throws IOException 399 { 400 indent(); 401 writeRaw("<option"); 402 writeAttributes(option.getAttributes()); 403 writeRaw(">"); 404 405 writeContent(option.getLabel()); 406 407 writeRaw("</option>"); 408 writeLineSeparator(); // extra formatting to look more like the RI. 409 410 } // protected void writeOption(Option option) throws IOException 411 412 /** 413 * Writes out an end tag. 414 * 415 * @param paramElem the element to output as an end tag 416 * 417 * @throws IOException on any I/O exceptions 418 */ 419 protected void endTag(Element paramElem) 420 throws IOException 421 { 422 String elem_name = paramElem.getName(); 423 424 //writeLineSeparator(); // Extra formatting to look more like the RI. 425 decrIndent(); 426 indent(); 427 writeRaw("</" + elem_name + ">"); 428 writeLineSeparator(); // Extra formatting to look more like the RI. 429 430 } // protected void endTag(Element paramElem) throws IOException 431 432 /** 433 * Writes out the comment. 434 * 435 * @param paramElem the element to output as a comment 436 */ 437 protected void comment(Element paramElem) 438 throws IOException, BadLocationException 439 { 440 AttributeSet attrSet = paramElem.getAttributes(); 441 442 String comment_str = (String) attrSet.getAttribute(HTML.Attribute.COMMENT); 443 444 writeRaw("<!--" + comment_str + "-->"); 445 446 } // protected void comment(Element paramElem) 447 // throws IOException, BadLocationException 448 449 /** 450 * Determines if element is a synthesized 451 * <code>javax.swing.text.Element</code> or not. 452 * 453 * @param element the element to test 454 * 455 * @return <code>true</code> if it is a synthesized element, 456 * <code>false</code> if it is a not synthesized element 457 */ 458 protected boolean synthesizedElement(Element element) 459 { 460 AttributeSet attrSet = element.getAttributes(); 461 Object tagType = attrSet.getAttribute(StyleConstants.NameAttribute); 462 463 if (tagType == HTML.Tag.CONTENT || tagType == HTML.Tag.COMMENT 464 || tagType == HTML.Tag.IMPLIED) 465 return true; 466 else 467 return false; 468 } // protected boolean synthesizedElement(Element element) 469 470 /** 471 * Determines if 472 * <code>javax.swing.text.StyleConstants.NameAttribute</code> 473 * matches tag or not. 474 * 475 * @param attrSet the <code>javax.swing.text.AttributeSet</code> of 476 * element to be matched 477 * @param tag the HTML.Tag to match 478 * 479 * @return <code>true</code> if it matches, 480 * <code>false</code> if it does not match 481 */ 482 protected boolean matchNameAttribute(AttributeSet attrSet, HTML.Tag tag) 483 { 484 Object tagType = attrSet.getAttribute(StyleConstants.NameAttribute); 485 486 if (tagType == tag) 487 return true; 488 else 489 return false; 490 } // protected boolean matchNameAttribute(AttributeSet attrSet, 491 // HTML.Tag tag) 492 493 /** 494 * Writes out an embedded tag. The tags not already in 495 * openEmbededTagHashSet will written out. 496 * 497 * @param attrSet the <code>javax.swing.text.AttributeSet</code> of 498 * the element to write out 499 * 500 * @throws IOException on any I/O exceptions 501 */ 502 protected void writeEmbeddedTags(AttributeSet attrSet) 503 throws IOException 504 { 505 Enumeration attrNameEnum = attrSet.getAttributeNames(); 506 507 while (attrNameEnum.hasMoreElements()) 508 { 509 Object key = attrNameEnum.nextElement(); 510 Object value = attrSet.getAttribute(key); 511 512 if (key instanceof HTML.Tag) 513 { 514 if (!openEmbededTagHashSet.contains(key)) 515 { 516 writeRaw("<" + key); 517 writeAttributes((AttributeSet) value); 518 writeRaw(">"); 519 openEmbededTagHashSet.add(key); 520 } // if(!openEmbededTagHashSet.contains(key)) 521 } // if(key instanceof HTML.Tag) 522 } // while(attrNameEnum.hasMoreElements()) 523 524 } // protected void writeEmbeddedTags(AttributeSet attrSet) 525 // throws IOException 526 527 /** 528 * Closes out an unwanted embedded tag. The tags from the 529 * openEmbededTagHashSet not found in attrSet will be written out. 530 * 531 * @param attrSet the AttributeSet of the element to write out 532 * 533 * @throws IOException on any I/O exceptions 534 */ 535 protected void closeOutUnwantedEmbeddedTags(AttributeSet attrSet) 536 throws IOException 537 { 538 Object[] tag_arr = openEmbededTagHashSet.toArray(); 539 540 for (int i = 0; i < tag_arr.length; i++) 541 { 542 HTML.Tag key = (HTML.Tag) tag_arr[i]; 543 544 if (!attrSet.isDefined(key)) 545 { 546 writeRaw("</" + key.toString() + ">"); 547 openEmbededTagHashSet.remove(key); 548 } // if(!attrSet.isDefined(key)) 549 } // for(int i = 0; i < tag_arr.length; i++) 550 551 } // protected void closeOutUnwantedEmbeddedTags(AttributeSet attrSet) 552 // throws IOException 553 554 /** 555 * Writes out a line separator. Overwrites the parent to write out a new 556 * line. 557 * 558 * @throws IOException on any I/O exceptions. 559 */ 560 protected void writeLineSeparator() 561 throws IOException 562 { 563 writeRaw(new_line_str); 564 } // protected void writeLineSeparator() throws IOException 565 566 /** 567 * Write to the writer. Character entites such as <, > 568 * are escaped appropriately. 569 * 570 * @param chars char array to write out 571 * @param off offset 572 * @param len length 573 * 574 * @throws IOException on any I/O exceptions 575 */ 576 protected void output(char[] chars, int off, int len) 577 throws IOException 578 { 579 CPStringBuilder strBuffer = new CPStringBuilder(); 580 581 for (int i = 0; i < chars.length; i++) 582 { 583 if (isCharHtmlEntity(chars[i])) 584 strBuffer.append(escapeCharHtmlEntity(chars[i])); 585 else 586 strBuffer.append(chars[i]); 587 } // for(int i = 0; i < chars.length; i++) 588 589 writeRaw(strBuffer.toString()); 590 591 } // protected void output(char[] chars, int off, int len) 592 // throws IOException 593 594 //------------------------------------------------------------------------- 595 // private methods 596 597 /** 598 * The main method used to traverse through the elements. 599 * 600 * @param paramElem element to traverse 601 * 602 * @throws IOException on any I/O exceptions 603 */ 604 private void traverse(Element paramElem) 605 throws IOException, BadLocationException 606 { 607 Element currElem = paramElem; 608 609 AttributeSet attrSet = currElem.getAttributes(); 610 611 closeOutUnwantedEmbeddedTags(attrSet); 612 613 // handle the tag 614 if (synthesizedElement(paramElem)) 615 { 616 if (matchNameAttribute(attrSet, HTML.Tag.CONTENT)) 617 { 618 writeEmbeddedTags(attrSet); 619 text(currElem); 620 } // if(matchNameAttribute(attrSet, HTML.Tag.CONTENT)) 621 else if (matchNameAttribute(attrSet, HTML.Tag.COMMENT)) 622 { 623 comment(currElem); 624 } // else if(matchNameAttribute(attrSet, HTML.Tag.COMMENT)) 625 else if (matchNameAttribute(attrSet, HTML.Tag.IMPLIED)) 626 { 627 int child_elem_count = currElem.getElementCount(); 628 629 if (child_elem_count > 0) 630 { 631 for (int i = 0; i < child_elem_count; i++) 632 { 633 Element childElem = paramElem.getElement(i); 634 635 traverse(childElem); 636 637 } // for(int i = 0; i < child_elem_count; i++) 638 } // if(child_elem_count > 0) 639 } // else if(matchNameAttribute(attrSet, HTML.Tag.IMPLIED)) 640 } // if(synthesizedElement(paramElem)) 641 else 642 { 643 // NOTE: 20061030 - fchoong - title is treated specially here. 644 // based on RI behavior. 645 if (matchNameAttribute(attrSet, HTML.Tag.TITLE)) 646 { 647 boolean fg_is_end_tag = false; 648 Enumeration attrNameEnum = attrSet.getAttributeNames(); 649 650 while (attrNameEnum.hasMoreElements()) 651 { 652 Object key = attrNameEnum.nextElement(); 653 Object value = attrSet.getAttribute(key); 654 655 if (key == HTML.Attribute.ENDTAG && value.equals("true")) 656 fg_is_end_tag = true; 657 } // while(attrNameEnum.hasMoreElements()) 658 659 if (fg_is_end_tag) 660 writeRaw("</title>"); 661 else 662 { 663 indent(); 664 writeRaw("<title>"); 665 666 String title_str = 667 (String) htmlDoc.getProperty(HTMLDocument.TitleProperty); 668 669 if (title_str != null) 670 writeContent(title_str); 671 672 } // else 673 } // if(matchNameAttribute(attrSet, HTML.Tag.TITLE)) 674 else if (matchNameAttribute(attrSet, HTML.Tag.PRE)) 675 { 676 // We pursue more stringent formating here. 677 attrSet = paramElem.getAttributes(); 678 679 indent(); 680 writeRaw("<pre"); 681 writeAttributes(attrSet); 682 writeRaw(">"); 683 684 int child_elem_count = currElem.getElementCount(); 685 686 for (int i = 0; i < child_elem_count; i++) 687 { 688 Element childElem = paramElem.getElement(i); 689 690 traverse(childElem); 691 692 } // for(int i = 0; i < child_elem_count; i++) 693 694 writeRaw("</pre>"); 695 696 } // else if(matchNameAttribute(attrSet, HTML.Tag.PRE)) 697 else if (matchNameAttribute(attrSet, HTML.Tag.SELECT)) 698 { 699 selectContent(attrSet); 700 } // else if(matchNameAttribute(attrSet, HTML.Tag.SELECT)) 701 else if (matchNameAttribute(attrSet, HTML.Tag.TEXTAREA)) 702 { 703 textAreaContent(attrSet); 704 } // else if(matchNameAttribute(attrSet, HTML.Tag.TEXTAREA)) 705 else 706 { 707 int child_elem_count = currElem.getElementCount(); 708 709 if (child_elem_count > 0) 710 { 711 startTag(currElem); 712 713 for (int i = 0; i < child_elem_count; i++) 714 { 715 Element childElem = paramElem.getElement(i); 716 717 traverse(childElem); 718 719 } // for(int i = 0; i < child_elem_count; i++) 720 721 endTag(currElem); 722 723 } // if(child_elem_count > 0) 724 else 725 { 726 emptyTag(currElem); 727 } // else 728 } // else 729 } // else 730 731 } // private void traverse(Element paramElem) 732 // throws IOException, BadLocationException 733 734 /** 735 * The method used to traverse through a html fragment. 736 * 737 * @param paramElem element to traverse 738 * 739 * @throws IOException on any I/O exceptions 740 */ 741 private void traverseHtmlFragment(Element paramElem) 742 throws IOException, BadLocationException 743 { 744 // NOTE: This method is similar to traverse(Element paramElem) 745 Element currElem = paramElem; 746 747 boolean fg_is_fragment_parent_elem = false; 748 boolean fg_is_start_and_end_elem = false; 749 750 if (htmlFragmentParentHashSet.contains(paramElem)) 751 fg_is_fragment_parent_elem = true; 752 753 if (paramElem == startElem) 754 fg_pass_start_elem = true; 755 756 if (paramElem == startElem && paramElem == endElem) 757 fg_is_start_and_end_elem = true; 758 759 AttributeSet attrSet = currElem.getAttributes(); 760 761 closeOutUnwantedEmbeddedTags(attrSet); 762 763 if (fg_is_fragment_parent_elem || (fg_pass_start_elem 764 && fg_pass_end_elem == false) || fg_is_start_and_end_elem) 765 { 766 // handle the tag 767 if (synthesizedElement(paramElem)) 768 { 769 if (matchNameAttribute(attrSet, HTML.Tag.CONTENT)) 770 { 771 writeEmbeddedTags(attrSet); 772 773 int content_offset = paramElem.getStartOffset(); 774 int content_length = currElem.getEndOffset() - content_offset; 775 776 if (doc_offset_remaining > 0) 777 { 778 if (content_length > doc_offset_remaining) 779 { 780 int split_len = content_length; 781 782 split_len = split_len - doc_offset_remaining; 783 784 if (split_len > doc_len_remaining) 785 split_len = doc_len_remaining; 786 787 // we need to split it. 788 String txt_value = htmlDoc.getText(content_offset 789 + doc_offset_remaining, split_len); 790 791 writeContent(txt_value); 792 793 doc_offset_remaining = 0; // the offset is used up. 794 doc_len_remaining = doc_len_remaining - split_len; 795 } // if(content_length > doc_offset_remaining) 796 else 797 { 798 // doc_offset_remaining is greater than the entire 799 // length of content 800 doc_offset_remaining = doc_offset_remaining 801 - content_length; 802 } // else 803 } // if(doc_offset_remaining > 0) 804 else if (content_length <= doc_len_remaining) 805 { 806 // we can fit the entire content. 807 text(currElem); 808 doc_len_remaining = doc_len_remaining - content_length; 809 } // else if(content_length <= doc_len_remaining) 810 else 811 { 812 // we need to split it. 813 String txt_value = htmlDoc.getText(content_offset, 814 doc_len_remaining); 815 816 writeContent(txt_value); 817 818 doc_len_remaining = 0; 819 } // else 820 821 } // if(matchNameAttribute(attrSet, HTML.Tag.CONTENT)) 822 else if (matchNameAttribute(attrSet, HTML.Tag.COMMENT)) 823 { 824 comment(currElem); 825 } // else if(matchNameAttribute(attrSet, HTML.Tag.COMMENT)) 826 else if (matchNameAttribute(attrSet, HTML.Tag.IMPLIED)) 827 { 828 int child_elem_count = currElem.getElementCount(); 829 830 if (child_elem_count > 0) 831 { 832 for (int i = 0; i < child_elem_count; i++) 833 { 834 Element childElem = paramElem.getElement(i); 835 836 traverseHtmlFragment(childElem); 837 838 } // for(int i = 0; i < child_elem_count; i++) 839 } // if(child_elem_count > 0) 840 } // else if(matchNameAttribute(attrSet, HTML.Tag.IMPLIED)) 841 } // if(synthesizedElement(paramElem)) 842 else 843 { 844 // NOTE: 20061030 - fchoong - the isLeaf() condition seems to 845 // generate the closest behavior to the RI. 846 if (paramElem.isLeaf()) 847 { 848 if (doc_offset_remaining > 0) 849 { 850 doc_offset_remaining--; 851 } // if(doc_offset_remaining > 0) 852 else if (doc_len_remaining > 0) 853 { 854 doc_len_remaining--; 855 } // else if(doc_len_remaining > 0) 856 } // if(paramElem.isLeaf()) 857 858 // NOTE: 20061030 - fchoong - title is treated specially here. 859 // based on RI behavior. 860 if (matchNameAttribute(attrSet, HTML.Tag.TITLE)) 861 { 862 boolean fg_is_end_tag = false; 863 Enumeration attrNameEnum = attrSet.getAttributeNames(); 864 865 while (attrNameEnum.hasMoreElements()) 866 { 867 Object key = attrNameEnum.nextElement(); 868 Object value = attrSet.getAttribute(key); 869 870 if (key == HTML.Attribute.ENDTAG && value.equals("true")) 871 fg_is_end_tag = true; 872 } // while(attrNameEnum.hasMoreElements()) 873 874 if (fg_is_end_tag) 875 writeRaw("</title>"); 876 else 877 { 878 indent(); 879 writeRaw("<title>"); 880 881 String title_str = 882 (String) htmlDoc.getProperty(HTMLDocument.TitleProperty); 883 884 if (title_str != null) 885 writeContent(title_str); 886 887 } // else 888 } // if(matchNameAttribute(attrSet, HTML.Tag.TITLE)) 889 else if (matchNameAttribute(attrSet, HTML.Tag.PRE)) 890 { 891 // We pursue more stringent formating here. 892 attrSet = paramElem.getAttributes(); 893 894 indent(); 895 writeRaw("<pre"); 896 writeAttributes(attrSet); 897 writeRaw(">"); 898 899 int child_elem_count = currElem.getElementCount(); 900 901 for (int i = 0; i < child_elem_count; i++) 902 { 903 Element childElem = paramElem.getElement(i); 904 905 traverseHtmlFragment(childElem); 906 907 } // for(int i = 0; i < child_elem_count; i++) 908 909 writeRaw("</pre>"); 910 911 } // else if(matchNameAttribute(attrSet, HTML.Tag.PRE)) 912 else if (matchNameAttribute(attrSet, HTML.Tag.SELECT)) 913 { 914 selectContent(attrSet); 915 } // else if(matchNameAttribute(attrSet, HTML.Tag.SELECT)) 916 else if (matchNameAttribute(attrSet, HTML.Tag.TEXTAREA)) 917 { 918 textAreaContent(attrSet); 919 } // else if(matchNameAttribute(attrSet, HTML.Tag.TEXTAREA)) 920 else 921 { 922 int child_elem_count = currElem.getElementCount(); 923 924 if (child_elem_count > 0) 925 { 926 startTag(currElem); 927 928 for (int i = 0; i < child_elem_count; i++) 929 { 930 Element childElem = paramElem.getElement(i); 931 932 traverseHtmlFragment(childElem); 933 934 } // for(int i = 0; i < child_elem_count; i++) 935 936 endTag(currElem); 937 938 } // if(child_elem_count > 0) 939 else 940 { 941 emptyTag(currElem); 942 } // else 943 } // else 944 } // else 945 946 } // if(fg_is_fragment_parent_elem || (fg_pass_start_elem 947 // && fg_pass_end_elem == false) || fg_is_start_and_end_elem) 948 949 if (paramElem == endElem) 950 fg_pass_end_elem = true; 951 952 } // private void traverseHtmlFragment(Element paramElem) 953 // throws IOException, BadLocationException 954 955 /** 956 * Write to the writer without any modifications. 957 * 958 * @param param_str the str to write out 959 * 960 * @throws IOException on any I/O exceptions 961 */ 962 private void writeRaw(String param_str) 963 throws IOException 964 { 965 super.output(param_str.toCharArray(), 0, param_str.length()); 966 } // private void writeRaw(char[] chars, int off, int len) 967 // throws IOException 968 969 /** 970 * Write to the writer, escaping HTML character entitie where neccessary. 971 * 972 * @param param_str the str to write out 973 * 974 * @throws IOException on any I/O exceptions 975 */ 976 private void writeContent(String param_str) 977 throws IOException 978 { 979 char[] str_char_arr = param_str.toCharArray(); 980 981 if (hasHtmlEntity(param_str)) 982 output(str_char_arr, 0, str_char_arr.length); 983 else 984 super.output(str_char_arr, 0, str_char_arr.length); 985 986 } // private void writeContent(String param_str) throws IOException 987 988 /** 989 * Use this for debugging. Writes out all attributes regardless of type. 990 * 991 * @param attrSet the <code>javax.swing.text.AttributeSet</code> to 992 * write out 993 * 994 * @throws IOException on any I/O exceptions 995 */ 996 private void writeAllAttributes(AttributeSet attrSet) 997 throws IOException 998 { 999 Enumeration attrNameEnum = attrSet.getAttributeNames(); 1000 1001 while (attrNameEnum.hasMoreElements()) 1002 { 1003 Object key = attrNameEnum.nextElement(); 1004 Object value = attrSet.getAttribute(key); 1005 1006 writeRaw(" " + key + "=\"" + value + "\""); 1007 writeRaw(" " + key.getClass().toString() + "=\"" 1008 + value.getClass().toString() + "\""); 1009 } // while(attrNameEnum.hasMoreElements()) 1010 1011 } // private void writeAllAttributes(AttributeSet attrSet) 1012 // throws IOException 1013 1014 /** 1015 * Tests if the str contains any html entities. 1016 * 1017 * @param param_str the str to test 1018 * 1019 * @return <code>true</code> if it has a html entity 1020 * <code>false</code> if it does not have a html entity 1021 */ 1022 private boolean hasHtmlEntity(String param_str) 1023 { 1024 boolean ret_bool = false; 1025 1026 for (int i = 0; i < html_entity_char_arr.length; i++) 1027 { 1028 if (param_str.indexOf(html_entity_char_arr[i]) != -1) 1029 { 1030 ret_bool = true; 1031 break; 1032 } // if(param_str.indexOf(html_entity_char_arr[i]) != -1) 1033 } // for(int i = 0; i < html_entity_char_arr.length; i++) 1034 1035 return ret_bool; 1036 } // private boolean hasHtmlEntity(String param_str) 1037 1038 /** 1039 * Tests if the char is a html entities. 1040 * 1041 * @param param_char the char to test 1042 * 1043 * @return <code>true</code> if it is a html entity 1044 * <code>false</code> if it is not a html entity. 1045 */ 1046 private boolean isCharHtmlEntity(char param_char) 1047 { 1048 boolean ret_bool = false; 1049 1050 for (int i = 0; i < html_entity_char_arr.length; i++) 1051 { 1052 if (param_char == html_entity_char_arr[i]) 1053 { 1054 ret_bool = true; 1055 break; 1056 } // if(param_char == html_entity_char_arr[i]) 1057 } // for(int i = 0; i < html_entity_char_arr.length; i++) 1058 1059 return ret_bool; 1060 } // private boolean hasHtmlEntity(String param_str) 1061 1062 /** 1063 * Escape html entities. 1064 * 1065 * @param param_char the char to escape 1066 * 1067 * @return escaped html entity. Original char is returned as a str if is 1068 * is not a html entity 1069 */ 1070 private String escapeCharHtmlEntity(char param_char) 1071 { 1072 String ret_str = "" + param_char; 1073 1074 for (int i = 0; i < html_entity_char_arr.length; i++) 1075 { 1076 if (param_char == html_entity_char_arr[i]) 1077 { 1078 ret_str = html_entity_escape_str_arr[i]; 1079 break; 1080 } // if(param_char == html_entity_char_arr[i]) 1081 } // for(int i = 0; i < html_entity_char_arr.length; i++) 1082 1083 return ret_str; 1084 } // private String escapeCharHtmlEntity(char param_char) 1085 1086 } // public class HTMLWriter extends AbstractWriter