Source for javax.swing.text.html.HTMLWriter

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