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

com.dslplatform.json.MyNumberConverter Maven / Gradle / Ivy

package com.dslplatform.json;

import java.io.IOException;
import java.math.BigDecimal;
import java.util.Arrays;

import static com.dslplatform.json.NumberConverter.numberException;

/**
 dsl-json number deserializers accept numbers wrapped in strings, which is not
 a desirable behaviour from my point of view. That's why this class was created,
 to overwrite that behaviour.
 */
public abstract class MyNumberConverter {

    private static NumberInfo readLongNumber(final JsonReader reader,
                                             final int start
                                            ) throws IOException {
        int i = reader.length() - start;
        char[] tmp = reader.prepareBuffer(start,
                                          i
                                         );
        while (!reader.isEndOfStream()) {
            while (i < tmp.length) {
                final char ch = (char) reader.read();
                tmp[i++] = ch;
                if (reader.isEndOfStream() || !(ch >= '0' && ch <= '9' || ch == '-' || ch == '+' || ch == '.' || ch == 'e' || ch == 'E')) {
                    return new NumberInfo(tmp,
                                          i
                    );
                }
            }
            final int newSize = tmp.length * 2;
            if (newSize > reader.maxNumberDigits) {
                throw reader.newParseErrorFormat("Too many digits detected in number",
                                                 tmp.length,
                                                 "Number of digits larger than %d. Unable to read number",
                                                 reader.maxNumberDigits
                                                );
            }
            tmp = Arrays.copyOf(tmp,
                                newSize
                               );
        }
        return new NumberInfo(tmp,
                              i
        );
    }

    public static int parseInt(final JsonReader reader) throws ParsingException {
        final int    start = reader.scanNumber();
        final int    end   = reader.getCurrentIndex();
        final byte[] buf   = reader.buffer;
        final byte   ch    = buf[start];
        if (ch == '-') {
            return parseNegativeInt(buf,
                                    reader,
                                    start,
                                    end
                                   );
        }
        return parsePositiveInt(buf,
                                reader,
                                start,
                                end,
                                0
                               );
    }

    private static int parseNegativeInt(final byte[] buf,
                                        final JsonReader reader,
                                        final int start,
                                        final int end
                                       ) throws ParsingException {
        int value = 0;
        int i     = start + 1;
        if (i == end) numberException(reader,
                                      start,
                                      end,
                                      "Digit not found"
                                     );
        for (; i < end; i++) {
            final int ind = buf[i] - 48;
            if (ind < 0 || ind > 9) {
                if (i > start + 1 && reader.allWhitespace(i,
                                                          end
                                                         )) return value;
                else if (i == end - 1 && buf[i] == '.') numberException(reader,
                                                                        start,
                                                                        end,
                                                                        "Number ends with a dot"
                                                                       );
                final BigDecimal v = parseNumberGeneric(reader.prepareBuffer(start,
                                                                             end - start
                                                                            ),
                                                        end - start,
                                                        reader
                                                       );
                if (v.scale() > 0) numberException(reader,
                                                   start,
                                                   end,
                                                   "Expecting int but found decimal value",
                                                   v
                                                  );
                return v.intValue();
            }
            value = (value << 3) + (value << 1) - ind;
            if (value > 0) {
                numberException(reader,
                                start,
                                end,
                                "Integer overflow detected"
                               );
            }
        }
        return value;
    }

    private static int parsePositiveInt(final byte[] buf,
                                        final JsonReader reader,
                                        final int start,
                                        final int end,
                                        final int offset
                                       ) throws ParsingException {
        int value = 0;
        int i     = start + offset;
        if (i == end) numberException(reader,
                                      start,
                                      end,
                                      "Digit not found"
                                     );
        for (; i < end; i++) {
            final int ind = buf[i] - 48;
            if (ind < 0 || ind > 9) {
                if (i > start + offset && reader.allWhitespace(i,
                                                               end
                                                              )) return value;
                else if (i == end - 1 && buf[i] == '.') numberException(reader,
                                                                        start,
                                                                        end,
                                                                        "Number ends with a dot"
                                                                       );
                final BigDecimal v = parseNumberGeneric(reader.prepareBuffer(start,
                                                                             end - start
                                                                            ),
                                                        end - start,
                                                        reader
                                                       );
                if (v.scale() > 0) numberException(reader,
                                                   start,
                                                   end,
                                                   "Expecting int but found decimal value",
                                                   v
                                                  );
                return v.intValue();

            }
            value = (value << 3) + (value << 1) + ind;
            if (value < 0) {
                numberException(reader,
                                start,
                                end,
                                "Integer overflow detected"
                               );
            }
        }
        return value;
    }

    private static BigDecimal parseNumberGeneric(final char[] buf,
                                                 final int len,
                                                 final JsonReader reader
                                                ) throws ParsingException {
        int end = len;
        while (end > 0 && Character.isWhitespace(buf[end - 1])) {
            end--;
        }
        if (end > reader.maxNumberDigits) {
            throw reader.newParseErrorWith("Too many digits detected in number",
                                           len,
                                           "",
                                           "Too many digits detected in number",
                                           end,
                                           ""
                                          );
        }
        try {
            return new BigDecimal(buf,
                                  0,
                                  end
            );
        } catch (NumberFormatException nfe) {
            throw reader.newParseErrorAt("Error parsing number",
                                         len,
                                         nfe
                                        );
        }
    }

