This implements basic form submit support for the Swing html package.
You can now try and start a google search (although, the results pages
render quite ugly right now).
Coincidentally this API-completes the package against JDK1.5. :-)
2006-11-30 Roman Kennke <[EMAIL PROTECTED]>
* javax/swing/text/html/FormSubmitEvent.java: New class.
* javax/swing/text/html/FormView.java
(SubmitThread): New class for submitting data in a separate thread.
(actionPerformed): Fetch the actual for data.
(addData): New helper method.
(getElementFormData): New helper method.
(getFormData): New helper method.
(getInputFormData): New helper method.
(submitData): Implemented.
* javax/swing/text/html/FrameView.java
(createComponent): Add this as hyperlink listener.
Set the target document as frame document.
(getTopEditorPane): New helper method.
(hyperlinkUpdate): Implementation of the HyperlinkListener interface.
(handleHyperlinkEvent): New helper method.
(handleFormSubmitEvent): New helper method.
* javax/swing/text/html/HTMLDocument.java
(HTMLReader.BaseAction.start): Track the base target.
(HTMLReader.BaseAction.end): Removed.
(baseTarget): New field.
(frameDocument): New field.
(getBaseTarget): New property accessor.
(isFrameDocument): New property accessor.
(processHTMLFrameHyperlinkEvent): Implemented.
(setFrameDocument): New property accessor.
(updateFrame): New helper method.
(updateFrameSet): New helper method.
* javax/swing/text/html/HTMLEditorKit.java
(LinkController.createHyperlinkEvent): Handle frame documents.
(autoFormSubmission): New field.
(HTMLEditorKit): Set autoFormSubmission to true.
(isAutoFormSubmission): New property accessor.
(setAutoFormSubmission): New property accessor.
/Roman
Index: javax/swing/text/html/FormSubmitEvent.java
===================================================================
RCS file: javax/swing/text/html/FormSubmitEvent.java
diff -N javax/swing/text/html/FormSubmitEvent.java
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ javax/swing/text/html/FormSubmitEvent.java 30 Nov 2006 19:37:07 -0000
@@ -0,0 +1,123 @@
+/* FormSubmitEvent.java -- Event fired on form submit
+ Copyright (C) 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package javax.swing.text.html;
+
+import java.net.URL;
+
+import javax.swing.text.Element;
+
+/**
+ * The event fired on form submit.
+ *
+ * @since 1.5
+ */
+public class FormSubmitEvent
+ extends HTMLFrameHyperlinkEvent
+{
+
+ // FIXME: Use enums when available.
+ /**
+ * The submit method.
+ */
+ public static class MethodType
+ {
+ /**
+ * Indicates a form submit with HTTP method POST.
+ */
+ public static final MethodType POST = new MethodType();
+
+ /**
+ * Indicates a form submit with HTTP method GET.
+ */
+ public static final MethodType GET = new MethodType();
+
+ private MethodType()
+ {
+ }
+ }
+
+ /**
+ * The submit method.
+ */
+ private MethodType method;
+
+ /**
+ * The actual submit data.
+ */
+ private String data;
+
+ /**
+ * Creates a new FormSubmitEvent.
+ *
+ * @param source the source
+ * @param type the type of hyperlink update
+ * @param url the action url
+ * @param el the associated element
+ * @param target the target attribute
+ * @param m the submit method
+ * @param d the submit data
+ */
+ FormSubmitEvent(Object source, EventType type, URL url, Element el,
+ String target, MethodType m, String d)
+ {
+ super(source, type, url, el, target);
+ method = m;
+ data = d;
+ }
+
+ /**
+ * Returns the submit data.
+ *
+ * @return the submit data
+ */
+ public String getData()
+ {
+ return data;
+ }
+
+ /**
+ * Returns the HTTP submit method.
+ *
+ * @return the HTTP submit method
+ */
+ public MethodType getMethod()
+ {
+ return method;
+ }
+}
Index: javax/swing/text/html/FormView.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/text/html/FormView.java,v
retrieving revision 1.5
diff -u -1 -5 -r1.5 FormView.java
--- javax/swing/text/html/FormView.java 3 Nov 2006 14:00:42 -0000 1.5
+++ javax/swing/text/html/FormView.java 30 Nov 2006 19:37:07 -0000
@@ -32,48 +32,56 @@
module. An independent module is a module which is not derived from
or based on this library. If you modify this library, you may extend
this exception to your version of the library, but you are not
obligated to do so. If you do not wish to do so, delete this
exception statement from your version. */
package javax.swing.text.html;
import java.awt.Component;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
+import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
+import java.net.URLConnection;
+import java.net.URLEncoder;
import javax.swing.ButtonModel;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
+import javax.swing.JEditorPane;
import javax.swing.JPasswordField;
import javax.swing.JRadioButton;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.JToggleButton;
+import javax.swing.SwingUtilities;
import javax.swing.UIManager;
+import javax.swing.event.HyperlinkEvent;
import javax.swing.text.AttributeSet;
+import javax.swing.text.BadLocationException;
import javax.swing.text.ComponentView;
import javax.swing.text.Document;
import javax.swing.text.Element;
+import javax.swing.text.ElementIterator;
import javax.swing.text.StyleConstants;
/**
* A View that renders HTML form elements like buttons and input fields.
* This is implemented as a [EMAIL PROTECTED] ComponentView} that creates different Swing
* component depending on the type and setting of the different form elements.
*
* Namely, this view creates the following components:
* <table>
* <tr><th>Element type</th><th>Swing component</th></tr>
* <tr><td>input, button</td><td>JButton</td></tr>
* <tr><td>input, checkbox</td><td>JButton</td></tr>
* <tr><td>input, image</td><td>JButton</td></tr>
* <tr><td>input, password</td><td>JButton</td></tr>
* <tr><td>input, radio</td><td>JButton</td></tr>
@@ -101,30 +109,238 @@
* Creates a new <code>MouseEventListener</code>.
*/
protected MouseEventListener()
{
// Nothing to do here.
}
public void mouseReleased(MouseEvent ev)
{
String data = getImageData(ev.getPoint());
imageSubmit(data);
}
}
/**
+ * Actually submits the form data.
+ */
+ private class SubmitThread
+ extends Thread
+ {
+ /**
+ * The submit data.
+ */
+ private String data;
+
+ /**
+ * Creates a new SubmitThread.
+ *
+ * @param d the submit data
+ */
+ SubmitThread(String d)
+ {
+ data = d;
+ }
+
+ /**
+ * Actually performs the submit.
+ */
+ public void run()
+ {
+ if (data.length() > 0)
+ {
+ final String method = getMethod();
+ final URL actionURL = getActionURL();
+ final String target = getTarget();
+ URLConnection conn;
+ final JEditorPane editor = (JEditorPane) getContainer();
+ final HTMLDocument doc = (HTMLDocument) editor.getDocument();
+ HTMLEditorKit kit = (HTMLEditorKit) editor.getEditorKit();
+ if (kit.isAutoFormSubmission())
+ {
+ try
+ {
+ final URL url;
+ if (method != null && method.equals("post"))
+ {
+ // Perform POST.
+ url = actionURL;
+ conn = url.openConnection();
+ postData(conn);
+ }
+ else
+ {
+ // Default to GET.
+ url = new URL(actionURL + "?" + data);
+ }
+ Runnable loadDoc = new Runnable()
+ {
+ public void run()
+ {
+ if (doc.isFrameDocument())
+ {
+ editor.fireHyperlinkUpdate(createSubmitEvent(method,
+ actionURL,
+ target));
+ }
+ else
+ {
+ try
+ {
+ editor.setPage(url);
+ }
+ catch (IOException ex)
+ {
+ // Oh well.
+ ex.printStackTrace();
+ }
+ }
+ }
+ };
+ SwingUtilities.invokeLater(loadDoc);
+ }
+ catch (MalformedURLException ex)
+ {
+ ex.printStackTrace();
+ }
+ catch (IOException ex)
+ {
+ ex.printStackTrace();
+ }
+ }
+ else
+ {
+ editor.fireHyperlinkUpdate(createSubmitEvent(method,actionURL,
+ target));
+ }
+ }
+ }
+
+ /**
+ * Determines the submit method.
+ *
+ * @return the submit method
+ */
+ private String getMethod()
+ {
+ AttributeSet formAtts = getFormAttributes();
+ String method = null;
+ if (formAtts != null)
+ {
+ method = (String) formAtts.getAttribute(HTML.Attribute.METHOD);
+ }
+ return method;
+ }
+
+ /**
+ * Determines the action URL.
+ *
+ * @return the action URL
+ */
+ private URL getActionURL()
+ {
+ AttributeSet formAtts = getFormAttributes();
+ HTMLDocument doc = (HTMLDocument) getElement().getDocument();
+ URL url = doc.getBase();
+ if (formAtts != null)
+ {
+ String action =
+ (String) formAtts.getAttribute(HTML.Attribute.ACTION);
+ if (action != null)
+ {
+ try
+ {
+ url = new URL(url, action);
+ }
+ catch (MalformedURLException ex)
+ {
+ url = null;
+ }
+ }
+ }
+ return url;
+ }
+
+ /**
+ * Fetches the target attribute.
+ *
+ * @return the target attribute or _self if none is present
+ */
+ private String getTarget()
+ {
+ AttributeSet formAtts = getFormAttributes();
+ String target = null;
+ if (formAtts != null)
+ {
+ target = (String) formAtts.getAttribute(HTML.Attribute.TARGET);
+ if (target != null)
+ target = target.toLowerCase();
+ }
+ if (target == null)
+ target = "_self";
+ return target;
+ }
+
+ /**
+ * Posts the form data over the specified connection.
+ *
+ * @param conn the connection
+ */
+ private void postData(URLConnection conn)
+ {
+ // TODO: Implement.
+ }
+
+ /**
+ * Determines the attributes from the relevant form tag.
+ *
+ * @return the attributes from the relevant form tag, <code>null</code>
+ * when there is no form tag
+ */
+ private AttributeSet getFormAttributes()
+ {
+ AttributeSet atts = null;
+ Element form = getFormElement();
+ if (form != null)
+ atts = form.getAttributes();
+ return atts;
+ }
+
+ /**
+ * Creates the submit event that should be fired.
+ *
+ * This is package private to avoid accessor methods.
+ *
+ * @param method the submit method
+ * @param actionURL the action URL
+ * @param target the target
+ *
+ * @return the submit event
+ */
+ FormSubmitEvent createSubmitEvent(String method, URL actionURL,
+ String target)
+ {
+ FormSubmitEvent.MethodType m = "post".equals(method)
+ ? FormSubmitEvent.MethodType.POST
+ : FormSubmitEvent.MethodType.GET;
+ return new FormSubmitEvent(FormView.this,
+ HyperlinkEvent.EventType.ACTIVATED,
+ actionURL, getElement(), target, m, data);
+ }
+ }
+
+ /**
* If the value attribute of an <code><input type="submit">>
* tag is not specified, then this string is used.
*
* @deprecated As of JDK1.3 the value is fetched from the UIManager property
* <code>FormView.submitButtonText</code>.
*/
public static final String SUBMIT =
UIManager.getString("FormView.submitButtonText");
/**
* If the value attribute of an <code><input type="reset">>
* tag is not specified, then this string is used.
*
* @deprecated As of JDK1.3 the value is fetched from the UIManager property
* <code>FormView.resetButtonText</code>.
@@ -328,44 +544,45 @@
* then the input focus is transferred to the next input element in the form,
* unless this text/password field is the last one, in which case the form
* is submitted.
*
* @param ev the action event
*/
public void actionPerformed(ActionEvent ev)
{
Element el = getElement();
Object tag = el.getAttributes().getAttribute(StyleConstants.NameAttribute);
if (tag.equals(HTML.Tag.INPUT))
{
AttributeSet atts = el.getAttributes();
String type = (String) atts.getAttribute(HTML.Attribute.TYPE);
if (type.equals("submit"))
- submitData(""); // FIXME: How to fetch the actual form data?
+ submitData(getFormData());
}
// FIXME: Implement the remaining actions.
}
/**
* Submits the form data. A separate thread is created to do the
* transmission.
*
* @param data the form data
*/
protected void submitData(String data)
{
- // FIXME: Implement this.
+ SubmitThread submitThread = new SubmitThread(data);
+ submitThread.start();
}
/**
* Submits the form data in response to a click on a
* <code><input type="image"></code> element.
*
* @param imageData the mouse click coordinates
*/
protected void imageSubmit(String imageData)
{
// FIXME: Implement this.
}
/**
* Determines the image data that should be submitted in response to a
@@ -378,16 +595,152 @@
String getImageData(Point p)
{
String name = (String) getElement().getAttributes()
.getAttribute(HTML.Attribute.NAME);
String data;
if (name == null || name.equals(""))
{
data = "x=" + p.x + "&y=" + p.y;
}
else
{
data = name + ".x=" + p.x + "&" + name + ".y=" + p.y;
}
return data;
}
+
+ /**
+ * Determines and returns the enclosing form element if there is any.
+ *
+ * This is package private to avoid accessor methods.
+ *
+ * @return the enclosing form element, or <code>null</code> if there is no
+ * enclosing form element
+ */
+ Element getFormElement()
+ {
+ Element form = null;
+ Element el = getElement();
+ while (el != null && form == null)
+ {
+ AttributeSet atts = el.getAttributes();
+ if (atts.getAttribute(StyleConstants.NameAttribute) == HTML.Tag.FORM)
+ form = el;
+ else
+ el = el.getParentElement();
+ }
+ return form;
+ }
+
+ /**
+ * Determines the form data that is about to be submitted.
+ *
+ * @return the form data
+ */
+ private String getFormData()
+ {
+ Element form = getFormElement();
+ StringBuilder b = new StringBuilder();
+ if (form != null)
+ {
+ ElementIterator i = new ElementIterator(form);
+ Element next;
+ while ((next = i.next()) != null)
+ {
+ if (next.isLeaf())
+ {
+ AttributeSet atts = next.getAttributes();
+ String type = (String) atts.getAttribute(HTML.Attribute.TYPE);
+ if (type != null && type.equals("submit")
+ && next != getElement())
+ {
+ // Skip this. This is not the actual submit trigger.
+ }
+ else if (type == null || ! type.equals("image"))
+ {
+ getElementFormData(next, b);
+ }
+ }
+ }
+ }
+ return b.toString();
+ }
+
+ /**
+ * Fetches the form data from the specified element and appends it to
+ * the data string.
+ *
+ * @param el the element from which to fetch form data
+ * @param b the data string
+ */
+ private void getElementFormData(Element el, StringBuilder b)
+ {
+ AttributeSet atts = el.getAttributes();
+ String name = (String) atts.getAttribute(HTML.Attribute.NAME);
+ if (name != null)
+ {
+ String value = null;
+ HTML.Tag tag = (HTML.Tag) atts.getAttribute(StyleConstants.NameAttribute);
+ if (tag == HTML.Tag.INPUT)
+ value = getInputFormData(atts);
+ // TODO: Implement textarea and select.
+ if (name != null && value != null)
+ {
+ addData(b, name, value);
+ }
+ }
+ }
+
+ /**
+ * Fetches form data from an input tag.
+ *
+ * @param atts the attributes from which to fetch the data
+ *
+ * @return the field value
+ */
+ private String getInputFormData(AttributeSet atts)
+ {
+ String type = (String) atts.getAttribute(HTML.Attribute.TYPE);
+ Object model = atts.getAttribute(StyleConstants.ModelAttribute);
+ String value = null;
+ if (type.equals("text") || type.equals("password"))
+ {
+ Document doc = (Document) model;
+ try
+ {
+ value = doc.getText(0, doc.getLength());
+ }
+ catch (BadLocationException ex)
+ {
+ // Sigh.
+ assert false;
+ }
+ }
+ else if (type.equals("hidden") || type.equals("submit"))
+ {
+ value = (String) atts.getAttribute(HTML.Attribute.VALUE);
+ if (value == null)
+ value = "";
+ }
+ // TODO: Implement the others. radio, checkbox and file.
+ return value;
+ }
+
+ /**
+ * Actually adds the specified data to the string. It URL encodes
+ * the name and value and handles separation of the fields.
+ *
+ * @param b the string at which the form data to be added
+ * @param name the name of the field
+ * @param value the value
+ */
+ private void addData(StringBuilder b, String name, String value)
+ {
+ if (b.length() > 0)
+ b.append('&');
+ String encName = URLEncoder.encode(name);
+ b.append(encName);
+ b.append('=');
+ String encValue = URLEncoder.encode(value);
+ b.append(encValue);
+ }
}
Index: javax/swing/text/html/FrameView.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/text/html/FrameView.java,v
retrieving revision 1.1
diff -u -1 -5 -r1.1 FrameView.java
--- javax/swing/text/html/FrameView.java 30 Nov 2006 13:43:06 -0000 1.1
+++ javax/swing/text/html/FrameView.java 30 Nov 2006 19:37:07 -0000
@@ -32,71 +32,202 @@
module. An independent module is a module which is not derived from
or based on this library. If you modify this library, you may extend
this exception to your version of the library, but you are not
obligated to do so. If you do not wish to do so, delete this
exception statement from your version. */
package javax.swing.text.html;
import java.awt.Component;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import javax.swing.JEditorPane;
+import javax.swing.event.HyperlinkEvent;
+import javax.swing.event.HyperlinkListener;
import javax.swing.text.AttributeSet;
import javax.swing.text.ComponentView;
import javax.swing.text.Element;
+import javax.swing.text.View;
/**
* A view that is responsible for rendering HTML frame tags.
* This is accomplished by a specialized [EMAIL PROTECTED] ComponentView}
* that embeds a JEditorPane with an own document.
*/
class FrameView
extends ComponentView
+ implements HyperlinkListener
{
/**
* Creates a new FrameView for the specified element.
*
* @param el the element for the view
*/
FrameView(Element el)
{
super(el);
}
/**
* Creates the element that will be embedded in the view.
* This will be a JEditorPane with the appropriate content set.
*
* @return the element that will be embedded in the view
*/
protected Component createComponent()
{
Element el = getElement();
AttributeSet atts = el.getAttributes();
JEditorPane html = new JEditorPane();
+ html.addHyperlinkListener(this);
URL base = ((HTMLDocument) el.getDocument()).getBase();
String srcAtt = (String) atts.getAttribute(HTML.Attribute.SRC);
if (srcAtt != null && ! srcAtt.equals(""))
{
try
{
URL page = new URL(base, srcAtt);
html.setPage(page);
- System.err.println("loading: " + page);
+ ((HTMLDocument) html.getDocument()).setFrameDocument(true);
}
catch (MalformedURLException ex)
{
// Leave page empty.
}
catch (IOException ex)
{
// Leave page empty.
}
}
return html;
}
+
+ /**
+ * Catches hyperlink events on that frame's editor and forwards it to
+ * the outermost editorpane.
+ */
+ public void hyperlinkUpdate(HyperlinkEvent event)
+ {
+ JEditorPane outer = getTopEditorPane();
+ if (outer != null)
+ {
+ if (event instanceof HTMLFrameHyperlinkEvent)
+ {
+ HTMLFrameHyperlinkEvent hfhe = (HTMLFrameHyperlinkEvent) event;
+ if (hfhe.getEventType() == HyperlinkEvent.EventType.ACTIVATED)
+ {
+ String target = hfhe.getTarget();
+ if (event instanceof FormSubmitEvent)
+ {
+ handleFormSubmitEvent(hfhe, outer, target);
+ }
+ else // No FormSubmitEvent.
+ {
+ handleHyperlinkEvent(hfhe, outer, target);
+ }
+ }
+ }
+ else
+ {
+ // Simply forward this event.
+ outer.fireHyperlinkUpdate(event);
+ }
+ }
+ }
+
+ /**
+ * Handles normal hyperlink events.
+ *
+ * @param event the event
+ * @param outer the top editor
+ * @param target the target
+ */
+ private void handleHyperlinkEvent(HyperlinkEvent event,
+ JEditorPane outer, String target)
+ {
+ if (target.equals("_top"))
+ {
+ try
+ {
+ outer.setPage(event.getURL());
+ }
+ catch (IOException ex)
+ {
+ // Well...
+ ex.printStackTrace();
+ }
+ }
+ if (! outer.isEditable())
+ {
+ outer.fireHyperlinkUpdate
+ (new HTMLFrameHyperlinkEvent(outer,
+ event.getEventType(),
+ event.getURL(),
+ event.getDescription(),
+ getElement(),
+ target));
+ }
+ }
+
+ /**
+ * Handles form submit events.
+ *
+ * @param event the event
+ * @param outer the top editor
+ * @param target the target
+ */
+ private void handleFormSubmitEvent(HTMLFrameHyperlinkEvent event,
+ JEditorPane outer,
+ String target)
+ {
+ HTMLEditorKit kit = (HTMLEditorKit) outer.getEditorKit();
+ if (kit != null && kit.isAutoFormSubmission())
+ {
+ if (target.equals("_top"))
+ {
+ try
+ {
+ outer.setPage(event.getURL());
+ }
+ catch (IOException ex)
+ {
+ // Well...
+ ex.printStackTrace();
+ }
+ }
+ else
+ {
+ HTMLDocument doc =
+ (HTMLDocument) outer.getDocument();
+ doc.processHTMLFrameHyperlinkEvent(event);
+ }
+ }
+ else
+ {
+ outer.fireHyperlinkUpdate(event);
+ }
+ }
+
+ /**
+ * Determines the topmost editor in a nested frameset.
+ *
+ * @return the topmost editor in a nested frameset
+ */
+ private JEditorPane getTopEditorPane()
+ {
+ View parent = getParent();
+ View top = null;
+ while (parent != null)
+ {
+ if (parent instanceof FrameSetView)
+ top = parent;
+ }
+ JEditorPane editor = null;
+ if (top != null)
+ editor = (JEditorPane) top.getContainer();
+ return editor;
+ }
}
Index: javax/swing/text/html/HTMLDocument.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/text/html/HTMLDocument.java,v
retrieving revision 1.53
diff -u -1 -5 -r1.53 HTMLDocument.java
--- javax/swing/text/html/HTMLDocument.java 30 Nov 2006 13:43:06 -0000 1.53
+++ javax/swing/text/html/HTMLDocument.java 30 Nov 2006 19:37:08 -0000
@@ -40,30 +40,32 @@
import gnu.classpath.NotImplementedException;
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.DefaultButtonModel;
import javax.swing.JEditorPane;
import javax.swing.JToggleButton;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.UndoableEditEvent;
import javax.swing.text.AbstractDocument;
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.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;
/**
@@ -77,31 +79,41 @@
*
* @author Audrius Meskauskas ([EMAIL PROTECTED])
* @author Anthony Balkissoon ([EMAIL PROTECTED])
* @author Lillian Angel ([EMAIL PROTECTED])
*/
public class HTMLDocument extends DefaultStyledDocument
{
/** A key for document properies. The value for the key is
* a Vector of Strings of comments not found in the body.
*/
public static final String AdditionalComments = "AdditionalComments";
URL baseURL = null;
boolean preservesUnknownTags = true;
int tokenThreshold = Integer.MAX_VALUE;
HTMLEditorKit.Parser parser;
-
+
+ /**
+ * 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(new GapContent(BUFFER_SIZE_DEFAULT), new StyleSheet());
}
/**
* Constructs an HTML document with the default content storage
* implementation and the specified style/attribute storage mechanism.
*
* @param styles - the style sheet
*/
@@ -353,35 +365,143 @@
{
return it.current() != null;
}
/**
* Type of tag for this iterator.
*/
public Tag getTag()
{
return tag;
}
}
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
* @return the Iterator
*/
public HTMLDocument.Iterator getIterator (HTML.Tag t)
{
return new HTMLDocument.LeafIterator(t, this);
}
/**
* An iterator over a particular type of tag.
*/
public abstract static class Iterator
{
@@ -942,44 +1062,33 @@
}
public void end(HTML.Tag tag)
{
popCharacterStyle();
}
}
class BaseAction extends TagAction
{
/**
* 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.
+ 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.
- }
}
class HeadAction extends BlockAction
{
/**
* 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.
super.start(t, a);
}
@@ -1943,16 +2052,50 @@
/**
* 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;
+ }
}
Index: javax/swing/text/html/HTMLEditorKit.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/text/html/HTMLEditorKit.java,v
retrieving revision 1.43
diff -u -1 -5 -r1.43 HTMLEditorKit.java
--- javax/swing/text/html/HTMLEditorKit.java 30 Nov 2006 13:43:06 -0000 1.43
+++ javax/swing/text/html/HTMLEditorKit.java 30 Nov 2006 19:37:09 -0000
@@ -231,34 +231,49 @@
String href,
AttributeSet anchor,
Element el)
{
URL url;
try
{
URL base = doc.getBase();
url = new URL(base, href);
}
catch (MalformedURLException ex)
{
url = null;
}
- // TODO: Handle frame documents and target here.
- HyperlinkEvent ev =
- new HyperlinkEvent(editor, HyperlinkEvent.EventType.ACTIVATED, url,
- href, el);
+ HyperlinkEvent ev;
+ if (doc.isFrameDocument())
+ {
+ String target = null;
+ if (anchor != null)
+ target = (String) anchor.getAttribute(HTML.Attribute.TARGET);
+ if (target == null || target.equals(""))
+ target = doc.getBaseTarget();
+ if (target == null || target.equals(""))
+ target = "_self";
+ ev = new HTMLFrameHyperlinkEvent(editor,
+ HyperlinkEvent.EventType.ACTIVATED,
+ url, href, el, target);
+ }
+ else
+ {
+ ev = new HyperlinkEvent(editor, HyperlinkEvent.EventType.ACTIVATED,
+ url, href, el);
+ }
return ev;
}
}
/**
* This class is used to insert a string of HTML into an existing
* document. At least 2 HTML.Tags need to be supplied. The first Tag (parentTag)
* identifies the parent in the document to add the elements to. The second, (addTag),
* identifies that the first tag should be added to the document as seen in the string.
* The parser will generate all appropriate (opening/closing tags_ even if they are not
* in the HTML string passed in.
*/
public static class InsertHTMLTextAction
extends HTMLTextAction
{
@@ -1053,37 +1068,46 @@
Parser parser;
/**
* The mouse listener used for links.
*/
private LinkController linkController;
/** The content type */
String contentType = "text/html";
/** The input attributes defined by default.css */
MutableAttributeSet inputAttributes;
/** The editor pane used. */
JEditorPane editorPane;
-
+
+ /**
+ * Whether or not the editor kit handles form submissions.
+ *
+ * @see #isAutoFormSubmission()
+ * @see #setAutoFormSubmission(boolean)
+ */
+ private boolean autoFormSubmission;
+
/**
* Constructs an HTMLEditorKit, creates a StyleContext, and loads the style sheet.
*/
public HTMLEditorKit()
{
linkController = new LinkController();
+ autoFormSubmission = true;
}
/**
* Gets a factory suitable for producing views of any
* models that are produced by this kit.
*
* @return the view factory suitable for producing views.
*/
public ViewFactory getViewFactory()
{
if (viewFactory == null)
viewFactory = new HTMLFactory();
return viewFactory;
}
@@ -1403,16 +1427,52 @@
}
/**
* Set the set of styles to be used to render the various HTML elements.
* These styles are specified in terms of CSS specifications. Each document
* produced by the kit will have a copy of the sheet which it can add the
* document specific styles to. By default, the StyleSheet specified is shared
* by all HTMLEditorKit instances.
*
* @param s - the new style sheet
*/
public void setStyleSheet(StyleSheet s)
{
styleSheet = s;
}
+
+ /**
+ * Returns <code>true</code> when forms should be automatically submitted
+ * by the editor kit. Set this to <code>false</code> when you want to
+ * intercept form submission. In this case you'd want to listen for
+ * hyperlink events on the document and handle FormSubmitEvents specially.
+ *
+ * The default is <code>true</code>.
+ *
+ * @return <code>true</code> when forms should be automatically submitted
+ * by the editor kit, <code>false</code> otherwise
+ *
+ * @since 1.5
+ *
+ * @see #setAutoFormSubmission(boolean)
+ * @see FormSubmitEvent
+ */
+ public boolean isAutoFormSubmission()
+ {
+ return autoFormSubmission;
+ }
+
+ /**
+ * Sets whether or not the editor kit should automatically submit forms.
+ *
+ * @param auto <code>true</code> when the editor kit should handle form
+ * submission, <code>false</code> otherwise
+ *
+ * @since 1.5
+ *
+ * @see #isAutoFormSubmission()
+ */
+ public void setAutoFormSubmission(boolean auto)
+ {
+ autoFormSubmission = auto;
+ }
}