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

com.github.leeonky.util.NumberParser Maven / Gradle / Ivy

The newest version!
package com.github.leeonky.util;

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

public class NumberParser {
    private static final PrimitiveIntegerPostfix BYTE_POSTFIX = new PrimitiveIntegerPostfix(1) {
        @Override
        public Number convertFrom(int number, String content) {
            if (number > Byte.MAX_VALUE || number < Byte.MIN_VALUE)
                throw new NumberOverflowException(content);
            return (byte) number;
        }
    }, SHORT_POSTFIX = new PrimitiveIntegerPostfix(1) {
        @Override
        public Number convertFrom(int number, String content) {
            if (number > Short.MAX_VALUE || number < Short.MIN_VALUE)
                throw new NumberOverflowException(content);
            return (short) number;
        }
    }, LONG_POSTFIX = new PrimitiveIntegerPostfix(1) {
        @Override
        public Number convertFrom(int number, String content) {
            return (long) number;
        }

        @Override
        public Number convertFrom(long number, String content) {
            return number;
        }
    };
    private static final StringNumberPostfix BIG_INTEGER_POSTFIX = new StringNumberPostfix(2) {
        @Override
        public Number convertFromBigInteger(String numberString, int radix, String content) {
            return new BigInteger(numberString, radix);
        }
    }, FLOAT_POSTFIX = new StringNumberPostfix(1) {
        @Override
        public Number convertFromDecimal(String numberString, String content) {
            return verifyInfinite(Float.parseFloat(numberString), content);
        }

        private Number verifyInfinite(float f, String content) {
            if (Float.isInfinite(f))
                throw new NumberOverflowException(content);
            return f;
        }

        @Override
        public Number convertFromBigInteger(String numberString, int radix, String content) {
            return verifyInfinite(Float.parseFloat(numberString), content);
        }
    }, DOUBLE_POSTFIX = new StringNumberPostfix(1) {
        @Override
        public Number convertFromDecimal(String numberString, String content) {
            return verifyInfinite(Double.parseDouble(numberString), content);
        }

        @Override
        public Number convertFromBigInteger(String numberString, int radix, String content) {
            return verifyInfinite(Double.parseDouble(numberString), content);
        }

        private Number verifyInfinite(double d, String content) {
            if (Double.isInfinite(d))
                throw new NumberOverflowException(content);
            return d;
        }
    }, BIG_DECIMAL_POSTFIX = new StringNumberPostfix(2) {
        @Override
        public Number convertFromDecimal(String numberString, String content) {
            return new BigDecimal(numberString);
        }

        @Override
        public Number convertFromBigInteger(String numberString, int radix, String content) {
            return new BigDecimal(numberString);
        }
    };

    public Number parse(String content) {
        if (content == null)
            return null;
        int length = content.length();
        if (length == 0)
            return null;
        int sign = 1;
        int index = 0;
        char c = content.charAt(index);
        if (c == '+') {
            if (++index == length)
                return null;
        } else if (c == '-') {
            if (++index == length)
                return null;
            sign = -1;
        }
        int radix = 10;
        if (index + 1 < length && content.charAt(index) == '0') {
            char radixChar = content.charAt(index + 1);
            if (isDigit(radixChar) || radixChar == '_') {
                index++;
                radix = 8;
            } else if (index + 2 < length) {
                if (radixChar == 'x' || radixChar == 'X') {
                    index += 2;
                    radix = 16;
                } else if (radixChar == 'b' || radixChar == 'B') {
                    char nextChar = content.charAt(index + 2);
                    if (nextChar == '0' || nextChar == '1') {
                        index += 2;
                        radix = 2;
                    }
                }
            }
        }
        c = content.charAt(length - 1);
        StringNumberPostfix postfix = fetchDecimalOrBigIntegerPostfix(content, radix, c);
        if (postfix != null) {
            if (index == (length -= postfix.length))
                return null;
            return continueParseBigInteger(radix, index, content, length, postfix, newStringBuilder(length, sign));
        }
        return parseFromInteger(content, length, sign, index, radix, fetchOtherPostfix(c));
    }

