All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.json.NumberConversionUtil Maven / Gradle / Ivy

Go to download

JSON is a light-weight, language independent, data interchange format. See http://www.JSON.org/ The files in this package implement JSON encoders/decoders in Java. It also includes the capability to convert between JSON and XML, HTTP headers, Cookies, and CDL. This is a reference implementation. There are a large number of JSON packages in Java. Perhaps someday the Java community will standardize on one. Until then, choose carefully.

There is a newer version: 20240303
Show newest version
package org.json;

import java.math.BigDecimal;
import java.math.BigInteger;

class NumberConversionUtil {

    /**
     * Converts a string to a number using the narrowest possible type. Possible
     * returns for this function are BigDecimal, Double, BigInteger, Long, and Integer.
     * When a Double is returned, it should always be a valid Double and not NaN or +-infinity.
     *
     * @param input value to convert
     * @return Number representation of the value.
     * @throws NumberFormatException thrown if the value is not a valid number. A public
     *      caller should catch this and wrap it in a {@link JSONException} if applicable.
     */
    static Number stringToNumber(final String input) throws NumberFormatException {
        String val = input;
        if (val.startsWith(".")){
            val = "0"+val;
        }
        if (val.startsWith("-.")){
            val = "-0."+val.substring(2);
        }
        char initial = val.charAt(0);
        if ( isNumericChar(initial) || initial == '-' ) {
            // decimal representation
            if (isDecimalNotation(val)) {
                // Use a BigDecimal all the time so we keep the original
                // representation. BigDecimal doesn't support -0.0, ensure we
                // keep that by forcing a decimal.
                try {
                    BigDecimal bd = new BigDecimal(val);
                    if(initial == '-' && BigDecimal.ZERO.compareTo(bd)==0) {
                        return Double.valueOf(-0.0);
                    }
                    return bd;
                } catch (NumberFormatException retryAsDouble) {
                    // this is to support "Hex Floats" like this: 0x1.0P-1074
                    try {
                        Double d = Double.valueOf(val);
                        if(d.isNaN() || d.isInfinite()) {
                            throw new NumberFormatException("val ["+input+"] is not a valid number.");
                        }
                        return d;
                    } catch (NumberFormatException ignore) {
                        throw new NumberFormatException("val ["+input+"] is not a valid number.");
                    }
                }
            }
            val = removeLeadingZerosOfNumber(input);
            initial = val.charAt(0);
            if(initial == '0' && val.length() > 1) {
                char at1 = val.charAt(1);
                if(isNumericChar(at1)) {
                    throw new NumberFormatException("val ["+input+"] is not a valid number.");
                }
            } else if (initial == '-' && val.length() > 2) {
                char at1 = val.charAt(1);
                char at2 = val.charAt(2);
                if(at1 == '0' && isNumericChar(at2)) {
                    throw new NumberFormatException("val ["+input+"] is not a valid number.");
                }
            }
            // integer representation.
            // This will narrow any values to the smallest reasonable Object representation
            // (Integer, Long, or BigInteger)

            // BigInteger down conversion: We use a similar bitLength compare as
            // BigInteger#intValueExact uses. Increases GC, but objects hold
            // only what they need. i.e. Less runtime overhead if the value is
            // long lived.
            BigInteger bi = new BigInteger(val);
            if(bi.bitLength() <= 31){
                return Integer.valueOf(bi.intValue());
            }
            if(bi.bitLength() <= 63){
                return Long.valueOf(bi.longValue());
            }
            return bi;
        }
        throw new NumberFormatException("val ["+input+"] is not a valid number.");
    }

    /**
     * Checks if the character is a numeric digit ('0' to '9').
     *
     * @param c The character to be checked.
     * @return true if the character is a numeric digit, false otherwise.
     */
    private static boolean isNumericChar(char c) {
        return (c <= '9' && c >= '0');
    }

    /**
     * Checks if the value could be considered a number in decimal number system.
     * @param value
     * @return
     */
    static boolean potentialNumber(String value){
        if (value == null || value.isEmpty()){
            return false;
        }
        return potentialPositiveNumberStartingAtIndex(value, (value.charAt(0)=='-'?1:0));
    }

    /**
     * Tests if the value should be tried as a decimal. It makes no test if there are actual digits.
     *
     * @param val value to test
     * @return true if the string is "-0" or if it contains '.', 'e', or 'E', false otherwise.
     */
    private static boolean isDecimalNotation(final String val) {
        return val.indexOf('.') > -1 || val.indexOf('e') > -1
                || val.indexOf('E') > -1 || "-0".equals(val);
    }

    private static boolean potentialPositiveNumberStartingAtIndex(String value,int index){
        if (index >= value.length()){
            return false;
        }
        return digitAtIndex(value, (value.charAt(index)=='.'?index+1:index));
    }

    private static boolean digitAtIndex(String value, int index){
        if (index >= value.length()){
            return false;
        }
        return value.charAt(index) >= '0' && value.charAt(index) <= '9';
    }

    /**
     * For a prospective number, remove the leading zeros
     * @param value prospective number
     * @return number without leading zeros
     */
    private static String removeLeadingZerosOfNumber(String value){
        if (value.equals("-")){return value;}
        boolean negativeFirstChar = (value.charAt(0) == '-');
        int counter = negativeFirstChar ? 1:0;
        while (counter < value.length()){
            if (value.charAt(counter) != '0'){
                if (negativeFirstChar) {return "-".concat(value.substring(counter));}
                return value.substring(counter);
            }
            ++counter;
        }
        if (negativeFirstChar) {return "-0";}
        return "0";
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy