You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@harmony.apache.org by ml...@apache.org on 2006/07/31 16:08:55 UTC
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...
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();
+ }
+}