    private PrimitiveIntegerPostfix fetchOtherPostfix(char c) {
        switch (c) {
            case 'y':
            case 'Y':
                return BYTE_POSTFIX;
            case 's':
            case 'S':
                return SHORT_POSTFIX;
            case 'l':
            case 'L':
                return LONG_POSTFIX;
        }
        return null;
    }

    private StringNumberPostfix fetchDecimalOrBigIntegerPostfix(String content, int radix, char c) {
        if (content.endsWith("bi") || content.endsWith("BI"))
            return BIG_INTEGER_POSTFIX;
        if (radix == 10) {
            if (content.endsWith("bd") || content.endsWith("BD"))
                return BIG_DECIMAL_POSTFIX;
            switch (c) {
                case 'f':
                case 'F':
                    return FLOAT_POSTFIX;
                case 'd':
                case 'D':
                    return DOUBLE_POSTFIX;
            }
        }
        return null;
    }

    private StringBuilder newStringBuilder(int length, int sign) {
        StringBuilder stringBuilder = new StringBuilder(length);
        if (sign == -1)
            stringBuilder.append('-');
        return stringBuilder;
    }

    private Number parseFromInteger(String content, int length, int sign, int index, int radix, PrimitiveIntegerPostfix postfix) {
        if (postfix != null) {
            if (index == (length -= postfix.length))
                return null;
        }
        int number = 0;
        int limit = sign == 1 ? -Integer.MAX_VALUE : Integer.MIN_VALUE;
        int limitBeforeMul = limit / radix;
        while (index < length) {
            char c = content.charAt(index++);
            if (c == '_' && index != length)
                continue;
            int digit = getDigit(radix, c);
            if (digit < 0) {
                if (isFloatDot(radix, c, index, length, content))
                    return parseDoubleWithDot(toStringBuilder(radix, sign, number, length), radix, content, index, length, postfix);
                if (isPowerChar(radix, c, index, length, content))
                    return parseDoubleWithPower(content, index, length, toStringBuilder(radix, sign, number, length), postfix);
                return null;
            }
            if (isOverflow(digit, number, limit, limitBeforeMul, radix))
                return continueParseLong(sign, radix, number, digit, index, content, length, postfix);
            number = number * radix - digit;
        }
        if (sign == 1)
            number = -number;
        return postfix != null ? postfix.convertFrom(number, content) : number;
    }

    private Number parseDoubleWithPower(String content, int index, int length, StringBuilder stringBuilder, StringNumberPostfix postfix) {
        stringBuilder.append('E');
        int eSign = 1;
        if (content.charAt(index) == '+') {
            if (++index == length)
                return null;
        }
        if (content.charAt(index) == '-') {
            if (++index == length)
                return null;
            eSign = -1;
        }
        if (eSign == -1)
            stringBuilder.append('-');
        while (index < length) {
            char c = content.charAt(index++);
            if (c == '_' && index != length)
                continue;
            if (notDigit(c))
                return null;
            stringBuilder.append(c);
        }
        return toDoubleOrBigDecimal(stringBuilder, postfix, content);
    }

    private boolean isPowerChar(int radix, char c, int index, int length, String content) {
        return (c == 'e' || c == 'E') && radix == 10
                && afterDigit(index, content) && beforeSignOrDigit(index, length, content);
    }

    private boolean beforeSignOrDigit(int index, int length, String content) {
        if (index >= length)
            return false;
        char c = content.charAt(index);
        return (isDigit(c) || c == '-' || c == '+');
    }

    private boolean isFloatDot(int radix, char c, int index, int length, String content) {
        return c == '.' && radix == 10 && afterDigit(index, content) && beforeDigit(index, length, content);
    }

    private boolean beforeDigit(int index, int length, String content) {
        return index < length && isDigit(content.charAt(index));
    }

    private boolean afterDigit(int index, String content) {
        return index > 1 && isDigit(content.charAt(index - 2));
    }

    private boolean notDigit(char c) {
        return c < '0' || c > '9';
    }

    private boolean isDigit(char c) {
        return c >= '0' && c <= '9';
    }

