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 gnu.java.lang.CPStringBuilder;
  41: 
  42: import java.io.IOException;
  43: import java.io.Writer;
  44: 
  45: import java.util.Enumeration;
  46: import java.util.HashSet;
  47: 
  48: import javax.swing.ComboBoxModel;
  49: 
  50: import javax.swing.text.AbstractWriter;
  51: import javax.swing.text.AttributeSet;
  52: import javax.swing.text.BadLocationException;
  53: import javax.swing.text.Document;
  54: import javax.swing.text.Element;
  55: import javax.swing.text.StyleConstants;
  56: 
  57: import javax.swing.text.html.HTML;
  58: import javax.swing.text.html.HTMLDocument;
  59: import javax.swing.text.html.Option;
  60: 
  61: /**
  62:  * HTMLWriter,
  63:  * A Writer for HTMLDocuments.
  64:  *
  65:  * @author David Fu (fchoong at netbeans.jp)
  66:  */
  67: 
  68: public class HTMLWriter
  69:   extends AbstractWriter
  70: {
  71:   /**
  72:    * We keep a reference of the writer passed by the construct.
  73:    */
  74:   private Writer outWriter = null;
  75: 
  76:   /**
  77:    * We keep a reference of the HTMLDocument passed by the construct.
  78:    */
  79:   private HTMLDocument htmlDoc = null; 
  80: 
  81:   /**
  82:    * Used to keep track of which embeded has been written out.
  83:    */
  84:   private HashSet openEmbededTagHashSet = null;
  85: 
  86:   private String new_line_str = "" + NEWLINE;
  87:     
  88:   private char[] html_entity_char_arr = {'<',    '>',    '&',     '"'};
  89: 
  90:   private String[] html_entity_escape_str_arr = {"&lt;", "&gt;", "&amp;", 
  91:                                                  "&quot;"};
  92: 
  93:   // variables used to output Html Fragment
  94:   private int doc_pos = -1;
  95:   private int doc_len = -1;
  96:   private int doc_offset_remaining = -1;
  97:   private int doc_len_remaining = -1;
  98:   private HashSet htmlFragmentParentHashSet = null;
  99:   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 &lt;, &gt;
 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