content: /*
   * Licensed to the Apache Software Foundation (ASF) under one or more
   * contributor license agreements.  See the NOTICE file distributed with
   * this work for additional information regarding copyright ownership.
   * The ASF licenses this file to you 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 org.apache.calcite.linq4j.tree;
  
  import org.checkerframework.checker.nullness.qual.Nullable;
  import org.joou.UByte;
  import org.joou.UInteger;
  import org.joou.ULong;
  import org.joou.UShort;
  import org.joou.Unsigned;
  
  import java.lang.reflect.Type;
  import java.math.BigDecimal;
  import java.math.BigInteger;
  import java.math.RoundingMode;
  
  /** Support for compiling unsigned types.
   * This class has a role similar to {@link Primitive} */
  public enum UnsignedType {
    UBYTE,
    USHORT,
    UINT,
    ULONG;
  
    public static @Nullable UnsignedType of(Type type) {
      if (type == UByte.class) {
        return UBYTE;
      } else if (type == UShort.class) {
        return USHORT;
      } else if (type == UInteger.class) {
        return UINT;
      } else if (type == ULong.class) {
        return ULONG;
      } else {
        return null;
      }
    }
  
    public String toJavaTypeName() {
      switch (this) {
      case UBYTE:
        return UByte.class.getSimpleName();
      case USHORT:
        return UShort.class.getSimpleName();
      case UINT:
        return UInteger.class.getSimpleName();
      case ULONG:
        return ULong.class.getSimpleName();
      default:
        throw new AssertionError("Unexpected unsigned type " + this);
      }
    }
  
    /** Get the name of the function that casts a value of type Object
     * to the current type. */
    public String getConvertFunctionName() {
      return "to" + this.toJavaTypeName();
    }
  
    public static BigInteger toBigInteger(ULong value) {
      // This function is missing from the ULong class
      long l = value.longValue();
      if (l >= 0L) {
        return BigInteger.valueOf(l);
      }
      return BigInteger.valueOf(l & Long.MAX_VALUE).add(ULong.MAX_VALUE_LONG);
    }
  
    // Convert a value to an unsigned type
  
    // The following methods are called in code generated by EnumUtils.convert
  
    // UByte
    public static UByte toUByte(byte n) {
      return toUByteNonNull(n, RoundingMode.DOWN);
    }
  
    public static UByte toUByte(short n) {
      return toUByteNonNull(n, RoundingMode.DOWN);
    }
  
    public static UByte toUByte(int n) {
      return toUByteNonNull(n, RoundingMode.DOWN);
    }
  
    public static UByte toUByte(long n) {
      return toUByteNonNull(n, RoundingMode.DOWN);
    }
  
    public static UByte toUByte(float n) {
      return toUByteNonNull(n, RoundingMode.DOWN);
    }
  
    public static UByte toUByte(double n) {
      return toUByteNonNull(n, RoundingMode.DOWN);
    }
  
    private static UByte toUByteNonNull(Number n, RoundingMode mode) {
      if (n instanceof BigDecimal) {
        BigDecimal d = ((BigDecimal) n).setScale(0, mode);
        return Unsigned.ubyte(d.longValueExact());
      } else if (n instanceof Byte || n instanceof Short || n instanceof Integer
          || n instanceof Long || n instanceof Float || n instanceof Double
          || n instanceof UByte || n instanceof UShort
          || n instanceof UInteger || n instanceof ULong) {
        // Using long ensures a range check
        return Unsigned.ubyte(n.longValue());
      }
      throw new IllegalArgumentException("Unexpected Number value " + n.getClass().getSimpleName());
    }
  
    public static @Nullable UByte toUByte(@Nullable Number n, RoundingMode mode) {
      if (n == null) {
        return null;
      }
      return toUByteNonNull(n, mode);
    }
  
    public static @Nullable UByte toUByte(@Nullable String s, RoundingMode unused) {
      if (s == null) {
        return null;
      }
      return Unsigned.ubyte(s);
    }
  
    // To UShort
  
    public static UShort toUShort(byte n) {
      return toUShortNonNull(n, RoundingMode.DOWN);
    }
  
    public static UShort toUShort(short n) {
      return toUShortNonNull(n, RoundingMode.DOWN);
    }
  
    public static UShort toUShort(int n) {
      return toUShortNonNull(n, RoundingMode.DOWN);
    }
  
    public static UShort toUShort(long n) {
      return toUShortNonNull(n, RoundingMode.DOWN);
    }
  
    public static UShort toUShort(float n) {
      return toUShortNonNull(n, RoundingMode.DOWN);
    }
  
    public static UShort toUShort(double n) {
      return toUShortNonNull(n, RoundingMode.DOWN);
    }
  
    private static UShort toUShortNonNull(Number n, RoundingMode mode) {
      if (n instanceof BigDecimal) {
        BigDecimal d = ((BigDecimal) n).setScale(0, mode);
        return Unsigned.ushort(d.intValueExact());
      } else if (n instanceof Byte || n instanceof Short || n instanceof Integer
          || n instanceof Long || n instanceof Float || n instanceof Double
          || n instanceof UByte || n instanceof UShort
          || n instanceof UInteger || n instanceof ULong) {
        long value = n.longValue();
        // ushort(long) is missing from the Unsigned class
        if (value < 0 || value > 65535) {
          throw new NumberFormatException("Value is out of range : " + n);
        }
        return Unsigned.ushort(n.intValue());
      }
      throw new IllegalArgumentException("Unexpected Number value " + n.getClass().getSimpleName());
    }
  
    public static @Nullable UShort toUShort(@Nullable Number n, RoundingMode mode) {
      if (n == null) {
        return null;
      }
      return toUShortNonNull(n, mode);
    }
  
    public static @Nullable UShort toUShort(@Nullable String s, RoundingMode unused) {
      if (s == null) {
        return null;
      }
      return Unsigned.ushort(s);
    }
  
    // UInteger
    public static UInteger toUInteger(byte n) {
      // Casting to long forces a range check
      return toUIntegerNonNull(n, RoundingMode.DOWN);
    }
  
    public static UInteger toUInteger(short n) {
      return toUIntegerNonNull(n, RoundingMode.DOWN);
    }
  
    public static UInteger toUInteger(int n) {
      return toUIntegerNonNull(n, RoundingMode.DOWN);
    }
  
    public static UInteger toUInteger(long n) {
      return toUIntegerNonNull(n, RoundingMode.DOWN);
    }
  
    public static UInteger toUInteger(float n) {
      return toUIntegerNonNull(n, RoundingMode.DOWN);
    }
  
    public static UInteger toUInteger(double n) {
      return toUIntegerNonNull(n, RoundingMode.DOWN);
    }
  
    private static UInteger toUIntegerNonNull(Number n, RoundingMode mode) {
      if (n instanceof BigDecimal) {
        BigDecimal d = ((BigDecimal) n).setScale(0, mode);
        return Unsigned.uint(d.longValueExact());
      } else if (n instanceof Byte || n instanceof Short || n instanceof Integer
          || n instanceof Long || n instanceof Float || n instanceof Double
          || n instanceof UByte || n instanceof UShort
          || n instanceof UInteger || n instanceof ULong) {
        // Using long ensures a range check
        return Unsigned.uint(n.longValue());
      }
      throw new IllegalArgumentException("Unexpected Number value " + n.getClass().getSimpleName());
    }
  
    public static @Nullable UInteger toUInteger(@Nullable Number n, RoundingMode mode) {
      if (n == null) {
        return null;
      }
      return toUIntegerNonNull(n, mode);
    }
  
    public static @Nullable UInteger toUInteger(@Nullable String s, RoundingMode unused) {
      if (s == null) {
        return null;
      }
      return Unsigned.uint(s);
    }
  
    // ULong
    public static ULong toULong(byte n) {
      // Casting to long forces a range check
      return toULongNonNull(n, RoundingMode.DOWN);
    }
  
    public static ULong toULong(short n) {
      return toULongNonNull(n, RoundingMode.DOWN);
    }
  
    public static ULong toULong(int n) {
      return toULongNonNull(n, RoundingMode.DOWN);
    }
  
    public static ULong toULong(long n) {
      return toULongNonNull(n, RoundingMode.DOWN);
    }
  
    public static ULong toULong(float n) {
      return toULongNonNull(n, RoundingMode.DOWN);
    }
  
    public static ULong toULong(double n) {
      return toULongNonNull(n, RoundingMode.DOWN);
    }
  
    private static ULong toULongNonNull(Number n, RoundingMode mode) {
      if (n instanceof BigDecimal) {
        BigDecimal d = ((BigDecimal) n).setScale(0, mode);
        return Unsigned.ulong(d.toBigIntegerExact());
      } else if (n instanceof Byte || n instanceof Short || n instanceof Integer
          || n instanceof Long || n instanceof Float || n instanceof Double
          || n instanceof UByte || n instanceof UShort || n instanceof UInteger) {
        long value = n.longValue();
        if (value < 0) {
          throw new NumberFormatException("Value is out of range : " + value);
        }
        return Unsigned.ulong(value);
      } else if (n instanceof ULong) {
        return (ULong) n;
      }
      throw new IllegalArgumentException("Unexpected Number value " + n.getClass().getSimpleName());
    }
  
    public static @Nullable ULong toULong(@Nullable Number n, RoundingMode mode) {
      if (n == null) {
        return null;
      }
      return toULongNonNull(n, mode);
    }
  
    public static @Nullable ULong toULong(@Nullable String s, RoundingMode unused) {
      if (s == null) {
        return null;
      }
      return Unsigned.ulong(s);
    }
    public static @Nullable UShort toUShort(Object o) {
      if (o == null) {
        return null;
      }
      if (o instanceof Number) {
        return UShort.valueOf(((Number) o).intValue());
      }
      throw new IllegalArgumentException("Cannot cast to UShort: " + o.getClass());
    }
  
    public static @Nullable UByte toUByte(Object o) {
      if (o == null) {
        return null;
      }
      if (o instanceof Number) {
        return UByte.valueOf(((Number) o).shortValue());
      }
      throw new IllegalArgumentException("Cannot cast to UByte: " + o.getClass());
    }
  
    public static @Nullable UInteger toUInteger(Object o) {
      if (o instanceof Number) {
        return UInteger.valueOf(((Number) o).longValue());
      }
      throw new IllegalArgumentException("Cannot cast to UInteger: " + o.getClass());
    }
  
    public static @Nullable ULong toULong(Object o) {
      if (o instanceof Number) {
        return ULong.valueOf(((Number) o).longValue());
      }
      throw new IllegalArgumentException("Cannot cast to ULong: " + o.getClass());
    }
  
  }
  