harmony-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From mloe...@apache.org
Subject svn commit: r427121 [7/29] - in /incubator/harmony/enhanced/classlib/trunk/modules/swing: make/ src/main/java/common/javax/swing/ src/main/java/common/javax/swing/text/ src/main/java/common/javax/swing/text/html/ src/main/java/common/javax/swing/text/h...
Date Mon, 31 Jul 2006 14:08:55 GMT
Added: incubator/harmony/enhanced/classlib/trunk/modules/swing/src/main/java/common/javax/swing/text/html/HTMLEditorKit.java
URL: http://svn.apache.org/viewvc/incubator/harmony/enhanced/classlib/trunk/modules/swing/src/main/java/common/javax/swing/text/html/HTMLEditorKit.java?rev=427121&view=auto
==============================================================================
--- incubator/harmony/enhanced/classlib/trunk/modules/swing/src/main/java/common/javax/swing/text/html/HTMLEditorKit.java (added)
+++ incubator/harmony/enhanced/classlib/trunk/modules/swing/src/main/java/common/javax/swing/text/html/HTMLEditorKit.java Mon Jul 31 07:08:47 2006
@@ -0,0 +1,873 @@
+/*
+ *  Copyright 2005 - 2006 The Apache Software Software Foundation or its licensors, as applicable.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+/**
+* @author Vadim L. Bogdanov, Alexander T. Simbirtsev
+* @version $Revision$
+*/
+package javax.swing.text.html;
+
+import java.awt.Color;
+import java.awt.Cursor;
+import java.awt.Graphics;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.Shape;
+import java.awt.event.ActionEvent;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseMotionListener;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.Serializable;
+import java.io.StringReader;
+import java.io.Writer;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleContext;
+import javax.swing.Action;
+import javax.swing.JEditorPane;
+import javax.swing.event.HyperlinkEvent;
+import javax.swing.text.AttributeSet;
+import javax.swing.text.BadLocationException;
+import javax.swing.text.Caret;
+import javax.swing.text.DefaultHighlighter;
+import javax.swing.text.Document;
+import javax.swing.text.EditorKit;
+import javax.swing.text.Element;
+import javax.swing.text.Highlighter;
+import javax.swing.text.JTextComponent;
+import javax.swing.text.MutableAttributeSet;
+import javax.swing.text.Position;
+import javax.swing.text.StyleConstants;
+import javax.swing.text.StyledEditorKit;
+import javax.swing.text.View;
+import javax.swing.text.ViewFactory;
+import javax.swing.text.Position.Bias;
+import javax.swing.text.html.parser.ParserDelegator;
+
+import org.apache.harmony.awt.text.TextUtils;
+import org.apache.harmony.x.swing.StringConstants;
+
+public class HTMLEditorKit extends StyledEditorKit implements Accessible {
+    public static class HTMLFactory implements ViewFactory {
+
+        public View create(final Element elem) {
+            HTML.Tag tag = getHTMLTagByElement(elem);
+
+            if (HTML.Tag.CONTENT.equals(tag)) {
+                return new InlineView(elem);
+
+            } else if (HTML.Tag.IMPLIED.equals(tag)
+                    || HTML.Tag.P.equals(tag)
+                    || HTML.Tag.H1.equals(tag)
+                    || HTML.Tag.H2.equals(tag)
+                    || HTML.Tag.H3.equals(tag)
+                    || HTML.Tag.H4.equals(tag)
+                    || HTML.Tag.H5.equals(tag)
+                    || HTML.Tag.H6.equals(tag)
+                    || HTML.Tag.DT.equals(tag)
+                    ) {
+                return new ParagraphView(elem);
+
+            } else if (HTML.Tag.MENU.equals(tag)
+                    || HTML.Tag.DIR.equals(tag)
+                    || HTML.Tag.UL.equals(tag)
+                    || HTML.Tag.OL.equals(tag)) {
+                return new ListView(elem);
+
+            } else if (HTML.Tag.LI.equals(tag)
+                    || HTML.Tag.DL.equals(tag)
+                    || HTML.Tag.DD.equals(tag)
+                    || HTML.Tag.BODY.equals(tag)
+                    || HTML.Tag.HTML.equals(tag)
+                    || HTML.Tag.CENTER.equals(tag)
+                    || HTML.Tag.DIV.equals(tag)
+                    || HTML.Tag.BLOCKQUOTE.equals(tag)
+                    || HTML.Tag.PRE.equals(tag)) {
+                return new BlockView(elem, View.Y_AXIS);
+
+            } else if (HTML.Tag.IMG.equals(tag)) {
+                return new ImageView(elem);
+
+            } else if (HTML.Tag.HR.equals(tag)) {
+                return new HRuleTagView(elem);
+
+            } else if (HTML.Tag.BR.equals(tag)) {
+                return new BRView(elem);
+
+            } else if (HTML.Tag.TABLE.equals(tag)) {
+                return new TableTagView(elem);
+
+            } else if (HTML.Tag.FORM.equals(tag)) {
+                return new BlockView(elem, View.X_AXIS);
+
+            } else if (HTML.Tag.INPUT.equals(tag)) {
+                return new FormView(elem);
+
+            } else if (HTML.Tag.SELECT.equals(tag)
+                    || HTML.Tag.TEXTAREA.equals(tag)) {
+                return new FormView(elem);
+
+            } else if (HTML.Tag.OBJECT.equals(tag)) {
+                return new ObjectView(elem);
+
+            } else if (HTML.Tag.FRAMESET.equals(tag)) {
+                return new FrameSetTagView(elem);
+
+            } else if (HTML.Tag.FRAME.equals(tag)) {
+                return new FrameTagView(elem);
+
+            } else if (HTML.Tag.NOFRAMES.equals(tag)) {
+                return new NoFramesTagView(elem);
+
+            } else if (HTML.Tag.HEAD.equals(tag)) {
+                return new HeadTagView(elem);
+            }
+
+            // TODO: uncomment the next line and remove throw
+            // when all tags are supported
+//            return new LabelView(elem);
+            throw new UnsupportedOperationException("Not implemented tag: " + tag);
+        }
+    }
+
+    public abstract static class HTMLTextAction extends StyledTextAction {
+        public HTMLTextAction(final String name) {
+            super(name);
+        }
+
+        protected int elementCountToTag(final HTMLDocument doc,
+                                        final int offset,
+                                        final HTML.Tag tag) {
+            int count = -1;
+            Element e;
+            for (e = doc.getCharacterElement(offset);
+                 e != null && !tag.equals(getHTMLTagByElement(e));
+                 e = e.getParentElement()) {
+                count++;
+            }
+            if (e == null) {
+                return -1;
+            }
+            return count;
+        }
+
+        protected Element findElementMatchingTag(final HTMLDocument doc,
+                                                 final int offset,
+                                                 final HTML.Tag tag) {
+            Element e = doc.getCharacterElement(offset);
+            while (e != null && !tag.equals(getHTMLTagByElement(e))) {
+                e = e.getParentElement();
+            }
+            return e;
+        }
+
+        protected Element[] getElementsAt(final HTMLDocument doc,
+                                          final int offset) {
+            ArrayList list = new ArrayList();
+            Element e = doc.getDefaultRootElement();
+            while (true) {
+                list.add(e);
+                if (e.getElementCount() == 0) {
+                    break;
+                }
+                e = e.getElement(e.getElementIndex(offset));
+            }
+
+            return (Element[])list.toArray(new Element[list.size()]);
+        }
+
+        protected HTMLDocument getHTMLDocument(final JEditorPane pane) {
+            Document doc = pane.getDocument();
+            if (doc instanceof HTMLDocument) {
+                return (HTMLDocument)doc;
+            }
+            throw new IllegalArgumentException("Document must be HTMLDocument");
+        }
+
+        protected HTMLEditorKit getHTMLEditorKit(final JEditorPane pane) {
+            EditorKit editorKit = pane.getEditorKit();
+            if (editorKit instanceof HTMLEditorKit) {
+                return (HTMLEditorKit)editorKit;
+            }
+            throw new IllegalArgumentException("Editor kit must be HTMLEditorKit");
+        }
+    }
+
+    public static class InsertHTMLTextAction extends HTMLTextAction {
+        protected HTML.Tag addTag;
+        protected HTML.Tag alternateAddTag;
+        protected HTML.Tag alternateParentTag;
+        protected String html;
+        protected HTML.Tag parentTag;
+
+        public InsertHTMLTextAction(final String name, final String html,
+                                    final HTML.Tag parentTag,
+                                    final HTML.Tag addTag,
+                                    final HTML.Tag alternateParentTag,
+                                    final HTML.Tag alternateAddTag) {
+            super(name);
+            this.html = html;
+            this.parentTag = parentTag;
+            this.addTag = addTag;
+            this.alternateParentTag = alternateParentTag;
+            this.alternateAddTag = alternateAddTag;
+        }
+
+        public InsertHTMLTextAction(final String name, final String html,
+                                    final HTML.Tag parentTag,
+                                    final HTML.Tag addTag) {
+            super(name);
+            this.html = html;
+            this.parentTag = parentTag;
+            this.addTag = addTag;
+        }
+
+        public void actionPerformed(final ActionEvent event) {
+            if (event == null) {
+                return;
+            }
+
+            JEditorPane editor = getEditor(event);
+            HTMLDocument doc = getHTMLDocument(editor);
+            int offset = editor.getCaretPosition();
+
+            HTML.Tag usedParentTag = parentTag;
+            HTML.Tag usedAddTag = addTag;
+            int popDepth = elementCountToTag(doc, offset, parentTag);
+            if (popDepth == -1 && alternateParentTag != null) {
+                usedParentTag = alternateParentTag;
+                usedAddTag = alternateAddTag;
+                popDepth = elementCountToTag(doc, offset, alternateParentTag);
+            }
+            if (popDepth == -1) {
+                return;
+            }
+
+            Element insertElement = findElementMatchingTag(doc, offset,
+                                                           usedParentTag);
+            if (insertElement.getStartOffset() == offset) {
+                insertAtBoundary(editor, doc, offset, insertElement, html,
+                                 usedParentTag, usedAddTag);
+            } else {
+                int pushDepth = 0;
+                insertHTML(editor, doc, offset, html,
+                           popDepth, pushDepth, usedAddTag);
+            }
+        }
+
+        protected void insertHTML(final JEditorPane editor,
+                                  final HTMLDocument doc, final int offset,
+                                  final String html, final int popDepth,
+                                  final int pushDepth, final HTML.Tag addTag) {
+            HTMLEditorKit editorKit = getHTMLEditorKit(editor);
+            try {
+                editorKit.insertHTML(doc, offset, html,
+                                     popDepth, pushDepth, addTag);
+            } catch (BadLocationException e) {
+                throw new RuntimeException(e);
+            } catch (IOException e) {
+                throw new RuntimeException(e);
+            }
+        }
+
+        protected void insertAtBoundary(final JEditorPane editor,
+                                        final HTMLDocument doc,
+                                        final int offset,
+                                        final Element insertElement,
+                                        final String html,
+                                        final HTML.Tag parentTag,
+                                        final HTML.Tag addTag) {
+            insertAtBoundaryImpl(editor, doc, offset, insertElement, html,
+                                 parentTag, addTag);
+        }
+
+        /**
+         * @deprecated
+         */
+        protected void insertAtBoundry(final JEditorPane editor,
+                                       final HTMLDocument doc,
+                                       final int offset,
+                                       final Element insertElement,
+                                       final String html,
+                                       final HTML.Tag parentTag,
+                                       final HTML.Tag addTag) {
+            insertAtBoundaryImpl(editor, doc, offset, insertElement, html,
+                                 parentTag, addTag);
+        }
+
+        private void insertAtBoundaryImpl(final JEditorPane editor,
+                                          final HTMLDocument doc,
+                                          final int offset,
+                                          final Element insertElement,
+                                          final String html,
+                                          final HTML.Tag parentTag,
+                                          final HTML.Tag addTag) {
+            int popDepth = elementCountToTag(doc, offset, parentTag) + 1;
+            int pushDepth = 1;
+            insertHTML(editor, doc, offset, html, popDepth, pushDepth, addTag);
+        }
+    }
+
+    public static class LinkController extends MouseAdapter
+            implements MouseMotionListener, Serializable {
+
+        private Element prevLinkUnderMouse;
+
+        public void mouseClicked(final MouseEvent e) {
+            JEditorPane pane = (JEditorPane)e.getSource();
+            if (pane.isEditable()) {
+                return;
+            }
+
+            int pos = pane.viewToModel(e.getPoint());
+            activateLink(pos, pane);
+        }
+
+        public void mouseDragged(final MouseEvent e) {
+            // does nothing
+        }
+
+        public void mouseMoved(final MouseEvent e) {
+            JEditorPane pane = (JEditorPane)e.getSource();
+            Element linkElement = getLinkElement(pane, e.getPoint());
+            updateMouseCursor(e, linkElement);
+
+            if (pane.isEditable()) {
+                return;
+            }
+
+            fireHyperlinkEvent(e, linkElement);
+        }
+
+        protected void activateLink(final int pos, final JEditorPane editor) {
+            Element elem = HTMLEditorKit.getLinkElement(editor, pos);
+            if (elem != null) {
+                HTMLEditorKit.fireHyperlinkEvent(editor,
+                        HyperlinkEvent.EventType.ACTIVATED, elem);
+            }
+        }
+
+        private void updateMouseCursor(final MouseEvent e,
+                                       final Element linkElement) {
+            JEditorPane pane = (JEditorPane)e.getSource();
+            HTMLEditorKit editorKit = (HTMLEditorKit)pane.getEditorKit();
+            if (!pane.isEditable() && linkElement != null) {
+                e.getComponent().setCursor(editorKit.getLinkCursor());
+            } else {
+                e.getComponent().setCursor(editorKit.getDefaultCursor());
+            }
+        }
+
+        private void fireHyperlinkEvent(final MouseEvent e,
+                                        final Element linkElement) {
+            if (prevLinkUnderMouse == linkElement) {
+                return;
+            }
+
+            JEditorPane pane = (JEditorPane)e.getSource();
+            if (prevLinkUnderMouse != null) {
+                HTMLEditorKit.fireHyperlinkEvent(pane,
+                        HyperlinkEvent.EventType.EXITED, prevLinkUnderMouse);
+            }
+            if (linkElement != null) {
+                HTMLEditorKit.fireHyperlinkEvent(pane,
+                        HyperlinkEvent.EventType.ENTERED, linkElement);
+            }
+            prevLinkUnderMouse = linkElement;
+        }
+
+        private Element getLinkElement(final JEditorPane pane, final Point p) {
+            return HTMLEditorKit.getLinkElement(pane, pane.viewToModel(p));
+        }
+    }
+
+    public abstract static class Parser {
+        public abstract void parse(final Reader r, final ParserCallback cb,
+                                   final boolean ignoreCharSet)
+                throws IOException;
+    }
+
+    public static class ParserCallback {
+        public static final Object IMPLIED = "_implied_";
+
+        public void flush() throws BadLocationException {
+        }
+
+        public void handleComment(final char[] data, final int pos) {
+        }
+
+        public void handleEndOfLineString(final String eol) {
+        }
+
+        public void handleEndTag(final HTML.Tag tag, final int pos) {
+        }
+
+        public void handleError(final String errorMsg, final int pos) {
+        }
+
+        public void handleSimpleTag(final HTML.Tag tag,
+                                    final MutableAttributeSet attr,
+                                    final int pos) {
+        }
+
+        public void handleStartTag(final HTML.Tag tag,
+                                   final MutableAttributeSet attr,
+                                   final int pos) {
+        }
+
+        public void handleText(final char[] data, final int pos) {
+        }
+    }
+
+    private static class NavigateLinkAction extends HTMLTextAction {
+        private static final HashMap highlightTags = new HashMap();
+
+        public NavigateLinkAction(final String name) {
+            super(name);
+        }
+
+        public void actionPerformed(final ActionEvent e) {
+            if (getEditor(e).isEditable()) {
+                return;
+            }
+
+            JEditorPane editor = getEditor(e);
+            Caret caret = editor.getCaret();
+            HTMLDocument doc = getHTMLDocument(editor);
+
+            Element link = getNextLinkElement(doc, caret.getDot(), isForward());
+            if (link != null) {
+                moveHighlight(editor,
+                              link.getStartOffset(), link.getEndOffset());
+            }
+        }
+
+        private boolean isForward() {
+            return "next-link-action".equals(getValue(NAME));
+        }
+
+        private static Element getNextLinkElement(final HTMLDocument doc,
+                                                  final int pos,
+                                                  final boolean forward) {
+            HTMLDocument.Iterator it = doc.getIterator(HTML.Tag.A);
+            int linkPos = -1;
+            for (; it.isValid(); it.next()) {
+                if (forward) {
+                    if (it.getStartOffset() > pos) {
+                        linkPos = it.getStartOffset();
+                        break;
+                    }
+                } else {
+                    if (it.getStartOffset() >= pos) {
+                        break;
+                    }
+                    linkPos = it.getStartOffset();
+                }
+            }
+
+            return linkPos != -1 ? doc.getCharacterElement(linkPos) : null;
+        }
+
+        private static void moveHighlight(final JEditorPane editor,
+                                          final int start, final int end) {
+            Highlighter highlighter = editor.getHighlighter();
+            Object tag = highlightTags.get(highlighter);
+            if (tag != null) {
+                highlighter.removeHighlight(tag);
+                highlightTags.remove(highlighter);
+            }
+            try {
+                tag = highlighter.addHighlight(start, end,
+                                               new LinkHighlightPainter());
+                highlightTags.put(highlighter, tag);
+                editor.getCaret().setDot(start);
+            } catch (final BadLocationException e) {
+            }
+        }
+
+        static void removeHighlight(final JEditorPane editor) {
+            Highlighter highlighter = editor.getHighlighter();
+            Object tag = highlightTags.get(highlighter);
+            if (tag != null) {
+                highlighter.removeHighlight(tag);
+                highlightTags.remove(highlighter);
+            }
+        }
+    }
+
+    private static class ActivateLinkAction extends HTMLTextAction {
+        public ActivateLinkAction() {
+            super("activate-link-action");
+        }
+
+        public void actionPerformed(final ActionEvent e) {
+            JEditorPane editor = getEditor(e);
+            if (!editor.isEditable()) {
+                activateLink(editor.getCaretPosition(), editor);
+            }
+        }
+    }
+
+    private static class LinkHighlightPainter
+            extends DefaultHighlighter.DefaultHighlightPainter {
+        public LinkHighlightPainter() {
+            super(Color.RED);
+        }
+
+        public Shape paintLayer(final Graphics g, final int p0, final int p1,
+                                final Shape shape, final JTextComponent jtc,
+                                final View view) {
+            return TextUtils.paintLayer(g, p0, p1, shape,
+                                        jtc.getSelectionColor(), view, false);
+        }
+    }
+
+    private static class HeadTagView extends View {
+        public HeadTagView(final Element elem) {
+            super(elem);
+        }
+
+        public float getPreferredSpan(final int axis) {
+            return 0.0f;
+        }
+
+        public int viewToModel(final float x, final float y,
+                               final Shape a, final Bias[] biasRet) {
+            return 0;
+        }
+
+        public void paint(final Graphics g, final Shape allocation) {
+        }
+
+        public Shape modelToView(final int pos, final Shape a,
+                                 final Bias b) throws BadLocationException {
+            return new Rectangle(0, 0);
+        }
+
+        public int getNextVisualPositionFrom(final int pos, final Bias b,
+                final Shape a, final int direction,
+                final Bias[] biasRet) throws BadLocationException {
+            if (direction != NORTH && direction != SOUTH
+                    && direction != EAST && direction != WEST) {
+                throw new IllegalArgumentException("Invalid direction");
+            }
+            biasRet[0] = Position.Bias.Forward;
+            return getEndOffset();
+        }
+    }
+
+    public static final String BOLD_ACTION = "html-bold-action";
+    public static final String COLOR_ACTION = "html-color-action";
+    public static final String FONT_CHANGE_BIGGER = "html-font-bigger";
+    public static final String FONT_CHANGE_SMALLER = "html-font-smaller";
+    public static final String IMG_ALIGN_BOTTOM = "html-image-align-bottom";
+    public static final String IMG_ALIGN_MIDDLE = "html-image-align-middle";
+    public static final String IMG_ALIGN_TOP = "html-image-align-top";
+    public static final String IMG_BORDER = "html-image-border";
+    public static final String ITALIC_ACTION = "html-italic-action";
+    public static final String LOGICAL_STYLE_ACTION = "html-logical-style-action";
+    public static final String PARA_INDENT_LEFT = "html-para-indent-left";
+    public static final String PARA_INDENT_RIGHT = "html-para-indent-right";
+
+    public static final String DEFAULT_CSS = "default.css";
+
+    private static StyleSheet styleSheet;
+    private static Parser parser;
+    private static ViewFactory viewFactory;
+    private static final LinkController linkController = new LinkController();
+    private static Action[] actions;
+
+    private Cursor defaultCursor = Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR);
+    private Cursor linkCursor = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR);
+    private boolean autoFormSubmission = true;
+
+    public HTMLEditorKit() {
+        if (actions == null) {
+            createStaticActions();
+        }
+    }
+
+    public Object clone() {
+        return super.clone();
+    }
+
+    public Document createDefaultDocument() {
+        HTMLDocument document = new HTMLDocument();
+        document.getStyleSheet().addStyleSheet(getStyleSheet());
+        document.setParser(getParser());
+        document.setAsynchronousLoadPriority(4);
+        return document;
+    }
+
+    public AccessibleContext getAccessibleContext() {
+        // TODO: implement
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    public Action[] getActions() {
+        return (Action[])actions.clone();
+    }
+
+    public String getContentType() {
+        return "text/html";
+    }
+
+    public MutableAttributeSet getInputAttributes() {
+        return super.getInputAttributes();
+    }
+
+    public ViewFactory getViewFactory() {
+        if (viewFactory == null) {
+            viewFactory = new HTMLFactory();
+        }
+        return viewFactory;
+    }
+
+    public void install(final JEditorPane pane) {
+        super.install(pane);
+
+        pane.addMouseListener(linkController);
+        pane.addMouseMotionListener(linkController);
+    }
+
+    public void deinstall(final JEditorPane pane) {
+        pane.removeMouseListener(linkController);
+        pane.removeMouseMotionListener(linkController);
+        NavigateLinkAction.removeHighlight(pane);
+
+        super.deinstall(pane);
+    }
+
+    public void setAutoFormSubmission(final boolean auto) {
+        autoFormSubmission = auto;
+    }
+
+    public boolean isAutoFormSubmission() {
+        return autoFormSubmission;
+    }
+
+    public void setDefaultCursor(final Cursor cursor) {
+        defaultCursor = cursor;
+    }
+
+    public Cursor getDefaultCursor() {
+        return defaultCursor;
+    }
+
+    public void setLinkCursor(final Cursor cursor) {
+        linkCursor = cursor;
+    }
+
+    public Cursor getLinkCursor() {
+        return linkCursor;
+    }
+
+    public void setStyleSheet(final StyleSheet ss) {
+        styleSheet = ss;
+    }
+
+    public StyleSheet getStyleSheet() {
+        if (styleSheet == null) {
+            styleSheet = new StyleSheet();
+            URL url = HTMLEditorKit.class.getResource(DEFAULT_CSS);
+            try {
+                styleSheet.loadRules(new BufferedReader(
+                    new InputStreamReader(url.openStream())), url);
+            } catch (final IOException e) {
+                e.printStackTrace();
+            }
+        }
+
+        return styleSheet;
+    }
+
+    public void insertHTML(final HTMLDocument doc, final int offset,
+                           final String html, final int popDepth,
+                           final int pushDepth, final HTML.Tag insertTag)
+            throws BadLocationException, IOException {
+        if (offset > doc.getLength()) {
+            throw new BadLocationException("Invalid position", offset);
+        }
+
+        ParserCallback htmlReader = doc.getReader(offset, popDepth,
+                                                  pushDepth, insertTag);
+        StringReader in = new StringReader(html);
+
+        getParser().parse(in, htmlReader, false);
+        htmlReader.flush();
+        in.close();
+    }
+
+    public void read(final Reader in, final Document doc, final int pos)
+            throws IOException, BadLocationException {
+        if (!(doc instanceof HTMLDocument)) {
+            super.read(in, doc, pos);
+            return;
+        }
+
+        HTMLDocument htmlDoc = (HTMLDocument)doc;
+        checkReadPosition(htmlDoc, pos);
+
+        ParserCallback htmlReader = htmlDoc.getReader(pos);
+        Object property = doc.getProperty(StringConstants
+                                          .IGNORE_CHARSET_DIRECTIVE);
+        getParser().parse(in, htmlReader, property == null ? false
+                          : ((Boolean)property).booleanValue());
+        htmlReader.flush();
+        in.close();
+    }
+
+    public void write(final Writer out, final Document doc, final int pos,
+                      final int len) throws IOException, BadLocationException {
+        HTMLDocument htmlDoc;
+        int fixedPos = pos;
+
+        if (doc instanceof HTMLDocument) {
+            htmlDoc = (HTMLDocument)doc;
+        } else {
+            htmlDoc = (HTMLDocument)createDefaultDocument();
+            htmlDoc.insertString(0, doc.getText(pos, len), null);
+            fixedPos = 0;
+        }
+
+        HTMLWriter writer = new HTMLWriter(out, htmlDoc, fixedPos, len);
+        writer.write();
+    }
+
+    protected Parser getParser() {
+        if (parser == null) {
+            parser = new ParserDelegator();
+        }
+
+        return parser;
+    }
+
+    protected void createInputAttributes(final Element elem,
+                                         final MutableAttributeSet set) {
+        super.createInputAttributes(elem, set);
+    }
+
+    static HTML.Tag getHTMLTagByElement(final Element elem) {
+        final Object result = elem.getAttributes().getAttribute(StyleConstants.NameAttribute);
+        return (result instanceof HTML.Tag) ? (HTML.Tag)result : null;
+    }
+
+    private static String getURLString(final Element e) {
+        AttributeSet aSet = (AttributeSet)e.getAttributes()
+                .getAttribute(HTML.Tag.A);
+        return aSet == null
+                ? null
+                : (String)aSet.getAttribute(HTML.Attribute.HREF);
+    }
+
+    private static void checkReadPosition(final HTMLDocument doc, final int pos)
+            throws BadLocationException {
+        if (pos < 0) {
+            throw new RuntimeException("Position cannot be negative");
+        }
+        if (pos > doc.getLength()) {
+            throw new BadLocationException("Invalid position", pos);
+        }
+        if (doc.getLength() != 0) {
+            Element body = doc.getElement(doc.getDefaultRootElement(),
+                                          StyleConstants.NameAttribute,
+                                          HTML.Tag.BODY);
+            if (pos < body.getStartOffset() || pos > body.getEndOffset()) {
+                throw new RuntimeException("Must insert inside body element");
+            }
+        }
+    }
+
+    private Action[] createStaticActions() {
+        if (actions == null) {
+            Action[] htmlActions = getDefaultActions();
+            Action[] styledActions = super.getActions();
+            actions = new Action[htmlActions.length + styledActions.length];
+            System.arraycopy(styledActions, 0, actions, 0, styledActions.length);
+            System.arraycopy(htmlActions, 0, actions, styledActions.length,
+                             htmlActions.length);
+        }
+        return actions;
+    }
+
+    private Action[] getDefaultActions() {
+        return new Action[] {
+            new InsertHTMLTextAction("InsertOrderedList", "<ol><li></li></ol>",
+                HTML.Tag.BODY, HTML.Tag.OL),
+            new InsertHTMLTextAction("InsertOrderedListItem", "<ol><li></li></ol>",
+                HTML.Tag.OL, HTML.Tag.LI, HTML.Tag.BODY, HTML.Tag.OL),
+            new InsertHTMLTextAction("InsertUnorderedList", "<ul><li></li></ul>",
+                HTML.Tag.BODY, HTML.Tag.UL),
+            new InsertHTMLTextAction("InsertUnorderedListItem", "<ul><li></li></ul>",
+                HTML.Tag.UL, HTML.Tag.LI, HTML.Tag.BODY, HTML.Tag.UL),
+            new InsertHTMLTextAction("InsertTable",
+                                     "<table border=1><tr><td></td></tr></table>",
+                                     HTML.Tag.BODY, HTML.Tag.TABLE),
+            new InsertHTMLTextAction("InsertTableDataCell",
+                                     "<table border=1><tr><td></td></tr></table>",
+                                     HTML.Tag.TR, HTML.Tag.TD,
+                                     HTML.Tag.BODY, HTML.Tag.TABLE),
+            new InsertHTMLTextAction("InsertTableRow",
+                                     "<table border=1><tr><td></td></tr></table>",
+                                     HTML.Tag.TABLE, HTML.Tag.TR,
+                                     HTML.Tag.BODY, HTML.Tag.TABLE),
+            new InsertHTMLTextAction("InsertPre", "<pre></pre>",
+                                     HTML.Tag.BODY, HTML.Tag.PRE),
+            new InsertHTMLTextAction("InsertHR", "<hr>", HTML.Tag.P, HTML.Tag.HR),
+            new NavigateLinkAction("next-link-action"),
+            new NavigateLinkAction("previous-link-action"),
+            new ActivateLinkAction()
+        };
+    }
+
+    private static void activateLink(final int pos, final JEditorPane editor) {
+        Element elem = getLinkElement(editor, pos);
+        if (elem != null) {
+            fireHyperlinkEvent(editor, HyperlinkEvent.EventType.ACTIVATED,
+                               elem);
+        }
+    }
+
+    private static void fireHyperlinkEvent(final JEditorPane pane,
+                                           final HyperlinkEvent.EventType eventID,
+                                           final Element elem) {
+        String urlString = getURLString(elem);
+
+        URL base = ((HTMLDocument)pane.getDocument()).getBase();
+        HyperlinkEvent event = new HyperlinkEvent(pane, eventID,
+                                                  HTML.resolveURL(urlString,
+                                                                  base),
+                                                  urlString, elem);
+        pane.fireHyperlinkUpdate(event);
+    }
+
+    private static Element getLinkElement(final JEditorPane pane, final int pos) {
+        Element e = (((HTMLDocument)pane.getDocument()).getCharacterElement(pos));
+        for (; e != null; e = e.getParentElement()) {
+            if (getURLString(e) != null) {
+                return e;
+            }
+        }
+
+        return null;
+    }
+}

