Skip to content

Commit 6df8766

Browse files
committed
improved impl
1 parent d64ab5d commit 6df8766

1 file changed

Lines changed: 129 additions & 155 deletions

File tree

src/main/java/org/htmlunit/html/HtmlNumberInput.java

Lines changed: 129 additions & 155 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,8 @@ public class HtmlNumberInput extends HtmlSelectableTextInput implements Labelabl
5454
super(qualifiedName, page, attributes);
5555

5656
final String value = getValueAttribute();
57-
if (!value.isEmpty()) {
58-
if (!StringUtils.containsOnly(value, VALID_CHARS)) {
59-
setRawValue("");
60-
}
57+
if (!value.isEmpty() && !StringUtils.containsOnly(value, VALID_CHARS)) {
58+
setRawValue("");
6159
}
6260
}
6361

@@ -82,12 +80,9 @@ public void setDefaultChecked(final boolean defaultChecked) {
8280
*/
8381
@Override
8482
protected void doType(final char c, final boolean lastType) {
85-
if (!hasFeature(JS_INPUT_NUMBER_ACCEPT_ALL)) {
86-
if (!ArrayUtils.contains(VALID_CHARS, c)) {
87-
return;
88-
}
83+
if (!hasFeature(JS_INPUT_NUMBER_ACCEPT_ALL) && !ArrayUtils.contains(VALID_CHARS, c)) {
84+
return;
8985
}
90-
9186
super.doType(c, lastType);
9287
}
9388

@@ -98,12 +93,12 @@ protected void doType(final char c, final boolean lastType) {
9893
public String getValue() {
9994
final String raw = getRawValue();
10095

101-
if (org.htmlunit.util.StringUtils.isBlank(raw)) {
96+
if (StringUtils.isBlank(raw)) {
10297
return "";
10398
}
10499

105-
if (org.htmlunit.util.StringUtils.equalsChar('-', raw)
106-
|| org.htmlunit.util.StringUtils.equalsChar('+', raw)) {
100+
if (StringUtils.equalsChar('-', raw)
101+
|| StringUtils.equalsChar('+', raw)) {
107102
return raw;
108103
}
109104

@@ -126,197 +121,176 @@ public String getValue() {
126121
}
127122

128123
/**
129-
* {@inheritDoc}
124+
* Attempts to parse the current raw value as a well-formed number per
125+
* this input's syntax rules (sign-only values, missing-step non-integer
126+
* values, and anything BigDecimal itself rejects all count as
127+
* unparseable). Centralizes what used to be duplicated, near-identically,
128+
* across hasRangeOverflowValidityState(), hasRangeUnderflowValidityState(),
129+
* and isStepMismatchValidityState().
130+
*
131+
* @return the parsed value, or {@code null} if the raw value is blank
132+
* OR is non-blank but not a well-formed number (the latter case is
133+
* what {@link #hasBadInputValidityState()} reports, NOT any of the
134+
* range/step methods -- callers here must not treat "unparseable"
135+
* as a range/step violation)
130136
*/
131-
@Override
132-
public boolean hasRangeOverflowValidityState() {
133-
if (super.hasRangeOverflowValidityState()) {
134-
return true;
135-
}
136-
137+
private BigDecimal parseNumericValue() {
137138
String rawValue = getRawValue();
138-
if (org.htmlunit.util.StringUtils.isBlank(rawValue)) {
139-
return false;
139+
if (StringUtils.isBlank(rawValue)) {
140+
return null;
140141
}
141142

142143
if (!hasFeature(JS_INPUT_NUMBER_ACCEPT_ALL)) {
143144
rawValue = rawValue.replaceAll("\\s", "");
144145
}
145-
if (!rawValue.isEmpty()) {
146-
if (org.htmlunit.util.StringUtils.equalsChar('-', rawValue)
147-
|| org.htmlunit.util.StringUtils.equalsChar('+', rawValue)) {
148-
return true;
149-
}
146+
if (rawValue.isEmpty()) {
147+
return null;
148+
}
150149

151-
// if we have no step, the value has to be an integer
152-
if (getStep().isEmpty()) {
153-
String val = rawValue;
154-
final int lastPos = val.length() - 1;
155-
if (lastPos >= 0 && val.charAt(lastPos) == '.') {
156-
if (hasFeature(JS_INPUT_NUMBER_DOT_AT_END_IS_DOUBLE)) {
157-
return true;
158-
}
159-
val = val.substring(0, lastPos);
160-
}
161-
if (!StringUtils.containsOnly(val, VALID_INT_CHARS)) {
162-
return true;
163-
}
164-
}
150+
if (StringUtils.equalsChar('-', rawValue) || StringUtils.equalsChar('+', rawValue)) {
151+
return null;
152+
}
165153

166-
final BigDecimal value;
167-
try {
168-
value = new BigDecimal(rawValue);
154+
// if we have no step, the value has to be an integer
155+
if (getStep().isEmpty()) {
156+
String val = rawValue;
157+
final int lastPos = val.length() - 1;
158+
if (lastPos >= 0 && val.charAt(lastPos) == '.') {
159+
if (hasFeature(JS_INPUT_NUMBER_DOT_AT_END_IS_DOUBLE)) {
160+
return null;
161+
}
162+
val = val.substring(0, lastPos);
169163
}
170-
catch (final NumberFormatException e) {
171-
return true;
164+
if (!StringUtils.containsOnly(val, VALID_INT_CHARS)) {
165+
return null;
172166
}
167+
}
173168

174-
if (!getMax().isEmpty()) {
175-
try {
176-
final BigDecimal max = new BigDecimal(getMax());
177-
if (value.compareTo(max) > 0) {
178-
return true;
179-
}
180-
}
181-
catch (final NumberFormatException ignored) {
182-
// ignore
183-
}
184-
}
169+
try {
170+
return new BigDecimal(rawValue);
171+
}
172+
catch (final NumberFormatException e) {
173+
return null;
174+
}
175+
}
176+
177+
/**
178+
* Parses an attribute string (min/max/step) as a BigDecimal, or
179+
* {@code null} if absent or malformed -- consolidates the several
180+
* separately-inlined try/catch blocks that used to parse min/max/step
181+
* individually in each constraint method.
182+
*/
183+
private static BigDecimal parseAttributeAsBigDecimal(final String attributeValue) {
184+
if (attributeValue.isEmpty()) {
185+
return null;
186+
}
187+
try {
188+
return new BigDecimal(attributeValue);
189+
}
190+
catch (final NumberFormatException ignored) {
191+
return null;
185192
}
186-
return false;
187193
}
188194

189195
/**
190196
* {@inheritDoc}
197+
* A raw value that isn't even a well-formed number is
198+
* {@link #hasBadInputValidityState()}'s concern, not a range violation
199+
* -- distinguished here via {@link #parseNumericValue()} returning
200+
* {@code null} only for genuinely malformed (non-blank) input.
191201
*/
192202
@Override
193-
public boolean hasRangeUnderflowValidityState() {
194-
if (super.hasRangeUnderflowValidityState()) {
203+
public boolean hasRangeOverflowValidityState() {
204+
if (super.hasRangeOverflowValidityState()) {
195205
return true;
196206
}
197207

198-
String rawValue = getRawValue();
199-
if (org.htmlunit.util.StringUtils.isBlank(rawValue)) {
208+
final String rawValue = getRawValue();
209+
if (StringUtils.isBlank(rawValue)) {
200210
return false;
201211
}
202212

203-
if (!hasFeature(JS_INPUT_NUMBER_ACCEPT_ALL)) {
204-
rawValue = rawValue.replaceAll("\\s", "");
213+
final BigDecimal value = parseNumericValue();
214+
if (value == null) {
215+
return false;
205216
}
206-
if (!rawValue.isEmpty()) {
207-
if (org.htmlunit.util.StringUtils.equalsChar('-', rawValue)
208-
|| org.htmlunit.util.StringUtils.equalsChar('+', rawValue)) {
209-
return true;
210-
}
211217

212-
// if we have no step, the value has to be an integer
213-
if (getStep().isEmpty()) {
214-
String val = rawValue;
215-
final int lastPos = val.length() - 1;
216-
if (lastPos >= 0 && val.charAt(lastPos) == '.') {
217-
if (hasFeature(JS_INPUT_NUMBER_DOT_AT_END_IS_DOUBLE)) {
218-
return true;
219-
}
220-
val = val.substring(0, lastPos);
221-
}
222-
if (!StringUtils.containsOnly(val, VALID_INT_CHARS)) {
223-
return true;
224-
}
225-
}
218+
final BigDecimal max = parseAttributeAsBigDecimal(getMax());
219+
return max != null && value.compareTo(max) > 0;
220+
}
226221

227-
final BigDecimal value;
228-
try {
229-
value = new BigDecimal(rawValue);
230-
}
231-
catch (final NumberFormatException e) {
232-
return true;
233-
}
222+
/**
223+
* {@inheritDoc}
224+
* See {@link #hasRangeOverflowValidityState()} for the malformed-value
225+
* / badInput distinction.
226+
*/
227+
@Override
228+
public boolean hasRangeUnderflowValidityState() {
229+
if (super.hasRangeUnderflowValidityState()) {
230+
return true;
231+
}
234232

235-
if (!getMin().isEmpty()) {
236-
try {
237-
final BigDecimal min = new BigDecimal(getMin());
238-
if (value.compareTo(min) < 0) {
239-
return true;
240-
}
241-
}
242-
catch (final NumberFormatException ignored) {
243-
// ignore
244-
}
245-
}
233+
final String rawValue = getRawValue();
234+
if (StringUtils.isBlank(rawValue)) {
235+
return false;
246236
}
247-
return false;
237+
238+
final BigDecimal value = parseNumericValue();
239+
if (value == null) {
240+
return false;
241+
}
242+
243+
final BigDecimal min = parseAttributeAsBigDecimal(getMin());
244+
return min != null && value.compareTo(min) < 0;
248245
}
249246

250247
/**
251248
* {@inheritDoc}
249+
* See {@link #hasRangeOverflowValidityState()} for the malformed-value
250+
* / badInput distinction.
252251
*/
253252
@Override
254253
public boolean isStepMismatchValidityState() {
255254
if (super.isStepMismatchValidityState()) {
256255
return true;
257256
}
258257

259-
String rawValue = getRawValue();
260-
if (org.htmlunit.util.StringUtils.isBlank(rawValue)) {
258+
final String rawValue = getRawValue();
259+
if (StringUtils.isBlank(rawValue)) {
261260
return false;
262261
}
263262

264-
if (!hasFeature(JS_INPUT_NUMBER_ACCEPT_ALL)) {
265-
rawValue = rawValue.replaceAll("\\s", "");
263+
final BigDecimal value = parseNumericValue();
264+
if (value == null) {
265+
return false;
266266
}
267-
if (!rawValue.isEmpty()) {
268-
if (org.htmlunit.util.StringUtils.equalsChar('-', rawValue)
269-
|| org.htmlunit.util.StringUtils.equalsChar('+', rawValue)) {
270-
return true;
271-
}
272267

273-
// if we have no step, the value has to be an integer
274-
if (getStep().isEmpty()) {
275-
String val = rawValue;
276-
final int lastPos = val.length() - 1;
277-
if (lastPos >= 0 && val.charAt(lastPos) == '.') {
278-
if (hasFeature(JS_INPUT_NUMBER_DOT_AT_END_IS_DOUBLE)) {
279-
return true;
280-
}
281-
val = val.substring(0, lastPos);
282-
}
283-
if (!StringUtils.containsOnly(val, VALID_INT_CHARS)) {
284-
return true;
285-
}
286-
}
268+
final BigDecimal step = parseAttributeAsBigDecimal(getStep());
269+
if (step == null) {
270+
return false;
271+
}
287272

288-
final BigDecimal value;
289-
try {
290-
value = new BigDecimal(rawValue);
291-
}
292-
catch (final NumberFormatException e) {
293-
return true;
294-
}
273+
BigDecimal min = parseAttributeAsBigDecimal(getMin());
274+
if (min == null) {
275+
min = BigDecimal.ZERO;
276+
}
295277

296-
if (!getMin().isEmpty()) {
297-
try {
298-
final BigDecimal min = new BigDecimal(getMin());
299-
if (value.compareTo(min) < 0) {
300-
return true;
301-
}
302-
303-
if (!getStep().isEmpty()) {
304-
try {
305-
final BigDecimal step = new BigDecimal(getStep());
306-
if (value.subtract(min).abs().remainder(step).doubleValue() > 0.0) {
307-
return true;
308-
}
309-
}
310-
catch (final NumberFormatException ignored) {
311-
// ignore
312-
}
313-
}
314-
}
315-
catch (final NumberFormatException ignored) {
316-
// ignore
317-
}
318-
}
278+
return value.subtract(min).abs().remainder(step).doubleValue() > 0.0;
279+
}
280+
281+
/**
282+
* {@inheritDoc}
283+
* The new home for the malformed-value case that used to make
284+
* hasRangeOverflowValidityState()/hasRangeUnderflowValidityState()/
285+
* isStepMismatchValidityState() all incorrectly return {@code true}
286+
* simultaneously.
287+
*/
288+
@Override
289+
public boolean hasBadInputValidityState() {
290+
final String rawValue = getRawValue();
291+
if (StringUtils.isBlank(rawValue)) {
292+
return false;
319293
}
320-
return false;
294+
return parseNumericValue() == null;
321295
}
322296
}

0 commit comments

Comments
 (0)