    rev 48858 : Work in progress for JDK-6303622
    Use generic in Swing components like JComboBox
    1. Added generic return type and parameter type for JComboBox.
    2. Modified JList related classes as well.
    3. Reverted the changes made to "getSelectedValues" as suggested by Joe.
    4. Modified files that were failing on Linux and Mac as well.

       1 /*
       2  * Copyright (c) 1998, 2017, Oracle and/or its affiliates. All rights reserved.
       3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       4  *
       5  * This code is free software; you can redistribute it and/or modify it
       6  * under the terms of the GNU General Public License version 2 only, as
       7  * published by the Free Software Foundation.  Oracle designates this
       8  * particular file as subject to the "Classpath" exception as provided
       9  * by Oracle in the LICENSE file that accompanied this code.
      10  *
      11  * This code is distributed in the hope that it will be useful, but WITHOUT
      12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
      13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
      14  * version 2 for more details (a copy is included in the LICENSE file that
      15  * accompanied this code).
      16  *
      17  * You should have received a copy of the GNU General Public License version
      18  * 2 along with this work; if not, write to the Free Software Foundation,
      19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
      20  *
      21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
      22  * or visit www.oracle.com if you need additional information or have any
      23  * questions.
      24  */
      25 package javax.swing.text.html;
      26
      27 import java.net.*;
      28 import java.io.*;
      29 import java.awt.*;
      30 import java.awt.event.*;
      31 import java.util.*;
      32 import javax.swing.*;
      33 import javax.swing.event.*;
      34 import javax.swing.text.*;
      35
      36 /**
      37  * Component decorator that implements the view interface
      38  * for form elements, &lt;input&gt;, &lt;textarea&gt;,
      39  * and &lt;select&gt;.  The model for the component is stored
      40  * as an attribute of the element (using StyleConstants.ModelAttribute),
      41  * and is used to build the component of the view.  The type
      42  * of the model is assumed to of the type that would be set by
      43  * <code>HTMLDocument.HTMLReader.FormAction</code>.  If there are
      44  * multiple views mapped over the document, they will share the
      45  * embedded component models.
      46  * <p>
      47  * The following table shows what components get built
      48  * by this view.
      49  *
      50  * <table class="striped">
      51  * <caption>Shows what components get built by this view</caption>
      52  * <thead>
      53  *   <tr>
      54  *     <th scope="col">Element Type
      55  *     <th scope="col">Component built
      56  * </thead>
      57  * <tbody>
      58  *   <tr>
      59  *     <th scope="row">input, type button
      60  *     <td>JButton
      61  *   <tr>
      62  *     <th scope="row">input, type checkbox
      63  *     <td>JCheckBox
      64  *   <tr>
      65  *     <th scope="row">input, type image
      66  *     <td>JButton
      67  *   <tr>
      68  *     <th scope="row">input, type password
      69  *     <td>JPasswordField
      70  *   <tr>
      71  *     <th scope="row">input, type radio
      72  *     <td>JRadioButton
      73  *   <tr>
      74  *     <th scope="row">input, type reset
      75  *     <td>JButton
      76  *   <tr>
      77  *     <th scope="row">input, type submit
      78  *     <td>JButton
      79  *   <tr>
      80  *     <th scope="row">input, type text
      81  *     <td>JTextField
      82  *   <tr>
      83  *     <th scope="row">select, size &gt; 1 or multiple attribute defined
      84  *     <td>JList in a JScrollPane
      85  *   <tr>
      86  *     <th scope="row">select, size unspecified or 1
      87  *     <td>JComboBox
      88  *   <tr>
      89  *     <th scope="row">textarea
      90  *     <td>JTextArea in a JScrollPane
      91  *   <tr>
      92  *     <th scope="row">input, type file
      93  *     <td>JTextField
      94  * </tbody>
      95  * </table>
      96  *
      97  * @author Timothy Prinzing
      98  * @author Sunita Mani
      99  */
     100 public class FormView extends ComponentView implements ActionListener {
     101
     102     /**
     103      * If a value attribute is not specified for a FORM input element
     104      * of type "submit", then this default string is used.
     105      *
     106      * @deprecated As of 1.3, value now comes from UIManager property
     107      *             FormView.submitButtonText
     108      */
     109     @Deprecated
     110     public static final String SUBMIT = new String("Submit Query");
     111     /**
     112      * If a value attribute is not specified for a FORM input element
     113      * of type "reset", then this default string is used.
     114      *
     115      * @deprecated As of 1.3, value comes from UIManager UIManager property
     116      *             FormView.resetButtonText
     117      */
     118     @Deprecated
     119     public static final String RESET = new String("Reset");
     120
     121     /**
     122      * Document attribute name for storing POST data. JEditorPane.getPostData()
     123      * uses the same name, should be kept in sync.
     124      */
     125     static final String PostDataProperty = "javax.swing.JEditorPane.postdata";
     126
     127     /**
     128      * Used to indicate if the maximum span should be the same as the
     129      * preferred span. This is used so that the Component's size doesn't
     130      * change if there is extra room on a line. The first bit is used for
     131      * the X direction, and the second for the y direction.
     132      */
     133     private short maxIsPreferred;
     134
     135     /**
     136      * Creates a new FormView object.
     137      *
     138      * @param elem the element to decorate
     139      */
     140     public FormView(Element elem) {
     141         super(elem);
     142     }
     143
     144     /**
     145      * Create the component.  This is basically a
     146      * big switch statement based upon the tag type
     147      * and html attributes of the associated element.
     148      */
     149     protected Component createComponent() {
     150         AttributeSet attr = getElement().getAttributes();
     151         HTML.Tag t = (HTML.Tag)
     152             attr.getAttribute(StyleConstants.NameAttribute);
     153         JComponent c = null;
     154         Object model = attr.getAttribute(StyleConstants.ModelAttribute);
     155
     156         // Remove listeners previously registered in shared model
     157         // when a new UI component is replaced.  See bug 7189299.
     158         removeStaleListenerForModel(model);
     159         if (t == HTML.Tag.INPUT) {
     160             c = createInputComponent(attr, model);
     161         } else if (t == HTML.Tag.SELECT) {
     162
     163             if (model instanceof OptionListModel) {
     164                 @SuppressWarnings("unchecked")
     165                 JList<?> list = new JList<>((ListModel) model);
     166                 int size = HTML.getIntegerAttributeValue(attr,
     167                                                          HTML.Attribute.SIZE,
     168                                                          1);
     169                 list.setVisibleRowCount(size);
     170                 list.setSelectionModel((ListSelectionModel)model);
     171                 c = new JScrollPane(list);
     172             } else {
     173                 @SuppressWarnings("unchecked")
     174                 JComboBox<?> tmp = new JComboBox<>((ComboBoxModel) model);
     175                 c = tmp;
     176                 maxIsPreferred = 3;
     177             }
     178         } else if (t == HTML.Tag.TEXTAREA) {
     179             JTextArea area = new JTextArea((Document) model);
     180             int rows = HTML.getIntegerAttributeValue(attr,
     181                                                      HTML.Attribute.ROWS,
     182                                                      1);
     183             area.setRows(rows);
     184             int cols = HTML.getIntegerAttributeValue(attr,
     185                                                      HTML.Attribute.COLS,
     186                                                      20);
     187             maxIsPreferred = 3;
     188             area.setColumns(cols);
     189             c = new JScrollPane(area,
     190                                 JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
     191                                 JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
     192         }
     193
     194         if (c != null) {
     195             c.setAlignmentY(1.0f);
     196         }
     197         return c;
     198     }
     199
     200
     201     /**
     202      * Creates a component for an &lt;INPUT&gt; element based on the
     203      * value of the "type" attribute.
     204      *
     205      * @param attr set of attributes associated with the &lt;INPUT&gt; element.
     206      * @param model the value of the StyleConstants.ModelAttribute
     207      * @return the component.
     208      */
     209     private JComponent createInputComponent(AttributeSet attr, Object model) {
     210         JComponent c = null;
     211         String type = (String) attr.getAttribute(HTML.Attribute.TYPE);
     212
     213         if (type.equals("submit") || type.equals("reset")) {
     214             String value = (String)
     215                 attr.getAttribute(HTML.Attribute.VALUE);
     216             if (value == null) {
     217                 if (type.equals("submit")) {
     218                     value = UIManager.getString("FormView.submitButtonText");
     219                 } else {
     220                     value = UIManager.getString("FormView.resetButtonText");
     221                 }
     222             }
     223             JButton button = new JButton(value);
     224             if (model != null) {
     225                 button.setModel((ButtonModel)model);
     226                 button.addActionListener(this);
     227             }
     228             c = button;
     229             maxIsPreferred = 3;
     230         } else if (type.equals("image")) {
     231             String srcAtt = (String) attr.getAttribute(HTML.Attribute.SRC);
     232             JButton button;
     233             try {
     234                 URL base = ((HTMLDocument)getElement().getDocument()).getBase();
     235                 URL srcURL = new URL(base, srcAtt);
     236                 Icon icon = new ImageIcon(srcURL);
     237                 button  = new JButton(icon);
     238             } catch (MalformedURLException e) {
     239                 button = new JButton(srcAtt);
     240             }
     241             if (model != null) {
     242                 button.setModel((ButtonModel)model);
     243                 button.addMouseListener(new MouseEventListener());
     244             }
     245             c = button;
     246             maxIsPreferred = 3;
     247         } else if (type.equals("checkbox")) {
     248             c = new JCheckBox();
     249             if (model != null) {
     250                 ((JCheckBox)c).setModel((JToggleButton.ToggleButtonModel) model);
     251             }
     252             maxIsPreferred = 3;
     253         } else if (type.equals("radio")) {
     254             c = new JRadioButton();
     255             if (model != null) {
     256                 ((JRadioButton)c).setModel((JToggleButton.ToggleButtonModel)model);
     257             }
     258             maxIsPreferred = 3;
     259         } else if (type.equals("text")) {
     260             int size = HTML.getIntegerAttributeValue(attr,
     261                                                      HTML.Attribute.SIZE,
     262                                                      -1);
     263             JTextField field;
     264             if (size > 0) {
     265                 field = new JTextField();
     266                 field.setColumns(size);
     267             }
     268             else {
     269                 field = new JTextField();
     270                 field.setColumns(20);
     271             }
     272             c = field;
     273             if (model != null) {
     274                 field.setDocument((Document) model);
     275             }
     276             field.addActionListener(this);
     277             maxIsPreferred = 3;
     278         } else if (type.equals("password")) {
     279             JPasswordField field = new JPasswordField();
     280             c = field;
     281             if (model != null) {
     282                 field.setDocument((Document) model);
     283             }
     284             int size = HTML.getIntegerAttributeValue(attr,
     285                                                      HTML.Attribute.SIZE,
     286                                                      -1);
     287             field.setColumns((size > 0) ? size : 20);
     288             field.addActionListener(this);
     289             maxIsPreferred = 3;
     290         } else if (type.equals("file")) {
     291             JTextField field = new JTextField();
     292             if (model != null) {
     293                 field.setDocument((Document)model);
     294             }
     295             int size = HTML.getIntegerAttributeValue(attr, HTML.Attribute.SIZE,
     296                                                      -1);
     297             field.setColumns((size > 0) ? size : 20);
     298             JButton browseButton = new JButton(UIManager.getString
     299                                            ("FormView.browseFileButtonText"));
     300             Box box = Box.createHorizontalBox();
     301             box.add(field);
     302             box.add(Box.createHorizontalStrut(5));
     303             box.add(browseButton);
     304             browseButton.addActionListener(new BrowseFileAction(
     305                                            attr, (Document)model));
     306             c = box;
     307             maxIsPreferred = 3;
     308         }
     309         return c;
     310     }
     311
     312     private void removeStaleListenerForModel(Object model) {
     313         if (model instanceof DefaultButtonModel) {
     314             // case of JButton whose model is DefaultButtonModel
     315             // Need to remove stale ActionListener, ChangeListener and
     316             // ItemListener that are instance of AbstractButton$Handler.
     317             DefaultButtonModel buttonModel = (DefaultButtonModel) model;
     318             String listenerClass = "javax.swing.AbstractButton$Handler";
     319             for (ActionListener listener : buttonModel.getActionListeners()) {
     320                 if (listenerClass.equals(listener.getClass().getName())) {
     321                     buttonModel.removeActionListener(listener);
     322                 }
     323             }
     324             for (ChangeListener listener : buttonModel.getChangeListeners()) {
     325                 if (listenerClass.equals(listener.getClass().getName())) {
     326                     buttonModel.removeChangeListener(listener);
     327                 }
     328             }
     329             for (ItemListener listener : buttonModel.getItemListeners()) {
     330                 if (listenerClass.equals(listener.getClass().getName())) {
     331                     buttonModel.removeItemListener(listener);
     332                 }
     333             }
     334         } else if (model instanceof AbstractListModel) {
     335             // case of JComboBox and JList
     336             // For JList, the stale ListDataListener is instance
     337             // BasicListUI$Handler.
     338             // For JComboBox, there are 2 stale ListDataListeners, which are
     339             // BasicListUI$Handler and BasicComboBoxUI$Handler.
     340             @SuppressWarnings("unchecked")
     341             AbstractListModel<?> listModel = (AbstractListModel) model;
     342             String listenerClass1 =
     343                     "javax.swing.plaf.basic.BasicListUI$Handler";
     344             String listenerClass2 =
     345                     "javax.swing.plaf.basic.BasicComboBoxUI$Handler";
     346             for (ListDataListener listener : listModel.getListDataListeners()) {
     347                 if (listenerClass1.equals(listener.getClass().getName())
     348                         || listenerClass2.equals(listener.getClass().getName()))
     349                 {
     350                     listModel.removeListDataListener(listener);
     351                 }
     352             }
     353         } else if (model instanceof AbstractDocument) {
     354             // case of JPasswordField, JTextField and JTextArea
     355             // All have 2 stale DocumentListeners.
     356             String listenerClass1 =
     357                     "javax.swing.plaf.basic.BasicTextUI$UpdateHandler";
     358             String listenerClass2 =
     359                     "javax.swing.text.DefaultCaret$Handler";
     360             AbstractDocument docModel = (AbstractDocument) model;
     361             for (DocumentListener listener : docModel.getDocumentListeners()) {
     362                 if (listenerClass1.equals(listener.getClass().getName())
     363                         || listenerClass2.equals(listener.getClass().getName()))
     364                 {
     365                     docModel.removeDocumentListener(listener);
     366                 }
     367             }
     368         }
     369     }
     370
     371     /**
     372      * Determines the maximum span for this view along an
     373      * axis. For certain components, the maximum and preferred span are the
     374      * same. For others this will return the value
     375      * returned by Component.getMaximumSize along the
     376      * axis of interest.
     377      *
     378      * @param axis may be either View.X_AXIS or View.Y_AXIS
     379      * @return   the span the view would like to be rendered into &gt;= 0.
     380      *           Typically the view is told to render into the span
     381      *           that is returned, although there is no guarantee.
     382      *           The parent may choose to resize or break the view.
     383      * @exception IllegalArgumentException for an invalid axis
     384      */
     385     public float getMaximumSpan(int axis) {
     386         switch (axis) {
     387         case View.X_AXIS:
     388             if ((maxIsPreferred & 1) == 1) {
     389                 super.getMaximumSpan(axis);
     390                 return getPreferredSpan(axis);
     391             }
     392             return super.getMaximumSpan(axis);
     393         case View.Y_AXIS:
     394             if ((maxIsPreferred & 2) == 2) {
     395                 super.getMaximumSpan(axis);
     396                 return getPreferredSpan(axis);
     397             }
     398             return super.getMaximumSpan(axis);
     399         default:
     400             break;
     401         }
     402         return super.getMaximumSpan(axis);
     403     }
     404
     405
     406     /**
     407      * Responsible for processing the ActionEvent.
     408      * If the element associated with the FormView,
     409      * has a type of "submit", "reset", "text" or "password"
     410      * then the action is processed.  In the case of a "submit"
     411      * the form is submitted.  In the case of a "reset"
     412      * the form is reset to its original state.
     413      * In the case of "text" or "password", if the
     414      * element is the last one of type "text" or "password",
     415      * the form is submitted.  Otherwise, focus is transferred
     416      * to the next component in the form.
     417      *
     418      * @param evt the ActionEvent.
     419      */
     420     public void actionPerformed(ActionEvent evt) {
     421         Element element = getElement();
     422         StringBuilder dataBuffer = new StringBuilder();
     423         HTMLDocument doc = (HTMLDocument)getDocument();
     424         AttributeSet attr = element.getAttributes();
     425
     426         String type = (String) attr.getAttribute(HTML.Attribute.TYPE);
     427
     428         if (type.equals("submit")) {
     429             getFormData(dataBuffer);
     430             submitData(dataBuffer.toString());
     431         } else if (type.equals("reset")) {
     432             resetForm();
     433         } else if (type.equals("text") || type.equals("password")) {
     434             if (isLastTextOrPasswordField()) {
     435                 getFormData(dataBuffer);
     436                 submitData(dataBuffer.toString());
     437             } else {
     438                 getComponent().transferFocus();
     439             }
     440         }
     441     }
     442
     443
     444     /**
     445      * This method is responsible for submitting the form data.
     446      * A thread is forked to undertake the submission.
     447      *
     448      * @param data data to submit
     449      */
     450     protected void submitData(String data) {
     451         Element form = getFormElement();
     452         AttributeSet attrs = form.getAttributes();
     453         HTMLDocument doc = (HTMLDocument) form.getDocument();
     454         URL base = doc.getBase();
     455
     456         String target = (String) attrs.getAttribute(HTML.Attribute.TARGET);
     457         if (target == null) {
     458             target = "_self";
     459         }
     460
     461         String method = (String) attrs.getAttribute(HTML.Attribute.METHOD);
     462         if (method == null) {
     463             method = "GET";
     464         }
     465         method = method.toLowerCase();
     466         boolean isPostMethod = method.equals("post");
     467         if (isPostMethod) {
     468             storePostData(doc, target, data);
     469         }
     470
     471         String action = (String) attrs.getAttribute(HTML.Attribute.ACTION);
     472         URL actionURL;
     473         try {
     474             actionURL = (action == null)
     475                 ? new URL(base.getProtocol(), base.getHost(),
     476                                         base.getPort(), base.getFile())
     477                 : new URL(base, action);
     478             if (!isPostMethod) {
     479                 String query = data.toString();
     480                 actionURL = new URL(actionURL + "?" + query);
     481             }
     482         } catch (MalformedURLException e) {
     483             actionURL = null;
     484         }
     485         final JEditorPane c = (JEditorPane) getContainer();
     486         HTMLEditorKit kit = (HTMLEditorKit) c.getEditorKit();
     487
     488         FormSubmitEvent formEvent = null;
     489         if (!kit.isAutoFormSubmission() || doc.isFrameDocument()) {
     490             FormSubmitEvent.MethodType methodType = isPostMethod
     491                     ? FormSubmitEvent.MethodType.POST
     492                     : FormSubmitEvent.MethodType.GET;
     493             formEvent = new FormSubmitEvent(
     494                     FormView.this, HyperlinkEvent.EventType.ACTIVATED,
     495                     actionURL, form, target, methodType, data);
     496
     497         }
     498         // setPage() may take significant time so schedule it to run later.
     499         final FormSubmitEvent fse = formEvent;
     500         final URL url = actionURL;
     501         SwingUtilities.invokeLater(new Runnable() {
     502             public void run() {
     503                 if (fse != null) {
     504                     c.fireHyperlinkUpdate(fse);
     505                 } else {
     506                     try {
     507                         c.setPage(url);
     508                     } catch (IOException e) {
     509                         UIManager.getLookAndFeel().provideErrorFeedback(c);
     510                     }
     511                 }
     512             }
     513         });
     514     }
     515
     516     private void storePostData(HTMLDocument doc, String target, String data) {
     517
     518         /* POST data is stored into the document property named by constant
     519          * PostDataProperty from where it is later retrieved by method
     520          * JEditorPane.getPostData().  If the current document is in a frame,
     521          * the data is initially put into the toplevel (frameset) document
     522          * property (named <PostDataProperty>.<Target frame name>).  It is the
     523          * responsibility of FrameView which updates the target frame
     524          * to move data from the frameset document property into the frame
     525          * document property.
     526          */
     527
     528         Document propDoc = doc;
     529         String propName = PostDataProperty;
     530
     531         if (doc.isFrameDocument()) {
     532             // find the top-most JEditorPane holding the frameset view.
     533             FrameView.FrameEditorPane p =
     534                     (FrameView.FrameEditorPane) getContainer();
     535             FrameView v = p.getFrameView();
     536             JEditorPane c = v.getOutermostJEditorPane();
     537             if (c != null) {
     538                 propDoc = c.getDocument();
     539                 propName += ("." + target);
     540             }
     541         }
     542
     543         propDoc.putProperty(propName, data);
     544     }
     545
     546     /**
     547      * MouseEventListener class to handle form submissions when
     548      * an input with type equal to image is clicked on.
     549      * A MouseListener is necessary since along with the image
     550      * data the coordinates associated with the mouse click
     551      * need to be submitted.
     552      */
     553     protected class MouseEventListener extends MouseAdapter {
     554
     555         public void mouseReleased(MouseEvent evt) {
     556             String imageData = getImageData(evt.getPoint());
     557             imageSubmit(imageData);
     558         }
     559     }
     560
     561     /**
     562      * This method is called to submit a form in response
     563      * to a click on an image -- an &lt;INPUT&gt; form
     564      * element of type "image".
     565      *
     566      * @param imageData the mouse click coordinates.
     567      */
     568     protected void imageSubmit(String imageData) {
     569
     570         StringBuilder dataBuffer = new StringBuilder();
     571         Element elem = getElement();
     572         HTMLDocument hdoc = (HTMLDocument)elem.getDocument();
     573         getFormData(dataBuffer);
     574         if (dataBuffer.length() > 0) {
     575             dataBuffer.append('&');
     576         }
     577         dataBuffer.append(imageData);
     578         submitData(dataBuffer.toString());
     579         return;
     580     }
     581
     582     /**
     583      * Extracts the value of the name attribute
     584      * associated with the input element of type
     585      * image.  If name is defined it is encoded using
     586      * the URLEncoder.encode() method and the
     587      * image data is returned in the following format:
     588      *      name + ".x" +"="+ x +"&"+ name +".y"+"="+ y
     589      * otherwise,
     590      *      "x="+ x +"&y="+ y
     591      *
     592      * @param point associated with the mouse click.
     593      * @return the image data.
     594      */
     595     @SuppressWarnings("deprecation")
     596     private String getImageData(Point point) {
     597
     598         String mouseCoords = point.x + ":" + point.y;
     599         int sep = mouseCoords.indexOf(':');
     600         String x = mouseCoords.substring(0, sep);
     601         String y = mouseCoords.substring(++sep);
     602         String name = (String) getElement().getAttributes().getAttribute(HTML.Attribute.NAME);
     603
     604         String data;
     605         if (name == null || name.equals("")) {
     606             data = "x="+ x +"&y="+ y;
     607         } else {
     608             name = URLEncoder.encode(name);
     609             data = name + ".x" +"="+ x +"&"+ name +".y"+"="+ y;
     610         }
     611         return data;
     612     }
     613
     614
     615     /**
     616      * The following methods provide functionality required to
     617      * iterate over a the elements of the form and in the case
     618      * of a form submission, extract the data from each model
     619      * that is associated with each form element, and in the
     620      * case of reset, reinitialize the each model to its
     621      * initial state.
     622      */
     623
     624
     625     /**
     626      * Returns the Element representing the <code>FORM</code>.
     627      */
     628     private Element getFormElement() {
     629         Element elem = getElement();
     630         while (elem != null) {
     631             if (elem.getAttributes().getAttribute
     632                 (StyleConstants.NameAttribute) == HTML.Tag.FORM) {
     633                 return elem;
     634             }
     635             elem = elem.getParentElement();
     636         }
     637         return null;
     638     }
     639
     640     /**
     641      * Iterates over the
     642      * element hierarchy, extracting data from the
     643      * models associated with the relevant form elements.
     644      * "Relevant" means the form elements that are part
     645      * of the same form whose element triggered the submit
     646      * action.
     647      *
     648      * @param buffer        the buffer that contains that data to submit
     649      */
     650     private void getFormData(StringBuilder buffer) {
     651         Element formE = getFormElement();
     652         if (formE != null) {
     653             ElementIterator it = new ElementIterator(formE);
     654             Element next;
     655
     656             while ((next = it.next()) != null) {
     657                 if (isControl(next)) {
     658                     String type = (String)next.getAttributes().getAttribute
     659                                        (HTML.Attribute.TYPE);
     660
     661                     if (type != null && type.equals("submit") &&
     662                         next != getElement()) {
     663                         // do nothing - this submit is not the trigger
     664                     } else if (type == null || !type.equals("image")) {
     665                         // images only result in data if they triggered
     666                         // the submit and they require that the mouse click
     667                         // coords be appended to the data.  Hence its
     668                         // processing is handled by the view.
     669                         loadElementDataIntoBuffer(next, buffer);
     670                     }
     671                 }
     672             }
     673         }
     674     }
     675
     676     /**
     677      * Loads the data
     678      * associated with the element into the buffer.
     679      * The format in which data is appended depends
     680      * on the type of the form element.  Essentially
     681      * data is loaded in name/value pairs.
     682      *
     683      */
     684     private void loadElementDataIntoBuffer(Element elem, StringBuilder buffer) {
     685
     686         AttributeSet attr = elem.getAttributes();
     687         String name = (String)attr.getAttribute(HTML.Attribute.NAME);
     688         if (name == null) {
     689             return;
     690         }
     691         String value = null;
     692         HTML.Tag tag = (HTML.Tag)elem.getAttributes().getAttribute
     693                                   (StyleConstants.NameAttribute);
     694
     695         if (tag == HTML.Tag.INPUT) {
     696             value = getInputElementData(attr);
     697         } else if (tag ==  HTML.Tag.TEXTAREA) {
     698             value = getTextAreaData(attr);
     699         } else if (tag == HTML.Tag.SELECT) {
     700             loadSelectData(attr, buffer);
     701         }
     702
     703         if (name != null && value != null) {
     704             appendBuffer(buffer, name, value);
     705         }
     706     }
     707
     708
     709     /**
     710      * Returns the data associated with an &lt;INPUT&gt; form
     711      * element.  The value of "type" attributes is
     712      * used to determine the type of the model associated
     713      * with the element and then the relevant data is
     714      * extracted.
     715      */
     716     private String getInputElementData(AttributeSet attr) {
     717
     718         Object model = attr.getAttribute(StyleConstants.ModelAttribute);
     719         String type = (String) attr.getAttribute(HTML.Attribute.TYPE);
     720         String value = null;
     721
     722         if (type.equals("text") || type.equals("password")) {
     723             Document doc = (Document)model;
     724             try {
     725                 value = doc.getText(0, doc.getLength());
     726             } catch (BadLocationException e) {
     727                 value = null;
     728             }
     729         } else if (type.equals("submit") || type.equals("hidden")) {
     730             value = (String) attr.getAttribute(HTML.Attribute.VALUE);
     731             if (value == null) {
     732                 value = "";
     733             }
     734         } else if (type.equals("radio") || type.equals("checkbox")) {
     735             ButtonModel m = (ButtonModel)model;
     736             if (m.isSelected()) {
     737                 value = (String) attr.getAttribute(HTML.Attribute.VALUE);
     738                 if (value == null) {
     739                     value = "on";
     740                 }
     741             }
     742         } else if (type.equals("file")) {
     743             Document doc = (Document)model;
     744             String path;
     745
     746             try {
     747                 path = doc.getText(0, doc.getLength());
     748             } catch (BadLocationException e) {
     749                 path = null;
     750             }
     751             if (path != null && path.length() > 0) {
     752                 value = path;
     753             }
     754         }
     755         return value;
     756     }
     757
     758     /**
     759      * Returns the data associated with the &lt;TEXTAREA&gt; form
     760      * element.  This is done by getting the text stored in the
     761      * Document model.
     762      */
     763     private String getTextAreaData(AttributeSet attr) {
     764         Document doc = (Document)attr.getAttribute(StyleConstants.ModelAttribute);
     765         try {
     766             return doc.getText(0, doc.getLength());
     767         } catch (BadLocationException e) {
     768             return null;
     769         }
     770     }
     771
     772
     773     /**
     774      * Loads the buffer with the data associated with the Select
     775      * form element.  Basically, only items that are selected
     776      * and have their name attribute set are added to the buffer.
     777      */
     778     private void loadSelectData(AttributeSet attr, StringBuilder buffer) {
     779
     780         String name = (String)attr.getAttribute(HTML.Attribute.NAME);
     781         if (name == null) {
     782             return;
     783         }
     784         Object m = attr.getAttribute(StyleConstants.ModelAttribute);
     785         if (m instanceof OptionListModel) {
     786             OptionListModel model = (OptionListModel) m;

     787
     788             for (int i = 0; i < model.getSize(); i++) {
     789                 if (model.isSelectedIndex(i)) {
     790                     Option option = model.getElementAt(i);
     791                     appendBuffer(buffer, name, option.getValue());
     792                 }
     793             }
     794         } else if (m instanceof ComboBoxModel) {
     795             @SuppressWarnings("unchecked")
     796             ComboBoxModel<Option> model = (ComboBoxModel<Option>)m;
     797             Option option = model.getSelectedItem();
     798             if (option != null) {
     799                 appendBuffer(buffer, name, option.getValue());
     800             }
     801         }
     802     }
     803
     804     /**
     805      * Appends name / value pairs into the
     806      * buffer.  Both names and values are encoded using the
     807      * URLEncoder.encode() method before being added to the
     808      * buffer.
     809      */
     810     @SuppressWarnings("deprecation")
     811     private void appendBuffer(StringBuilder buffer, String name, String value) {
     812         if (buffer.length() > 0) {
     813             buffer.append('&');
     814         }
     815         String encodedName = URLEncoder.encode(name);
     816         buffer.append(encodedName);
     817         buffer.append('=');
     818         String encodedValue = URLEncoder.encode(value);
     819         buffer.append(encodedValue);
     820     }
     821
     822     /**
     823      * Returns true if the Element <code>elem</code> represents a control.
     824      */
     825     private boolean isControl(Element elem) {
     826         return elem.isLeaf();
     827     }
     828
     829     /**
     830      * Iterates over the element hierarchy to determine if
     831      * the element parameter, which is assumed to be an
     832      * &lt;INPUT&gt; element of type password or text, is the last
     833      * one of either kind, in the form to which it belongs.
     834      */
     835     boolean isLastTextOrPasswordField() {
     836         Element parent = getFormElement();
     837         Element elem = getElement();
     838
     839         if (parent != null) {
     840             ElementIterator it = new ElementIterator(parent);
     841             Element next;
     842             boolean found = false;
     843
     844             while ((next = it.next()) != null) {
     845                 if (next == elem) {
     846                     found = true;
     847                 }
     848                 else if (found && isControl(next)) {
     849                     AttributeSet elemAttr = next.getAttributes();
     850
     851                     if (HTMLDocument.matchNameAttribute
     852                                      (elemAttr, HTML.Tag.INPUT)) {
     853                         String type = (String)elemAttr.getAttribute
     854                                                   (HTML.Attribute.TYPE);
     855
     856                         if ("text".equals(type) || "password".equals(type)) {
     857                             return false;
     858                         }
     859                     }
     860                 }
     861             }
     862         }
     863         return true;
     864     }
     865
     866     /**
     867      * Resets the form
     868      * to its initial state by reinitializing the models
     869      * associated with each form element to their initial
     870      * values.
     871      *
     872      * param elem the element that triggered the reset
     873      */
     874     void resetForm() {
     875         Element parent = getFormElement();
     876
     877         if (parent != null) {
     878             ElementIterator it = new ElementIterator(parent);
     879             Element next;
     880
     881             while((next = it.next()) != null) {
     882                 if (isControl(next)) {
     883                     AttributeSet elemAttr = next.getAttributes();
     884                     Object m = elemAttr.getAttribute(StyleConstants.
     885                                                      ModelAttribute);
     886                     if (m instanceof TextAreaDocument) {
     887                         TextAreaDocument doc = (TextAreaDocument)m;
     888                         doc.reset();
     889                     } else if (m instanceof PlainDocument) {
     890                         try {
     891                             PlainDocument doc =  (PlainDocument)m;
     892                             doc.remove(0, doc.getLength());
     893                             if (HTMLDocument.matchNameAttribute
     894                                              (elemAttr, HTML.Tag.INPUT)) {
     895                                 String value = (String)elemAttr.
     896                                            getAttribute(HTML.Attribute.VALUE);
     897                                 if (value != null) {
     898                                     doc.insertString(0, value, null);
     899                                 }
     900                             }
     901                         } catch (BadLocationException e) {
     902                         }
     903                     } else if (m instanceof OptionListModel) {
     904                         OptionListModel model = (OptionListModel) m;

     905                         int size = model.getSize();
     906                         for (int i = 0; i < size; i++) {
     907                             model.removeIndexInterval(i, i);
     908                         }
     909                         BitSet selectionRange = model.getInitialSelection();
     910                         for (int i = 0; i < selectionRange.size(); i++) {
     911                             if (selectionRange.get(i)) {
     912                                 model.addSelectionInterval(i, i);
     913                             }
     914                         }
     915                     } else if (m instanceof OptionComboBoxModel) {
     916                         OptionComboBoxModel model = (OptionComboBoxModel) m;

     917                         Option option = model.getInitialSelection();
     918                         if (option != null) {
     919                             model.setSelectedItem(option);
     920                         }
     921                     } else if (m instanceof JToggleButton.ToggleButtonModel) {
     922                         boolean checked = ((String)elemAttr.getAttribute
     923                                            (HTML.Attribute.CHECKED) != null);
     924                         JToggleButton.ToggleButtonModel model =
     925                                         (JToggleButton.ToggleButtonModel)m;
     926                         model.setSelected(checked);
     927                     }
     928                 }
     929             }
     930         }
     931     }
     932
     933
     934     /**
     935      * BrowseFileAction is used for input type == file. When the user
     936      * clicks the button a JFileChooser is brought up allowing the user
     937      * to select a file in the file system. The resulting path to the selected
     938      * file is set in the text field (actually an instance of Document).
     939      */
     940     private class BrowseFileAction implements ActionListener {
     941         private AttributeSet attrs;
     942         private Document model;
     943
     944         BrowseFileAction(AttributeSet attrs, Document model) {
     945             this.attrs = attrs;
     946             this.model = model;
     947         }
     948
     949         public void actionPerformed(ActionEvent ae) {
     950             // PENDING: When mime support is added to JFileChooser use the
     951             // accept value of attrs.
     952             JFileChooser fc = new JFileChooser();
     953             fc.setMultiSelectionEnabled(false);
     954             if (fc.showOpenDialog(getContainer()) ==
     955                   JFileChooser.APPROVE_OPTION) {
     956                 File selected = fc.getSelectedFile();
     957
     958                 if (selected != null) {
     959                     try {
     960                         if (model.getLength() > 0) {
     961                             model.remove(0, model.getLength());
     962                         }
     963                         model.insertString(0, selected.getPath(), null);
     964                     } catch (BadLocationException ble) {}
     965                 }
     966             }
     967         }
     968     }
     969 }
    --- EOF ---