0001: // Jericho HTML Parser - Java based library for analysing and manipulating HTML
0002: // Version 2.5
0003: // Copyright (C) 2007 Martin Jericho
0004: // http://jerichohtml.sourceforge.net/
0005: //
0006: // This library is free software; you can redistribute it and/or
0007: // modify it under the terms of either one of the following licences:
0008: //
0009: // 1. The Eclipse Public License (EPL) version 1.0,
0010: // included in this distribution in the file licence-epl-1.0.html
0011: // or available at http://www.eclipse.org/legal/epl-v10.html
0012: //
0013: // 2. The GNU Lesser General Public License (LGPL) version 2.1 or later,
0014: // included in this distribution in the file licence-lgpl-2.1.txt
0015: // or available at http://www.gnu.org/licenses/lgpl.txt
0016: //
0017: // This library is distributed on an "AS IS" basis,
0018: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
0019: // See the individual licence texts for more details.
0020:
0021: package au.id.jericho.lib.html;
0022:
0023: import java.util.*;
0024:
0025: /**
0026: * Represents an HTML form <a target="_blank" href="http://www.w3.org/TR/html401/interact/forms.html#form-controls">control</a>.
0027: * <p>
0028: * A <code>FormControl</code> consists of a single {@linkplain #getElement() element}
0029: * that matches one of the {@linkplain FormControlType form control types}.
0030: * <p>
0031: * The term <i><a name="OutputElement">output element</a></i> is used to describe the element that is
0032: * {@linkplain OutputSegment#writeTo(Writer) output} if this form control is {@linkplain OutputDocument#replace(FormControl) replaced}
0033: * in an {@link OutputDocument}.
0034: * <p>
0035: * A <i><a name="PredefinedValueControl">predefined value control</a></i> is a form control for which
0036: * {@link #getFormControlType()}.{@link FormControlType#hasPredefinedValue() hasPredefinedValue()}
0037: * returns <code>true</code>. It has a {@linkplain #getFormControlType() control type} of
0038: * {@link FormControlType#CHECKBOX CHECKBOX}, {@link FormControlType#RADIO RADIO}, {@link FormControlType#BUTTON BUTTON},
0039: * {@link FormControlType#SUBMIT SUBMIT}, {@link FormControlType#IMAGE IMAGE}, {@link FormControlType#SELECT_SINGLE SELECT_SINGLE}
0040: * or {@link FormControlType#SELECT_MULTIPLE SELECT_MULTIPLE}.
0041: * <p>
0042: * A <i><a name="UserValueControl">user value control</a></i> is a form control for which
0043: * {@link #getFormControlType()}.{@link FormControlType#hasPredefinedValue() hasPredefinedValue()}
0044: * returns <code>false</code>. It has a {@linkplain #getFormControlType() control type} of
0045: * {@link FormControlType#FILE FILE}, {@link FormControlType#HIDDEN HIDDEN}, {@link FormControlType#PASSWORD PASSWORD},
0046: * {@link FormControlType#TEXT TEXT} or {@link FormControlType#TEXTAREA TEXTAREA}.
0047: * <p>
0048: * The functionality of most significance to users of this class relates to the
0049: * <i><a name="DisplayCharacteristics">display characteristics</a></i> of the <a href="#OutputElement">output element</a>,
0050: * manipulated using the {@link #setDisabled(boolean)} and {@link #setOutputStyle(FormControlOutputStyle)} methods.
0051: * <p>
0052: * As a general rule, the operations dealing with the control's <a href="#SubmissionValue">submission values</a>
0053: * are better performed on a {@link FormFields} or {@link FormField} object, which provide a more
0054: * intuitive interface by grouping form controls of the same {@linkplain #getName() name} together.
0055: * The higher abstraction level of these classes means they can automatically ensure that the
0056: * <a href="#SubmissionValue">submission values</a> of their constituent controls are consistent with each other,
0057: * for example by ensuring that only one {@link FormControlType#RADIO RADIO} control with a given name is
0058: * {@link #isChecked() checked} at a time.
0059: * <p>
0060: * A {@link FormFields} object can be directly {@linkplain FormFields#FormFields(Collection) constructed} from
0061: * a collection of <code>FormControl</code> objects.
0062: * <p>
0063: * <code>FormControl</code> instances are obtained using the {@link Element#getFormControl()} method or are created automatically
0064: * with the creation of a {@link FormFields} object via the {@link Segment#findFormFields()} method.
0065: *
0066: * @see FormControlType
0067: * @see FormFields
0068: * @see FormField
0069: */
0070: public abstract class FormControl extends Segment {
0071: FormControlType formControlType;
0072: String name;
0073: ElementContainer elementContainer;
0074: FormControlOutputStyle outputStyle = FormControlOutputStyle.NORMAL;
0075:
0076: private static final String CHECKBOX_NULL_DEFAULT_VALUE = "on";
0077: private static Comparator COMPARATOR = new PositionComparator();
0078:
0079: static FormControl construct(final Element element) {
0080: final String tagName = element.getStartTag().getName();
0081: if (tagName == Tag.INPUT) {
0082: final String typeAttributeValue = element.getAttributes()
0083: .getRawValue(Attribute.TYPE);
0084: if (typeAttributeValue == null)
0085: return new InputFormControl(element,
0086: FormControlType.TEXT);
0087: FormControlType formControlType = FormControlType
0088: .getFromInputElementType(typeAttributeValue
0089: .toLowerCase());
0090: if (formControlType == null) {
0091: if (element.source.logger.isInfoEnabled())
0092: element.source.logger
0093: .info(element.source
0094: .getRowColumnVector(element.begin)
0095: .appendTo(new StringBuffer(200))
0096: .append(
0097: ": INPUT control with unrecognised type \"")
0098: .append(typeAttributeValue)
0099: .append(
0100: "\" assumed to be type \"text\"")
0101: .toString());
0102: formControlType = FormControlType.TEXT;
0103: }
0104: if (formControlType == FormControlType.TEXT)
0105: return new InputFormControl(element, formControlType);
0106: if (formControlType == FormControlType.CHECKBOX
0107: || formControlType == FormControlType.RADIO)
0108: return new RadioCheckboxFormControl(element,
0109: formControlType);
0110: if (formControlType == FormControlType.SUBMIT)
0111: return new SubmitFormControl(element, formControlType);
0112: if (formControlType == FormControlType.IMAGE)
0113: return new ImageSubmitFormControl(element);
0114: if (formControlType == FormControlType.NON_FORM_CONTROL)
0115: return null;
0116: // formControlType is HIDDEN || PASSWORD || FILE
0117: return new InputFormControl(element, formControlType);
0118: } else if (tagName == Tag.SELECT) {
0119: return new SelectFormControl(element);
0120: } else if (tagName == Tag.TEXTAREA) {
0121: return new TextAreaFormControl(element);
0122: } else if (tagName == Tag.BUTTON) {
0123: return "submit".equalsIgnoreCase(element.getAttributes()
0124: .getRawValue(Attribute.TYPE)) ? new SubmitFormControl(
0125: element, FormControlType.BUTTON)
0126: : null;
0127: } else {
0128: return null;
0129: }
0130: }
0131:
0132: private FormControl(final Element element,
0133: final FormControlType formControlType,
0134: final boolean loadPredefinedValue) {
0135: super (element.source, element.begin, element.end);
0136: elementContainer = new ElementContainer(element,
0137: loadPredefinedValue);
0138: this .formControlType = formControlType;
0139: name = element.getAttributes().getValue(Attribute.NAME);
0140: verifyName();
0141: }
0142:
0143: /**
0144: * Returns the {@linkplain FormControlType type} of this form control.
0145: * @return the {@linkplain FormControlType type} of this form control.
0146: */
0147: public final FormControlType getFormControlType() {
0148: return formControlType;
0149: }
0150:
0151: /**
0152: * Returns the <a target="_blank" href="http://www.w3.org/TR/html401/interact/forms.html#control-name">name</a> of the control.
0153: * <p>
0154: * The name comes from the value of the <code>name</code> {@linkplain Attribute attribute} of the
0155: * {@linkplain #getElement() form control's element}, not the {@linkplain Element#getName() name of the element} itself.
0156: * <p>
0157: * Since a {@link FormField} is simply a group of controls with the same name, the terms <i>control name</i> and
0158: * <i>field name</i> are for the most part synonymous, with only a possible difference in case differentiating them.
0159: * <p>
0160: * In contrast to the {@link FormField#getName()} method, this method always returns the name using the original case
0161: * from the source document, regardless of the current setting of the
0162: * {@link Config#CurrentCompatibilityMode}<code>.</code>{@link Config.CompatibilityMode#isFormFieldNameCaseInsensitive() FormFieldNameCaseInsensitive} property.
0163: *
0164: * @return the <a target="_blank" href="http://www.w3.org/TR/html401/interact/forms.html#control-name">name</a> of the control.
0165: */
0166: public final String getName() {
0167: return name;
0168: }
0169:
0170: /**
0171: * Returns the {@linkplain Element element} representing this form control in the source document.
0172: * <p>
0173: * The {@linkplain Element#getAttributes() attributes} of this source element should correspond with the
0174: * <a href="#OutputAttributes">output attributes</a> if the
0175: * <a href="#DisplayCharacteristics">display characteristics</a> or <a href="FormField.html#SubmissionValue">submission values</a>
0176: * have not been modified.
0177: *
0178: * @return the {@linkplain Element element} representing this form control in the source document.
0179: */
0180: public final Element getElement() {
0181: return elementContainer.element;
0182: }
0183:
0184: /**
0185: * Returns an iterator over the {@link Tag#OPTION OPTION} {@linkplain Element elements} contained within this control, in order of appearance.
0186: * <p>
0187: * This method is only relevant to form controls with a {@linkplain #getFormControlType() type} of
0188: * {@link FormControlType#SELECT_SINGLE SELECT_SINGLE} or {@link FormControlType#SELECT_MULTIPLE SELECT_MULTIPLE}.
0189: *
0190: * @return an iterator over the {@link Tag#OPTION OPTION} {@linkplain Element elements} contained within this control, in order of appearance.
0191: * @throws UnsupportedOperationException if the {@linkplain #getFormControlType() type} of this control is not {@link FormControlType#SELECT_SINGLE SELECT_SINGLE} or {@link FormControlType#SELECT_MULTIPLE SELECT_MULTIPLE}.
0192: */
0193: public Iterator getOptionElementIterator() {
0194: // overridden in SelectFormControl
0195: throw new UnsupportedOperationException(
0196: "Only SELECT controls contain OPTION elements");
0197: }
0198:
0199: /**
0200: * Returns the current {@linkplain FormControlOutputStyle output style} of this form control.
0201: * <p>
0202: * This property affects how this form control is displayed if it has been {@linkplain OutputDocument#replace(FormControl) replaced}
0203: * in an {@link OutputDocument}.
0204: * See the documentation of the {@link FormControlOutputStyle} class for information on the available output styles.
0205: * <p>
0206: * The default output style for every form control is {@link FormControlOutputStyle#NORMAL}.
0207: *
0208: * @return the current {@linkplain FormControlOutputStyle output style} of this form control.
0209: * @see #setOutputStyle(FormControlOutputStyle)
0210: */
0211: public FormControlOutputStyle getOutputStyle() {
0212: return outputStyle;
0213: }
0214:
0215: /**
0216: * Sets the {@linkplain FormControlOutputStyle output style} of this form control.
0217: * <p>
0218: * See the {@link #getOutputStyle()} method for a full description of this property.
0219: *
0220: * @param outputStyle the new {@linkplain FormControlOutputStyle output style} of this form control.
0221: */
0222: public void setOutputStyle(final FormControlOutputStyle outputStyle) {
0223: this .outputStyle = outputStyle;
0224: }
0225:
0226: /**
0227: * Returns a map of the names and values of this form control's <a href="#OutputAttributes">output attributes</a>.
0228: * <p>
0229: * The term <i><a name="OutputAttributes">output attributes</a></i> is used in this library to refer to the
0230: * <a target="_blank" href="http://www.w3.org/TR/html401/intro/sgmltut.html#h-3.2.2">attributes</a> of a form control's
0231: * <a href="#OutputElement">output element</a>.
0232: * <p>
0233: * The map keys are the <code>String</code> attribute names, which should all be in lower case.
0234: * The map values are the corresponding <code>CharSequence</code> attribute values, with a <code>null</code> value given
0235: * to an attribute that {@linkplain Attribute#hasValue() has no value}.
0236: * <p>
0237: * Direct manipulation of the returned map affects the attributes of this form control's <a href="#OutputElement">output element</a>.
0238: * It is the responsibility of the user to ensure that all entries added to the map use the correct key and value types,
0239: * and that all keys (attribute names) are in lower case.
0240: * <p>
0241: * It is recommended that the <a href="#SubmissionValueModificationMethods">submission value modification methods</a>
0242: * are used to modify attributes that affect the <a href="#SubmissionValue">submission value</a> of the control
0243: * rather than manipulating the attributes map directly.
0244: * <p>
0245: * An iteration over the map entries will return the attributes in the same order as they appeared in the source document, or
0246: * at the end if the attribute was not present in the source document.
0247: * <p>
0248: * The returned attributes only correspond with those of the {@linkplain #getElement() source element} if the control's
0249: * <a href="#DisplayCharacteristics">display characteristics</a> and <a href="#SubmissionValue">submission values</a>
0250: * have not been modified.
0251: *
0252: * @return a map of the names and values of this form control's <a href="#OutputAttributes">output attributes</a>.
0253: */
0254: public final Map getAttributesMap() {
0255: return elementContainer.getAttributesMap();
0256: }
0257:
0258: /**
0259: * Indicates whether this form control is <a target="_blank" href="http://www.w3.org/TR/html401/interact/forms.html#adef-disabled">disabled</a>.
0260: * <p>
0261: * The form control is disabled if the attribute
0262: * "<code><a target="_blank" href="http://www.w3.org/TR/html401/interact/forms.html#adef-disabled">disabled</a></code>"
0263: * is present in its <a href="#OutputElement">output element</a>.
0264: * <p>
0265: * The return value is is logically equivalent to {@link #getAttributesMap()}<code>.containsKey("disabled")</code>,
0266: * but using this property may be more efficient in some circumstances.
0267: *
0268: * @return <code>true</code> if this form control is <a target="_blank" href="http://www.w3.org/TR/html401/interact/forms.html#adef-disabled">disabled</a>, otherwise <code>false</code>.
0269: */
0270: public final boolean isDisabled() {
0271: return elementContainer.getBooleanAttribute(Attribute.DISABLED);
0272: }
0273:
0274: /**
0275: * Sets whether this form control is <a target="_blank" href="http://www.w3.org/TR/html401/interact/forms.html#adef-disabled">disabled</a>.
0276: * <p>
0277: * If the argument supplied to this method is <code>true</code> and the <code>disabled</code> attribute is not already present
0278: * in the output element, the full
0279: * <a target="_blank" href="http://www.w3.org/TR/xhtml1/">XHTML</a> compatible attribute <code>disabled="disabled"</code> is added.
0280: * If the attribute is already present, it is left unchanged.
0281: * <p>
0282: * If the argument supplied to this method is <code>false</code>, the attribute is removed from the output element.
0283: * <p>
0284: * See the {@link #isDisabled()} method for more information.
0285: *
0286: * @param disabled the new value of this property.
0287: */
0288: public final void setDisabled(final boolean disabled) {
0289: elementContainer.setBooleanAttribute(Attribute.DISABLED,
0290: disabled);
0291: }
0292:
0293: /**
0294: * Indicates whether this form control is <a target="_blank" href="http://www.w3.org/TR/html401/interact/forms.html#adef-checked">checked</a>.
0295: * <p>
0296: * The term <i>checked</i> is used to describe a checkbox or radio button control that is selected, which is the case if the attribute
0297: * "<code><a target="_blank" href="http://www.w3.org/TR/html401/interact/forms.html#adef-checked">checked</a></code>"
0298: * is present in its <a href="#OutputElement">output element</a>.
0299: * <p>
0300: * This property is only relevant to form controls with a {@linkplain #getFormControlType() type} of
0301: * {@link FormControlType#CHECKBOX} or {@link FormControlType#RADIO}, and throws an <code>UnsupportedOperationException</code>
0302: * for other control types.
0303: * <p>
0304: * Use one of the <a href="#SubmissionValueModificationMethods">submission value modification methods</a> to change the value
0305: * of this property.
0306: * <p>
0307: * If this control is a checkbox, you can set it to checked by calling
0308: * {@link #setValue(CharSequence) setValue}<code>(</code>{@link #getName()}<code>)</code>, and set it to unchecked by calling
0309: * {@link #clearValues()}.
0310: * <p>
0311: * If this control is a radio button, you should use the {@link FormField#setValue(CharSequence)} method or one of the other
0312: * higher level <a href="#SubmissionValueModificationMethods">submission value modification methods</a>
0313: * to set the control to checked, as calling {@link #setValue(CharSequence)} method on this object
0314: * in the same way as for a checkbox does not automatically uncheck all other radio buttons with the same name.
0315: * Even calling {@link #clearValues()} on this object to ensure that this radio button is unchecked is not recommended, as
0316: * it can lead to a situation where all the radio buttons with this name are unchecked.
0317: * The <a target="_blank" href="http://www.w3.org/TR/html401/interact/forms.html#radio">HTML 4.01 specification of radio buttons</a>
0318: * recommends against this situation because it is not defined how user agents should handle it, and behaviour differs amongst browsers.
0319: * <p>
0320: * The return value is logically equivalent to {@link #getAttributesMap()}<code>.containsKey("checked")</code>,
0321: * but using this property may be more efficient in some circumstances.
0322: *
0323: * @return <code>true</code> if this form control is <a target="_blank" href="http://www.w3.org/TR/html401/interact/forms.html#adef-checked">checked</a>, otherwise <code>false</code>.
0324: * @throws UnsupportedOperationException if the {@linkplain #getFormControlType() type} of this control is not {@link FormControlType#CHECKBOX} or {@link FormControlType#RADIO}.
0325: */
0326: public boolean isChecked() {
0327: throw new UnsupportedOperationException(
0328: "This property is only relevant for CHECKBOX and RADIO controls");
0329: }
0330:
0331: /**
0332: * Returns the <a target="_blank" href="http://www.w3.org/TR/html401/interact/forms.html#initial-value">initial value</a> of this control if it has a {@linkplain FormControlType#hasPredefinedValue() predefined value}.
0333: * <p>
0334: * Only <a href="#PredefinedValueControl">predefined value controls</a> can return a non-<code>null</code> result.
0335: * All other control types return <code>null</code>.
0336: * <p>
0337: * {@link FormControlType#CHECKBOX CHECKBOX} and {@link FormControlType#RADIO RADIO} controls have a guaranteed
0338: * predefined value determined by the value of its compulsory
0339: * <code><a target="_blank" href="http://www.w3.org/TR/html401/interact/forms.html#adef-value-INPUT">value</a></code>
0340: * attribute. If the attribute is not present in the source document, this library assigns the control a default
0341: * predefined value of "<code>on</code>", consistent with popular browsers.
0342: * <p>
0343: * {@link FormControlType#SUBMIT SUBMIT}, {@link FormControlType#BUTTON BUTTON} and {@link FormControlType#IMAGE IMAGE}
0344: * controls have an optional predefined value determined by the value of its
0345: * <code><a target="_blank" href="http://www.w3.org/TR/html401/interact/forms.html#adef-value-INPUT">value</a></code>
0346: * attribute. This value is
0347: * <a target="_blank" href="http://www.w3.org/TR/html401/interact/forms.html#successful-controls">successful</a>
0348: * only in the control used to submit the form.
0349: * <p>
0350: * {@link FormControlType#SELECT_SINGLE} and {@link FormControlType#SELECT_MULTIPLE} controls are special cases
0351: * because they usually contain multiple
0352: * <code><a target="_blank" href="http://www.w3.org/TR/html401/interact/forms.html#edef-OPTION">OPTION</a></code>
0353: * elements, each with its own predefined value.
0354: * In this case the {@link #getPredefinedValues()} method should be used instead, which returns a collection of all the
0355: * control's predefined values. Attempting to call this method on a <code>SELECT</code> control results in
0356: * a <code>java.lang.UnsupportedOperationException</code>.
0357: * <p>
0358: * The predefined value of a control is not affected by changes to the
0359: * <a href="#SubmissionValue">submission value</a> of the control.
0360: *
0361: * @return the <a target="_blank" href="http://www.w3.org/TR/html401/interact/forms.html#initial-value">initial value</a> of this control if it has a {@linkplain FormControlType#hasPredefinedValue() predefined value}, or <code>null</code> if none.
0362: */
0363: public String getPredefinedValue() {
0364: return elementContainer.predefinedValue;
0365: }
0366:
0367: /**
0368: * Returns a collection of all {@linkplain #getPredefinedValue() predefined values} in this control.
0369: * <p>
0370: * All objects in the returned collection are of type <code>String</code>, with no <code>null</code> entries.
0371: * <p>
0372: * This method is most useful for
0373: * <code><a target="_blank" href="http://www.w3.org/TR/html401/interact/forms.html#edef-SELECT">SELECT</a></code>
0374: * controls since they typically contain multiple predefined values.
0375: * In other controls it returns a collection with zero or one item based on the output of the
0376: * {@link #getPredefinedValue()} method, so for efficiency it is recommended to use the
0377: * {@link #getPredefinedValue()} method instead.
0378: * <p>
0379: * The multiple predefined values of a
0380: * <code><a target="_blank" href="http://www.w3.org/TR/html401/interact/forms.html#edef-SELECT">SELECT</a></code>
0381: * control are defined by the
0382: * <code><a target="_blank" href="http://www.w3.org/TR/html401/interact/forms.html#edef-OPTION">OPTION</a></code>
0383: * elements within it.
0384: * Each <code>OPTION</code> element has an
0385: * <a target="_blank" href="http://www.w3.org/TR/html401/interact/forms.html#initial-value">initial value</a>
0386: * determined by the value of its
0387: * <code><a target="_blank" href="http://www.w3.org/TR/html401/interact/forms.html#adef-value-OPTION">value</a></code>
0388: * attribute, or if this attribute is not present, by its
0389: * {@linkplain CharacterReference#decode(CharSequence) decoded} {@linkplain Element#getContent() content}
0390: * text with {@linkplain CharacterReference#decodeCollapseWhiteSpace(CharSequence) collapsed white space}.
0391: * <p>
0392: * The predefined values of a control are not affected by changes to the
0393: * <a href="#SubmissionValue">submission values</a> of the control.
0394: *
0395: * @return a collection of all {@linkplain #getPredefinedValue() predefined values} in this control, guaranteed not <code>null</code>.
0396: * @see FormField#getPredefinedValues()
0397: */
0398: public Collection getPredefinedValues() {
0399: return getPredefinedValue() != null ? Collections
0400: .singleton(getPredefinedValue())
0401: : Collections.EMPTY_SET;
0402: }
0403:
0404: /**
0405: * Returns a collection of the control's <a href="#SubmissionValue">submission values</a>.
0406: * <p>
0407: * All objects in the returned collection are of type <code>CharSequence</code>, with no <code>null</code> entries.
0408: * <p>
0409: * The term <i><a name="SubmissionValue">submission value</a></i> is used in this library to refer to the value the control
0410: * would contribute to the
0411: * <a target="_blank" href="http://www.w3.org/TR/html401/interact/forms.html#form-data-set">form data set</a>
0412: * of a <a target="_blank" href="http://www.w3.org/TR/html401/interact/forms.html#submit-format">submitted</a>
0413: * form, assuming no modification of the control's
0414: * <a target="_blank" href="http://www.w3.org/TR/html401/interact/forms.html#current-value">current value</a> by the
0415: * <a target="_blank" href="http://www.w3.org/TR/html401/conform.html#didx-user_agent">user agent</a> or by end user interaction.
0416: * <p>
0417: * For <a href="#UserValueControl">user value controls</a>, the submission value corresponds to the
0418: * control's <a target="_blank" href="http://www.w3.org/TR/html401/interact/forms.html#initial-value">initial value</a>.
0419: * <p>
0420: * The definition of the submission value for each <a href="#PredefinedValueControl">predefined value control</a> type is as follows:
0421: * <p>
0422: * {@link FormControlType#CHECKBOX CHECKBOX} and {@link FormControlType#RADIO RADIO} controls
0423: * have a submission value specified by its {@linkplain #getPredefinedValue() predefined value}
0424: * if it is {@link #isChecked() checked}, otherwise it has no submission value.
0425: * <p>
0426: * {@link FormControlType#SELECT_SINGLE SELECT_SINGLE} and {@link FormControlType#SELECT_MULTIPLE SELECT_MULTIPLE} controls
0427: * have submission values specified by the
0428: * <a target="_blank" href="http://www.w3.org/TR/html401/interact/forms.html#adef-value-OPTION">values</a> of the control's
0429: * <a target="_blank" href="http://www.w3.org/TR/html401/interact/forms.html#adef-selected">selected</a>
0430: * <code>OPTION</code> elements.
0431: * <p>
0432: * Only a {@link FormControlType#SELECT_MULTIPLE SELECT_MULTIPLE} control can have more than one submission value,
0433: * all other {@linkplain FormControlType control types} return a collection containing either one value or no values.
0434: * A {@link FormControlType#SELECT_SINGLE SELECT_SINGLE} control only returns multiple submission values
0435: * if it illegally contains multiple selected options in the source document.
0436: * <p>
0437: * {@link FormControlType#SUBMIT SUBMIT}, {@link FormControlType#BUTTON BUTTON}, and {@link FormControlType#IMAGE IMAGE}
0438: * controls are only ever
0439: * <a target="_blank" href="http://www.w3.org/TR/html401/interact/forms.html#successful-controls">successful</a>
0440: * when they are activated by the user to
0441: * <a target="_blank" href="http://www.w3.org/TR/html401/interact/forms.html#submit-format">submit</a> the form.
0442: * Because the submission value is intended to be a static representation of a control's data without
0443: * interaction by the user, this library never associates submission values with
0444: * {@linkplain FormControlType#isSubmit() submit} buttons, so this method always returns an empty collection for these
0445: * control types.
0446: * <p>
0447: * The <a href="#SubmissionValue">submission value(s)</a> of a control can be modified for subsequent output in
0448: * an {@link OutputDocument} using the various
0449: * <i><a name="SubmissionValueModificationMethods">submission value modification methods</a></i>, namely:<br />
0450: * {@link FormField#setValue(CharSequence)}<br />
0451: * {@link FormField#addValue(CharSequence)}<br />
0452: * {@link FormField#setValues(Collection)}<br />
0453: * {@link FormField#clearValues()}<br />
0454: * {@link FormFields#setValue(String fieldName, CharSequence value)}<br />
0455: * {@link FormFields#addValue(String fieldName, CharSequence value)}<br />
0456: * {@link FormFields#setDataSet(Map)}<br />
0457: * {@link FormFields#clearValues()}<br />
0458: * {@link #setValue(CharSequence) FormControl.setValue(CharSequence)}<br />
0459: * {@link #addValue(CharSequence) FormControl.addValue(CharSequence)}<br />
0460: * {@link #clearValues() FormControl.clearValues()}<br />
0461: * <p>
0462: * The values returned by this method reflect any changes made using the submission value modification methods,
0463: * in contrast to methods found in the {@link Attributes} and {@link Attribute} classes, which always reflect the source document.
0464: *
0465: * @return a collection of the control's <i>submission values</i>, guaranteed not <code>null</code>.
0466: * @see #getPredefinedValues()
0467: */
0468: public Collection getValues() {
0469: final HashSet values = new HashSet();
0470: addValuesTo(values);
0471: return values;
0472: }
0473:
0474: /**
0475: * Clears the control's existing <a href="#SubmissionValue">submission values</a>.
0476: * <p>
0477: * This is equivalent to {@link #setValue(CharSequence) setValue(null)}.
0478: * <p>
0479: * NOTE: The {@link FormFields} and {@link FormField} classes provide a more appropriate abstraction level for the modification of form control submission values.
0480: *
0481: * @see FormFields#clearValues()
0482: * @see FormField#clearValues()
0483: */
0484: public final void clearValues() {
0485: setValue(null);
0486: }
0487:
0488: /**
0489: * Sets the control's <a href="#SubmissionValue">submission value</a> *.
0490: * <p>
0491: * * NOTE: The {@link FormFields} and {@link FormField} classes provide a more appropriate abstraction level for the modification of form control submission values.
0492: * Consider using the {@link FormFields#setValue(String fieldName, CharSequence value)} method instead.
0493: * <p>
0494: * The specified value replaces any existing <a href="#SubmissionValue">submission values</a> of the control.
0495: * <p>
0496: * The return value indicates whether the control has "accepted" the value.
0497: * For <a href="#UserValueControl">user value controls</a>, the return value is always <code>true</code>.
0498: * <p>
0499: * For <a href="#PredefinedValueControl">predefined value controls</a>,
0500: * calling this method does not affect the control's
0501: * {@linkplain #getPredefinedValues() predefined values}, but instead determines whether the control (or its options) become
0502: * <code><a target="_blank" href="http://www.w3.org/TR/html401/interact/forms.html#adef-checked">checked</a></code> or
0503: * <code><a target="_blank" href="http://www.w3.org/TR/html401/interact/forms.html#adef-selected">selected</a></code>
0504: * as detailed below:
0505: * <p>
0506: * {@link FormControlType#CHECKBOX CHECKBOX} and {@link FormControlType#RADIO RADIO} controls become {@link #isChecked() checked}
0507: * and the method returns <code>true</code> if the specified value matches the control's predefined value (case sensitive),
0508: * otherwise the control becomes unchecked and the method returns <code>false</code>.
0509: * Note that any other controls with the same {@linkplain #getName() name} are not unchecked if this control becomes checked,
0510: * possibly resulting in an invalid state where multiple <code>RADIO</code> controls are checked at the same time.
0511: * The {@link FormField#setValue(CharSequence)} method avoids such problems and its use is recommended over this method.
0512: * <p>
0513: * {@link FormControlType#SELECT_SINGLE SELECT_SINGLE} and {@link FormControlType#SELECT_MULTIPLE SELECT_MULTIPLE}
0514: * controls receive the specified value by selecting the option with the matching value and deselecting all others.
0515: * If none of the options match, all are deselected.
0516: * The return value of this method indicates whether one of the options matched.
0517: * <p>
0518: * {@link FormControlType#SUBMIT SUBMIT}, {@link FormControlType#BUTTON BUTTON}, and {@link FormControlType#IMAGE IMAGE}
0519: * controls never have a <a href="#SubmissionValue">submission value</a>, so calling this method has no effect and
0520: * always returns <code>false</code>.
0521: *
0522: * @param value the new <a href="#SubmissionValue">submission value</a> of this control, or <code>null</code> to clear the control of all submission values.
0523: * @return <code>true</code> if the control accepts the value, otherwise <code>false</code>.
0524: * @see FormFields#setValue(String fieldName, CharSequence value)
0525: */
0526: public abstract boolean setValue(CharSequence value);
0527:
0528: /**
0529: * Adds the specified value to this control's <a href="#SubmissionValue">submission values</a> *.
0530: * <p>
0531: * * NOTE: The {@link FormFields} and {@link FormField} classes provide a more appropriate abstraction level for the modification of form control submission values.
0532: * Consider using the {@link FormFields#addValue(String fieldName, CharSequence value)} method instead.
0533: * <p>
0534: * This is almost equivalent to {@link #setValue(CharSequence)}, with only the following differences:
0535: * <p>
0536: * {@link FormControlType#CHECKBOX CHECKBOX} controls retain their existing <a href="#SubmissionValue">submission value</a>
0537: * instead of becoming unchecked if the specified value does not match the control's {@linkplain #getPredefinedValue() predefined value}.
0538: * <p>
0539: * {@link FormControlType#SELECT_MULTIPLE SELECT_MULTIPLE} controls retain their existing
0540: * <a href="#SubmissionValue">submission values</a>, meaning that the control's
0541: * <code><a target="_blank" href="http://www.w3.org/TR/html401/interact/forms.html#edef-OPTION">OPTION</a></code>
0542: * elements whose {@linkplain #getPredefinedValues() predefined values} do not match the specified value are not deselected.
0543: * This is the only type of control that can have multiple submission values within the one control.
0544: *
0545: * @param value the value to add to this control's <a href="#SubmissionValue">submission values</a>, must not be <code>null</code>.
0546: * @return <code>true</code> if the control accepts the value, otherwise <code>false</code>.
0547: * @see FormFields#addValue(String fieldName, CharSequence value)
0548: */
0549: public boolean addValue(final CharSequence value) {
0550: return setValue(value);
0551: }
0552:
0553: abstract void addValuesTo(Collection collection); // should not add null values, values must be of type CharSequence
0554:
0555: abstract void addToFormFields(FormFields formFields);
0556:
0557: abstract void replaceInOutputDocument(OutputDocument outputDocument);
0558:
0559: public String getDebugInfo() {
0560: final StringBuffer sb = new StringBuffer();
0561: sb.append(formControlType).append(" name=\"").append(name)
0562: .append('"');
0563: if (elementContainer.predefinedValue != null)
0564: sb.append(" PredefinedValue=\"").append(
0565: elementContainer.predefinedValue).append('"');
0566: sb.append(" - ").append(getElement().getDebugInfo());
0567: return sb.toString();
0568: }
0569:
0570: static final class InputFormControl extends FormControl {
0571: // TEXT, HIDDEN, PASSORD or FILE
0572: public InputFormControl(final Element element,
0573: final FormControlType formControlType) {
0574: super (element, formControlType, false);
0575: }
0576:
0577: public boolean setValue(final CharSequence value) {
0578: elementContainer.setAttributeValue(Attribute.VALUE, value);
0579: return true;
0580: }
0581:
0582: void addValuesTo(final Collection collection) {
0583: addValueTo(collection, elementContainer
0584: .getAttributeValue(Attribute.VALUE));
0585: }
0586:
0587: void addToFormFields(final FormFields formFields) {
0588: formFields.add(this );
0589: }
0590:
0591: void replaceInOutputDocument(final OutputDocument outputDocument) {
0592: if (outputStyle == FormControlOutputStyle.REMOVE) {
0593: outputDocument.remove(getElement());
0594: } else if (outputStyle == FormControlOutputStyle.DISPLAY_VALUE) {
0595: String output = null;
0596: if (formControlType != FormControlType.HIDDEN) {
0597: CharSequence value = elementContainer
0598: .getAttributeValue(Attribute.VALUE);
0599: if (formControlType == FormControlType.PASSWORD
0600: && value != null)
0601: value = getString(
0602: FormControlOutputStyle.ConfigDisplayValue.PasswordChar,
0603: value.length());
0604: output = getDisplayValueHTML(value, false);
0605: }
0606: outputDocument.replace(getElement(), output);
0607: } else {
0608: replaceAttributesInOutputDocumentIfModified(outputDocument);
0609: }
0610: }
0611: }
0612:
0613: static final class TextAreaFormControl extends FormControl {
0614: // TEXTAREA
0615: public CharSequence value = UNCHANGED;
0616: private static final String UNCHANGED = new String();
0617:
0618: public TextAreaFormControl(final Element element) {
0619: super (element, FormControlType.TEXTAREA, false);
0620: }
0621:
0622: public boolean setValue(final CharSequence value) {
0623: this .value = value;
0624: return true;
0625: }
0626:
0627: void addValuesTo(final Collection collection) {
0628: addValueTo(collection, getValue());
0629: }
0630:
0631: void addToFormFields(final FormFields formFields) {
0632: formFields.add(this );
0633: }
0634:
0635: void replaceInOutputDocument(final OutputDocument outputDocument) {
0636: if (outputStyle == FormControlOutputStyle.REMOVE) {
0637: outputDocument.remove(getElement());
0638: } else if (outputStyle == FormControlOutputStyle.DISPLAY_VALUE) {
0639: outputDocument.replace(getElement(),
0640: getDisplayValueHTML(getValue(), true));
0641: } else {
0642: replaceAttributesInOutputDocumentIfModified(outputDocument);
0643: if (value != UNCHANGED)
0644: outputDocument.replace(getElement().getContent(),
0645: CharacterReference.encode(value));
0646: }
0647: }
0648:
0649: private CharSequence getValue() {
0650: return (value == UNCHANGED) ? CharacterReference
0651: .decode(getElement().getContent()) : value;
0652: }
0653: }
0654:
0655: static final class RadioCheckboxFormControl extends FormControl {
0656: // RADIO or CHECKBOX
0657: public RadioCheckboxFormControl(final Element element,
0658: final FormControlType formControlType) {
0659: super (element, formControlType, true);
0660: if (elementContainer.predefinedValue == null) {
0661: elementContainer.predefinedValue = CHECKBOX_NULL_DEFAULT_VALUE;
0662: if (element.source.logger.isInfoEnabled())
0663: element.source.logger
0664: .info(element.source
0665: .getRowColumnVector(element.begin)
0666: .appendTo(new StringBuffer(200))
0667: .append(
0668: ": compulsory \"value\" attribute of ")
0669: .append(formControlType)
0670: .append(" control \"")
0671: .append(name)
0672: .append(
0673: "\" is missing, assuming the value \"")
0674: .append(CHECKBOX_NULL_DEFAULT_VALUE)
0675: .append('"').toString());
0676: }
0677: }
0678:
0679: public boolean setValue(final CharSequence value) {
0680: return elementContainer.setSelected(value,
0681: Attribute.CHECKED, false);
0682: }
0683:
0684: public boolean addValue(final CharSequence value) {
0685: return elementContainer.setSelected(value,
0686: Attribute.CHECKED,
0687: formControlType == FormControlType.CHECKBOX);
0688: }
0689:
0690: void addValuesTo(final Collection collection) {
0691: if (isChecked())
0692: addValueTo(collection, getPredefinedValue());
0693: }
0694:
0695: public boolean isChecked() {
0696: return elementContainer
0697: .getBooleanAttribute(Attribute.CHECKED);
0698: }
0699:
0700: void addToFormFields(final FormFields formFields) {
0701: formFields.add(this );
0702: }
0703:
0704: void replaceInOutputDocument(final OutputDocument outputDocument) {
0705: if (outputStyle == FormControlOutputStyle.REMOVE) {
0706: outputDocument.remove(getElement());
0707: } else {
0708: if (outputStyle == FormControlOutputStyle.DISPLAY_VALUE) {
0709: final String html = isChecked() ? FormControlOutputStyle.ConfigDisplayValue.CheckedHTML
0710: : FormControlOutputStyle.ConfigDisplayValue.UncheckedHTML;
0711: if (html != null) {
0712: outputDocument.replace(getElement(), html);
0713: return;
0714: }
0715: setDisabled(true);
0716: }
0717: replaceAttributesInOutputDocumentIfModified(outputDocument);
0718: }
0719: }
0720: }
0721:
0722: static class SubmitFormControl extends FormControl {
0723: // BUTTON, SUBMIT or (in subclass) IMAGE
0724: public SubmitFormControl(final Element element,
0725: final FormControlType formControlType) {
0726: super (element, formControlType, true);
0727: }
0728:
0729: public boolean setValue(final CharSequence value) {
0730: return false;
0731: }
0732:
0733: void addValuesTo(final Collection collection) {
0734: }
0735:
0736: void addToFormFields(final FormFields formFields) {
0737: if (getPredefinedValue() != null)
0738: formFields.add(this );
0739: }
0740:
0741: void replaceInOutputDocument(final OutputDocument outputDocument) {
0742: if (outputStyle == FormControlOutputStyle.REMOVE) {
0743: outputDocument.remove(getElement());
0744: } else {
0745: if (outputStyle == FormControlOutputStyle.DISPLAY_VALUE)
0746: setDisabled(true);
0747: replaceAttributesInOutputDocumentIfModified(outputDocument);
0748: }
0749: }
0750: }
0751:
0752: static final class ImageSubmitFormControl extends SubmitFormControl {
0753: // IMAGE
0754: public ImageSubmitFormControl(final Element element) {
0755: super (element, FormControlType.IMAGE);
0756: }
0757:
0758: void addToFormFields(final FormFields formFields) {
0759: super .addToFormFields(formFields);
0760: formFields.addName(this , name + ".x");
0761: formFields.addName(this , name + ".y");
0762: }
0763: }
0764:
0765: static final class SelectFormControl extends FormControl {
0766: // SELECT_MULTIPLE or SELECT_SINGLE
0767: public ElementContainer[] optionElementContainers;
0768:
0769: public SelectFormControl(final Element element) {
0770: super (
0771: element,
0772: element.getAttributes().get(Attribute.MULTIPLE) != null ? FormControlType.SELECT_MULTIPLE
0773: : FormControlType.SELECT_SINGLE, false);
0774: final List optionElements = element
0775: .findAllElements(Tag.OPTION);
0776: optionElementContainers = new ElementContainer[optionElements
0777: .size()];
0778: int x = 0;
0779: for (final Iterator i = optionElements.iterator(); i
0780: .hasNext();) {
0781: final ElementContainer optionElementContainer = new ElementContainer(
0782: (Element) i.next(), true);
0783: if (optionElementContainer.predefinedValue == null)
0784: // use the content of the element if it has no value attribute
0785: optionElementContainer.predefinedValue = CharacterReference
0786: .decodeCollapseWhiteSpace(optionElementContainer.element
0787: .getContent());
0788: optionElementContainers[x++] = optionElementContainer;
0789: }
0790: }
0791:
0792: public String getPredefinedValue() {
0793: throw new UnsupportedOperationException(
0794: "Use getPredefinedValues() method instead on SELECT controls");
0795: }
0796:
0797: public Collection getPredefinedValues() {
0798: final ArrayList arrayList = new ArrayList(
0799: optionElementContainers.length);
0800: for (int i = 0; i < optionElementContainers.length; i++)
0801: arrayList
0802: .add(optionElementContainers[i].predefinedValue);
0803: return arrayList;
0804: }
0805:
0806: public Iterator getOptionElementIterator() {
0807: return new OptionElementIterator();
0808: }
0809:
0810: public boolean setValue(final CharSequence value) {
0811: return addValue(value, false);
0812: }
0813:
0814: public boolean addValue(final CharSequence value) {
0815: return addValue(value,
0816: formControlType == FormControlType.SELECT_MULTIPLE);
0817: }
0818:
0819: private boolean addValue(final CharSequence value,
0820: final boolean allowMultipleValues) {
0821: boolean valueFound = false;
0822: for (int i = 0; i < optionElementContainers.length; i++) {
0823: if (optionElementContainers[i].setSelected(value,
0824: Attribute.SELECTED, allowMultipleValues))
0825: valueFound = true;
0826: }
0827: return valueFound;
0828: }
0829:
0830: void addValuesTo(final Collection collection) {
0831: for (int i = 0; i < optionElementContainers.length; i++) {
0832: if (optionElementContainers[i]
0833: .getBooleanAttribute(Attribute.SELECTED))
0834: addValueTo(collection,
0835: optionElementContainers[i].predefinedValue);
0836: }
0837: }
0838:
0839: void addToFormFields(final FormFields formFields) {
0840: for (int i = 0; i < optionElementContainers.length; i++)
0841: formFields.add(this ,
0842: optionElementContainers[i].predefinedValue);
0843: }
0844:
0845: void replaceInOutputDocument(final OutputDocument outputDocument) {
0846: if (outputStyle == FormControlOutputStyle.REMOVE) {
0847: outputDocument.remove(getElement());
0848: } else if (outputStyle == FormControlOutputStyle.DISPLAY_VALUE) {
0849: final StringBuffer sb = new StringBuffer(100);
0850: for (int i = 0; i < optionElementContainers.length; i++) {
0851: if (optionElementContainers[i]
0852: .getBooleanAttribute(Attribute.SELECTED)) {
0853: appendCollapseWhiteSpace(sb,
0854: optionElementContainers[i].element
0855: .getContent());
0856: sb
0857: .append(FormControlOutputStyle.ConfigDisplayValue.MultipleValueSeparator);
0858: }
0859: }
0860: if (sb.length() > 0)
0861: sb
0862: .setLength(sb.length()
0863: - FormControlOutputStyle.ConfigDisplayValue.MultipleValueSeparator
0864: .length()); // remove last separator
0865: outputDocument.replace(getElement(),
0866: getDisplayValueHTML(sb, false));
0867: } else {
0868: replaceAttributesInOutputDocumentIfModified(outputDocument);
0869: for (int i = 0; i < optionElementContainers.length; i++) {
0870: optionElementContainers[i]
0871: .replaceAttributesInOutputDocumentIfModified(outputDocument);
0872: }
0873: }
0874: }
0875:
0876: private final class OptionElementIterator implements Iterator {
0877: private int i = 0;
0878:
0879: public boolean hasNext() {
0880: return i < optionElementContainers.length;
0881: }
0882:
0883: public Object next() {
0884: if (!hasNext())
0885: throw new NoSuchElementException();
0886: return optionElementContainers[i++].element;
0887: }
0888:
0889: public void remove() {
0890: throw new UnsupportedOperationException();
0891: }
0892: }
0893: }
0894:
0895: final String getDisplayValueHTML(final CharSequence text,
0896: final boolean whiteSpaceFormatting) {
0897: final StringBuffer sb = new StringBuffer((text == null ? 0
0898: : text.length() * 2) + 50);
0899: sb.append('<').append(
0900: FormControlOutputStyle.ConfigDisplayValue.ElementName);
0901: for (final Iterator i = FormControlOutputStyle.ConfigDisplayValue.AttributeNames
0902: .iterator(); i.hasNext();) {
0903: final String attributeName = i.next().toString();
0904: final CharSequence attributeValue = elementContainer
0905: .getAttributeValue(attributeName);
0906: if (attributeValue == null)
0907: continue;
0908: Attribute.appendHTML(sb, attributeName, attributeValue);
0909: }
0910: sb.append('>');
0911: if (text == null || text.length() == 0)
0912: sb
0913: .append(FormControlOutputStyle.ConfigDisplayValue.EmptyHTML);
0914: else
0915: CharacterReference.appendEncode(sb, text,
0916: whiteSpaceFormatting);
0917: sb.append(EndTagType.START_DELIMITER_PREFIX).append(
0918: FormControlOutputStyle.ConfigDisplayValue.ElementName)
0919: .append('>');
0920: return sb.toString();
0921: }
0922:
0923: final void replaceAttributesInOutputDocumentIfModified(
0924: final OutputDocument outputDocument) {
0925: elementContainer
0926: .replaceAttributesInOutputDocumentIfModified(outputDocument);
0927: }
0928:
0929: static List findAll(final Segment segment) {
0930: final ArrayList list = new ArrayList();
0931: findAll(segment, list, Tag.INPUT);
0932: findAll(segment, list, Tag.TEXTAREA);
0933: findAll(segment, list, Tag.SELECT);
0934: findAll(segment, list, Tag.BUTTON);
0935: Collections.sort(list, COMPARATOR);
0936: return list;
0937: }
0938:
0939: private static void findAll(final Segment segment,
0940: final ArrayList list, final String tagName) {
0941: for (final Iterator i = segment.findAllElements(tagName)
0942: .iterator(); i.hasNext();) {
0943: final FormControl formControl = ((Element) i.next())
0944: .getFormControl();
0945: if (formControl != null)
0946: list.add(formControl);
0947: }
0948: }
0949:
0950: private static CharSequence getString(final char ch,
0951: final int length) {
0952: if (length == 0)
0953: return "";
0954: final StringBuffer sb = new StringBuffer(length);
0955: for (int i = 0; i < length; i++)
0956: sb.append(ch);
0957: return sb.toString();
0958: }
0959:
0960: private void verifyName() {
0961: if (formControlType.isSubmit())
0962: return;
0963: String missingOrBlank;
0964: if (name == null) {
0965: missingOrBlank = "missing";
0966: } else {
0967: if (name.length() != 0)
0968: return;
0969: missingOrBlank = "blank";
0970: }
0971: final Source source = getElement().source;
0972: if (source.logger.isInfoEnabled())
0973: source.logger.info(getElement().source.getRowColumnVector(
0974: getElement().begin).appendTo(new StringBuffer(200))
0975: .append(": compulsory \"name\" attribute of ")
0976: .append(formControlType).append(" control is ")
0977: .append(missingOrBlank).toString());
0978: }
0979:
0980: private static final void addValueTo(final Collection collection,
0981: final CharSequence value) {
0982: collection.add(value != null ? value : "");
0983: }
0984:
0985: private static final class PositionComparator implements Comparator {
0986: public int compare(final Object o1, final Object o2) {
0987: int formControl1Begin = ((FormControl) o1).getElement()
0988: .getBegin();
0989: int formControl2Begin = ((FormControl) o2).getElement()
0990: .getBegin();
0991: if (formControl1Begin < formControl2Begin)
0992: return -1;
0993: if (formControl1Begin > formControl2Begin)
0994: return 1;
0995: return 0;
0996: }
0997: }
0998:
0999: //////////////////////////////////////////////////////////////////////////////////////
1000:
1001: static final class ElementContainer {
1002: // Contains the information common to both a FormControl and to each OPTION element
1003: // within a SELECT FormControl
1004: public final Element element;
1005: public Map attributesMap = null;
1006: public String predefinedValue; // never null for option, checkbox or radio elements
1007:
1008: public ElementContainer(final Element element,
1009: final boolean loadPredefinedValue) {
1010: this .element = element;
1011: predefinedValue = loadPredefinedValue ? element
1012: .getAttributes().getValue(Attribute.VALUE) : null;
1013: }
1014:
1015: public Map getAttributesMap() {
1016: if (attributesMap == null)
1017: attributesMap = element.getAttributes().getMap(true);
1018: return attributesMap;
1019: }
1020:
1021: public boolean setSelected(final CharSequence value,
1022: final String selectedOrChecked,
1023: final boolean allowMultipleValues) {
1024: if (value != null
1025: && predefinedValue.equals(value.toString())) {
1026: setBooleanAttribute(selectedOrChecked, true);
1027: return true;
1028: }
1029: if (!allowMultipleValues)
1030: setBooleanAttribute(selectedOrChecked, false);
1031: return false;
1032: }
1033:
1034: public CharSequence getAttributeValue(final String attributeName) {
1035: if (attributesMap != null)
1036: return (CharSequence) attributesMap.get(attributeName);
1037: else
1038: return element.getAttributes().getValue(attributeName);
1039: }
1040:
1041: public void setAttributeValue(final String attributeName,
1042: final CharSequence value) {
1043: // null value indicates attribute should be removed.
1044: if (value == null) {
1045: setBooleanAttribute(attributeName, false);
1046: return;
1047: }
1048: if (attributesMap != null) {
1049: attributesMap.put(attributeName, value);
1050: return;
1051: }
1052: final String valueString = value.toString();
1053: final CharSequence existingValue = getAttributeValue(attributeName);
1054: if (existingValue != null
1055: && existingValue.toString().equals(valueString))
1056: return;
1057: getAttributesMap().put(attributeName, valueString);
1058: }
1059:
1060: public boolean getBooleanAttribute(final String attributeName) {
1061: if (attributesMap != null)
1062: return attributesMap.containsKey(attributeName);
1063: else
1064: return element.getAttributes().get(attributeName) != null;
1065: }
1066:
1067: public void setBooleanAttribute(final String attributeName,
1068: final boolean value) {
1069: final boolean oldValue = getBooleanAttribute(attributeName);
1070: if (value == oldValue)
1071: return;
1072: if (value)
1073: getAttributesMap().put(attributeName, attributeName); // xhtml compatible attribute
1074: else
1075: getAttributesMap().remove(attributeName);
1076: }
1077:
1078: public void replaceAttributesInOutputDocumentIfModified(
1079: final OutputDocument outputDocument) {
1080: if (attributesMap != null)
1081: outputDocument.replace(element.getAttributes(),
1082: attributesMap);
1083: }
1084: }
1085: }
|