KickJava   Java API By Example, From Geeks To Geeks.

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


1 /*
2  * @(#)MinimalHTMLWriter.java 1.14 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
8 package javax.swing.text.html;
9
10 import java.io.Writer JavaDoc;
11 import java.io.IOException JavaDoc;
12 import java.util.*;
13 import java.awt.Color JavaDoc;
14 import javax.swing.text.*;
15
16 /**
17  * MinimalHTMLWriter is a fallback writer used by the
18  * HTMLEditorKit to write out HTML for a document that
19  * is a not produced by the EditorKit.
20  *
21  * The format for the document is:
22  * <pre>
23  * &lt;html&gt;
24  * &lt;head&gt;
25  * &lt;style&gt;
26  * &lt;!-- list of named styles
27  * p.normal {
28  * font-family: SansSerif;
29  * margin-height: 0;
30  * font-size: 14
31  * }
32  * --&gt;
33  * &lt;/style&gt;
34  * &lt;/head&gt;
35  * &lt;body&gt;
36  * &lt;p style=normal&gt;
37  * <b>Bold, italic, and underline attributes
38  * of the run are emitted as HTML tags.
39  * The remaining attributes are emitted as
40  * part of the style attribute of a &lt;span&gt; tag.
41  * The syntax is similar to inline styles.</b>
42  * &lt;/p&gt;
43  * &lt;/body&gt;
44  * &lt;/html&gt;
45  * </pre>
46  *
47  * @author Sunita Mani
48  * @version 1.14, 12/19/03
49  */

