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

io.stargate.db.schema.NumberCoercion Maven / Gradle / Ivy

There is a newer version: 2.1.0-BETA-19
Show newest version
/*
 * Copyright DataStax, Inc.
 *
 * Please see the included license file for details.
 */
package io.stargate.db.schema;

import static io.stargate.db.schema.NumberCoercion.Result.coercionFailed;
import static io.stargate.db.schema.NumberCoercion.Result.coercionOk;

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

/**
 * We're doing lots of boxing/unboxing here, which is inefficient. This type of validation will be
 * done for every insert of a value, so we should measure performance and improve.
 */
class NumberCoercion {
  private static byte toByteExact(short value) {
    byte narrowed = (byte) value;
    if (narrowed != value) {
      throw new ArithmeticException("short out of byte range");
    }
    return narrowed;
  }

  private static byte toByteExact(int value) {
    byte narrowed = (byte) value;
    if (narrowed != value) {
      throw new ArithmeticException("int out of byte range");
    }
    return narrowed;
  }

  private static byte toByteExact(long value) {
    byte narrowed = (byte) value;
    if (narrowed != value) {
      throw new ArithmeticException("long out of byte range");
    }
    return narrowed;
  }

  private static byte toByteExact(double value) {
    byte narrowed = (byte) value;
    if (narrowed != value) {
      throw new ArithmeticException("double out of byte range");
    }
    return narrowed;
  }

  private static byte toByteExact(float value) {
    byte narrowed = (byte) value;
    if (narrowed != value) {
      throw new ArithmeticException("float out of byte range");
    }
    return narrowed;
  }

  private static short toShortExact(int value) {
    short narrowed = (short) value;
    if (narrowed != value) {
      throw new ArithmeticException("int out of short range");
    }
    return narrowed;
  }

  private static short toShortExact(long value) {
    short narrowed = (short) value;
    if (narrowed != value) {
      throw new ArithmeticException("long out of short range");
    }
    return narrowed;
  }

  private static short toShortExact(double value) {
    short narrowed = (short) value;
    if (narrowed != value) {
      throw new ArithmeticException("double out of short range");
    }
    return narrowed;
  }

  private static short toShortExact(float value) {
    short narrowed = (short) value;
    if (narrowed != value) {
      throw new ArithmeticException("float out of short range");
    }
    return narrowed;
  }

  private static int toIntExact(long value) {
    int narrowed = (int) value;
    if (narrowed != value) {
      throw new ArithmeticException("long out of int range");
    }
    return narrowed;
  }

  private static float toFloatExact(double value) {
    float narrowed = (float) value;
    if (Double.compare(narrowed, value) != 0) {
      throw new ArithmeticException("double out of float range");
    }
    return narrowed;
  }

  private static float toFloatExact(BigDecimal value) {
    float narrowed = value.floatValue();
    if (BigDecimal.valueOf(narrowed).compareTo(value) != 0) {
      throw new ArithmeticException("BigDecimal out of float range");
    }
    return narrowed;
  }

  private static double toDoubleExact(BigDecimal value) {
    double narrowed = value.doubleValue();

    if (BigDecimal.valueOf(narrowed).compareTo(value) != 0) {
      throw new ArithmeticException("BigDecimal out of double range");
    }
    return narrowed;
  }

  private static boolean isFloat(Object value) {
    return value instanceof Float;
  }

  private static boolean isDouble(Object value) {
    return value instanceof Double;
  }

  private static boolean isBigDecimal(Object value) {
    return value instanceof BigDecimal;
  }

  private static boolean isByte(Object value) {
    return value instanceof Byte;
  }

  private static boolean isShort(Object value) {
    return value instanceof Short;
  }

  private static boolean isInt(Object value) {
    return value instanceof Integer;
  }

  private static boolean isLong(Object value) {
    return value instanceof Long;
  }

  private static boolean isBigInt(Object value) {
    return value instanceof BigInteger;
  }

