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