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+
26+package javax.swing.text.html;
27+
28+import java.io.Writer;
29+import java.io.IOException;
30+import java.util.*;
31+import java.awt.Color;
32+import 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+
68+public 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+}