0001: package com.meterware.httpunit;
0002:
0003: /********************************************************************************************************************
0004: * $Id: WebForm.java,v 1.102 2004/12/26 20:33:34 russgold Exp $
0005: *
0006: * Copyright (c) 2000-2004, Russell Gold
0007: *
0008: * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
0009: * documentation files (the "Software"), to deal in the Software without restriction, including without limitation
0010: * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and
0011: * to permit persons to whom the Software is furnished to do so, subject to the following conditions:
0012: *
0013: * The above copyright notice and this permission notice shall be included in all copies or substantial portions
0014: * of the Software.
0015: *
0016: * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
0017: * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
0018: * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
0019: * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
0020: * DEALINGS IN THE SOFTWARE.
0021: *
0022: *******************************************************************************************************************/
0023: import com.meterware.httpunit.scripting.NamedDelegate;
0024: import com.meterware.httpunit.scripting.ScriptableDelegate;
0025:
0026: import java.io.IOException;
0027: import java.io.File;
0028: import java.net.URL;
0029: import java.net.UnknownServiceException;
0030: import java.util.ArrayList;
0031: import java.util.HashMap;
0032: import java.util.List;
0033: import java.util.Map;
0034: import java.util.Vector;
0035:
0036: import org.w3c.dom.Node;
0037: import org.xml.sax.SAXException;
0038:
0039: /**
0040: * This class represents a form in an HTML page. Users of this class may examine the parameters
0041: * defined for the form, the structure of the form (as a DOM), or the text of the form. They
0042: * may also create a {@link WebRequest} to simulate the submission of the form.
0043: *
0044: * @author <a href="mailto:russgold@httpunit.org">Russell Gold</a>
0045: **/
0046: public class WebForm extends WebRequestSource {
0047: private static final FormParameter UNKNOWN_PARAMETER = new FormParameter();
0048: private Button[] _buttons;
0049:
0050: /** Predicate to match a link's name. **/
0051: public final static HTMLElementPredicate MATCH_NAME;
0052:
0053: /**
0054: * Submits this form using the web client from which it was originally obtained.
0055: **/
0056: public WebResponse submit() throws IOException, SAXException {
0057: return submit(getDefaultButton());
0058: }
0059:
0060: /**
0061: * Submits this form using the web client from which it was originally obtained.
0062: * Will usually return the result of that submission; however, if the submit button's 'onclick'
0063: * or the form's 'onsubmit' event is triggered and
0064: * inhibits the submission, will return the updated contents of the frame containing this form.
0065: **/
0066: public WebResponse submit(SubmitButton button) throws IOException,
0067: SAXException {
0068: return (button == null || button.doOnClickEvent()) ? doFormSubmit(button)
0069: : getCurrentFrameContents();
0070: }
0071:
0072: /**
0073: * Submits this form using the web client from which it was originally obtained.
0074: * Will usually return the result of that submission; however, if the submit button's 'onclick'
0075: * or the form's 'onsubmit' event is triggered and
0076: * inhibits the submission, will return the updated contents of the frame containing this form.
0077: * @since 1.6
0078: **/
0079: public WebResponse submit(SubmitButton button, int x, int y)
0080: throws IOException, SAXException {
0081: return button.doOnClickEvent() ? doFormSubmit(button, x, y)
0082: : getCurrentFrameContents();
0083: }
0084:
0085: /**
0086: * Submits this form using the web client from which it was originally obtained, ignoring any buttons defined for the form.
0087: * @since 1.6
0088: **/
0089: public WebResponse submitNoButton() throws SAXException,
0090: IOException {
0091: return submit(SubmitButton
0092: .createFakeSubmitButton(this /* fake */));
0093: }
0094:
0095: protected WebResponse submitRequest(String event, WebRequest request)
0096: throws IOException, SAXException {
0097: try {
0098: return super .submitRequest(event, request);
0099: } catch (UnknownServiceException e) {
0100: throw new UnsupportedActionException(
0101: "HttpUnit does not support "
0102: + request.getURL().getProtocol()
0103: + " URLs in form submissions");
0104: }
0105: }
0106:
0107: /**
0108: * Submits the form without also invoking the button's "onclick" event.
0109: */
0110: WebResponse doFormSubmit(SubmitButton button) throws IOException,
0111: SAXException {
0112: return submitRequest(getAttribute("onsubmit"),
0113: getRequest(button));
0114: }
0115:
0116: WebResponse doFormSubmit(SubmitButton button, int x, int y)
0117: throws IOException, SAXException {
0118: return submitRequest(getAttribute("onsubmit"), getRequest(
0119: button, x, y));
0120: }
0121:
0122: /**
0123: * Returns the method defined for this form.
0124: **/
0125: public String getMethod() {
0126: return getAttribute("method", "GET");
0127: }
0128:
0129: /**
0130: * Returns the action defined for this form.
0131: **/
0132: public String getAction() {
0133: return getDestination();
0134: }
0135:
0136: /**
0137: * Returns true if a parameter with given name exists in this form.
0138: **/
0139: public boolean hasParameterNamed(String soughtName) {
0140: return getFormParameters().containsKey(soughtName);
0141: }
0142:
0143: /**
0144: * Returns true if a parameter starting with a given name exists,
0145: **/
0146: public boolean hasParameterStartingWithPrefix(String prefix) {
0147: String[] names = getParameterNames();
0148: for (int i = 0; i < names.length; i++) {
0149: if (names[i].startsWith(prefix))
0150: return true;
0151: }
0152: return false;
0153: }
0154:
0155: /**
0156: * Returns an array containing all of the buttons defined for this form.
0157: **/
0158: public Button[] getButtons() {
0159: if (_buttons == null) {
0160: FormControl[] controls = getFormControls();
0161: ArrayList buttonList = new ArrayList();
0162: for (int i = 0; i < controls.length; i++) {
0163: FormControl control = controls[i];
0164: if (control instanceof Button)
0165: buttonList.add(control);
0166: }
0167: _buttons = (Button[]) buttonList
0168: .toArray(new Button[buttonList.size()]);
0169: }
0170: return _buttons;
0171: }
0172:
0173: public Button getButton(HTMLElementPredicate predicate,
0174: Object criteria) {
0175: Button[] buttons = getButtons();
0176: for (int i = 0; i < buttons.length; i++) {
0177: if (predicate.matchesCriteria(buttons[i], criteria))
0178: return buttons[i];
0179: }
0180: return null;
0181: }
0182:
0183: /**
0184: * Convenience method which returns the button with the specified ID.
0185: */
0186: public Button getButtonWithID(String buttonID) {
0187: return getButton(Button.WITH_ID, buttonID);
0188: }
0189:
0190: /**
0191: * Returns an array containing the submit buttons defined for this form.
0192: **/
0193: public SubmitButton[] getSubmitButtons() {
0194: if (_submitButtons == null) {
0195: Vector buttons = getSubmitButtonVector();
0196: _submitButtons = new SubmitButton[buttons.size()];
0197: buttons.copyInto(_submitButtons);
0198: }
0199: return _submitButtons;
0200: }
0201:
0202: /**
0203: * Returns the submit button defined in this form with the specified name.
0204: * If more than one such button exists, will return the first found.
0205: * If no such button is found, will return null.
0206: **/
0207: public SubmitButton getSubmitButton(String name) {
0208: SubmitButton[] buttons = getSubmitButtons();
0209: for (int i = 0; i < buttons.length; i++) {
0210: if (buttons[i].getName().equals(name)) {
0211: return buttons[i];
0212: }
0213: }
0214: return null;
0215: }
0216:
0217: /**
0218: * Returns the submit button defined in this form with the specified name and value.
0219: * If more than one such button exists, will return the first found.
0220: * If no such button is found, will return null.
0221: **/
0222: public SubmitButton getSubmitButton(String name, String value) {
0223: SubmitButton[] buttons = getSubmitButtons();
0224: for (int i = 0; i < buttons.length; i++) {
0225: if (buttons[i].getName().equals(name)
0226: && buttons[i].getValue().equals(value)) {
0227: return buttons[i];
0228: }
0229: }
0230: return null;
0231: }
0232:
0233: /**
0234: * Returns the submit button defined in this form with the specified ID.
0235: * If more than one such button exists, will return the first found.
0236: * If no such button is found, will return null.
0237: **/
0238: public SubmitButton getSubmitButtonWithID(String ID) {
0239: SubmitButton[] buttons = getSubmitButtons();
0240: for (int i = 0; i < buttons.length; i++) {
0241: if (buttons[i].getID().equals(ID)) {
0242: return buttons[i];
0243: }
0244: }
0245: return null;
0246: }
0247:
0248: /**
0249: * Creates and returns a web request which will simulate the submission of this form with a button with the specified name and value.
0250: **/
0251: public WebRequest getRequest(String submitButtonName,
0252: String submitButtonValue) {
0253: SubmitButton sb = getSubmitButton(submitButtonName,
0254: submitButtonValue);
0255: if (sb == null)
0256: throw new IllegalSubmitButtonException(submitButtonName,
0257: submitButtonValue);
0258: return getRequest(sb);
0259: }
0260:
0261: /**
0262: * Creates and returns a web request which will simulate the submission of this form with a button with the specified name.
0263: **/
0264: public WebRequest getRequest(String submitButtonName) {
0265: SubmitButton sb = getSubmitButton(submitButtonName);
0266: if (sb == null)
0267: throw new IllegalSubmitButtonException(submitButtonName, "");
0268: return getRequest(sb);
0269: }
0270:
0271: /**
0272: * Creates and returns a web request which will simulate the submission of this form by pressing the specified button.
0273: * If the button is null, simulates the pressing of the default button.
0274: **/
0275: public WebRequest getRequest(SubmitButton button) {
0276: return getRequest(button, 0, 0);
0277: }
0278:
0279: /**
0280: * Creates and returns a web request which will simulate the submission of this form by pressing the specified button.
0281: * If the button is null, simulates the pressing of the default button.
0282: **/
0283: public WebRequest getRequest(SubmitButton button, int x, int y) {
0284: if (button == null)
0285: button = getDefaultButton();
0286:
0287: if (HttpUnitOptions.getParameterValuesValidated()) {
0288: if (button == null) {
0289: throw new IllegalUnnamedSubmitButtonException();
0290: } else if (button.isFake()) {
0291: // bypass checks
0292: } else if (!getSubmitButtonVector().contains(button)) {
0293: throw new IllegalSubmitButtonException(button);
0294: } else if (button.isDisabled()) {
0295: throw new DisabledSubmitButtonException(button);
0296: }
0297: }
0298:
0299: SubmitButton[] buttons = getSubmitButtons();
0300: for (int i = 0; i < buttons.length; i++) {
0301: buttons[i].setPressed(false);
0302: }
0303: button.setPressed(true);
0304:
0305: if (getMethod().equalsIgnoreCase("post")) {
0306: return new PostMethodWebRequest(this , button, x, y);
0307: } else {
0308: return new GetMethodWebRequest(this , WebRequest
0309: .newParameterHolder(this ), button, x, y);
0310: }
0311: }
0312:
0313: /**
0314: * Creates and returns a web request which includes the specified button. If no button is specified, will include
0315: * the default button, if any. No parameter validation will be done on the returned request and no scripts
0316: * will be run when it is submitted.
0317: **/
0318: public WebRequest newUnvalidatedRequest(SubmitButton button) {
0319: return newUnvalidatedRequest(button, 0, 0);
0320: }
0321:
0322: /**
0323: * Creates and returns a web request which includes the specified button and position. If no button is specified,
0324: * will include the default button, if any. No parameter validation will be done on the returned request
0325: * and no scripts will be run when it is submitted.
0326: **/
0327: public WebRequest newUnvalidatedRequest(SubmitButton button, int x,
0328: int y) {
0329: if (button == null)
0330: button = getDefaultButton();
0331:
0332: SubmitButton[] buttons = getSubmitButtons();
0333: for (int i = 0; i < buttons.length; i++) {
0334: buttons[i].setPressed(false);
0335: }
0336: button.setPressed(true);
0337:
0338: if (getMethod().equalsIgnoreCase("post")) {
0339: return new PostMethodWebRequest(this ,
0340: new UncheckedParameterHolder(this ), button, x, y);
0341: } else {
0342: return new GetMethodWebRequest(this ,
0343: new UncheckedParameterHolder(this ), button, x, y);
0344: }
0345: }
0346:
0347: private WebRequest getScriptedSubmitRequest() {
0348: SubmitButton[] buttons = getSubmitButtons();
0349: for (int i = 0; i < buttons.length; i++) {
0350: buttons[i].setPressed(false);
0351: }
0352:
0353: if (getMethod().equalsIgnoreCase("post")) {
0354: return new PostMethodWebRequest(this );
0355: } else {
0356: return new GetMethodWebRequest(this );
0357: }
0358:
0359: }
0360:
0361: /**
0362: * Returns the default value of the named parameter. If the parameter does not exist returns null.
0363: **/
0364: public String getParameterValue(String name) {
0365: String[] values = getParameterValues(name);
0366: return values.length == 0 ? null : values[0];
0367: }
0368:
0369: /**
0370: * Returns the displayed options defined for the specified parameter name.
0371: **/
0372: public String[] getOptions(String name) {
0373: return getParameter(name).getOptions();
0374: }
0375:
0376: /**
0377: * Returns the option values defined for the specified parameter name.
0378: **/
0379: public String[] getOptionValues(String name) {
0380: return getParameter(name).getOptionValues();
0381: }
0382:
0383: /**
0384: * Returns true if the named parameter accepts multiple values.
0385: **/
0386: public boolean isMultiValuedParameter(String name) {
0387: return getParameter(name).isMultiValuedParameter();
0388: }
0389:
0390: /**
0391: * Returns the number of text parameters in this form with the specified name.
0392: **/
0393: public int getNumTextParameters(String name) {
0394: return getParameter(name).getNumTextParameters();
0395: }
0396:
0397: /**
0398: * Returns true if the named parameter accepts free-form text.
0399: **/
0400: public boolean isTextParameter(String name) {
0401: return getParameter(name).isTextParameter();
0402: }
0403:
0404: void setSubmitAsMime(boolean mimeEncoded) {
0405: throw new IllegalStateException(
0406: "May not change the encoding for a validated request created from a form");
0407: }
0408:
0409: /**
0410: * Returns true if this form is to be submitted using mime encoding (the default is URL encoding).
0411: **/
0412: public boolean isSubmitAsMime() {
0413: return "multipart/form-data"
0414: .equalsIgnoreCase(getAttribute("enctype"));
0415: }
0416:
0417: /**
0418: * Resets all parameters to their initial values.
0419: */
0420: public void reset() {
0421: String event = getAttribute("onreset");
0422: if (event.length() == 0 || getScriptableObject().doEvent(event))
0423: resetControls();
0424: }
0425:
0426: private void resetControls() {
0427: FormControl[] controls = getFormControls();
0428: for (int i = 0; i < controls.length; i++) {
0429: controls[i].reset();
0430: }
0431: }
0432:
0433: /**
0434: * Returns an object which provides scripting access to this form.
0435: **/
0436: public Scriptable getScriptableObject() {
0437: if (_scriptable == null) {
0438: _scriptable = new Scriptable();
0439: _scriptable.setScriptEngine(getBaseResponse()
0440: .getScriptableObject().getDocument()
0441: .getScriptEngine(_scriptable));
0442: }
0443: return _scriptable;
0444: }
0445:
0446: //---------------------------------- WebRequestSource methods --------------------------------
0447:
0448: /**
0449: * Returns the character set encoding for this form.
0450: **/
0451: public String getCharacterSet() {
0452: return _characterSet;
0453: }
0454:
0455: /**
0456: * Returns true if the named parameter accepts files for upload.
0457: **/
0458: public boolean isFileParameter(String name) {
0459: return getParameter(name).isFileParameter();
0460: }
0461:
0462: /**
0463: * Returns an array containing the names of the parameters defined for this form.
0464: **/
0465: public String[] getParameterNames() {
0466: ArrayList parameterNames = new ArrayList(getFormParameters()
0467: .keySet());
0468: return (String[]) parameterNames
0469: .toArray(new String[parameterNames.size()]);
0470: }
0471:
0472: /**
0473: * Returns the multiple default values of the named parameter.
0474: **/
0475: public String[] getParameterValues(String name) {
0476: final FormParameter parameter = getParameter(name);
0477: return parameter.getValues();
0478: }
0479:
0480: /**
0481: * Returns true if the named parameter is read-only. If more than one control exists with the same name,
0482: * will return true only if all such controls are read-only.
0483: **/
0484: public boolean isReadOnlyParameter(String name) {
0485: return getParameter(name).isReadOnlyParameter();
0486: }
0487:
0488: /**
0489: * Returns true if the named parameter is disabled. If more than one control exists with the same name,
0490: * will return true only if all such controls are read-only.
0491: **/
0492: public boolean isDisabledParameter(String name) {
0493: return getParameter(name).isDisabledParameter();
0494: }
0495:
0496: /**
0497: * Returns true if the named parameter is hidden. If more than one control exists with the same name,
0498: * will return true only if all such controls are hidden.
0499: **/
0500: public boolean isHiddenParameter(String name) {
0501: return getParameter(name).isHiddenParameter();
0502: }
0503:
0504: /**
0505: * Creates and returns a web request which will simulate the submission of this form with an unnamed submit button.
0506: **/
0507: public WebRequest getRequest() {
0508: return getRequest((SubmitButton) null);
0509: }
0510:
0511: /**
0512: * Creates and returns a web request based on the current state of this form. No parameter validation will be done
0513: * and there is no guarantee over the order of parameters transmitted.
0514: */
0515: public WebRequest newUnvalidatedRequest() {
0516: return newUnvalidatedRequest(null);
0517: }
0518:
0519: /**
0520: * Returns the scriptable delegate.
0521: */
0522:
0523: public ScriptableDelegate getScriptableDelegate() {
0524: return getScriptableObject();
0525: }
0526:
0527: /**
0528: * Records a parameter defined by including it in the destination URL. Ignores any parameters whose name matches
0529: * a form control.
0530: **/
0531: protected void addPresetParameter(String name, String value) {
0532: FormControl[] formControls = getFormControls();
0533: for (int i = 0; i < formControls.length; i++) {
0534: if (formControls[i].getName().equals(name))
0535: return;
0536: }
0537: _presets.add(new PresetFormParameter(this , name, value));
0538: }
0539:
0540: protected String getEmptyParameterValue() {
0541: return null;
0542: }
0543:
0544: //---------------------------------- ParameterHolder methods --------------------------------
0545:
0546: /**
0547: * Specifies the position at which an image button (if any) was clicked.
0548: **/
0549: void selectImageButtonPosition(SubmitButton imageButton, int x,
0550: int y) {
0551: imageButton.setLocation(x, y);
0552: }
0553:
0554: /**
0555: * Iterates through the fixed, predefined parameters in this holder, recording them in the supplied parameter processor.\
0556: * These parameters always go on the URL, no matter what encoding method is used.
0557: **/
0558:
0559: void recordPredefinedParameters(ParameterProcessor processor)
0560: throws IOException {
0561: FormControl[] controls = getPresetParameters();
0562: for (int i = 0; i < controls.length; i++) {
0563: controls[i].addValues(processor, getCharacterSet());
0564: }
0565: }
0566:
0567: /**
0568: * Iterates through the parameters in this holder, recording them in the supplied parameter processor.
0569: **/
0570: void recordParameters(ParameterProcessor processor)
0571: throws IOException {
0572: FormControl[] controls = getFormControls();
0573: for (int i = 0; i < controls.length; i++) {
0574: controls[i].addValues(processor, getCharacterSet());
0575: }
0576: }
0577:
0578: /**
0579: * Removes a parameter name from this collection.
0580: **/
0581: public void removeParameter(String name) {
0582: setParameter(name, NO_VALUES);
0583: }
0584:
0585: /**
0586: * Sets the value of a parameter in this form.
0587: **/
0588: public void setParameter(String name, String value) {
0589: setParameter(name, new String[] { value });
0590: }
0591:
0592: /**
0593: * Sets the multiple values of a parameter in this form. This is generally used when there are multiple
0594: * controls with the same name in the form.
0595: */
0596: public void setParameter(String name, final String[] values) {
0597: FormParameter parameter = getParameter(name);
0598: if (parameter == UNKNOWN_PARAMETER)
0599: throw new NoSuchParameterException(name);
0600: parameter.setValues(values);
0601: }
0602:
0603: /**
0604: * Sets the multiple values of a file upload parameter in a web request.
0605: **/
0606: public void setParameter(String name, UploadFileSpec[] files) {
0607: FormParameter parameter = getParameter(name);
0608: if (parameter == null)
0609: throw new NoSuchParameterException(name);
0610: parameter.setFiles(files);
0611: }
0612:
0613: /**
0614: * Sets the single value of a file upload parameter in this form.
0615: * A more convenient way to do this than using {@link #setParameter(String,UploadFileSpec[])}
0616: * @since 1.6
0617: */
0618: public void setParameter(String name, File file) {
0619: setParameter(name, new UploadFileSpec[] { new UploadFileSpec(
0620: file) });
0621: }
0622:
0623: /**
0624: * Toggles the value of the specified checkbox parameter.
0625: * @param name the name of the checkbox parameter
0626: * @throws IllegalArgumentException if the specified parameter is not a checkbox or there is more than one
0627: * control with that name.
0628: * @since 1.5.4
0629: */
0630: public void toggleCheckbox(String name) {
0631: FormParameter parameter = getParameter(name);
0632: if (parameter == null)
0633: throw new NoSuchParameterException(name);
0634: parameter.toggleCheckbox();
0635: }
0636:
0637: /**
0638: * Toggles the value of the specified checkbox parameter.
0639: * @param name the name of the checkbox parameter
0640: * @param value of the checkbox parameter
0641: * @throws IllegalArgumentException if the specified parameter is not a checkbox or if there is no checkbox
0642: * with the specified name and value.
0643: * @since 1.6
0644: */
0645: public void toggleCheckbox(String name, String value) {
0646: FormParameter parameter = getParameter(name);
0647: if (parameter == null)
0648: throw new NoSuchParameterException(name);
0649: parameter.toggleCheckbox(value);
0650: }
0651:
0652: /**
0653: * Sets the value of the specified checkbox parameter.
0654: * @param name the name of the checkbox parameter
0655: * @param state the new state of the checkbox
0656: * @throws IllegalArgumentException if the specified parameter is not a checkbox or there is more than one
0657: * control with that name.
0658: * @since 1.5.4
0659: */
0660: public void setCheckbox(String name, boolean state) {
0661: FormParameter parameter = getParameter(name);
0662: if (parameter == null)
0663: throw new NoSuchParameterException(name);
0664: parameter.setValue(state);
0665: }
0666:
0667: /**
0668: * Sets the value of the specified checkbox parameter.
0669: * @param name the name of the checkbox parameter
0670: * @param value of the checkbox parameter
0671: * @param state the new state of the checkbox
0672: * @throws IllegalArgumentException if the specified parameter is not a checkbox or if there is no checkbox
0673: * with the specified name and value.
0674: * @since 1.6
0675: */
0676: public void setCheckbox(String name, String value, boolean state) {
0677: FormParameter parameter = getParameter(name);
0678: if (parameter == null)
0679: throw new NoSuchParameterException(name);
0680: parameter.setValue(value, state);
0681: }
0682:
0683: public class Scriptable extends HTMLElementScriptable implements
0684: NamedDelegate {
0685: public String getAction() {
0686: return WebForm.this .getAction();
0687: }
0688:
0689: public void setAction(String newAction) {
0690: setDestination(newAction);
0691: _presetParameters = null;
0692: }
0693:
0694: public void submit() throws IOException, SAXException {
0695: submitRequest(getScriptedSubmitRequest());
0696: }
0697:
0698: public void reset() throws IOException, SAXException {
0699: resetControls();
0700: }
0701:
0702: public String getName() {
0703: return WebForm.this .getID().length() != 0 ? WebForm.this
0704: .getID() : WebForm.this .getName();
0705: }
0706:
0707: public Object get(String propertyName) {
0708: if (propertyName.equals("target")) {
0709: return getTarget();
0710: } else if (propertyName.equals("length")) {
0711: return new Integer(getFormControls().length);
0712: } else {
0713: final FormParameter parameter = getParameter(propertyName);
0714: if (parameter != UNKNOWN_PARAMETER)
0715: return parameter.getScriptableObject();
0716: FormControl control = getControlWithID(propertyName);
0717: return control == null ? super .get(propertyName)
0718: : control.getScriptableDelegate();
0719: }
0720: }
0721:
0722: /**
0723: * Sets the value of the named property. Will throw a runtime exception if the property does not exist or
0724: * cannot accept the specified value.
0725: **/
0726: public void set(String propertyName, Object value) {
0727: if (propertyName.equals("target")) {
0728: setTargetAttribute(value.toString());
0729: } else if (value instanceof String) {
0730: setParameterValue(propertyName, (String) value);
0731: } else if (value instanceof Number) {
0732: setParameterValue(propertyName, HttpUnitUtils
0733: .trimmedValue((Number) value));
0734: } else {
0735: super .set(propertyName, value);
0736: }
0737: }
0738:
0739: public void setParameterValue(String name, String value) {
0740: final Object scriptableObject = getParameter(name)
0741: .getScriptableObject();
0742: if (scriptableObject instanceof ScriptableDelegate) {
0743: ((ScriptableDelegate) scriptableObject).set("value",
0744: value);
0745: } else if (scriptableObject instanceof ScriptableDelegate[]) {
0746: ((ScriptableDelegate[]) scriptableObject)[0].set(
0747: "value", value);
0748: }
0749: }
0750:
0751: public ScriptableDelegate[] getElementDelegates() {
0752: FormControl[] controls = getFormControls();
0753: ScriptableDelegate[] result = new ScriptableDelegate[controls.length];
0754: for (int i = 0; i < result.length; i++) {
0755: result[i] = controls[i].getScriptableDelegate();
0756: }
0757: return result;
0758: }
0759:
0760: public ScriptableDelegate[] getElementsByTagName(String name)
0761: throws SAXException {
0762: return getDelegates(getHTMLPage().getElementsByTagName(
0763: getNode(), name));
0764: }
0765:
0766: Scriptable() {
0767: super (WebForm.this );
0768: }
0769: }
0770:
0771: //---------------------------------- package members --------------------------------
0772:
0773: /**
0774: * Contructs a web form given the URL of its source page and the DOM extracted
0775: * from that page.
0776: **/
0777: WebForm(WebResponse response, URL baseURL, Node node,
0778: FrameSelector frame, String defaultTarget,
0779: String characterSet) {
0780: super (response, node, baseURL, NodeUtils.getNodeAttribute(node,
0781: "action"), frame, defaultTarget);
0782: _characterSet = characterSet;
0783: }
0784:
0785: /**
0786: * Returns the form control which is part of this form with the specified ID.
0787: */
0788: FormControl getControlWithID(String id) {
0789: FormControl[] controls = getFormControls();
0790: for (int i = 0; i < controls.length; i++) {
0791: FormControl control = controls[i];
0792: if (control.getID().equals(id))
0793: return control;
0794: }
0795: return null;
0796: }
0797:
0798: //---------------------------------- private members --------------------------------
0799:
0800: private final static String[] NO_VALUES = new String[0];
0801:
0802: /** The attributes of the form parameters. **/
0803: private FormControl[] _formControls;
0804:
0805: /** The submit buttons in this form. **/
0806: private SubmitButton[] _submitButtons;
0807:
0808: /** The character set in which the form will be submitted. **/
0809: private String _characterSet;
0810:
0811: /** A map of parameter names to form parameter objects. **/
0812: private Map _formParameters;
0813:
0814: /** The Scriptable object associated with this form. **/
0815: private Scriptable _scriptable;
0816:
0817: private Vector _buttonVector;
0818:
0819: private FormControl[] _presetParameters;
0820: private ArrayList _presets;
0821:
0822: private SubmitButton getDefaultButton() {
0823: if (getSubmitButtons().length == 1) {
0824: return getSubmitButtons()[0];
0825: } else {
0826: return getSubmitButton("");
0827: }
0828: }
0829:
0830: private Vector getSubmitButtonVector() {
0831: if (_buttonVector == null) {
0832: _buttonVector = new Vector();
0833: FormControl[] controls = getFormControls();
0834: for (int i = 0; i < controls.length; i++) {
0835: FormControl control = controls[i];
0836: if (control instanceof SubmitButton)
0837: _buttonVector.add(control);
0838: }
0839:
0840: if (_buttonVector.isEmpty())
0841: _buttonVector.addElement(new SubmitButton(this ));
0842: }
0843: return _buttonVector;
0844: }
0845:
0846: private FormControl[] getPresetParameters() {
0847: if (_presetParameters == null) {
0848: _presets = new ArrayList();
0849: loadDestinationParameters();
0850: _presetParameters = (FormControl[]) _presets
0851: .toArray(new FormControl[_presets.size()]);
0852: }
0853: return _presetParameters;
0854: }
0855:
0856: private ArrayList _controlList = new ArrayList();
0857:
0858: FormControl newFormControl(Node child) {
0859: return FormControl.newFormParameter(this , child);
0860: }
0861:
0862: void addFormControl(FormControl control) {
0863: _controlList.add(control);
0864: _formControls = null;
0865: _formParameters = null;
0866: }
0867:
0868: /**
0869: * Returns an array of form parameter attributes for this form.
0870: **/
0871: private FormControl[] getFormControls() {
0872: if (_formControls == null) {
0873: _formControls = (FormControl[]) _controlList
0874: .toArray(new FormControl[_controlList.size()]);
0875: }
0876: return _formControls;
0877: }
0878:
0879: private FormParameter getParameter(String name) {
0880: final FormParameter parameter = ((FormParameter) getFormParameters()
0881: .get(name));
0882: return parameter != null ? parameter : UNKNOWN_PARAMETER;
0883: }
0884:
0885: /**
0886: * Returns a map of parameter name to form parameter objects. Each form parameter object represents the set of form
0887: * controls with a particular name. Unnamed parameters are ignored.
0888: */
0889: private Map getFormParameters() {
0890: if (_formParameters == null) {
0891: _formParameters = new HashMap();
0892: loadFormParameters(getPresetParameters());
0893: loadFormParameters(getFormControls());
0894: }
0895: return _formParameters;
0896: }
0897:
0898: private void loadFormParameters(FormControl[] controls) {
0899: for (int i = 0; i < controls.length; i++) {
0900: if (controls[i].getName().length() == 0)
0901: continue;
0902: FormParameter parameter = (FormParameter) _formParameters
0903: .get(controls[i].getName());
0904: if (parameter == null) {
0905: parameter = new FormParameter();
0906: _formParameters.put(controls[i].getName(), parameter);
0907: }
0908: parameter.addControl(controls[i]);
0909: }
0910: }
0911:
0912: static {
0913: MATCH_NAME = new HTMLElementPredicate() {
0914: public boolean matchesCriteria(Object htmlElement,
0915: Object criteria) {
0916: return HttpUnitUtils.matches(((WebForm) htmlElement)
0917: .getName(), (String) criteria);
0918: };
0919: };
0920:
0921: }
0922:
0923: //===========================---===== exception class NoSuchParameterException =========================================
0924:
0925: /**
0926: * This exception is thrown on an attempt to set a parameter to a value not permitted to it by the form.
0927: **/
0928: class NoSuchParameterException extends
0929: IllegalRequestParameterException {
0930:
0931: NoSuchParameterException(String parameterName) {
0932: _parameterName = parameterName;
0933: }
0934:
0935: public String getMessage() {
0936: return "No parameter named '" + _parameterName
0937: + "' is defined in the form";
0938: }
0939:
0940: private String _parameterName;
0941:
0942: }
0943:
0944: //============================= exception class IllegalUnnamedSubmitButtonException ======================================
0945:
0946: /**
0947: * This exception is thrown on an attempt to define a form request with a button not defined on that form.
0948: **/
0949: class IllegalUnnamedSubmitButtonException extends
0950: IllegalRequestParameterException {
0951:
0952: IllegalUnnamedSubmitButtonException() {
0953: }
0954:
0955: public String getMessage() {
0956: return "This form has more than one submit button, none unnamed. You must specify the button to be used.";
0957: }
0958:
0959: }
0960:
0961: //============================= exception class IllegalSubmitButtonException ======================================
0962:
0963: /**
0964: * This exception is thrown on an attempt to define a form request with a button not defined on that form.
0965: **/
0966: class IllegalSubmitButtonException extends
0967: IllegalRequestParameterException {
0968:
0969: IllegalSubmitButtonException(SubmitButton button) {
0970: _name = button.getName();
0971: _value = button.getValue();
0972: }
0973:
0974: IllegalSubmitButtonException(String name, String value) {
0975: _name = name;
0976: _value = value;
0977: }
0978:
0979: public String getMessage() {
0980: return "Specified submit button (name=\"" + _name
0981: + "\" value=\"" + _value
0982: + "\") not part of this form.";
0983: }
0984:
0985: private String _name;
0986: private String _value;
0987:
0988: }
0989:
0990: //============================= exception class IllegalUnnamedSubmitButtonException ======================================
0991:
0992: /**
0993: * This exception is thrown on an attempt to define a form request with a button not defined on that form.
0994: **/
0995: class DisabledSubmitButtonException extends IllegalStateException {
0996:
0997: DisabledSubmitButtonException(SubmitButton button) {
0998: _name = button.getName();
0999: _value = button.getValue();
1000: }
1001:
1002: public String getMessage() {
1003: return "The specified button (name='"
1004: + _name
1005: + "' value='"
1006: + _value
1007: + "' is disabled and may not be used to submit this form.";
1008: }
1009:
1010: private String _name;
1011: private String _value;
1012:
1013: }
1014:
1015: }
1016:
1017: //========================================== class PresetFormParameter =================================================
1018:
1019: class PresetFormParameter extends FormControl {
1020:
1021: PresetFormParameter(WebForm form, String name, String value) {
1022: super (form);
1023: _name = name;
1024: _value = value;
1025: }
1026:
1027: /**
1028: * Returns the name of this control..
1029: **/
1030: public String getName() {
1031: return _name;
1032: }
1033:
1034: /**
1035: * Returns true if this control is read-only.
1036: **/
1037: public boolean isReadOnly() {
1038: return true;
1039: }
1040:
1041: /**
1042: * Returns true if this control accepts free-form text.
1043: **/
1044: public boolean isTextControl() {
1045: return true;
1046: }
1047:
1048: /**
1049: * Remove any required values for this control from the list, throwing an exception if they are missing.
1050: **/
1051: void claimRequiredValues(List values) {
1052: if (_value != null)
1053: claimValueIsRequired(values, _value);
1054: }
1055:
1056: public String getType() {
1057: return UNDEFINED_TYPE;
1058: }
1059:
1060: /**
1061: * Returns the current value(s) associated with this control. These values will be transmitted to the server
1062: * if the control is 'successful'.
1063: **/
1064: public String[] getValues() {
1065: if (_values == null)
1066: _values = new String[] { _value };
1067: return _values;
1068: }
1069:
1070: void addValues(ParameterProcessor processor, String characterSet)
1071: throws IOException {
1072: processor.addParameter(_name, _value, characterSet);
1073: }
1074:
1075: private String _name;
1076: private String _value;
1077: private String[] _values;
1078: }
|