Tech Stuff - Java

Plenty of resources for Java folks on the web. This page documents a problem for which we failed to find (or steal, snaffle, purloin as you choose) a solution on the web. If it's useful feel free to use (steal, snaffle or purloin as you choose).

In short, we wanted to be able to intercept editing of certain INPUT tags on HTML FORMs within a JEditorPane and call specialized editors that provide unique formatting for the value(s) being edited immediately the user showed an intention of wishing to edit these fields. In our case possibly any one of 5 - 10 specialized editors are called, depending on the type of field being edited. These editors typically popup a Modal JDialog and offer content specific editing support, formatting and validation. Some fields we ignore, such as short text editing and SELECT tags and let the default editor handle them. While the processes described reflects our specific requirement they should work for anything with some mods which we note in the code snippet comments where sensible.

Note: We are not Java guys, necessity, as always, however, is the mother of invention. There may be be BQF (better, quicker, faster) and in the case of Java "purer" ways of doing this stuff that we failed to find. If so write us off as the Java neophytes that we willingly admit to being.

Contents - Notes

  1. Problem Description
  2. Finding the INPUT tags for the Triggers
  3. Handling The Triggers
  4. Form Submission (Submit button)

Problem Description

When using JEditorPane with HTML Forms (FORM tag) the editing function for INPUT tags with type="text" is handled in a JTextField, type="password" in a JPasswordField (SELECT tags use JComboBox and TEXTAREA uses JTextArea and yet others for check boxes and radio buttons). These editing components are not visible using the object hierarchy obtained from the HTMLDocument interface of JEditorPane. The HTMLDocument hierarchy terminates in a Document object. The Document interface supports documentListeners - all of which are triggered after the edit has been completed. Specifically, the Document interface does not support mouseListeners which would allow notification immediately the user enters a field or double clicks (or however you want to trigger the edit interface).

However, the JTextField, JPasswordField (and JComboBox, JTextArea and others) components do allow multiple listener types and specifically (in our case) mouseListeners. Interestingly, they also terminate in the Document object. Meaning that we can find the final Document by descending the HTMLDocument interface, and if we can find and set appropriate triggers on the JTextField (and others as required) we could get to the same Document.

The only remaining problem is the Document interface has no references back up its hierarchy to the HTMLDocument(for example, a getParent method), so if we reach the Document using the JTextField object we have no idea which INPUT tag it relates to and since it's the INPUT tag which contains interesting things like the name attribute which we want to use to invoke our selective editors.

However, all things are finally possible and the key to tying up the two access routes (via the HTMLDocument and separately via the JTextField/JPasswordField) lies in the Document object's get/putProperty methods. Read on for the gory details.

HTML Form Analysis and Set Triggers

The first step is to find the various INPUT tags in the FORM and track them down to the Document. In the same fragment we show finding the various JTextFields (using an incredibly useful scraper utility) that we want to use as mouseListener triggers. Here's the commented fragment showing what we did:

// somewhere on a distant planet we set up the JEditorPane and HTMLEditorKit
// and introduced them to each other
private JEditorPane editor = new JEditorPane();
// Custom HTMLEditorKit (see last snippet)
private SubmitHTMLForm htmlEditor = new SubmitHTMLForm(this, viewer);

editor.setEditorKitForContentType("text/html", htmlEditor);