Added: incubator/harmony/enhanced/classlib/trunk/modules/swing/src/main/java/common/javax/swing/text/html/HTMLFrameHyperlinkEvent.java
URL: http://svn.apache.org/viewvc/incubator/harmony/enhanced/classlib/trunk/modules/swing/src/main/java/common/javax/swing/text/html/HTMLFrameHyperlinkEvent.java?rev=427121&view=auto
==============================================================================
--- incubator/harmony/enhanced/classlib/trunk/modules/swing/src/main/java/common/javax/swing/text/html/HTMLFrameHyperlinkEvent.java (added)
+++ incubator/harmony/enhanced/classlib/trunk/modules/swing/src/main/java/common/javax/swing/text/html/HTMLFrameHyperlinkEvent.java Mon Jul 31 07:08:47 2006
@@ -0,0 +1,59 @@
+/*
+ *  Copyright 2005 - 2006 The Apache Software Software Foundation or its licensors, as applicable.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+/**
+ * @author Alexander T. Simbirtsev
+ * @version $Revision$
+ */
+package javax.swing.text.html;
+
+import java.net.URL;
+import javax.swing.event.HyperlinkEvent;
+import javax.swing.text.Element;
+
+public class HTMLFrameHyperlinkEvent extends HyperlinkEvent {
+
+    private final String target;
+
+    public HTMLFrameHyperlinkEvent(final Object source, final EventType type,
+                                   final URL targetURL,
+                                   final Element sourceElement,
+                                   final String targetFrame) {
+        this(source, type, targetURL, null, sourceElement, targetFrame);
+    }
+
+    public HTMLFrameHyperlinkEvent(final Object source, final EventType type,
+                                   final URL targetURL, final String targetFrame) {
+        this(source, type, targetURL, null, null, targetFrame);
+    }
+
+    public HTMLFrameHyperlinkEvent(final Object source, final EventType type,
+                                   final URL targetURL, final String desc,
+                                   final String targetFrame) {
+        this(source, type, targetURL, desc, null, targetFrame);
+    }
+
+    public HTMLFrameHyperlinkEvent(final Object source, final EventType type,
+                                   final URL targetURL, final String desc,
+                                   final Element sourceElement,
+                                   final String targetFrame) {
+        super(source, type, targetURL, desc, sourceElement);
+        target = targetFrame;
+    }
+
+    public String getTarget() {
+        return target;
+    }
+}

