@@ -26,6 +26,8 @@ of this software and associated documentation files (the "Software"), to deal
26
26
27
27
import java .io .Reader ;
28
28
import java .io .StringReader ;
29
+ import java .math .BigDecimal ;
30
+ import java .math .BigInteger ;
29
31
import java .util .Iterator ;
30
32
31
33
/**
@@ -424,17 +426,20 @@ private static boolean parse(XMLTokener x, JSONObject context, String name, XMLP
424
426
*/
425
427
// To maintain compatibility with the Android API, this method is a direct copy of
426
428
// the one in JSONObject. Changes made here should be reflected there.
429
+ // This method should not make calls out of the XML object.
427
430
public static Object stringToValue (String string ) {
428
- if (string .equals ("" )) {
431
+ if ("" .equals (string )) {
429
432
return string ;
430
433
}
431
- if (string .equalsIgnoreCase ("true" )) {
434
+
435
+ // check JSON key words true/false/null
436
+ if ("true" .equalsIgnoreCase (string )) {
432
437
return Boolean .TRUE ;
433
438
}
434
- if (string . equalsIgnoreCase ( "false" )) {
439
+ if ("false" . equalsIgnoreCase ( string )) {
435
440
return Boolean .FALSE ;
436
441
}
437
- if (string . equalsIgnoreCase ( "null" )) {
442
+ if ("null" . equalsIgnoreCase ( string )) {
438
443
return JSONObject .NULL ;
439
444
}
440
445
@@ -446,28 +451,84 @@ public static Object stringToValue(String string) {
446
451
char initial = string .charAt (0 );
447
452
if ((initial >= '0' && initial <= '9' ) || initial == '-' ) {
448
453
try {
449
- // if we want full Big Number support this block can be replaced with:
450
- // return stringToNumber(string);
451
- if (string .indexOf ('.' ) > -1 || string .indexOf ('e' ) > -1
452
- || string .indexOf ('E' ) > -1 || "-0" .equals (string )) {
453
- Double d = Double .valueOf (string );
454
- if (!d .isInfinite () && !d .isNaN ()) {
455
- return d ;
454
+ return stringToNumber (string );
455
+ } catch (Exception ignore ) {
456
+ }
457
+ }
458
+ return string ;
459
+ }
460
+
461
+ /**
462
+ * direct copy of {@link JSONObject#stringToNumber(String)} to maintain Android support.
463
+ */
464
+ private static Number stringToNumber (final String val ) throws NumberFormatException {
465
+ char initial = val .charAt (0 );
466
+ if ((initial >= '0' && initial <= '9' ) || initial == '-' ) {
467
+ // decimal representation
468
+ if (isDecimalNotation (val )) {
469
+ // Use a BigDecimal all the time so we keep the original
470
+ // representation. BigDecimal doesn't support -0.0, ensure we
471
+ // keep that by forcing a decimal.
472
+ try {
473
+ BigDecimal bd = new BigDecimal (val );
474
+ if (initial == '-' && BigDecimal .ZERO .compareTo (bd )==0 ) {
475
+ return Double .valueOf (-0.0 );
456
476
}
457
- } else {
458
- Long myLong = Long .valueOf (string );
459
- if (string .equals (myLong .toString ())) {
460
- if (myLong .longValue () == myLong .intValue ()) {
461
- return Integer .valueOf (myLong .intValue ());
477
+ return bd ;
478
+ } catch (NumberFormatException retryAsDouble ) {
479
+ // this is to support "Hex Floats" like this: 0x1.0P-1074
480
+ try {
481
+ Double d = Double .valueOf (val );
482
+ if (d .isNaN () || d .isInfinite ()) {
483
+ throw new NumberFormatException ("val [" +val +"] is not a valid number." );
462
484
}
463
- return myLong ;
485
+ return d ;
486
+ } catch (NumberFormatException ignore ) {
487
+ throw new NumberFormatException ("val [" +val +"] is not a valid number." );
464
488
}
465
489
}
466
- } catch (Exception ignore ) {
467
490
}
491
+ // block items like 00 01 etc. Java number parsers treat these as Octal.
492
+ if (initial == '0' && val .length () > 1 ) {
493
+ char at1 = val .charAt (1 );
494
+ if (at1 >= '0' && at1 <= '9' ) {
495
+ throw new NumberFormatException ("val [" +val +"] is not a valid number." );
496
+ }
497
+ } else if (initial == '-' && val .length () > 2 ) {
498
+ char at1 = val .charAt (1 );
499
+ char at2 = val .charAt (2 );
500
+ if (at1 == '0' && at2 >= '0' && at2 <= '9' ) {
501
+ throw new NumberFormatException ("val [" +val +"] is not a valid number." );
502
+ }
503
+ }
504
+ // integer representation.
505
+ // This will narrow any values to the smallest reasonable Object representation
506
+ // (Integer, Long, or BigInteger)
507
+
508
+ // BigInteger down conversion: We use a similar bitLenth compare as
509
+ // BigInteger#intValueExact uses. Increases GC, but objects hold
510
+ // only what they need. i.e. Less runtime overhead if the value is
511
+ // long lived.
512
+ BigInteger bi = new BigInteger (val );
513
+ if (bi .bitLength () <= 31 ){
514
+ return Integer .valueOf (bi .intValue ());
515
+ }
516
+ if (bi .bitLength () <= 63 ){
517
+ return Long .valueOf (bi .longValue ());
518
+ }
519
+ return bi ;
468
520
}
469
- return string ;
521
+ throw new NumberFormatException ( "val [" + val + "] is not a valid number." ) ;
470
522
}
523
+
524
+ /**
525
+ * direct copy of {@link JSONObject#isDecimalNotation(String)} to maintain Android support.
526
+ */
527
+ private static boolean isDecimalNotation (final String val ) {
528
+ return val .indexOf ('.' ) > -1 || val .indexOf ('e' ) > -1
529
+ || val .indexOf ('E' ) > -1 || "-0" .equals (val );
530
+ }
531
+
471
532
472
533
/**
473
534
* Convert a well-formed (but not necessarily valid) XML string into a
0 commit comments