{ *********************************************************************** }
{                                                                         }
{ Delphi Visual Component Library                                         }
{                                                                         }
{ Copyright (c) 2002-2004 Borland Software Corporation                    }
{                                                                         }
{ *********************************************************************** }

unit Borland.Vcl.Convert;

interface

uses
  System.Reflection, SysUtils, Variants, ConvUtils;

type
  TConvert = packed record(IComparable, IConvertible)
  public
    var
      Value: Double;
      ConvType: TConvType;

    class function From(const AValue: Double; const AType: TConvType): TConvert; overload; static;
    class function From(const AValue: string): TConvert; overload; static;

    function IsEmpty: Boolean;
    function TypeName: string;
    function Family: TConvFamily;
    function FamilyName: string;

    class function Compare(const ALeft, ARight: TConvert): Int32; static;
    class function Equals(const Left, Right: TConvert): Boolean; overload; static;
    function Equals(AValue: TObject): Boolean; overload; override;
    function ToString: String; overload; override;

    {IComparable support}
    function CompareTo(ARight: TObject): Int32;

    {IConvertible support}
    function GetTypeCode: TypeCode;
    function ToBoolean(AProvider: IFormatProvider): System.Boolean;
    function ToByte(AProvider: IFormatProvider): System.Byte;
    function ToDateTime(AProvider: IFormatProvider): System.DateTime;
    function ToDecimal(AProvider: IFormatProvider): System.Decimal;
    function ToDouble(AProvider: IFormatProvider): System.Double;
    function ToInt16(AProvider: IFormatProvider): System.Int16;
    function ToInt32(AProvider: IFormatProvider): System.Int32;
    function ToInt64(AProvider: IFormatProvider): System.Int64;
    function ToSByte(AProvider: IFormatProvider): System.SByte;
    function ToSingle(AProvider: IFormatProvider): System.Single;
    function ToString(AProvider: IFormatProvider): System.String; overload;
    function ToUInt16(AProvider: IFormatProvider): System.UInt16;
    function ToUInt32(AProvider: IFormatProvider): System.UInt32;
    function ToUInt64(AProvider: IFormatProvider): System.UInt64;
    function ToChar(AProvider: IFormatProvider): System.Char;
    function ToType(AType: System.Type; AProvider: IFormatProvider): System.Object;

    class function FromObject(AObject: System.Object): TConvert; static;

    class operator Trunc(const AValue: TConvert): Int64;
    class operator Round(const AValue: TConvert): Int64;

    class operator Negative(const AValue: TConvert): TConvert;

    class operator Add(const ALeft, ARight: TConvert): TConvert;
    class operator Subtract(const ALeft, ARight: TConvert): TConvert;
    class operator Multiply(const ALeft: TConvert; ARight: Double): TConvert;
    class operator Divide(const ALeft: TConvert; ARight: Double): TConvert; overload;
    class operator Divide(const ALeft, ARight: TConvert): Double; overload;
    class operator IntDivide(const ALeft: TConvert; ARight: Double): TConvert; overload;
    class operator IntDivide(const ALeft, ARight: TConvert): Integer; overload;

    class operator Equal(const ALeft, ARight: TConvert): Boolean;
    class operator NotEqual(const ALeft, ARight: TConvert): Boolean;
    class operator LessThan(const ALeft, ARight: TConvert): Boolean;
    class operator LessThanOrEqual(const ALeft, ARight: TConvert): Boolean;
    class operator GreaterThan(const ALeft, ARight: TConvert): Boolean;
    class operator GreaterThanOrEqual(const ALeft, ARight: TConvert): Boolean;

    class operator Implicit(const AValue: Variant): TConvert;
    class operator Implicit(const AValue: TConvert): Extended;
    class operator Implicit(const AValue: TConvert): Double;
    class operator Implicit(const AValue: TConvert): Integer;
    class operator Implicit(const AValue: TConvert): Int64;

    function AsType(const AType: TConvType): TConvert;
  end;

implementation

{$RANGECHECKS ON}
{$OVERFLOWCHECKS ON}
{$FINITEFLOAT ON}

uses
  Math, Types, StdConvs, RTLConsts;

{ TConvert }

class function TConvert.From(const AValue: Double; const AType: TConvType): TConvert;
begin
  Result.ConvType := AType;
  Result.Value := AValue;
end;

class function TConvert.From(const AValue: string): TConvert;
begin
  if not TryStrToConvUnit(AValue, Result.Value, Result.ConvType) then
    InvalidCastError(SNotAConversionString);
end;

class operator TConvert.Implicit(const AValue: Variant): TConvert;
begin
  Result := FromObject(TObject(AValue));
end;

class operator TConvert.Implicit(const AValue: TConvert): Extended;
begin
  Result := AValue.Value;
end;

