diff options
Diffstat (limited to 'libjava/classpath/javax/swing/text/html/HTMLDocument.java')
-rw-r--r-- | libjava/classpath/javax/swing/text/html/HTMLDocument.java | 931 |
1 files changed, 749 insertions, 182 deletions
diff --git a/libjava/classpath/javax/swing/text/html/HTMLDocument.java b/libjava/classpath/javax/swing/text/html/HTMLDocument.java index 0bfc338df45..f3d3ce3faaf 100644 --- a/libjava/classpath/javax/swing/text/html/HTMLDocument.java +++ b/libjava/classpath/javax/swing/text/html/HTMLDocument.java @@ -39,19 +39,22 @@ exception statement from your version. */ package javax.swing.text.html; import gnu.classpath.NotImplementedException; -import gnu.javax.swing.text.html.CharacterAttributeTranslator; -import gnu.javax.swing.text.html.parser.htmlAttributeSet; import java.io.IOException; import java.io.StringReader; +import java.net.MalformedURLException; import java.net.URL; +import java.util.ArrayList; import java.util.HashMap; import java.util.Stack; import java.util.Vector; +import javax.swing.ButtonGroup; +import javax.swing.DefaultButtonModel; import javax.swing.JEditorPane; +import javax.swing.ListSelectionModel; import javax.swing.event.DocumentEvent; -import javax.swing.event.HyperlinkEvent.EventType; +import javax.swing.event.UndoableEditEvent; import javax.swing.text.AbstractDocument; import javax.swing.text.AttributeSet; import javax.swing.text.BadLocationException; @@ -60,6 +63,7 @@ import javax.swing.text.Element; import javax.swing.text.ElementIterator; import javax.swing.text.GapContent; import javax.swing.text.MutableAttributeSet; +import javax.swing.text.PlainDocument; import javax.swing.text.SimpleAttributeSet; import javax.swing.text.StyleConstants; import javax.swing.text.html.HTML.Tag; @@ -87,16 +91,24 @@ public class HTMLDocument extends DefaultStyledDocument boolean preservesUnknownTags = true; int tokenThreshold = Integer.MAX_VALUE; HTMLEditorKit.Parser parser; - StyleSheet styleSheet; - AbstractDocument.Content content; - + + /** + * Indicates whether this document is inside a frame or not. + */ + private boolean frameDocument; + + /** + * Package private to avoid accessor methods. + */ + String baseTarget; + /** * Constructs an HTML document using the default buffer size and a default * StyleSheet. */ public HTMLDocument() { - this(null); + this(new GapContent(BUFFER_SIZE_DEFAULT), new StyleSheet()); } /** @@ -119,14 +131,7 @@ public class HTMLDocument extends DefaultStyledDocument */ public HTMLDocument(AbstractDocument.Content c, StyleSheet styles) { - this.content = c; - if (styles == null) - { - styles = new StyleSheet(); - styles.importStyleSheet(getClass().getResource(HTMLEditorKit. - DEFAULT_CSS)); - } - this.styleSheet = styles; + super(c, styles); } /** @@ -137,7 +142,7 @@ public class HTMLDocument extends DefaultStyledDocument */ public StyleSheet getStyleSheet() { - return styleSheet; + return (StyleSheet) getAttributeContext(); } /** @@ -191,8 +196,6 @@ public class HTMLDocument extends DefaultStyledDocument protected Element createLeafElement(Element parent, AttributeSet a, int p0, int p1) { - RunElement el = new RunElement(parent, a, p0, p1); - el.addAttribute(StyleConstants.NameAttribute, HTML.Tag.CONTENT); return new RunElement(parent, a, p0, p1); } @@ -269,7 +272,7 @@ public class HTMLDocument extends DefaultStyledDocument public void setBase(URL u) { baseURL = u; - styleSheet.setBase(u); + getStyleSheet().setBase(u); } /** @@ -374,11 +377,119 @@ public class HTMLDocument extends DefaultStyledDocument } public void processHTMLFrameHyperlinkEvent(HTMLFrameHyperlinkEvent event) - throws NotImplementedException { - // TODO: Implement this properly. + String target = event.getTarget(); + Element el = event.getSourceElement(); + URL url = event.getURL(); + if (target.equals("_self")) + { + updateFrame(el, url); + } + else if (target.equals("_parent")) + { + updateFrameSet(el.getParentElement(), url); + } + else + { + Element targetFrame = findFrame(target); + if (targetFrame != null) + updateFrame(targetFrame, url); + } } - + + /** + * Finds the named frame inside this document. + * + * @param target the name to look for + * + * @return the frame if there is a matching frame, <code>null</code> + * otherwise + */ + private Element findFrame(String target) + { + ElementIterator i = new ElementIterator(this); + Element next = null; + while ((next = i.next()) != null) + { + AttributeSet atts = next.getAttributes(); + if (atts.getAttribute(StyleConstants.NameAttribute) == HTML.Tag.FRAME) + { + String name = (String) atts.getAttribute(HTML.Attribute.NAME); + if (name != null && name.equals(target)) + break; + } + } + return next; + } + + /** + * Updates the frame that is represented by the specified element to + * refer to the specified URL. + * + * @param el the element + * @param url the new url + */ + private void updateFrame(Element el, URL url) + { + try + { + writeLock(); + DefaultDocumentEvent ev = + new DefaultDocumentEvent(el.getStartOffset(), 1, + DocumentEvent.EventType.CHANGE); + AttributeSet elAtts = el.getAttributes(); + AttributeSet copy = elAtts.copyAttributes(); + MutableAttributeSet matts = (MutableAttributeSet) elAtts; + ev.addEdit(new AttributeUndoableEdit(el, copy, false)); + matts.removeAttribute(HTML.Attribute.SRC); + matts.addAttribute(HTML.Attribute.SRC, url.toString()); + ev.end(); + fireChangedUpdate(ev); + fireUndoableEditUpdate(new UndoableEditEvent(this, ev)); + } + finally + { + writeUnlock(); + } + } + + /** + * Updates the frameset that is represented by the specified element + * to create a frame that refers to the specified URL. + * + * @param el the element + * @param url the url + */ + private void updateFrameSet(Element el, URL url) + { + int start = el.getStartOffset(); + int end = el.getEndOffset(); + + StringBuilder html = new StringBuilder(); + html.append("<frame"); + if (url != null) + { + html.append(" src=\""); + html.append(url.toString()); + html.append("\""); + } + html.append('>'); + if (getParser() == null) + setParser(new HTMLEditorKit().getParser()); + try + { + setOuterHTML(el, html.toString()); + } + catch (BadLocationException ex) + { + ex.printStackTrace(); + } + catch (IOException ex) + { + ex.printStackTrace(); + } + } + /** * Gets an iterator for the given HTML.Tag. * @param t the requested HTML.Tag @@ -461,6 +572,8 @@ public class HTMLDocument extends DefaultStyledDocument String name = null; if (tag != null) name = tag.toString(); + if (name == null) + name = super.getName(); return name; } } @@ -497,6 +610,8 @@ public class HTMLDocument extends DefaultStyledDocument String name = null; if (tag != null) name = tag.toString(); + if (name == null) + name = super.getName(); return name; } @@ -518,24 +633,33 @@ public class HTMLDocument extends DefaultStyledDocument * @author Anthony Balkissoon abalkiss at redhat dot com */ public class HTMLReader extends HTMLEditorKit.ParserCallback - { + { + /** + * The maximum token threshold. We don't grow it larger than this. + */ + private static final int MAX_THRESHOLD = 10000; + + /** + * The threshold growth factor. + */ + private static final int GROW_THRESHOLD = 5; + /** * Holds the current character attribute set * */ protected MutableAttributeSet charAttr = new SimpleAttributeSet(); - protected Vector parseBuffer = new Vector(); - + protected Vector<ElementSpec> parseBuffer = new Vector<ElementSpec>(); + + /** + * The parse stack. It holds the current element tree path. + */ + private Stack<HTML.Tag> parseStack = new Stack<HTML.Tag>(); + /** * A stack for character attribute sets * */ Stack charAttrStack = new Stack(); - - /** - * The parse stack. This stack holds HTML.Tag objects that reflect the - * current position in the parsing process. - */ - Stack parseStack = new Stack(); /** A mapping between HTML.Tag objects and the actions that handle them **/ HashMap tagToAction; @@ -571,13 +695,68 @@ public class HTMLDocument extends DefaultStyledDocument /** A temporary variable that helps with the printing out of debug information **/ boolean debug = false; - - void print (String line) - { - if (debug) - System.out.println (line); - } - + + /** + * This is true when we are inside a pre tag. + */ + boolean inPreTag = false; + + /** + * This is true when we are inside a style tag. This will add text + * content inside this style tag beeing parsed as CSS. + * + * This is package private to avoid accessor methods. + */ + boolean inStyleTag = false; + + /** + * This is true when we are inside a <textarea> tag. Any text + * content will then be added to the text area. + * + * This is package private to avoid accessor methods. + */ + boolean inTextArea = false; + + /** + * This contains all stylesheets that are somehow read, either + * via embedded style tags, or via linked stylesheets. The + * elements will be String objects containing a stylesheet each. + */ + ArrayList styles; + + /** + * The document model for a textarea. + * + * This is package private to avoid accessor methods. + */ + ResetablePlainDocument textAreaDocument; + + /** + * The current model of a select tag. Can be a ComboBoxModel or a + * ListModel depending on the type of the select box. + */ + Object selectModel; + + /** + * The current option beeing read. + */ + Option option; + + /** + * The current number of options in the current select model. + */ + int numOptions; + + /** + * The current button groups mappings. + */ + HashMap buttonGroups; + + /** + * The token threshold. This gets increased while loading. + */ + private int threshold; + public class TagAction { /** @@ -633,13 +812,12 @@ public class HTMLDocument extends DefaultStyledDocument // Put the old attribute set on the stack. pushCharacterStyle(); - // Translate tag.. return if succesful. - if(CharacterAttributeTranslator.translateTag(charAttr, t, a)) - return; + // Initialize with link pseudo class. + if (t == HTML.Tag.A) + a.addAttribute(HTML.Attribute.PSEUDO_CLASS, "link"); // Just add the attributes in <code>a</code>. - if (a != null) - charAttr.addAttribute(t, a.copyAttributes()); + charAttr.addAttribute(t, a.copyAttributes()); } /** @@ -651,7 +829,11 @@ public class HTMLDocument extends DefaultStyledDocument popCharacterStyle(); } } - + + /** + * Processes elements that make up forms: <input>, <textarea>, + * <select> and <option>. + */ public class FormAction extends SpecialAction { /** @@ -659,10 +841,73 @@ public class HTMLDocument extends DefaultStyledDocument * of tags associated with this Action. */ public void start(HTML.Tag t, MutableAttributeSet a) - throws NotImplementedException { - // FIXME: Implement. - print ("FormAction.start not implemented"); + if (t == HTML.Tag.INPUT) + { + String type = (String) a.getAttribute(HTML.Attribute.TYPE); + if (type == null) + { + type = "text"; // Default to 'text' when nothing was specified. + a.addAttribute(HTML.Attribute.TYPE, type); + } + setModel(type, a); + } + else if (t == HTML.Tag.TEXTAREA) + { + inTextArea = true; + textAreaDocument = new ResetablePlainDocument(); + a.addAttribute(StyleConstants.ModelAttribute, textAreaDocument); + } + else if (t == HTML.Tag.SELECT) + { + int size = HTML.getIntegerAttributeValue(a, HTML.Attribute.SIZE, + 1); + boolean multi = a.getAttribute(HTML.Attribute.MULTIPLE) != null; + if (size > 1 || multi) + { + SelectListModel m = new SelectListModel(); + if (multi) + m.getSelectionModel().setSelectionMode(ListSelectionModel + .MULTIPLE_INTERVAL_SELECTION); + selectModel = m; + } + else + { + selectModel = new SelectComboBoxModel(); + } + a.addAttribute(StyleConstants.ModelAttribute, selectModel); + } + if (t == HTML.Tag.OPTION) + { + option = new Option(a); + if (selectModel instanceof SelectListModel) + { + SelectListModel m = (SelectListModel) selectModel; + m.addElement(option); + if (option.isSelected()) + { + m.getSelectionModel().addSelectionInterval(numOptions, + numOptions); + m.addInitialSelection(numOptions); + } + } + else if (selectModel instanceof SelectComboBoxModel) + { + SelectComboBoxModel m = (SelectComboBoxModel) selectModel; + m.addElement(option); + if (option.isSelected()) + { + m.setSelectedItem(option); + m.setInitialSelection(option); + } + } + numOptions++; + } + else + { + // Build the element. + super.start(t, a); + } } /** @@ -670,13 +915,106 @@ public class HTMLDocument extends DefaultStyledDocument * with this Action. */ public void end(HTML.Tag t) - throws NotImplementedException { - // FIXME: Implement. - print ("FormAction.end not implemented"); + if (t == HTML.Tag.OPTION) + { + option = null; + } + else + { + if (t == HTML.Tag.TEXTAREA) + { + inTextArea = false; + } + else if (t == HTML.Tag.SELECT) + { + selectModel = null; + numOptions = 0; + } + // Finish the element. + super.end(t); + } + } + + private void setModel(String type, MutableAttributeSet attrs) + { + if (type.equals("submit") || type.equals("reset") + || type.equals("image")) + { + // Create button. + attrs.addAttribute(StyleConstants.ModelAttribute, + new DefaultButtonModel()); + } + else if (type.equals("text") || type.equals("password")) + { + String text = (String) attrs.getAttribute(HTML.Attribute.VALUE); + ResetablePlainDocument doc = new ResetablePlainDocument(); + if (text != null) + { + doc.setInitialText(text); + try + { + doc.insertString(0, text, null); + } + catch (BadLocationException ex) + { + // Shouldn't happen. + assert false; + } + } + attrs.addAttribute(StyleConstants.ModelAttribute, doc); + } + else if (type.equals("file")) + { + attrs.addAttribute(StyleConstants.ModelAttribute, + new PlainDocument()); + } + else if (type.equals("checkbox") || type.equals("radio")) + { + ResetableToggleButtonModel model = + new ResetableToggleButtonModel(); + if (attrs.getAttribute(HTML.Attribute.SELECTED) != null) + { + model.setSelected(true); + model.setInitial(true); + } + if (type.equals("radio")) + { + String name = (String) attrs.getAttribute(HTML.Attribute.NAME); + if (name != null) + { + if (buttonGroups == null) + buttonGroups = new HashMap(); + ButtonGroup group = (ButtonGroup) buttonGroups.get(name); + if (group == null) + { + group = new ButtonGroup(); + buttonGroups.put(name, group); + } + model.setGroup(group); + } + } + attrs.addAttribute(StyleConstants.ModelAttribute, model); + } + } + } + + /** + * Called for form tags. + */ + class FormTagAction + extends BlockAction + { + /** + * Clears the button group mapping. + */ + public void end(HTML.Tag t) + { + super.end(t); + buttonGroups = null; } } - + /** * This action indicates that the content between starting and closing HTML * elements (like script - /script) should not be visible. The content is @@ -707,7 +1045,10 @@ public class HTMLDocument extends DefaultStyledDocument blockClose(t); } } - + + /** + * Handles <isindex> tags. + */ public class IsindexAction extends TagAction { /** @@ -715,10 +1056,10 @@ public class HTMLDocument extends DefaultStyledDocument * of tags associated with this Action. */ public void start(HTML.Tag t, MutableAttributeSet a) - throws NotImplementedException { - // FIXME: Implement. - print ("IsindexAction.start not implemented"); + blockOpen(HTML.Tag.IMPLIED, new SimpleAttributeSet()); + addSpecialElement(t, a); + blockClose(HTML.Tag.IMPLIED); } } @@ -730,7 +1071,7 @@ public class HTMLDocument extends DefaultStyledDocument */ public void start(HTML.Tag t, MutableAttributeSet a) { - blockOpen(t, a); + super.start(t, a); } /** @@ -739,10 +1080,13 @@ public class HTMLDocument extends DefaultStyledDocument */ public void end(HTML.Tag t) { - blockClose(t); + super.end(t); } } - + + /** + * This action is performed when a <pre> tag is parsed. + */ public class PreAction extends BlockAction { /** @@ -750,11 +1094,11 @@ public class HTMLDocument extends DefaultStyledDocument * of tags associated with this Action. */ public void start(HTML.Tag t, MutableAttributeSet a) - throws NotImplementedException { - // FIXME: Implement. - print ("PreAction.start not implemented"); - super.start(t, a); + inPreTag = true; + blockOpen(t, a); + a.addAttribute(CSS.Attribute.WHITE_SPACE, "pre"); + blockOpen(HTML.Tag.IMPLIED, a); } /** @@ -762,11 +1106,10 @@ public class HTMLDocument extends DefaultStyledDocument * with this Action. */ public void end(HTML.Tag t) - throws NotImplementedException { - // FIXME: Implement. - print ("PreAction.end not implemented"); - super.end(t); + blockClose(HTML.Tag.IMPLIED); + inPreTag = false; + blockClose(t); } } @@ -798,7 +1141,6 @@ public class HTMLDocument extends DefaultStyledDocument throws NotImplementedException { // FIXME: Implement. - print ("AreaAction.start not implemented"); } /** @@ -809,10 +1151,44 @@ public class HTMLDocument extends DefaultStyledDocument throws NotImplementedException { // FIXME: Implement. - print ("AreaAction.end not implemented"); } } - + + /** + * Converts HTML tags to CSS attributes. + */ + class ConvertAction + extends TagAction + { + + public void start(HTML.Tag tag, MutableAttributeSet atts) + { + pushCharacterStyle(); + charAttr.addAttribute(tag, atts.copyAttributes()); + StyleSheet styleSheet = getStyleSheet(); + // TODO: Add other tags here. + if (tag == HTML.Tag.FONT) + { + String color = (String) atts.getAttribute(HTML.Attribute.COLOR); + if (color != null) + styleSheet.addCSSAttribute(charAttr, CSS.Attribute.COLOR, color); + String face = (String) atts.getAttribute(HTML.Attribute.FACE); + if (face != null) + styleSheet.addCSSAttribute(charAttr, CSS.Attribute.FONT_FAMILY, + face); + String size = (String) atts.getAttribute(HTML.Attribute.SIZE); + if (size != null) + styleSheet.addCSSAttribute(charAttr, CSS.Attribute.FONT_SIZE, + size); + } + } + + public void end(HTML.Tag tag) + { + popCharacterStyle(); + } + } + class BaseAction extends TagAction { /** @@ -820,22 +1196,9 @@ public class HTMLDocument extends DefaultStyledDocument * of tags associated with this Action. */ public void start(HTML.Tag t, MutableAttributeSet a) - throws NotImplementedException { - // FIXME: Implement. - print ("BaseAction.start not implemented"); + baseTarget = (String) a.getAttribute(HTML.Attribute.TARGET); } - - /** - * Called when an end tag is seen for one of the types of tags associated - * with this Action. - */ - public void end(HTML.Tag t) - throws NotImplementedException - { - // FIXME: Implement. - print ("BaseAction.end not implemented"); - } } class HeadAction extends BlockAction @@ -848,7 +1211,6 @@ public class HTMLDocument extends DefaultStyledDocument throws NotImplementedException { // FIXME: Implement. - print ("HeadAction.start not implemented: "+t); super.start(t, a); } @@ -857,37 +1219,87 @@ public class HTMLDocument extends DefaultStyledDocument * with this Action. */ public void end(HTML.Tag t) - throws NotImplementedException { - // FIXME: Implement. - print ("HeadAction.end not implemented: "+t); + // We read in all the stylesheets that are embedded or referenced + // inside the header. + if (styles != null) + { + int numStyles = styles.size(); + for (int i = 0; i < numStyles; i++) + { + String style = (String) styles.get(i); + getStyleSheet().addRule(style); + } + } super.end(t); - } + } } - class LinkAction extends TagAction + class LinkAction extends HiddenAction { /** * This method is called when a start tag is seen for one of the types * of tags associated with this Action. */ public void start(HTML.Tag t, MutableAttributeSet a) - throws NotImplementedException { - // FIXME: Implement. - print ("LinkAction.start not implemented"); + super.start(t, a); + String type = (String) a.getAttribute(HTML.Attribute.TYPE); + if (type == null) + type = "text/css"; + if (type.equals("text/css")) + { + String rel = (String) a.getAttribute(HTML.Attribute.REL); + String media = (String) a.getAttribute(HTML.Attribute.MEDIA); + String title = (String) a.getAttribute(HTML.Attribute.TITLE); + if (media == null) + media = "all"; + else + media = media.toLowerCase(); + if (rel != null) + { + rel = rel.toLowerCase(); + if ((media.indexOf("all") != -1 + || media.indexOf("screen") != -1) + && (rel.equals("stylesheet"))) + { + String href = (String) a.getAttribute(HTML.Attribute.HREF); + URL url = null; + try + { + url = new URL(baseURL, href); + } + catch (MalformedURLException ex) + { + try + { + url = new URL(href); + } + catch (MalformedURLException ex2) + { + url = null; + } + } + if (url != null) + { + try + { + getStyleSheet().importStyleSheet(url); + } + catch (Exception ex) + { + // Don't let exceptions and runtime exceptions + // in CSS parsing disprupt the HTML parsing + // process. But inform the user/developer + // on the console about it. + ex.printStackTrace(); + } + } + } + } + } } - /** - * Called when an end tag is seen for one of the types of tags associated - * with this Action. - */ - public void end(HTML.Tag t) - throws NotImplementedException - { - // FIXME: Implement. - print ("LinkAction.end not implemented"); - } } class MapAction extends TagAction @@ -900,7 +1312,6 @@ public class HTMLDocument extends DefaultStyledDocument throws NotImplementedException { // FIXME: Implement. - print ("MapAction.start not implemented"); } /** @@ -911,7 +1322,6 @@ public class HTMLDocument extends DefaultStyledDocument throws NotImplementedException { // FIXME: Implement. - print ("MapAction.end not implemented"); } } @@ -925,7 +1335,6 @@ public class HTMLDocument extends DefaultStyledDocument throws NotImplementedException { // FIXME: Implement. - print ("MetaAction.start not implemented"); } /** @@ -936,10 +1345,9 @@ public class HTMLDocument extends DefaultStyledDocument throws NotImplementedException { // FIXME: Implement. - print ("MetaAction.end not implemented"); } } - + class StyleAction extends TagAction { /** @@ -947,10 +1355,8 @@ public class HTMLDocument extends DefaultStyledDocument * of tags associated with this Action. */ public void start(HTML.Tag t, MutableAttributeSet a) - throws NotImplementedException { - // FIXME: Implement. - print ("StyleAction.start not implemented"); + inStyleTag = true; } /** @@ -958,10 +1364,8 @@ public class HTMLDocument extends DefaultStyledDocument * with this Action. */ public void end(HTML.Tag t) - throws NotImplementedException { - // FIXME: Implement. - print ("StyleAction.end not implemented"); + inStyleTag = false; } } @@ -975,7 +1379,6 @@ public class HTMLDocument extends DefaultStyledDocument throws NotImplementedException { // FIXME: Implement. - print ("TitleAction.start not implemented"); } /** @@ -986,7 +1389,6 @@ public class HTMLDocument extends DefaultStyledDocument throws NotImplementedException { // FIXME: Implement. - print ("TitleAction.end not implemented"); } } @@ -998,13 +1400,11 @@ public class HTMLDocument extends DefaultStyledDocument public HTMLReader(int offset, int popDepth, int pushDepth, HTML.Tag insertTag) { - print ("HTMLReader created with pop: "+popDepth - + " push: "+pushDepth + " offset: "+offset - + " tag: "+insertTag); this.insertTag = insertTag; this.offset = offset; this.popDepth = popDepth; this.pushDepth = pushDepth; + threshold = getTokenThreshold(); initTags(); } @@ -1028,7 +1428,7 @@ public class HTMLDocument extends DefaultStyledDocument StyleAction styleAction = new StyleAction(); TitleAction titleAction = new TitleAction(); - + ConvertAction convertAction = new ConvertAction(); tagToAction.put(HTML.Tag.A, characterAction); tagToAction.put(HTML.Tag.ADDRESS, characterAction); tagToAction.put(HTML.Tag.APPLET, hiddenAction); @@ -1051,8 +1451,8 @@ public class HTMLDocument extends DefaultStyledDocument tagToAction.put(HTML.Tag.DL, blockAction); tagToAction.put(HTML.Tag.DT, paragraphAction); tagToAction.put(HTML.Tag.EM, characterAction); - tagToAction.put(HTML.Tag.FONT, characterAction); - tagToAction.put(HTML.Tag.FORM, blockAction); + tagToAction.put(HTML.Tag.FONT, convertAction); + tagToAction.put(HTML.Tag.FORM, new FormTagAction()); tagToAction.put(HTML.Tag.FRAME, specialAction); tagToAction.put(HTML.Tag.FRAMESET, blockAction); tagToAction.put(HTML.Tag.H1, paragraphAction); @@ -1142,18 +1542,28 @@ public class HTMLDocument extends DefaultStyledDocument */ public void flush() throws BadLocationException { - DefaultStyledDocument.ElementSpec[] elements; - elements = new DefaultStyledDocument.ElementSpec[parseBuffer.size()]; - parseBuffer.copyInto(elements); - parseBuffer.removeAllElements(); - if (offset == 0) - create(elements); - else - insert(offset, elements); + flushImpl(); + } - offset += HTMLDocument.this.getLength() - offset; + /** + * Flushes the buffer and handle partial inserts. + * + */ + private void flushImpl() + throws BadLocationException + { + int oldLen = getLength(); + int size = parseBuffer.size(); + ElementSpec[] elems = new ElementSpec[size]; + parseBuffer.copyInto(elems); + if (oldLen == 0) + create(elems); + else + insert(offset, elems); + parseBuffer.removeAllElements(); + offset += getLength() - oldLen; } - + /** * This method is called by the parser to indicate a block of * text was encountered. Should insert the text appropriately. @@ -1163,8 +1573,24 @@ public class HTMLDocument extends DefaultStyledDocument */ public void handleText(char[] data, int pos) { - if (data != null && data.length > 0) - addContent(data, 0, data.length); + if (shouldInsert() && data != null && data.length > 0) + { + if (inTextArea) + textAreaContent(data); + else if (inPreTag) + preContent(data); + else if (option != null) + option.setLabel(new String(data)); + else if (inStyleTag) + { + if (styles == null) + styles = new ArrayList(); + styles.add(new String(data)); + } + else + addContent(data, 0, data.length); + + } } /** @@ -1214,8 +1640,7 @@ public class HTMLDocument extends DefaultStyledDocument TagAction action = (TagAction) tagToAction.get(HTML.Tag.COMMENT); if (action != null) { - action.start(HTML.Tag.COMMENT, - htmlAttributeSet.EMPTY_HTML_ATTRIBUTE_SET); + action.start(HTML.Tag.COMMENT, new SimpleAttributeSet()); action.end(HTML.Tag.COMMENT); } } @@ -1277,7 +1702,6 @@ public class HTMLDocument extends DefaultStyledDocument public void handleEndOfLineString(String eol) { // FIXME: Implement. - print ("HTMLReader.handleEndOfLineString not implemented yet"); } /** @@ -1287,22 +1711,48 @@ public class HTMLDocument extends DefaultStyledDocument * @param data the text to add to the textarea */ protected void textAreaContent(char[] data) - throws NotImplementedException { - // FIXME: Implement. - print ("HTMLReader.textAreaContent not implemented yet"); + try + { + int offset = textAreaDocument.getLength(); + String text = new String(data); + textAreaDocument.setInitialText(text); + textAreaDocument.insertString(offset, text, null); + } + catch (BadLocationException ex) + { + // Must not happen as we insert at a model location that we + // got from the document itself. + assert false; + } } /** * Adds the given text that was encountered in a <PRE> element. - * + * This adds synthesized lines to hold the text runs. + * * @param data the text */ protected void preContent(char[] data) - throws NotImplementedException { - // FIXME: Implement - print ("HTMLReader.preContent not implemented yet"); + int start = 0; + for (int i = 0; i < data.length; i++) + { + if (data[i] == '\n') + { + addContent(data, start, i - start + 1); + blockClose(HTML.Tag.IMPLIED); + MutableAttributeSet atts = new SimpleAttributeSet(); + atts.addAttribute(CSS.Attribute.WHITE_SPACE, "pre"); + blockOpen(HTML.Tag.IMPLIED, atts); + start = i + 1; + } + } + if (start < data.length) + { + // Add remaining last line. + addContent(data, start, data.length - start); + } } /** @@ -1314,17 +1764,48 @@ public class HTMLDocument extends DefaultStyledDocument */ protected void blockOpen(HTML.Tag t, MutableAttributeSet attr) { - printBuffer(); - DefaultStyledDocument.ElementSpec element; + if (inImpliedParagraph()) + blockClose(HTML.Tag.IMPLIED); + // Push the new tag on top of the stack. parseStack.push(t); + + DefaultStyledDocument.ElementSpec element; + AbstractDocument.AttributeContext ctx = getAttributeContext(); AttributeSet copy = attr.copyAttributes(); copy = ctx.addAttribute(copy, StyleConstants.NameAttribute, t); element = new DefaultStyledDocument.ElementSpec(copy, DefaultStyledDocument.ElementSpec.StartTagType); parseBuffer.addElement(element); - printBuffer(); + } + + /** + * Returns true when we are currently inside a paragraph, either + * a real one or an implied, false otherwise. + * + * @return + */ + private boolean inParagraph() + { + boolean inParagraph = false; + if (! parseStack.isEmpty()) + { + HTML.Tag top = parseStack.peek(); + inParagraph = top == HTML.Tag.P || top == HTML.Tag.IMPLIED; + } + return inParagraph; + } + + private boolean inImpliedParagraph() + { + boolean inParagraph = false; + if (! parseStack.isEmpty()) + { + HTML.Tag top = parseStack.peek(); + inParagraph = top == HTML.Tag.IMPLIED; + } + return inParagraph; } /** @@ -1335,32 +1816,29 @@ public class HTMLDocument extends DefaultStyledDocument */ protected void blockClose(HTML.Tag t) { - printBuffer(); DefaultStyledDocument.ElementSpec element; + if (inImpliedParagraph() && t != HTML.Tag.IMPLIED) + blockClose(HTML.Tag.IMPLIED); + + // Pull the token from the stack. + if (! parseStack.isEmpty()) // Just to be sure. + parseStack.pop(); + // If the previous tag is a start tag then we insert a synthetic // content tag. DefaultStyledDocument.ElementSpec prev; - prev = (DefaultStyledDocument.ElementSpec) - parseBuffer.get(parseBuffer.size() - 1); - if (prev.getType() == DefaultStyledDocument.ElementSpec.StartTagType) + prev = parseBuffer.size() > 0 ? (DefaultStyledDocument.ElementSpec) + parseBuffer.get(parseBuffer.size() - 1) : null; + if (prev != null && + prev.getType() == DefaultStyledDocument.ElementSpec.StartTagType) { - AbstractDocument.AttributeContext ctx = getAttributeContext(); - AttributeSet attributes = ctx.getEmptySet(); - attributes = ctx.addAttribute(attributes, StyleConstants.NameAttribute, - HTML.Tag.CONTENT); - element = new DefaultStyledDocument.ElementSpec(attributes, - DefaultStyledDocument.ElementSpec.ContentType, - new char[0], 0, 0); - parseBuffer.add(element); + addContent(new char[]{' '}, 0, 1); } element = new DefaultStyledDocument.ElementSpec(null, DefaultStyledDocument.ElementSpec.EndTagType); parseBuffer.addElement(element); - printBuffer(); - if (parseStack.size() > 0) - parseStack.pop(); } /** @@ -1389,6 +1867,11 @@ public class HTMLDocument extends DefaultStyledDocument protected void addContent(char[] data, int offs, int length, boolean generateImpliedPIfNecessary) { + if (generateImpliedPIfNecessary && ! inParagraph()) + { + blockOpen(HTML.Tag.IMPLIED, new SimpleAttributeSet()); + } + AbstractDocument.AttributeContext ctx = getAttributeContext(); DefaultStyledDocument.ElementSpec element; AttributeSet attributes = null; @@ -1405,16 +1888,16 @@ public class HTMLDocument extends DefaultStyledDocument DefaultStyledDocument.ElementSpec.ContentType, data, offs, length); - printBuffer(); // Add the element to the buffer parseBuffer.addElement(element); - printBuffer(); - if (parseBuffer.size() > HTMLDocument.this.getTokenThreshold()) + if (parseBuffer.size() > threshold) { + if (threshold <= MAX_THRESHOLD) + threshold *= GROW_THRESHOLD; try { - flush(); + flushImpl(); } catch (BadLocationException ble) { @@ -1431,29 +1914,23 @@ public class HTMLDocument extends DefaultStyledDocument */ protected void addSpecialElement(HTML.Tag t, MutableAttributeSet a) { + if (t != HTML.Tag.FRAME && ! inParagraph()) + { + blockOpen(HTML.Tag.IMPLIED, new SimpleAttributeSet()); + } + a.addAttribute(StyleConstants.NameAttribute, t); - // Migrate from the rather htmlAttributeSet to the faster, lighter and - // unchangeable alternative implementation. - AttributeSet copy = a.copyAttributes(); - // The two spaces are required because some special elements like HR // must be broken. At least two characters are needed to break into the // two parts. DefaultStyledDocument.ElementSpec spec = - new DefaultStyledDocument.ElementSpec(copy, + new DefaultStyledDocument.ElementSpec(a.copyAttributes(), DefaultStyledDocument.ElementSpec.ContentType, - new char[] {' ', ' '}, 0, 2 ); + new char[] {' '}, 0, 1 ); parseBuffer.add(spec); } - void printBuffer() - { - print ("\n*********BUFFER**********"); - for (int i = 0; i < parseBuffer.size(); i ++) - print (" "+parseBuffer.get(i)); - print ("***************************"); - } } /** @@ -1533,10 +2010,6 @@ public class HTMLDocument extends DefaultStyledDocument } }; - // Set the parent HTML tag. - reader.parseStack.push(parent.getAttributes().getAttribute( - StyleConstants.NameAttribute)); - return reader; } @@ -1728,4 +2201,98 @@ public void setOuterHTML(Element elem, String htmlText) // TODO charset getParser().parse(new StringReader(htmlText), reader, true); } + + /** + * Overridden to tag content with the synthetic HTML.Tag.CONTENT + * tag. + */ + protected void insertUpdate(DefaultDocumentEvent evt, AttributeSet att) + { + if (att == null) + { + SimpleAttributeSet sas = new SimpleAttributeSet(); + sas.addAttribute(StyleConstants.NameAttribute, HTML.Tag.CONTENT); + att = sas; + } + super.insertUpdate(evt, att); + } + + /** + * Returns <code>true</code> when this document is inside a frame, + * <code>false</code> otherwise. + * + * @return <code>true</code> when this document is inside a frame, + * <code>false</code> otherwise + */ + boolean isFrameDocument() + { + return frameDocument; + } + + /** + * Set <code>true</code> when this document is inside a frame, + * <code>false</code> otherwise. + * + * @param frameDoc <code>true</code> when this document is inside a frame, + * <code>false</code> otherwise + */ + void setFrameDocument(boolean frameDoc) + { + frameDocument = frameDoc; + } + + /** + * Returns the target that is specified in the base tag, if this is the case. + * + * @return the target that is specified in the base tag, if this is the case + */ + String getBaseTarget() + { + return baseTarget; + } + + /** + * Updates the A tag's pseudo class value in response to a hyperlink + * action. + * + * @param el the corresponding element + * @param value the new value + */ + void updateSpecialClass(Element el, HTML.Attribute cl, String value) + { + try + { + writeLock(); + DefaultDocumentEvent ev = + new DefaultDocumentEvent(el.getStartOffset(), 1, + DocumentEvent.EventType.CHANGE); + AttributeSet elAtts = el.getAttributes(); + AttributeSet anchorAtts = (AttributeSet) elAtts.getAttribute(HTML.Tag.A); + if (anchorAtts != null) + { + AttributeSet copy = elAtts.copyAttributes(); + StyleSheet ss = getStyleSheet(); + if (value != null) + { + anchorAtts = ss.addAttribute(anchorAtts, cl, value); + } + else + { + anchorAtts = ss.removeAttribute(anchorAtts, cl); + } + MutableAttributeSet matts = (MutableAttributeSet) elAtts; + ev.addEdit(new AttributeUndoableEdit(el, copy, false)); + matts.removeAttribute(HTML.Tag.A); + matts.addAttribute(HTML.Tag.A, anchorAtts); + ev.end(); + fireChangedUpdate(ev); + fireUndoableEditUpdate(new UndoableEditEvent(this, ev)); + } + } + finally + { + writeUnlock(); + } + } + } |