  private static BigInteger toBigInteger(Object value) {
    if (isBigInt(value)) {
      return (BigInteger) value;
    }
    if (isBigDecimal(value)) {
      return ((BigDecimal) value).toBigIntegerExact();
    }
    if (isLong(value)) {
      return BigInteger.valueOf((long) value);
    }
    if (isDouble(value)) {
      return BigInteger.valueOf(((Double) value).longValue());
    }
    if (isInt(value)) {
      return BigInteger.valueOf((int) value);
    }
    if (isFloat(value)) {
      return BigInteger.valueOf(((Float) value).longValue());
    }
    if (isShort(value)) {
      return BigInteger.valueOf((short) value);
    }
    return BigInteger.valueOf((byte) value);
  }

  private static long toLong(Object value) {
    if (isBigInt(value)) {
      return ((BigInteger) value).longValueExact();
    }
    if (isBigDecimal(value)) {
      return ((BigDecimal) value).longValueExact();
    }
    if (isLong(value)) {
      return (long) value;
    }
    if (isDouble(value)) {
      return ((Double) value).longValue();
    }
    if (isInt(value)) {
      return (int) value;
    }
    if (isFloat(value)) {
      return ((Float) value).longValue();
    }
    if (isShort(value)) {
      return (short) value;
    }
    return (byte) value;
  }

  private static int toInt(Object value) {
    if (isBigInt(value)) {
      return ((BigInteger) value).intValueExact();
    }
    if (isBigDecimal(value)) {
      return ((BigDecimal) value).intValueExact();
    }
    if (isLong(value)) {
      return toIntExact((long) value);
    }
    if (isDouble(value)) {
      return ((Double) value).intValue();
    }
    if (isInt(value)) {
      return (int) value;
    }
    if (isFloat(value)) {
      return ((Float) value).intValue();
    }
    if (isShort(value)) {
      return (short) value;
    }
    return (byte) value;
  }

  private static short toShort(Object value) {
    if (isBigInt(value)) {
      return ((BigInteger) value).shortValueExact();
    }
    if (isBigDecimal(value)) {
      return ((BigDecimal) value).shortValueExact();
    }
    if (isLong(value)) {
      return toShortExact((long) value);
    }
    if (isDouble(value)) {
      return toShortExact((double) value);
    }
    if (isInt(value)) {
      return toShortExact((int) value);
    }
    if (isFloat(value)) {
      return toShortExact((float) value);
    }
    if (isShort(value)) {
      return (short) value;
    }
    return (byte) value;
  }

  private static byte toByte(Object value) {
    if (isBigInt(value)) {
      return ((BigInteger) value).byteValueExact();
    }
    if (isBigDecimal(value)) {
      return ((BigDecimal) value).byteValueExact();
    }
    if (isLong(value)) {
      return toByteExact((long) value);
    }
    if (isDouble(value)) {
      return toByteExact((double) value);
    }
    if (isInt(value)) {
      return toByteExact((int) value);
    }
    if (isFloat(value)) {
      return toByteExact((float) value);
    }
    if (isShort(value)) {
      return toByteExact((short) value);
    }
    return (byte) value;
  }

  private static BigDecimal toBigDecimal(Object value) {
    if (isBigDecimal(value)) {
      return (BigDecimal) value;
    }
    if (isDouble(value)) {
      return BigDecimal.valueOf((double) value);
    }
    return BigDecimal.valueOf((float) value);
  }

  private static double toDouble(Object value) {
    if (isBigDecimal(value)) {
      return toDoubleExact((BigDecimal) value);
    }
    if (isDouble(value)) {
      return (double) value;
    }
    return (float) value;
  }

  private static float toFloat(Object value) {
    if (isBigDecimal(value)) {
      return toFloatExact((BigDecimal) value);
    } else if (isDouble(value)) {
      return toFloatExact((double) value);
    }
    return (float) value;
  }

  private static boolean isDecimalNumber(Object value) {
    return (isBigDecimal(value) && ((BigDecimal) value).stripTrailingZeros().scale() > 0)
        || (isDouble(value) && ((double) value % 1 > 0))
        || (isFloat(value) && ((float) value % 1 > 0));
  }