    private Number parseDoubleWithDot(StringBuilder stringBuilder, int radix, String content, int index, int length, StringNumberPostfix postfix) {
        stringBuilder.append('.');
        while (index < length) {
            char c = content.charAt(index++);
            if (c == '_' && index != length)
                continue;
            if (isPowerChar(radix, c, index, length, content))
                return parseDoubleWithPower(content, index, length, stringBuilder, postfix);
            if (notDigit(c))
                return null;
            stringBuilder.append(c);
        }
        return toDoubleOrBigDecimal(stringBuilder, postfix, content);
    }

    private Number toDoubleOrBigDecimal(StringBuilder stringBuilder, StringNumberPostfix postfix, String content) {
        String numberString = stringBuilder.toString();
        if (postfix != null)
            return postfix.convertFromDecimal(numberString, content);
        double d = Double.parseDouble(numberString);
        if (Double.isInfinite(d))
            return new BigDecimal(numberString);
        return d;
    }

    private Number continueParseLong(int sign, int radix, long number, int digit, int index,
                                     String content, int length, PrimitiveIntegerPostfix postfix) {
        number = number * radix - digit;
        long limitLong = sign == 1 ? -Long.MAX_VALUE : Long.MIN_VALUE;
        long limitBeforeMulLong = limitLong / radix;
        while (index < length) {
            char c = content.charAt(index++);
            if (c == '_' && index != length)
                continue;
            digit = getDigit(radix, c);
            if (digit < 0) {
                if (isFloatDot(radix, c, index, length, content))
                    return parseDoubleWithDot(toStringBuilder(radix, sign, number, length), radix, content, index, length, postfix);
                if (isPowerChar(radix, c, index, length, content))
                    return parseDoubleWithPower(content, index, length, toStringBuilder(radix, sign, number, length), postfix);
                return null;
            }
            if (isOverflow(digit, number, limitLong, limitBeforeMulLong, radix))
                return continueParseBigInteger(radix, index, content, length, postfix,
                        toStringBuilder(radix, sign, number, length).append(c));
            number = number * radix - digit;
        }
        if (sign == 1)
            number = -number;
        return postfix == null ? number : postfix.convertFrom(number, content);
    }

    private Number continueParseBigInteger(int radix, int index, String content, int length,
                                           StringNumberPostfix postfix, StringBuilder stringBuilder) {
        while (index < length) {
            char c = content.charAt(index++);
            if (c == '_' && index != length)
                continue;
            int digit = getDigit(radix, c);
            if (digit < 0) {
                if (isFloatDot(radix, c, index, length, content))
                    return parseDoubleWithDot(stringBuilder, radix, content, index, length, postfix);
                if (isPowerChar(radix, c, index, length, content))
                    return parseDoubleWithPower(content, index, length, stringBuilder, postfix);
                return null;
            }
            stringBuilder.append(c);
        }
        return postfix == null ? new BigInteger(stringBuilder.toString(), radix)
                : postfix.convertFromBigInteger(stringBuilder.toString(), radix, content);
    }

    private StringBuilder toStringBuilder(int radix, int sign, long number, int length) {
        return newStringBuilder(length, sign).append(Long.toString(-number, radix));
    }

    private boolean isOverflow(int digit, int number, int limit, int limitBeforeMul, int radix) {
        return number < limitBeforeMul || number * radix < limit + digit;
    }

    private boolean isOverflow(int digit, long number, long limit, long limitBeforeMul, int radix) {
        return number < limitBeforeMul || number * radix < limit + digit;
    }

    private int getDigit(int radix, char c) {
        int value = -1;
        if (c <= '9')
            value = c - '0';
        else if (c >= 'a')
            value = c - 'a' + 10;
        else if (c >= 'A')
            value = c - 'A' + 10;
        if (value >= 0 && value < radix)
            return value;
        return -1;
    }

    static class StringNumberPostfix {
        public final int length;

        public StringNumberPostfix(int length) {
            this.length = length;
        }

        public Number convertFromBigInteger(String numberString, int radix, String content) {
            throw new NumberOverflowException(content);
        }

        public Number convertFromDecimal(String numberString, String content) {
            throw new NumberOverflowException(content);
        }
    }

    static abstract class PrimitiveIntegerPostfix extends StringNumberPostfix {
        protected PrimitiveIntegerPostfix(int length) {
            super(length);
        }

        public abstract Number convertFrom(int number, String content);

        public Number convertFrom(long number, String content) {
            throw new NumberOverflowException(content);
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy