KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > javax > swing > text > html > HTMLWriter


1 /*
2  * @(#)HTMLWriter.java 1.36 03/12/19
3  *
4  * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7 package javax.swing.text.html;
8
9 import javax.swing.text.*;
10 import java.io.Writer JavaDoc;
11 import java.util.Stack JavaDoc;
12 import java.util.Enumeration JavaDoc;
13 import java.util.Vector JavaDoc;
14 import java.io.IOException JavaDoc;
15 import java.util.StringTokenizer JavaDoc;
16 import java.util.NoSuchElementException JavaDoc;
17 import java.net.URL JavaDoc;
18
19 /**
20  * This is a writer for HTMLDocuments.
21  *
22  * @author Sunita Mani
23  * @version 1.26, 02/02/00
24  */

25
26
27 public class HTMLWriter extends AbstractWriter {
28     /*
29      * Stores all elements for which end tags have to
30      * be emitted.
31      */

32     private Stack JavaDoc blockElementStack = new Stack JavaDoc();
33     private boolean inContent = false;
34     private boolean inPre = false;
35     /** When inPre is true, this will indicate the end offset of the pre
36      * element. */

37     private int preEndOffset;
38     private boolean inTextArea = false;
39     private boolean newlineOutputed = false;
40     private boolean completeDoc;
41
42     /*
43      * Stores all embedded tags. Embedded tags are tags that are
44      * stored as attributes in other tags. Generally they're
45      * character level attributes. Examples include
46      * <b>, <i>, <font>, and <a>.
47      */

48     private Vector JavaDoc tags = new Vector JavaDoc(10);
49
50     /**
51      * Values for the tags.
52      */

53     private Vector JavaDoc tagValues = new Vector JavaDoc(10);
54
55     /**
56      * Used when writing out content.
57      */

58     private Segment segment;
59
60     /*
61      * This is used in closeOutUnwantedEmbeddedTags.
62      */

63     private Vector JavaDoc tagsToRemove = new Vector JavaDoc(10);
64
65     /**
66      * Set to true after the head has been output.
67      */

68     private boolean wroteHead;
69
70     /**
71      * Set to true when entities (such as <) should be replaced.
72      */

73     private boolean replaceEntities;
74
75     /**
76      * Temporary buffer.
77      */

78     private char[] tempChars;
79
80
81     /**
82      * Creates a new HTMLWriter.
83      *
84      * @param w a Writer
85      * @param doc an HTMLDocument
86      *
87      */

88     public HTMLWriter(Writer JavaDoc w, HTMLDocument JavaDoc doc) {
89     this(w, doc, 0, doc.getLength());
90     }
91
92     /**
93      * Creates a new HTMLWriter.
94      *
95      * @param w a Writer
96      * @param doc an HTMLDocument
97      * @param pos the document location from which to fetch the content
98      * @param len the amount to write out
99      */

100     public HTMLWriter(Writer JavaDoc w, HTMLDocument JavaDoc doc, int pos, int len) {
101     super(w, doc, pos, len);
102     completeDoc = (pos == 0 && len == doc.getLength());
103     setLineLength(80);
104     }
105
106     /**
107      * Iterates over the
108      * Element tree and controls the writing out of
109      * all the tags and its attributes.
110      *
111      * @exception IOException on any I/O error
112      * @exception BadLocationException if pos represents an invalid
113      * location within the document.
114      *
115      */

116     public void write() throws IOException JavaDoc, BadLocationException {
117     ElementIterator it = getElementIterator();
118     Element current = null;
119     Element next = null;
120
121     wroteHead = false;
122     setCurrentLineLength(0);
123     replaceEntities = false;
124     setCanWrapLines(false);
125     if (segment == null) {
126         segment = new Segment();
127     }
128     inPre = false;
129         boolean forcedBody = false;
130     while ((next = it.next()) != null) {
131         if (!inRange(next)) {
132                 if (completeDoc && next.getAttributes().getAttribute(
133                         StyleConstants.NameAttribute) == HTML.Tag.BODY) {
134                     forcedBody = true;
135                 }
136                 else {
137                     continue;
138                 }
139         }
140         if (current != null) {
141         
142         /*
143           if next is child of current increment indent
144         */

145
146         if (indentNeedsIncrementing(current, next)) {
147                     incrIndent();
148         } else if (current.getParentElement() != next.getParentElement()) {
149             /*
150                next and current are not siblings
151                so emit end tags for items on the stack until the
152                item on top of the stack, is the parent of the
153                next.
154             */

155             Element top = (Element)blockElementStack.peek();
156             while (top != next.getParentElement()) {
157             /*
158                pop() will return top.
159             */

160             blockElementStack.pop();
161             if (!synthesizedElement(top)) {
162                             AttributeSet attrs = top.getAttributes();
163                 if (!matchNameAttribute(attrs, HTML.Tag.PRE) &&
164                                 !isFormElementWithContent(attrs)) {
165                 decrIndent();
166                 }
167                 endTag(top);
168             }
169             top = (Element)blockElementStack.peek();
170             }
171         } else if (current.getParentElement() == next.getParentElement()) {
172             /*
173                if next and current are siblings the indent level
174                is correct. But, we need to make sure that if current is
175                on the stack, we pop it off, and put out its end tag.
176             */

177             Element top = (Element)blockElementStack.peek();
178             if (top == current) {
179             blockElementStack.pop();
180             endTag(top);
181             }
182         }
183         }
184         if (!next.isLeaf() || isFormElementWithContent(next.getAttributes())) {
185         blockElementStack.push(next);
186         startTag(next);
187         } else {
188         emptyTag(next);
189         }
190         current = next;
191     }
192     /* Emit all remaining end tags */
193
194     /* A null parameter ensures that all embedded tags
195        currently in the tags vector have their
196        corresponding end tags written out.
197     */

198     closeOutUnwantedEmbeddedTags(null);
199
200         if (forcedBody) {
201             blockElementStack.pop();
202             endTag(current);
203         }
204     while (!blockElementStack.empty()) {
205         current = (Element)blockElementStack.pop();
206         if (!synthesizedElement(current)) {
207                 AttributeSet attrs = current.getAttributes();
208         if (!matchNameAttribute(attrs, HTML.Tag.PRE) &&
209                               !isFormElementWithContent(attrs)) {
210             decrIndent();
211         }
212         endTag(current);
213         }
214     }
215
216     if (completeDoc) {
217         writeAdditionalComments();
218     }
219
220     segment.array = null;
221     }
222
223
224     /**
225      * Writes out the attribute set. Ignores all
226      * attributes with a key of type HTML.Tag,
227      * attributes with a key of type StyleConstants,
228      * and attributes with a key of type
229      * HTML.Attribute.ENDTAG.
230      *
231      * @param attr an AttributeSet
232      * @exception IOException on any I/O error
233      *
234      */

235     protected void writeAttributes(AttributeSet attr) throws IOException JavaDoc {
236     // translate css attributes to html
237
convAttr.removeAttributes(convAttr);
238     convertToHTML32(attr, convAttr);
239
240     Enumeration JavaDoc names = convAttr.getAttributeNames();
241     while (names.hasMoreElements()) {
242         Object JavaDoc name = names.nextElement();
243         if (name instanceof HTML.Tag JavaDoc ||
244         name instanceof StyleConstants ||
245         name == HTML.Attribute.ENDTAG) {
246         continue;
247         }
248         write(" " + name + "=\"" + convAttr.getAttribute(name) + "\"");
249     }
250     }
251
252     /**
253      * Writes out all empty elements (all tags that have no
254      * corresponding end tag).
255      *
256      * @param elem an Element
257      * @exception IOException on any I/O error
258      * @exception BadLocationException if pos represents an invalid
259      * location within the document.
260      */

261     protected void emptyTag(Element elem) throws BadLocationException, IOException JavaDoc {
262
263     if (!inContent && !inPre) {
264         indent();
265     }
266
267     AttributeSet attr = elem.getAttributes();
268     closeOutUnwantedEmbeddedTags(attr);
269     writeEmbeddedTags(attr);
270
271     if (matchNameAttribute(attr, HTML.Tag.CONTENT)) {
272         inContent = true;
273         text(elem);
274     } else if (matchNameAttribute(attr, HTML.Tag.COMMENT)) {
275         comment(elem);
276     } else {
277         boolean isBlock = isBlockTag(elem.getAttributes());
278         if (inContent && isBlock ) {
279         writeLineSeparator();
280         indent();
281         }
282
283         Object JavaDoc nameTag = (attr != null) ? attr.getAttribute
284                       (StyleConstants.NameAttribute) : null;
285         Object JavaDoc endTag = (attr != null) ? attr.getAttribute
286                       (HTML.Attribute.ENDTAG) : null;
287
288         boolean outputEndTag = false;
289         // If an instance of an UNKNOWN Tag, or an instance of a
290
// tag that is only visible during editing
291
//
292
if (nameTag != null && endTag != null &&
293         (endTag instanceof String JavaDoc) &&
294         ((String JavaDoc)endTag).equals("true")) {
295         outputEndTag = true;
296         }
297
298         if (completeDoc && matchNameAttribute(attr, HTML.Tag.HEAD)) {
299         if (outputEndTag) {
300             // Write out any styles.
301
writeStyles(((HTMLDocument JavaDoc)getDocument()).getStyleSheet());
302         }
303         wroteHead = true;
304         }
305
306         write('<');
307         if (outputEndTag) {
308         write('/');
309         }
310         write(elem.getName());
311         writeAttributes(attr);
312         write('>');
313         if (matchNameAttribute(attr, HTML.Tag.TITLE) && !outputEndTag) {
314         Document doc = elem.getDocument();
315         String JavaDoc title = (String JavaDoc)doc.getProperty(Document.TitleProperty);
316         write(title);
317         } else if (!inContent || isBlock) {
318         writeLineSeparator();
319         if (isBlock && inContent) {
320             indent();
321         }
322         }
323     }
324     }
325
326     /**
327      * Determines if the HTML.Tag associated with the
328      * element is a block tag.
329      *
330      * @param attr an AttributeSet
331      * @return true if tag is block tag, false otherwise.
332      */

333     protected boolean isBlockTag(AttributeSet attr) {
334     Object JavaDoc o = attr.getAttribute(StyleConstants.NameAttribute);
335     if (o instanceof HTML.Tag JavaDoc) {
336         HTML.Tag JavaDoc name = (HTML.Tag JavaDoc) o;
337         return name.isBlock();
338     }
339     return false;
340     }
341
342
343     /**
344      * Writes out a start tag for the element.
345      * Ignores all synthesized elements.
346      *
347      * @param elem an Element
348      * @exception IOException on any I/O error
349      */

350     protected void startTag(Element elem) throws IOException JavaDoc, BadLocationException {
351     
352     if (synthesizedElement(elem)) {
353         return;
354     }
355
356     // Determine the name, as an HTML.Tag.
357
AttributeSet attr = elem.getAttributes();
358     Object JavaDoc nameAttribute = attr.getAttribute(StyleConstants.NameAttribute);
359     HTML.Tag JavaDoc name;
360     if (nameAttribute instanceof HTML.Tag JavaDoc) {
361         name = (HTML.Tag JavaDoc)nameAttribute;
362     }
363     else {
364         name = null;
365     }
366
367     if (name == HTML.Tag.PRE) {
368         inPre = true;
369         preEndOffset = elem.getEndOffset();
370     }
371
372     // write out end tags for item on stack
373
closeOutUnwantedEmbeddedTags(attr);
374
375     if (inContent) {
376         writeLineSeparator();
377         inContent = false;
378         newlineOutputed = false;
379     }
380
381     if (completeDoc && name == HTML.Tag.BODY && !wroteHead) {
382         // If the head has not been output, output it and the styles.
383
wroteHead = true;
384         indent();
385         write("<head>");
386         writeLineSeparator();
387         incrIndent();
388         writeStyles(((HTMLDocument JavaDoc)getDocument()).getStyleSheet());
389         decrIndent();
390         writeLineSeparator();
391         indent();
392         write("</head>");
393         writeLineSeparator();
394     }
395
396     indent();
397     write('<');
398     write(elem.getName());
399     writeAttributes(attr);
400     write('>');
401     if (name != HTML.Tag.PRE) {
402         writeLineSeparator();
403     }
404
405     if (name == HTML.Tag.TEXTAREA) {
406         textAreaContent(elem.getAttributes());
407     } else if (name == HTML.Tag.SELECT) {
408         selectContent(elem.getAttributes());
409     } else if (completeDoc && name == HTML.Tag.BODY) {
410         // Write out the maps, which is not stored as Elements in
411
// the Document.
412
writeMaps(((HTMLDocument JavaDoc)getDocument()).getMaps());
413     }
414     else if (name == HTML.Tag.HEAD) {
415         wroteHead = true;
416             incrIndent();
417             writeStyles(((HTMLDocument JavaDoc)getDocument()).getStyleSheet());
418             decrIndent();
419     }
420     HTMLDocument JavaDoc document = null;
421     if (name == HTML.Tag.BODY
422         && (document = (HTMLDocument JavaDoc)getDocument()).hasBaseTag()) {
423             incrIndent();
424         indent();
425         write("<base href = \"" + document.getBase() + "\">");
426         writeLineSeparator();
427             decrIndent();
428     }
429     
430     }
431
432     
433     /**
434      * Writes out text that is contained in a TEXTAREA form
435      * element.
436      *
437      * @param attr an AttributeSet
438      * @exception IOException on any I/O error
439      * @exception BadLocationException if pos represents an invalid
440      * location within the document.
441      */

442     protected void textAreaContent(AttributeSet attr) throws BadLocationException, IOException JavaDoc {
443     Document doc = (Document)attr.getAttribute(StyleConstants.ModelAttribute);
444     if (doc != null && doc.getLength() > 0) {
445         if (segment == null) {
446         segment = new Segment();
447         }
448         doc.getText(0, doc.getLength(), segment);
449         if (segment.count > 0) {
450         inTextArea = true;
451         incrIndent();
452         indent();
453         setCanWrapLines(true);
454         replaceEntities = true;
455         write(segment.array, segment.offset, segment.count);
456         replaceEntities = false;
457         setCanWrapLines(false);
458         writeLineSeparator();
459         inTextArea = false;
460         decrIndent();
461         }
462     }
463     }
464
465
466     /**
467      * Writes out text. If a range is specified when the constructor
468      * is invoked, then only the appropriate range of text is written
469      * out.
470      *
471      * @param elem an Element
472      * @exception IOException on any I/O error
473      * @exception BadLocationException if pos represents an invalid
474      * location within the document.
475      */

476     protected void text(Element elem) throws BadLocationException, IOException JavaDoc {
477     int start = Math.max(getStartOffset(), elem.getStartOffset());
478     int end = Math.min(getEndOffset(), elem.getEndOffset());
479     if (start < end) {
480         if (segment == null) {
481         segment = new Segment();
482         }
483         getDocument().getText(start, end - start, segment);
484         newlineOutputed = false;
485         if (segment.count > 0) {
486         if (segment.array[segment.offset + segment.count - 1] == '\n'){
487             newlineOutputed = true;
488         }
489         if (inPre && end == preEndOffset) {
490             if (segment.count > 1) {
491             segment.count--;
492             }
493             else {
494             return;
495             }
496         }
497         replaceEntities = true;
498         setCanWrapLines(!inPre);
499         write(segment.array, segment.offset, segment.count);
500         setCanWrapLines(false);
501         replaceEntities = false;
502         }
503     }
504     }
505
506     /**
507      * Writes out the content of the SELECT form element.
508      *
509      * @param attr the AttributeSet associated with the form element
510      * @exception IOException on any I/O error
511      */

512     protected void selectContent(AttributeSet attr) throws IOException JavaDoc {
513     Object JavaDoc model = attr.getAttribute(StyleConstants.ModelAttribute);
514     incrIndent();
515     if (model instanceof OptionListModel JavaDoc) {
516         OptionListModel JavaDoc listModel = (OptionListModel JavaDoc)model;
517         int size = listModel.getSize();
518         for (int i = 0; i < size; i++) {
519         Option JavaDoc option = (Option JavaDoc)listModel.getElementAt(i);
520         writeOption(option);
521         }
522     } else if (model instanceof OptionComboBoxModel JavaDoc) {
523         OptionComboBoxModel JavaDoc comboBoxModel = (OptionComboBoxModel JavaDoc)model;
524         int size = comboBoxModel.getSize();
525         for (int i = 0; i < size; i++) {
526         Option JavaDoc option = (Option JavaDoc)comboBoxModel.getElementAt(i);
527         writeOption(option);
528         }
529     }
530     decrIndent();
531     }
532
533
534     /**
535      * Writes out the content of the Option form element.
536      * @param option an Option
537      * @exception IOException on any I/O error
538      *
539      */

540     protected void writeOption(Option JavaDoc option) throws IOException JavaDoc {
541     
542     indent();
543     write('<');
544     write("option");
545         // PENDING: should this be changed to check for null first?
546
Object JavaDoc value = option.getAttributes().getAttribute
547                               (HTML.Attribute.VALUE);
548     if (value != null) {
549         write(" value="+ value);
550     }
551     if (option.isSelected()) {
552         write(" selected");
553     }
554     write('>');
555     if (option.getLabel() != null) {
556         write(option.getLabel());
557     }
558     writeLineSeparator();
559     }
560
561     /**
562      * Writes out an end tag for the element.
563      *
564      * @param elem an Element
565      * @exception IOException on any I/O error
566      */

567     protected void endTag(Element elem) throws IOException JavaDoc {
568     if (synthesizedElement(elem)) {
569         return;
570     }
571
572     // write out end tags for item on stack
573
closeOutUnwantedEmbeddedTags(elem.getAttributes());
574     if (inContent) {
575         if (!newlineOutputed && !inPre) {
576         writeLineSeparator();
577         }
578         newlineOutputed = false;
579         inContent = false;
580     }
581     if (!inPre) {
582         indent();
583     }
584     if (matchNameAttribute(elem.getAttributes(), HTML.Tag.PRE)) {
585         inPre = false;
586     }
587         write('<');
588         write('/');
589         write(elem.getName());
590         write('>');
591     writeLineSeparator();
592     }
593
594
595
596     /**
597      * Writes out comments.
598      *
599      * @param elem an Element
600      * @exception IOException on any I/O error
601      * @exception BadLocationException if pos represents an invalid
602      * location within the document.
603      */

604     protected void comment(Element elem) throws BadLocationException, IOException JavaDoc {
605     AttributeSet as = elem.getAttributes();
606     if (matchNameAttribute(as, HTML.Tag.COMMENT)) {
607         Object JavaDoc comment = as.getAttribute(HTML.Attribute.COMMENT);
608         if (comment instanceof String JavaDoc) {
609         writeComment((String JavaDoc)comment);
610         }
611         else {
612         writeComment(null);
613         }
614     }
615     }
616
617
618     /**
619      * Writes out comment string.
620      *
621      * @param string the comment
622      * @exception IOException on any I/O error
623      * @exception BadLocationException if pos represents an invalid
624      * location within the document.
625      */

626     void writeComment(String JavaDoc string) throws IOException JavaDoc {
627     write("<!--");
628     if (string != null) {
629         write(string);
630     }
631     write("-->");
632     writeLineSeparator();
633     }
634
635
636     /**
637      * Writes out any additional comments (comments outside of the body)
638      * stored under the property HTMLDocument.AdditionalComments.
639      */

640     void writeAdditionalComments() throws IOException JavaDoc {
641     Object JavaDoc comments = getDocument().getProperty
642                                     (HTMLDocument.AdditionalComments);
643
644     if (comments instanceof Vector JavaDoc) {
645         Vector JavaDoc v = (Vector JavaDoc)comments;
646         for (int counter = 0, maxCounter = v.size(); counter < maxCounter;
647          counter++) {
648         writeComment(v.elementAt(counter).toString());
649         }
650     }
651     }
652
653
654     /**
655      * Returns true if the element is a
656      * synthesized element. Currently we are only testing
657      * for the p-implied tag.
658      */

659     protected boolean synthesizedElement(Element elem) {
660     if (matchNameAttribute(elem.getAttributes(), HTML.Tag.IMPLIED)) {
661         return true;
662     }
663     return false;
664     }
665
666
667     /**
668      * Returns true if the StyleConstants.NameAttribute is
669      * equal to the tag that is passed in as a parameter.
670      */

671     protected boolean matchNameAttribute(AttributeSet attr, HTML.Tag JavaDoc tag) {
672     Object JavaDoc o = attr.getAttribute(StyleConstants.NameAttribute);
673     if (o instanceof HTML.Tag JavaDoc) {
674         HTML.Tag JavaDoc name = (HTML.Tag JavaDoc) o;
675         if (name == tag) {
676         return true;
677         }
678     }
679     return false;
680     }
681
682     /**
683      * Searches for embedded tags in the AttributeSet
684      * and writes them out. It also stores these tags in a vector
685      * so that when appropriate the corresponding end tags can be
686      * written out.
687      *
688      * @exception IOException on any I/O error
689      */

690     protected void writeEmbeddedTags(AttributeSet attr) throws IOException JavaDoc {
691     
692     // translate css attributes to html
693
attr = convertToHTML(attr, oConvAttr);
694
695     Enumeration JavaDoc names = attr.getAttributeNames();
696     while (names.hasMoreElements()) {
697         Object JavaDoc name = names.nextElement();
698         if (name instanceof HTML.Tag JavaDoc) {
699         HTML.Tag JavaDoc tag = (HTML.Tag JavaDoc)name;
700         if (tag == HTML.Tag.FORM || tags.contains(tag)) {
701             continue;
702         }
703         write('<');
704         write(tag.toString());
705         Object JavaDoc o = attr.getAttribute(tag);
706         if (o != null && o instanceof AttributeSet) {
707             writeAttributes((AttributeSet)o);
708         }
709         write('>');
710         tags.addElement(tag);
711         tagValues.addElement(o);
712         }
713     }
714     }
715
716
717     /**
718      * Searches the attribute set for a tag, both of which
719      * are passed in as a parameter. Returns true if no match is found
720      * and false otherwise.
721      */

722     private boolean noMatchForTagInAttributes(AttributeSet attr, HTML.Tag JavaDoc t,
723                           Object JavaDoc tagValue) {
724     if (attr != null && attr.isDefined(t)) {
725         Object JavaDoc newValue = attr.getAttribute(t);
726
727         if ((tagValue == null) ? (newValue == null) :
728         (newValue != null && tagValue.equals(newValue))) {
729         return false;
730         }
731     }
732     return true;
733     }
734
735
736     /**
737      * Searches the attribute set and for each tag
738      * that is stored in the tag vector. If the tag isnt found,
739      * then the tag is removed from the vector and a corresponding
740      * end tag is written out.
741      *
742      * @exception IOException on any I/O error
743      */

744     protected void closeOutUnwantedEmbeddedTags(AttributeSet attr) throws IOException JavaDoc {
745
746     tagsToRemove.removeAllElements();
747
748     // translate css attributes to html
749
attr = convertToHTML(attr, null);
750
751     HTML.Tag JavaDoc t;
752     Object JavaDoc tValue;
753     int firstIndex = -1;
754     int size = tags.size();
755     // First, find all the tags that need to be removed.
756
for (int i = size - 1; i >= 0; i--) {
757         t = (HTML.Tag JavaDoc)tags.elementAt(i);
758         tValue = tagValues.elementAt(i);
759         if ((attr == null) || noMatchForTagInAttributes(attr, t, tValue)) {
760         firstIndex = i;
761         tagsToRemove.addElement(t);
762         }
763     }
764     if (firstIndex != -1) {
765         // Then close them out.
766
boolean removeAll = ((size - firstIndex) == tagsToRemove.size());
767         for (int i = size - 1; i >= firstIndex; i--) {
768         t = (HTML.Tag JavaDoc)tags.elementAt(i);
769         if (removeAll || tagsToRemove.contains(t)) {
770             tags.removeElementAt(i);
771             tagValues.removeElementAt(i);
772         }
773         write('<');
774         write('/');
775         write(t.toString());
776         write('>');
777         }
778         // Have to output any tags after firstIndex that still remaing,
779
// as we closed them out, but they should remain open.
780
size = tags.size();
781         for (int i = firstIndex; i < size; i++) {
782         t = (HTML.Tag JavaDoc)tags.elementAt(i);
783         write('<');
784         write(t.toString());
785         Object JavaDoc o = tagValues.elementAt(i);
786         if (o != null && o instanceof AttributeSet) {
787             writeAttributes((AttributeSet)o);
788         }
789         write('>');
790         }
791     }
792     }
793
794
795     /**
796      * Determines if the element associated with the attributeset
797      * is a TEXTAREA or SELECT. If true, returns true else
798      * false
799      */

800     private boolean isFormElementWithContent(AttributeSet attr) {
801     if (matchNameAttribute(attr, HTML.Tag.TEXTAREA) ||
802         matchNameAttribute(attr, HTML.Tag.SELECT)) {
803         return true;
804     }
805     return false;
806     }
807
808
809     /**
810      * Determines whether a the indentation needs to be
811      * incremented. Basically, if next is a child of current, and
812      * next is NOT a synthesized element, the indent level will be
813      * incremented. If there is a parent-child relationship and "next"
814      * is a synthesized element, then its children must be indented.
815      * This state is maintained by the indentNext boolean.
816      *
817      * @return boolean that's true if indent level
818      * needs incrementing.
819      */

820     private boolean indentNext = false;
821     private boolean indentNeedsIncrementing(Element current, Element next) {
822     if ((next.getParentElement() == current) && !inPre) {
823         if (indentNext) {
824         indentNext = false;
825         return true;
826         } else if (synthesizedElement(next)) {
827         indentNext = true;
828         } else if (!synthesizedElement(current)){
829         return true;
830         }
831     }
832     return false;
833     }
834
835     /**
836      * Outputs the maps as elements. Maps are not stored as elements in
837      * the document, and as such this is used to output them.
838      */

839     void writeMaps(Enumeration JavaDoc maps) throws IOException JavaDoc {
840     if (maps != null) {
841         while(maps.hasMoreElements()) {
842         Map JavaDoc map = (Map JavaDoc)maps.nextElement();
843         String JavaDoc name = map.getName();
844
845         incrIndent();
846         indent();
847         write("<map");
848         if (name != null) {
849             write(" name=\"");
850             write(name);
851             write("\">");
852         }
853         else {
854             write('>');
855         }
856         writeLineSeparator();
857         incrIndent();
858
859         // Output the areas
860
AttributeSet[] areas = map.getAreas();
861         if (areas != null) {
862             for (int counter = 0, maxCounter = areas.length;
863              counter < maxCounter; counter++) {
864             indent();
865             write("<area");
866             writeAttributes(areas[counter]);
867             write("></area>");
868             writeLineSeparator();
869             }
870         }
871         decrIndent();
872         indent();
873         write("</map>");
874         writeLineSeparator();
875         decrIndent();
876         }
877     }
878     }
879
880     /**
881      * Outputs the styles as a single element. Styles are not stored as
882      * elements, but part of the document. For the time being styles are
883      * written out as a comment, inside a style tag.
884      */

885     void writeStyles(StyleSheet JavaDoc sheet) throws IOException JavaDoc {
886     if (sheet != null) {
887         Enumeration JavaDoc styles = sheet.getStyleNames();
888         if (styles != null) {
889         boolean outputStyle = false;
890         while (styles.hasMoreElements()) {
891             String JavaDoc name = (String JavaDoc)styles.nextElement();
892             // Don't write out the default style.
893
if (!StyleContext.DEFAULT_STYLE.equals(name) &&
894             writeStyle(name, sheet.getStyle(name), outputStyle)) {
895             outputStyle = true;
896             }
897         }
898         if (outputStyle) {
899             writeStyleEndTag();
900         }
901         }
902     }
903     }
904
905     /**
906      * Outputs the named style. <code>outputStyle</code> indicates
907      * whether or not a style has been output yet. This will return
908      * true if a style is written.
909      */

910     boolean writeStyle(String JavaDoc name, Style style, boolean outputStyle)
911              throws IOException JavaDoc{
912     boolean didOutputStyle = false;
913     Enumeration JavaDoc attributes = style.getAttributeNames();
914     if (attributes != null) {
915         while (attributes.hasMoreElements()) {
916         Object JavaDoc attribute = attributes.nextElement();
917         if (attribute instanceof CSS.Attribute JavaDoc) {
918             String JavaDoc value = style.getAttribute(attribute).toString();
919             if (value != null) {
920             if (!outputStyle) {
921                 writeStyleStartTag();
922                 outputStyle = true;
923             }
924             if (!didOutputStyle) {
925                 didOutputStyle = true;
926                 indent();
927                 write(name);
928                 write(" {");
929             }
930             else {
931                 write(";");
932             }
933             write(' ');
934             write(attribute.toString());
935             write(": ");
936             write(value);
937             }
938         }
939         }
940     }
941     if (didOutputStyle) {
942         write(" }");
943         writeLineSeparator();
944     }
945     return didOutputStyle;
946     }
947
948     void writeStyleStartTag() throws IOException JavaDoc {
949     indent();
950     write("<style type=\"text/css\">");
951     incrIndent();
952     writeLineSeparator();
953     indent();
954     write("<!--");
955     incrIndent();
956     writeLineSeparator();
957     }
958
959     void writeStyleEndTag() throws IOException JavaDoc {
960     decrIndent();
961     indent();
962     write("-->");
963     writeLineSeparator();
964     decrIndent();
965     indent();
966     write("</style>");
967     writeLineSeparator();
968     indent();
969     }
970
971     // --- conversion support ---------------------------
972

973     /**
974      * Convert the give set of attributes to be html for
975      * the purpose of writing them out. Any keys that
976      * have been converted will not appear in the resultant
977      * set. Any keys not converted will appear in the
978      * resultant set the same as the received set.<p>
979      * This will put the converted values into <code>to</code>, unless
980      * it is null in which case a temporary AttributeSet will be returned.
981      */

982     AttributeSet convertToHTML(AttributeSet from, MutableAttributeSet to) {
983     if (to == null) {
984         to = convAttr;
985     }
986     to.removeAttributes(to);
987     if (writeCSS) {
988         convertToHTML40(from, to);
989     } else {
990         convertToHTML32(from, to);
991     }
992     return to;
993     }
994
995     /**
996      * If true, the writer will emit CSS attributes in preference
997      * to HTML tags/attributes (i.e. It will emit an HTML 4.0
998      * style).
999      */

1000    private boolean writeCSS = false;
1001
1002    /**
1003     * Buffer for the purpose of attribute conversion
1004     */

1005    private MutableAttributeSet convAttr = new SimpleAttributeSet();
1006
1007    /**
1008     * Buffer for the purpose of attribute conversion. This can be
1009     * used if convAttr is being used.
1010     */

1011    private MutableAttributeSet oConvAttr = new SimpleAttributeSet();
1012
1013    /**
1014     * Create an older style of HTML attributes. This will
1015     * convert character level attributes that have a StyleConstants
1016     * mapping over to an HTML tag/attribute. Other CSS attributes
1017     * will be placed in an HTML style attribute.
1018     */

1019    private static void convertToHTML32(AttributeSet from, MutableAttributeSet to) {
1020    if (from == null) {
1021        return;
1022    }
1023    Enumeration JavaDoc keys = from.getAttributeNames();
1024    String JavaDoc value = "";
1025    while (keys.hasMoreElements()) {
1026        Object JavaDoc key = keys.nextElement();
1027        if (key instanceof CSS.Attribute JavaDoc) {
1028        if ((key == CSS.Attribute.FONT_FAMILY) ||
1029            (key == CSS.Attribute.FONT_SIZE) ||
1030            (key == CSS.Attribute.COLOR)) {
1031            
1032            createFontAttribute((CSS.Attribute JavaDoc)key, from, to);
1033        } else if (key == CSS.Attribute.FONT_WEIGHT) {
1034            // add a bold tag is weight is bold
1035
CSS.FontWeight JavaDoc weightValue = (CSS.FontWeight JavaDoc)
1036            from.getAttribute(CSS.Attribute.FONT_WEIGHT);
1037            if ((weightValue != null) && (weightValue.getValue() > 400)) {
1038            addAttribute(to, HTML.Tag.B, SimpleAttributeSet.EMPTY);
1039            }
1040        } else if (key == CSS.Attribute.FONT_STYLE) {
1041            String JavaDoc s = from.getAttribute(key).toString();
1042            if (s.indexOf("italic") >= 0) {
1043            addAttribute(to, HTML.Tag.I, SimpleAttributeSet.EMPTY);
1044            }
1045        } else if (key == CSS.Attribute.TEXT_DECORATION) {
1046            String JavaDoc decor = from.getAttribute(key).toString();
1047            if (decor.indexOf("underline") >= 0) {
1048            addAttribute(to, HTML.Tag.U, SimpleAttributeSet.EMPTY);
1049            }
1050            if (decor.indexOf("line-through") >= 0) {
1051            addAttribute(to, HTML.Tag.STRIKE, SimpleAttributeSet.EMPTY);
1052            }
1053        } else if (key == CSS.Attribute.VERTICAL_ALIGN) {
1054            String JavaDoc vAlign = from.getAttribute(key).toString();
1055            if (vAlign.indexOf("sup") >= 0) {
1056            addAttribute(to, HTML.Tag.SUP, SimpleAttributeSet.EMPTY);
1057            }
1058            if (vAlign.indexOf("sub") >= 0) {
1059            addAttribute(to, HTML.Tag.SUB, SimpleAttributeSet.EMPTY);
1060            }
1061        } else if (key == CSS.Attribute.TEXT_ALIGN) {
1062            addAttribute(to, HTML.Attribute.ALIGN,
1063                    from.getAttribute(key).toString());
1064        } else {
1065            // default is to store in a HTML style attribute
1066
if (value.length() > 0) {
1067            value = value + "; ";
1068            }
1069            value = value + key + ": " + from.getAttribute(key);
1070        }
1071        } else {
1072        Object JavaDoc attr = from.getAttribute(key);
1073        if (attr instanceof AttributeSet) {
1074            attr = ((AttributeSet)attr).copyAttributes();
1075        }
1076        addAttribute(to, key, attr);
1077        }
1078    }
1079    if (value.length() > 0) {
1080        to.addAttribute(HTML.Attribute.STYLE, value);
1081    }
1082    }
1083
1084    /**
1085     * Add an attribute only if it doesn't exist so that we don't
1086     * loose information replacing it with SimpleAttributeSet.EMPTY
1087     */

1088    private static void addAttribute(MutableAttributeSet to, Object JavaDoc key, Object JavaDoc value) {
1089    Object JavaDoc attr = to.getAttribute(key);
1090    if (attr == null || attr == SimpleAttributeSet.EMPTY) {
1091            to.addAttribute(key, value);
1092    } else {
1093        if (attr instanceof MutableAttributeSet &&
1094        value instanceof AttributeSet) {
1095        ((MutableAttributeSet)attr).addAttributes((AttributeSet)value);
1096        }
1097    }
1098    }
1099
1100    /**
1101     * Create/update an HTML &lt;font&gt; tag attribute. The
1102     * value of the attribute should be a MutableAttributeSet so
1103     * that the attributes can be updated as they are discovered.
1104     */

1105    private static void createFontAttribute(CSS.Attribute JavaDoc a, AttributeSet from,
1106                    MutableAttributeSet to) {
1107    MutableAttributeSet fontAttr = (MutableAttributeSet)
1108        to.getAttribute(HTML.Tag.FONT);
1109    if (fontAttr == null) {
1110        fontAttr = new SimpleAttributeSet();
1111        to.addAttribute(HTML.Tag.FONT, fontAttr);
1112    }
1113    // edit the parameters to the font tag
1114
String JavaDoc htmlValue = from.getAttribute(a).toString();
1115    if (a == CSS.Attribute.FONT_FAMILY) {
1116        fontAttr.addAttribute(HTML.Attribute.FACE, htmlValue);
1117    } else if (a == CSS.Attribute.FONT_SIZE) {
1118        fontAttr.addAttribute(HTML.Attribute.SIZE, htmlValue);
1119    } else if (a == CSS.Attribute.COLOR) {
1120        fontAttr.addAttribute(HTML.Attribute.COLOR, htmlValue);
1121    }
1122    }
1123    
1124    /**
1125     * Copies the given AttributeSet to a new set, converting
1126     * any CSS attributes found to arguments of an HTML style
1127     * attribute.
1128     */

1129    private static void convertToHTML40(AttributeSet from, MutableAttributeSet to) {
1130    Enumeration JavaDoc keys = from.getAttributeNames();
1131    String JavaDoc value = "";
1132    while (keys.hasMoreElements()) {
1133        Object JavaDoc key = keys.nextElement();
1134        if (key instanceof CSS.Attribute JavaDoc) {
1135        value = value + " " + key + "=" + from.getAttribute(key) + ";";
1136        } else {
1137        to.addAttribute(key, from.getAttribute(key));
1138        }
1139    }
1140    if (value.length() > 0) {
1141        to.addAttribute(HTML.Attribute.STYLE, value);
1142    }
1143    }
1144
1145    //
1146
// Overrides the writing methods to only break a string when
1147
// canBreakString is true.
1148
// In a future release it is likely AbstractWriter will get this
1149
// functionality.
1150
//
1151

1152    /**
1153     * Writes the line separator. This is overriden to make sure we don't
1154     * replace the newline content in case it is outside normal ascii.
1155     */

1156    protected void writeLineSeparator() throws IOException JavaDoc {
1157    boolean oldReplace = replaceEntities;
1158    replaceEntities = false;
1159    super.writeLineSeparator();
1160    replaceEntities = oldReplace;
1161    }
1162
1163    /**
1164     * This method is overriden to map any character entities, such as
1165     * &lt; to &amp;lt;. <code>super.output</code> will be invoked to
1166     * write the content.
1167     */

1168    protected void output(char[] chars, int start, int length)
1169               throws IOException JavaDoc {
1170    if (!replaceEntities) {
1171        super.output(chars, start, length);
1172        return;
1173    }
1174    int last = start;
1175    length += start;
1176    for (int counter = start; counter < length; counter++) {
1177        // This will change, we need better support character level
1178
// entities.
1179
switch(chars[counter]) {
1180        // Character level entities.
1181
case '<':
1182        if (counter > last) {
1183            super.output(chars, last, counter - last);
1184        }
1185        last = counter + 1;
1186        output("&lt;");
1187        break;
1188        case '>':
1189        if (counter > last) {
1190            super.output(chars, last, counter - last);
1191        }
1192        last = counter + 1;
1193        output("&gt;");
1194        break;
1195        case '&':
1196        if (counter > last) {
1197            super.output(chars, last, counter - last);
1198        }
1199        last = counter + 1;
1200        output("&amp;");
1201        break;
1202        case '"':
1203        if (counter > last) {
1204            super.output(chars, last, counter - last);
1205        }
1206        last = counter + 1;
1207        output("&quot;");
1208        break;
1209        // Special characters
1210
case '\n':
1211        case '\t':
1212        case '\r':
1213        break;
1214        default:
1215        if (chars[counter] < ' ' || chars[counter] > 127) {
1216            if (counter > last) {
1217            super.output(chars, last, counter - last);
1218            }
1219            last = counter + 1;
1220            // If the character is outside of ascii, write the
1221
// numeric value.
1222
output("&#");
1223            output(String.valueOf((int)chars[counter]));
1224            output(";");
1225        }
1226        break;
1227        }
1228    }
1229    if (last < length) {
1230        super.output(chars, last, length - last);
1231    }
1232    }
1233
1234    /**
1235     * This directly invokes super's <code>output</code> after converting
1236     * <code>string</code> to a char[].
1237     */

1238    private void output(String JavaDoc string) throws IOException JavaDoc {
1239    int length = string.length();
1240    if (tempChars == null || tempChars.length < length) {
1241        tempChars = new char[length];
1242    }
1243    string.getChars(0, length, tempChars, 0);
1244    super.output(tempChars, 0, length);
1245    }
1246}
1247
Popular Tags