    public static long parseLong(final JsonReader reader) throws IOException {
        final int    start = reader.scanNumber();
        final int    end   = reader.getCurrentIndex();
        final byte[] buf   = reader.buffer;
        final byte   ch    = buf[start];
        int          i     = start;
        long         value = 0;
        if (ch == '-') {
            i = start + 1;
            if (i == end) numberException(reader,
                                          start,
                                          end,
                                          "Digit not found"
                                         );
            for (; i < end; i++) {
                final int ind = buf[i] - 48;
                if (ind < 0 || ind > 9) {
                    if (i > start + 1 && reader.allWhitespace(i,
                                                              end
                                                             )) return value;
                    return parseLongGeneric(reader,
                                            start,
                                            end
                                           );
                }
                value = (value << 3) + (value << 1) - ind;
                if (value > 0) {
                    numberException(reader,
                                    start,
                                    end,
                                    "Long overflow detected"
                                   );
                }
            }
            return value;
        }
        if (i == end) numberException(reader,
                                      start,
                                      end,
                                      "Digit not found"
                                     );
        for (; i < end; i++) {
            final int ind = buf[i] - 48;
            if (ind < 0 || ind > 9) {
                if (ch == '+' && i > start + 1 && reader.allWhitespace(i,
                                                                       end
                                                                      )) return value;
                else if (ch != '+' && i > start && reader.allWhitespace(i,
                                                                        end
                                                                       )) return value;
                return parseLongGeneric(reader,
                                        start,
                                        end
                                       );
            }
            value = (value << 3) + (value << 1) + ind;
            if (value < 0) {
                numberException(reader,
                                start,
                                end,
                                "Long overflow detected"
                               );
            }
        }
        return value;
    }

    private static long parseLongGeneric(final JsonReader reader,
                                         final int start,
                                         final int end
                                        ) throws IOException {
        final int len = end - start;
        final char[] buf = reader.prepareBuffer(start,
                                                len
                                               );
        if (len > 0 && buf[len - 1] == '.') numberException(reader,
                                                            start,
                                                            end,
                                                            "Number ends with a dot"
                                                           );
        final BigDecimal v = parseNumberGeneric(buf,
                                                len,
                                                reader
                                               );
        if (v.scale() > 0) numberException(reader,
                                           start,
                                           end,
                                           "Expecting long, but found decimal value ",
                                           v
                                          );
        return v.longValue();
    }

    public static BigDecimal parseDecimal(final JsonReader reader) throws IOException {
        final int start = reader.scanNumber();
        int       end   = reader.getCurrentIndex();
        int       len   = end - start;
        if (len > 18) {
            end = reader.findNonWhitespace(end);
            len = end - start;
            if (end == reader.length()) {
                final NumberInfo info = readLongNumber(reader,
                                                       start
                                                      );
                return parseNumberGeneric(info.buffer,
                                          info.length,
                                          reader
                                         );
            }
            else if (len > 18) {
                return parseNumberGeneric(reader.prepareBuffer(start,
                                                               len
                                                              ),
                                          len,
                                          reader
                                         );
            }
        }
        final byte[] buf = reader.buffer;
        final byte   ch  = buf[start];
        if (ch == '-') {
            return parseNegativeDecimal(buf,
                                        reader,
                                        start,
                                        end
                                       );
        }
        return parsePositiveDecimal(buf,
                                    reader,
                                    start,
                                    end
                                   );
    }