class operator TConvert.Implicit(const AValue: TConvert): Double;
begin
  Result := AValue.Value;
end;

class operator TConvert.Implicit(const AValue: TConvert): Integer;
begin
  Result := Round(AValue.Value);
end;

class operator TConvert.Implicit(const AValue: TConvert): Int64;
begin
  Result := Round(AValue.Value);
end;


class function TConvert.FromObject(AObject: System.Object): TConvert;
begin
  if AObject = nil then
    InvalidCastError(SInvalidCastString)
  else if AObject is TConvert then
    Result := TConvert(AObject)
  else if AObject is DateTime then
    Result := From(DateTime(AObject).ToOADate, tuDays)
  else if AObject is TDateTime then
    Result := From(TDateTime(AObject), tuDays)
  else
    Result := From(String(Variant(AObject)));
end;

class function TConvert.Compare(const ALeft, ARight: TConvert): Int32;
begin
  Result := Integer(ConvUnitCompareValue(ALeft.Value, ALeft.ConvType,
                                         ARight.Value, ARight.ConvType));
end;

class function TConvert.Equals(const Left, Right: TConvert): Boolean;
begin
  Result := Left = Right;
end;

function TConvert.ToString: String;
begin
  Result := ToString(nil);
end;

function TConvert.Equals(AValue: TObject): Boolean;
begin
  Result := (AValue is TConvert) and
            (Self = TConvert(AValue));
end;

function TConvert.CompareTo(ARight: TObject): Integer;
var
  LRight: TConvert;
begin
  if ARight is TConvert then
    LRight := ARight as TConvert
  else
    LRight := TConvert.FromObject(ARight);

  Result := Compare(Self, LRight);
end;

function TConvert.GetTypeCode: TypeCode;
begin
  Result := TypeCode.Object;
end;

function TConvert.ToBoolean(AProvider: IFormatProvider): System.Boolean;
begin
  Result := Value <> 0;
end;

function TConvert.ToByte(AProvider: IFormatProvider): System.Byte;
begin
  Result := Round(Value);
end;

function TConvert.ToDateTime(AProvider: IFormatProvider): System.DateTime;
begin
  if Family = cbTime then
    Result := System.DateTime.FromOADate(ConvertFrom(ConvType, Value))
  else
    Result := System.DateTime.FromOADate(Value);
end;

function TConvert.ToDecimal(AProvider: IFormatProvider): System.Decimal;
begin
  Result := System.Decimal.Create(Value);
end;

function TConvert.ToDouble(AProvider: IFormatProvider): System.Double;
begin
  Result := Value;
end;

function TConvert.ToInt16(AProvider: IFormatProvider): System.Int16;
begin
  Result := Round(Value);
end;

function TConvert.ToInt32(AProvider: IFormatProvider): System.Int32;
begin
  Result := Round(Value);
end;

function TConvert.ToInt64(AProvider: IFormatProvider): System.Int64;
begin
  Result := Round(Value);
end;

function TConvert.ToSByte(AProvider: IFormatProvider): System.SByte;
begin
  Result := Round(Value);
end;

function TConvert.ToSingle(AProvider: IFormatProvider): System.Single;
begin
  Result := Value;
end;

function TConvert.ToString(AProvider: IFormatProvider): System.string;
begin
  Result := Format('%s %s', [System.Convert.ToString(Value, AProvider),
    TypeName]);
end;

function TConvert.ToUInt16(AProvider: IFormatProvider): System.UInt16;
begin
  Result := Round(Value);
end;

function TConvert.ToUInt32(AProvider: IFormatProvider): System.UInt32;
begin
  Result := Round(Value);
end;

function TConvert.ToUInt64(AProvider: IFormatProvider): System.UInt64;
begin
  Result := System.Convert.ToUInt64(Value);
end;

function TConvert.ToChar(AProvider: IFormatProvider): System.Char;
begin
  Result := ToString(AProvider)[1];
end;

function TConvert.ToType(AType: System.Type; AProvider: IFormatProvider): System.Object;
begin
  Result := self;
  case System.Type.GetTypeCode(AType) of
    TypeCode.Boolean:
      Result := TObject(ToBoolean(AProvider));
    TypeCode.Byte:
      Result := TObject(ToByte(AProvider));
    TypeCode.Char:
      Result := TObject(ToChar(AProvider));
    TypeCode.DateTime:
      Result := TObject(ToDateTime(AProvider));
    TypeCode.Decimal:
      Result := TObject(ToDecimal(AProvider));
    TypeCode.Double:
      Result := TObject(ToDouble(AProvider));
    TypeCode.Empty:
      Result := nil;
    TypeCode.Int16:
      Result := TObject(ToInt16(AProvider));
    TypeCode.Int32:
      Result := TObject(ToInt32(AProvider));
    TypeCode.Int64:
      Result := TObject(ToInt64(AProvider));
    TypeCode.SByte:
      Result := TObject(ToSByte(AProvider));
    TypeCode.Single:
      Result := TObject(ToSingle(AProvider));
    TypeCode.String:
      Result := TObject(ToString(AProvider));
    TypeCode.UInt16:
      Result := TObject(ToUInt16(AProvider));
    TypeCode.UInt32:
      Result := TObject(ToUInt32(AProvider));
    TypeCode.UInt64:
      Result := TObject(ToUInt64(AProvider));
    TypeCode.Object:
      if not AType.IsInstanceOfType(self) then
        raise System.InvalidCastException.Create;
  else
    raise System.InvalidCastException.Create;
  end;
