/**
   * Tell whether this numeric type can be stored into from the given type.
   *
   * @param thisType The TypeId of this type
   * @param otherType The TypeId of the other type.
   * @param cf A ClassFactory
   */
  public boolean numberStorable(TypeId thisType, TypeId otherType, ClassFactory cf) {
    /*
     ** Numbers can be stored into from other number types.
     ** Also, user types with compatible classes can be stored into numbers.
     */
    if ((otherType.isNumericTypeId()) || (otherType.isBooleanTypeId())) return true;

    /*
     ** If the other type is user-defined, use the java types to determine
     ** assignability.
     */
    return userTypeStorable(thisType, otherType, cf);
  }
  /**
   * Determine whether thisType is storable in otherType due to otherType being a user type.
   *
   * @param thisType The TypeId of the value to be stored
   * @param otherType The TypeId of the value to be stored in
   * @return true if thisType is storable in otherType
   */
  protected boolean userTypeStorable(TypeId thisType, TypeId otherType, ClassFactory cf) {
    /*
     ** If the other type is user-defined, use the java types to determine
     ** assignability.
     */
    if (otherType.userType()) {
      return cf.getClassInspector()
          .assignableTo(
              thisType.getCorrespondingJavaTypeName(), otherType.getCorrespondingJavaTypeName());
    }

    return false;
  }
  /**
   * Tell whether this numeric type can be converted to the given type.
   *
   * @param otherType The TypeId of the other type.
   * @param forDataTypeFunction was this called from a scalarFunction like CHAR() or DOUBLE()
   */
  public boolean numberConvertible(TypeId otherType, boolean forDataTypeFunction) {

    // Can't convert numbers to long types
    if (otherType.isLongConcatableTypeId()) return false;

    // Numbers can only be converted to other numbers,
    // and CHAR, (not VARCHARS or LONGVARCHAR).
    // Only with the CHAR() or VARCHAR()function can they be converted.
    boolean retval =
        ((otherType.isNumericTypeId()) || (otherType.isBooleanTypeId()) || (otherType.userType()));

    // For CHAR  Conversions, function can convert
    // Floating types
    if (forDataTypeFunction)
      retval = retval || (otherType.isFixedStringTypeId() && (getTypeId().isFloatingPointTypeId()));

    retval = retval || (otherType.isFixedStringTypeId() && (!getTypeId().isFloatingPointTypeId()));

    return retval;
  }
 /**
  * Tell whether this type (numeric) is compatible with the given type.
  *
  * @param otherType The TypeId of the other type.
  */
 public boolean compatible(TypeId otherType) {
   // Numbers can only be compatible with other numbers.
   return (otherType.isNumericTypeId());
 }
  /**
   * @see TypeCompiler#resolveArithmeticOperation
   * @exception StandardException Thrown on error
   */
  public DataTypeDescriptor resolveArithmeticOperation(
      DataTypeDescriptor leftType, DataTypeDescriptor rightType, String operator)
      throws StandardException {
    NumericTypeCompiler higherTC;
    DataTypeDescriptor higherType;
    boolean nullable;
    int precision, scale, maximumWidth;

    /*
     ** Check the right type to be sure it's a number.  By convention,
     ** we call this method off the TypeId of the left operand, so if
     ** we get here, we know the left operand is a number.
     */
    if (SanityManager.DEBUG)
      SanityManager.ASSERT(
          leftType.getTypeId().isNumericTypeId(),
          "The left type is supposed to be a number because we're resolving an arithmetic operator");

    TypeId leftTypeId = leftType.getTypeId();
    TypeId rightTypeId = rightType.getTypeId();

    boolean supported = true;

    if (!(rightTypeId.isNumericTypeId())) {
      supported = false;
    }

    if (TypeCompiler.MOD_OP.equals(operator)) {
      switch (leftTypeId.getJDBCTypeId()) {
        case java.sql.Types.TINYINT:
        case java.sql.Types.SMALLINT:
        case java.sql.Types.INTEGER:
        case java.sql.Types.BIGINT:
          break;
        default:
          supported = false;
          break;
      }
      switch (rightTypeId.getJDBCTypeId()) {
        case java.sql.Types.TINYINT:
        case java.sql.Types.SMALLINT:
        case java.sql.Types.INTEGER:
        case java.sql.Types.BIGINT:
          break;
        default:
          supported = false;
          break;
      }
    }

    if (!supported) {
      throw StandardException.newException(
          SQLState.LANG_BINARY_OPERATOR_NOT_SUPPORTED,
          operator,
          leftType.getTypeId().getSQLTypeName(),
          rightType.getTypeId().getSQLTypeName());
    }

    /*
     ** Take left as the higher precedence if equal
     */
    if (rightTypeId.typePrecedence() > leftTypeId.typePrecedence()) {
      higherType = rightType;
      higherTC = (NumericTypeCompiler) getTypeCompiler(rightTypeId);
    } else {
      higherType = leftType;
      higherTC = (NumericTypeCompiler) getTypeCompiler(leftTypeId);
    }

    /* The calculation of precision and scale should be based upon
     * the type with higher precedence, which is going to be the result
     * type, this is also to be consistent with maximumWidth.  Beetle 3906.
     */
    precision = higherTC.getPrecision(operator, leftType, rightType);
    scale = higherTC.getScale(operator, leftType, rightType);

    if (higherType.getTypeId().isDecimalTypeId()) {
      maximumWidth = (scale > 0) ? precision + 3 : precision + 1;

      /*
       ** Be careful not to overflow
       */
      if (maximumWidth < precision) {
        maximumWidth = Integer.MAX_VALUE;
      }
    } else {
      maximumWidth = higherType.getMaximumWidth();
    }

    /* The result is nullable if either side is nullable */
    nullable = leftType.isNullable() || rightType.isNullable();

    /*
     ** The higher type does not have the right nullability.  Create a
     ** new DataTypeDescriptor that has the correct type and nullability.
     **
     ** It's OK to call the implementation of the DataTypeDescriptorFactory
     ** here, because we're in the same package.
     */
    return new DataTypeDescriptor(higherType.getTypeId(), precision, scale, nullable, maximumWidth);
  }