1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package org.htmlunit.html;
16
17 import static org.htmlunit.util.StringUtils.toRootLowerCase;
18
19 import java.io.IOException;
20 import java.util.Map;
21
22 import org.htmlunit.Page;
23 import org.htmlunit.ScriptResult;
24 import org.htmlunit.SgmlPage;
25 import org.htmlunit.javascript.host.event.Event;
26 import org.htmlunit.javascript.host.event.KeyboardEvent;
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43 public class HtmlRadioButtonInput extends HtmlInput implements LabelableElement {
44
45
46
47
48 private static final String DEFAULT_VALUE = "on";
49
50 private boolean defaultCheckedState_;
51 private boolean checkedState_;
52
53
54
55
56
57
58
59
60
61
62
63 HtmlRadioButtonInput(final String qualifiedName, final SgmlPage page,
64 final Map<String, DomAttr> attributes) {
65 super(qualifiedName, page, attributes);
66
67 if (getAttributeDirect(VALUE_ATTRIBUTE) == ATTRIBUTE_NOT_DEFINED) {
68 setRawValue(DEFAULT_VALUE);
69 }
70
71 defaultCheckedState_ = hasAttribute(ATTRIBUTE_CHECKED);
72 checkedState_ = defaultCheckedState_;
73 }
74
75
76
77
78
79 @Override
80 public boolean isChecked() {
81 return checkedState_;
82 }
83
84
85
86
87
88 @Override
89 public void reset() {
90 setChecked(defaultCheckedState_);
91 }
92
93 void setCheckedInternal(final boolean isChecked) {
94 checkedState_ = isChecked;
95 }
96
97
98
99
100
101
102
103
104 @Override
105 public Page setChecked(final boolean isChecked) {
106 Page page = getPage();
107
108 final boolean changed = isChecked() != isChecked;
109 checkedState_ = isChecked;
110 if (isChecked) {
111 updateRadioButtonSelection(page);
112 }
113
114 if (changed) {
115 final ScriptResult scriptResult = fireEvent(Event.TYPE_CHANGE);
116 if (scriptResult != null && page != null) {
117 page = page.getEnclosingWindow().getWebClient().getCurrentWindow().getEnclosedPage();
118 }
119 }
120 return page;
121 }
122
123
124
125
126 @Override
127 protected void doType(final int keyCode, final boolean lastType) throws IOException {
128 if (keyCode == KeyboardEvent.DOM_VK_SPACE) {
129 click();
130 return;
131 }
132
133 super.doType(keyCode, lastType);
134 }
135
136
137
138
139
140
141
142
143 @Override
144 protected boolean doClickStateUpdate(final boolean shiftKey, final boolean ctrlKey) throws IOException {
145 final boolean changed = !isChecked();
146 updateRadioButtonSelection(getPage());
147
148 super.doClickStateUpdate(shiftKey, ctrlKey);
149 return changed;
150 }
151
152
153
154
155 private void updateRadioButtonSelection(final Page page) {
156 final HtmlForm form = getEnclosingForm();
157 if (form != null) {
158 form.setCheckedRadioButton(this);
159 }
160 else if (page != null && page.isHtmlPage()) {
161 setCheckedForPage((HtmlPage) page);
162 }
163 }
164
165
166
167
168 private void setCheckedForPage(final HtmlPage htmlPage) {
169 final String name = getNameAttribute();
170 for (final HtmlElement htmlElement : htmlPage.getHtmlElementDescendants()) {
171 if (htmlElement instanceof HtmlRadioButtonInput radioInput) {
172 if (name.equals(radioInput.getNameAttribute())
173 && radioInput.getEnclosingForm() == null) {
174 radioInput.setCheckedInternal(radioInput == this);
175 }
176 }
177 }
178 }
179
180
181
182
183 @Override
184 protected void doClickFireChangeEvent() {
185 executeOnChangeHandlerIfAppropriate(this);
186 }
187
188
189
190
191 @Override
192 protected void preventDefault() {
193 checkedState_ = !checkedState_;
194 }
195
196
197
198
199
200
201 @Override
202 public void setDefaultValue(final String defaultValue) {
203 super.setDefaultValue(defaultValue);
204 setValue(defaultValue);
205 }
206
207
208
209
210
211 @Override
212 public void setValue(final String newValue) {
213 super.setValue(newValue);
214 super.setDefaultValue(newValue);
215 }
216
217
218
219
220
221 @Override
222 public void setDefaultChecked(final boolean defaultChecked) {
223 defaultCheckedState_ = defaultChecked;
224 setChecked(isDefaultChecked());
225 }
226
227
228
229
230
231 @Override
232 public boolean isDefaultChecked() {
233 return defaultCheckedState_;
234 }
235
236
237
238
239 @Override
240 protected boolean isStateUpdateFirst() {
241 return true;
242 }
243
244
245
246
247 @Override
248 protected void onAddedToPage() {
249 super.onAddedToPage();
250 setChecked(isChecked());
251 }
252
253 @Override
254 protected Object getInternalValue() {
255 return isChecked();
256 }
257
258 @Override
259 void handleFocusLostValueChanged() {
260
261 }
262
263
264
265
266 @Override
267 protected void setAttributeNS(final String namespaceURI, final String qualifiedName, final String attributeValue,
268 final boolean notifyAttributeChangeListeners, final boolean notifyMutationObservers) {
269 final String qualifiedNameLC = toRootLowerCase(qualifiedName);
270
271 if (VALUE_ATTRIBUTE.equals(qualifiedNameLC)) {
272 super.setAttributeNS(namespaceURI, qualifiedNameLC, attributeValue, notifyAttributeChangeListeners,
273 notifyMutationObservers);
274 setRawValue(attributeValue);
275 return;
276 }
277
278 if (ATTRIBUTE_CHECKED.equals(qualifiedNameLC)) {
279 checkedState_ = true;
280 }
281 super.setAttributeNS(namespaceURI, qualifiedNameLC, attributeValue, notifyAttributeChangeListeners,
282 notifyMutationObservers);
283 }
284
285
286
287
288 @Override
289 protected boolean propagateClickStateUpdateToParent() {
290 return false;
291 }
292
293 @Override
294 public boolean isValueMissingValidityState() {
295 if (ATTRIBUTE_NOT_DEFINED == getAttributeDirect(ATTRIBUTE_REQUIRED)) {
296 return false;
297 }
298 final String name = getNameAttribute();
299 if (ATTRIBUTE_NOT_DEFINED == name) {
300 return false;
301 }
302
303 for (final HtmlElement htmlElement : getPage().getHtmlElementDescendants()) {
304 if (htmlElement instanceof HtmlRadioButtonInput radioInput) {
305 if (name.equals(radioInput.getNameAttribute()) && radioInput.isChecked()) {
306 return false;
307 }
308 }
309 }
310 return true;
311 }
312 }