OSDN Git Service

libjava/ChangeLog:
[pf3gnuchains/gcc-fork.git] / libjava / classpath / javax / swing / text / html / HTMLWriter.java
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