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

io.vertx.db2client.impl.drda.CrossConverters Maven / Gradle / Ivy

There is a newer version: 5.0.0.CR1
Show newest version
/*
 * Copyright (C) 2019,2020 IBM Corporation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package io.vertx.db2client.impl.drda;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.io.StringWriter;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.nio.charset.Charset;
import java.sql.Array;
import java.sql.Blob;
import java.sql.Clob;
import java.sql.Date;
import java.sql.Ref;
import java.sql.Time;
import java.sql.Timestamp;
import java.sql.Types;
import java.time.LocalDate;
import java.time.LocalTime;
import java.util.Calendar;
import java.util.Locale;

// All currently supported types are mapped to one of the following jdbc types:
// Types.SMALLINT;
// Types.INTEGER;
// Types.BIGINT;
// Types.REAL;
// Types.DOUBLE;
// Types.DECIMAL;
// Types.DATE;
// Types.TIME;
// Types.TIMESTAMP;
// Types.CHAR;
// Types.VARCHAR;
// Types.LONGVARCHAR;
// Types.CLOB;
// Types.BLOB;
//

final class CrossConverters {

    /**
     * Value used to signal unknown length of data.
     */
    public static final int UNKNOWN_LENGTH = Integer.MIN_VALUE;

    private final static BigDecimal bdMaxByteValue__ =
            BigDecimal.valueOf(Byte.MAX_VALUE);
    private final static BigDecimal bdMinByteValue__ =
            BigDecimal.valueOf(Byte.MIN_VALUE);
    private final static BigDecimal bdMaxShortValue__ =
            BigDecimal.valueOf(Short.MAX_VALUE);
    private final static BigDecimal bdMinShortValue__ =
            BigDecimal.valueOf(Short.MIN_VALUE);
    private final static BigDecimal bdMaxIntValue__ =
            BigDecimal.valueOf(Integer.MAX_VALUE);
    private final static BigDecimal bdMinIntValue__ =
            BigDecimal.valueOf(Integer.MIN_VALUE);
    private final static BigDecimal bdMaxLongValue__ =
            BigDecimal.valueOf(Long.MAX_VALUE);
    private final static BigDecimal bdMinLongValue__ =
            BigDecimal.valueOf(Long.MIN_VALUE);
    private final static BigDecimal bdMaxFloatValue__ =
            new BigDecimal(Float.MAX_VALUE);
    private final static BigDecimal bdMinFloatValue__ =
            new BigDecimal(-Float.MAX_VALUE);
    private final static BigDecimal bdMaxDoubleValue__ =
            new BigDecimal(Double.MAX_VALUE);
    private final static BigDecimal bdMinDoubleValue__ =
            new BigDecimal(-Double.MAX_VALUE);

    // Since BigDecimals are immutable, we can return pointers to these canned 0's and 1's.
    private final static BigDecimal bdZero__ = BigDecimal.valueOf(0);
    private final static BigDecimal bdOne__ = BigDecimal.valueOf(1);

    private CrossConverters() {
    }

    // ---------------------------------------------------------------------------
    // The following methods are used for input cross conversion.
    // ---------------------------------------------------------------------------

    //---------------------------- setObject() methods ---------------------------

    // Convert from boolean source to target type.
    // In support of PS.setBoolean().
    static final Object setObject(int targetType, boolean source) {
        short numVal = source ? (short) 1 : 0;
        switch (targetType) {
        case ClientTypes.BIT:
        case ClientTypes.BOOLEAN:
            return Boolean.valueOf(source);

        case ClientTypes.SMALLINT:
            return Short.valueOf(numVal);

        case ClientTypes.INTEGER:
            return Integer.valueOf(numVal);

        case ClientTypes.BIGINT:
            return Long.valueOf(numVal);

        case ClientTypes.REAL:
            return Float.valueOf(numVal);

        case ClientTypes.DOUBLE:
            return Double.valueOf(numVal);

        case ClientTypes.DECIMAL:
            return BigDecimal.valueOf(numVal);

        case ClientTypes.CHAR:
        case ClientTypes.VARCHAR:
        case ClientTypes.LONGVARCHAR:
            return String.valueOf(source);

        default:
            throw new IllegalArgumentException("SQLState.LANG_DATA_TYPE_SET_MISMATCH " +
                "boolean" + ClientTypes.getTypeString(targetType));
        }
    }

    // Convert from byte source to target type
    // In support of PS.setByte()
    static final Object setObject(int targetType, byte source) {
        return setObject(targetType, (short) source);
    }

    // Convert from short source to target type
    // In support of PS.setShort()
    static final Object setObject(int targetType, short source) {
        switch (targetType) {
        case ClientTypes.BIT:
        case ClientTypes.BOOLEAN:
            return Boolean.valueOf(source != 0);

        case ClientTypes.SMALLINT:
            return Short.valueOf(source);

        case ClientTypes.INTEGER:
            return Integer.valueOf(source);

        case ClientTypes.BIGINT:
            return Long.valueOf(source);

        case ClientTypes.REAL:
            return Float.valueOf(source);

        case ClientTypes.DOUBLE:
            return Double.valueOf(source);

        case ClientTypes.DECIMAL:
            return BigDecimal.valueOf(source);

        case ClientTypes.CHAR:
        case ClientTypes.VARCHAR:
        case ClientTypes.LONGVARCHAR:
            return String.valueOf(source);

        default:
            throw new IllegalArgumentException("SQLState.LANG_DATA_TYPE_SET_MISMATCH " +
                "byte" + ClientTypes.getTypeString(targetType));
        }
    }

    // Convert from integer source to target type
    // In support of PS.setInt()
    static final Object setObject(int targetType, int source) {
        switch (targetType) {
        case ClientTypes.BIT:
        case ClientTypes.BOOLEAN:
            return Boolean.valueOf(source != 0);

        case ClientTypes.SMALLINT:
            if (source > Short.MAX_VALUE || source < Short.MIN_VALUE) {
                throw new IllegalArgumentException("Outside range for SMALLINT (Short) " + source);
            }
            return Short.valueOf((short) source);

        case ClientTypes.INTEGER:
            return Integer.valueOf(source);

        case ClientTypes.BIGINT:
            return Long.valueOf(source);

        case ClientTypes.REAL:
            return Float.valueOf(source);

        case ClientTypes.DOUBLE:
            return Double.valueOf(source);

        case ClientTypes.DECIMAL:
            return BigDecimal.valueOf(source);

        case ClientTypes.CHAR:
        case ClientTypes.VARCHAR:
        case ClientTypes.LONGVARCHAR:
            return String.valueOf(source);

        default:
            throw new IllegalArgumentException("SQLState.LANG_DATA_TYPE_SET_MISMATCH int " + ClientTypes.getTypeString(targetType));
        }
    }

    static final boolean setBooleanFromObject(Object source, int sourceType) {
        switch (sourceType) {
        case ClientTypes.SMALLINT:
            return getBooleanFromShort(((Short) source).shortValue());
        case ClientTypes.INTEGER:
            return getBooleanFromInt(((Integer) source).intValue());
        case ClientTypes.BIGINT:
            return getBooleanFromLong(((BigInteger) source).longValue());
        case ClientTypes.REAL:
            return getBooleanFromFloat(((Float) source).floatValue());
        case ClientTypes.DOUBLE:
            return getBooleanFromDouble(((Double) source).doubleValue());
        case ClientTypes.DECIMAL:
            return getBooleanFromLong(((BigDecimal) source).longValue());
        case ClientTypes.CHAR:
        case ClientTypes.VARCHAR:
        case ClientTypes.LONGVARCHAR:
            return getBooleanFromString((String) source);
        default:
            throw new IllegalArgumentException(ClientTypes.getTypeString(sourceType) +  " boolean");
        }
    }

    static final byte setByteFromObject(Object source, int sourceType) {
        switch (sourceType) {
        case ClientTypes.SMALLINT:
            return getByteFromShort(((Short) source).shortValue());
        case ClientTypes.INTEGER:
            return getByteFromInt(((Integer) source).intValue());
        case ClientTypes.BIGINT:
            return getByteFromLong(((BigInteger) source).longValue());
        case ClientTypes.REAL:
            return getByteFromFloat(((Float) source).floatValue());
        case ClientTypes.DOUBLE:
            return getByteFromDouble(((Double) source).doubleValue());
        case ClientTypes.DECIMAL:
            return getByteFromLong(((BigDecimal) source).longValue());
        case ClientTypes.CHAR:
        case ClientTypes.VARCHAR:
        case ClientTypes.LONGVARCHAR:
            return getByteFromString((String) source);
        default:
            throw new IllegalArgumentException(ClientTypes.getTypeString(sourceType) +  " byte");
        }
    }

    // Convert from long source to target type
    // In support of PS.setLong()
    static final Object setObject(int targetType, long source) {
        switch (targetType) {
        case ClientTypes.BIT:
        case ClientTypes.BOOLEAN:
            return Boolean.valueOf(source != 0);

        case ClientTypes.SMALLINT:
            if (source > Short.MAX_VALUE || source < Short.MIN_VALUE) {
                throw new IllegalArgumentException("Value outside range for SMALLINT: " + source);
            }
            return Short.valueOf((short) source);

        case ClientTypes.INTEGER:
            if (source > Integer.MAX_VALUE || source < Integer.MIN_VALUE) {
                throw new IllegalArgumentException("Value outside range for INTEGER: " + source);
            }
            return Integer.valueOf((int) source);

        case ClientTypes.BIGINT:
            return Long.valueOf(source);

        case ClientTypes.REAL:
            return Float.valueOf(source);

        case ClientTypes.DOUBLE:
            return Double.valueOf(source);

        case ClientTypes.DECIMAL:
            return BigDecimal.valueOf(source);

        case ClientTypes.CHAR:
        case ClientTypes.VARCHAR:
        case ClientTypes.LONGVARCHAR:
            return String.valueOf(source);

        default:
            throw new IllegalArgumentException("SQLState.LANG_DATA_TYPE_SET_MISMATCH long" + ClientTypes.getTypeString(targetType));
        }
    }

    // Convert from floating point source to target type
    // In support of PS.setFloat()
    static final Object setObject(int targetType, float source) {
        switch (targetType) {
        case ClientTypes.BIT:
        case ClientTypes.BOOLEAN:
            return Boolean.valueOf(source != 0);

        case ClientTypes.SMALLINT:
            if (source > Short.MAX_VALUE || source < Short.MIN_VALUE) {
                throw new IllegalArgumentException("Value outside range for SMALLINT: " + source);
            }
            return Short.valueOf((short) source);

        case ClientTypes.INTEGER:
            if (source > Integer.MAX_VALUE || source < Integer.MIN_VALUE) {
                throw new IllegalArgumentException("Value outside range for INTEGER: " + source);
            }
            return Integer.valueOf((int) source);

        case ClientTypes.BIGINT:
            if (source > Long.MAX_VALUE || source < Long.MIN_VALUE) {
                throw new IllegalArgumentException("Value outside range for BIGINT: " + source);
            }
            return Long.valueOf((long) source);

        case ClientTypes.REAL:
                    // change the check from (source > Float.MAX_VALUE || source < -Float.MIN_VALUE))
                    // to the following:
                    //-----------------------------------------------------------------------------------
                    //   -infinity                             0                            +infinity
                    //           |__________________________|======|________________________|
                    //  <-3.4E+38|                          |      |                        |>+3.4E+38
                    //           |                          |      |_________________       |
                    //           |                          |-1.4E-45 +1.79E+308
                    //            |                          |      |_________________       |
                    //            |                          |-4.9E-324  Short.MAX_VALUE || source < Short.MIN_VALUE) {
                throw new IllegalArgumentException("Value outside range for SMALLINT: " + source);
            }
            return Short.valueOf((short) source);

        case ClientTypes.INTEGER:
            if (source > Integer.MAX_VALUE || source < Integer.MIN_VALUE) {
                throw new IllegalArgumentException("Value outside range for INTEGER: " + source);
            }
            return Integer.valueOf((int) source);

        case ClientTypes.BIGINT:
            if (source > Long.MAX_VALUE || source < Long.MIN_VALUE) {
                throw new IllegalArgumentException("Value outside range for BIGINT: " + source);
            }
            return Long.valueOf((long) source);

        case ClientTypes.REAL:
            if (source > Float.MAX_VALUE || source < -Float.MAX_VALUE) {
                throw new IllegalArgumentException("Value outside range for REAL: " + source);
            }
            return Float.valueOf((float) source);

        case ClientTypes.DOUBLE:
                    // change the check from (source > Double.MAX_VALUE || source < -Double.MIN_VALUE))
                    // to the following:
                    //-------------------------------------------------------------------------------------
                    //    -infinity                             0                            +infinity
                    //            |__________________________|======|________________________|
                    // <-1.79E+308|                          |      |                        |>+1.79E+308
                    //            |                          |      |_________________       |
                    //            |                          |-4.9E-324 
     * Get a boolean value from a CHAR column. In order to match the embedded
     * driver and JCC we return false iff the CHAR value is "0" or "false".
     * 

* *

* Leading and trailing whitespace is removed from the input string before * it's compared to "0" and "false". No other normalization is performed. * Specifically, no case conversion is performed, so the comparison is * case sensitive, and everything that doesn't exactly match "0" or "false" * will be considered true. *

* * @param source the value of a CHAR column * @return false if source is "0" or "false", true otherwise */ static final boolean getBooleanFromString(String source) { String trimmed = source.trim(); return !(trimmed.equals("0") || trimmed.equals("false")); } //---------------------------- getByte*() methods ---------------------------- static final byte getByteFromShort(short source) { if (source > Byte.MAX_VALUE || source < Byte.MIN_VALUE) { throw new IllegalArgumentException("Value outside range for TINYINT: " + source); } return (byte) source; } static final byte getByteFromInt(int source) { if (source > Byte.MAX_VALUE || source < Byte.MIN_VALUE) { throw new IllegalArgumentException("Value outside range for TINYINT: " + source); } return (byte) source; } static final byte getByteFromLong(long source) { if (source > Byte.MAX_VALUE || source < Byte.MIN_VALUE) { throw new IllegalArgumentException("Value outside range for TINYINT: " + source); } return (byte) source; } static final byte getByteFromFloat(float source) { if (source > Byte.MAX_VALUE || source < Byte.MIN_VALUE) { throw new IllegalArgumentException("Value outside range for TINYINT: " + source); } return (byte) source; } static final byte getByteFromDouble(double source) { if (source > Byte.MAX_VALUE || source < Byte.MIN_VALUE) { throw new IllegalArgumentException("Value outside range for TINYINT: " + source); } return (byte) source; } static final byte getByteFromBoolean(boolean source) { return source ? (byte) 1 : (byte) 0; } static final byte getByteFromString(String source) { return parseByte(source); } //---------------------------- getShort*() methods --------------------------- static final short getShortFromInt(int source) { if (source > Short.MAX_VALUE || source < Short.MIN_VALUE) { throw new IllegalArgumentException("Value outside range for SMALLINT: " + source); } return (short) source; } static final short getShortFromLong(long source) { if (source > Short.MAX_VALUE || source < Short.MIN_VALUE) { throw new IllegalArgumentException("Value outside range for SMALLINT: " + source); } return (short) source; } static final short getShortFromFloat(float source) { if (source > Short.MAX_VALUE || source < Short.MIN_VALUE) { throw new IllegalArgumentException("Value outside range for SMALLINT: " + source); } return (short) source; } static final short getShortFromDouble(double source) { if (source > Short.MAX_VALUE || source < Short.MIN_VALUE) { throw new IllegalArgumentException("Value outside range for SMALLINT: " + source); } return (short) source; } static final short getShortFromBoolean(boolean source) { return source ? (short) 1 : (short) 0; } static final short getShortFromString(String source) { try { return parseShort(source); } catch (NumberFormatException e) { throw new IllegalArgumentException("SQLState.LANG_FORMAT_EXCEPTION short", e); } } //---------------------------- getInt*() methods ----------------------------- static final int getIntFromLong(long source) { if (source > Integer.MAX_VALUE || source < Integer.MIN_VALUE) { throw new IllegalArgumentException("Value outside range for INTEGER: " + source); } return (int) source; } static final int getIntFromFloat(float source) { if (source > Integer.MAX_VALUE || source < Integer.MIN_VALUE) { throw new IllegalArgumentException("Value outside range for INTEGER: " + source); } return (int) source; } static final int getIntFromDouble(double source) { if (source > Integer.MAX_VALUE || source < Integer.MIN_VALUE) { throw new IllegalArgumentException("Value outside range for INTEGER: " + source); } return (int) source; } static final int getIntFromBoolean(boolean source) { return source ? (int) 1 : (int) 0; } static final int getIntFromString(String source) { return parseInt(source); } //---------------------------- getLong*() methods ---------------------------- static final long getLongFromFloat(float source) { if (source > Long.MAX_VALUE || source < Long.MIN_VALUE) { throw new IllegalArgumentException("Value outside range for BIGINT: " + source); } return (long) source; } static final long getLongFromDouble(double source) { if (source > Long.MAX_VALUE || source < Long.MIN_VALUE) { throw new IllegalArgumentException("Value outside range for BIGINT: " + source); } return (long) source; } static final long getLongFromBoolean(boolean source) { return source ? (long) 1 : (long) 0; } static final long getLongFromString(String source) { return parseLong(source); } //---------------------------- getFloat*() methods --------------------------- static final float getFloatFromDouble(double source) { if (Float.isInfinite((float)source)) { throw new IllegalArgumentException("Value outside range for DOUBLE: " + source); } return (float) source; } static final float getFloatFromBoolean(boolean source) { return source ? (float) 1 : (float) 0; } static final float getFloatFromString(String source) { return Float.parseFloat(source.trim()); } //---------------------------- getDouble*() methods -------------------------- static final double getDoubleFromBoolean(boolean source) { return source ? (double) 1 : (double) 0; } static final double getDoubleFromString(String source) { return Double.parseDouble(source.trim()); } //---------------------------- getBigDecimal*() methods ---------------------- static final BigDecimal getBigDecimalFromString(String source) { // Unfortunately, the big decimal constructor calls java.lang.Long.parseLong(), // which doesn't like spaces, so we have to call trim() to get rid of the spaces from CHAR columns. return new BigDecimal(source.trim()); } //---------------------------- getString*() methods -------------------------- static final String getStringFromBytes(byte[] bytes) { StringBuffer stringBuffer = new StringBuffer(bytes.length * 2); for (int i = 0; i < bytes.length; i++) { String hexForByte = Integer.toHexString(bytes[i] & 0xff); // If the byte is x0-F, prepend a "0" in front to ensure 2 char representation if (hexForByte.length() == 1) { stringBuffer.append('0'); } stringBuffer.append(hexForByte); } return stringBuffer.toString(); } // All Numeric, and Date/Time types use String.valueOf (source) //---------------------------- getDate*() methods ---------------------------- static final LocalDate getDateFromString(String source) { return LocalDate.parse(source); // return date_valueOf(source); } //---------------------------- getTime*() methods ---------------------------- static final LocalTime getTimeFromString(String source) { return LocalTime.parse(source); // return time_valueOf(source, cal); } //---------------------------- getTimestamp*() methods ----------------------- static final Timestamp getTimestampFromString(String source, Calendar cal) { return timestamp_valueOf(source, cal); } /** * Initialize the date components of a {@code java.util.Calendar} from * a string on the format YYYY-MM-DD. All other components are left * untouched. * * @param cal the calendar whose date components to initialize * @param date a string representing a date * @throws IllegalArgumentException if the date string is not on the * format YYYY-MM-DD */ private static void initDatePortion(Calendar cal, String date) { // Expect string on format YYYY-MM-DD if (date.length() != 10 || date.charAt(4) != '-' || date.charAt(7) != '-') { throw new IllegalArgumentException(); } int year = digit(date.charAt(0)) * 1000 + digit(date.charAt(1)) * 100 + digit(date.charAt(2)) * 10 + digit(date.charAt(3)); int month = digit(date.charAt(5)) * 10 + digit(date.charAt(6)) - 1; // subtract one since // Calendar.JANUARY == 0 int day = digit(date.charAt(8)) * 10 + digit(date.charAt(9)); cal.set(year, month, day); } /** * Convert a character to a digit. * * @param ch the character * @return the corresponding digit (0-9) * @throws IllegalArgumentException if {@code ch} doesn't represent a digit */ private static int digit(char ch) { int result = Character.digit(ch, 10); if (result == -1) { throw new IllegalArgumentException(); } return result; } /** * Initialize the time components of a {@code java.util.Calendar} from a * string on the format HH:MM:SS. All other components are left untouched. * * @param cal the calendar whose time components to initialize * @param time a string representing a time * @throws IllegalArgumentException if the time string is not on the * format HH:MM:SS */ private static void initTimePortion(Calendar cal, String time) { // Expect string on format HH:MM:SS if (time.length() != 8 || time.charAt(2) != ':' || time.charAt(5) != ':') { throw new IllegalArgumentException(); } int hour = digit(time.charAt(0)) * 10 + digit(time.charAt(1)); int minute = digit(time.charAt(3)) * 10 + digit(time.charAt(4)); int second = digit(time.charAt(6)) * 10 + digit(time.charAt(7)); cal.set(Calendar.HOUR_OF_DAY, hour); cal.set(Calendar.MINUTE, minute); cal.set(Calendar.SECOND, second); } /** * Convert a string to a timestamp in the specified calendar. Accept the * same format as {@code java.sql.Timestamp.valueOf()}. * * @param s the string to parse * @param cal the calendar (or null to use the default calendar) * @return a {@code java.sql.Timestamp} value that represents the timestamp * in the calendar {@code cal} * @throws IllegalArgumentException if the format of the string is invalid */ private static Timestamp timestamp_valueOf(String s, Calendar cal) { if (s == null) { throw new IllegalArgumentException(); } s = s.trim(); if (cal == null) { return Timestamp.valueOf(s); } cal.clear(); // Split into date and time components String[] dateAndTime = s.split(" "); if (dateAndTime.length != 2) { throw new IllegalArgumentException(); } String dateString = dateAndTime[0]; String timeAndNanoString = dateAndTime[1]; initDatePortion(cal, dateString); // Split the time and nano components. The nano component is optional, // and is separated from the time component with a decimal point. String[] timeAndNanos = timeAndNanoString.split("\\."); if (timeAndNanos.length < 1 || timeAndNanos.length > 2) { throw new IllegalArgumentException(); } String timeString = timeAndNanos[0]; initTimePortion(cal, timeString); int nanos = 0; if (timeAndNanos.length > 1) { String nanoString = timeAndNanos[1]; int extraZeros = 9 - nanoString.length(); if (extraZeros < 0) { throw new IllegalArgumentException(); } // parseInt() may throw NumberFormatException. NFE is a subclass // of IllegalArgumentException, so no need to document separately // in the javadoc. nanos = Integer.parseInt(nanoString); for (int i = 0; i < extraZeros; i++) { nanos *= 10; } } Timestamp ts = new Timestamp(cal.getTimeInMillis()); ts.setNanos(nanos); return ts; } private static byte parseByte(String s) throws NumberFormatException { int i = parseInt(s); if (i < Byte.MIN_VALUE || i > Byte.MAX_VALUE) { throw new NumberFormatException(); } return (byte) i; } private static short parseShort(String s) throws NumberFormatException { int i = parseInt(s); if (i < Short.MIN_VALUE || i > Short.MAX_VALUE) { throw new NumberFormatException(); } return (short) i; } // Custom version of java.lang.parseInt() that allows for space padding of char fields. private static int parseInt(String s) throws NumberFormatException { if (s == null) { throw new NumberFormatException("null"); } int result = 0; boolean negative = false; int i = 0; int max = s.length(); int limit; int multmin; int digit; if (max == 0) { throw new NumberFormatException(s); } if (s.charAt(0) == '-') { negative = true; limit = Integer.MIN_VALUE; i++; } else { limit = -Integer.MAX_VALUE; } multmin = limit / 10; // Special handle the first digit to get things started. if (i < max) { digit = Character.digit(s.charAt(i++), 10); if (digit < 0) { throw new NumberFormatException(s); } else { result = -digit; } } // Now handle all the subsequent digits or space padding. while (i < max) { char c = s.charAt(i++); if (c == ' ') { skipPadding(s, i, max); break; } // Accumulating negatively avoids surprises near MAX_VALUE digit = Character.digit(c, 10); if (digit < 0) { throw new NumberFormatException(s); } if (result < multmin) { throw new NumberFormatException(s); } result *= 10; if (result < limit + digit) { throw new NumberFormatException(s); } result -= digit; } if (negative) { if (i > 1) { return result; } else { // Only got "-" throw new NumberFormatException(s); } } else { return -result; } } private static long parseLong(String s) throws NumberFormatException { if (s == null) { throw new NumberFormatException("null"); } long result = 0; boolean negative = false; int i = 0, max = s.length(); long limit; long multmin; int digit; if (max == 0) { throw new NumberFormatException(s); } if (s.charAt(0) == '-') { negative = true; limit = Long.MIN_VALUE; i++; } else { limit = -Long.MAX_VALUE; } multmin = limit / 10; if (i < max) { digit = Character.digit(s.charAt(i++), 10); if (digit < 0) { throw new NumberFormatException(s); } else { result = -digit; } } while (i < max) { char c = s.charAt(i++); if (c == ' ') { skipPadding(s, i, max); break; } // Accumulating negatively avoids surprises near MAX_VALUE digit = Character.digit(c, 10); if (digit < 0) { throw new NumberFormatException(s); } if (result < multmin) { throw new NumberFormatException(s); } result *= 10; if (result < limit + digit) { throw new NumberFormatException(s); } result -= digit; } if (negative) { if (i > 1) { return result; } else { // Only got "-" throw new NumberFormatException(s); } } else { return -result; } } private static void skipPadding(String s, int i, int length) throws NumberFormatException { while (i < length) { if (s.charAt(i++) != ' ') { throw new NumberFormatException(s); } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy