Clover coverage report -
Coverage timestamp: Sun Apr 18 2004 21:32:30 EDT
file stats: LOC: 1,244   Methods: 34
NCLOC: 732   Classes: 1
 
 Source file Conditionals Statements Methods TOTAL
FixedHTMLWriter.java 55.1% 61.8% 70.6% 59.8%
coverage coverage
 1   
 import javax.swing.text.*;
 2   
 import javax.swing.text.html.*;
 3   
 import java.io.*;
 4   
 import java.util.*;
 5   
 
 6   
 
 7   
 /**
 8   
  * FixedHTMLWriter
 9   
  *
 10   
  * <p>This is the standard HTMLWriter from package
 11   
  * javax.swing.text.html. It only has been changed in
 12   
  * method 'createFontAttribute' so that font sizes
 13   
  * are written correctly inside content</p>.
 14   
  *
 15   
  * @version stage 11, April 27, 2003
 16   
  */
 17   
 
 18   
 public class FixedHTMLWriter extends HTMLWriter {
 19   
     /*
 20   
      * Stores all elements for which end tags have to
 21   
      * be emitted.
 22   
      */
 23   
     private Stack blockElementStack = new Stack();
 24   
     private boolean inContent = false;
 25   
     private boolean inPre = false;
 26   
     /** When inPre is true, this will indicate the end offset of the pre
 27   
      * element. */
 28   
     private int preEndOffset;
 29   
     private boolean inTextArea = false;
 30   
     private boolean newlineOutputed = false;
 31   
     private boolean completeDoc;
 32   
 
 33   
     /*
 34   
      * Stores all embedded tags. Embedded tags are tags that are
 35   
      * stored as attributes in other tags. Generally they're
 36   
      * character level attributes.  Examples include
 37   
      * &lt;b&gt;, &lt;i&gt;, &lt;font&gt;, and &lt;a&gt;.
 38   
      */
 39   
     private Vector tags = new Vector(10);
 40   
 
 41   
     /**
 42   
      * Values for the tags.
 43   
      */
 44   
     private Vector tagValues = new Vector(10);
 45   
 
 46   
     /**
 47   
      * Used when writing out content.
 48   
      */
 49   
     private Segment segment;
 50   
 
 51   
     /*
 52   
      * This is used in closeOutUnwantedEmbeddedTags.
 53   
      */
 54   
     private Vector tagsToRemove = new Vector(10);
 55   
 
 56   
     /**
 57   
      * Set to true after the head has been output.
 58   
      */
 59   
     private boolean wroteHead;
 60   
 
 61   
     /**
 62   
      * Set to true when entities (such as &lt;) should be replaced.
 63   
      */
 64   
     private boolean replaceEntities;
 65   
 
 66   
     /**
 67   
      * Temporary buffer.
 68   
      */
 69   
     private char[] tempChars;
 70   
 
 71   
 
 72   
     /**
 73   
      * Creates a new HTMLWriter.
 74   
      *
 75   
      * @param w   a Writer
 76   
      * @param doc  an HTMLDocument
 77   
      *
 78   
      */
 79  4
     public FixedHTMLWriter(Writer w, HTMLDocument doc) {
 80  4
         this(w, doc, 0, doc.getLength());
 81   
     }
 82   
 
 83   
     /**
 84   
      * Creates a new HTMLWriter.
 85   
      *
 86   
      * @param w  a Writer
 87   
      * @param doc an HTMLDocument
 88   
      * @param pos the document location from which to fetch the content
 89   
      * @param len the amount to write out
 90   
      */
 91  28
     public FixedHTMLWriter(Writer w, HTMLDocument doc, int pos, int len) {
 92  28
         super(w, doc, pos, len);
 93  28
         completeDoc = (pos == 0 && len == doc.getLength());
 94  28
         setLineLength(80);
 95   
     }
 96   
 
 97   
     /**
 98   
      * Iterates over the
 99   
      * Element tree and controls the writing out of
 100   
      * all the tags and its attributes.
 101   
      *
 102   
      * @exception IOException on any I/O error
 103   
      * @exception BadLocationException if pos represents an invalid
 104   
      *            location within the document.
 105   
      *
 106   
      */
 107  28
     public void write() throws IOException, BadLocationException {
 108  28
         ElementIterator it = getElementIterator();
 109  28
         Element current = null;
 110  28
         Element next = null;
 111   
 
 112  28
         wroteHead = false;
 113  28
         setCurrentLineLength(0);
 114  28
         replaceEntities = false;
 115  28
         setCanWrapLines(false);
 116  28
         if (segment == null) {
 117  28
             segment = new Segment();
 118   
         }
 119  28
         inPre = false;
 120  28
         boolean forcedBody = false;
 121  ?
         while ((next = it.next()) != null) {
 122  318
             if (!inRange(next)) {
 123  29
                 if (completeDoc && next.getAttributes().getAttribute(
 124   
                         StyleConstants.NameAttribute) == HTML.Tag.BODY) {
 125  0
                     forcedBody = true;
 126   
                 }
 127   
                 else {
 128  29
                     continue;
 129   
                 }
 130   
             }
 131  289
             if (current != null) {
 132   
 
 133   
                 /*
 134   
                   if next is child of current increment indent
 135   
                 */
 136   
 
 137  261
                 if (indentNeedsIncrementing(current, next)) {
 138  172
                     incrIndent();
 139  89
                 } else if (current.getParentElement() != next.getParentElement()) {
 140   
                     /*
 141   
                        next and current are not siblings
 142   
                        so emit end tags for items on the stack until the
 143   
                        item on top of the stack, is the parent of the
 144   
                        next.
 145   
                     */
 146  85
                     Element top = (Element)blockElementStack.peek();
 147  85
                     while (top != next.getParentElement()) {
 148   
                         /*
 149   
                            pop() will return top.
 150   
                         */
 151  108
                         blockElementStack.pop();
 152  108
                         if (!synthesizedElement(top)) {
 153  90
                             AttributeSet attrs = top.getAttributes();
 154  90
                             if (!matchNameAttribute(attrs, HTML.Tag.PRE) &&
 155   
                                 !isFormElementWithContent(attrs)) {
 156  90
                                 decrIndent();
 157   
                             }
 158  90
                             endTag(top);
 159   
                         }
 160  108
                         top = (Element)blockElementStack.peek();
 161   
                     }
 162  4
                 } else if (current.getParentElement() == next.getParentElement()) {
 163   
                     /*
 164   
                        if next and current are siblings the indent level
 165   
                        is correct.  But, we need to make sure that if current is
 166   
                        on the stack, we pop it off, and put out its end tag.
 167   
                     */
 168  4
                     Element top = (Element)blockElementStack.peek();
 169  4
                     if (top == current) {
 170  0
                         blockElementStack.pop();
 171  0
                         endTag(top);
 172   
                     }
 173   
                 }
 174   
             }
 175  289
             if (!next.isLeaf() || isFormElementWithContent(next.getAttributes())) {
 176  207
                 blockElementStack.push(next);
 177  207
                 startTag(next);
 178   
             } else {
 179  82
                 emptyTag(next);
 180   
             }
 181  289
             current = next;
 182   
         }
 183   
         /* Emit all remaining end tags */
 184   
 
 185   
         /* A null parameter ensures that all embedded tags
 186   
            currently in the tags vector have their
 187   
            corresponding end tags written out.
 188   
         */
 189  28
         closeOutUnwantedEmbeddedTags(null);
 190   
 
 191  28
         if (forcedBody) {
 192  0
             blockElementStack.pop();
 193  0
             endTag(current);
 194   
         }
 195  28
         while (!blockElementStack.empty()) {
 196  99
             current = (Element)blockElementStack.pop();
 197  99
             if (!synthesizedElement(current)) {
 198  82
                 AttributeSet attrs = current.getAttributes();
 199  82
                 if (!matchNameAttribute(attrs, HTML.Tag.PRE) &&
 200   
                               !isFormElementWithContent(attrs)) {
 201  82
                     decrIndent();
 202   
                 }
 203  82
                 endTag(current);
 204   
             }
 205   
         }
 206   
 
 207  28
         if (completeDoc) {
 208  28
             writeAdditionalComments();
 209   
         }
 210   
 
 211  28
         segment.array = null;
 212   
     }
 213   
 
 214   
 
 215   
     /**
 216   
      * Writes out the attribute set.  Ignores all
 217   
      * attributes with a key of type HTML.Tag,
 218   
      * attributes with a key of type StyleConstants,
 219   
      * and attributes with a key of type
 220   
      * HTML.Attribute.ENDTAG.
 221   
      *
 222   
      * @param attr   an AttributeSet
 223   
      * @exception IOException on any I/O error
 224   
      *
 225   
      */
 226  180
     protected void writeAttributes(AttributeSet attr) throws IOException {
 227   
         // translate css attributes to html
 228  180
         convAttr.removeAttributes(convAttr);
 229  180
         convertToHTML32(attr, convAttr);
 230   
 
 231  180
         Enumeration names = convAttr.getAttributeNames();
 232  180
         while (names.hasMoreElements()) {
 233  313
             Object name = names.nextElement();
 234  313
             if (name instanceof HTML.Tag ||
 235   
                 name instanceof StyleConstants ||
 236   
                 name == HTML.Attribute.ENDTAG) {
 237  183
                 continue;
 238   
             }
 239   
             //System.out.println("writeAttributes, writing converted " + name + ": " + convAttr.getAttribute(name));
 240  130
             write(" " + name + "=\"" + convAttr.getAttribute(name) + "\"");
 241   
         }
 242   
     }
 243   
 
 244   
     /**
 245   
      * Writes out all empty elements (all tags that have no
 246   
      * corresponding end tag).
 247   
      *
 248   
      * @param elem   an Element
 249   
      * @exception IOException on any I/O error
 250   
      * @exception BadLocationException if pos represents an invalid
 251   
      *            location within the document.
 252   
      */
 253  82
     protected void emptyTag(Element elem) throws BadLocationException, IOException {
 254   
 
 255  82
         if (!inContent && !inPre) {
 256  80
             indent();
 257   
         }
 258   
 
 259  82
         AttributeSet attr = elem.getAttributes();
 260  82
         closeOutUnwantedEmbeddedTags(attr);
 261  82
         writeEmbeddedTags(attr);
 262   
 
 263  82
         if (matchNameAttribute(attr, HTML.Tag.CONTENT)) {
 264  78
             inContent = true;
 265  78
             text(elem);
 266  4
         } else if (matchNameAttribute(attr, HTML.Tag.COMMENT)) {
 267  0
             comment(elem);
 268   
         }  else {
 269  4
             boolean isBlock = isBlockTag(elem.getAttributes());
 270  4
             if (inContent && isBlock ) {
 271  0
                 writeLineSeparator();
 272  0
                 indent();
 273   
             }
 274   
 
 275  4
             Object nameTag = (attr != null) ? attr.getAttribute
 276   
                               (StyleConstants.NameAttribute) : null;
 277  4
             Object endTag = (attr != null) ? attr.getAttribute
 278   
                               (HTML.Attribute.ENDTAG) : null;
 279   
 
 280  4
             boolean outputEndTag = false;
 281   
             // If an instance of an UNKNOWN Tag, or an instance of a
 282   
             // tag that is only visible during editing
 283   
             //
 284  4
             if (nameTag != null && endTag != null &&
 285   
                 (endTag instanceof String) &&
 286   
                 ((String)endTag).equals("true")) {
 287  1
                 outputEndTag = true;
 288   
             }
 289   
 
 290  4
             if (completeDoc && matchNameAttribute(attr, HTML.Tag.HEAD)) {
 291  0
                 if (outputEndTag) {
 292   
                     // Write out any styles.
 293  0
                     writeStyles(((HTMLDocument)getDocument()).getStyleSheet());
 294   
                 }
 295   
                 //System.out.println("FixedHTMLWriter.emptyTag: setting wroteHead=true");
 296   
                 //wroteHead = true;
 297   
             }
 298   
 
 299  4
             write('<');
 300  4
             if (outputEndTag) {
 301   
               //System.out.println("FixedHTMLWriter.emptyTag ENDTAG");
 302  1
                 write('/');
 303   
             }
 304  4
             write(elem.getName());
 305   
             //System.out.println("FixedHTMLWriter.emptyTag write " + elem.getName());
 306  4
             writeAttributes(attr);
 307  4
             write('>');
 308   
 
 309  4
             if (matchNameAttribute(attr, HTML.Tag.TITLE) && !outputEndTag) {
 310  1
                 Document doc = elem.getDocument();
 311  1
                 String title = (String)doc.getProperty(Document.TitleProperty);
 312   
                 //System.out.println("FixedHTMLWriter.emptyTag: writing title, title=" + title);
 313  1
                 write(title);
 314  3
             } else if (!inContent || isBlock) {
 315   
               //System.out.println("FixedHTMLWriter.emptyTag: !inContent || inBlock, writing line separator");
 316  3
                 writeLineSeparator();
 317  3
                 if (isBlock && inContent) {
 318   
                   //System.out.println("FixedHTMLWriter.emptyTag: isBlock && inContent, writing indent");
 319  0
                     indent();
 320   
                 }
 321   
             }
 322   
         }
 323   
     }
 324   
 
 325   
     /**
 326   
      * Determines if the HTML.Tag associated with the
 327   
      * element is a block tag.
 328   
      *
 329   
      * @param attr  an AttributeSet
 330   
      * @return  true if tag is block tag, false otherwise.
 331   
      */
 332  4
     protected boolean isBlockTag(AttributeSet attr) {
 333  4
         Object o = attr.getAttribute(StyleConstants.NameAttribute);
 334  4
         if (o instanceof HTML.Tag) {
 335  4
             HTML.Tag name = (HTML.Tag) o;
 336  4
             return name.isBlock();
 337   
         }
 338  0
         return false;
 339   
     }
 340   
 
 341   
 
 342   
     /**
 343   
      * Writes out a start tag for the element.
 344   
      * Ignores all synthesized elements.
 345   
      *
 346   
      * @param elem   an Element
 347   
      * @exception IOException on any I/O error
 348   
      */
 349  207
     protected void startTag(Element elem) throws IOException, BadLocationException {
 350   
 
 351   
       //System.out.println("FixedHTMLWriter.startTag elem=" + elem.getName());
 352   
 
 353  207
         if (synthesizedElement(elem)) {
 354  35
             return;
 355   
         }
 356   
 
 357   
         // Determine the name, as an HTML.Tag.
 358  172
         AttributeSet attr = elem.getAttributes();
 359  172
         Object nameAttribute = attr.getAttribute(StyleConstants.NameAttribute);
 360  172
         HTML.Tag name;
 361  172
         if (nameAttribute instanceof HTML.Tag) {
 362  172
             name = (HTML.Tag)nameAttribute;
 363   
         }
 364   
         else {
 365  0
             name = null;
 366   
         }
 367   
 
 368  172
         if (name == HTML.Tag.PRE) {
 369  0
             inPre = true;
 370  0
             preEndOffset = elem.getEndOffset();
 371   
         }
 372   
 
 373   
         // write out end tags for item on stack
 374  172
         closeOutUnwantedEmbeddedTags(attr);
 375   
 
 376  172
         if (inContent) {
 377  4
             writeLineSeparator();
 378  4
             inContent = false;
 379  4
             newlineOutputed = false;
 380   
         }
 381   
 
 382   
 
 383  172
         if (completeDoc && name == HTML.Tag.BODY && !wroteHead) {
 384   
             // If the head has not been output, output it and the styles.
 385  14
             wroteHead = true;
 386  14
             indent();
 387  14
             write("<head>");
 388  14
             writeLineSeparator();
 389  14
             incrIndent();
 390  14
             writeStyles(((HTMLDocument)getDocument()).getStyleSheet());
 391  14
             decrIndent();
 392  14
             writeLineSeparator();
 393  14
             indent();
 394  14
             write("</head>");
 395  14
             writeLineSeparator();
 396   
         }
 397   
 
 398   
 
 399  172
         indent();
 400  172
         write('<');
 401  172
         write(elem.getName());
 402  172
         writeAttributes(attr);
 403  172
         write('>');
 404  172
         if (name != HTML.Tag.PRE) {
 405  172
             writeLineSeparator();
 406   
         }
 407   
 
 408  172
         if (name == HTML.Tag.TEXTAREA) {
 409  0
             textAreaContent(elem.getAttributes());
 410  172
         } else if (name == HTML.Tag.SELECT) {
 411  0
             selectContent(elem.getAttributes());
 412  172
         } else if (completeDoc && name == HTML.Tag.BODY) {
 413   
             // Write out the maps, which is not stored as Elements in
 414   
             // the Document.
 415   
             //writeMaps(((HTMLDocument)getDocument()).getMaps());
 416   
         }
 417  144
         else if (name == HTML.Tag.HEAD) {
 418   
           //System.out.println("FixedHTMLWriter.startTag: setting wroteHead=true");
 419  14
           wroteHead = true;
 420   
         }
 421   
     }
 422   
 
 423   
 
 424   
     /**
 425   
      * Writes out text that is contained in a TEXTAREA form
 426   
      * element.
 427   
      *
 428   
      * @param attr  an AttributeSet
 429   
      * @exception IOException on any I/O error
 430   
      * @exception BadLocationException if pos represents an invalid
 431   
      *            location within the document.
 432   
      */
 433  0
     protected void textAreaContent(AttributeSet attr) throws BadLocationException, IOException {
 434  0
         Document doc = (Document)attr.getAttribute(StyleConstants.ModelAttribute);
 435  0
         if (doc != null && doc.getLength() > 0) {
 436  0
             if (segment == null) {
 437  0
                 segment = new Segment();
 438   
             }
 439  0
             doc.getText(0, doc.getLength(), segment);
 440  0
             if (segment.count > 0) {
 441  0
                 inTextArea = true;
 442  0
                 incrIndent();
 443  0
                 indent();
 444  0
                 setCanWrapLines(true);
 445  0
                 replaceEntities = true;
 446  0
                 write(segment.array, segment.offset, segment.count);
 447  0
                 replaceEntities = false;
 448  0
                 setCanWrapLines(false);
 449  0
                 writeLineSeparator();
 450  0
                 inTextArea = false;
 451  0
                 decrIndent();
 452   
             }
 453   
         }
 454   
     }
 455   
 
 456   
 
 457   
     /**
 458   
      * Writes out text.  If a range is specified when the constructor
 459   
      * is invoked, then only the appropriate range of text is written
 460   
      * out.
 461   
      *
 462   
      * @param elem   an Element
 463   
      * @exception IOException on any I/O error
 464   
      * @exception BadLocationException if pos represents an invalid
 465   
      *            location within the document.
 466   
      */
 467  78
     protected void text(Element elem) throws BadLocationException, IOException {
 468  78
         int start = Math.max(getStartOffset(), elem.getStartOffset());
 469  78
         int end = Math.min(getEndOffset(), elem.getEndOffset());
 470  78
         if (start < end) {
 471  77
             if (segment == null) {
 472  0
                 segment = new Segment();
 473   
             }
 474  77
             getDocument().getText(start, end - start, segment);
 475  77
             newlineOutputed = false;
 476  77
             if (segment.count > 0) {
 477  77
                 if (segment.array[segment.offset + segment.count - 1] == '\n'){
 478  55
                     newlineOutputed = true;
 479   
                 }
 480  77
                 if (inPre && end == preEndOffset) {
 481  0
                     if (segment.count > 1) {
 482  0
                         segment.count--;
 483   
                     }
 484   
                     else {
 485  0
                         return;
 486   
                     }
 487   
                 }
 488  77
                 replaceEntities = true;
 489  77
                 setCanWrapLines(!inPre);
 490  77
                 write(segment.array, segment.offset, segment.count);
 491  77
                 setCanWrapLines(false);
 492  77
                 replaceEntities = false;
 493   
             }
 494   
         }
 495   
     }
 496   
 
 497   
     /**
 498   
      * Writes out the content of the SELECT form element.
 499   
      *
 500   
      * @param attr the AttributeSet associated with the form element
 501   
      * @exception IOException on any I/O error
 502   
      */
 503  0
     protected void selectContent(AttributeSet attr) throws IOException {
 504   
       /*
 505   
         Object model = attr.getAttribute(StyleConstants.ModelAttribute);
 506   
         incrIndent();
 507   
         if (model instanceof OptionListModel) {
 508   
             OptionListModel listModel = (OptionListModel)model;
 509   
             int size = listModel.getSize();
 510   
             for (int i = 0; i < size; i++) {
 511   
                 Option option = (Option)listModel.getElementAt(i);
 512   
                 writeOption(option);
 513   
             }
 514   
         } else if (model instanceof OptionComboBoxModel) {
 515   
             OptionComboBoxModel comboBoxModel = (OptionComboBoxModel)model;
 516   
             int size = comboBoxModel.getSize();
 517   
             for (int i = 0; i < size; i++) {
 518   
                 Option option = (Option)comboBoxModel.getElementAt(i);
 519   
                 writeOption(option);
 520   
             }
 521   
         }
 522   
         decrIndent();
 523   
       */
 524   
     }
 525   
 
 526   
 
 527   
     /**
 528   
      * Writes out the content of the Option form element.
 529   
      * @param option  an Option
 530   
      * @exception IOException on any I/O error
 531   
      *
 532   
      */
 533  0
     protected void writeOption(Option option) throws IOException {
 534   
 
 535  0
         indent();
 536  0
         write('<');
 537  0
         write("option");
 538   
         // PENDING: should this be changed to check for null first?
 539  0
         Object value = option.getAttributes().getAttribute
 540   
                               (HTML.Attribute.VALUE);
 541  0
         if (value != null) {
 542  0
             write(" value="+ value);
 543   
         }
 544  0
         if (option.isSelected()) {
 545  0
             write(" selected");
 546   
         }
 547  0
         write('>');
 548  0
         if (option.getLabel() != null) {
 549  0
             write(option.getLabel());
 550   
         }
 551  0
         writeLineSeparator();
 552   
     }
 553   
 
 554   
     /**
 555   
      * Writes out an end tag for the element.
 556   
      *
 557   
      * @param elem    an Element
 558   
      * @exception IOException on any I/O error
 559   
      */
 560  172
     protected void endTag(Element elem) throws IOException {
 561   
       //System.out.println("FixedHTMLWriter.endTag elem=" + elem.getName());
 562  172
         if (synthesizedElement(elem)) {
 563  0
             return;
 564   
         }
 565  172
         if (matchNameAttribute(elem.getAttributes(), HTML.Tag.PRE)) {
 566  0
             inPre = false;
 567   
         }
 568   
 
 569   
         // write out end tags for item on stack
 570  172
         closeOutUnwantedEmbeddedTags(elem.getAttributes());
 571  172
         if (inContent) {
 572  72
             if (!newlineOutputed) {
 573  21
                 writeLineSeparator();
 574   
             }
 575  72
             newlineOutputed = false;
 576  72
             inContent = false;
 577   
         }
 578  172
         indent();
 579  172
         write('<');
 580  172
         write('/');
 581  172
         write(elem.getName());
 582  172
         write('>');
 583  172
         writeLineSeparator();
 584   
     }
 585   
 
 586   
 
 587   
 
 588   
     /**
 589   
      * Writes out comments.
 590   
      *
 591   
      * @param elem    an Element
 592   
      * @exception IOException on any I/O error
 593   
      * @exception BadLocationException if pos represents an invalid
 594   
      *            location within the document.
 595   
      */
 596  0
     protected void comment(Element elem) throws BadLocationException, IOException {
 597  0
         AttributeSet as = elem.getAttributes();
 598  0
         if (matchNameAttribute(as, HTML.Tag.COMMENT)) {
 599  0
             Object comment = as.getAttribute(HTML.Attribute.COMMENT);
 600  0
             if (comment instanceof String) {
 601  0
                 writeComment((String)comment);
 602   
             }
 603   
             else {
 604  0
                 writeComment(null);
 605   
             }
 606   
         }
 607   
     }
 608   
 
 609   
 
 610   
     /**
 611   
      * Writes out comment string.
 612   
      *
 613   
      * @param string   the comment
 614   
      * @exception IOException on any I/O error
 615   
      * @exception BadLocationException if pos represents an invalid
 616   
      *            location within the document.
 617   
      */
 618  0
     void writeComment(String string) throws IOException {
 619  0
         write("<!--");
 620  0
         if (string != null) {
 621  0
             write(string);
 622   
         }
 623  0
         write("-->");
 624  0
         writeLineSeparator();
 625   
     }
 626   
 
 627   
 
 628   
     /**
 629   
      * Writes out any additional comments (comments outside of the body)
 630   
      * stored under the property HTMLDocument.AdditionalComments.
 631   
      */
 632  28
     void writeAdditionalComments() throws IOException {
 633  28
         Object comments = getDocument().getProperty
 634   
                                         (HTMLDocument.AdditionalComments);
 635   
 
 636  28
         if (comments instanceof Vector) {
 637  0
             Vector v = (Vector)comments;
 638  0
             for (int counter = 0, maxCounter = v.size(); counter < maxCounter;
 639   
                  counter++) {
 640  0
                 writeComment(v.elementAt(counter).toString());
 641   
             }
 642   
         }
 643   
     }
 644   
 
 645   
 
 646   
     /**
 647   
      * Returns true if the element is a
 648   
      * synthesized element.  Currently we are only testing
 649   
      * for the p-implied tag.
 650   
      */
 651  901
     protected boolean synthesizedElement(Element elem) {
 652  901
         if (matchNameAttribute(elem.getAttributes(), HTML.Tag.IMPLIED)) {
 653  105
             return true;
 654   
         }
 655  796
         return false;
 656   
     }
 657   
 
 658   
 
 659   
     /**
 660   
      * Returns true if the StyleConstants.NameAttribute is
 661   
      * equal to the tag that is passed in as a parameter.
 662   
      */
 663  1847
     protected boolean matchNameAttribute(AttributeSet attr, HTML.Tag tag) {
 664  1847
         Object o = attr.getAttribute(StyleConstants.NameAttribute);
 665  1847
         if (o instanceof HTML.Tag) {
 666  1847
             HTML.Tag name = (HTML.Tag) o;
 667  1847
             if (name == tag) {
 668  185
                 return true;
 669   
             }
 670   
         }
 671  1662
         return false;
 672   
     }
 673   
 
 674   
     /**
 675   
      * Searches for embedded tags in the AttributeSet
 676   
      * and writes them out.  It also stores these tags in a vector
 677   
      * so that when appropriate the corresponding end tags can be
 678   
      * written out.
 679   
      *
 680   
      * @exception IOException on any I/O error
 681   
      */
 682  82
     protected void writeEmbeddedTags(AttributeSet attr) throws IOException {
 683   
 
 684   
         // translate css attributes to html
 685  82
         attr = convertToHTML(attr, oConvAttr);
 686   
 
 687  82
         Enumeration names = attr.getAttributeNames();
 688  82
         while (names.hasMoreElements()) {
 689  91
             Object name = names.nextElement();
 690  91
             if (name instanceof HTML.Tag) {
 691  4
                 HTML.Tag tag = (HTML.Tag)name;
 692  4
                 if (tag == HTML.Tag.FORM || tags.contains(tag)) {
 693  0
                     continue;
 694   
                 }
 695  4
                 write('<');
 696   
                 //System.out.println("FixedHTMLWriter writeEmbeddedTags tag=" + tag.toString());
 697  4
                 write(tag.toString());
 698  4
                 Object o = attr.getAttribute(tag);
 699  4
                 if (o != null && o instanceof AttributeSet) {
 700  4
                     writeAttributes((AttributeSet)o);
 701   
                 }
 702  4
                 write('>');
 703  4
                 tags.addElement(tag);
 704  4
                 tagValues.addElement(o);
 705   
             }
 706   
         }
 707   
     }
 708   
 
 709   
 
 710   
     /**
 711   
      * Searches the attribute set for a tag, both of which
 712   
      * are passed in as a parameter.  Returns true if no match is found
 713   
      * and false otherwise.
 714   
      */
 715  4
     private boolean noMatchForTagInAttributes(AttributeSet attr, HTML.Tag t,
 716   
                                               Object tagValue) {
 717  4
         if (attr != null && attr.isDefined(t)) {
 718  0
             Object newValue = attr.getAttribute(t);
 719   
 
 720  0
             if ((tagValue == null) ? (newValue == null) :
 721   
                 (newValue != null && tagValue.equals(newValue))) {
 722  0
                 return false;
 723   
             }
 724   
         }
 725  4
         return true;
 726   
     }
 727   
 
 728   
 
 729   
     /**
 730   
      * Searches the attribute set and for each tag
 731   
      * that is stored in the tag vector.  If the tag isnt found,
 732   
      * then the tag is removed from the vector and a corresponding
 733   
      * end tag is written out.
 734   
      *
 735   
      * @exception IOException on any I/O error
 736   
      */
 737  454
     protected void closeOutUnwantedEmbeddedTags(AttributeSet attr) throws IOException {
 738   
 
 739  454
         tagsToRemove.removeAllElements();
 740   
 
 741   
         // translate css attributes to html
 742  454
         attr = convertToHTML(attr, null);
 743   
 
 744  454
         HTML.Tag t;
 745  454
         Object tValue;
 746  454
         int firstIndex = -1;
 747  454
         int size = tags.size();
 748   
         // First, find all the tags that need to be removed.
 749  454
         for (int i = size - 1; i >= 0; i--) {
 750  4
             t = (HTML.Tag)tags.elementAt(i);
 751  4
             tValue = tagValues.elementAt(i);
 752  4
             if ((attr == null) || noMatchForTagInAttributes(attr, t, tValue)) {
 753  4
                 firstIndex = i;
 754  4
                 tagsToRemove.addElement(t);
 755   
             }
 756   
         }
 757  454
         if (firstIndex != -1) {
 758   
             // Then close them out.
 759  4
             boolean removeAll = ((size - firstIndex) == tagsToRemove.size());
 760  4
             for (int i = size - 1; i >= firstIndex; i--) {
 761  4
                 t = (HTML.Tag)tags.elementAt(i);
 762  4
                 if (removeAll || tagsToRemove.contains(t)) {
 763  4
                     tags.removeElementAt(i);
 764  4
                     tagValues.removeElementAt(i);
 765   
                 }
 766  4
                 write('<');
 767  4
                 write('/');
 768  4
                 write(t.toString());
 769  4
                 write('>');
 770   
             }
 771   
             // Have to output any tags after firstIndex that still remaing,
 772   
             // as we closed them out, but they should remain open.
 773  4
             size = tags.size();
 774  4
             for (int i = firstIndex; i < size; i++) {
 775  0
                 t = (HTML.Tag)tags.elementAt(i);
 776  0
                 write('<');
 777  0
                 write(t.toString());
 778  0
                 Object o = tagValues.elementAt(i);
 779  0
                 if (o != null && o instanceof AttributeSet) {
 780  0
                     writeAttributes((AttributeSet)o);
 781   
                 }
 782  0
                 write('>');
 783   
             }
 784   
         }
 785   
     }
 786   
 
 787   
 
 788   
     /**
 789   
      * Determines if the element associated with the attributeset
 790   
      * is a TEXTAREA or SELECT.  If true, returns true else
 791   
      * false
 792   
      */
 793  254
     private boolean isFormElementWithContent(AttributeSet attr) {
 794  254
         if (matchNameAttribute(attr, HTML.Tag.TEXTAREA) ||
 795   
             matchNameAttribute(attr, HTML.Tag.SELECT)) {
 796  0
             return true;
 797   
         }
 798  254
         return false;
 799   
     }
 800   
 
 801   
 
 802   
     /**
 803   
      * Determines whether a the indentation needs to be
 804   
      * incremented.  Basically, if next is a child of current, and
 805   
      * next is NOT a synthesized element, the indent level will be
 806   
      * incremented.  If there is a parent-child relationship and "next"
 807   
      * is a synthesized element, then its children must be indented.
 808   
      * This state is maintained by the indentNext boolean.
 809   
      *
 810   
      * @return boolean that's true if indent level
 811   
      *         needs incrementing.
 812   
      */
 813   
     private boolean indentNext = false;
 814  261
     private boolean indentNeedsIncrementing(Element current, Element next) {
 815  261
         if ((next.getParentElement() == current) && !inPre) {
 816  207
             if (indentNext) {
 817  33
                 indentNext = false;
 818  33
                 return true;
 819  174
             } else if (synthesizedElement(next)) {
 820  33
                 indentNext = true;
 821  141
             } else if (!synthesizedElement(current)){
 822  139
                 return true;
 823   
             }
 824   
         }
 825  89
         return false;
 826   
     }
 827   
 
 828   
     /**
 829   
      * Outputs the maps as elements. Maps are not stored as elements in
 830   
      * the document, and as such this is used to output them.
 831   
      */
 832  0
     void writeMaps(Enumeration maps) throws IOException {
 833   
       /*
 834   
         if (maps != null) {
 835   
             while(maps.hasMoreElements()) {
 836   
                 Map map = (Map)maps.nextElement();
 837   
                 String name = map.getName();
 838   
 
 839   
                 incrIndent();
 840   
                 indent();
 841   
                 write("<map");
 842   
                 if (name != null) {
 843   
                     write(" name=\"");
 844   
                     write(name);
 845   
                     write("\">");
 846   
                 }
 847   
                 else {
 848   
                     write('>');
 849   
                 }
 850   
                 writeLineSeparator();
 851   
                 incrIndent();
 852   
 
 853   
                 // Output the areas
 854   
                 AttributeSet[] areas = map.getAreas();
 855   
                 if (areas != null) {
 856   
                     for (int counter = 0, maxCounter = areas.length;
 857   
                          counter < maxCounter; counter++) {
 858   
                         indent();
 859   
                         write("<area");
 860   
                         writeAttributes(areas[counter]);
 861   
                         write("></area>");
 862   
                         writeLineSeparator();
 863   
                     }
 864   
                 }
 865   
                 decrIndent();
 866   
                 indent();
 867   
                 write("</map>");
 868   
                 writeLineSeparator();
 869   
                 decrIndent();
 870   
             }
 871   
         }
 872   
       */
 873   
     }
 874   
 
 875   
     /**
 876   
      * Outputs the styles as a single element. Styles are not stored as
 877   
      * elements, but part of the document. For the time being styles are
 878   
      * written out as a comment, inside a style tag.
 879   
      */
 880  14
     void writeStyles(StyleSheet sheet) throws IOException {
 881   
       //System.out.println("FixedHTMLWriter.writeStyles");
 882  14
         if (sheet != null) {
 883  14
             Enumeration styles = sheet.getStyleNames();
 884  14
             if (styles != null) {
 885  14
                 boolean outputStyle = false;
 886  14
                 while (styles.hasMoreElements()) {
 887  854
                     String name = (String)styles.nextElement();
 888   
                     // Don't write out the default style.
 889  854
                     if (!StyleContext.DEFAULT_STYLE.equals(name) &&
 890   
                         writeStyle(name, sheet.getStyle(name), outputStyle)) {
 891  0
                         outputStyle = true;
 892   
                     }
 893   
                 }
 894  14
                 if (outputStyle) {
 895  0
                     writeStyleEndTag();
 896   
                 }
 897   
             }
 898   
         }
 899   
     }
 900   
 
 901   
     /**
 902   
      * Outputs the named style. <code>outputStyle</code> indicates
 903   
      * whether or not a style has been output yet. This will return
 904   
      * true if a style is written.
 905   
      */
 906  840
     boolean writeStyle(String name, Style style, boolean outputStyle)
 907   
                  throws IOException{
 908  840
         boolean didOutputStyle = false;
 909  840
         Enumeration attributes = style.getAttributeNames();
 910  840
         if (attributes != null) {
 911  840
             while (attributes.hasMoreElements()) {
 912  1680
                 Object attribute = attributes.nextElement();
 913  1680
                 if (attribute instanceof CSS.Attribute) {
 914  0
                     String value = style.getAttribute(attribute).toString();
 915  0
                     if (value != null) {
 916  0
                         if (!outputStyle) {
 917  0
                             writeStyleStartTag();
 918  0
                             outputStyle = true;
 919   
                         }
 920  0
                         if (!didOutputStyle) {
 921  0
                             didOutputStyle = true;
 922  0
                             indent();
 923  0
                             write(name);
 924  0
                             write(" {");
 925   
                         }
 926   
                         else {
 927  0
                             write(";");
 928   
                         }
 929  0
                         write(' ');
 930  0
                         write(attribute.toString());
 931  0
                         write(": ");
 932  0
                         write(value);
 933   
                     }
 934   
                 }
 935   
             }
 936   
         }
 937  840
         if (didOutputStyle) {
 938  0
             write(" }");
 939  0
             writeLineSeparator();
 940   
         }
 941  840
         return didOutputStyle;
 942   
     }
 943   
 
 944  0
     void writeStyleStartTag() throws IOException {
 945   
       //System.out.println("FixedHTMLWriter.writeStyleStartTag");
 946  0
         indent();
 947  0
         write("<style type=\"text/css\">");
 948  0
         incrIndent();
 949  0
         writeLineSeparator();
 950  0
         indent();
 951  0
         write("<!--");
 952  0
         incrIndent();
 953  0
         writeLineSeparator();
 954   
     }
 955   
 
 956  0
     void writeStyleEndTag() throws IOException {
 957   
       //System.out.println("FixedHTMLWriter.writeStyleEndTag");
 958  0
         decrIndent();
 959  0
         indent();
 960  0
         write("-->");
 961  0
         writeLineSeparator();
 962  0
         decrIndent();
 963  0
         indent();
 964  0
         write("</style>");
 965  0
         writeLineSeparator();
 966  0
         indent();
 967   
     }
 968   
 
 969   
     // --- conversion support ---------------------------
 970   
 
 971   
     /**
 972   
      * Convert the give set of attributes to be html for
 973   
      * the purpose of writing them out.  Any keys that
 974   
      * have been converted will not appear in the resultant
 975   
      * set.  Any keys not converted will appear in the
 976   
      * resultant set the same as the received set.<p>
 977   
      * This will put the converted values into <code>to</code>, unless
 978   
      * it is null in which case a temporary AttributeSet will be returned.
 979   
      */
 980  536
     AttributeSet convertToHTML(AttributeSet from, MutableAttributeSet to) {
 981  536
         if (to == null) {
 982  454
             to = convAttr;
 983   
         }
 984  536
         to.removeAttributes(to);
 985  536
         if (writeCSS) {
 986  0
             convertToHTML40(from, to);
 987   
         } else {
 988  536
             convertToHTML32(from, to);
 989   
         }
 990  536
         return to;
 991   
     }
 992   
 
 993   
     /**
 994   
      * If true, the writer will emit CSS attributes in preference
 995   
      * to HTML tags/attributes (i.e. It will emit an HTML 4.0
 996   
      * style).
 997   
      */
 998   
     private boolean writeCSS = false;
 999   
 
 1000   
     /**
 1001   
      * Buffer for the purpose of attribute conversion
 1002   
      */
 1003   
     private MutableAttributeSet convAttr = new SimpleAttributeSet();
 1004   
 
 1005   
     /**
 1006   
      * Buffer for the purpose of attribute conversion. This can be
 1007   
      * used if convAttr is being used.
 1008   
      */
 1009   
     private MutableAttributeSet oConvAttr = new SimpleAttributeSet();
 1010   
 
 1011   
     /**
 1012   
      * Create an older style of HTML attributes.  This will
 1013   
      * convert character level attributes that have a StyleConstants
 1014   
      * mapping over to an HTML tag/attribute.  Other CSS attributes
 1015   
      * will be placed in an HTML style attribute.
 1016   
      */
 1017  716
     private static void convertToHTML32(AttributeSet from, MutableAttributeSet to) {
 1018  716
         if (from == null) {
 1019  28
             return;
 1020   
         }
 1021  688
         Enumeration keys = from.getAttributeNames();
 1022  688
         String value = "";
 1023  688
         while (keys.hasMoreElements()) {
 1024  2059
             Object key = keys.nextElement();
 1025  2059
             if (key instanceof CSS.Attribute) {
 1026  1199
                 if ((key == CSS.Attribute.FONT_FAMILY) ||
 1027   
                     (key == CSS.Attribute.FONT_SIZE) ||
 1028   
                     (key == CSS.Attribute.COLOR)) {
 1029  4
                     createFontAttribute((CSS.Attribute)key, from, to);
 1030  1195
                 } else if (key == CSS.Attribute.FONT_WEIGHT) {
 1031   
                     // add a bold tag is weight is bold
 1032   
                   /*
 1033   
                     CSS.FontWeight weightValue = (CSS.FontWeight)
 1034   
                         from.getAttribute(CSS.Attribute.FONT_WEIGHT);
 1035   
                     if ((weightValue != null) && (weightValue.getValue() > 400)) {
 1036   
                         to.addAttribute(HTML.Tag.B, SimpleAttributeSet.EMPTY);
 1037   
                     }
 1038   
                   */
 1039  0
                   Object weightValue =
 1040   
                               from.getAttribute(CSS.Attribute.FONT_WEIGHT);
 1041  0
                   if (weightValue != null) {
 1042  0
                     if(weightValue.toString().equalsIgnoreCase("bold")) {
 1043  0
                       to.addAttribute(HTML.Tag.B, SimpleAttributeSet.EMPTY);
 1044   
                     }
 1045   
                   }
 1046  1195
                 } else if (key == CSS.Attribute.FONT_STYLE) {
 1047  4
                     String s = from.getAttribute(key).toString();
 1048  4
                     if (s.indexOf("italic") >= 0) {
 1049  0
                         to.addAttribute(HTML.Tag.I, SimpleAttributeSet.EMPTY);
 1050   
                     }
 1051  1191
                 } else if (key == CSS.Attribute.TEXT_DECORATION) {
 1052  0
                     String decor = from.getAttribute(key).toString();
 1053  0
                     if (decor.indexOf("underline") >= 0) {
 1054  0
                         to.addAttribute(HTML.Tag.U, SimpleAttributeSet.EMPTY);
 1055   
                     }
 1056  0
                     if (decor.indexOf("line-through") >= 0) {
 1057  0
                         to.addAttribute(HTML.Tag.STRIKE, SimpleAttributeSet.EMPTY);
 1058   
                     }
 1059  1191
                 } else if (key == CSS.Attribute.VERTICAL_ALIGN) {
 1060  0
                     String vAlign = from.getAttribute(key).toString();
 1061  0
                     if (vAlign.indexOf("sup") >= 0) {
 1062  0
                         to.addAttribute(HTML.Tag.SUP, SimpleAttributeSet.EMPTY);
 1063   
                     }
 1064  0
                     if (vAlign.indexOf("sub") >= 0) {
 1065  0
                         to.addAttribute(HTML.Tag.SUB, SimpleAttributeSet.EMPTY);
 1066   
                     }
 1067  1191
                 } else if (key == CSS.Attribute.TEXT_ALIGN) {
 1068  3
                     to.addAttribute(HTML.Attribute.ALIGN,
 1069   
                                     from.getAttribute(key).toString());
 1070   
                 } else {
 1071   
                     // default is to store in a HTML style attribute
 1072  1188
                     if (value.length() > 0) {
 1073  957
                         value = value + "; ";
 1074   
                     }
 1075  1188
                     value = value + key + ": " + from.getAttribute(key);
 1076   
                 }
 1077   
             } else {
 1078  860
                 to.addAttribute(key, from.getAttribute(key));
 1079   
             }
 1080   
         }
 1081  688
         if (value.length() > 0) {
 1082  231
             to.addAttribute(HTML.Attribute.STYLE, value);
 1083   
         }
 1084   
     }
 1085   
 
 1086   
     /**
 1087   
      * Create/update an HTML &lt;font&gt; tag attribute.  The
 1088   
      * value of the attribute should be a MutableAttributeSet so
 1089   
      * that the attributes can be updated as they are discovered.
 1090   
      */
 1091  4
     private static void createFontAttribute(CSS.Attribute a, AttributeSet from,
 1092   
         MutableAttributeSet to) {
 1093  4
       MutableAttributeSet fontAttr = (MutableAttributeSet)
 1094   
                                      to.getAttribute(HTML.Tag.FONT);
 1095  4
       if (fontAttr == null) {
 1096  4
         fontAttr = new SimpleAttributeSet();
 1097   
       }
 1098   
       // edit the parameters to the font tag
 1099  4
       String htmlValue = from.getAttribute(a).toString();
 1100  4
       if (a == CSS.Attribute.FONT_FAMILY) {
 1101  0
         fontAttr.addAttribute(HTML.Attribute.FACE, htmlValue);
 1102   
       }
 1103  4
       else if (a == CSS.Attribute.FONT_SIZE) {
 1104  4
         if(htmlValue.startsWith("+") || htmlValue.startsWith("-")) {
 1105  0
           fontAttr.addAttribute(HTML.Attribute.SIZE, htmlValue);
 1106   
         }
 1107   
         else {
 1108  4
           fontAttr.addAttribute(HTML.Attribute.SIZE, Integer.toString(Util.getRelativeSize(htmlValue)));
 1109   
         }
 1110   
       }
 1111  0
       else if (a == CSS.Attribute.COLOR) {
 1112  0
         fontAttr.addAttribute(HTML.Attribute.COLOR, htmlValue);
 1113   
       }
 1114   
       //System.out.println("FixedHTMLwriter createFontAttribute \r\n");
 1115   
       //de.calcom.cclib.html.HTMLDiag hd = new de.calcom.cclib.html.HTMLDiag();
 1116   
       //hd.listAttributes(fontAttr, 4);
 1117  4
       to.addAttribute(HTML.Tag.FONT, fontAttr);
 1118   
     }
 1119   
 
 1120   
     /**
 1121   
      * Copies the given AttributeSet to a new set, converting
 1122   
      * any CSS attributes found to arguments of an HTML style
 1123   
      * attribute.
 1124   
      */
 1125  0
     private static void convertToHTML40(AttributeSet from, MutableAttributeSet to) {
 1126  0
         Enumeration keys = from.getAttributeNames();
 1127  0
         String value = "";
 1128  0
         while (keys.hasMoreElements()) {
 1129  0
             Object key = keys.nextElement();
 1130  0
             if (key instanceof CSS.Attribute) {
 1131  0
                 value = value + " " + key + "=" + from.getAttribute(key) + ";";
 1132   
             } else {
 1133  0
                 to.addAttribute(key, from.getAttribute(key));
 1134   
             }
 1135   
         }
 1136  0
         if (value.length() > 0) {
 1137  0
             to.addAttribute(HTML.Attribute.STYLE, value);
 1138   
         }
 1139   
     }
 1140   
 
 1141   
     //
 1142   
     // Overrides the writing methods to only break a string when
 1143   
     // canBreakString is true.
 1144   
     // In a future release it is likely AbstractWriter will get this
 1145   
     // functionality.
 1146   
     //
 1147   
 
 1148   
     /**
 1149   
      * Writes the line separator. This is overriden to make sure we don't
 1150   
      * replace the newline content in case it is outside normal ascii.
 1151   
      */
 1152  469
     protected void writeLineSeparator() throws IOException {
 1153  469
         boolean oldReplace = replaceEntities;
 1154  469
         replaceEntities = false;
 1155  469
         super.writeLineSeparator();
 1156  469
         replaceEntities = oldReplace;
 1157   
     }
 1158   
 
 1159   
     /**
 1160   
      * This method is overriden to map any character entities, such as
 1161   
      * &lt; to &amp;lt;. <code>super.output</code> will be invoked to
 1162   
      * write the content.
 1163   
      */
 1164  2347
     protected void output(char[] chars, int start, int length)
 1165   
                    throws IOException {
 1166  2347
         if (!replaceEntities) {
 1167  2325
             super.output(chars, start, length);
 1168  2325
             return;
 1169   
         }
 1170  22
         int last = start;
 1171  22
         length += start;
 1172  22
         for (int counter = start; counter < length; counter++) {
 1173   
             // This will change, we need better support character level
 1174   
             // entities.
 1175  112
             switch(chars[counter]) {
 1176   
                 // Character level entities.
 1177  0
             case '<':
 1178  0
                 if (counter > last) {
 1179  0
                     super.output(chars, last, counter - last);
 1180   
                 }
 1181  0
                 last = counter + 1;
 1182  0
                 output("&lt;");
 1183  0
                 break;
 1184  0
             case '>':
 1185  0
                 if (counter > last) {
 1186  0
                     super.output(chars, last, counter - last);
 1187   
                 }
 1188  0
                 last = counter + 1;
 1189  0
                 output("&gt;");
 1190  0
                 break;
 1191  0
             case '&':
 1192  0
                 if (counter > last) {
 1193  0
                     super.output(chars, last, counter - last);
 1194   
                 }
 1195  0
                 last = counter + 1;
 1196  0
                 output("&amp;");
 1197  0
                 break;
 1198  0
             case '"':
 1199  0
                 if (counter > last) {
 1200  0
                     super.output(chars, last, counter - last);
 1201   
                 }
 1202  0
                 last = counter + 1;
 1203  0
                 output("&quot;");
 1204  0
                 break;
 1205   
                 // Special characters
 1206  0
             case '\n':
 1207  0
             case '\t':
 1208  0
             case '\r':
 1209  0
                 break;
 1210  112
             default:
 1211  112
                 if (chars[counter] < ' ' || chars[counter] > 127) {
 1212  0
                     if (counter > last) {
 1213  0
                         super.output(chars, last, counter - last);
 1214   
                     }
 1215  0
                     last = counter + 1;
 1216   
                     // If the character is outside of ascii, write the
 1217   
                     // numeric value.
 1218  0
                     output("&#");
 1219  0
                     output(String.valueOf((int)chars[counter]));
 1220  0
                     output(";");
 1221   
                 }
 1222  112
                 break;
 1223   
             }
 1224   
         }
 1225  22
         if (last < length) {
 1226  22
             super.output(chars, last, length - last);
 1227   
         }
 1228   
     }
 1229   
 
 1230   
     /**
 1231   
      * This directly invokes super's <code>output</code> after converting
 1232   
      * <code>string</code> to a char[].
 1233   
      */
 1234  0
     private void output(String string) throws IOException {
 1235   
       //System.out.println("FixedHTMLWriter.output string=" + string);
 1236  0
         int length = string.length();
 1237  0
         if (tempChars == null || tempChars.length < length) {
 1238  0
             tempChars = new char[length];
 1239   
         }
 1240  0
         string.getChars(0, length, tempChars, 0);
 1241  0
         super.output(tempChars, 0, length);
 1242   
     }
 1243   
 }
 1244