Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: com/meterware/httpunit/WebForm.java


1   package com.meterware.httpunit;
2   /********************************************************************************************************************
3   * $Id: WebForm.java,v 1.101 2004/09/29 17:15:25 russgold Exp $
4   *
5   * Copyright (c) 2000-2004, Russell Gold
6   *
7   * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
8   * documentation files (the "Software"), to deal in the Software without restriction, including without limitation
9   * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and
10  * to permit persons to whom the Software is furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in all copies or substantial portions
13  * of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
16  * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
18  * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
19  * DEALINGS IN THE SOFTWARE.
20  *
21  *******************************************************************************************************************/
22  import com.meterware.httpunit.scripting.NamedDelegate;
23  import com.meterware.httpunit.scripting.ScriptableDelegate;
24  
25  import java.io.IOException;
26  import java.io.File;
27  import java.net.URL;
28  import java.net.UnknownServiceException;
29  import java.util.ArrayList;
30  import java.util.HashMap;
31  import java.util.List;
32  import java.util.Map;
33  import java.util.Vector;
34  
35  import org.w3c.dom.Node;
36  import org.xml.sax.SAXException;
37  
38  
39  /**
40   * This class represents a form in an HTML page. Users of this class may examine the parameters
41   * defined for the form, the structure of the form (as a DOM), or the text of the form. They
42   * may also create a {@link WebRequest} to simulate the submission of the form.
43   *
44   * @author <a href="mailto:russgold@httpunit.org">Russell Gold</a>
45   **/
46  public class WebForm extends WebRequestSource {
47      private static final FormParameter UNKNOWN_PARAMETER = new FormParameter();
48      private Button[] _buttons;
49  
50  
51      /** Predicate to match a link's name. **/
52      public final static HTMLElementPredicate MATCH_NAME;
53  
54      /**
55       * Submits this form using the web client from which it was originally obtained.
56       **/
57      public WebResponse submit() throws IOException, SAXException {
58          return submit( getDefaultButton() );
59      }
60  
61  
62      /**
63       * Submits this form using the web client from which it was originally obtained.
64       * Will usually return the result of that submission; however, if the submit button's 'onclick'
65       * or the form's 'onsubmit' event is triggered and
66       * inhibits the submission, will return the updated contents of the frame containing this form.
67       **/
68      public WebResponse submit( SubmitButton button ) throws IOException, SAXException {
69          return (button == null || button.doOnClickEvent()) ? doFormSubmit( button ) : getCurrentFrameContents();
70      }
71  
72  
73      /**
74       * Submits this form using the web client from which it was originally obtained.
75       * Will usually return the result of that submission; however, if the submit button's 'onclick'
76       * or the form's 'onsubmit' event is triggered and
77       * inhibits the submission, will return the updated contents of the frame containing this form.
78       * @since 1.6
79       **/
80      public WebResponse submit( SubmitButton button, int x, int y ) throws IOException, SAXException {
81          return button.doOnClickEvent() ? doFormSubmit( button, x, y ) : getCurrentFrameContents();
82      }
83  
84  
85      /**
86       * Submits this form using the web client from which it was originally obtained, ignoring any buttons defined for the form.
87       * @since 1.6
88       **/
89       public WebResponse submitNoButton() throws SAXException, IOException {
90          return submit( SubmitButton.createFakeSubmitButton( this /* fake */ ) );
91      }
92  
93  
94      protected WebResponse submitRequest( String event, WebRequest request ) throws IOException, SAXException {
95          try {
96              return super.submitRequest( event, request );
97          } catch (UnknownServiceException e) {
98              throw new UnsupportedActionException( "HttpUnit does not support " + request.getURL().getProtocol() + " URLs in form submissions" );
99          }
100     }
101 
102 
103     /**
104      * Submits the form without also invoking the button's "onclick" event.
105      */
106     WebResponse doFormSubmit( SubmitButton button ) throws IOException, SAXException {
107         return submitRequest( getAttribute( "onsubmit" ), getRequest( button ) );
108     }
109 
110 
111     WebResponse doFormSubmit( SubmitButton button, int x, int y ) throws IOException, SAXException {
112         return submitRequest( getAttribute( "onsubmit" ), getRequest( button, x, y ) );
113     }
114 
115 
116     /**
117      * Returns the method defined for this form.
118      **/
119     public String getMethod() {
120         return getAttribute( "method", "GET" );
121     }
122 
123 
124     /**
125      * Returns the action defined for this form.
126      **/
127     public String getAction() {
128         return getDestination();
129      }
130 
131 
132     /**
133      * Returns true if a parameter with given name exists in this form.
134      **/
135     public boolean hasParameterNamed( String soughtName ) {
136         return getFormParameters().containsKey( soughtName );
137     }
138 
139 
140     /**
141      * Returns true if a parameter starting with a given name exists,
142      **/
143     public boolean hasParameterStartingWithPrefix( String prefix ) {
144         String[] names = getParameterNames();
145         for (int i = 0; i < names.length; i++) {
146             if (names[i].startsWith( prefix )) return true;
147         }
148         return false;
149     }
150 
151 
152     /**
153      * Returns an array containing all of the buttons defined for this form.
154      **/
155     public Button[] getButtons() {
156         if (_buttons == null) {
157             FormControl[] controls = getFormControls();
158             ArrayList buttonList = new ArrayList();
159             for (int i = 0; i < controls.length; i++) {
160                 FormControl control = controls[ i ];
161                 if (control instanceof Button) buttonList.add( control );
162             }
163             _buttons = (Button[]) buttonList.toArray( new Button[ buttonList.size() ] );
164         }
165         return _buttons;
166     }
167 
168 
169     public Button getButton( HTMLElementPredicate predicate, Object criteria ) {
170         Button[] buttons = getButtons();
171         for (int i = 0; i < buttons.length; i++) {
172             if (predicate.matchesCriteria( buttons[i], criteria )) return buttons[i];
173         }
174         return null;
175     }
176 
177 
178     /**
179      * Convenience method which returns the button with the specified ID.
180      */
181     public Button getButtonWithID( String buttonID ) {
182         return getButton( Button.WITH_ID, buttonID );
183     }
184 
185 
186     /**
187      * Returns an array containing the submit buttons defined for this form.
188      **/
189     public SubmitButton[] getSubmitButtons() {
190         if (_submitButtons == null) {
191             Vector buttons = getSubmitButtonVector();
192             _submitButtons = new SubmitButton[ buttons.size() ];
193             buttons.copyInto( _submitButtons );
194         }
195         return _submitButtons;
196     }
197 
198 
199     /**
200      * Returns the submit button defined in this form with the specified name.
201      * If more than one such button exists, will return the first found.
202      * If no such button is found, will return null.
203      **/
204     public SubmitButton getSubmitButton( String name ) {
205         SubmitButton[] buttons = getSubmitButtons();
206         for (int i = 0; i < buttons.length; i++) {
207             if (buttons[i].getName().equals( name )) {
208                 return buttons[i];
209             }
210         }
211         return null;
212     }
213 
214 
215     /**
216      * Returns the submit button defined in this form with the specified name and value.
217      * If more than one such button exists, will return the first found.
218      * If no such button is found, will return null.
219      **/
220     public SubmitButton getSubmitButton( String name, String value ) {
221         SubmitButton[] buttons = getSubmitButtons();
222         for (int i = 0; i < buttons.length; i++) {
223             if (buttons[i].getName().equals( name ) && buttons[i].getValue().equals( value )) {
224                 return buttons[i];
225             }
226         }
227         return null;
228     }
229 
230 
231     /**
232      * Returns the submit button defined in this form with the specified ID.
233      * If more than one such button exists, will return the first found.
234      * If no such button is found, will return null.
235      **/
236     public SubmitButton getSubmitButtonWithID( String ID ) {
237         SubmitButton[] buttons = getSubmitButtons();
238         for (int i = 0; i < buttons.length; i++) {
239             if (buttons[i].getID().equals( ID )) {
240                 return buttons[i];
241             }
242         }
243         return null;
244     }
245 
246 
247     /**
248      * Creates and returns a web request which will simulate the submission of this form with a button with the specified name and value.
249      **/
250     public WebRequest getRequest( String submitButtonName, String submitButtonValue ) {
251         SubmitButton sb = getSubmitButton( submitButtonName, submitButtonValue );
252         if (sb == null) throw new IllegalSubmitButtonException( submitButtonName, submitButtonValue );
253         return getRequest( sb );
254     }
255 
256 
257     /**
258      * Creates and returns a web request which will simulate the submission of this form with a button with the specified name.
259      **/
260     public WebRequest getRequest( String submitButtonName ) {
261         SubmitButton sb = getSubmitButton( submitButtonName );
262         if (sb == null) throw new IllegalSubmitButtonException( submitButtonName, "" );
263         return getRequest( sb );
264     }
265 
266 
267     /**
268      * Creates and returns a web request which will simulate the submission of this form by pressing the specified button.
269      * If the button is null, simulates the pressing of the default button.
270      **/
271     public WebRequest getRequest( SubmitButton button ) {
272         return getRequest( button, 0, 0 );
273     }
274 
275 
276     /**
277      * Creates and returns a web request which will simulate the submission of this form by pressing the specified button.
278      * If the button is null, simulates the pressing of the default button.
279      **/
280     public WebRequest getRequest( SubmitButton button, int x, int y ) {
281         if (button == null) button = getDefaultButton();
282 
283         if (HttpUnitOptions.getParameterValuesValidated()) {
284             if (button == null) {
285                 throw new IllegalUnnamedSubmitButtonException();
286             } else if (button.isFake()) {
287                 // bypass checks
288             } else if (!getSubmitButtonVector().contains( button )) {
289                 throw new IllegalSubmitButtonException( button );
290             } else if (button.isDisabled()) {
291                 throw new DisabledSubmitButtonException( button );
292             }
293         }
294 
295         SubmitButton[] buttons = getSubmitButtons();
296         for (int i = 0; i < buttons.length; i++) {
297             buttons[i].setPressed( false );
298         }
299         button.setPressed( true );
300 
301         if (getMethod().equalsIgnoreCase( "post" )) {
302             return new PostMethodWebRequest( this, button, x, y );
303         } else {
304             return new GetMethodWebRequest( this, WebRequest.newParameterHolder( this ), button, x, y );
305         }
306     }
307 
308 
309     /**
310      * Creates and returns a web request which includes the specified button. If no button is specified, will include
311      * the default button, if any. No parameter validation will be done on the returned request and no scripts
312      * will be run when it is submitted.
313      **/
314     public WebRequest newUnvalidatedRequest( SubmitButton button ) {
315         return newUnvalidatedRequest( button, 0, 0 );
316     }
317 
318 
319     /**
320      * Creates and returns a web request which includes the specified button and position. If no button is specified,
321      * will include the default button, if any. No parameter validation will be done on the returned request
322      * and no scripts will be run when it is submitted.
323      **/
324     public WebRequest newUnvalidatedRequest( SubmitButton button, int x, int y ) {
325         if (button == null) button = getDefaultButton();
326 
327         SubmitButton[] buttons = getSubmitButtons();
328         for (int i = 0; i < buttons.length; i++) {
329             buttons[i].setPressed( false );
330         }
331         button.setPressed( true );
332 
333         if (getMethod().equalsIgnoreCase( "post" )) {
334             return new PostMethodWebRequest( this, new UncheckedParameterHolder( this ), button, x, y );
335         } else {
336             return new GetMethodWebRequest( this, new UncheckedParameterHolder( this ), button, x, y );
337         }
338     }
339 
340 
341     private WebRequest getScriptedSubmitRequest() {
342         SubmitButton[] buttons = getSubmitButtons();
343         for (int i = 0; i < buttons.length; i++) {
344             buttons[i].setPressed( false );
345         }
346 
347         if (getMethod().equalsIgnoreCase( "post" )) {
348             return new PostMethodWebRequest( this );
349         } else {
350             return new GetMethodWebRequest( this );
351         }
352 
353     }
354 
355 
356     /**
357      * Returns the default value of the named parameter.  If the parameter does not exist returns null.
358      **/
359     public String getParameterValue( String name ) {
360         String[] values = getParameterValues( name );
361         return values.length == 0 ? null : values[0];
362     }
363 
364 
365     /**
366      * Returns the displayed options defined for the specified parameter name.
367      **/
368     public String[] getOptions( String name ) {
369         return getParameter( name ).getOptions();
370     }
371 
372 
373     /**
374      * Returns the option values defined for the specified parameter name.
375      **/
376     public String[] getOptionValues( String name ) {
377         return getParameter( name ).getOptionValues();
378     }
379 
380 
381     /**
382      * Returns true if the named parameter accepts multiple values.
383      **/
384     public boolean isMultiValuedParameter( String name ) {
385         return getParameter( name ).isMultiValuedParameter();
386     }
387 
388 
389     /**
390      * Returns the number of text parameters in this form with the specified name.
391      **/
392     public int getNumTextParameters( String name ) {
393         return getParameter( name ).getNumTextParameters();
394     }
395 
396 
397     /**
398      * Returns true if the named parameter accepts free-form text.
399      **/
400     public boolean isTextParameter( String name ) {
401         return getParameter( name ).isTextParameter();
402     }
403 
404 
405     void setSubmitAsMime( boolean mimeEncoded ) {
406         throw new IllegalStateException( "May not change the encoding for a validated request created from a form" );
407     }
408 
409 
410     /**
411      * Returns true if this form is to be submitted using mime encoding (the default is URL encoding).
412      **/
413     public boolean isSubmitAsMime() {
414         return "multipart/form-data".equalsIgnoreCase( getAttribute( "enctype" ) );
415     }
416 
417 
418     /**
419      * Resets all parameters to their initial values.
420      */
421     public void reset() {
422         String event = getAttribute( "onreset" );
423         if (event.length() == 0 || getScriptableObject().doEvent( event )) resetControls();
424     }
425 
426 
427     private void resetControls() {
428         FormControl[] controls = getFormControls();
429         for (int i = 0; i < controls.length; i++) {
430             controls[i].reset();
431         }
432     }
433 
434 
435     /**
436      * Returns an object which provides scripting access to this form.
437      **/
438     public Scriptable getScriptableObject() {
439         if (_scriptable == null) {
440             _scriptable = new Scriptable();
441             _scriptable.setScriptEngine( getBaseResponse().getScriptableObject().getDocument().getScriptEngine( _scriptable ) );
442         }
443         return _scriptable;
444     }
445 
446 
447 //---------------------------------- WebRequestSource methods --------------------------------
448 
449     /**
450      * Returns the character set encoding for this form.
451      **/
452     public String getCharacterSet() {
453         return _characterSet;
454     }
455 
456 
457     /**
458      * Returns true if the named parameter accepts files for upload.
459      **/
460     public boolean isFileParameter( String name ) {
461         return getParameter( name ).isFileParameter();
462     }
463 
464 
465     /**
466      * Returns an array containing the names of the parameters defined for this form.
467      **/
468     public String[] getParameterNames() {
469         ArrayList parameterNames = new ArrayList( getFormParameters().keySet() );
470         return (String[]) parameterNames.toArray( new String[ parameterNames.size() ] );
471     }
472 
473 
474     /**
475      * Returns the multiple default values of the named parameter.
476      **/
477     public String[] getParameterValues( String name ) {
478         final FormParameter parameter = getParameter( name );
479         return parameter.getValues();
480     }
481 
482 
483     /**
484      * Returns true if the named parameter is read-only. If more than one control exists with the same name,
485      * will return true only if all such controls are read-only.
486      **/
487     public boolean isReadOnlyParameter( String name ) {
488         return getParameter( name ).isReadOnlyParameter();
489     }
490 
491 
492     /**
493      * Returns true if the named parameter is disabled. If more than one control exists with the same name,
494      * will return true only if all such controls are read-only.
495      **/
496     public boolean isDisabledParameter( String name ) {
497         return getParameter( name ).isDisabledParameter();
498     }
499 
500 
501     /**
502      * Returns true if the named parameter is hidden. If more than one control exists with the same name,
503      * will return true only if all such controls are hidden.
504      **/
505     public boolean isHiddenParameter( String name ) {
506         return getParameter( name ).isHiddenParameter();
507     }
508 
509 
510     /**
511      * Creates and returns a web request which will simulate the submission of this form with an unnamed submit button.
512      **/
513     public WebRequest getRequest() {
514         return getRequest( (SubmitButton) null );
515     }
516 
517 
518     /**
519      * Creates and returns a web request based on the current state of this form. No parameter validation will be done
520      * and there is no guarantee over the order of parameters transmitted.
521      */
522     public WebRequest newUnvalidatedRequest() {
523         return newUnvalidatedRequest( null );
524     }
525 
526 
527     /**
528      * Returns the scriptable delegate.
529      */
530 
531     public ScriptableDelegate getScriptableDelegate() {
532         return getScriptableObject();
533     }
534 
535 
536     /**
537      * Records a parameter defined by including it in the destination URL. Ignores any parameters whose name matches
538      * a form control.
539      **/
540     protected void addPresetParameter( String name, String value ) {
541         FormControl[] formControls = getFormControls();
542         for (int i = 0; i < formControls.length; i++) {
543             if (formControls[i].getName().equals( name)) return;
544         }
545         _presets.add( new PresetFormParameter( this, name, value ) );
546     }
547 
548 
549     protected String getEmptyParameterValue() {
550         return null;
551     }
552 
553 
554 //---------------------------------- ParameterHolder methods --------------------------------
555 
556 
557     /**
558      * Specifies the position at which an image button (if any) was clicked.
559      **/
560     void selectImageButtonPosition( SubmitButton imageButton, int x, int y ) {
561         imageButton.setLocation( x, y );
562     }
563 
564 
565     /**
566      * Iterates through the fixed, predefined parameters in this holder, recording them in the supplied parameter processor.\
567      * These parameters always go on the URL, no matter what encoding method is used.
568      **/
569 
570     void recordPredefinedParameters( ParameterProcessor processor ) throws IOException {
571         FormControl[] controls = getPresetParameters();
572         for (int i = 0; i < controls.length; i++) {
573             controls[i].addValues( processor, getCharacterSet() );
574         }
575     }
576 
577 
578     /**
579      * Iterates through the parameters in this holder, recording them in the supplied parameter processor.
580      **/
581     void recordParameters( ParameterProcessor processor ) throws IOException {
582         FormControl[] controls = getFormControls();
583         for (int i = 0; i < controls.length; i++) {
584             controls[i].addValues( processor, getCharacterSet() );
585         }
586     }
587 
588 
589     /**
590      * Removes a parameter name from this collection.
591      **/
592     public void removeParameter( String name ) {
593         setParameter( name, NO_VALUES );
594     }
595 
596 
597     /**
598      * Sets the value of a parameter in this form.
599      **/
600     public void setParameter( String name, String value ) {
601         setParameter( name, new String[] { value } );
602     }
603 
604 
605     /**
606      * Sets the multiple values of a parameter in this form. This is generally used when there are multiple
607      * controls with the same name in the form.
608      */
609     public void setParameter( String name, final String[] values ) {
610         FormParameter parameter = getParameter( name );
611         if (parameter == UNKNOWN_PARAMETER) throw new NoSuchParameterException( name );
612         parameter.setValues( values );
613     }
614 
615 
616     /**
617      * Sets the multiple values of a file upload parameter in a web request.
618      **/
619     public void setParameter( String name, UploadFileSpec[] files ) {
620         FormParameter parameter = getParameter( name );
621         if (parameter == null) throw new NoSuchParameterException( name );
622         parameter.setFiles( files );
623     }
624 
625 
626     /**
627      * Sets the single value of a file upload parameter in this form.
628      * A more convenient way to do this than using {@link #setParameter(String,UploadFileSpec[])}
629      * @since 1.6
630      */
631     public void setParameter( String name, File file ) {
632         setParameter( name, new UploadFileSpec[] { new UploadFileSpec( file ) } );
633     }
634 
635 
636     /**
637      * Toggles the value of the specified checkbox parameter.
638      * @param name the name of the checkbox parameter
639      * @throws IllegalArgumentException if the specified parameter is not a checkbox or there is more than one
640      *         control with that name.
641      * @since 1.5.4
642      */
643     public void toggleCheckbox( String name ) {
644         FormParameter parameter = getParameter( name );
645         if (parameter == null) throw new NoSuchParameterException( name );
646         parameter.toggleCheckbox();
647     }
648 
649 
650     /**
651      * Toggles the value of the specified checkbox parameter.
652      * @param name the name of the checkbox parameter
653      * @param value of the checkbox parameter
654      * @throws IllegalArgumentException if the specified parameter is not a checkbox or if there is no checkbox
655      *         with the specified name and value.
656      * @since 1.6
657      */
658     public void toggleCheckbox( String name, String value ) {
659         FormParameter parameter = getParameter( name );
660         if (parameter == null) throw new NoSuchParameterException( name );
661         parameter.toggleCheckbox( value );
662     }
663 
664 
665     /**
666      * Sets the value of the specified checkbox parameter.
667      * @param name the name of the checkbox parameter
668      * @param state the new state of the checkbox
669      * @throws IllegalArgumentException if the specified parameter is not a checkbox or there is more than one
670      *         control with that name.
671      * @since 1.5.4
672      */
673     public void setCheckbox( String name, boolean state ) {
674         FormParameter parameter = getParameter( name );
675         if (parameter == null) throw new NoSuchParameterException( name );
676         parameter.setValue( state );
677     }
678 
679 
680     /**
681      * Sets the value of the specified checkbox parameter.
682      * @param name the name of the checkbox parameter
683      * @param value of the checkbox parameter
684      * @param state the new state of the checkbox
685      * @throws IllegalArgumentException if the specified parameter is not a checkbox or if there is no checkbox
686      *         with the specified name and value.
687      * @since 1.6
688      */
689     public void setCheckbox( String name, String value, boolean state ) {
690         FormParameter parameter = getParameter( name );
691         if (parameter == null) throw new NoSuchParameterException( name );
692         parameter.setValue( value, state );
693     }
694 
695 
696     public class Scriptable extends HTMLElementScriptable implements NamedDelegate {
697         public String getAction() { return WebForm.this.getAction(); }
698         public void setAction( String newAction ) { setDestination( newAction ); _presetParameters = null; }
699 
700 
701         public void submit() throws IOException, SAXException {
702             submitRequest( getScriptedSubmitRequest() );
703         }
704 
705 
706         public void reset() throws IOException, SAXException {
707             resetControls();
708         }
709 
710 
711         public String getName() {
712             return WebForm.this.getID().length() != 0 ? WebForm.this.getID() : WebForm.this.getName();
713         }
714 
715 
716         public Object get( String propertyName ) {
717             if (propertyName.equals( "target" )) {
718                 return getTarget();
719             } else if (propertyName.equals( "length" )) {
720                 return new Integer(getFormControls().length);
721             } else {
722                 final FormParameter parameter = getParameter( propertyName );
723                 if (parameter != UNKNOWN_PARAMETER) return parameter.getScriptableObject();
724                 FormControl control = getControlWithID( propertyName );
725                 return control == null ? super.get( propertyName ) : control.getScriptableDelegate();
726             }
727         }
728 
729 
730         /**
731          * Sets the value of the named property. Will throw a runtime exception if the property does not exist or
732          * cannot accept the specified value.
733          **/
734         public void set( String propertyName, Object value ) {
735             if (propertyName.equals( "target" )) {
736                 setTargetAttribute( value.toString() );
737             } else if (value instanceof String) {
738                 setParameterValue( propertyName, (String) value );
739             } else if (value instanceof Number) {
740                 setParameterValue( propertyName, roundedValue( (Number) value ) );
741             } else {
742                 super.set( propertyName, value );
743             }
744         }
745 
746 
747         /**
748          * Trim off any trailing zeros (and the decimal point if the result is an integer).
749          */
750         private String roundedValue( Number number ) {
751             String rawNumber = number.toString();
752             if (rawNumber.indexOf('.') == -1) return rawNumber;
753 
754             int index = rawNumber.length();
755             while (rawNumber.charAt( index-1 ) == '0') index--;
756             if (rawNumber.charAt( index-1 ) == '.') index--;
757             return rawNumber.substring( 0, index );
758         }
759 
760 
761         public void setParameterValue( String name, String value ) {
762             final Object scriptableObject = getParameter( name ).getScriptableObject();
763             if (scriptableObject instanceof ScriptableDelegate) {
764                 ((ScriptableDelegate) scriptableObject).set( "value", value );
765             } else if (scriptableObject instanceof ScriptableDelegate[]) {
766                 ((ScriptableDelegate[]) scriptableObject)[0].set( "value", value );
767             }
768         }
769 
770 
771         public ScriptableDelegate[] getElementDelegates() {
772             FormControl[] controls = getFormControls();
773             ScriptableDelegate[] result = new ScriptableDelegate[ controls.length ];
774             for (int i = 0; i < result.length; i++) {
775                 result[i] = controls[i].getScriptableDelegate();
776             }
777             return result;
778         }
779 
780 
781         public ScriptableDelegate[] getElementsByTagName( String name ) throws SAXException {
782             return getDelegates( getHTMLPage().getElementsByTagName( getNode(), name ) );
783         }
784 
785 
786         Scriptable() {
787             super( WebForm.this );
788         }
789     }
790 
791 
792 //---------------------------------- package members --------------------------------
793 
794     /**
795      * Contructs a web form given the URL of its source page and the DOM extracted
796      * from that page.
797      **/
798     WebForm( WebResponse response, URL baseURL, Node node, FrameSelector frame, String defaultTarget, String characterSet ) {
799         super( response, node, baseURL, NodeUtils.getNodeAttribute( node, "action" ), frame, defaultTarget );
800         _characterSet = characterSet;
801     }
802 
803 
804     /**
805      * Returns the form control which is part of this form with the specified ID.
806      */
807     FormControl getControlWithID( String id ) {
808         FormControl[] controls = getFormControls();
809         for (int i = 0; i < controls.length; i++) {
810             FormControl control = controls[i];
811             if (control.getID().equals(id)) return control;
812         }
813         return null;
814     }
815 
816 
817 //---------------------------------- private members --------------------------------
818 
819     private final static String[] NO_VALUES = new String[0];
820 
821     /** The attributes of the form parameters. **/
822     private FormControl[] _formControls;
823 
824     /** The submit buttons in this form. **/
825     private SubmitButton[] _submitButtons;
826 
827     /** The character set in which the form will be submitted. **/
828     private String         _characterSet;
829 
830     /** A map of parameter names to form parameter objects. **/
831     private Map            _formParameters;
832 
833     /** The Scriptable object associated with this form. **/
834     private Scriptable _scriptable;
835 
836     private Vector _buttonVector;
837 
838     private FormControl[] _presetParameters;
839     private ArrayList     _presets;
840 
841 
842     private SubmitButton getDefaultButton() {
843         if (getSubmitButtons().length == 1) {
844             return getSubmitButtons()[0];
845         } else {
846             return getSubmitButton( "" );
847         }
848     }
849 
850 
851     private Vector getSubmitButtonVector() {
852         if (_buttonVector == null) {
853             _buttonVector = new Vector();
854             FormControl[] controls = getFormControls();
855             for (int i = 0; i < controls.length; i++) {
856                 FormControl control = controls[ i ];
857                 if (control instanceof SubmitButton) _buttonVector.add( control );
858             }
859 
860             if (_buttonVector.isEmpty()) _buttonVector.addElement( new SubmitButton( this ) );
861         }
862         return _buttonVector;
863     }
864 
865 
866     private FormControl[] getPresetParameters() {
867         if (_presetParameters == null) {
868             _presets = new ArrayList();
869             loadDestinationParameters();
870             _presetParameters = (FormControl[]) _presets.toArray( new FormControl[ _presets.size() ] );
871         }
872         return _presetParameters;
873     }
874 
875 
876     private ArrayList _controlList = new ArrayList();
877 
878     FormControl newFormControl( Node child ) {
879         return FormControl.newFormParameter( this, child );
880     }
881 
882 
883     void addFormControl( FormControl control ) {
884         _controlList.add( control );
885         _formControls = null;
886         _formParameters = null;
887     }
888 
889 
890     /**
891      * Returns an array of form parameter attributes for this form.
892      **/
893     private FormControl[] getFormControls() {
894         if (_formControls == null) {
895             _formControls = (FormControl[]) _controlList.toArray( new FormControl[ _controlList.size() ] );
896         }
897         return _formControls;
898     }
899 
900 
901     private FormParameter getParameter( String name ) {
902         final FormParameter parameter = ((FormParameter) getFormParameters().get( name ));
903         return parameter != null ? parameter : UNKNOWN_PARAMETER;
904     }
905 
906 
907     /**
908      * Returns a map of parameter name to form parameter objects. Each form parameter object represents the set of form
909      * controls with a particular name. Unnamed parameters are ignored.
910      */
911     private Map getFormParameters() {
912         if (_formParameters == null) {
913             _formParameters = new HashMap();
914             loadFormParameters( getPresetParameters() );
915             loadFormParameters( getFormControls() );
916         }
917         return _formParameters;
918     }
919 
920 
921     private void loadFormParameters( FormControl[] controls ) {
922         for (int i = 0; i < controls.length; i++) {
923             if (controls[i].getName().length() == 0) continue;
924             FormParameter parameter = (FormParameter) _formParameters.get( controls[i].getName() );
925             if (parameter == null) {
926                 parameter = new FormParameter();
927                 _formParameters.put( controls[i].getName(), parameter );
928             }
929             parameter.addControl( controls[i] );
930         }
931     }
932 
933 
934     static {
935         MATCH_NAME = new HTMLElementPredicate() {
936             public boolean matchesCriteria( Object htmlElement, Object criteria ) {
937                 return HttpUnitUtils.matches( ((WebForm) htmlElement).getName(), (String) criteria );
938             };
939         };
940 
941     }
942 
943 
944 //===========================---===== exception class NoSuchParameterException =========================================
945 
946 
947     /**
948      * This exception is thrown on an attempt to set a parameter to a value not permitted to it by the form.
949      **/
950     class NoSuchParameterException extends IllegalRequestParameterException {
951 
952 
953         NoSuchParameterException( String parameterName ) {
954             _parameterName = parameterName;
955         }
956 
957 
958         public String getMessage() {
959             return "No parameter named '" + _parameterName + "' is defined in the form";
960         }
961 
962 
963         private String _parameterName;
964 
965     }
966 
967 
968 //============================= exception class IllegalUnnamedSubmitButtonException ======================================
969 
970 
971     /**
972      * This exception is thrown on an attempt to define a form request with a button not defined on that form.
973      **/
974     class IllegalUnnamedSubmitButtonException extends IllegalRequestParameterException {
975 
976 
977         IllegalUnnamedSubmitButtonException() {
978         }
979 
980 
981         public String getMessage() {
982             return "This form has more than one submit button, none unnamed. You must specify the button to be used.";
983         }
984 
985     }
986 
987 
988 //============================= exception class IllegalSubmitButtonException ======================================
989 
990 
991     /**
992      * This exception is thrown on an attempt to define a form request with a button not defined on that form.
993      **/
994     class IllegalSubmitButtonException extends IllegalRequestParameterException {
995 
996 
997         IllegalSubmitButtonException( SubmitButton button ) {
998             _name  = button.getName();
999             _value = button.getValue();
1000        }
1001
1002
1003        IllegalSubmitButtonException( String name, String value ) {
1004            _name = name;
1005            _value = value;
1006        }
1007
1008
1009        public String getMessage() {
1010            return "Specified submit button (name=\"" + _name + "\" value=\"" + _value + "\") not part of this form.";
1011        }
1012
1013
1014        private String _name;
1015        private String _value;
1016
1017    }
1018
1019//============================= exception class IllegalUnnamedSubmitButtonException ======================================
1020
1021
1022    /**
1023     * This exception is thrown on an attempt to define a form request with a button not defined on that form.
1024     **/
1025    class DisabledSubmitButtonException extends IllegalStateException {
1026
1027
1028        DisabledSubmitButtonException( SubmitButton button ) {
1029            _name  = button.getName();
1030            _value = button.getValue();
1031        }
1032
1033
1034        public String getMessage() {
1035            return "The specified button (name='" + _name + "' value='" + _value
1036                   + "' is disabled and may not be used to submit this form.";
1037        }
1038
1039
1040        private String _name;
1041        private String _value;
1042
1043    }
1044
1045}
1046
1047
1048
1049//========================================== class PresetFormParameter =================================================
1050
1051
1052    class PresetFormParameter extends FormControl {
1053
1054        PresetFormParameter( WebForm form, String name, String value ) {
1055            super( form );
1056            _name   = name;
1057            _value  = value;
1058        }
1059
1060
1061        /**
1062         * Returns the name of this control..
1063         **/
1064        public String getName() {
1065            return _name;
1066        }
1067
1068
1069        /**
1070         * Returns true if this control is read-only.
1071         **/
1072        public boolean isReadOnly() {
1073            return true;
1074        }
1075
1076
1077        /**
1078         * Returns true if this control accepts free-form text.
1079         **/
1080        public boolean isTextControl() {
1081            return true;
1082        }
1083
1084
1085        /**
1086         * Remove any required values for this control from the list, throwing an exception if they are missing.
1087         **/
1088        void claimRequiredValues( List values ) {
1089            if (_value != null) claimValueIsRequired( values, _value );
1090        }
1091
1092
1093        public String getType() {
1094            return UNDEFINED_TYPE;
1095        }
1096
1097        /**
1098         * Returns the current value(s) associated with this control. These values will be transmitted to the server
1099         * if the control is 'successful'.
1100         **/
1101        public String[] getValues() {
1102            if (_values == null) _values = new String[] { _value };
1103            return _values;
1104        }
1105
1106
1107        void addValues( ParameterProcessor processor, String characterSet ) throws IOException {
1108            processor.addParameter( _name, _value, characterSet );
1109        }
1110
1111
1112        private String   _name;
1113        private String   _value;
1114        private String[] _values;
1115    }
1116
1117
1118
1119
1120