Added: incubator/harmony/enhanced/classlib/trunk/modules/swing/src/main/java/common/javax/swing/text/html/HTMLWriter.java
URL: http://svn.apache.org/viewvc/incubator/harmony/enhanced/classlib/trunk/modules/swing/src/main/java/common/javax/swing/text/html/HTMLWriter.java?rev=427121&view=auto
==============================================================================
--- incubator/harmony/enhanced/classlib/trunk/modules/swing/src/main/java/common/javax/swing/text/html/HTMLWriter.java (added)
+++ incubator/harmony/enhanced/classlib/trunk/modules/swing/src/main/java/common/javax/swing/text/html/HTMLWriter.java Mon Jul 31 07:08:47 2006
@@ -0,0 +1,560 @@
+/*
+ *  Copyright 2005 - 2006 The Apache Software Software Foundation or its licensors, as applicable.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/**
+ * @author Vadim L. Bogdanov
+ * @version $Revision$
+ */
+package javax.swing.text.html;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.io.Writer;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.Stack;
+import java.util.Vector;
+
+import javax.swing.text.AbstractWriter;
+import javax.swing.text.AttributeSet;
+import javax.swing.text.BadLocationException;
+import javax.swing.text.Document;
+import javax.swing.text.Element;
+import javax.swing.text.ElementIterator;
+import javax.swing.text.Style;
+import javax.swing.text.StyleConstants;
+import javax.swing.text.html.parser.DTD;
+import javax.swing.text.html.parser.Entity;
+
+import org.apache.harmony.x.swing.text.html.form.FormSelectModel;
+import org.apache.harmony.x.swing.text.html.form.FormTextModel;
+import org.apache.harmony.x.swing.text.html.form.FormOptionGroup;
+
+public class HTMLWriter extends AbstractWriter {
+    private static final int DEFAULT_LINE_LENGHT = 80;
+    private static DTD dtd;
+
+    private boolean writingContent;
+    private boolean indentEmptyTag = true;
+    private boolean preformatted;
+    private boolean toWriteHead = true;
+    private Stack elemsStack;
+    private Vector openEmbeddedTags = new Vector();
+    private boolean isOptionGroupOpen;
+
+    public HTMLWriter(final Writer w, final HTMLDocument doc) {
+        super(w, doc);
+        setLineLength(DEFAULT_LINE_LENGHT);
+    }
+
+    public HTMLWriter(final Writer w, final HTMLDocument doc,
+                      final int pos, final int len) {
+        super(w, doc, pos, len);
+        setLineLength(DEFAULT_LINE_LENGHT);
+    }
+
+    public void write() throws IOException, BadLocationException {
+        if (elemsStack == null) {
+            elemsStack = new Stack();
+        }
+        ElementIterator it = getElementIterator();
+
+        Element e;
+        while ((e = it.next()) != null) {
+            if (!elemsStack.isEmpty()) {
+                while (!elemsStack.isEmpty()
+                        && elemsStack.peek() != e.getParentElement()) {
+                    if (!synthesizedElement((Element)elemsStack.peek())) {
+                        decrIndent();
+                    }
+                    if (!preformatted && getCurrentLineLength() != 0) {
+                        writeLineSeparator();
+                    }
+                    endTag((Element)elemsStack.pop());
+                }
+            }
+
+            if (isEmptyTag(getHTMLTag(e.getAttributes()))) {
+                emptyTag(e);
+            } else if (matchNameAttribute(e.getAttributes(), HTML.Tag.TITLE)) {
+                continue;
+            } else {
+                if (getCurrentLineLength() != 0 && getCanWrapLines()) {
+                    writeLineSeparator();
+                }
+                startTag(e);
+                elemsStack.push(e);
+                if (!synthesizedElement(e)) {
+                    incrIndent();
+                }
+                indentEmptyTag = !preformatted;
+                if (matchNameAttribute(e.getAttributes(), HTML.Tag.HEAD)) {
+                    writeDocumentProperties();
+                }
+            }
+        }
+
+        while (!elemsStack.isEmpty()) {
+            if (!synthesizedElement((Element)elemsStack.peek())) {
+                decrIndent();
+            }
+            if (!preformatted && getCurrentLineLength() != 0) {
+                writeLineSeparator();
+            }
+            endTag((Element)elemsStack.pop());
+        }
+
+        writeAdditionalComment();
+    }
+
+    protected void comment(final Element elem)
+        throws BadLocationException, IOException {
+
+        if (!matchNameAttribute(elem.getAttributes(), HTML.Tag.COMMENT)) {
+            return;
+        }
+
+        Object comment = elem.getAttributes().getAttribute(HTML.Attribute.COMMENT);
+        write("<!--");
+        if (comment != null) {
+            write(comment.toString());
+        }
+        write("-->");
+        writeLineSeparator();
+    }
+
+    protected void emptyTag(final Element elem)
+        throws BadLocationException, IOException {
+
+        if (indentEmptyTag) {
+            indent();
+            indentEmptyTag = false;
+        }
+        closeOutUnwantedEmbeddedTags(elem.getAttributes());
+        writeEmbeddedTags(elem.getAttributes());
+        if (matchNameAttribute(elem.getAttributes(), HTML.Tag.CONTENT)) {
+            text(elem);
+        } else if (matchNameAttribute(elem.getAttributes(), HTML.Tag.COMMENT)) {
+            comment(elem);
+        } else {
+            write("<" + getHTMLTag(elem.getAttributes()).toString());
+            writeAttributes(elem.getAttributes());
+            write('>');
+            if (!elem.isLeaf()) {
+                writeLineSeparator();
+            }
+        }
+    }
+
+    protected boolean isBlockTag(final AttributeSet attr) {
+        HTML.Tag tag = getHTMLTag(attr);
+        return tag == null ? false : tag.isBlock();
+    }
+
+    protected boolean matchNameAttribute(final AttributeSet attr,
+                                         final HTML.Tag tag) {
+        return tag == null ? false : tag.equals(getHTMLTag(attr));
+    }
+
+    protected boolean synthesizedElement(final Element elem) {
+        return matchNameAttribute(elem.getAttributes(), HTML.Tag.IMPLIED);
+    }
+
+    protected void output(final char[] chars, final int start, final int length)
+        throws IOException {
+
+        if (!writingContent) {
+            super.output(chars, start, length);
+            return;
+        }
+
+        StringBuffer buffer = new StringBuffer();
+        int writtenLength = 0;
+        for (int i = 0; i < length; i++) {
+            String entity = getEntity(chars[start + i]);
+            if (entity != null) {
+                buffer.append(chars, start + writtenLength, i - writtenLength);
+                buffer.append(entity);
+                writtenLength = i + 1;
+            }
+        }
+        if (writtenLength == 0) {
+            super.output(chars, start, length);
+        } else {
+            buffer.append(chars, start + writtenLength, length - writtenLength);
+            char[] content = buffer.toString().toCharArray();
+            super.output(content, 0, content.length);
+        }
+    }
+
+    protected void startTag(final Element elem)
+        throws IOException, BadLocationException {
+
+        if (synthesizedElement(elem)) {
+            return;
+        }
+
+        closeOutUnwantedEmbeddedTags(elem.getAttributes());
+        indent();
+
+        if (matchNameAttribute(elem.getAttributes(), HTML.Tag.HEAD)) {
+            toWriteHead = false;
+        } else if (toWriteHead
+                && matchNameAttribute(elem.getAttributes(), HTML.Tag.BODY)) {
+            writeSynthesizedHead();
+        }
+
+        HTML.Tag tag = getHTMLTag(elem.getAttributes());
+        writeTag(tag, elem.getAttributes());
+
+        preformatted = tag.isPreformatted();
+        if (!preformatted) {
+            writeLineSeparator();
+        }
+
+        if (matchNameAttribute(elem.getAttributes(), HTML.Tag.TEXTAREA)) {
+            textAreaContent(elem.getAttributes());
+        } else if (matchNameAttribute(elem.getAttributes(), HTML.Tag.SELECT)) {
+            selectContent(elem.getAttributes());
+        }
+    }
+
+    protected void endTag(final Element elem) throws IOException {
+        if (synthesizedElement(elem)) {
+            return;
+        }
+
+        closeOutUnwantedEmbeddedTags(elem.getAttributes());
+        if (!preformatted) {
+            indent();
+        }
+        HTML.Tag tag = getHTMLTag(elem.getAttributes());
+        write("</" + tag + ">");
+        writeLineSeparator();
+    }
+
+    protected void text(final Element elem)
+        throws BadLocationException, IOException {
+
+        writingContent = true;
+        super.text(elem);
+        writingContent = false;
+    }
+
+    protected void selectContent(final AttributeSet attr) throws IOException {
+        FormSelectModel model =
+            (FormSelectModel)attr.getAttribute(StyleConstants.ModelAttribute);
+        incrIndent();
+        for (int i = 0; i < model.getOptionCount(); i++) {
+            if (model.getOption(i) instanceof FormOptionGroup) {
+                startOptionGroup(model.getOption(i));
+            } else {
+                writeOption(model.getOption(i));
+            }
+        }
+        endOptionGroup();
+        decrIndent();
+    }
+
+    protected void textAreaContent(final AttributeSet attr)
+        throws BadLocationException, IOException {
+
+        FormTextModel model = (FormTextModel)attr
+                .getAttribute(StyleConstants.ModelAttribute);
+        incrIndent();
+        writingContent = true;
+        indent();
+        write(model.getInitialContent());
+        writingContent = false;
+        decrIndent();
+        writeLineSeparator();
+    }
+
+    protected void writeAttributes(final AttributeSet attr) throws IOException {
+        writeCSSAttributes(attr, " style=\"", "\"");
+
+        for (Enumeration attrs = attr.getAttributeNames();
+             attrs.hasMoreElements();) {
+
+            Object a = attrs.nextElement();
+            if (a instanceof HTML.Tag
+                    || a instanceof StyleConstants
+                    || a instanceof CSS.Attribute
+                    || HTML.Attribute.ENDTAG.equals(a)) {
+                continue;
+            }
+            write(" " + a + "=\"" + attr.getAttribute(a) + "\"");
+        }
+    }
+
+    protected void writeEmbeddedTags(final AttributeSet attr) throws IOException {
+        for (Enumeration attrs = attr.getAttributeNames();
+             attrs.hasMoreElements();) {
+
+            Object a = attrs.nextElement();
+            if (a instanceof HTML.Tag && !openEmbeddedTags.contains(a)) {
+                Object value = attr.getAttribute(a);
+                writeTag((HTML.Tag)a,
+                         value instanceof AttributeSet ? (AttributeSet)value : null);
+                openEmbeddedTags.add(a);
+            }
+        }
+    }
+
+    protected void closeOutUnwantedEmbeddedTags(final AttributeSet attr)
+        throws IOException {
+
+        int start = 0;
+        while (start < openEmbeddedTags.size()
+                && attr.isDefined(openEmbeddedTags.get(start))) {
+            start++;
+        }
+
+        for (int i = openEmbeddedTags.size() - 1; i >= start; i--) {
+            Object a = openEmbeddedTags.get(i);
+            write("</" + a + ">");
+            openEmbeddedTags.remove(a);
+        }
+    }
+
+    protected void writeLineSeparator() throws IOException {
+        super.writeLineSeparator();
+    }
+
+    protected void writeOption(final Option option) throws IOException {
+        indent();
+        StringBuffer buffer = new StringBuffer(50);
+        buffer.append("<option");
+        String value =
+            (String)option.getAttributes().getAttribute(HTML.Attribute.VALUE);
+        if (value != null) {
+            buffer.append(" value=");
+            buffer.append(value);
+        }
+        if (option.isSelected()) {
+            buffer.append(" selected");
+        }
+        buffer.append(">");
+        if (option.getLabel() != null) {
+            buffer.append(option.getLabel());
+        }
+
+        write(buffer.toString());
+        writeLineSeparator();
+    }
+
+    private void startOptionGroup(final Option option) throws IOException {
+        if (isOptionGroupOpen) {
+            endOptionGroup();
+        }
+
+        isOptionGroupOpen = true;
+        indent();
+        StringBuffer buffer = new StringBuffer(50);
+        buffer.append("<optgroup");
+        if (option.getLabel() != null) {
+            buffer.append(" label=\"");
+            buffer.append(option.getLabel());
+            buffer.append("\"");
+        }
+        buffer.append(">");
+
+        write(buffer.toString());
+        writeLineSeparator();
+        incrIndent();
+    }
+
+    private void endOptionGroup() throws IOException {
+        if (!isOptionGroupOpen) {
+            return;
+        }
+
+        decrIndent();
+        indent();
+        write("</optgroup>");
+        writeLineSeparator();
+        isOptionGroupOpen = false;
+    }
+
+    protected boolean getCanWrapLines() {
+        return super.getCanWrapLines() && (!preformatted && writingContent);
+    }
+
+    private static HTML.Tag getHTMLTag(final AttributeSet attrs) {
+        return (HTML.Tag)attrs.getAttribute(StyleConstants.NameAttribute);
+    }
+
+    private String getEntity(final char ch) {
+        boolean useName = ch == '<' || ch == '>' || ch == '&' || ch == '"';
+        Entity entity = (Entity)getDTD().entityHash.get(new Integer(ch));
+        if (entity == null) {
+            return null;
+        } else if (useName) {
+            return "&" + entity.getName() + ";";
+        } else {
+            return "&#" + Integer.toString(ch) + ";";
+        }
+    }
+
+    private static DTD getDTD() {
+        if (dtd == null) {
+            try {
+                dtd = DTD.getDTD("writer");
+                dtd.read(new DataInputStream(
+                        dtd.getClass().getResourceAsStream("transitional401.bdtd")));
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        }
+
+        return dtd;
+    }
+
+    private boolean isEmptyTag(final HTML.Tag tag) {
+        if (HTML.Tag.CONTENT.equals(tag) || HTML.Tag.COMMENT.equals(tag)) {
+            return true;
+        }
+        return getDTD().getElement(tag.toString()).isEmpty();
+    }
+
+    private void writeTag(final HTML.Tag tag, final AttributeSet attrs)
+        throws IOException {
+
+        write("<" + tag);
+        if (attrs != null) {
+            writeAttributes(attrs);
+        }
+        write('>');
+    }
+
+    private void writeSynthesizedHead() throws IOException {
+        writeTag(HTML.Tag.HEAD, null);
+        writeLineSeparator();
+        writeLineSeparator();
+        indent();
+        write("</" + HTML.Tag.HEAD + ">");
+        writeLineSeparator();
+        indent();
+    }
+
+    private void writeDocumentProperties() throws IOException {
+        writeEmbeddedStyleSheet();
+        writeDocumentTitle();
+        writeDocumentBase();
+    }
+
+    private void writeEmbeddedStyleSheet() throws IOException {
+        StyleSheet ss = ((HTMLDocument)getDocument()).getStyleSheet();
+        Enumeration styles = ss.getStyleNames();
+        boolean firstRule = true;
+        while (styles.hasMoreElements()) {
+            String styleName = (String)styles.nextElement();
+            if (StyleSheet.DEFAULT_STYLE.equals(styleName)) {
+                continue;
+            }
+            if (firstRule) {
+                indent();
+                write("<style type=\"text/css\">");
+                writeLineSeparator();
+                incrIndent();
+                indent();
+                write("<!--");
+                writeLineSeparator();
+                incrIndent();
+                firstRule = false;
+            }
+            Style styleRule = ss.getStyle(styleName);
+            indent();
+            write(styleName);
+            writeCSSAttributes(styleRule, " { ", " }");
+            writeLineSeparator();
+        }
+        if (!firstRule) {
+            decrIndent();
+            indent();
+            write("-->");
+            writeLineSeparator();
+            decrIndent();
+            indent();
+            write("</style>");
+            writeLineSeparator();
+        }
+    }
+
+    private void writeCSSAttributes(final AttributeSet attr,
+                                    final String begin,
+                                    final String end) throws IOException {
+        boolean firstCSSAttr = true;
+        for (Enumeration attrs = attr.getAttributeNames();
+            attrs.hasMoreElements();) {
+
+            Object a = attrs.nextElement();
+            if (a instanceof CSS.Attribute) {
+                if (firstCSSAttr) {
+                    firstCSSAttr = false;
+                    write(begin);
+                } else {
+                    write("; ");
+                }
+                write(a + ": " + attr.getAttribute(a));
+            }
+        }
+        if (!firstCSSAttr) {
+            write(end);
+        }
+    }
+
+    private void writeDocumentTitle() throws IOException {
+        Object title = getDocument().getProperty(Document.TitleProperty);
+        if (title == null) {
+            return;
+        }
+
+        indent();
+        write("<title>");
+        write(title.toString());
+        write("</title>");
+        writeLineSeparator();
+    }
+
+    private void writeDocumentBase() throws IOException {
+        URL url = (URL)getDocument().getProperty(HTMLDocument.INITIAL_BASE_PROPERTY);
+        if (url == null) {
+            return;
+        }
+
+        indent();
+        write("<base href=\"");
+        write(url.toString());
+        write("\">");
+    }
+
+    private void writeAdditionalComment() throws IOException {
+        Object comments = getDocument().getProperty(HTMLDocument.AdditionalComments);
+        if (comments == null) {
+            return;
+        }
+
+        Vector strings = (Vector)comments;
+        for (Iterator it = strings.iterator(); it.hasNext();) {
+            write("<!--");
+            write(it.next().toString());
+            write("-->");
+            writeLineSeparator();
+        }
+    }
+}

Added: incubator/harmony/enhanced/classlib/trunk/modules/swing/src/main/java/common/javax/swing/text/html/ImageView.java
URL: http://svn.apache.org/viewvc/incubator/harmony/enhanced/classlib/trunk/modules/swing/src/main/java/common/javax/swing/text/html/ImageView.java?rev=427121&view=auto
==============================================================================
--- incubator/harmony/enhanced/classlib/trunk/modules/swing/src/main/java/common/javax/swing/text/html/ImageView.java (added)
+++ incubator/harmony/enhanced/classlib/trunk/modules/swing/src/main/java/common/javax/swing/text/html/ImageView.java Mon Jul 31 07:08:47 2006
@@ -0,0 +1,256 @@
+/*
+ *  Copyright 2005 - 2006 The Apache Software Software Foundation or its licensors, as applicable.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+/**
+ * @author Alexey A. Ivanov
+ * @version $Revision$
+ */
+package javax.swing.text.html;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Graphics;
+import java.awt.Image;
+import java.awt.Rectangle;
+import java.awt.Shape;
+import java.awt.Toolkit;
+import java.net.URL;
+
+import javax.swing.Icon;
+import javax.swing.event.DocumentEvent;
+import javax.swing.text.AttributeSet;
+import javax.swing.text.BadLocationException;
+import javax.swing.text.Element;
+import javax.swing.text.Highlighter;
+import javax.swing.text.JTextComponent;
+import javax.swing.text.LayeredHighlighter;
+import javax.swing.text.View;
+import javax.swing.text.ViewFactory;
+import javax.swing.text.Position.Bias;
+
+import org.apache.harmony.x.swing.text.html.HTMLIconFactory;
+
+public class ImageView extends View {
+    private static Icon loadingImageIcon;
+    private static Icon noImageIcon;
+
+    private AttributeSet attrs;
+
+    private BackgroundImageLoader loader;
+    private String src;
+    private URL imageURL;
+
+    private boolean synchronous;
+
+    private Color color;
+
+    public ImageView(final Element element) {
+        super(element);
+        setPropertiesFromAttributes();
+    }
+
+    public Image getImage() {
+        return loader.image;
+    }
+
+    public URL getImageURL() {
+        if (imageURL == null) {
+            URL base = ((HTMLDocument)getDocument()).getBase();
+            imageURL = HTML.resolveURL(src, base);
+        }
+        return imageURL;
+    }
+
+    public Icon getLoadingImageIcon() {
+        return HTMLIconFactory.getLoadingImageIcon();
+    }
+
+    public Icon getNoImageIcon() {
+        return HTMLIconFactory.getNoImageIcon();
+    }
+
+    public void setLoadsSynchronously(final boolean synchronous) {
+        this.synchronous = synchronous;
+    }
+
+    public boolean getLoadsSynchronously() {
+        return synchronous;
+    }
+
+    public float getPreferredSpan(final int axis) {
+        if (loader.isError()) {
+            String alt = getAltText();
+            FontMetrics metrics = null;
+            if (alt != null) {
+                Font font = getStyleSheet().getFont(getAttributes());
+                metrics = Toolkit.getDefaultToolkit().getFontMetrics(font);
+            }
+
+            return axis == X_AXIS
+                   ? getNoImageIcon().getIconWidth()
+                     + (metrics != null ? metrics.stringWidth(alt) : 0)
+                   : Math.max(getNoImageIcon().getIconHeight(),
+                     (metrics != null ? metrics.getHeight() : 0));
+        }
+        if (!loader.isReady()) {
+            return axis == X_AXIS ? getLoadingImageIcon().getIconWidth()
+                                  : getLoadingImageIcon().getIconHeight();
+        }
+        if (axis == X_AXIS) {
+            return loader.getWidth();
+        }
+        return loader.getHeight();
+    }
+
+    public String getToolTipText(final float x, final float y,
+                                 final Shape shape) {
+        return getAltText();
+    }
+
+    public String getAltText() {
+        return (String)getElement().getAttributes()
+                       .getAttribute(HTML.Attribute.ALT);
+    }
+
+    public void paint(final Graphics g, final Shape shape) {
+        Rectangle rc = shape.getBounds();
+
+        // TODO change layered highlight painting code
+        JTextComponent tc = (JTextComponent)getContainer();
+        Highlighter hl = tc.getHighlighter();
+        if (hl instanceof LayeredHighlighter) {
+            ((LayeredHighlighter)hl).paintLayeredHighlights(g,
+                                                            getStartOffset(),
+                                                            getEndOffset(),
+                                                            shape, tc, this);
+        }
+
+        if (loader.isError()) {
+            getNoImageIcon().paintIcon(null, g, rc.x, rc.y);
+            Color oldColor = g.getColor();
+            g.setColor(color);
+
+            String alt = getAltText();
+            if (alt != null) {
+                Font oldFont = g.getFont();
+
+                Font font = getStyleSheet().getFont(getAttributes());
+                g.setFont(font);
+                FontMetrics metrics = g.getFontMetrics();
+                g.drawString(alt, rc.x + getNoImageIcon().getIconWidth(),
+                             rc.y + metrics.getAscent());
+
+                g.setFont(oldFont);
+            }
+            g.drawRect(rc.x, rc.y, rc.width - 1, rc.height - 1);
+            g.setColor(oldColor);
+            return;
+        }
+
+        if (!loader.isReady()) {
+            if (!synchronous) {
+                getLoadingImageIcon().paintIcon(null, g, rc.x, rc.y);
+            } else {
+                loader.waitForImage();
+            }
+            return;
+        }
+
+        g.drawImage(getImage(), rc.x, rc.y, rc.width, rc.height, loader);
+    }
+
+    public Shape modelToView(final int pos, final Shape shape, final Bias bias)
+        throws BadLocationException {
+
+        Rectangle rc = shape.getBounds();
+        if (pos <= getStartOffset()) {
+            return new Rectangle(rc.x, rc.y, 0, rc.height);
+        }
+        return new Rectangle(rc.x + rc.width, rc.y, 0, rc.height);
+    }
+
+    public int viewToModel(final float x, final float y, final Shape shape,
+                           final Bias[] bias) {
+
+        Rectangle rc = shape.getBounds();
+        if (x < rc.x + rc.width/* / 2*/) {
+            bias[0] = Bias.Forward;
+            return getStartOffset();
+        }
+        bias[0] = Bias.Backward;
+        return getEndOffset();
+    }
+
+    public float getAlignment(final int axis) {
+        if (axis == Y_AXIS) {
+            return 1;
+        }
+        return super.getAlignment(axis);
+    }
+
+    public AttributeSet getAttributes() {
+        return attrs;
+    }
+
+    public void changedUpdate(final DocumentEvent event, final Shape shape,
+                              final ViewFactory factory) {
+        setPropertiesFromAttributes();
+        super.changedUpdate(event, shape, factory);
+    }
+
+    protected StyleSheet getStyleSheet() {
+        return ((HTMLDocument)getDocument()).getStyleSheet();
+    }
+
+    protected void setPropertiesFromAttributes() {
+        attrs = getStyleSheet().getViewAttributes(this);
+
+        src = (String)getElement().getAttributes()
+                      .getAttribute(HTML.Attribute.SRC);
+
+        Object size = getAttributes().getAttribute(CSS.Attribute.WIDTH);
+        int desiredWidth = -1;
+        if (size instanceof CSS.Length) {
+            desiredWidth = ((CSS.Length)size).intValue(this);
+        }
+
+        size = getAttributes().getAttribute(CSS.Attribute.HEIGHT);
+        int desiredHeight = -1;
+        if (size instanceof CSS.Length) {
+            desiredHeight = ((CSS.Length)size).intValue(this);
+        }
+        createImage(desiredWidth, desiredHeight);
+
+        color = getStyleSheet().getForeground(getAttributes());
+    }
+
+    private void createImage(final int desiredWidth, final int desiredHeight) {
+        imageURL = null;
+        loader = new BackgroundImageLoader(getImageURL(),
+                                           desiredWidth, desiredHeight) {
+            protected void onReady() {
+                super.onReady();
+                preferenceChanged(ImageView.this, true, true);
+            }
+
+            protected void onError() {
+                super.onError();
+                preferenceChanged(ImageView.this, true, true);
+            }
+        };
+    }
+}

Added: incubator/harmony/enhanced/classlib/trunk/modules/swing/src/main/java/common/javax/swing/text/html/InlineView.java
URL: http://svn.apache.org/viewvc/incubator/harmony/enhanced/classlib/trunk/modules/swing/src/main/java/common/javax/swing/text/html/InlineView.java?rev=427121&view=auto
==============================================================================
--- incubator/harmony/enhanced/classlib/trunk/modules/swing/src/main/java/common/javax/swing/text/html/InlineView.java (added)
+++ incubator/harmony/enhanced/classlib/trunk/modules/swing/src/main/java/common/javax/swing/text/html/InlineView.java Mon Jul 31 07:08:47 2006
@@ -0,0 +1,128 @@
+/*
+ *  Copyright 2005 - 2006 The Apache Software Software Foundation or its licensors, as applicable.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+/**
+ * @author Alexey A. Ivanov
+ * @version $Revision$
+ */
+package javax.swing.text.html;
+
+import java.awt.Shape;
+import java.text.BreakIterator;
+import java.text.CharacterIterator;
+
+import javax.swing.event.DocumentEvent;
+import javax.swing.text.AttributeSet;
+import javax.swing.text.Element;
+import javax.swing.text.LabelView;
+import javax.swing.text.Segment;
+import javax.swing.text.View;
+import javax.swing.text.ViewFactory;
+
+public class InlineView extends LabelView {
+    private AttributeSet attrs;
+    private boolean canBreak;
+
+    public InlineView(final Element elem) {
+        super(elem);
+        canBreak = canBreak();
+    }
+
+    public int getBreakWeight(final int axis, final float x, final float len) {
+        if (!canBreak) {
+            return BadBreakWeight;
+        }
+        return super.getBreakWeight(axis, x, len);
+    }
+
+    public View breakView(final int axis, final int startOffset,
+                          final float x, final float len) {
+        if (!canBreak) {
+            return this;
+        }
+        return super.breakView(axis, startOffset, x, len);
+    }
+
+    public AttributeSet getAttributes() {
+        if (attrs == null) {
+            attrs = getStyleSheet().getViewAttributes(this);
+        }
+        return attrs;
+    }
+
+    public void changedUpdate(final DocumentEvent event, final Shape alloc,
+                              final ViewFactory factory) {
+        attrs = getStyleSheet().getViewAttributes(this);
+        canBreak = canBreak();
+        super.changedUpdate(event, alloc, factory);
+    }
+
+    protected StyleSheet getStyleSheet() {
+        return ((HTMLDocument)getDocument()).getStyleSheet();
+    }
+
+    protected void setPropertiesFromAttributes() {
+        // TODO setPropertiesFromAttrs: 'text-decoration' for overline, blink
+        // TODO setPropertiesFromAttrs: 'vertical-align' for alignment???
+        super.setPropertiesFromAttributes();
+        canBreak = canBreak();
+    }
+
+    final int getLongestWordSpan() {
+        int result = 0;
+
+        final int startOffset = getStartOffset();
+        final int endOffset = getEndOffset();
+
+        final BreakIterator bi = BreakIterator.getWordInstance();
+        final Segment text = getText(startOffset, endOffset);
+        bi.setText(text);
+
+        int prev;
+        int curr = bi.first() - text.offset;
+        int next;
+        do {
+            prev = curr;
+            next = bi.next();
+            curr = next != BreakIterator.DONE ? next - text.offset
+                                              : endOffset - startOffset;
+            if (!isWhitespace(getText(startOffset + prev,
+                                      startOffset + curr))) {
+                result = Math.max(result,
+                                  (int)getPartialSpan(startOffset + prev,
+                                                      startOffset + curr));
+            }
+        } while (next != BreakIterator.DONE);
+
+        return result;
+    }
+
+    private boolean canBreak() {
+        Object ws = getAttributes().getAttribute(CSS.Attribute.WHITE_SPACE);
+        return ws == null
+               || ((CSS.WhiteSpace)ws).getIndex() != CSS.WhiteSpace.NOWRAP;
+    }
+
+    // TODO refactor isWhitespace: text.GlyphView and text.html.InlineView
+    private static boolean isWhitespace(final CharacterIterator text) {
+        boolean result;
+        char c = text.first();
+        do {
+            result = Character.isWhitespace(c);
+            c = text.next();
+        } while (result && c != CharacterIterator.DONE);
+        return result;
+    }
+}

Added: incubator/harmony/enhanced/classlib/trunk/modules/swing/src/main/java/common/javax/swing/text/html/InvisibleTagView.java
URL: http://svn.apache.org/viewvc/incubator/harmony/enhanced/classlib/trunk/modules/swing/src/main/java/common/javax/swing/text/html/InvisibleTagView.java?rev=427121&view=auto
==============================================================================
--- incubator/harmony/enhanced/classlib/trunk/modules/swing/src/main/java/common/javax/swing/text/html/InvisibleTagView.java (added)
+++ incubator/harmony/enhanced/classlib/trunk/modules/swing/src/main/java/common/javax/swing/text/html/InvisibleTagView.java Mon Jul 31 07:08:47 2006
@@ -0,0 +1,55 @@
+/*
+ *  Copyright 2005 - 2006 The Apache Software Software Foundation or its licensors, as applicable.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+/**
+ * @author Vadim L. Bogdanov
+ * @version $Revision$
+ */
+package javax.swing.text.html;
+
+import java.awt.Graphics;
+import java.awt.Rectangle;
+import java.awt.Shape;
+
+import javax.swing.text.BadLocationException;
+import javax.swing.text.Element;
+import javax.swing.text.View;
+import javax.swing.text.Position.Bias;
+
+class InvisibleTagView extends View {
+    InvisibleTagView(final Element elem) {
+        super(elem);
+    }
+
+    public float getPreferredSpan(final int axis) {
+        return 0f;
+    }
+
+    public void paint(Graphics g, Shape alloc) {
+    }
+
+    public Shape modelToView(int pos, Shape shape, Bias bias) throws BadLocationException {
+        Rectangle r = shape.getBounds();
+        return new Rectangle(r.x, r.y, 0, r.height);
+    }
+
+    public int viewToModel(float x, float y, Shape shape, Bias[] biasReturn) {
+        return getStartOffset();
+    }
+
+    public boolean isVisible() {
+        return false;
+    }
+}

Added: incubator/harmony/enhanced/classlib/trunk/modules/swing/src/main/java/common/javax/swing/text/html/ListView.java
URL: http://svn.apache.org/viewvc/incubator/harmony/enhanced/classlib/trunk/modules/swing/src/main/java/common/javax/swing/text/html/ListView.java?rev=427121&view=auto
==============================================================================
--- incubator/harmony/enhanced/classlib/trunk/modules/swing/src/main/java/common/javax/swing/text/html/ListView.java (added)
+++ incubator/harmony/enhanced/classlib/trunk/modules/swing/src/main/java/common/javax/swing/text/html/ListView.java Mon Jul 31 07:08:47 2006
@@ -0,0 +1,57 @@
+/*
+ *  Copyright 2005 - 2006 The Apache Software Software Foundation or its licensors, as applicable.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+/**
+ * @author Alexey A. Ivanov
+ * @version $Revision$
+ */
+package javax.swing.text.html;
+
+import java.awt.Graphics;
+import java.awt.Rectangle;
+import java.awt.Shape;
+import javax.swing.text.Element;
+import javax.swing.text.html.StyleSheet.ListPainter;
+
+public class ListView extends BlockView {
+    private ListPainter listPainter;
+
+    public ListView(final Element element) {
+        super(element, Y_AXIS);
+    }
+
+    public float getAlignment(final int axis) {
+        return 0.5f;
+    }
+
+    public void paint(final Graphics g, final Shape allocation) {
+        super.paint(g, allocation);
+    }
+
+    protected void paintChild(final Graphics g,
+                              final Rectangle alloc,
+                              final int index) {
+        listPainter.paint(g,
+                          alloc.x, alloc.y, alloc.width, alloc.height,
+                          this, index);
+        super.paintChild(g, alloc, index);
+    }
+
+    protected void setPropertiesFromAttributes() {
+        super.setPropertiesFromAttributes();
+        listPainter = getStyleSheet().getListPainter(getAttributes());
+    }
+}
+

Added: incubator/harmony/enhanced/classlib/trunk/modules/swing/src/main/java/common/javax/swing/text/html/MinimalHTMLWriter.java
URL: http://svn.apache.org/viewvc/incubator/harmony/enhanced/classlib/trunk/modules/swing/src/main/java/common/javax/swing/text/html/MinimalHTMLWriter.java?rev=427121&view=auto
==============================================================================
--- incubator/harmony/enhanced/classlib/trunk/modules/swing/src/main/java/common/javax/swing/text/html/MinimalHTMLWriter.java (added)
+++ incubator/harmony/enhanced/classlib/trunk/modules/swing/src/main/java/common/javax/swing/text/html/MinimalHTMLWriter.java Mon Jul 31 07:08:47 2006
@@ -0,0 +1,385 @@
+/*
+ *  Copyright 2005 - 2006 The Apache Software Software Foundation or its licensors, as applicable.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/**
+ * @author Vadim L. Bogdanov
+ * @version $Revision$
+ */
+package javax.swing.text.html;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.Enumeration;
+import java.util.Vector;
+
+import javax.swing.text.AbstractDocument;
+import javax.swing.text.AbstractWriter;
+import javax.swing.text.AttributeSet;
+import javax.swing.text.BadLocationException;
+import javax.swing.text.DefaultStyledDocument;
+import javax.swing.text.Document;
+import javax.swing.text.Element;
+import javax.swing.text.SimpleAttributeSet;
+import javax.swing.text.Style;
+import javax.swing.text.StyleConstants;
+import javax.swing.text.StyleContext;
+import javax.swing.text.StyledDocument;
+
+public class MinimalHTMLWriter extends AbstractWriter {
+    private static final AttributeSet EMPTY_ATTR_SET = new SimpleAttributeSet();
+    private Vector openEmbeddedTags = new Vector();
+    private boolean inFontTag;
+
+    public MinimalHTMLWriter(final Writer w, final StyledDocument doc,
+                             final int pos, final int len) {
+
+        super(w, doc, pos, len);
+    }
+
+    public MinimalHTMLWriter(final Writer w, final StyledDocument doc) {
+        super(w, doc);
+    }
+
+    protected boolean inFontTag() {
+        return inFontTag;
+    }
+
+    protected boolean isText(final Element elem) {
+        return AbstractDocument.ContentElementName.equals(elem.getName());
+    }
+
+    protected void startFontTag(final String style) throws IOException {
+        writeStartTag("<font style=\"" + style + "\">");
+    }
+
+    protected void endFontTag() throws IOException {
+        writeLineSeparator();
+        writeEndTag("</font>");
+    }
+
+    protected void text(final Element elem) throws IOException,
+            BadLocationException {
+
+        String content = getText(elem);
+        int textStart = Math.max(getStartOffset(), elem.getStartOffset())
+            - elem.getStartOffset();
+        int textEnd = Math.min(getEndOffset(), elem.getEndOffset())
+            - elem.getStartOffset();
+        if (textEnd > textStart && content.charAt(textEnd - 1) == NEWLINE) {
+            textEnd--;
+        }
+
+        if (textEnd > textStart) {
+            write(content.toCharArray(), textStart, textEnd - textStart);
+        }
+    }
+
+    public void write() throws IOException, BadLocationException {
+        writeStartTag("<html>");
+        writeHeader();
+        writeBody();
+        writeEndTag("</html>");
+    }
+
+    protected void writeAttributes(final AttributeSet attr) throws IOException {
+        for (Enumeration attrs = attr.getAttributeNames();
+                attrs.hasMoreElements();) {
+
+            Object a = attrs.nextElement();
+
+            String attrString = writeAttributeAsCSS(a, attr.getAttribute(a));
+            if (attrString != null) {
+                indent();
+                write(attrString);
+                write(";");
+                writeLineSeparator();
+            }
+        }
+    }
+
+    protected void writeBody() throws IOException, BadLocationException {
+        writeStartTag("<body>");
+
+        Element root = getDocument().getDefaultRootElement();
+        for (int i = root.getElementIndex(getStartOffset());
+             i < root.getElementCount();
+             i++) {
+
+            Element e = root.getElement(i);
+            if (!inRange(e)) {
+                break;
+            }
+            writeStartParagraph(e);
+            writeParagraphElements(e);
+            writeEndParagraph();
+        }
+
+        writeEndTag("</body>");
+    }
+
+    protected void writeContent(final Element elem, final boolean needsIndenting)
+            throws IOException, BadLocationException {
+
+        writeNonHTMLAttributes(elem.getAttributes());
+        if (needsIndenting) {
+            indent();
+        }
+        writeHTMLTags(elem.getAttributes());
+        text(elem);
+    }
+
+    protected void writeStartTag(final String tag) throws IOException {
+        indent();
+        write(tag);
+        writeLineSeparator();
+        incrIndent();
+    }
+
+    protected void writeEndParagraph() throws IOException {
+        writeEndHTMLTags(EMPTY_ATTR_SET);
+        writeLineSeparator();
+        if (inFontTag()) {
+            writeEndSpan();
+        }
+        writeEndTag("</p>");
+    }
+
+    protected void writeEndTag(final String endTag) throws IOException {
+        decrIndent();
+        indent();
+        write(endTag);
+        writeLineSeparator();
+    }
+
+    protected void writeHTMLTags(final AttributeSet attr) throws IOException {
+        if (StyleConstants.isBold(attr)) {
+            writeHTMLTagIfNeeded(HTML.Tag.B);
+        }
+        if (StyleConstants.isItalic(attr)) {
+            writeHTMLTagIfNeeded(HTML.Tag.I);
+        }
+        if (StyleConstants.isUnderline(attr)) {
+            writeHTMLTagIfNeeded(HTML.Tag.U);
+        }
+    }
+
+    protected void writeHeader() throws IOException {
+        writeStartTag("<head>");
+
+        writeStartTag("<style type=\"text/css\">");
+        writeStartTag("<!--");
+        writeStyles();
+        writeEndTag("-->");
+        writeEndTag("</style>");
+
+        writeDocumentTitle();
+
+        writeEndTag("</head>");
+    }
+
+    protected void writeImage(final Element elem) throws IOException {
+        indent();
+    }
+
+    protected void writeComponent(final Element elem) throws IOException {
+        indent();
+    }
+
+    protected void writeLeaf(final Element elem) throws IOException {
+        if (StyleConstants.IconElementName.equals(elem.getName())) {
+            writeImage(elem);
+        } else if (StyleConstants.ComponentElementName.equals(elem.getName())) {
+            writeComponent(elem);
+        } else {
+            indent();
+        }
+    }
+
+    protected void writeNonHTMLAttributes(final AttributeSet attr)
+            throws IOException {
+
+        writeStartSpan(attr);
+    }
+
+    protected void writeStartParagraph(final Element elem) throws IOException {
+        writeStartTag("<p class=" + getParagraphStyleName(elem) + ">");
+    }
+
+    protected void writeStyles() throws IOException {
+        if (!(getDocument() instanceof DefaultStyledDocument)) {
+            // XXX: it's not clear what to do in this case
+            throw new UnsupportedOperationException("Not implemented");
+        }
+
+        StyledDocument styledDocument = (StyledDocument)getDocument();
+        Enumeration styles = ((DefaultStyledDocument)getDocument()).getStyleNames();
+        while (styles.hasMoreElements()) {
+            String styleName = (String)styles.nextElement();
+            if (StyleSheet.DEFAULT_STYLE.equals(styleName)) {
+                continue;
+            }
+            indent();
+            write("p." + styleName + " {");
+            writeLineSeparator();
+            incrIndent();
+            writeAttributes(styledDocument.getStyle(styleName));
+            decrIndent();
+            indent();
+            write("}");
+            writeLineSeparator();
+        }
+    }
+
+    private void writeHTMLTagIfNeeded(final HTML.Tag tag) throws IOException {
+        if (!openEmbeddedTags.contains(tag)) {
+            write("<" + tag.toString() + ">");
+            openEmbeddedTags.add(tag);
+        }
+    }
+
+    private void writeEndHTMLTagIfNeeded(final HTML.Tag tag) throws IOException {
+        if (openEmbeddedTags.contains(tag)) {
+            write("</" + tag.toString() + ">");
+            openEmbeddedTags.remove(tag);
+        }
+    }
+
+    // HTML tags opening sequence is <b>, <i>, <u>,
+    // so, we have to close it in reverce order
+    private void writeEndHTMLTags(final AttributeSet attr) throws IOException {
+        if (!StyleConstants.isUnderline(attr)) {
+            writeEndHTMLTagIfNeeded(HTML.Tag.U);
+        }
+        if (!StyleConstants.isItalic(attr)) {
+            writeEndHTMLTagIfNeeded(HTML.Tag.I);
+        }
+        if (!StyleConstants.isBold(attr)) {
+            writeEndHTMLTagIfNeeded(HTML.Tag.B);
+        }
+    }
+
+    private static String getParagraphStyleName(final Element par) {
+        AttributeSet attrs = par.getAttributes();
+        Object style = attrs.getAttribute(StyleConstants.ResolveAttribute);
+        return style instanceof Style
+            ? ((Style)style).getName()
+            : StyleContext.DEFAULT_STYLE;
+    }
+
+    private void writeStartSpan(final AttributeSet attr) throws IOException {
+        if (inFontTag()) {
+            writeEndSpan();
+        }
+
+        boolean firstAttr = true;
+        for (Enumeration attrs = attr.getAttributeNames();
+                attrs.hasMoreElements();) {
+
+            Object a = attrs.nextElement();
+            if (StyleConstants.Italic.equals(a)
+                    || StyleConstants.Bold.equals(a)
+                    || StyleConstants.Underline.equals(a)
+                    || a instanceof StyleConstants.ParagraphConstants) {
+                continue;
+            }
+            if (!firstAttr) {
+                write("; ");
+            } else {
+                writeEndHTMLTags(EMPTY_ATTR_SET);
+                indent();
+                write("<span style=\"");
+            }
+            String attrString = writeAttributeAsCSS(a, attr.getAttribute(a));
+            if (attrString != null) {
+                write(attrString);
+                firstAttr = false;
+            }
+        }
+
+        if (!firstAttr) {
+            write("\">");
+            writeLineSeparator();
+            incrIndent();
+            inFontTag = true;
+        } else {
+            writeEndHTMLTags(attr);
+        }
+    }
+
+    private void writeEndSpan() throws IOException {
+        writeEndHTMLTags(EMPTY_ATTR_SET);
+        if (!isLineEmpty()) {
+            writeLineSeparator();
+        }
+        writeEndTag("</span>");
+        inFontTag = false;
+    }
+
+    private String writeAttributeAsCSS(final Object attr, final Object value)
+            throws IOException {
+
+        Object cssAttr = convertToCSSAttribute(attr);
+        if (cssAttr == null) {
+            return null;
+        }
+
+        Object cssValue = convertToCSSValue(cssAttr, value);
+        return cssAttr.toString() + ": " + cssValue.toString();
+    }
+
+    private void writeParagraphElements(final Element paragraph)
+            throws IOException, BadLocationException {
+
+        for (int i = paragraph.getElementIndex(getStartOffset());
+             i < paragraph.getElementCount();
+             i++) {
+            Element e = paragraph.getElement(i);
+            if (!inRange(e)) {
+                break;
+            }
+            if (isText(e)) {
+                writeContent(e, isLineEmpty());
+            } else {
+                writeLeaf(e);
+            }
+        }
+    }
+
+    private static Object convertToCSSAttribute(final Object attr) {
+        return CSS.mapToCSSForced(attr);
+    }
+
+    private static Object convertToCSSValue(final Object cssAttr,
+                                            final Object value) {
+        return cssAttr instanceof CSS.Attribute
+            ? ((CSS.Attribute)cssAttr).getConverter().toCSS(value)
+            : value;
+    }
+
+    // TODO: the same method exists in HTMLWriter
+    private void writeDocumentTitle() throws IOException {
+        Object title = getDocument().getProperty(Document.TitleProperty);
+        if (title == null) {
+            return;
+        }
+
+        indent();
+        write("<title>");
+        write(title.toString());
+        write("</title>");
+        writeLineSeparator();
+    }
+}



Mime
View raw message