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