@@ -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