end;

class operator TConvert.Trunc(const AValue: TConvert): Int64;
begin
  Result := Trunc(AValue.Value);
end;

class operator TConvert.Round(const AValue: TConvert): Int64;
begin
  Result := Round(AValue.Value);
end;

class operator TConvert.Negative(const AValue: TConvert): TConvert;
begin
  Result.Value := -AValue.Value;
  Result.ConvType := AValue.ConvType;
end;

class operator TConvert.Add(const ALeft, ARight: TConvert): TConvert;
begin
  Result.Value := ConvUnitInc(ALeft.Value, ALeft.ConvType, ARight.Value, ARight.ConvType);
  Result.ConvType := ALeft.ConvType;
end;

class operator TConvert.Subtract(const ALeft, ARight: TConvert): TConvert;
begin
  Result.Value := ConvUnitDec(ALeft.Value, ALeft.ConvType, ARight.Value, ARight.ConvType);
  Result.ConvType := ALeft.ConvType;
end;

class operator TConvert.Multiply(const ALeft: TConvert; ARight: Double): TConvert;
begin
  Result.Value := ALeft.Value * ARight;
  Result.ConvType := ALeft.ConvType;
end;

class operator TConvert.Divide(const ALeft, ARight: TConvert): Double;
begin
  Result := ALeft.Value / Convert(ARight.Value, ARight.ConvType, ALeft.ConvType);
end;

class operator TConvert.Divide(const ALeft: TConvert; ARight: Double): TConvert;
begin
  Result.Value := ALeft.Value / ARight;
  Result.ConvType := ALeft.ConvType;
end;

class operator TConvert.IntDivide(const ALeft, ARight: TConvert): Integer;
begin
  Result := Trunc(ALeft.Value / ARight.Value);
end;

class operator TConvert.IntDivide(const ALeft: TConvert; ARight: Double): TConvert;
begin
  Result.Value := Trunc(ALeft.Value / ARight);
  Result.ConvType := ALeft.ConvType;
end;

class operator TConvert.Equal(const ALeft, ARight: TConvert): Boolean;
begin
  Result := ConvUnitSameValue(ALeft.Value, ALeft.ConvType, ARight.Value, ARight.ConvType);
end;

class operator TConvert.NotEqual(const ALeft, ARight: TConvert): Boolean;
begin
  Result := not (ALeft = ARight);
end;

class operator TConvert.LessThan(const ALeft, ARight: TConvert): Boolean;
begin
  Result := ConvUnitCompareValue(ALeft.Value, ALeft.ConvType,
    ARight.Value, ARight.ConvType) = LessThanValue;
end;

class operator TConvert.LessThanOrEqual(const ALeft, ARight: TConvert): Boolean;
begin
  Result := ConvUnitCompareValue(ALeft.Value, ALeft.ConvType,
    ARight.Value, ARight.ConvType) <> GreaterThanValue;
end;

class operator TConvert.GreaterThan(const ALeft, ARight: TConvert): Boolean;
begin
  Result := ConvUnitCompareValue(ALeft.Value, ALeft.ConvType,
    ARight.Value, ARight.ConvType) = GreaterThanValue;
end;

class operator TConvert.GreaterThanOrEqual(const ALeft, ARight: TConvert): Boolean;
begin
  Result := ConvUnitCompareValue(ALeft.Value, ALeft.ConvType,
    ARight.Value, ARight.ConvType) <> LessThanValue;
end;

function TConvert.AsType(const AType: TConvType): TConvert;
begin
  Result.Value := Convert(Value, ConvType, AType);
  Result.ConvType := AType;
end;


function TConvert.TypeName: string;
begin
  Result := ConvTypeToDescription(ConvType);
end;

function TConvert.Family: TConvFamily;
begin
  Result := ConvTypeToFamily(ConvType);
end;

function TConvert.FamilyName: string;
begin
  Result := ConvFamilyToDescription(ConvTypeToFamily(ConvType));
end;

function TConvert.IsEmpty: Boolean;
begin
  Result := ConvType = CIllegalConvType;
end;

end.