50
51 public class MinimalHTMLWriter extends AbstractWriter {
52
53     /**
54      * These static finals are used to
55      * tweak and query the fontMask about which
56      * of these tags need to be generated or
57      * terminated.
58      */

59     private static final int BOLD = 0x01;
60     private static final int ITALIC = 0x02;
61     private static final int UNDERLINE = 0x04;
62
63     // Used to map StyleConstants to CSS.
64
private static final CSS JavaDoc css = new CSS JavaDoc();
65
66     private int fontMask = 0;
67
68     int startOffset = 0;
69     int endOffset = 0;
70
71     /**
72      * Stores the attributes of the previous run.
73      * Used to compare with the current run's
74      * attributeset. If identical, then a
75      * &lt;span&gt; tag is not emitted.
76      */

77     private AttributeSet fontAttributes;
78
79     /**
80      * Maps from style name as held by the Document, to the archived
81      * style name (style name written out). These may differ.
82      */

83     private Hashtable styleNameMapping;
84
85     /**
86      * Creates a new MinimalHTMLWriter.
87      *
88      * @param w Writer
89      * @param doc StyledDocument
90      *
91      */

92     public MinimalHTMLWriter(Writer JavaDoc w, StyledDocument doc) {
93     super(w, doc);
94     }
95
96     /**
97      * Creates a new MinimalHTMLWriter.
98      *
99      * @param w Writer
100      * @param doc StyledDocument
101      * @param pos The location in the document to fetch the
102      * content.
103      * @param len The amount to write out.
104      *
105      */

106     public MinimalHTMLWriter(Writer JavaDoc w, StyledDocument doc, int pos, int len) {
107     super(w, doc, pos, len);
108     }
109
110     /**
111      * Generates HTML output
112      * from a StyledDocument.
113      *
114      * @exception IOException on any I/O error
115      * @exception BadLocationException if pos represents an invalid
116      * location within the document.
117      *
118      */

119     public void write() throws IOException JavaDoc, BadLocationException {
120     styleNameMapping = new Hashtable();
121     writeStartTag("<html>");
122     writeHeader();
123     writeBody();
124     writeEndTag("</html>");
125     }
126
127
128     /**
129      * Writes out all the attributes for the
130      * following types:
131      * StyleConstants.ParagraphConstants,
132      * StyleConstants.CharacterConstants,
133      * StyleConstants.FontConstants,
134      * StyleConstants.ColorConstants.
135      * The attribute name and value are separated by a colon.
136      * Each pair is separated by a semicolon.
137      *
138      * @exception IOException on any I/O error
139      */

140     protected void writeAttributes(AttributeSet attr) throws IOException JavaDoc {
141     Enumeration attributeNames = attr.getAttributeNames();
142     while (attributeNames.hasMoreElements()) {
143         Object JavaDoc name = attributeNames.nextElement();
144         if ((name instanceof StyleConstants.ParagraphConstants) ||
145         (name instanceof StyleConstants.CharacterConstants) ||
146         (name instanceof StyleConstants.FontConstants) ||
147         (name instanceof StyleConstants.ColorConstants)) {
148         indent();
149         write(name.toString());
150         write(':');
151         write(css.styleConstantsValueToCSSValue
152               ((StyleConstants)name, attr.getAttribute(name)).
153               toString());
154         write(';');
155         write(NEWLINE);
156         }
157     }
158     }
159
160
161     /**
162      * Writes out text.
163      *
164      * @exception IOException on any I/O error
165      */

166     protected void text(Element elem) throws IOException JavaDoc, BadLocationException {
167     String JavaDoc contentStr = getText(elem);
168     if ((contentStr.length() > 0) &&
169         (contentStr.charAt(contentStr.length()-1) == NEWLINE)) {
170         contentStr = contentStr.substring(0, contentStr.length()-1);
171     }
172     if (contentStr.length() > 0) {
173         write(contentStr);
174     }
175     }
176
177     /**
178      * Writes out a start tag appropriately
179      * indented. Also increments the indent level.
180      *
181      * @exception IOException on any I/O error
182      */

183     protected void writeStartTag(String JavaDoc tag) throws IOException JavaDoc {
184     indent();
185     write(tag);
186     write(NEWLINE);
187     incrIndent();
188     }
189
190
191     /**
192      * Writes out an end tag appropriately
193      * indented. Also decrements the indent level.
194      *
195      * @exception IOException on any I/O error
196      */

197     protected void writeEndTag(String JavaDoc endTag) throws IOException JavaDoc {
198     decrIndent();
199     indent();
200     write(endTag);
201     write(NEWLINE);
202     }
203
204
205     /**
206      * Writes out the &lt;head&gt; and &lt;style&gt;
207      * tags, and then invokes writeStyles() to write
208      * out all the named styles as the content of the
209      * &lt;style&gt; tag. The content is surrounded by
210      * valid HTML comment markers to ensure that the
211      * document is viewable in applications/browsers
212      * that do not support the tag.
213      *
214      * @exception IOException on any I/O error
215      */

216     protected void writeHeader() throws IOException JavaDoc {
217     writeStartTag("<head>");
218     writeStartTag("<style>");
219     writeStartTag("<!--");
220     writeStyles();
221     writeEndTag("-->");
222     writeEndTag("</style>");
223     writeEndTag("</head>");
224     }
225
226
227
228     /**
229      * Writes out all the named styles as the
230      * content of the &lt;style&gt; tag.
231      *
232      * @exception IOException on any I/O error
233      */

234     protected void writeStyles() throws IOException JavaDoc {
235     /*
236      * Access to DefaultStyledDocument done to workaround
237      * a missing API in styled document to access the
238      * stylenames.
239      */

240     DefaultStyledDocument styledDoc = ((DefaultStyledDocument)getDocument());
241     Enumeration styleNames = styledDoc.getStyleNames();
242
243     while (styleNames.hasMoreElements()) {
244         Style s = styledDoc.getStyle((String JavaDoc)styleNames.nextElement());
245
246         /** PENDING: Once the name attribute is removed
247         from the list we check check for 0. **/

248         if (s.getAttributeCount() == 1 &&
249         s.isDefined(StyleConstants.NameAttribute)) {
250         continue;
251         }
252         indent();
253         write("p." + addStyleName(s.getName()));
254         write(" {\n");
255         incrIndent();
256         writeAttributes(s);
257         decrIndent();
258         indent();
259         write("}\n");
260     }
261     }
262
263
264     /**
265      * Iterates over the elements in the document
266      * and processes elements based on whether they are
267      * branch elements or leaf elements. This method specially handles
268      * leaf elements that are text.
269      *
270      * @exception IOException on any I/O error
271      */

272     protected void writeBody() throws IOException JavaDoc, BadLocationException {
273     ElementIterator it = getElementIterator();
274
275     /*
276       This will be a section element for a styled document.
277       We represent this element in HTML as the body tags.
278       Therefore we ignore it.
279      */

280     it.current();
281
282     Element next = null;
283
284     writeStartTag("<body>");
285
286     boolean inContent = false;
287
288     while((next = it.next()) != null) {
289         if (!inRange(next)) {
290         continue;
291         }
292         if (next instanceof AbstractDocument.BranchElement) {
293         if (inContent) {
294             writeEndParagraph();
295             inContent = false;
296             fontMask = 0;
297         }
298         writeStartParagraph(next);
299         } else if (isText(next)) {
300         writeContent(next, !inContent);
301         inContent = true;
302         } else {
303         writeLeaf(next);
304         inContent = true;
305         }
306     }
307     if (inContent) {
308         writeEndParagraph();
309     }
310     writeEndTag("</body>");
311     }
312
313
314     /**
315      * Emits an end tag for a &lt;p&gt;
316      * tag. Before writing out the tag, this method ensures
317      * that all other tags that have been opened are
318      * appropriately closed off.
319      *
320      * @exception IOException on any I/O error
321      */

322     protected void writeEndParagraph() throws IOException JavaDoc {
323     writeEndMask(fontMask);
324     if (inFontTag()) {
325         endSpanTag();
326     } else {
327         write(NEWLINE);
328     }
329     writeEndTag("</p>");
330     }
331
332
333     /**
334      * Emits the start tag for a paragraph. If
335      * the paragraph has a named style associated with it,
336      * then this method also generates a class attribute for the
337      * &lt;p&gt; tag and sets its value to be the name of the
338      * style.
339      *
340      * @exception IOException on any I/O error
341      */

342     protected void writeStartParagraph(Element elem) throws IOException JavaDoc {
343     AttributeSet attr = elem.getAttributes();
344     Object JavaDoc resolveAttr = attr.getAttribute(StyleConstants.ResolveAttribute);
345     if (resolveAttr instanceof StyleContext.NamedStyle) {
346         writeStartTag("<p class=" + mapStyleName(((StyleContext.NamedStyle)resolveAttr).getName()) + ">");
347     } else {
348         writeStartTag("<p>");
349     }
350     }
351
352
353     /**
354      * Responsible for writing out other non-text leaf
355      * elements.
356      *
357      * @exception IOException on any I/O error
358      */

359     protected void writeLeaf(Element elem) throws IOException JavaDoc {
360     indent();
361     if (elem.getName() == StyleConstants.IconElementName) {
362         writeImage(elem);
363     } else if (elem.getName() == StyleConstants.ComponentElementName) {
364         writeComponent(elem);
365     }
366     }
367
368
369     /**
370      * Responsible for handling Icon Elements;
371      * deliberately unimplemented. How to implement this method is
372      * an issue of policy. For example, if you're generating
373      * an &lt;img&gt; tag, how should you
374      * represent the src attribute (the location of the image)?
375      * In certain cases it could be a URL, in others it could
376      * be read from a stream.
377      *
378      * @param elem element of type StyleConstants.IconElementName
379      */

380     protected void writeImage(Element elem) throws IOException JavaDoc {
381     }
382
383
384     /**
385      * Responsible for handling Component Elements;
386      * deliberately unimplemented.
387      * How this method is implemented is a matter of policy.
388      */

389     protected void writeComponent(Element elem) throws IOException JavaDoc {
390     }
391
392
393     /**
394      * Returns true if the element is a text element.
395      *
396      */

397     protected boolean isText(Element elem) {
398     return (elem.getName() == AbstractDocument.ContentElementName);
399     }
400
401
402     /**
403      * Writes out the attribute set
404      * in an HTML-compliant manner.
405      *
406      * @exception IOException on any I/O error
407      * @exception BadLocationException if pos represents an invalid
408      * location within the document.
409      */

410     protected void writeContent(Element elem, boolean needsIndenting)
411     throws IOException JavaDoc, BadLocationException {
412
413     AttributeSet attr = elem.getAttributes();
414     writeNonHTMLAttributes(attr);
415     if (needsIndenting) {
416         indent();
417     }
418     writeHTMLTags(attr);
419     text(elem);
420     }
421
422
423     /**
424      * Generates
425      * bold &lt;b&gt;, italic &lt;i&gt;, and &lt;u&gt; tags for the
426      * text based on its attribute settings.
427      *
428      * @exception IOException on any I/O error
429      */

430
431     protected void writeHTMLTags(AttributeSet attr) throws IOException JavaDoc {
432
433     int oldMask = fontMask;
434     setFontMask(attr);
435
436     int endMask = 0;
437     int startMask = 0;
438     if ((oldMask & BOLD) != 0) {
439         if ((fontMask & BOLD) == 0) {
440         endMask |= BOLD;
441         }
442     } else if ((fontMask & BOLD) != 0) {
443         startMask |= BOLD;
444     }
445
446     if ((oldMask & ITALIC) != 0) {
447         if ((fontMask & ITALIC) == 0) {
448         endMask |= ITALIC;
449         }
450     } else if ((fontMask & ITALIC) != 0) {
451         startMask |= ITALIC;
452     }
453
454     if ((oldMask & UNDERLINE) != 0) {
455         if ((fontMask & UNDERLINE) == 0) {
456         endMask |= UNDERLINE;
457         }
458     } else if ((fontMask & UNDERLINE) != 0) {
459         startMask |= UNDERLINE;
460     }
461     writeEndMask(endMask);
462     writeStartMask(startMask);
463     }
464
465
466     /**
467      * Tweaks the appropriate bits of fontMask
468      * to reflect whether the text is to be displayed in
469      * bold, italic, and/or with an underline.
470      *
471      */

472     private void setFontMask(AttributeSet attr) {
473     if (StyleConstants.isBold(attr)) {
474         fontMask |= BOLD;
475     }
476
477     if (StyleConstants.isItalic(attr)) {
478         fontMask |= ITALIC;
479     }
480
481     if (StyleConstants.isUnderline(attr)) {
482         fontMask |= UNDERLINE;
483     }
484     }
485
486
487
488
489     /**
490      * Writes out start tags &lt;u&gt;, &lt;i&gt;, and &lt;b&gt; based on
491      * the mask settings.
492      *
493      * @exception IOException on any I/O error
494      */

495     private void writeStartMask(int mask) throws IOException JavaDoc {
496     if (mask != 0) {
497         if ((mask & UNDERLINE) != 0) {
498         write("<u>");
499         }
500         if ((mask & ITALIC) != 0) {
501         write("<i>");
502         }
503         if ((mask & BOLD) != 0) {
504         write("<b>");
505         }
506     }
507     }
508
509     /**
510      * Writes out end tags for &lt;u&gt;, &lt;i&gt;, and &lt;b&gt; based on
511      * the mask settings.
512      *
513      * @exception IOException on any I/O error
514      */

515     private void writeEndMask(int mask) throws IOException JavaDoc {
516     if (mask != 0) {
517         if ((mask & BOLD) != 0) {
518         write("</b>");
519         }
520         if ((mask & ITALIC) != 0) {
521         write("</i>");
522         }
523         if ((mask & UNDERLINE) != 0) {
524         write("</u>");
525         }
526     }
527     }
528
529
530     /**
531      * Writes out the remaining
532      * character-level attributes (attributes other than bold,
533      * italic, and underline) in an HTML-compliant way. Given that
534      * attributes such as font family and font size have no direct
535      * mapping to HTML tags, a &lt;span&gt; tag is generated and its
536      * style attribute is set to contain the list of remaining
537      * attributes just like inline styles.
538      *
539      * @exception IOException on any I/O error
540      */

541     protected void writeNonHTMLAttributes(AttributeSet attr) throws IOException JavaDoc {
542
543     String JavaDoc style = "";
544     String JavaDoc separator = "; ";
545
546     if (inFontTag() && fontAttributes.isEqual(attr)) {
547         return;
548     }
549
550     boolean first = true;
551     Color JavaDoc color = (Color JavaDoc)attr.getAttribute(StyleConstants.Foreground);
552     if (color != null) {
553         style += "color: " + css.styleConstantsValueToCSSValue
554                             ((StyleConstants)StyleConstants.Foreground,
555                      color);
556         first = false;
557     }
558     Integer JavaDoc size = (Integer JavaDoc)attr.getAttribute(StyleConstants.FontSize);
559     if (size != null) {
560         if (!first) {
561         style += separator;
562         }
563         style += "font-size: " + size.intValue() + "pt";
564         first = false;
565     }
566
567     String JavaDoc family = (String JavaDoc)attr.getAttribute(StyleConstants.FontFamily);
568     if (family != null) {
569         if (!first) {
570         style += separator;
571         }
572         style += "font-family: " + family;
573         first = false;
574     }
575
576     if (style.length() > 0) {
577         if (fontMask != 0) {
578         writeEndMask(fontMask);
579         fontMask = 0;
580         }
581         startSpanTag(style);
582         fontAttributes = attr;
583     }
584     else if (fontAttributes != null) {
585         writeEndMask(fontMask);
586         fontMask = 0;
587         endSpanTag();
588     }
589     }
590
591
592     /**
593      * Returns true if we are currently in a &lt;font&gt; tag.
594      */

595     protected boolean inFontTag() {
596     return (fontAttributes != null);
597     }
598
599     /**
600      * This is no longer used, instead &lt;span&gt; will be written out.
601      * <p>
602      * Writes out an end tag for the &lt;font&gt; tag.
603      *
604      * @exception IOException on any I/O error
605      */

606     protected void endFontTag() throws IOException JavaDoc {
607     write(NEWLINE);
608     writeEndTag("</font>");
609     fontAttributes = null;
610     }
611
612
613     /**
614      * This is no longer used, instead &lt;span&gt; will be written out.
615      * <p>
616      * Writes out a start tag for the &lt;font&gt; tag.
617      * Because font tags cannot be nested,
618      * this method closes out
619      * any enclosing font tag before writing out a
620      * new start tag.
621      *
622      * @exception IOException on any I/O error
623      */

624     protected void startFontTag(String JavaDoc style) throws IOException JavaDoc {
625     boolean callIndent = false;
626     if (inFontTag()) {
627         endFontTag();
628         callIndent = true;
629     }
630     writeStartTag("<font style=\"" + style + "\">");
631     if (callIndent) {
632         indent();
633     }
634     }
635
636     /**
637      * Writes out a start tag for the &lt;font&gt; tag.
638      * Because font tags cannot be nested,
639      * this method closes out
640      * any enclosing font tag before writing out a
641      * new start tag.
642      *
643      * @exception IOException on any I/O error
644      */

645     private void startSpanTag(String JavaDoc style) throws IOException JavaDoc {
646     boolean callIndent = false;
647     if (inFontTag()) {
648         endSpanTag();
649         callIndent = true;
650     }
651     writeStartTag("<span style=\"" + style + "\">");
652     if (callIndent) {
653         indent();
654     }
655     }
656
657     /**
658      * Writes out an end tag for the &lt;span&gt; tag.
659      *
660      * @exception IOException on any I/O error
661      */

662     private void endSpanTag() throws IOException JavaDoc {
663     write(NEWLINE);
664     writeEndTag("</span>");
665     fontAttributes = null;
666     }
667
668     /**
669      * Adds the style named <code>style</code> to the style mapping. This
670      * returns the name that should be used when outputting. CSS does not
671      * allow the full Unicode set to be used as a style name.
672      */

673     private String JavaDoc addStyleName(String JavaDoc style) {
674     if (styleNameMapping == null) {
675         return style;
676     }
677     StringBuffer JavaDoc sb = null;
678     for (int counter = style.length() - 1; counter >= 0; counter--) {
679         if (!isValidCharacter(style.charAt(counter))) {
680         if (sb == null) {
681             sb = new StringBuffer JavaDoc(style);
682         }
683         sb.setCharAt(counter, 'a');
684         }
685     }
686     String JavaDoc mappedName = (sb != null) ? sb.toString() : style;
687     while (styleNameMapping.get(mappedName) != null) {
688         mappedName = mappedName + 'x';
689     }
690     styleNameMapping.put(style, mappedName);
691     return mappedName;
692     }
693
694     /**
695      * Returns the mapped style name corresponding to <code>style</code>.
696      */

697     private String JavaDoc mapStyleName(String JavaDoc style) {
698     if (styleNameMapping == null) {
699         return style;
700     }
701     String JavaDoc retValue = (String JavaDoc)styleNameMapping.get(style);
702     return (retValue == null) ? style : retValue;
703     }
704
705     private boolean isValidCharacter(char character) {
706     return ((character >= 'a' && character <= 'z') ||
707         (character >= 'A' && character <= 'Z'));
708     }
709 }
710
Popular Tags