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.BrowserVersionFeatures.JS_INPUT_NUMBER_ACCEPT_ALL;
18 import static org.htmlunit.BrowserVersionFeatures.JS_INPUT_NUMBER_DOT_AT_END_IS_DOUBLE;
19
20 import java.math.BigDecimal;
21 import java.util.Map;
22
23 import org.htmlunit.SgmlPage;
24 import org.htmlunit.html.parser.HtmlNumberParser;
25 import org.htmlunit.util.ArrayUtils;
26 import org.htmlunit.util.StringUtils;
27
28
29
30
31
32
33
34
35
36
37
38 public class HtmlNumberInput extends HtmlSelectableTextInput implements LabelableElement {
39
40 private static final char[] VALID_INT_CHARS = "0123456789-".toCharArray();
41 private static final char[] VALID_CHARS = "0123456789-+.eE".toCharArray();
42
43
44
45
46
47
48
49
50 HtmlNumberInput(final String qualifiedName, final SgmlPage page,
51 final Map<String, DomAttr> attributes) {
52 super(qualifiedName, page, attributes);
53
54 final String value = getValueAttribute();
55 if (!value.isEmpty()
56
57 && !HtmlNumberParser.isValid(value, false, hasFeature(JS_INPUT_NUMBER_DOT_AT_END_IS_DOUBLE))) {
58
59 setRawValue("");
60 }
61 }
62
63
64
65
66 @Override
67 protected boolean isSubmittableByEnter() {
68 return true;
69 }
70
71
72
73
74 @Override
75 public void setDefaultChecked(final boolean defaultChecked) {
76
77 }
78
79
80
81
82 @Override
83 protected void doType(final char c, final boolean lastType) {
84 if (!hasFeature(JS_INPUT_NUMBER_ACCEPT_ALL) && !ArrayUtils.contains(VALID_CHARS, c)) {
85 return;
86 }
87 super.doType(c, lastType);
88 }
89
90
91
92
93 @Override
94 public String getValue() {
95 String raw = getRawValue();
96
97 if (StringUtils.isBlank(raw)) {
98 return "";
99 }
100
101 if (StringUtils.equalsChar('-', raw) || StringUtils.equalsChar('+', raw)) {
102 return raw;
103 }
104
105
106
107
108 if (raw.endsWith(".")) {
109 raw = raw.substring(0, raw.length() - 1);
110 }
111
112 if (!HtmlNumberParser.isValid(raw, !hasFeature(JS_INPUT_NUMBER_DOT_AT_END_IS_DOUBLE), false)) {
113 return "";
114 }
115
116 if (raw.length() > 1 && raw.charAt(0) == '+') {
117 raw = raw.substring(1, raw.length());
118 }
119
120 return raw;
121 }
122
123
124
125
126 @Override
127 public void setValue(final String newValue) {
128 if (StringUtils.isBlank(newValue) || !HtmlNumberParser.isValid(newValue, false, false)) {
129 super.setValue("");
130 return;
131 }
132
133 super.setValue(newValue);
134 }
135
136
137
138
139
140
141
142 private static BigDecimal parseAttributeAsBigDecimal(final String attributeValue) {
143 if (attributeValue.isEmpty()) {
144 return null;
145 }
146 try {
147 return new BigDecimal(attributeValue);
148 }
149 catch (final NumberFormatException ignored) {
150 return null;
151 }
152 }
153
154
155
156
157
158
159 @Override
160 public boolean hasRangeOverflowValidityState() {
161 if (super.hasRangeOverflowValidityState()) {
162 return true;
163 }
164
165 final String rawValue = getRawValue();
166 if (StringUtils.isBlank(rawValue)) {
167 return false;
168 }
169
170 final BigDecimal value = HtmlNumberParser.parse(rawValue, true, true);
171 if (value == null) {
172 return false;
173 }
174
175 final BigDecimal max = parseAttributeAsBigDecimal(getMax());
176 return max != null && value.compareTo(max) > 0;
177 }
178
179
180
181
182
183
184 @Override
185 public boolean hasRangeUnderflowValidityState() {
186 if (super.hasRangeUnderflowValidityState()) {
187 return true;
188 }
189
190 final String rawValue = getRawValue();
191 if (StringUtils.isBlank(rawValue)) {
192 return false;
193 }
194
195 final BigDecimal value = HtmlNumberParser.parse(rawValue, true, true);
196 if (value == null) {
197 return false;
198 }
199
200 final BigDecimal min = parseAttributeAsBigDecimal(getMin());
201 return min != null && value.compareTo(min) < 0;
202 }
203
204
205
206
207
208
209 @Override
210 public boolean isStepMismatchValidityState() {
211 if (super.isStepMismatchValidityState()) {
212 return true;
213 }
214
215 final String rawValue = getRawValue();
216 if (StringUtils.isBlank(rawValue)) {
217 return false;
218 }
219
220 final BigDecimal value = HtmlNumberParser.parse(rawValue, true, true);
221 if (value == null) {
222 return false;
223 }
224
225 BigDecimal step = parseAttributeAsBigDecimal(getStep());
226 if (step == null) {
227 step = BigDecimal.ONE;
228 }
229
230 BigDecimal min = parseAttributeAsBigDecimal(getMin());
231 if (min == null) {
232 min = BigDecimal.ZERO;
233 }
234
235 return value.subtract(min).abs().remainder(step).doubleValue() > 0.0;
236 }
237
238
239
240
241
242
243
244
245 @Override
246 public boolean hasBadInputValidityState() {
247 final String rawValue = getRawValue();
248 if (StringUtils.isBlank(rawValue)) {
249 return false;
250 }
251
252 return !HtmlNumberParser.isValid(rawValue, !hasFeature(JS_INPUT_NUMBER_DOT_AT_END_IS_DOUBLE), true);
253 }
254 }