    private static BigDecimal parsePositiveDecimal(final byte[] buf,
                                                   final JsonReader reader,
                                                   final int start,
                                                   final int end
                                                  ) throws IOException {
        long value = 0;
        byte ch    = ' ';
        int  i     = start;
        for (; i < end; i++) {
            ch = buf[i];
            if (ch == '.' || ch == 'e' || ch == 'E') break;
            final int ind = ch - 48;
            if (ind < 0 || ind > 9) {
                if (i > start && reader.allWhitespace(i,
                                                      end
                                                     )) return BigDecimal.valueOf(value);
                numberException(reader,
                                start,
                                end,
                                "Unknown digit",
                                (char) ch
                               );
            }
            value = (value << 3) + (value << 1) + ind;
        }
        if (i == start) numberException(reader,
                                        start,
                                        end,
                                        "Digit not found"
                                       );
        else if (i == end) return BigDecimal.valueOf(value);
        else if (ch == '.') {
            i++;
            if (i == end) numberException(reader,
                                          start,
                                          end,
                                          "Number ends with a dot"
                                         );
            int dp = i;
            for (; i < end; i++) {
                ch = buf[i];
                if (ch == 'e' || ch == 'E') break;
                final int ind = ch - 48;
                if (ind < 0 || ind > 9) {
                    if (reader.allWhitespace(i,
                                             end
                                            )) return BigDecimal.valueOf(value,
                                                                         i - dp
                                                                        );
                    numberException(reader,
                                    start,
                                    end,
                                    "Unknown digit",
                                    (char) ch
                                   );
                }
                value = (value << 3) + (value << 1) + ind;
            }
            if (i == end) return BigDecimal.valueOf(value,
                                                    end - dp
                                                   );
            else if (ch == 'e' || ch == 'E') {
                final int ep = i;
                i++;
                ch = buf[i];
                final int exp;
                if (ch == '-') {
                    exp = parseNegativeInt(buf,
                                           reader,
                                           i,
                                           end
                                          );
                }
                else if (ch == '+') {
                    exp = parsePositiveInt(buf,
                                           reader,
                                           i,
                                           end,
                                           1
                                          );
                }
                else {
                    exp = parsePositiveInt(buf,
                                           reader,
                                           i,
                                           end,
                                           0
                                          );
                }
                return BigDecimal.valueOf(value,
                                          ep - dp - exp
                                         );
            }
            return BigDecimal.valueOf(value,
                                      end - dp
                                     );
        }
        else if (ch == 'e' || ch == 'E') {
            i++;
            ch = buf[i];
            final int exp;
            if (ch == '-') {
                exp = parseNegativeInt(buf,
                                       reader,
                                       i,
                                       end
                                      );
            }
            else if (ch == '+') {
                exp = parsePositiveInt(buf,
                                       reader,
                                       i,
                                       end,
                                       1
                                      );
            }
            else {
                exp = parsePositiveInt(buf,
                                       reader,
                                       i,
                                       end,
                                       0
                                      );
            }
            return BigDecimal.valueOf(value,
                                      -exp
                                     );
        }
        return BigDecimal.valueOf(value);
    }

    private static BigDecimal parseNegativeDecimal(final byte[] buf,
                                                   final JsonReader reader,
                                                   final int start,
                                                   final int end
                                                  ) throws IOException {
        long value = 0;
        byte ch    = ' ';
        int  i     = start + 1;
        for (; i < end; i++) {
            ch = buf[i];
            if (ch == '.' || ch == 'e' || ch == 'E') break;
            final int ind = ch - 48;
            if (ind < 0 || ind > 9) {
                if (i > start + 1 && reader.allWhitespace(i,
                                                          end
                                                         )) return BigDecimal.valueOf(value);
                numberException(reader,
                                start,
                                end,
                                "Unknown digit",
                                (char) ch
                               );
            }
            value = (value << 3) + (value << 1) - ind;
        }
        if (i == start + 1) numberException(reader,
                                            start,
                                            end,
                                            "Digit not found"
                                           );
        else if (i == end) return BigDecimal.valueOf(value);
        else if (ch == '.') {
            i++;
            if (i == end) numberException(reader,
                                          start,
                                          end,
                                          "Number ends with a dot"
                                         );
            int dp = i;
            for (; i < end; i++) {
                ch = buf[i];
                if (ch == 'e' || ch == 'E') break;
                final int ind = ch - 48;
                if (ind < 0 || ind > 9) {
                    if (reader.allWhitespace(i,
                                             end
                                            )) return BigDecimal.valueOf(value,
                                                                         i - dp
                                                                        );
                    numberException(reader,
                                    start,
                                    end,
                                    "Unknown digit",
                                    (char) ch
                                   );
                }
                value = (value << 3) + (value << 1) - ind;
            }
            if (i == end) return BigDecimal.valueOf(value,
                                                    end - dp
                                                   );
            else if (ch == 'e' || ch == 'E') {
                final int ep = i;
                i++;
                ch = buf[i];
                final int exp;
                if (ch == '-') {
                    exp = parseNegativeInt(buf,
                                           reader,
                                           i,
                                           end
                                          );
                }
                else if (ch == '+') {
                    exp = parsePositiveInt(buf,
                                           reader,
                                           i,
                                           end,
                                           1
                                          );
                }
                else {
                    exp = parsePositiveInt(buf,
                                           reader,
                                           i,
                                           end,
                                           0
                                          );
                }
                return BigDecimal.valueOf(value,
                                          ep - dp - exp
                                         );
            }
            return BigDecimal.valueOf(value,
                                      end - dp
                                     );
        }
        else if (ch == 'e' || ch == 'E') {
            i++;
            ch = buf[i];
            final int exp;
            if (ch == '-') {
                exp = parseNegativeInt(buf,
                                       reader,
                                       i,
                                       end
                                      );
            }
            else if (ch == '+') {
                exp = parsePositiveInt(buf,
                                       reader,
                                       i,
                                       end,
                                       1
                                      );
            }
            else {
                exp = parsePositiveInt(buf,
                                       reader,
                                       i,
                                       end,
                                       0
                                      );
            }
            return BigDecimal.valueOf(value,
                                      -exp
                                     );
        }
        return BigDecimal.valueOf(value);
    }

    static class NumberInfo {
        final char[] buffer;
        final int length;

        NumberInfo(final char[] buffer,
                   final int length
                  ) {
            this.buffer = buffer;
            this.length = length;
        }
    }


}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy