unit XData.Model.Types;

{$I XData.Inc}

interface

uses
  SysUtils, Bcl.SysUtils,
  Bcl.Collections.Common,
  XData.Model.Classes;

type
  TXDataBinaryType = class(TXDataPrimitiveType)
  strict protected
    function GetName: string; override;
    function GetTypeKind: TXTypeKind; override;
  public
    function ValueToLiteral(Value: TBytes; var Literal: string): boolean;
    function LiteralToValue(const Literal: string; var Value: TBytes): boolean;
  end;

  TXDataBooleanType = class(TXDataPrimitiveType)
  strict protected
    function GetName: string; override;
    function GetTypeKind: TXTypeKind; override;
  public
    function ValueToLiteral(Value: Boolean; var Literal: string): boolean;
    function LiteralToValue(const Literal: string; var Value: boolean): boolean;
  end;

  TXDataByteType = class(TXDataPrimitiveType)
  strict protected
    function GetName: string; override;
    function GetTypeKind: TXTypeKind; override;
  public
    function ValueToLiteral(Value: byte; var Literal: string): boolean;
    function LiteralToValue(const Literal: string; var Value: byte): boolean;
  end;

  TXDataDateTimeType = class(TXDataPrimitiveType)
  strict protected
    function GetName: string; override;
    function GetTypeKind: TXTypeKind; override;
  public
    function ValueToLiteral(Value: TDateTime; var Literal: string): boolean;
    function LiteralToValue(const Literal: string; var Value: TDateTime): boolean;
  end;

  TXDataDateType = class(TXDataPrimitiveType)
  strict protected
    function GetName: string; override;
    function GetTypeKind: TXTypeKind; override;
  public
    function ValueToLiteral(Value: TDate; var Literal: string): boolean;
    function LiteralToValue(const Literal: string; var Value: TDate): boolean;
  end;

  TXDataCurrencyType = class(TXDataPrimitiveType)
  strict protected
    function GetName: string; override;
    function GetTypeKind: TXTypeKind; override;
  {$IFNDEF PAS2JS}
  strict private
    FFormatSettings: TFormatSettings;
  public
    procedure AfterConstruction; override;
    function ValueToLiteral(Value: Currency; var Literal: string): boolean;
    function LiteralToValue(const Literal: string; var Value: Currency): boolean;
  {$ENDIF}
  end;

  TXDataDoubleType = class(TXDataPrimitiveType)
  strict protected
    function GetName: string; override;
    function GetTypeKind: TXTypeKind; override;
  public
    function ValueToLiteral(Value: Double; var Literal: string): boolean;
    function LiteralToValue(const Literal: string; var Value: double): boolean;
  end;

  TXDataGuidType = class(TXDataPrimitiveType)
  strict protected
    function GetName: string; override;
    function GetTypeKind: TXTypeKind; override;
  public
    {$IFDEF PAS2JS}
    function ValueToLiteral(Value: string; var Literal: string): boolean;
    function LiteralToValue(const Literal: string; var Value: string): boolean;
    {$ELSE}
    function ValueToLiteral(Value: TGuid; var Literal: string): boolean;
    function LiteralToValue(const Literal: string; var Value: TGuid): boolean;
    {$ENDIF}
  end;

  TXDataVariantType = class(TXDataPrimitiveType)
  strict protected
    function GetName: string; override;
    function GetTypeKind: TXTypeKind; override;
  public
  {$IFNDEF PAS2JS}
    function ValueToLiteral(Value: Variant; var Literal: string): boolean;
    function LiteralToValue(const Literal: string; var Value: Variant): boolean;
  {$ELSE}
    function ValueToLiteral(Value: JSValue; var Literal: string): boolean;
    function LiteralToValue(const Literal: string; var Value: JSValue): boolean;
  {$ENDIF}
  end;

  TXDataInt16Type = class(TXDataPrimitiveType)
  strict protected
    function GetName: string; override;
    function GetTypeKind: TXTypeKind; override;
  public
    function ValueToLiteral(Value: Integer; var Literal: string): boolean;
    function LiteralToValue(const Literal: string; var Value: Integer): boolean;
  end;

  TXDataInt32Type = class(TXDataPrimitiveType)
  strict protected
    function GetName: string; override;
    function GetTypeKind: TXTypeKind; override;
  public
    function ValueToLiteral(const Value: Integer; var Literal: string): boolean;
    function LiteralToValue(const Literal: string; var Value: integer): boolean;
  end;

  TXDataInt64Type = class(TXDataPrimitiveType)
  strict protected
    function GetName: string; override;
    function GetTypeKind: TXTypeKind; override;
  public
    {$IFDEF PAS2JS}
    function ValueToLiteral(const Value: Integer; var Literal: string): boolean;
    function LiteralToValue(const Literal: string; var Value: integer): boolean;
    {$ELSE}
    function ValueToLiteral(Value: Int64; var Literal: string): boolean;
    function LiteralToValue(const Literal: string; var Value: Int64): boolean;
    {$ENDIF}
  end;

  TXDataSByteType = class(TXDataPrimitiveType)
  strict protected
    function GetName: string; override;
    function GetTypeKind: TXTypeKind; override;
  public
    function ValueToLiteral(Value: ShortInt; var Literal: string): boolean;
    function LiteralToValue(const Literal: string; var Value: ShortInt): boolean;
  end;

  TXDataStringType = class(TXDataPrimitiveType)
  strict protected
    function GetName: string; override;
    function GetTypeKind: TXTypeKind; override;
  public
    function ValueToLiteral(const Value: string; var Literal: string): boolean;
    function LiteralToValue(const Literal: string; var Value: string): boolean;
  end;

  TXDataTimeType = class(TXDataPrimitiveType)
  strict protected
    function GetName: string; override;
    function GetTypeKind: TXTypeKind; override;
  public
    function ValueToLiteral(Value: TTime; var Literal: string): boolean;
    function LiteralToValue(const Literal: string; var Value: TTime): boolean;
  end;

  TXDataStreamType = class(TXDataPrimitiveType)
  strict protected
    function GetName: string; override;
    function GetTypeKind: TXTypeKind; override;
  end;

  TXDataTypeMap = TStringMap;

  TXDataPrimitiveTypes = class
  strict private
    class var
      FInstance: TXDataPrimitiveTypes;
  strict private
    FTypes: TXDataTypeMap;
    FBinaryType: TXDataBinaryType;
    FBooleanType: TXDataBooleanType;
    FByteType: TXDataByteType;
    FDateTimeType: TXDataDateTimeType;
    FDateType: TXDataDateType;
    FDoubleType: TXDataDoubleType;
    FInt16Type: TXDataInt16Type;
    FInt32Type: TXDataInt32Type;
    FInt64Type: TXDataInt64Type;
    FSByteType: TXDataSByteType;
    FStringType: TXDataStringType;
    FTimeType: TXDataTimeType;
    FStreamType: TXDataStreamType;
    FGuidType: TXDataGuidType;
    FCurrencyType: TXDataCurrencyType;
    FVariantType: TXDataVariantType;
    procedure PrivateCreate;
    procedure PrivateDestroy;
    procedure AddType(const Key: string; Value: TXDataPrimitiveType);
  public
    class function Instance: TXDataPrimitiveTypes;
    class procedure ClassDestroy;
    function Find(const AName: string): TXDataPrimitiveType; overload;
    destructor Destroy; override;
    property BinaryType: TXDataBinaryType read FBinaryType;
    property BooleanType: TXDataBooleanType read FBooleanType;
    property ByteType: TXDataByteType read FByteType;
    property DateTimeType: TXDataDateTimeType read FDateTimeType;
    property DateType: TXDataDateType read FDateType;
    property DoubleType: TXDataDoubleType read FDoubleType;
    property Int16Type: TXDataInt16Type read FInt16Type;
    property Int32Type: TXDataInt32Type read FInt32Type;
    property Int64Type: TXDataInt64Type read FInt64Type;
    property SByteType: TXDataSByteType read FSByteType;
    property StringType: TXDataStringType read FStringType;
    property TimeType: TXDataTimeType read FTimeType;
    property StreamType: TXDataStreamType read FStreamType;
    property GuidType: TXDataGuidType read FGuidType;
    property CurrencyType: TXDataCurrencyType read FCurrencyType;
    property VariantType: TXDataVariantType read FVariantType;
  end;

// helper methods for some base types. Should refactor this to a better place later
function LiteralQuote(const Str: string): string;
function LiteralUnquote(var Str: string): boolean;

implementation

uses
  {$IFDEF PAS2JS}
  JS,
  {$ELSE}
  System.Variants,
  {$ENDIF}
  Bcl.Utils;

function LiteralUnquote(var Str: string): boolean;
var
  Len: integer;
begin
  Len := Length(Str);
  Result := (Len >= 2) and (Str[1] = '''') and (Str[Len] = '''');
  if Result then
    Str := Copy(Str, 2, Len - 2);
end;

function LiteralQuote(const Str: string): string;
begin
  Result := '''' + Str + '''';
end;

{ TXDataPrimitiveTypes }

procedure TXDataPrimitiveTypes.AddType(const Key: string;
  Value: TXDataPrimitiveType);
begin
  FTypes.Add(Key, Value);
end;

destructor TXDataPrimitiveTypes.Destroy;
begin
  PrivateDestroy;
  inherited;
end;

class procedure TXDataPrimitiveTypes.ClassDestroy;
begin
  if FInstance <> nil then
  begin
    FInstance.Free;
    FInstance := nil;
  end;
end;

function TXDataPrimitiveTypes.Find(const AName: string): TXDataPrimitiveType;
begin
  if FTypes.ContainsKey(Lowercase(AName)) then
    Result := TXDataPrimitiveType(FTypes[Lowercase(AName)])
  else
    Result := nil;
end;

class function TXDataPrimitiveTypes.Instance: TXDataPrimitiveTypes;
begin
  if FInstance = nil then
  begin
    FInstance := TXDataPrimitiveTypes.Create;
    FInstance.PrivateCreate;
  end;
  Result := FInstance;
end;

procedure TXDataPrimitiveTypes.PrivateCreate;
begin
  FTypes := TXDataTypeMap.Create(False);

  FBinaryType := TXDataBinaryType.Create;
  FBooleanType := TXDataBooleanType.Create;
  FByteType := TXDataByteType.Create;
  FDateTimeType := TXDataDateTimeType.Create;
  FDateType := TXDataDateType.Create;
  FDoubleType := TXDataDoubleType.Create;
  FInt16Type := TXDataInt16Type.Create;
  FInt32Type := TXDataInt32Type.Create;
  FInt64Type := TXDataInt64Type.Create;
  FSByteType := TXDataSByteType.Create;
  FStringType := TXDataStringType.Create;
  FTimeType := TXDataTimeType.Create;
  FStreamType := TXDataStreamType.Create;
  FGuidType := TXDataGuidType.Create;
  FCurrencyType := TXDataCurrencyType.Create;
  FVariantType := TXDataVariantType.Create;

  AddType(Lowercase(FBinaryType.Name), FBinaryType);
  AddType(Lowercase(FBooleanType.Name), FBooleanType);
  AddType(Lowercase(FByteType.Name), FByteType);
  AddType(Lowercase(FDateTimeType.Name), FDateTimeType);
  AddType(Lowercase(FDateType.Name), FDateType);
  AddType(Lowercase(FDoubleType.Name), FDoubleType);
  AddType(Lowercase(FInt16Type.Name), FInt16Type);
  AddType(Lowercase(FInt32Type.Name), FInt32Type);
  AddType(Lowercase(FInt64Type.Name), FInt64Type);
  AddType(Lowercase(FSByteType.Name), FSByteType);
  AddType(Lowercase(FStringType.Name), FStringType);
  AddType(Lowercase(FTimeType.Name), FTimeType);
  AddType(Lowercase(FStreamType.Name), FStreamType);
  AddType(Lowercase(FGuidType.Name), FGuidType);
  AddType(Lowercase(FCurrencyType.Name), FCurrencyType);
  AddType(Lowercase(FVariantType.Name), FVariantType);
end;

procedure TXDataPrimitiveTypes.PrivateDestroy;
begin
  FTypes.Free;
  FVariantType.Free;
  FCurrencyType.Free;
  FGuidType.Free;
  FBinaryType.Free;
  FBooleanType.Free;
  FByteType.Free;
  FDateTimeType.Free;
  FDateType.Free;
  FDoubleType.Free;
  FInt16Type.Free;
  FInt32Type.Free;
  FInt64Type.Free;
  FSByteType.Free;
  FStringType.Free;
  FTimeType.Free;
  FStreamType.Free;
end;

{ TXDataStreamType }

function TXDataStreamType.GetName: string;
begin
  Result := 'Stream';
end;

function TXDataStreamType.GetTypeKind: TXTypeKind;
begin
  Result := TXTypeKind.xtStream;
end;

{ TXDataBinaryType }

function TXDataBinaryType.GetName: string;
begin
  Result := 'Binary';
end;

function TXDataBinaryType.GetTypeKind: TXTypeKind;
begin
  Result := TXTypeKind.xtBinary;
end;

function TXDataBinaryType.LiteralToValue(const Literal: string;
  var Value: TBytes): boolean;
begin
  Value := TBclUtils.DecodeBase64(Literal);
  Result := true;
end;

function TXDataBinaryType.ValueToLiteral(Value: TBytes;
  var Literal: string): boolean;
begin
  Literal := TBclUtils.EncodeBase64(Value);
  Result := true;
end;

{ TXDataBooleanType }

function TXDataBooleanType.GetName: string;
begin
  Result := 'Boolean';
end;

function TXDataBooleanType.GetTypeKind: TXTypeKind;
begin
  Result := TXTypeKind.xtBoolean;
end;

function TXDataBooleanType.LiteralToValue(const Literal: string;
  var Value: boolean): boolean;
begin
  if SameText(Literal, 'true') then
  begin
    Value := true;
    Result := true;
  end
  else
  if SameText(Literal, 'false') then
  begin
    Value := false;
    Result := true;
  end
  else
    Result := false;
end;

function TXDataBooleanType.ValueToLiteral(Value: Boolean; var Literal: string): boolean;
begin
  if Value then
    Literal := 'true'
  else
    Literal := 'false';
  Result := true;
end;

{ TXDataByteType }

function TXDataByteType.GetName: string;
begin
  Result := 'Byte';
end;

function TXDataByteType.GetTypeKind: TXTypeKind;
begin
  Result := TXTypeKind.xtByte;
end;

function TXDataByteType.LiteralToValue(const Literal: string; var Value: byte): boolean;
var
  V: integer;
begin
  Result := TryStrToInt(Literal, V);
  Value := byte(V);
end;

function TXDataByteType.ValueToLiteral(Value: byte; var Literal: string): boolean;
begin
  Literal := IntToStr(Value);
  Result := True;
end;

{ TXDataDateTimeType }

function TXDataDateTimeType.GetName: string;
begin
  Result := 'DateTime'; // DateTimeOffset
end;

function TXDataDateTimeType.GetTypeKind: TXTypeKind;
begin
  Result := TXTypeKind.xtDateTime;
end;

function TXDataDateTimeType.LiteralToValue(const Literal: string;
  var Value: TDateTime): boolean;
begin
  Result := TBclUtils.TryISOToDateTime(Literal, Value);
end;

function TXDataDateTimeType.ValueToLiteral(Value: TDateTime;
  var Literal: string): boolean;
begin
  Result := true;
  Literal := TBclUtils.DateTimeToISO(Value, false);
end;

{ TXDataCurrencyType }

{$IFNDEF PAS2JS}
procedure TXDataCurrencyType.AfterConstruction;
begin
  inherited;
  TBclUtils.InitFormatSettings(FFormatSettings);
  FFormatSettings.DecimalSeparator := '.';
  FFormatSettings.ThousandSeparator := #0;
end;
{$ENDIF}

function TXDataCurrencyType.GetName: string;
begin
  Result := 'Currency';
end;

function TXDataCurrencyType.GetTypeKind: TXTypeKind;
begin
  Result := TXTypeKind.xtCurrency;
end;

{$IFNDEF PAS2JS}
function TXDataCurrencyType.LiteralToValue(const Literal: string; var Value: Currency): boolean;
begin
  Result := TryStrToCurr(Literal, Value, FFormatSettings);
end;
{$ENDIF}

{$IFNDEF PAS2JS}
function TXDataCurrencyType.ValueToLiteral(Value: Currency; var Literal: string): boolean;
begin
  Literal := CurrToStr(Value, FFormatSettings);
  Result := true;
end;
{$ENDIF}

{ TXDataDoubleType }

function TXDataDoubleType.GetName: string;
begin
  Result := 'Double';
end;

function TXDataDoubleType.GetTypeKind: TXTypeKind;
begin
  Result := TXTypeKind.xtDouble;
end;

function TXDataDoubleType.LiteralToValue(const Literal: string;
  var Value: double): boolean;
begin
  Result := TryStrToNumber(Literal, Value);
end;

function TXDataDoubleType.ValueToLiteral(Value: Double;
  var Literal: string): boolean;
begin
  Literal := NumberToStr(Value);
  Result := true;
end;

{ TXDataGuidType }

function TXDataGuidType.GetName: string;
begin
  Result := 'Guid';
end;

function TXDataGuidType.GetTypeKind: TXTypeKind;
begin
  Result := TXTypeKind.xtGuid;
end;

{$IFDEF PAS2JS}
function TXDataGuidType.LiteralToValue(const Literal: string;
  var Value: string): boolean;
begin
  Result := True;
  case Length(Literal) of
    36: Value := Literal;
    38: Value := Copy(Literal, 2, 36);
  else
    Result := False;
  end;
end;

function TXDataGuidType.ValueToLiteral(Value: string;
  var Literal: string): boolean;
begin
  Result := True;
  case Length(Value) of
    36: Literal := Value;
    38: Literal := Copy(Value, 2, 36);
  else
    Result := False;
  end;
end;
{$ELSE}
function TXDataGuidType.LiteralToValue(const Literal: string;
  var Value: TGuid): boolean;
begin
  Result := TBclUtils.TryStringToGuid(Literal, Value);
end;

function TXDataGuidType.ValueToLiteral(Value: TGuid;
  var Literal: string): boolean;
begin
  Literal := Copy(GuidToString(Value), 2, 36);
  Result := true;
end;
{$ENDIF}

{ TXDataInt16Type }

function TXDataInt16Type.GetName: string;
begin
  Result := 'Int16';
end;

function TXDataInt16Type.GetTypeKind: TXTypeKind;
begin
  Result := TXTypeKind.xtInt16;
end;

function TXDataInt16Type.LiteralToValue(const Literal: string;
  var Value: integer): boolean;
begin
  Result := TryStrToInt(Literal, Value);
end;

function TXDataInt16Type.ValueToLiteral(Value: Integer;
  var Literal: string): boolean;
begin
  Literal := IntToStr(Value);
  Result := True;
end;

{ TXDataInt32Type }

function TXDataInt32Type.GetName: string;
begin
  Result := 'Int32';
end;

function TXDataInt32Type.GetTypeKind: TXTypeKind;
begin
  Result := TXTypeKind.xtInt32;
end;

function TXDataInt32Type.LiteralToValue(const Literal: string;
  var Value: integer): boolean;
begin
  Result := TryStrToInt(Literal, Value);
end;

function TXDataInt32Type.ValueToLiteral(const Value: Integer;
  var Literal: string): boolean;
begin
  Literal := IntToStr(Value);
  Result := True;
end;

{ TXDataInt64Type }

function TXDataInt64Type.GetName: string;
begin
  Result := 'Int64';
end;

function TXDataInt64Type.GetTypeKind: TXTypeKind;
begin
  Result := TXTypeKind.xtInt64;
end;

{$IFDEF PAS2JS}
function TXDataInt64Type.LiteralToValue(const Literal: string;
  var Value: Integer): boolean;
begin
  Result := TryStrToInt(Literal, Value);
end;

function TXDataInt64Type.ValueToLiteral(const Value: Integer;
  var Literal: string): boolean;
begin
  Literal := IntToStr(Value);
  Result := True;
end;
{$ELSE}
function TXDataInt64Type.LiteralToValue(const Literal: string;
  var Value: Int64): boolean;
begin
  Result := TryStrToInt64(Literal, Value);
end;

function TXDataInt64Type.ValueToLiteral(Value: Int64;
  var Literal: string): boolean;
begin
  Literal := IntToStr(Value);
  Result := True;
end;
{$ENDIF}

{ TXDataSByteType }

function TXDataSByteType.GetName: string;
begin
  Result := 'SByte';
end;

function TXDataSByteType.GetTypeKind: TXTypeKind;
begin
  Result := TXTypeKind.xtSByte;
end;

function TXDataSByteType.LiteralToValue(const Literal: string; var Value: ShortInt): boolean;
var
  V: integer;
begin
  Result := TryStrToInt(Literal, V);
  Value := ShortInt(V);
end;

function TXDataSByteType.ValueToLiteral(Value: ShortInt; var Literal: string): boolean;
begin
  Literal := IntToStr(Value);
  Result := True;
end;

{ TXDataStringType }

function TXDataStringType.GetName: string;
begin
  Result := 'String';
end;

function TXDataStringType.GetTypeKind: TXTypeKind;
begin
  Result := TXTypeKind.xtText;
end;

function TXDataStringType.LiteralToValue(const Literal: string;
  var Value: string): boolean;
begin
  Value := Literal;
  Result := true;
end;

function TXDataStringType.ValueToLiteral(const Value: string;
  var Literal: string): boolean;
begin
  // strictly we should convert it to UTF-8. Let's keep it this way as a trade-off for speed
  Literal := Value;
  Result := true;
end;

{ TXDataTimeType }

function TXDataTimeType.GetName: string;
begin
  Result := 'Time';
end;

function TXDataTimeType.GetTypeKind: TXTypeKind;
begin
  Result := TXTypeKind.xtTime;
end;

function TXDataTimeType.LiteralToValue(const Literal: string;
  var Value: TTime): boolean;
begin
  Result := TBclUtils.TryISOToTime(Literal, Value);
end;

function TXDataTimeType.ValueToLiteral(Value: TTime;
  var Literal: string): boolean;
begin
  Result := true;
  Literal := TBclUtils.TimeToISO(Value);
end;

{ TXDataDateType }

function TXDataDateType.GetName: string;
begin
  Result := 'Date';
end;

function TXDataDateType.GetTypeKind: TXTypeKind;
begin
  Result := TXTypeKind.xtDate;
end;

function TXDataDateType.LiteralToValue(const Literal: string; var Value: TDate): boolean;
begin
  Result := TBclUtils.TryISOToDate(Literal, Value);
end;

function TXDataDateType.ValueToLiteral(Value: TDate; var Literal: string): boolean;
begin
  Result := true;
  Literal := TBclUtils.DateToISO(Value);
end;

{ TXDataVariantType }

function TXDataVariantType.GetName: string;
begin
  Result := 'Variant';
end;

function TXDataVariantType.GetTypeKind: TXTypeKind;
begin
  Result := TXTypeKind.xtVariant;
end;

{$IFDEF PAS2JS}
function TXDataVariantType.LiteralToValue(const Literal: string;
  var Value: JSValue): boolean;
var
  T: TXDataPrimitiveTypes;
  BoolValue: Boolean;
  Int32Value: Integer;
  Int64Value: Integer;
  DoubleValue: Double;
  StringValue: string;
begin
  T := TXDataPrimitiveTypes.Instance;
  Result := True;
  if Literal = 'null' then
    Value := nil
  else
  if T.BooleanType.LiteralToValue(Literal, BoolValue) then
    Value := BoolValue
  else
  if T.Int32Type.LiteralToValue(Literal, Int32Value) then
    Value := Int32Value
  else
  if T.Int64Type.LiteralToValue(Literal, Int64Value) then
    Value := Int64Value
  else
  if T.DoubleType.LiteralToValue(Literal, DoubleValue) then
    Value := DoubleValue
  else
  if T.StringType.LiteralToValue(Literal, StringValue) and LiteralUnquote(StringValue) then
    Value := StringValue
  else
    Result := False;
end;

function TXDataVariantType.ValueToLiteral(Value: JSValue;
  var Literal: string): boolean;
var
  T: TXDataPrimitiveTypes;
begin
  T := TXDataPrimitiveTypes.Instance;
  Result := True;
  case GetValueType(Value) of
    jvtNull:
      Literal := 'null';
    jvtBoolean:
      Result := T.BooleanType.ValueToLiteral(Boolean(Value), Literal);
    jvtInteger:
      Result := T.Int32Type.ValueToLiteral(NativeInt(Value), Literal);
    jvtFloat:
      Result := T.DoubleType.ValueToLiteral(Double(Value), Literal);
    jvtString:
      begin
        Result := T.StringType.ValueToLiteral(string(Value), Literal);
        Literal := LiteralQuote(Literal);
      end;
  else
    Result := False;
  end;
end;
{$ELSE}
function TXDataVariantType.LiteralToValue(const Literal: string;
  var Value: Variant): boolean;
var
  T: TXDataPrimitiveTypes;
  BoolValue: Boolean;
  Int32Value: Integer;
  Int64Value: Int64;
  DoubleValue: Double;
  StringValue: string;
begin
  T := TXDataPrimitiveTypes.Instance;
  Result := True;
  if Literal = 'null' then
    Value := NULL
  else
  if T.BooleanType.LiteralToValue(Literal, BoolValue) then
    Value := BoolValue
  else
  if T.Int32Type.LiteralToValue(Literal, Int32Value) then
    Value := Int32Value
  else
  if T.Int64Type.LiteralToValue(Literal, Int64Value) then
    Value := Int64Value
  else
  if T.DoubleType.LiteralToValue(Literal, DoubleValue) then
    Value := DoubleValue
  else
  if T.StringType.LiteralToValue(Literal, StringValue) and LiteralUnquote(StringValue) then
    Value := StringValue
  else
    Result := False;
end;

function TXDataVariantType.ValueToLiteral(Value: Variant;
  var Literal: string): boolean;
var
  VarData: TVarData;
  T: TXDataPrimitiveTypes;
begin
  T := TXDataPrimitiveTypes.Instance;
  VarData := TVarData(Value);
  Result := True;
  case VarType(Value) of
    varEmpty, varNull:
      Literal := 'null';
    varBoolean:
      Result := T.BooleanType.ValueToLiteral(VarData.VBoolean, Literal);
    varShortInt:
      Result := T.Int32Type.ValueToLiteral(VarData.VShortInt, Literal);
    varSmallint:
      Result := T.Int32Type.ValueToLiteral(VarData.VSmallInt, Literal);
    varInteger:
      Result := T.Int32Type.ValueToLiteral(VarData.VInteger, Literal);
    varSingle:
      Result := T.DoubleType.ValueToLiteral(VarData.VSingle, Literal);
    varDouble:
      Result := T.DoubleType.ValueToLiteral(VarData.VDouble, Literal);
    varCurrency:
      Result := T.CurrencyType.ValueToLiteral(VarData.VCurrency, Literal);
//    varDate:
//      Result := T.DaType.ValueToLiteral(VarData.VCurrency, Literal);
//      Writer.WriteDouble(VarData.VDate);
    varOleStr:
      begin
        Result := T.StringType.ValueToLiteral(VarData.VOleStr, Literal);
        Literal := LiteralQuote(Literal);
      end;
    varError:
      Result := T.Int32Type.ValueToLiteral(VarData.VError, Literal);
    varByte:
      Result := T.Int32Type.ValueToLiteral(VarData.VByte, Literal);
    varWord:
      Result := T.Int32Type.ValueToLiteral(VarData.VWord, Literal);
    varLongWord:
      Result := T.Int32Type.ValueToLiteral(VarData.VLongWord, Literal);
    varInt64:
      Result := T.Int64Type.ValueToLiteral(VarData.VInt64, Literal);
    varUInt64:
      Result := T.Int64Type.ValueToLiteral(VarData.VUInt64, Literal);
    varString:
      begin
        {$IFNDEF NEXTGEN}
        Result := T.StringType.ValueToLiteral(string(AnsiString(VarData.VString)), Literal);
        {$ELSE}
        Result := T.StringType.ValueToLiteral(string(VarData.VString), Literal);
        {$ENDIF}
        Literal := LiteralQuote(Literal);
      end;
    varUString:
      begin
        Result := T.StringType.ValueToLiteral(UnicodeString(VarData.VUString), Literal);
        Literal := LiteralQuote(Literal);
      end
  else
    Result := False;
  end;
end;
{$ENDIF}

initialization
{$IFNDEF PAS2JS}
finalization
  TXDataPrimitiveTypes.ClassDestroy;
{$ENDIF}

end.

