@@ -26,6 +26,8 @@ of this software and associated documentation files (the "Software"), to deal
2626
2727import java .io .Reader ;
2828import java .io .StringReader ;
29+ import java .math .BigDecimal ;
30+ import java .math .BigInteger ;
2931import java .util .Iterator ;
3032
3133/**
@@ -424,17 +426,20 @@ private static boolean parse(XMLTokener x, JSONObject context, String name, XMLP
424426 */
425427 // To maintain compatibility with the Android API, this method is a direct copy of
426428 // the one in JSONObject. Changes made here should be reflected there.
429+ // This method should not make calls out of the XML object.
427430 public static Object stringToValue (String string ) {
428- if (string .equals ("" )) {
431+ if ("" .equals (string )) {
429432 return string ;
430433 }
431- if (string .equalsIgnoreCase ("true" )) {
434+
435+ // check JSON key words true/false/null
436+ if ("true" .equalsIgnoreCase (string )) {
432437 return Boolean .TRUE ;
433438 }
434- if (string . equalsIgnoreCase ( "false" )) {
439+ if ("false" . equalsIgnoreCase ( string )) {
435440 return Boolean .FALSE ;
436441 }
437- if (string . equalsIgnoreCase ( "null" )) {
442+ if ("null" . equalsIgnoreCase ( string )) {
438443 return JSONObject .NULL ;
439444 }
440445
@@ -446,28 +451,84 @@ public static Object stringToValue(String string) {
446451 char initial = string .charAt (0 );
447452 if ((initial >= '0' && initial <= '9' ) || initial == '-' ) {
448453 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 );
456476 }
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." );
462484 }
463- return myLong ;
485+ return d ;
486+ } catch (NumberFormatException ignore ) {
487+ throw new NumberFormatException ("val [" +val +"] is not a valid number." );
464488 }
465489 }
466- } catch (Exception ignore ) {
467490 }
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 ;
468520 }
469- return string ;
521+ throw new NumberFormatException ( "val [" + val + "] is not a valid number." ) ;
470522 }
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+
471532
472533 /**
473534 * Convert a well-formed (but not necessarily valid) XML string into a
0 commit comments