// some private/public method that add a new HTML page 
private void setHTMLPageData(String htmlText){
   // htmlText may or may not contain a FORM
   // but is a fully formed HTML document
   // process the resultant HTML document
   HTMLDocument hd = (HTMLDocument)editor.getDocument();
   // get a reference to the FORM tag if present starting from the document root
   Element elem = hd.getElement(hd.getDefaultRootElement(), 
              StyleConstants.NameAttribute, HTML.Tag.FORM);
   if(elem != null){
       // we have a form tag
       // now find all the input tags of type=text/password
       ElementIterator it = new ElementIterator(elem);
       Element input = null;
       while((input = it.next()) != null ){
         HTML.Tag tag = (HTML.Tag)input.getAttributes().getAttribute(StyleConstants.NameAttribute);
         // we are only interested in INPUT tags - add SELECT/TEXTAREA as required
         // normally tons of TABLE, TR and TD tags as well cluttering up the layout
         if (tag == HTML.Tag.INPUT) {
            // get the type HTML attribute     		
            String type = (String)input.getAttributes().getAttribute(HTML.Attribute.TYPE);
            // filter out the types we are interested in
            if(type.equals("text") || type.equals("password")){
                AttributeSet attr = input.getAttributes();
                // now get the name= HTML attribute which is the unique data we are interested in
                // but could be , say, the value= HTML attribute
                String name = (String)attr.getAttribute(HTML.Attribute.NAME);
                // oops - no name lets get out of here PDQ
                if (name != null) {
                     // get the Document object for this INPUT tag         
                     Document doc = (Document)attr.getAttribute(StyleConstants.ModelAttribute);
                     // add name= of INPUT tag to the document - will be retrieved by mouseListener 
                     // property is key and value (both objects - so lots of flexibility)
                     doc.putProperty(HTMLID, name); 		       
      // Now re-process the form looking for editing components
      // Find all the JTextFields/JPasswordFields in the current HTML document
      List<JTextField> tf = SwingUtils.getDescendantsOfType(JTextField.class, editor);
      for(JTextField f: tf){
         // you could use any event type supported by JTextField
         // we wanted a double-click-to-edit interface        	
 catch (Exception e) 
   // yes it can all go horribly wrong

Note: SwingUtils (written by Darryl Burke - no copyright claimed) is a fantastic 10 line function that will find any class in its container (JEditorPane). In this case we are using the SwingUtils.getDescendantsOfType to find the JTextField (also finds JPasswordField) from the fully rendered HTML document where we found the FORM tag. No other way to find these components (that we could find). Having found the target objects we simply add a mouseListener to them. The trivial mouse handling code is shown next.

Handle the JTextField Triggers

A nothing-special-mouseListener defined, obviously, in the same class as the above fragment:

     * mouseListener inherited methods
     * only interested in the mouseClicked event all the 
     * rest are null methods

    public void mouseClicked(MouseEvent e) {
      // click threshold reached?
      if(e.getClickCount() == 2){
         // get the event source
         JTextField tf = (JTextField) e.getSource();
         // and derive its Document interface
         Document doc = tf.getDocument();
         // extract the original HTML atttribute name
         String name = (String)doc.getProperty(HTMLID);
         if(name != null){
           // do some interesting stuff based (in our case) on the name

    public void mousePressed(MouseEvent e) {


    public void mouseReleased(MouseEvent e) {


    public void mouseEntered(MouseEvent e) {


    public void mouseExited(MouseEvent e) {


Form Submission (Submit Button)

Plenty of stuff about this scattered round the web but it's included here for the sake of completeness since the final handling of all the edits above comes only when the user submits the form.

The key to intercepting the Submit button in the HTML FORM is to extend the HTMLFactory of HTMLEditorKit and use this, in turn, to extend FormView which hooks the submitData method. A commented fragment is shown below:

// to get at HTMLFactory we need HTMLEditorKit
public class SubmitHTMLForm extends HTMLEditorKit
    private final ViewFactory viewFactory;
    public SubmitHTMLForm(...whatever construction params you need ...)
        //trigger creating the factory class
        viewFactory = new SubmitHTMLFactory(.....);

    // method returns our extented HTMLFactory
    public ViewFactory getViewFactory()
        return viewFactory; 
    // extended HTMLFactory
    public static class SubmitHTMLFactory extends HTMLEditorKit.HTMLFactory 
        public SubmitHTMLFactory(.. construction params as required ..)
        /** overload the create method to serve up our own version of FormView
         *  (optionally ImageView if required)
        public View create(Element elem) 
            // let the superclass do the hard work
            View v = super.create(elem);
            if (v instanceof FormView){
              // INPUT, SELECT and TEXTAREA tags generate FormView
              // we only require type=submit
              String type = (String)elem.getAttributes().getAttribute(HTML.Attribute.TYPE);
                 v = new HTMLFormView(elem, .. other params as required...); 
            return v;
		class HTMLFormView extends FormView
    public HTMLFormView(Element elem, .. other params as required by app ...) 

     *    This method over-rides the
     *    method in the standard class which would try to submit data to a web server
     *    @param data = HTML PUT query String
     *    Notes:
     *    1. Query string is in escaped HTML format (may need to decode to UTF-8 etc.)
     *    2. Series of name=value pairs for the INPUT, SELECT and TEXTAREA tags delimited by &
     *    3. The Submit button also appears in the Query String!
    protected void submitData(String data) 
        // do your stuff with the Query String, but decode first...maybe
        // ns = URLDecoder.decode(data, "UTF-8");