  private static boolean isNaturalNumber(Object value) {
    return isBigInt(value) || isLong(value) || isInt(value) || isShort(value) || isByte(value);
  }

  /**
   * Checks if the given columnType class can accept the given value using custom Narrowing &
   * Widening rules.
   *
   * 

Please note that Narrowing rules are different from the JLS - 5.1.3 Narrowing Primitive * Conversions. * *

The general rule is that narrowing & widening is done only for *

  • 1. Natural numbers: BigInteger > Long > Integer > Short > Byte *
  • 2. Decimal numbers: BigDecimal > Double > Float Narrowing & widening e.g. a BigDecimal to * an Integer is not supported and will return false. * * @param columnType The underlying java type of a column * @param value The value to be applied to the given columnType * @return A {@link Result} object with true if the given columnType can accept the * given value using the above described Narrowing & Widening rules, false * otherwise. {@link Result} also contains the narrowed/widened value. * @throws ArithmeticException in case a value gets out of range when narrowing it to a lower * type. */ public static Result coerceToColumnType(Class columnType, Object value) { if (!(value instanceof Number)) { return coercionFailed(value); } if (columnTypeRequiresNaturalNumber(columnType) && isDecimalNumber(value)) { return coercionFailed(value); } if (columnTypeRequiresDecimalNumber(columnType) && isNaturalNumber(value)) { return coercionFailed(value); } if (columnTypeIsBigInteger(columnType)) { return coercionOk(toBigInteger(value)); } if (columnTypeIsLong(columnType)) { return coercionOk(toLong(value)); } if (columnTypeIsInt(columnType)) { return coercionOk(toInt(value)); } if (columnTypeIsShort(columnType)) { return coercionOk(toShort(value)); } if (columnTypeIsByte(columnType)) { return coercionOk(toByte(value)); } if (columnTypeIsBigDecimal(columnType)) { return coercionOk(toBigDecimal(value)); } if (columnTypeIsDouble(columnType)) { return coercionOk(toDouble(value)); } if (columnTypeIsFloat(columnType)) { return coercionOk(toFloat(value)); } return coercionFailed(value); } private static boolean columnTypeRequiresNaturalNumber(Class columnType) { return columnTypeIsBigInteger(columnType) || columnTypeIsLong(columnType) || columnTypeIsInt(columnType) || columnTypeIsShort(columnType) || columnTypeIsByte(columnType); } private static boolean columnTypeRequiresDecimalNumber(Class columnType) { return columnTypeIsBigDecimal(columnType) || columnTypeIsDouble(columnType) || columnTypeIsFloat(columnType); } private static boolean columnTypeIsFloat(Class columnType) { return Float.class.equals(columnType); } private static boolean columnTypeIsDouble(Class columnType) { return Double.class.equals(columnType); } private static boolean columnTypeIsBigDecimal(Class columnType) { return BigDecimal.class.equals(columnType); } private static boolean columnTypeIsByte(Class columnType) { return Byte.class.equals(columnType); } private static boolean columnTypeIsShort(Class columnType) { return Short.class.equals(columnType); } private static boolean columnTypeIsInt(Class columnType) { return Integer.class.equals(columnType); } private static boolean columnTypeIsLong(Class columnType) { return Long.class.equals(columnType); } private static boolean columnTypeIsBigInteger(Class columnType) { return BigInteger.class.equals(columnType); } static class Result { final boolean columnTypeAcceptsValue; final Object validatedValue; Result(boolean columnTypeAcceptsValue, Object validatedValue) { this.columnTypeAcceptsValue = columnTypeAcceptsValue; this.validatedValue = validatedValue; } public static Result coercionFailed(Object validatedValue) { return new Result(false, validatedValue); } public static Result coercionOk(Object validatedValue) { return new Result(true, validatedValue); } public boolean columnTypeAcceptsValue() { return columnTypeAcceptsValue; } public Object getValidatedValue() { return validatedValue; } } }




  • © 2015 - 2025 Weber Informatics LLC | Privacy Policy