001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: /**
019: * @author Vadim L. Bogdanov
020: * @version $Revision$
021: */package javax.swing.text.html;
022:
023: import java.io.IOException;
024: import java.io.Writer;
025: import java.util.Enumeration;
026: import java.util.Vector;
027:
028: import javax.swing.text.AbstractDocument;
029: import javax.swing.text.AbstractWriter;
030: import javax.swing.text.AttributeSet;
031: import javax.swing.text.BadLocationException;
032: import javax.swing.text.DefaultStyledDocument;
033: import javax.swing.text.Document;
034: import javax.swing.text.Element;
035: import javax.swing.text.SimpleAttributeSet;
036: import javax.swing.text.Style;
037: import javax.swing.text.StyleConstants;
038: import javax.swing.text.StyleContext;
039: import javax.swing.text.StyledDocument;
040:
041: import org.apache.harmony.x.swing.internal.nls.Messages;
042:
043: public class MinimalHTMLWriter extends AbstractWriter {
044: private static final AttributeSet EMPTY_ATTR_SET = new SimpleAttributeSet();
045: private Vector openEmbeddedTags = new Vector();
046: private boolean inFontTag;
047:
048: public MinimalHTMLWriter(final Writer w, final StyledDocument doc,
049: final int pos, final int len) {
050:
051: super (w, doc, pos, len);
052: }
053:
054: public MinimalHTMLWriter(final Writer w, final StyledDocument doc) {
055: super (w, doc);
056: }
057:
058: protected boolean inFontTag() {
059: return inFontTag;
060: }
061:
062: protected boolean isText(final Element elem) {
063: return AbstractDocument.ContentElementName.equals(elem
064: .getName());
065: }
066:
067: protected void startFontTag(final String style) throws IOException {
068: writeStartTag("<font style=\"" + style + "\">");
069: }
070:
071: protected void endFontTag() throws IOException {
072: writeLineSeparator();
073: writeEndTag("</font>");
074: }
075:
076: protected void text(final Element elem) throws IOException,
077: BadLocationException {
078:
079: String content = getText(elem);
080: int textStart = Math.max(getStartOffset(), elem
081: .getStartOffset())
082: - elem.getStartOffset();
083: int textEnd = Math.min(getEndOffset(), elem.getEndOffset())
084: - elem.getStartOffset();
085: if (textEnd > textStart
086: && content.charAt(textEnd - 1) == NEWLINE) {
087: textEnd--;
088: }
089:
090: if (textEnd > textStart) {
091: write(content.toCharArray(), textStart, textEnd - textStart);
092: }
093: }
094:
095: public void write() throws IOException, BadLocationException {
096: writeStartTag("<html>");
097: writeHeader();
098: writeBody();
099: writeEndTag("</html>");
100: }
101:
102: protected void writeAttributes(final AttributeSet attr)
103: throws IOException {
104: for (Enumeration attrs = attr.getAttributeNames(); attrs
105: .hasMoreElements();) {
106:
107: Object a = attrs.nextElement();
108:
109: String attrString = writeAttributeAsCSS(a, attr
110: .getAttribute(a));
111: if (attrString != null) {
112: indent();
113: write(attrString);
114: write(";");
115: writeLineSeparator();
116: }
117: }
118: }
119:
120: protected void writeBody() throws IOException, BadLocationException {
121: writeStartTag("<body>");
122:
123: Element root = getDocument().getDefaultRootElement();
124: for (int i = root.getElementIndex(getStartOffset()); i < root
125: .getElementCount(); i++) {
126:
127: Element e = root.getElement(i);
128: if (!inRange(e)) {
129: break;
130: }
131: writeStartParagraph(e);
132: writeParagraphElements(e);
133: writeEndParagraph();
134: }
135:
136: writeEndTag("</body>");
137: }
138:
139: protected void writeContent(final Element elem,
140: final boolean needsIndenting) throws IOException,
141: BadLocationException {
142:
143: writeNonHTMLAttributes(elem.getAttributes());
144: if (needsIndenting) {
145: indent();
146: }
147: writeHTMLTags(elem.getAttributes());
148: text(elem);
149: }
150:
151: protected void writeStartTag(final String tag) throws IOException {
152: indent();
153: write(tag);
154: writeLineSeparator();
155: incrIndent();
156: }
157:
158: protected void writeEndParagraph() throws IOException {
159: writeEndHTMLTags(EMPTY_ATTR_SET);
160: writeLineSeparator();
161: if (inFontTag()) {
162: writeEndSpan();
163: }
164: writeEndTag("</p>");
165: }
166:
167: protected void writeEndTag(final String endTag) throws IOException {
168: decrIndent();
169: indent();
170: write(endTag);
171: writeLineSeparator();
172: }
173:
174: protected void writeHTMLTags(final AttributeSet attr)
175: throws IOException {
176: if (StyleConstants.isBold(attr)) {
177: writeHTMLTagIfNeeded(HTML.Tag.B);
178: }
179: if (StyleConstants.isItalic(attr)) {
180: writeHTMLTagIfNeeded(HTML.Tag.I);
181: }
182: if (StyleConstants.isUnderline(attr)) {
183: writeHTMLTagIfNeeded(HTML.Tag.U);
184: }
185: }
186:
187: protected void writeHeader() throws IOException {
188: writeStartTag("<head>");
189:
190: writeStartTag("<style type=\"text/css\">");
191: writeStartTag("<!--");
192: writeStyles();
193: writeEndTag("-->");
194: writeEndTag("</style>");
195:
196: writeDocumentTitle();
197:
198: writeEndTag("</head>");
199: }
200:
201: protected void writeImage(final Element elem) throws IOException {
202: indent();
203: }
204:
205: protected void writeComponent(final Element elem)
206: throws IOException {
207: indent();
208: }
209:
210: protected void writeLeaf(final Element elem) throws IOException {
211: if (StyleConstants.IconElementName.equals(elem.getName())) {
212: writeImage(elem);
213: } else if (StyleConstants.ComponentElementName.equals(elem
214: .getName())) {
215: writeComponent(elem);
216: } else {
217: indent();
218: }
219: }
220:
221: protected void writeNonHTMLAttributes(final AttributeSet attr)
222: throws IOException {
223:
224: writeStartSpan(attr);
225: }
226:
227: protected void writeStartParagraph(final Element elem)
228: throws IOException {
229: writeStartTag("<p class=" + getParagraphStyleName(elem) + ">");
230: }
231:
232: protected void writeStyles() throws IOException {
233: if (!(getDocument() instanceof DefaultStyledDocument)) {
234: // XXX: it's not clear what to do in this case
235: throw new UnsupportedOperationException(Messages
236: .getString("swing.9F")); //$NON-NLS-1$
237: }
238:
239: StyledDocument styledDocument = (StyledDocument) getDocument();
240: Enumeration styles = ((DefaultStyledDocument) getDocument())
241: .getStyleNames();
242: while (styles.hasMoreElements()) {
243: String styleName = (String) styles.nextElement();
244: if (StyleSheet.DEFAULT_STYLE.equals(styleName)) {
245: continue;
246: }
247: indent();
248: write("p." + styleName + " {");
249: writeLineSeparator();
250: incrIndent();
251: writeAttributes(styledDocument.getStyle(styleName));
252: decrIndent();
253: indent();
254: write("}");
255: writeLineSeparator();
256: }
257: }
258:
259: private void writeHTMLTagIfNeeded(final HTML.Tag tag)
260: throws IOException {
261: if (!openEmbeddedTags.contains(tag)) {
262: write("<" + tag.toString() + ">");
263: openEmbeddedTags.add(tag);
264: }
265: }
266:
267: private void writeEndHTMLTagIfNeeded(final HTML.Tag tag)
268: throws IOException {
269: if (openEmbeddedTags.contains(tag)) {
270: write("</" + tag.toString() + ">");
271: openEmbeddedTags.remove(tag);
272: }
273: }
274:
275: // HTML tags opening sequence is <b>, <i>, <u>,
276: // so, we have to close it in reverce order
277: private void writeEndHTMLTags(final AttributeSet attr)
278: throws IOException {
279: if (!StyleConstants.isUnderline(attr)) {
280: writeEndHTMLTagIfNeeded(HTML.Tag.U);
281: }
282: if (!StyleConstants.isItalic(attr)) {
283: writeEndHTMLTagIfNeeded(HTML.Tag.I);
284: }
285: if (!StyleConstants.isBold(attr)) {
286: writeEndHTMLTagIfNeeded(HTML.Tag.B);
287: }
288: }
289:
290: private static String getParagraphStyleName(final Element par) {
291: AttributeSet attrs = par.getAttributes();
292: Object style = attrs
293: .getAttribute(StyleConstants.ResolveAttribute);
294: return style instanceof Style ? ((Style) style).getName()
295: : StyleContext.DEFAULT_STYLE;
296: }
297:
298: private void writeStartSpan(final AttributeSet attr)
299: throws IOException {
300: if (inFontTag()) {
301: writeEndSpan();
302: }
303:
304: boolean firstAttr = true;
305: for (Enumeration attrs = attr.getAttributeNames(); attrs
306: .hasMoreElements();) {
307:
308: Object a = attrs.nextElement();
309: if (StyleConstants.Italic.equals(a)
310: || StyleConstants.Bold.equals(a)
311: || StyleConstants.Underline.equals(a)
312: || a instanceof StyleConstants.ParagraphConstants) {
313: continue;
314: }
315: if (!firstAttr) {
316: write("; ");
317: } else {
318: writeEndHTMLTags(EMPTY_ATTR_SET);
319: indent();
320: write("<span style=\"");
321: }
322: String attrString = writeAttributeAsCSS(a, attr
323: .getAttribute(a));
324: if (attrString != null) {
325: write(attrString);
326: firstAttr = false;
327: }
328: }
329:
330: if (!firstAttr) {
331: write("\">");
332: writeLineSeparator();
333: incrIndent();
334: inFontTag = true;
335: } else {
336: writeEndHTMLTags(attr);
337: }
338: }
339:
340: private void writeEndSpan() throws IOException {
341: writeEndHTMLTags(EMPTY_ATTR_SET);
342: if (!isLineEmpty()) {
343: writeLineSeparator();
344: }
345: writeEndTag("</span>");
346: inFontTag = false;
347: }
348:
349: private String writeAttributeAsCSS(final Object attr,
350: final Object value) throws IOException {
351:
352: Object cssAttr = convertToCSSAttribute(attr);
353: if (cssAttr == null) {
354: return null;
355: }
356:
357: Object cssValue = convertToCSSValue(cssAttr, value);
358: return cssAttr.toString() + ": " + cssValue.toString();
359: }
360:
361: private void writeParagraphElements(final Element paragraph)
362: throws IOException, BadLocationException {
363:
364: for (int i = paragraph.getElementIndex(getStartOffset()); i < paragraph
365: .getElementCount(); i++) {
366: Element e = paragraph.getElement(i);
367: if (!inRange(e)) {
368: break;
369: }
370: if (isText(e)) {
371: writeContent(e, isLineEmpty());
372: } else {
373: writeLeaf(e);
374: }
375: }
376: }
377:
378: private static Object convertToCSSAttribute(final Object attr) {
379: return CSS.mapToCSSForced(attr);
380: }
381:
382: private static Object convertToCSSValue(final Object cssAttr,
383: final Object value) {
384: return cssAttr instanceof CSS.Attribute ? ((CSS.Attribute) cssAttr)
385: .getConverter().toCSS(value)
386: : value;
387: }
388:
389: // TODO: the same method exists in HTMLWriter
390: private void writeDocumentTitle() throws IOException {
391: Object title = getDocument()
392: .getProperty(Document.TitleProperty);
393: if (title == null) {
394: return;
395: }
396:
397: indent();
398: write("<title>");
399: write(title.toString());
400: write("</title>");
401: writeLineSeparator();
402: }
403: }
|