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 embedded has been written out.
083 */
084 private HashSet<HTML.Tag> openEmbeddedTagHashSet = 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<Element> 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 openEmbeddedTagHashSet = new HashSet<HTML.Tag>();
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 openEmbeddedTagHashSet = new HashSet<HTML.Tag>();
133
134 doc_pos = pos;
135 doc_offset_remaining = pos;
136 doc_len = len;
137 doc_len_remaining = len;
138 htmlFragmentParentHashSet = new HashSet<Element>();
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 HTML.Tag[] tag_arr =
201 openEmbeddedTagHashSet.toArray(new HTML.Tag[openEmbeddedTagHashSet.size()]);
202
203 for (int i = 0; i < tag_arr.length; i++)
204 {
205 writeRaw("</" + tag_arr[i].toString() + ">");
206 } // for(int i = 0; i < tag_arr.length; i++)
207
208 } // public void write() throws IOException, BadLocationException
209
210 /**
211 * Writes all the attributes in the attrSet, except for attrbutes with
212 * keys of <code>javax.swing.text.html.HTML.Tag</code>,
213 * <code>javax.swing.text.StyleConstants</code> or
214 * <code>javax.swing.text.html.HTML.Attribute.ENDTAG</code>.
215 *
216 * @param attrSet attrSet to write out
217 *
218 * @throws IOException on any I/O exceptions
219 */
220 protected void writeAttributes(AttributeSet attrSet)
221 throws IOException
222 {
223 Enumeration<?> attrNameEnum = attrSet.getAttributeNames();
224
225 while (attrNameEnum.hasMoreElements())
226 {
227 Object key = attrNameEnum.nextElement();
228 Object value = attrSet.getAttribute(key);
229
230 // HTML.Attribute.ENDTAG is an instance, not a class.
231 if (!((key instanceof HTML.Tag) || (key instanceof StyleConstants)
232 || (key == HTML.Attribute.ENDTAG)))
233 {
234 if (key == HTML.Attribute.SELECTED)
235 writeRaw(" selected");
236 else if (key == HTML.Attribute.CHECKED)
237 writeRaw(" checked");
238 else
239 writeRaw(" " + key + "=\"" + value + "\"");
240 } // if(!((key instanceof HTML.Tag) || (key instanceof
241 // StyleConstants) || (key == HTML.Attribute.ENDTAG)))
242 } // while(attrNameEnum.hasMoreElements())
243
244 } // protected void writeAttributes(AttributeSet attrSet) throws IOException
245
246 /**
247 * Writes out an empty tag. i.e. a tag without any child elements.
248 *
249 * @param paramElem the element to output as an empty tag
250 *
251 * @throws IOException on any I/O exceptions
252 * @throws BadLocationException if a pos is not a valid position in the
253 * html doc element
254 */
255 protected void emptyTag(Element paramElem)
256 throws IOException, BadLocationException
257 {
258 String elem_name = paramElem.getName();
259 AttributeSet attrSet = paramElem.getAttributes();
260
261 writeRaw("<" + elem_name);
262 writeAttributes(attrSet);
263 writeRaw(">");
264
265 if (isBlockTag(attrSet))
266 {
267 writeRaw("</" + elem_name + ">");
268 } // if(isBlockTag(attrSet))
269
270 } // protected void emptyTag(Element paramElem)
271 // throws IOException, BadLocationException
272
273 /**
274 * Determines if it is a block tag or not.
275 *
276 * @param attrSet the attrSet of the element
277 *
278 * @return <code>true</code> if it is a block tag
279 * <code>false</code> if it is a not block tag
280 */
281 protected boolean isBlockTag(AttributeSet attrSet)
282 {
283 return ((HTML.Tag)
284 attrSet.getAttribute(StyleConstants.NameAttribute)).isBlock();
285 } // protected boolean isBlockTag(AttributeSet attrSet)
286
287 /**
288 * Writes out a start tag. Synthesized elements are skipped.
289 *
290 * @param paramElem the element to output as a start tag
291 * @throws IOException on any I/O exceptions
292 * @throws BadLocationException if a pos is not a valid position in the
293 * html doc element
294 */
295 protected void startTag(Element paramElem)
296 throws IOException, BadLocationException
297 {
298 // NOTE: Sysnthesized elements do no call this method at all.
299 String elem_name = paramElem.getName();
300 AttributeSet attrSet = paramElem.getAttributes();
301
302 indent();
303 writeRaw("<" + elem_name);
304 writeAttributes(attrSet);
305 writeRaw(">");
306 writeLineSeparator(); // Extra formatting to look more like the RI.
307 incrIndent();
308
309 } // protected void startTag(Element paramElem)
310 // throws IOException, BadLocationException
311
312 /**
313 * Writes out the contents of a textarea.
314 *
315 * @param attrSet the attrSet of the element to output as a text area
316 * @throws IOException on any I/O exceptions
317 * @throws BadLocationException if a pos is not a valid position in the
318 * html doc element
319 */
320 protected void textAreaContent(AttributeSet attrSet)
321 throws IOException, BadLocationException
322 {
323 writeLineSeparator(); // Extra formatting to look more like the RI.
324 indent();
325 writeRaw("<textarea");
326 writeAttributes(attrSet);
327 writeRaw(">");
328
329 Document tempDocument =
330 (Document) attrSet.getAttribute(StyleConstants.ModelAttribute);
331
332 writeRaw(tempDocument.getText(0, tempDocument.getLength()));
333 indent();
334 writeRaw("</textarea>");
335
336 } // protected void textAreaContent(AttributeSet attrSet)
337 // throws IOException, BadLocationException
338
339 /**
340 * Writes out text, within the appropriate range if it is specified.
341 *
342 * @param paramElem the element to output as a text
343 * @throws IOException on any I/O exceptions
344 * @throws BadLocationException if a pos is not a valid position in the
345 * html doc element
346 */
347 protected void text(Element paramElem)
348 throws IOException, BadLocationException
349 {
350 int offset = paramElem.getStartOffset();
351 int len = paramElem.getEndOffset() - paramElem.getStartOffset();
352 String txt_value = htmlDoc.getText(offset, len);
353
354 writeContent(txt_value);
355
356 } // protected void text(Element paramElem)
357 // throws IOException, BadLocationException
358
359 /**
360 * Writes out the contents of a select element.
361 *
362 * @param attrSet the attrSet of the element to output as a select box
363 *
364 * @throws IOException on any I/O exceptions
365 */
366 protected void selectContent(AttributeSet attrSet)
367 throws IOException
368 {
369 writeLineSeparator(); // Extra formatting to look more like the RI.
370 indent();
371 writeRaw("<select");
372 writeAttributes(attrSet);
373 writeRaw(">");
374 incrIndent();
375 writeLineSeparator(); // extra formatting to look more like the RI.
376
377 ComboBoxModel comboBoxModel =
378 (ComboBoxModel) attrSet.getAttribute(StyleConstants.ModelAttribute);
379
380 for (int i = 0; i < comboBoxModel.getSize(); i++)
381 {
382 writeOption((Option) comboBoxModel.getElementAt(i));
383 } // for(int i = 0; i < comboBoxModel.getSize(); i++)
384
385 decrIndent();
386 indent();
387 writeRaw("</select>");
388
389 } // protected void selectContent(AttributeSet attrSet) throws IOException
390
391 /**
392 * Writes out the contents of an option element.
393 *
394 * @param option the option object to output as a select option
395 *
396 * @throws IOException on any I/O exceptions
397 */
398 protected void writeOption(Option option)
399 throws IOException
400 {
401 indent();
402 writeRaw("<option");
403 writeAttributes(option.getAttributes());
404 writeRaw(">");
405
406 writeContent(option.getLabel());
407
408 writeRaw("</option>");
409 writeLineSeparator(); // extra formatting to look more like the RI.
410
411 } // protected void writeOption(Option option) throws IOException
412
413 /**
414 * Writes out an end tag.
415 *
416 * @param paramElem the element to output as an end tag
417 *
418 * @throws IOException on any I/O exceptions
419 */
420 protected void endTag(Element paramElem)
421 throws IOException
422 {
423 String elem_name = paramElem.getName();
424
425 //writeLineSeparator(); // Extra formatting to look more like the RI.
426 decrIndent();
427 indent();
428 writeRaw("</" + elem_name + ">");
429 writeLineSeparator(); // Extra formatting to look more like the RI.
430
431 } // protected void endTag(Element paramElem) throws IOException
432
433 /**
434 * Writes out the comment.
435 *
436 * @param paramElem the element to output as a comment
437 */
438 protected void comment(Element paramElem)
439 throws IOException, BadLocationException
440 {
441 AttributeSet attrSet = paramElem.getAttributes();
442
443 String comment_str = (String) attrSet.getAttribute(HTML.Attribute.COMMENT);
444
445 writeRaw("<!--" + comment_str + "-->");
446
447 } // protected void comment(Element paramElem)
448 // throws IOException, BadLocationException
449
450 /**
451 * Determines if element is a synthesized
452 * <code>javax.swing.text.Element</code> or not.
453 *
454 * @param element the element to test
455 *
456 * @return <code>true</code> if it is a synthesized element,
457 * <code>false</code> if it is a not synthesized element
458 */
459 protected boolean synthesizedElement(Element element)
460 {
461 AttributeSet attrSet = element.getAttributes();
462 Object tagType = attrSet.getAttribute(StyleConstants.NameAttribute);
463
464 if (tagType == HTML.Tag.CONTENT || tagType == HTML.Tag.COMMENT
465 || tagType == HTML.Tag.IMPLIED)
466 return true;
467 else
468 return false;
469 } // protected boolean synthesizedElement(Element element)
470
471 /**
472 * Determines if
473 * <code>javax.swing.text.StyleConstants.NameAttribute</code>
474 * matches tag or not.
475 *
476 * @param attrSet the <code>javax.swing.text.AttributeSet</code> of
477 * element to be matched
478 * @param tag the HTML.Tag to match
479 *
480 * @return <code>true</code> if it matches,
481 * <code>false</code> if it does not match
482 */
483 protected boolean matchNameAttribute(AttributeSet attrSet, HTML.Tag tag)
484 {
485 Object tagType = attrSet.getAttribute(StyleConstants.NameAttribute);
486
487 if (tagType == tag)
488 return true;
489 else
490 return false;
491 } // protected boolean matchNameAttribute(AttributeSet attrSet,
492 // HTML.Tag tag)
493
494 /**
495 * Writes out an embedded tag. The tags not already in
496 * openEmbededTagHashSet will written out.
497 *
498 * @param attrSet the <code>javax.swing.text.AttributeSet</code> of
499 * the element to write out
500 *
501 * @throws IOException on any I/O exceptions
502 */
503 protected void writeEmbeddedTags(AttributeSet attrSet)
504 throws IOException
505 {
506 Enumeration<?> attrNameEnum = attrSet.getAttributeNames();
507
508 while (attrNameEnum.hasMoreElements())
509 {
510 Object key = attrNameEnum.nextElement();
511 Object value = attrSet.getAttribute(key);
512
513 if (key instanceof HTML.Tag)
514 {
515 if (!openEmbeddedTagHashSet.contains(key))
516 {
517 writeRaw("<" + key);
518 writeAttributes((AttributeSet) value);
519 writeRaw(">");
520 openEmbeddedTagHashSet.add((HTML.Tag) key);
521 } // if(!openEmbededTagHashSet.contains(key))
522 } // if(key instanceof HTML.Tag)
523 } // while(attrNameEnum.hasMoreElements())
524
525 } // protected void writeEmbeddedTags(AttributeSet attrSet)
526 // throws IOException
527
528 /**
529 * Closes out an unwanted embedded tag. The tags from the
530 * openEmbededTagHashSet not found in attrSet will be written out.
531 *
532 * @param attrSet the AttributeSet of the element to write out
533 *
534 * @throws IOException on any I/O exceptions
535 */
536 protected void closeOutUnwantedEmbeddedTags(AttributeSet attrSet)
537 throws IOException
538 {
539 HTML.Tag[] tag_arr =
540 openEmbeddedTagHashSet.toArray(new HTML.Tag[openEmbeddedTagHashSet.size()]);
541
542 for (int i = 0; i < tag_arr.length; i++)
543 {
544 HTML.Tag key = tag_arr[i];
545
546 if (!attrSet.isDefined(key))
547 {
548 writeRaw("</" + key.toString() + ">");
549 openEmbeddedTagHashSet.remove(key);
550 } // if(!attrSet.isDefined(key))
551 } // for(int i = 0; i < tag_arr.length; i++)
552
553 } // protected void closeOutUnwantedEmbeddedTags(AttributeSet attrSet)
554 // throws IOException
555
556 /**
557 * Writes out a line separator. Overwrites the parent to write out a new
558 * line.
559 *
560 * @throws IOException on any I/O exceptions.
561 */
562 protected void writeLineSeparator()
563 throws IOException
564 {
565 writeRaw(new_line_str);
566 } // protected void writeLineSeparator() throws IOException
567
568 /**
569 * Write to the writer. Character entites such as <, >
570 * are escaped appropriately.
571 *
572 * @param chars char array to write out
573 * @param off offset
574 * @param len length
575 *
576 * @throws IOException on any I/O exceptions
577 */
578 protected void output(char[] chars, int off, int len)
579 throws IOException
580 {
581 CPStringBuilder strBuffer = new CPStringBuilder();
582
583 for (int i = 0; i < chars.length; i++)
584 {
585 if (isCharHtmlEntity(chars[i]))
586 strBuffer.append(escapeCharHtmlEntity(chars[i]));
587 else
588 strBuffer.append(chars[i]);
589 } // for(int i = 0; i < chars.length; i++)
590
591 writeRaw(strBuffer.toString());
592
593 } // protected void output(char[] chars, int off, int len)
594 // throws IOException
595
596 //-------------------------------------------------------------------------
597 // private methods
598
599 /**
600 * The main method used to traverse through the elements.
601 *
602 * @param paramElem element to traverse
603 *
604 * @throws IOException on any I/O exceptions
605 */
606 private void traverse(Element paramElem)
607 throws IOException, BadLocationException
608 {
609 Element currElem = paramElem;
610
611 AttributeSet attrSet = currElem.getAttributes();
612
613 closeOutUnwantedEmbeddedTags(attrSet);
614
615 // handle the tag
616 if (synthesizedElement(paramElem))
617 {
618 if (matchNameAttribute(attrSet, HTML.Tag.CONTENT))
619 {
620 writeEmbeddedTags(attrSet);
621 text(currElem);
622 } // if(matchNameAttribute(attrSet, HTML.Tag.CONTENT))
623 else if (matchNameAttribute(attrSet, HTML.Tag.COMMENT))
624 {
625 comment(currElem);
626 } // else if(matchNameAttribute(attrSet, HTML.Tag.COMMENT))
627 else if (matchNameAttribute(attrSet, HTML.Tag.IMPLIED))
628 {
629 int child_elem_count = currElem.getElementCount();
630
631 if (child_elem_count > 0)
632 {
633 for (int i = 0; i < child_elem_count; i++)
634 {
635 Element childElem = paramElem.getElement(i);
636
637 traverse(childElem);
638
639 } // for(int i = 0; i < child_elem_count; i++)
640 } // if(child_elem_count > 0)
641 } // else if(matchNameAttribute(attrSet, HTML.Tag.IMPLIED))
642 } // if(synthesizedElement(paramElem))
643 else
644 {
645 // NOTE: 20061030 - fchoong - title is treated specially here.
646 // based on RI behavior.
647 if (matchNameAttribute(attrSet, HTML.Tag.TITLE))
648 {
649 boolean fg_is_end_tag = false;
650 Enumeration<?> attrNameEnum = attrSet.getAttributeNames();
651
652 while (attrNameEnum.hasMoreElements())
653 {
654 Object key = attrNameEnum.nextElement();
655 Object value = attrSet.getAttribute(key);
656
657 if (key == HTML.Attribute.ENDTAG && value.equals("true"))
658 fg_is_end_tag = true;
659 } // while(attrNameEnum.hasMoreElements())
660
661 if (fg_is_end_tag)
662 writeRaw("</title>");
663 else
664 {
665 indent();
666 writeRaw("<title>");
667
668 String title_str =
669 (String) htmlDoc.getProperty(HTMLDocument.TitleProperty);
670
671 if (title_str != null)
672 writeContent(title_str);
673
674 } // else
675 } // if(matchNameAttribute(attrSet, HTML.Tag.TITLE))
676 else if (matchNameAttribute(attrSet, HTML.Tag.PRE))
677 {
678 // We pursue more stringent formating here.
679 attrSet = paramElem.getAttributes();
680
681 indent();
682 writeRaw("<pre");
683 writeAttributes(attrSet);
684 writeRaw(">");
685
686 int child_elem_count = currElem.getElementCount();
687
688 for (int i = 0; i < child_elem_count; i++)
689 {
690 Element childElem = paramElem.getElement(i);
691
692 traverse(childElem);
693
694 } // for(int i = 0; i < child_elem_count; i++)
695
696 writeRaw("</pre>");
697
698 } // else if(matchNameAttribute(attrSet, HTML.Tag.PRE))
699 else if (matchNameAttribute(attrSet, HTML.Tag.SELECT))
700 {
701 selectContent(attrSet);
702 } // else if(matchNameAttribute(attrSet, HTML.Tag.SELECT))
703 else if (matchNameAttribute(attrSet, HTML.Tag.TEXTAREA))
704 {
705 textAreaContent(attrSet);
706 } // else if(matchNameAttribute(attrSet, HTML.Tag.TEXTAREA))
707 else
708 {
709 int child_elem_count = currElem.getElementCount();
710
711 if (child_elem_count > 0)
712 {
713 startTag(currElem);
714
715 for (int i = 0; i < child_elem_count; i++)
716 {
717 Element childElem = paramElem.getElement(i);
718
719 traverse(childElem);
720
721 } // for(int i = 0; i < child_elem_count; i++)
722
723 endTag(currElem);
724
725 } // if(child_elem_count > 0)
726 else
727 {
728 emptyTag(currElem);
729 } // else
730 } // else
731 } // else
732
733 } // private void traverse(Element paramElem)
734 // throws IOException, BadLocationException
735
736 /**
737 * The method used to traverse through a html fragment.
738 *
739 * @param paramElem element to traverse
740 *
741 * @throws IOException on any I/O exceptions
742 */
743 private void traverseHtmlFragment(Element paramElem)
744 throws IOException, BadLocationException
745 {
746 // NOTE: This method is similar to traverse(Element paramElem)
747 Element currElem = paramElem;
748
749 boolean fg_is_fragment_parent_elem = false;
750 boolean fg_is_start_and_end_elem = false;
751
752 if (htmlFragmentParentHashSet.contains(paramElem))
753 fg_is_fragment_parent_elem = true;
754
755 if (paramElem == startElem)
756 fg_pass_start_elem = true;
757
758 if (paramElem == startElem && paramElem == endElem)
759 fg_is_start_and_end_elem = true;
760
761 AttributeSet attrSet = currElem.getAttributes();
762
763 closeOutUnwantedEmbeddedTags(attrSet);
764
765 if (fg_is_fragment_parent_elem || (fg_pass_start_elem
766 && fg_pass_end_elem == false) || fg_is_start_and_end_elem)
767 {
768 // handle the tag
769 if (synthesizedElement(paramElem))
770 {
771 if (matchNameAttribute(attrSet, HTML.Tag.CONTENT))
772 {
773 writeEmbeddedTags(attrSet);
774
775 int content_offset = paramElem.getStartOffset();
776 int content_length = currElem.getEndOffset() - content_offset;
777
778 if (doc_offset_remaining > 0)
779 {
780 if (content_length > doc_offset_remaining)
781 {
782 int split_len = content_length;
783
784 split_len = split_len - doc_offset_remaining;
785
786 if (split_len > doc_len_remaining)
787 split_len = doc_len_remaining;
788
789 // we need to split it.
790 String txt_value = htmlDoc.getText(content_offset
791 + doc_offset_remaining, split_len);
792
793 writeContent(txt_value);
794
795 doc_offset_remaining = 0; // the offset is used up.
796 doc_len_remaining = doc_len_remaining - split_len;
797 } // if(content_length > doc_offset_remaining)
798 else
799 {
800 // doc_offset_remaining is greater than the entire
801 // length of content
802 doc_offset_remaining = doc_offset_remaining
803 - content_length;
804 } // else
805 } // if(doc_offset_remaining > 0)
806 else if (content_length <= doc_len_remaining)
807 {
808 // we can fit the entire content.
809 text(currElem);
810 doc_len_remaining = doc_len_remaining - content_length;
811 } // else if(content_length <= doc_len_remaining)
812 else
813 {
814 // we need to split it.
815 String txt_value = htmlDoc.getText(content_offset,
816 doc_len_remaining);
817
818 writeContent(txt_value);
819
820 doc_len_remaining = 0;
821 } // else
822
823 } // if(matchNameAttribute(attrSet, HTML.Tag.CONTENT))
824 else if (matchNameAttribute(attrSet, HTML.Tag.COMMENT))
825 {
826 comment(currElem);
827 } // else if(matchNameAttribute(attrSet, HTML.Tag.COMMENT))
828 else if (matchNameAttribute(attrSet, HTML.Tag.IMPLIED))
829 {
830 int child_elem_count = currElem.getElementCount();
831
832 if (child_elem_count > 0)
833 {
834 for (int i = 0; i < child_elem_count; i++)
835 {
836 Element childElem = paramElem.getElement(i);
837
838 traverseHtmlFragment(childElem);
839
840 } // for(int i = 0; i < child_elem_count; i++)
841 } // if(child_elem_count > 0)
842 } // else if(matchNameAttribute(attrSet, HTML.Tag.IMPLIED))
843 } // if(synthesizedElement(paramElem))
844 else
845 {
846 // NOTE: 20061030 - fchoong - the isLeaf() condition seems to
847 // generate the closest behavior to the RI.
848 if (paramElem.isLeaf())
849 {
850 if (doc_offset_remaining > 0)
851 {
852 doc_offset_remaining--;
853 } // if(doc_offset_remaining > 0)
854 else if (doc_len_remaining > 0)
855 {
856 doc_len_remaining--;
857 } // else if(doc_len_remaining > 0)
858 } // if(paramElem.isLeaf())
859
860 // NOTE: 20061030 - fchoong - title is treated specially here.
861 // based on RI behavior.
862 if (matchNameAttribute(attrSet, HTML.Tag.TITLE))
863 {
864 boolean fg_is_end_tag = false;
865 Enumeration<?> attrNameEnum = attrSet.getAttributeNames();
866
867 while (attrNameEnum.hasMoreElements())
868 {
869 Object key = attrNameEnum.nextElement();
870 Object value = attrSet.getAttribute(key);
871
872 if (key == HTML.Attribute.ENDTAG && value.equals("true"))
873 fg_is_end_tag = true;
874 } // while(attrNameEnum.hasMoreElements())
875
876 if (fg_is_end_tag)
877 writeRaw("</title>");
878 else
879 {
880 indent();
881 writeRaw("<title>");
882
883 String title_str =
884 (String) htmlDoc.getProperty(HTMLDocument.TitleProperty);
885
886 if (title_str != null)
887 writeContent(title_str);
888
889 } // else
890 } // if(matchNameAttribute(attrSet, HTML.Tag.TITLE))
891 else if (matchNameAttribute(attrSet, HTML.Tag.PRE))
892 {
893 // We pursue more stringent formating here.
894 attrSet = paramElem.getAttributes();
895
896 indent();
897 writeRaw("<pre");
898 writeAttributes(attrSet);
899 writeRaw(">");
900
901 int child_elem_count = currElem.getElementCount();
902
903 for (int i = 0; i < child_elem_count; i++)
904 {
905 Element childElem = paramElem.getElement(i);
906
907 traverseHtmlFragment(childElem);
908
909 } // for(int i = 0; i < child_elem_count; i++)
910
911 writeRaw("</pre>");
912
913 } // else if(matchNameAttribute(attrSet, HTML.Tag.PRE))
914 else if (matchNameAttribute(attrSet, HTML.Tag.SELECT))
915 {
916 selectContent(attrSet);
917 } // else if(matchNameAttribute(attrSet, HTML.Tag.SELECT))
918 else if (matchNameAttribute(attrSet, HTML.Tag.TEXTAREA))
919 {
920 textAreaContent(attrSet);
921 } // else if(matchNameAttribute(attrSet, HTML.Tag.TEXTAREA))
922 else
923 {
924 int child_elem_count = currElem.getElementCount();
925
926 if (child_elem_count > 0)
927 {
928 startTag(currElem);
929
930 for (int i = 0; i < child_elem_count; i++)
931 {
932 Element childElem = paramElem.getElement(i);
933
934 traverseHtmlFragment(childElem);
935
936 } // for(int i = 0; i < child_elem_count; i++)
937
938 endTag(currElem);
939
940 } // if(child_elem_count > 0)
941 else
942 {
943 emptyTag(currElem);
944 } // else
945 } // else
946 } // else
947
948 } // if(fg_is_fragment_parent_elem || (fg_pass_start_elem
949 // && fg_pass_end_elem == false) || fg_is_start_and_end_elem)
950
951 if (paramElem == endElem)
952 fg_pass_end_elem = true;
953
954 } // private void traverseHtmlFragment(Element paramElem)
955 // throws IOException, BadLocationException
956
957 /**
958 * Write to the writer without any modifications.
959 *
960 * @param param_str the str to write out
961 *
962 * @throws IOException on any I/O exceptions
963 */
964 private void writeRaw(String param_str)
965 throws IOException
966 {
967 super.output(param_str.toCharArray(), 0, param_str.length());
968 } // private void writeRaw(char[] chars, int off, int len)
969 // throws IOException
970
971 /**
972 * Write to the writer, escaping HTML character entitie where neccessary.
973 *
974 * @param param_str the str to write out
975 *
976 * @throws IOException on any I/O exceptions
977 */
978 private void writeContent(String param_str)
979 throws IOException
980 {
981 char[] str_char_arr = param_str.toCharArray();
982
983 if (hasHtmlEntity(param_str))
984 output(str_char_arr, 0, str_char_arr.length);
985 else
986 super.output(str_char_arr, 0, str_char_arr.length);
987
988 } // private void writeContent(String param_str) throws IOException
989
990 /**
991 * Use this for debugging. Writes out all attributes regardless of type.
992 *
993 * @param attrSet the <code>javax.swing.text.AttributeSet</code> to
994 * write out
995 *
996 * @throws IOException on any I/O exceptions
997 */
998 private void writeAllAttributes(AttributeSet attrSet)
999 throws IOException
1000 {
1001 Enumeration<?> attrNameEnum = attrSet.getAttributeNames();
1002
1003 while (attrNameEnum.hasMoreElements())
1004 {
1005 Object key = attrNameEnum.nextElement();
1006 Object value = attrSet.getAttribute(key);
1007
1008 writeRaw(" " + key + "=\"" + value + "\"");
1009 writeRaw(" " + key.getClass().toString() + "=\""
1010 + value.getClass().toString() + "\"");
1011 } // while(attrNameEnum.hasMoreElements())
1012
1013 } // private void writeAllAttributes(AttributeSet attrSet)
1014 // throws IOException
1015
1016 /**
1017 * Tests if the str contains any html entities.
1018 *
1019 * @param param_str the str to test
1020 *
1021 * @return <code>true</code> if it has a html entity
1022 * <code>false</code> if it does not have a html entity
1023 */
1024 private boolean hasHtmlEntity(String param_str)
1025 {
1026 boolean ret_bool = false;
1027
1028 for (int i = 0; i < html_entity_char_arr.length; i++)
1029 {
1030 if (param_str.indexOf(html_entity_char_arr[i]) != -1)
1031 {
1032 ret_bool = true;
1033 break;
1034 } // if(param_str.indexOf(html_entity_char_arr[i]) != -1)
1035 } // for(int i = 0; i < html_entity_char_arr.length; i++)
1036
1037 return ret_bool;
1038 } // private boolean hasHtmlEntity(String param_str)
1039
1040 /**
1041 * Tests if the char is a html entities.
1042 *
1043 * @param param_char the char to test
1044 *
1045 * @return <code>true</code> if it is a html entity
1046 * <code>false</code> if it is not a html entity.
1047 */
1048 private boolean isCharHtmlEntity(char param_char)
1049 {
1050 boolean ret_bool = false;
1051
1052 for (int i = 0; i < html_entity_char_arr.length; i++)
1053 {
1054 if (param_char == html_entity_char_arr[i])
1055 {
1056 ret_bool = true;
1057 break;
1058 } // if(param_char == html_entity_char_arr[i])
1059 } // for(int i = 0; i < html_entity_char_arr.length; i++)
1060
1061 return ret_bool;
1062 } // private boolean hasHtmlEntity(String param_str)
1063
1064 /**
1065 * Escape html entities.
1066 *
1067 * @param param_char the char to escape
1068 *
1069 * @return escaped html entity. Original char is returned as a str if is
1070 * is not a html entity
1071 */
1072 private String escapeCharHtmlEntity(char param_char)
1073 {
1074 String ret_str = "" + param_char;
1075
1076 for (int i = 0; i < html_entity_char_arr.length; i++)
1077 {
1078 if (param_char == html_entity_char_arr[i])
1079 {
1080 ret_str = html_entity_escape_str_arr[i];
1081 break;
1082 } // if(param_char == html_entity_char_arr[i])
1083 } // for(int i = 0; i < html_entity_char_arr.length; i++)
1084
1085 return ret_str;
1086 } // private String escapeCharHtmlEntity(char param_char)
1087
1088 } // public class HTMLWriter extends AbstractWriter