History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/javax/swing/text/html/MinimalHTMLWriter.java (1 of 18352 files). Show all

Unified · Split

java/pkg/javax/swing/text/html/MinimalHTMLWriter.java+726−0
@@ -0,0 +1,726 @@
1/*
2 * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package javax.swing.text.html;
27
28import java.io.Writer;
29import java.io.IOException;
30import java.util.*;
31import java.awt.Color;
32import javax.swing.text.*;
33
34/**
35 * MinimalHTMLWriter is a fallback writer used by the
36 * HTMLEditorKit to write out HTML for a document that
37 * is a not produced by the EditorKit.
38 *
39 * The format for the document is:
40 * <pre>
41 * &lt;html&gt;
42 * &lt;head&gt;
43 * &lt;style&gt;
44 * &lt;!-- list of named styles
45 * p.normal {
46 * font-family: SansSerif;
47 * margin-height: 0;
48 * font-size: 14
49 * }
50 * --&gt;
51 * &lt;/style&gt;
52 * &lt;/head&gt;
53 * &lt;body&gt;
54 * &lt;p style=normal&gt;
55 * <b>Bold, italic, and underline attributes
56 * of the run are emitted as HTML tags.
57 * The remaining attributes are emitted as
58 * part of the style attribute of a &lt;span&gt; tag.
59 * The syntax is similar to inline styles.</b>
60 * &lt;/p&gt;
61 * &lt;/body&gt;
62 * &lt;/html&gt;
63 * </pre>
64 *
65 * @author Sunita Mani
66 */
67
68public class MinimalHTMLWriter extends AbstractWriter {
69
70 /**
71 * These static finals are used to
72 * tweak and query the fontMask about which
73 * of these tags need to be generated or
74 * terminated.
75 */
76 private static final int BOLD = 0x01;
77 private static final int ITALIC = 0x02;
78 private static final int UNDERLINE = 0x04;
79
80 // Used to map StyleConstants to CSS.
81 private static final CSS css = new CSS();
82
83 private int fontMask = 0;
84
85 int startOffset = 0;
86 int endOffset = 0;
87
88 /**
89 * Stores the attributes of the previous run.
90 * Used to compare with the current run's
91 * attributeset. If identical, then a
92 * &lt;span&gt; tag is not emitted.
93 */
94 private AttributeSet fontAttributes;
95
96 /**
97 * Maps from style name as held by the Document, to the archived
98 * style name (style name written out). These may differ.
99 */
100 private Hashtable<String, String> styleNameMapping;
101
102 /**
103 * Creates a new MinimalHTMLWriter.
104 *
105 * @param w Writer
106 * @param doc StyledDocument
107 *
108 */
109 public MinimalHTMLWriter(Writer w, StyledDocument doc) {
110 super(w, doc);
111 }
112
113 /**
114 * Creates a new MinimalHTMLWriter.
115 *
116 * @param w Writer
117 * @param doc StyledDocument
118 * @param pos The location in the document to fetch the
119 * content.
120 * @param len The amount to write out.
121 *
122 */
123 public MinimalHTMLWriter(Writer w, StyledDocument doc, int pos, int len) {
124 super(w, doc, pos, len);
125 }
126
127 /**
128 * Generates HTML output
129 * from a StyledDocument.
130 *
131 * @exception IOException on any I/O error
132 * @exception BadLocationException if pos represents an invalid
133 * location within the document.
134 *
135 */
136 public void write() throws IOException, BadLocationException {
137 styleNameMapping = new Hashtable<String, String>();
138 writeStartTag("<html>");
139 writeHeader();
140 writeBody();
141 writeEndTag("</html>");
142 }
143
144
145 /**
146 * Writes out all the attributes for the
147 * following types:
148 * StyleConstants.ParagraphConstants,
149 * StyleConstants.CharacterConstants,
150 * StyleConstants.FontConstants,
151 * StyleConstants.ColorConstants.
152 * The attribute name and value are separated by a colon.
153 * Each pair is separated by a semicolon.
154 *
155 * @exception IOException on any I/O error
156 */
157 protected void writeAttributes(AttributeSet attr) throws IOException {
158 Enumeration attributeNames = attr.getAttributeNames();
159 while (attributeNames.hasMoreElements()) {
160 Object name = attributeNames.nextElement();
161 if ((name instanceof StyleConstants.ParagraphConstants) ||
162 (name instanceof StyleConstants.CharacterConstants) ||
163 (name instanceof StyleConstants.FontConstants) ||
164 (name instanceof StyleConstants.ColorConstants)) {
165 indent();
166 write(name.toString());
167 write(':');
168 write(css.styleConstantsValueToCSSValue
169 ((StyleConstants)name, attr.getAttribute(name)).
170 toString());
171 write(';');
172 write(NEWLINE);
173 }
174 }
175 }
176
177
178 /**
179 * Writes out text.
180 *
181 * @exception IOException on any I/O error
182 */
183 protected void text(Element elem) throws IOException, BadLocationException {
184 String contentStr = getText(elem);
185 if ((contentStr.length() > 0) &&
186 (contentStr.charAt(contentStr.length()-1) == NEWLINE)) {
187 contentStr = contentStr.substring(0, contentStr.length()-1);
188 }
189 if (contentStr.length() > 0) {
190 write(contentStr);
191 }
192 }
193
194 /**
195 * Writes out a start tag appropriately
196 * indented. Also increments the indent level.
197 *
198 * @exception IOException on any I/O error
199 */
200 protected void writeStartTag(String tag) throws IOException {
201 indent();
202 write(tag);
203 write(NEWLINE);
204 incrIndent();
205 }
206
207
208 /**
209 * Writes out an end tag appropriately
210 * indented. Also decrements the indent level.
211 *
212 * @exception IOException on any I/O error
213 */
214 protected void writeEndTag(String endTag) throws IOException {
215 decrIndent();
216 indent();
217 write(endTag);
218 write(NEWLINE);
219 }
220
221
222 /**
223 * Writes out the &lt;head&gt; and &lt;style&gt;
224 * tags, and then invokes writeStyles() to write
225 * out all the named styles as the content of the
226 * &lt;style&gt; tag. The content is surrounded by
227 * valid HTML comment markers to ensure that the
228 * document is viewable in applications/browsers
229 * that do not support the tag.
230 *
231 * @exception IOException on any I/O error
232 */
233 protected void writeHeader() throws IOException {
234 writeStartTag("<head>");
235 writeStartTag("<style>");
236 writeStartTag("<!--");
237 writeStyles();
238 writeEndTag("-->");
239 writeEndTag("</style>");
240 writeEndTag("</head>");
241 }
242
243
244
245 /**
246 * Writes out all the named styles as the
247 * content of the &lt;style&gt; tag.
248 *
249 * @exception IOException on any I/O error
250 */
251 protected void writeStyles() throws IOException {
252 /*
253 * Access to DefaultStyledDocument done to workaround
254 * a missing API in styled document to access the
255 * stylenames.
256 */
257 DefaultStyledDocument styledDoc = ((DefaultStyledDocument)getDocument());
258 Enumeration styleNames = styledDoc.getStyleNames();
259
260 while (styleNames.hasMoreElements()) {
261 Style s = styledDoc.getStyle((String)styleNames.nextElement());
262
263 /** PENDING: Once the name attribute is removed
264 from the list we check check for 0. **/
265 if (s.getAttributeCount() == 1 &&
266 s.isDefined(StyleConstants.NameAttribute)) {
267 continue;
268 }
269 indent();
270 write("p." + addStyleName(s.getName()));
271 write(" {\n");
272 incrIndent();
273 writeAttributes(s);
274 decrIndent();
275 indent();
276 write("}\n");
277 }
278 }
279
280
281 /**
282 * Iterates over the elements in the document
283 * and processes elements based on whether they are
284 * branch elements or leaf elements. This method specially handles
285 * leaf elements that are text.
286 *
287 * @exception IOException on any I/O error
288 */
289 protected void writeBody() throws IOException, BadLocationException {
290 ElementIterator it = getElementIterator();
291
292 /*
293 This will be a section element for a styled document.
294 We represent this element in HTML as the body tags.
295 Therefore we ignore it.
296 */
297 it.current();
298
299 Element next;
300
301 writeStartTag("<body>");
302
303 boolean inContent = false;
304
305 while((next = it.next()) != null) {
306 if (!inRange(next)) {
307 continue;
308 }
309 if (next instanceof AbstractDocument.BranchElement) {
310 if (inContent) {
311 writeEndParagraph();
312 inContent = false;
313 fontMask = 0;
314 }
315 writeStartParagraph(next);
316 } else if (isText(next)) {
317 writeContent(next, !inContent);
318 inContent = true;
319 } else {
320 writeLeaf(next);
321 inContent = true;
322 }
323 }
324 if (inContent) {
325 writeEndParagraph();
326 }
327 writeEndTag("</body>");
328 }
329
330
331 /**
332 * Emits an end tag for a &lt;p&gt;
333 * tag. Before writing out the tag, this method ensures
334 * that all other tags that have been opened are
335 * appropriately closed off.
336 *
337 * @exception IOException on any I/O error
338 */
339 protected void writeEndParagraph() throws IOException {
340 writeEndMask(fontMask);
341 if (inFontTag()) {
342 endSpanTag();
343 } else {
344 write(NEWLINE);
345 }
346 writeEndTag("</p>");
347 }
348
349
350 /**
351 * Emits the start tag for a paragraph. If
352 * the paragraph has a named style associated with it,
353 * then this method also generates a class attribute for the
354 * &lt;p&gt; tag and sets its value to be the name of the
355 * style.
356 *
357 * @exception IOException on any I/O error
358 */
359 protected void writeStartParagraph(Element elem) throws IOException {
360 AttributeSet attr = elem.getAttributes();
361 Object resolveAttr = attr.getAttribute(StyleConstants.ResolveAttribute);
362 if (resolveAttr instanceof StyleContext.NamedStyle) {
363 writeStartTag("<p class=" + mapStyleName(((StyleContext.NamedStyle)resolveAttr).getName()) + ">");
364 } else {
365 writeStartTag("<p>");
366 }
367 }
368
369
370 /**
371 * Responsible for writing out other non-text leaf
372 * elements.
373 *
374 * @exception IOException on any I/O error
375 */
376 protected void writeLeaf(Element elem) throws IOException {
377 indent();
378 if (elem.getName() == StyleConstants.IconElementName) {
379 writeImage(elem);
380 } else if (elem.getName() == StyleConstants.ComponentElementName) {
381 writeComponent(elem);
382 }
383 }
384
385
386 /**
387 * Responsible for handling Icon Elements;
388 * deliberately unimplemented. How to implement this method is
389 * an issue of policy. For example, if you're generating
390 * an &lt;img&gt; tag, how should you
391 * represent the src attribute (the location of the image)?
392 * In certain cases it could be a URL, in others it could
393 * be read from a stream.
394 *
395 * @param elem element of type StyleConstants.IconElementName
396 */
397 protected void writeImage(Element elem) throws IOException {
398 }
399
400
401 /**
402 * Responsible for handling Component Elements;
403 * deliberately unimplemented.
404 * How this method is implemented is a matter of policy.
405 */
406 protected void writeComponent(Element elem) throws IOException {
407 }
408
409
410 /**
411 * Returns true if the element is a text element.
412 *
413 */
414 protected boolean isText(Element elem) {
415 return (elem.getName() == AbstractDocument.ContentElementName);
416 }
417
418
419 /**
420 * Writes out the attribute set
421 * in an HTML-compliant manner.
422 *
423 * @exception IOException on any I/O error
424 * @exception BadLocationException if pos represents an invalid
425 * location within the document.
426 */
427 protected void writeContent(Element elem, boolean needsIndenting)
428 throws IOException, BadLocationException {
429
430 AttributeSet attr = elem.getAttributes();
431 writeNonHTMLAttributes(attr);
432 if (needsIndenting) {
433 indent();
434 }
435 writeHTMLTags(attr);
436 text(elem);
437 }
438
439
440 /**
441 * Generates
442 * bold &lt;b&gt;, italic &lt;i&gt;, and &lt;u&gt; tags for the
443 * text based on its attribute settings.
444 *
445 * @exception IOException on any I/O error
446 */
447
448 protected void writeHTMLTags(AttributeSet attr) throws IOException {
449
450 int oldMask = fontMask;
451 setFontMask(attr);
452
453 int endMask = 0;
454 int startMask = 0;
455 if ((oldMask & BOLD) != 0) {
456 if ((fontMask & BOLD) == 0) {
457 endMask |= BOLD;
458 }
459 } else if ((fontMask & BOLD) != 0) {
460 startMask |= BOLD;
461 }
462
463 if ((oldMask & ITALIC) != 0) {
464 if ((fontMask & ITALIC) == 0) {
465 endMask |= ITALIC;
466 }
467 } else if ((fontMask & ITALIC) != 0) {
468 startMask |= ITALIC;
469 }
470
471 if ((oldMask & UNDERLINE) != 0) {
472 if ((fontMask & UNDERLINE) == 0) {
473 endMask |= UNDERLINE;
474 }
475 } else if ((fontMask & UNDERLINE) != 0) {
476 startMask |= UNDERLINE;
477 }
478 writeEndMask(endMask);
479 writeStartMask(startMask);
480 }
481
482
483 /**
484 * Tweaks the appropriate bits of fontMask
485 * to reflect whether the text is to be displayed in
486 * bold, italic, and/or with an underline.
487 *
488 */
489 private void setFontMask(AttributeSet attr) {
490 if (StyleConstants.isBold(attr)) {
491 fontMask |= BOLD;
492 }
493
494 if (StyleConstants.isItalic(attr)) {
495 fontMask |= ITALIC;
496 }
497
498 if (StyleConstants.isUnderline(attr)) {
499 fontMask |= UNDERLINE;
500 }
501 }
502
503
504
505
506 /**
507 * Writes out start tags &lt;u&gt;, &lt;i&gt;, and &lt;b&gt; based on
508 * the mask settings.
509 *
510 * @exception IOException on any I/O error
511 */
512 private void writeStartMask(int mask) throws IOException {
513 if (mask != 0) {
514 if ((mask & UNDERLINE) != 0) {
515 write("<u>");
516 }
517 if ((mask & ITALIC) != 0) {
518 write("<i>");
519 }
520 if ((mask & BOLD) != 0) {
521 write("<b>");
522 }
523 }
524 }
525
526 /**
527 * Writes out end tags for &lt;u&gt;, &lt;i&gt;, and &lt;b&gt; based on
528 * the mask settings.
529 *
530 * @exception IOException on any I/O error
531 */
532 private void writeEndMask(int mask) throws IOException {
533 if (mask != 0) {
534 if ((mask & BOLD) != 0) {
535 write("</b>");
536 }
537 if ((mask & ITALIC) != 0) {
538 write("</i>");
539 }
540 if ((mask & UNDERLINE) != 0) {
541 write("</u>");
542 }
543 }
544 }
545
546
547 /**
548 * Writes out the remaining
549 * character-level attributes (attributes other than bold,
550 * italic, and underline) in an HTML-compliant way. Given that
551 * attributes such as font family and font size have no direct
552 * mapping to HTML tags, a &lt;span&gt; tag is generated and its
553 * style attribute is set to contain the list of remaining
554 * attributes just like inline styles.
555 *
556 * @exception IOException on any I/O error
557 */
558 protected void writeNonHTMLAttributes(AttributeSet attr) throws IOException {
559
560 String style = "";
561 String separator = "; ";
562
563 if (inFontTag() && fontAttributes.isEqual(attr)) {
564 return;
565 }
566
567 boolean first = true;
568 Color color = (Color)attr.getAttribute(StyleConstants.Foreground);
569 if (color != null) {
570 style += "color: " + css.styleConstantsValueToCSSValue
571 ((StyleConstants)StyleConstants.Foreground,
572 color);
573 first = false;
574 }
575 Integer size = (Integer)attr.getAttribute(StyleConstants.FontSize);
576 if (size != null) {
577 if (!first) {
578 style += separator;
579 }
580 style += "font-size: " + size.intValue() + "pt";
581 first = false;
582 }
583
584 String family = (String)attr.getAttribute(StyleConstants.FontFamily);
585 if (family != null) {
586 if (!first) {
587 style += separator;
588 }
589 style += "font-family: " + family;
590 first = false;
591 }
592
593 if (style.length() > 0) {
594 if (fontMask != 0) {
595 writeEndMask(fontMask);
596 fontMask = 0;
597 }
598 startSpanTag(style);
599 fontAttributes = attr;
600 }
601 else if (fontAttributes != null) {
602 writeEndMask(fontMask);
603 fontMask = 0;
604 endSpanTag();
605 }
606 }
607
608
609 /**
610 * Returns true if we are currently in a &lt;font&gt; tag.
611 */
612 protected boolean inFontTag() {
613 return (fontAttributes != null);
614 }
615
616 /**
617 * This is no longer used, instead &lt;span&gt; will be written out.
618 * <p>
619 * Writes out an end tag for the &lt;font&gt; tag.
620 *
621 * @exception IOException on any I/O error
622 */
623 protected void endFontTag() throws IOException {
624 write(NEWLINE);
625 writeEndTag("</font>");
626 fontAttributes = null;
627 }
628
629
630 /**
631 * This is no longer used, instead &lt;span&gt; will be written out.
632 * <p>
633 * Writes out a start tag for the &lt;font&gt; tag.
634 * Because font tags cannot be nested,
635 * this method closes out
636 * any enclosing font tag before writing out a
637 * new start tag.
638 *
639 * @exception IOException on any I/O error
640 */
641 protected void startFontTag(String style) throws IOException {
642 boolean callIndent = false;
643 if (inFontTag()) {
644 endFontTag();
645 callIndent = true;
646 }
647 writeStartTag("<font style=\"" + style + "\">");
648 if (callIndent) {
649 indent();
650 }
651 }
652
653 /**
654 * Writes out a start tag for the &lt;font&gt; tag.
655 * Because font tags cannot be nested,
656 * this method closes out
657 * any enclosing font tag before writing out a
658 * new start tag.
659 *
660 * @exception IOException on any I/O error
661 */
662 private void startSpanTag(String style) throws IOException {
663 boolean callIndent = false;
664 if (inFontTag()) {
665 endSpanTag();
666 callIndent = true;
667 }
668 writeStartTag("<span style=\"" + style + "\">");
669 if (callIndent) {
670 indent();
671 }
672 }
673
674 /**
675 * Writes out an end tag for the &lt;span&gt; tag.
676 *
677 * @exception IOException on any I/O error
678 */
679 private void endSpanTag() throws IOException {
680 write(NEWLINE);
681 writeEndTag("</span>");
682 fontAttributes = null;
683 }
684
685 /**
686 * Adds the style named <code>style</code> to the style mapping. This
687 * returns the name that should be used when outputting. CSS does not
688 * allow the full Unicode set to be used as a style name.
689 */
690 private String addStyleName(String style) {
691 if (styleNameMapping == null) {
692 return style;
693 }
694 StringBuilder sb = null;
695 for (int counter = style.length() - 1; counter >= 0; counter--) {
696 if (!isValidCharacter(style.charAt(counter))) {
697 if (sb == null) {
698 sb = new StringBuilder(style);
699 }
700 sb.setCharAt(counter, 'a');
701 }
702 }
703 String mappedName = (sb != null) ? sb.toString() : style;
704 while (styleNameMapping.get(mappedName) != null) {
705 mappedName = mappedName + 'x';
706 }
707 styleNameMapping.put(style, mappedName);
708 return mappedName;
709 }
710
711 /**
712 * Returns the mapped style name corresponding to <code>style</code>.
713 */
714 private String mapStyleName(String style) {
715 if (styleNameMapping == null) {
716 return style;
717 }
718 String retValue = styleNameMapping.get(style);
719 return (retValue == null) ? style : retValue;
720 }
721
722 private boolean isValidCharacter(char character) {
723 return ((character >= 'a' && character <= 'z') ||
724 (character >= 'A' && character <= 'Z'));
725 }
726}