{
    This file is part of the Web Service Toolkit
    Copyright (c) 2007 by Inoussa OUEDRAOGO

    This file is provide under modified LGPL licence
    ( the files COPYING.modifiedLGPL and COPYING.LGPL).


    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
}
{$INCLUDE wst_global.inc}
unit base_json_formatter;

interface
uses
  Classes, SysUtils, TypInfo, Contnrs,
  base_service_intf,
  fpjson;

const
  sFORMAT = 'format';
  s_json_ContentType = 'application/json';
  s_json = 'json';
  s_inner_value = '__';
  
  s_json_code = 'code';
  s_json_error = 'error';
  s_json_id = 'id';
  s_json_message = 'message';
  s_json_method = 'method';
  s_json_name = 'name';
  s_json_params = 'params';
  s_json_result = 'result';
  s_json_version = 'version';
  s_json_rpc_version_10 = '1.0';
  s_json_rpc_version_11 = '1.1';
  
  stNilScope = stBase + 7;
  
type

  TJonRPCVersion = ( jsonRPC_10, jsonRPC_11 );
  TJsonInteger = Integer;
  TEnumIntType = Int64;

  EJsonRpcException = class(EBaseRemoteException)
  end;

  { TStackItem }

  TStackItem = class
  private
    FScopeObject: TJSONData;
    FScopeType: TScopeType;
  protected
    function GetItemCount() : Integer;virtual;
  public
    constructor Create(AScopeObject : TJSONData;AScopeType : TScopeType);
    function FindNode(var ANodeName : string):TJSONData;virtual;abstract;
    function CreateStringBuffer(
      Const AName  : string;
      const AValue : TJSONStringType
    ) : TJSONData;virtual;abstract;
    function CreateIntBuffer(
      Const AName  : string;
      const AValue : TJsonInteger
    ) : TJSONData;virtual;abstract;
    function CreateInt64Buffer(
      Const AName  : string;
      const AValue : Int64
    ) : TJSONData;virtual; abstract;
{$IFDEF HAS_QWORD}
    function CreateUInt64Buffer(
      Const AName  : string;
      const AValue : QWord
    ) : TJSONData;virtual;
{$ENDIF HAS_QWORD}
    function CreateFloatBuffer(
      Const AName  : string;
      const AValue : TJSONFloat
    ) : TJSONData;virtual;abstract;
    function CreateBoolBuffer(
      Const AName  : string;
      const AValue : Boolean
    ) : TJSONData;virtual;abstract;
    function CreateObjectBuffer(const AName : string) : TJSONObject;virtual;abstract;
    function CreateArrayBuffer(const AName : string) : TJSONArray;virtual;abstract;
    function GetScopeItemNames(const AReturnList : TStrings) : Integer;virtual;abstract;
    function NilItem(AItem : TJSONData) : TJSONData;virtual;abstract;
    property ScopeObject : TJSONData Read FScopeObject;
    property ScopeType : TScopeType Read FScopeType;
    property ItemCount : Integer read GetItemCount;
  end;
  
  { TObjectStackItem }

  TObjectStackItem = class(TStackItem)
  protected
    function GetDataObject() : TJSONObject;{$IFDEF USE_INLINE}inline;{$ENDIF}
  public
    constructor Create(AScopeObject : TJSONObject);
    function FindNode(var ANodeName : string):TJSONData;override;
    function CreateStringBuffer(
      const AName  : string;
      const AValue : TJSONStringType
    ) : TJSONData;override;
    function CreateIntBuffer(
      Const AName  : string;
      const AValue : TJsonInteger
    ) : TJSONData;override;
    function CreateInt64Buffer(
      Const AName  : string;
      const AValue : Int64
    ) : TJSONData; override;
    function CreateFloatBuffer(
      Const AName  : string;
      const AValue : TJSONFloat
    ) : TJSONData;override;
    function CreateBoolBuffer(
      Const AName  : string;
      const AValue : Boolean
    ) : TJSONData;override;
    function CreateObjectBuffer(const AName : string) : TJSONObject;override;
    function CreateArrayBuffer(const AName : string) : TJSONArray;override;
    function NilItem(AItem : TJSONData) : TJSONData;override;
    function GetScopeItemNames(const AReturnList : TStrings) : Integer;override;
  end;

  { TArrayStackItem }

  TArrayStackItem = class(TStackItem)
  private
    FIndex : PtrInt;
  protected
    function GetDataObject() : TJSONArray;{$IFDEF USE_INLINE}inline;{$ENDIF}
  public
    constructor Create(AScopeObject : TJSONArray);
    function FindNode(var ANodeName : string):TJSONData;override;
    function CreateStringBuffer(
      const AName  : string;
      const AValue : TJSONStringType
    ) : TJSONData;override;
    function CreateIntBuffer(
      Const AName  : string;
      const AValue : TJsonInteger
    ) : TJSONData;override; 
    function CreateInt64Buffer(
      Const AName  : string;
      const AValue : Int64
    ) : TJSONData; override; 
    function CreateFloatBuffer(
      Const AName  : string;
      const AValue : TJSONFloat
    ) : TJSONData;override;
    function CreateBoolBuffer(
      Const AName  : string;
      const AValue : Boolean
    ) : TJSONData;override;
    function CreateObjectBuffer(const AName : string) : TJSONObject;override;
    function CreateArrayBuffer(const AName : string) : TJSONArray;override;
    function NilItem(AItem : TJSONData) : TJSONData;override;
    function GetScopeItemNames(const AReturnList : TStrings) : Integer;override;
  end;

  { TNullStackItem }

  TNullStackItem = class(TStackItem)
  private
    procedure RaiseNotApplicable();{$IFDEF USE_INLINE}inline;{$ENDIF}
  public
    constructor Create(AScopeObject : TJSONNull);
    function FindNode(var ANodeName : string):TJSONData;override;
    function CreateStringBuffer(
      const AName  : string;
      const AValue : TJSONStringType
    ) : TJSONData;override;
    function CreateIntBuffer(
      Const AName  : string;
      const AValue : TJsonInteger
    ) : TJSONData;override; 
    function CreateInt64Buffer(
      Const AName  : string;
      const AValue : Int64
    ) : TJSONData; override; 
    function CreateFloatBuffer(
      Const AName  : string;
      const AValue : TJSONFloat
    ) : TJSONData;override;
    function CreateBoolBuffer(
      Const AName  : string;
      const AValue : Boolean
    ) : TJSONData;override;
    function CreateObjectBuffer(const AName : string) : TJSONObject;override;
    function CreateArrayBuffer(const AName : string) : TJSONArray;override;
    function NilItem(AItem : TJSONData) : TJSONData;override;
    function GetScopeItemNames(const AReturnList : TStrings) : Integer;override;
  end;

  { TJsonRpcBaseFormatter }

  TJsonRpcBaseFormatter = class(TSimpleFactoryItem,IFormatterBase)
  private
    FPropMngr : IPropertyManager;
    FRootData : TJSONData;
    FSerializationStyle : TSerializationStyle;
    FStack : TObjectStack;
  protected
    function GetRootData() : TJSONObject;{$IFDEF USE_INLINE}inline;{$ENDIF}
    function GetCurrentScope : String;
    function HasScope():Boolean;{$IFDEF USE_INLINE}inline;{$ENDIF}
    procedure CheckScope();{$IFDEF USE_INLINE}inline;{$ENDIF}
    procedure ClearStack();
    function StackTop():TStackItem;{$IFDEF USE_INLINE}inline;{$ENDIF}
    function PopStack():TStackItem;{$IFDEF USE_INLINE}inline;{$ENDIF}
    procedure PushStack(const AScopeObject : TJSONData;const AScopeType : TScopeType);{$IFDEF USE_INLINE}inline;{$ENDIF}
  protected
    procedure PutEnum(
      Const AName     : String;
      Const ATypeInfo : PTypeInfo;
      Const AData     : TEnumIntType
    );{$IFDEF USE_INLINE}inline;{$ENDIF}
    procedure PutBool(
      Const AName     : String;
      Const ATypeInfo : PTypeInfo;
      Const AData     : Boolean
    );{$IFDEF USE_INLINE}inline;{$ENDIF}
    procedure PutAnsiChar(
      Const AName     : String;
      Const ATypeInfo : PTypeInfo;
      Const AData     : AnsiChar
    );{$IFDEF USE_INLINE}inline;{$ENDIF}
    procedure PutWideChar(
      Const AName     : String;
      Const ATypeInfo : PTypeInfo;
      Const AData     : WideChar
    );{$IFDEF USE_INLINE}inline;{$ENDIF}
    procedure PutInt64(
      Const AName     : String;
      Const ATypeInfo : PTypeInfo;
      Const AData     : Int64
    );{$IFDEF USE_INLINE}inline;{$ENDIF}
{$IFDEF HAS_QWORD}
    procedure PutUInt64(
      Const AName     : String;
      Const ATypeInfo : PTypeInfo;
      Const AData     : QWord
    );{$IFDEF USE_INLINE}inline;{$ENDIF}
{$ENDIF HAS_QWORD}
    procedure PutStr(
      Const AName     : String;
      Const ATypeInfo : PTypeInfo;
      Const AData     : String
    );{$IFDEF USE_INLINE}inline;{$ENDIF}
{$IFDEF WST_UNICODESTRING}
    procedure PutUnicodeStr(
      Const AName     : String;
      Const ATypeInfo : PTypeInfo;
      Const AData     : UnicodeString
    );{$IFDEF USE_INLINE}inline;{$ENDIF}
{$ENDIF WST_UNICODESTRING}
    procedure PutWideStr(
      Const AName     : String;
      Const ATypeInfo : PTypeInfo;
      Const AData     : WideString
    );{$IFDEF USE_INLINE}inline;{$ENDIF}
    procedure PutFloat(
      Const AName     : String;
      Const ATypeInfo : PTypeInfo;
      Const AData     : Extended
    );{$IFDEF USE_INLINE}inline;{$ENDIF}
    procedure PutObj(
      Const AName     : String;
      Const ATypeInfo : PTypeInfo;
      Const AData     : TObject
    );{$IFDEF USE_INLINE}inline;{$ENDIF}
    procedure PutRecord(
      const AName     : string;
      const ATypeInfo : PTypeInfo;
      const AData     : Pointer
    );{$IFDEF USE_INLINE}inline;{$ENDIF}
    
    function GetDataBuffer(
      var AName : string;
      out AResBuffer : TJSONData
    ) : Boolean;{$IFDEF USE_INLINE}inline;{$ENDIF}
    function GetEnum(
      Const ATypeInfo : PTypeInfo;
      Var   AName     : String;
      Var   AData     : TEnumIntType
    ) : Boolean;{$IFDEF USE_INLINE}inline;{$ENDIF}
    function GetBool(
      Const ATypeInfo : PTypeInfo;
      Var   AName     : String;
      Var   AData     : Boolean
    ) : Boolean;{$IFDEF USE_INLINE}inline;{$ENDIF}
    {$IFDEF FPC}
    function GetAnsiChar(
      Const ATypeInfo : PTypeInfo;
      Var   AName     : String;
      Var   AData     : AnsiChar
    ) : Boolean;{$IFDEF USE_INLINE}inline;{$ENDIF}
    function GetWideChar(
      Const ATypeInfo : PTypeInfo;
      Var   AName     : String;
      Var   AData     : WideChar
    ) : Boolean;{$IFDEF USE_INLINE}inline;{$ENDIF}
    function GetInt(
      Const ATypeInfo : PTypeInfo;
      Var   AName     : String;
      Var   AData     : Integer
    ) : Boolean;{$IFDEF USE_INLINE}inline;{$ENDIF}
    {$ENDIF}
    function GetInt64(
      Const ATypeInfo : PTypeInfo;
      Var   AName     : String;
      Var   AData     : Int64
    ) : Boolean;{$IFDEF USE_INLINE}inline;{$ENDIF}
{$IFDEF HAS_QWORD}
    function GetUInt64(
      Const ATypeInfo : PTypeInfo;
      Var   AName     : String;
      Var   AData     : QWord
    ) : Boolean;{$IFDEF USE_INLINE}inline;{$ENDIF}
{$ENDIF HAS_QWORD}
    function GetFloat(
      Const ATypeInfo : PTypeInfo;
      Var   AName     : String;
      Var   AData     : Extended
    ) : Boolean;{$IFDEF USE_INLINE}inline;{$ENDIF}
    function GetStr(
      Const ATypeInfo : PTypeInfo;
      Var   AName     : String;
      Var   AData     : String
    ) : Boolean;{$IFDEF USE_INLINE}inline;{$ENDIF}
{$IFDEF WST_UNICODESTRING}
    function GetUnicodeStr(
      Const ATypeInfo : PTypeInfo;
      Var   AName     : String;
      Var   AData     : UnicodeString
    ) : Boolean;{$IFDEF USE_INLINE}inline;{$ENDIF}
{$ENDIF WST_UNICODESTRING}
    function GetWideStr(
      Const ATypeInfo : PTypeInfo;
      Var   AName     : String;
      Var   AData     : WideString
    ) : Boolean;{$IFDEF USE_INLINE}inline;{$ENDIF}
    function GetObj(
      Const ATypeInfo : PTypeInfo;
      Var   AName     : String;
      Var   AData     : TObject
    ) : Boolean;{$IFDEF USE_INLINE}inline;{$ENDIF}
    function GetRecord(
      const ATypeInfo : PTypeInfo;
      var   AName     : String;
      var   AData     : Pointer
    ) : Boolean;{$IFDEF USE_INLINE}inline;{$ENDIF}
  public
    procedure SetSerializationStyle(const ASerializationStyle : TSerializationStyle);
    function GetSerializationStyle():TSerializationStyle;
    function GetFormatName() : string;
    function GetPropertyManager():IPropertyManager;
    procedure Clear();

    procedure BeginObject(
      Const AName      : string;
      Const ATypeInfo  : PTypeInfo
    );
    procedure BeginArray(
      const AName         : string;
      const ATypeInfo     : PTypeInfo;
      const AItemTypeInfo : PTypeInfo;
      const ABounds       : Array Of Integer;
      const AStyle        : TArrayStyle
    );
    procedure NilCurrentScope();
    function IsCurrentScopeNil():Boolean;
    procedure EndScope();
    procedure AddScopeAttribute(Const AName,AValue : string);
    function BeginObjectRead(
      var   AScopeName : string;
      const ATypeInfo  : PTypeInfo
    ) : Integer;
    function BeginArrayRead(
      var   AScopeName : string;
      const ATypeInfo  : PTypeInfo;
      const AStyle     : TArrayStyle;
      const AItemName  : string
    ):Integer;
    function GetScopeItemNames(const AReturnList : TStrings) : Integer;
    procedure EndScopeRead();
    property CurrentScope : String Read GetCurrentScope;

    procedure BeginHeader();
    procedure EndHeader();

    procedure Put(
      Const AName     : String;
      Const ATypeInfo : PTypeInfo;
      Const AData
    );overload;
    procedure Put(
      const ANameSpace : string;
      Const AName     : String;
      Const ATypeInfo : PTypeInfo;
      Const AData
    );overload;
    procedure PutScopeInnerValue(
      const ATypeInfo : PTypeInfo;
      const AData
    );
    function Get(
      const ATypeInfo : PTypeInfo;
      var   AName     : string;
      var   AData
    ) : Boolean;overload;
    function Get(
      const ATypeInfo  : PTypeInfo;
      const ANameSpace : string;
      var   AName      : string;
      var   AData
    ) : Boolean;overload;
    procedure GetScopeInnerValue(
      const ATypeInfo : PTypeInfo;
      var   AData
    );
    function ReadBuffer(const AName : string; out AResBuffer : string) : Boolean;
    procedure WriteBuffer(const AValue : string);

    procedure SaveToStream(AStream : TStream);
    procedure LoadFromStream(AStream : TStream);

    // This procedures will raise exceptions!!!
    procedure Error(Const AMsg:string);overload;
    procedure Error(Const AMsg:string; Const AArgs : array of const);overload;
  public
    constructor Create();override;
    destructor Destroy();override;
  end;
  
  
implementation
uses 
  jsonparser, imp_utils, wst_consts;

function FindObject(AObject : TJSONObject; const AName : TJSONStringType) : TJSONData;
var
  i : Integer;
begin
  i := AObject.IndexOfName(AName);
  if ( i >= 0 ) then
    Result := AObject.Items[i]
  else
    Result := nil;
end;

{ TJsonRpcBaseFormatter }

function TJsonRpcBaseFormatter.HasScope() : Boolean;
begin
  Result := FStack.AtLeast(1);
end;

procedure TJsonRpcBaseFormatter.CheckScope();
begin
  if not HasScope() then
    Error(SERR_NoScope);
end;

procedure TJsonRpcBaseFormatter.ClearStack();
var
  i, c : Integer;
begin
  c := FStack.Count;
  for I := 1 to c do
    FStack.Pop().Free();
end;

function TJsonRpcBaseFormatter.StackTop() : TStackItem;
begin
  CheckScope();
  Result := FStack.Peek() as TStackItem;
end;

function TJsonRpcBaseFormatter.PopStack() : TStackItem;
begin
  CheckScope();
  Result := FStack.Pop() as TStackItem;
end;

procedure TJsonRpcBaseFormatter.PushStack(const AScopeObject : TJSONData; const AScopeType : TScopeType);
begin
  case AScopeType of
    stObject : FStack.Push(TObjectStackItem.Create(AScopeObject as TJSONObject));
    stArray  : FStack.Push(TArrayStackItem.Create(AScopeObject as TJSONArray));
    stNilScope  : FStack.Push(TNullStackItem.Create(AScopeObject as TJSONNull));
    else
    Assert(False);
  end;
  if not Assigned(FRootData) then
    FRootData := AScopeObject;
end;

function TJsonRpcBaseFormatter.GetRootData() : TJSONObject;
begin
  Result := TJSONObject(FRootData);
end;

procedure TJsonRpcBaseFormatter.PutEnum(
  const AName : String;
  const ATypeInfo : PTypeInfo;
  const AData : TEnumIntType
);
begin
  StackTop().CreateIntBuffer(AName,AData);
end;

procedure TJsonRpcBaseFormatter.PutBool(
  const AName : String;
  const ATypeInfo : PTypeInfo;
  const AData : Boolean
);
begin
  StackTop().CreateBoolBuffer(AName,AData);
end;

procedure TJsonRpcBaseFormatter.PutAnsiChar(
  const AName: String;
  const ATypeInfo: PTypeInfo;
  const AData: AnsiChar
);
begin
  StackTop().CreateStringBuffer(AName,AData);
end;

procedure TJsonRpcBaseFormatter.PutWideChar(
  const AName: String;
  const ATypeInfo: PTypeInfo;
  const AData: WideChar
);
begin
  StackTop().CreateStringBuffer(AName,AData);
end;

procedure TJsonRpcBaseFormatter.PutInt64(
  const AName : String;
  const ATypeInfo : PTypeInfo;
  const AData : Int64
);
begin
  StackTop().CreateInt64Buffer(AName,AData);
end;

{$IFDEF HAS_QWORD}
procedure TJsonRpcBaseFormatter.PutUInt64(
  const AName : String;
  const ATypeInfo : PTypeInfo;
  const AData : QWord
);
begin
  StackTop().CreateUInt64Buffer(AName,AData);
end;
{$ENDIF HAS_QWORD}

procedure TJsonRpcBaseFormatter.PutStr(
  const AName : String;
  const ATypeInfo : PTypeInfo;
  const AData : String
);
begin
  StackTop().CreateStringBuffer(AName,AData);
end;

{$IFDEF WST_UNICODESTRING}
procedure TJsonRpcBaseFormatter.PutUnicodeStr(
  const AName: String;
  const ATypeInfo: PTypeInfo;
  const AData: UnicodeString
);
begin
  StackTop().CreateStringBuffer(AName,AData);
end;
{$ENDIF WST_UNICODESTRING}

procedure TJsonRpcBaseFormatter.PutWideStr(
  const AName: String;
  const ATypeInfo: PTypeInfo;
  const AData: WideString
);
begin
  StackTop().CreateStringBuffer(AName,AData);
end;

procedure TJsonRpcBaseFormatter.PutFloat(
  const AName : String;
  const ATypeInfo : PTypeInfo;
  const AData : Extended
);
begin
  StackTop().CreateFloatBuffer(AName,AData);
end;

procedure TJsonRpcBaseFormatter.PutObj(
  const AName : String;
  const ATypeInfo : PTypeInfo;
  const AData : TObject
);
begin
  TBaseRemotableClass(GetTypeData(ATypeInfo)^.ClassType).Save(AData As TBaseRemotable, Self,AName,ATypeInfo);
end;

procedure TJsonRpcBaseFormatter.PutRecord(
  const AName : string;
  const ATypeInfo : PTypeInfo;
  const AData : Pointer
);
begin
  TRemotableRecordEncoder.Save(AData,Self,AName,ATypeInfo);
end;

function TJsonRpcBaseFormatter.GetDataBuffer(
  var AName : string;
  out AResBuffer : TJSONData
) : Boolean;
begin
  AResBuffer := StackTop().FindNode(AName);
  Result := ( AResBuffer <> nil );
end;

function TJsonRpcBaseFormatter.GetEnum(
  const ATypeInfo : PTypeInfo;
  var AName : String;
  var AData : TEnumIntType
) : Boolean;
var
  locBuffer : TJSONData;
begin
  Result := GetDataBuffer(AName,locBuffer);
  if Result then
    AData := locBuffer.AsInteger;
end;

function TJsonRpcBaseFormatter.GetBool(
  const ATypeInfo : PTypeInfo;
  var AName : String;
  var AData : Boolean
) : Boolean;
var
  locBuffer : TJSONData;
begin
  Result := GetDataBuffer(AName,locBuffer);
  if Result then
    AData := locBuffer.AsBoolean;
end;

function TJsonRpcBaseFormatter.GetAnsiChar(
  const ATypeInfo: PTypeInfo;
  var   AName: String;
  var   AData: AnsiChar
) : Boolean;
var
  tmpString : TJSONStringType;
  locBuffer : TJSONData;
begin
  Result := GetDataBuffer(AName,locBuffer);
  if Result then begin
    tmpString := locBuffer.AsString;
    if ( Length(tmpString) > 0 ) then
      AData := tmpString[1]
    else
      AData := #0;
  end;
end;

function TJsonRpcBaseFormatter.GetWideChar(
  const ATypeInfo: PTypeInfo;
  var   AName: String;
  var   AData: WideChar
) : Boolean;
var
  tmpString : TJSONStringType;
  locBuffer : TJSONData;
begin
  Result := GetDataBuffer(AName,locBuffer);
  if Result  then begin
    tmpString := locBuffer.AsString;
    if ( Length(tmpString) > 0 ) then
      AData := tmpString[1]
    else
      AData := #0;
  end;
end;

function TJsonRpcBaseFormatter.GetInt(
  const ATypeInfo : PTypeInfo;
  var AName : String;
  var AData : Integer
) : Boolean;
var
  locBuffer : TJSONData;
begin
  Result := GetDataBuffer(AName,locBuffer);
  if Result then
    AData := locBuffer.AsInteger;
end;

function TJsonRpcBaseFormatter.GetInt64(
  const ATypeInfo : PTypeInfo;
  var AName : String;
  var AData : Int64
) : Boolean;
var
  locBuffer : TJSONData;
begin
  Result := GetDataBuffer(AName,locBuffer);
  if Result then begin
    if ( locBuffer.JSONType = jtNumber ) and ( TJSONNumber(locBuffer).NumberType = ntInteger ) then
      AData := locBuffer.AsInteger
    else
      AData := Round(locBuffer.AsFloat);
  end;
end;

{$IFDEF HAS_QWORD}
function TJsonRpcBaseFormatter.GetUInt64(
  const ATypeInfo : PTypeInfo;
  var AName : String;
  var AData : QWord
) : Boolean;
var
  locBuffer : TJSONData;
  locExtData : TJSONFloat;
  tmp : QWord;
begin
  Result := GetDataBuffer(AName,locBuffer);
  if Result then begin
    if ( locBuffer.JSONType = jtNumber ) and ( TJSONNumber(locBuffer).NumberType = ntInteger ) then begin
      AData := locBuffer.AsInteger
    end else begin
      locExtData := locBuffer.AsFloat;
      if ( locExtData > High(Int64) ) then begin
        locExtData := locExtData - High(Int64);
        AData := High(Int64);
        tmp := Round(locExtData);
        AData := AData + tmp;
      end else begin
        AData := Round(locExtData);
      end;
    end;
  end;
end;
{$ENDIF HAS_QWORD}

function TJsonRpcBaseFormatter.GetFloat(
  const ATypeInfo : PTypeInfo;
  var AName : String;
  var AData : Extended
) : Boolean;
var
  locBuffer : TJSONData;
begin
  Result := GetDataBuffer(AName,locBuffer);
  if Result then
    AData := locBuffer.AsFloat;
end;

function TJsonRpcBaseFormatter.GetStr(
  const ATypeInfo : PTypeInfo;
  var AName : String;
  var AData : String
) : Boolean;
var
  locBuffer : TJSONData;
begin
  Result := GetDataBuffer(AName,locBuffer);
  if Result then
    AData := locBuffer.AsString;
end;

{$IFDEF WST_UNICODESTRING}
function TJsonRpcBaseFormatter.GetUnicodeStr(
  const ATypeInfo: PTypeInfo;
  var   AName: String;
  var   AData: UnicodeString
) : Boolean;
var
  locBuffer : TJSONData;
begin
  Result := GetDataBuffer(AName,locBuffer);
  if Result then
    AData := locBuffer.AsString;
end;
{$ENDIF WST_UNICODESTRING}

function TJsonRpcBaseFormatter.GetWideStr(
  const ATypeInfo: PTypeInfo;
  var   AName: String;
  var   AData: WideString
) : Boolean;
var
  locBuffer : TJSONData;
begin
  Result := GetDataBuffer(AName,locBuffer);
  if Result then
    AData := locBuffer.AsString;
end;

function TJsonRpcBaseFormatter.GetObj(
  const ATypeInfo : PTypeInfo;
  var AName : String;
  var AData : TObject
) : Boolean;
begin
  { TODO -cEXCEPTION_SAFE : Load() should be a function ! }
  TBaseRemotableClass(GetTypeData(ATypeInfo)^.ClassType).Load(AData, Self,AName,ATypeInfo);
  Result := True;
end;

function TJsonRpcBaseFormatter.GetRecord(
  const ATypeInfo : PTypeInfo;
  var AName : String;
  var AData : Pointer
) : Boolean;
begin
  { TODO -cEXCEPTION_SAFE : Load() should be a function ! }
  TRemotableRecordEncoder.Load(AData, Self,AName,ATypeInfo);
  Result := True;
end;

procedure TJsonRpcBaseFormatter.SetSerializationStyle(const ASerializationStyle : TSerializationStyle);
begin
  FSerializationStyle := ASerializationStyle;
end;

function TJsonRpcBaseFormatter.GetSerializationStyle() : TSerializationStyle;
begin
  Result := FSerializationStyle;
end;

function TJsonRpcBaseFormatter.GetFormatName(): string;
begin
  Result := s_json;
end;

function TJsonRpcBaseFormatter.GetPropertyManager() : IPropertyManager;
begin
  If Not Assigned(FPropMngr) Then
    FPropMngr := TPublishedPropertyManager.Create(Self);
  Result := FPropMngr;
end;

function TJsonRpcBaseFormatter.GetCurrentScope : string;
begin
  CheckScope();
  Result := '';
end;

procedure TJsonRpcBaseFormatter.Clear();
begin
  ClearStack();
  FreeAndNil(FRootData);
end;

procedure TJsonRpcBaseFormatter.BeginObject(
  const AName : string;
  const ATypeInfo : PTypeInfo
);
var
  elt : TJSONObject;
begin
  if HasScope() then
    elt := StackTop().CreateObjectBuffer(AName)
  else
    elt := TJSONObject.Create();
  PushStack(elt,stObject);
end;

procedure TJsonRpcBaseFormatter.BeginArray(
  const AName         : string;
  const ATypeInfo     : PTypeInfo;
  const AItemTypeInfo : PTypeInfo;
  const ABounds       : Array Of Integer;
  const AStyle        : TArrayStyle
);
var
  i, j, k : Integer;
  locObj : TJSONData;
begin
  if ( Length(ABounds) < 2 ) then
    Error(SERR_InvalidArrayBounds);
  i := ABounds[0];
  j := ABounds[1];
  k := ( j - i + 1 );
  if ( k < 0 ) then
    Error(SERR_InvalidArrayBounds);
  if HasScope() then
    locObj := StackTop().CreateArrayBuffer(AName)
  else
    locObj := TJSONArray.Create();
  PushStack(locObj,stArray);
end;

procedure TJsonRpcBaseFormatter.NilCurrentScope();
var
  stkItem : TStackItem;
begin
  if not FStack.AtLeast(2) then
    Error(SERR_RootObjectCannotBeNIL);
  stkItem := PopStack();
  try
    PushStack(StackTop().NilItem(stkItem.ScopeObject),stNilScope);
  finally
    stkItem.Free();
  end;
end;

function TJsonRpcBaseFormatter.IsCurrentScopeNil() : Boolean;
begin
  Result := ( StackTop().ScopeType = stNilScope );
end;

procedure TJsonRpcBaseFormatter.EndScope();
begin
  FStack.Pop().Free();
end;

procedure TJsonRpcBaseFormatter.AddScopeAttribute(const AName, AValue : string);
begin
  Put(AName,TypeInfo(string),AValue);
end;

function TJsonRpcBaseFormatter.BeginObjectRead(
  var AScopeName : string;
  const ATypeInfo : PTypeInfo
) : Integer;
var
  locNode : TJSONData;
  stk : TStackItem;
begin
  stk := StackTop();
  locNode := stk.FindNode(AScopeName);
  if not Assigned(locNode) then begin
    Error(SERR_ScopeNotFound,[AScopeName]);
  end;
  case locNode.JSONType() of
    jtObject : PushStack(locNode,stObject);
    jtNull   : PushStack(locNode,stNilScope);
    else
      Error('object or Nil expected, name : %s.',[AScopeName]);
  end;
  Result := StackTop().GetItemCount();
end;

function TJsonRpcBaseFormatter.BeginArrayRead(
  var AScopeName  : string;
  const ATypeInfo : PTypeInfo;
  const AStyle    : TArrayStyle;
  const AItemName : string
): Integer;
var
  locNode : TJSONData;
  stk : TStackItem;
begin
  stk := StackTop();
  locNode := stk.FindNode(AScopeName);
  if ( locNode <> nil ) then begin
    case locNode.JSONType() of
      jtArray : PushStack(locNode,stArray);
      jtNull   : PushStack(locNode,stNilScope);
      else
        Error('array or Nil expected, name : %s.',[AScopeName]);
    end;
    Result := StackTop().GetItemCount();
  end else begin
    Result := -1;
  end;
end;

function TJsonRpcBaseFormatter.GetScopeItemNames(const AReturnList : TStrings) : Integer;
begin
  CheckScope();
  Result := StackTop().GetScopeItemNames(AReturnList);
end;

procedure TJsonRpcBaseFormatter.EndScopeRead();
begin
  PopStack().Free();
end;

procedure TJsonRpcBaseFormatter.BeginHeader();
begin

end;

procedure TJsonRpcBaseFormatter.EndHeader();
begin

end;

procedure TJsonRpcBaseFormatter.Put(
  const AName : String;
  const ATypeInfo : PTypeInfo;
  const AData
);
Var
  int64Data : Int64;
{$IFDEF HAS_QWORD}
  uint64Data : QWord;
{$ENDIF HAS_QWORD}
  strData : string;
  objData : TObject;
  boolData : Boolean;
  enumData : TEnumIntType;
  floatDt : Extended;
{$IFDEF WST_UNICODESTRING}
  unicodeStrData : UnicodeString;
{$ENDIF WST_UNICODESTRING}
  wideStrData : WideString;
  ansiCharData : AnsiChar;
  wideCharData : WideChar;
begin
  Case ATypeInfo^.Kind Of
    tkChar :
      begin
        ansiCharData := AnsiChar(AData);
        PutAnsiChar(AName,ATypeInfo,ansiCharData);
      end;
    tkWChar :
      begin
        wideCharData := WideChar(AData);
        PutWideChar(AName,ATypeInfo,wideCharData);
      end;
    tkLString{$IFDEF FPC},tkAString{$ENDIF} :
      Begin
        strData := String(AData);
        PutStr(AName,ATypeInfo,strData);
      End;
{$IFDEF WST_UNICODESTRING}
    tkUString :
      begin
        unicodeStrData := UnicodeString(AData);
        PutUnicodeStr(AName,ATypeInfo,unicodeStrData);
      end;
{$ENDIF WST_UNICODESTRING}
    tkWString :
      begin
        wideStrData := WideString(AData);
        PutWideStr(AName,ATypeInfo,wideStrData);
      end;
    tkInt64 :
      Begin
        int64Data := Int64(AData);
        PutInt64(AName,ATypeInfo,int64Data);
      End;
{$IFDEF HAS_QWORD}
    tkQWord :
      Begin
        uint64Data := QWord(AData);
        PutUInt64(AName,ATypeInfo,uint64Data);
      End;
{$ENDIF HAS_QWORD}
    tkClass :
      Begin
        objData := TObject(AData);
        PutObj(AName,ATypeInfo,objData);
      End;
    tkRecord :
      begin
        PutRecord(AName,ATypeInfo,Pointer(@AData));
      end;
    {$IFDEF FPC}
    tkBool :
      Begin
        boolData := Boolean(AData);
        PutBool(AName,ATypeInfo,boolData);
      End;
    {$ENDIF}
    tkInteger, tkEnumeration :
      begin
      {$IFDEF WST_DELPHI}
        if ( ATypeInfo^.Kind = tkEnumeration ) and
           ( GetTypeData(ATypeInfo)^.BaseType^ = TypeInfo(Boolean) )
        then begin
        boolData := Boolean(AData);
        PutBool(AName,ATypeInfo,boolData);
        end else begin
      {$ENDIF}
          enumData := 0;
          Case GetTypeData(ATypeInfo)^.OrdType Of
            otSByte : enumData := ShortInt(AData);
            otUByte : enumData := Byte(AData);
            otSWord : enumData := SmallInt(AData);
            otUWord : enumData := Word(AData);
            otSLong : enumData := LongInt(AData);
            otULong : enumData := LongWord(AData);
          End;
          If ( ATypeInfo^.Kind = tkInteger ) Then
            PutInt64(AName,ATypeInfo,enumData)
          Else
            PutEnum(AName,ATypeInfo,enumData);
      {$IFDEF WST_DELPHI}
        end;
      {$ENDIF}
      end;
    tkFloat :
      Begin
        floatDt := 0;
        Case GetTypeData(ATypeInfo)^.FloatType Of
          ftSingle : floatDt := Single(AData);
          ftDouble : floatDt := Double(AData);
          ftExtended : floatDt := Extended(AData);
          ftCurr : floatDt := Currency(AData);
          ftComp : floatDt := Comp(AData);
        End;
        PutFloat(AName,ATypeInfo,floatDt);
      End;
  End;
end;

procedure TJsonRpcBaseFormatter.Put(
  const ANameSpace : string;
  const AName : String;
  const ATypeInfo : PTypeInfo;
  const AData
);
begin
  Put(AName,ATypeInfo,AData);
end;

procedure TJsonRpcBaseFormatter.PutScopeInnerValue(const ATypeInfo : PTypeInfo; const AData);
var
  locName : string;
  int64Data : Int64;
{$IFDEF HAS_QWORD}
  uint64Data : QWord;
{$ENDIF HAS_QWORD}
  strData : string;
  boolData : Boolean;
  enumData : TEnumIntType;
  floatDt : Extended;
  wideStrData : WideString;
{$IFDEF WST_UNICODESTRING}
  unicodeStrData : UnicodeString;
{$ENDIF WST_UNICODESTRING}
  ansiCharData : AnsiChar;
  wideCharData : WideChar;
begin
  locName := s_inner_value;
  Case ATypeInfo^.Kind Of
    tkChar :
      begin
        ansiCharData := AnsiChar(AData);
        PutAnsiChar(locName,ATypeInfo,ansiCharData);
      end;
    tkWChar :
      begin
        wideCharData := WideChar(AData);
        PutWideChar(locName,ATypeInfo,wideCharData);
      end;
    tkLString{$IFDEF FPC},tkAString{$ENDIF} :
      Begin
        strData := String(AData);
        PutStr(locName,ATypeInfo,strData);
      End;
    tkWString :
      begin
        wideStrData := WideString(AData);
        PutWideStr(locName,ATypeInfo,wideStrData);
      end;
{$IFDEF WST_UNICODESTRING}
    tkUString :
      begin
        unicodeStrData := UnicodeString(AData);
        PutUnicodeStr(locName,ATypeInfo,unicodeStrData);
      end;
{$ENDIF WST_UNICODESTRING}
    tkInt64 :
      Begin
        int64Data := Int64(AData);
        PutInt64(locName,ATypeInfo,int64Data);
      End;
{$IFDEF HAS_QWORD}
    tkQWord :
      Begin
        uint64Data := QWord(AData);
        PutUInt64(locName,ATypeInfo,uint64Data);
      End;
{$ENDIF HAS_QWORD}
    tkClass, tkRecord :
      begin
        raise EJsonRpcException.Create('Inner Scope value must be a "simple type" value.');
      end;
    {$IFDEF FPC}
    tkBool :
      Begin
        boolData := Boolean(AData);
        PutBool(locName,ATypeInfo,boolData);
      End;
    {$ENDIF}
    tkInteger, tkEnumeration :
      begin
      {$IFDEF WST_DELPHI}
        if ( ATypeInfo^.Kind = tkEnumeration ) and
           ( GetTypeData(ATypeInfo)^.BaseType^ = TypeInfo(Boolean) )
        then begin
          boolData := Boolean(AData);
          PutBool(locName,ATypeInfo,boolData);
        end else begin
      {$ENDIF}
          enumData := 0;
          Case GetTypeData(ATypeInfo)^.OrdType Of
            otSByte : enumData := ShortInt(AData);
            otUByte : enumData := Byte(AData);
            otSWord : enumData := SmallInt(AData);
            otUWord : enumData := Word(AData);
            otSLong : enumData := LongInt(AData);
            otULong : enumData := LongWord(AData);
          End;
          If ( ATypeInfo^.Kind = tkInteger ) Then
            PutInt64(locName,ATypeInfo,enumData)
          Else
            PutEnum(locName,ATypeInfo,enumData);
      {$IFDEF WST_DELPHI}
        end;
      {$ENDIF}
      end;
    tkFloat :
      Begin
        floatDt := 0;
        Case GetTypeData(ATypeInfo)^.FloatType Of
          ftSingle : floatDt := Single(AData);
          ftDouble : floatDt := Double(AData);
          ftExtended : floatDt := Extended(AData);
          ftCurr : floatDt := Currency(AData);
          ftComp : floatDt := Comp(AData);
        End;
        PutFloat(locName,ATypeInfo,floatDt);
      End;
  End;
end;

function TJsonRpcBaseFormatter.Get(
  const ATypeInfo : PTypeInfo;
  var AName : String;
  var AData
) : Boolean;
Var
  int64Data : Int64;
{$IFDEF HAS_QWORD}
  uint64Data : QWord;
{$ENDIF HAS_QWORD}
  strData : string;
  objData : TObject;
  boolData : Boolean;
  enumData : TEnumIntType;
  floatDt : Extended;
  recObject : Pointer;
{$IFDEF WST_UNICODESTRING}
  unicodeStrData : UnicodeString;
{$ENDIF WST_UNICODESTRING}
  WideStrData : WideString;
  ansiCharData : AnsiChar;
  wideCharData : WideChar;
begin
  Case ATypeInfo^.Kind Of
    tkChar :
      begin
        ansiCharData := #0;
        Result := GetAnsiChar(ATypeInfo,AName,ansiCharData);
        if Result then
          AnsiChar(AData) := ansiCharData;
      end;
    tkWChar :
      begin
        wideCharData := #0;
        Result := GetWideChar(ATypeInfo,AName,wideCharData);
        if Result then
          WideChar(AData) := wideCharData;
      end;
    tkInt64 :
      Begin
        int64Data := 0;
        Result := GetInt64(ATypeInfo,AName,int64Data);
        if Result then
          Int64(AData) := int64Data;
      End;
{$IFDEF HAS_QWORD}
    tkQWord :
      Begin
        uint64Data := 0;
        Result := GetUInt64(ATypeInfo,AName,uint64Data);
        if Result then
          QWord(AData) := uint64Data;
      End;
{$ENDIF HAS_QWORD}
    tkLString{$IFDEF FPC},tkAString{$ENDIF} :
      Begin
        strData := '';
        Result := GetStr(ATypeInfo,AName,strData);
        if Result then
          String(AData) := strData;
      End;
{$IFDEF WST_UNICODESTRING}
    tkUString :
      Begin
        unicodeStrData := '';
        Result := GetUnicodeStr(ATypeInfo,AName,unicodeStrData);
        if Result then
          UnicodeString(AData) := unicodeStrData;
      End;
{$ENDIF WST_UNICODESTRING}
    tkWString :
      Begin
        WideStrData := '';
        Result := GetWideStr(ATypeInfo,AName,WideStrData);
        if Result then
          WideString(AData) := WideStrData;
      End;
    tkClass :
      Begin
        objData := TObject(AData);
        Result := GetObj(ATypeInfo,AName,objData);
        if Result then
          TObject(AData) := objData;
      End;
    tkRecord :
      begin
        recObject := Pointer(@AData);
        Result := GetRecord(ATypeInfo,AName,recObject);
      end;
    {$IFDEF FPC}
    tkBool :
      Begin
        boolData := False;
        Result := GetBool(ATypeInfo,AName,boolData);
        if Result then
          Boolean(AData) := boolData;
      End;
    {$ENDIF}
    tkInteger, tkEnumeration :
      Begin
      {$IFDEF WST_DELPHI}
        if ( ATypeInfo^.Kind = tkEnumeration ) and
           ( GetTypeData(ATypeInfo)^.BaseType^ = TypeInfo(Boolean) )
        then begin
          boolData := False;
          Result := GetBool(ATypeInfo,AName,boolData);
          if Result then
            Boolean(AData) := boolData;
        end else begin
      {$ENDIF}
          enumData := 0;
          if ( ATypeInfo^.Kind = tkInteger ) then
            Result := GetInt64(ATypeInfo,AName,enumData)
          else
            Result := GetEnum(ATypeInfo,AName,enumData);
          if Result then begin
            case GetTypeData(ATypeInfo)^.OrdType of
              otSByte : ShortInt(AData)    := enumData;
              otUByte : Byte(AData)        := enumData;
              otSWord : SmallInt(AData)    := enumData;
              otUWord : Word(AData)        := enumData;
              otSLong : LongInt(AData)     := enumData;
              otULong : LongWord(AData)    := enumData;
            end;
          end;
      {$IFDEF WST_DELPHI}
        end;
      {$ENDIF}
      End;
    tkFloat :
      Begin
        floatDt := 0;
        Result := GetFloat(ATypeInfo,AName,floatDt);
        if Result then begin
          case GetTypeData(ATypeInfo)^.FloatType of
            ftSingle : Single(AData)    := floatDt;
            ftDouble : Double(AData)    := floatDt;
            ftExtended : Extended(AData)    := floatDt;
            ftCurr : Currency(AData)    := floatDt;
{$IFDEF HAS_COMP}
            ftComp : Comp(AData)    := floatDt;
{$ENDIF}
          end;
        end;
      End;
  End;
end;

function TJsonRpcBaseFormatter.Get(
  const ATypeInfo : PTypeInfo;
  const ANameSpace : string;
  var AName : string;
  var AData
) : Boolean;
begin
  Result := Get(ATypeInfo,AName,AData);
end;

procedure TJsonRpcBaseFormatter.GetScopeInnerValue(const ATypeInfo : PTypeInfo; var AData);
var
  locName : string;
  int64Data : Int64;
{$IFDEF HAS_QWORD}
  uint64Data : QWord;
{$ENDIF HAS_QWORD}
  strData : string;
  boolData : Boolean;
  enumData : TEnumIntType;
  floatDt : Extended;
  wideStrData : WideString;
{$IFDEF WST_UNICODESTRING}
  unicodeStrData : UnicodeString;
{$ENDIF WST_UNICODESTRING}
  ansiCharData : AnsiChar;
  wideCharData : WideChar;
begin
  locName := s_inner_value;
  Case ATypeInfo^.Kind Of
    tkChar :
      begin
        ansiCharData := #0;
        GetAnsiChar(ATypeInfo,locName,ansiCharData);
        AnsiChar(AData) := ansiCharData;
      end;
    tkWChar :
      begin
        wideCharData := #0;
        GetWideChar(ATypeInfo,locName,wideCharData);
        WideChar(AData) := wideCharData;
      end;
    tkInt64 :
      Begin
        int64Data := 0;
        GetInt64(ATypeInfo,locName,int64Data);
        Int64(AData) := int64Data;
      End;
{$IFDEF HAS_QWORD}
    tkQWord :
      Begin
        uint64Data := 0;
        GetUInt64(ATypeInfo,locName,uint64Data);
        QWord(AData) := uint64Data;
      End;
{$ENDIF HAS_QWORD}
    tkLString{$IFDEF FPC},tkAString{$ENDIF} :
      Begin
        strData := '';
        GetStr(ATypeInfo,locName,strData);
        String(AData) := strData;
      End;
    tkWString :
      begin
        wideStrData := '';
        GetWideStr(ATypeInfo,locName,wideStrData);
        WideString(AData) := wideStrData;
      end;
{$IFDEF WST_UNICODESTRING}
    tkUString :
      begin
        unicodeStrData := '';
        GetUnicodeStr(ATypeInfo,locName,unicodeStrData);
        UnicodeString(AData) := unicodeStrData;
      end;
{$ENDIF WST_UNICODESTRING}
    tkClass, tkRecord :
      Begin
        raise EJsonRpcException.Create('Inner Scope value must be a "simple type" value.');
      End;
    {$IFDEF FPC}
    tkBool :
      Begin
        boolData := False;
        GetBool(ATypeInfo,locName,boolData);
        Boolean(AData) := boolData;
      End;
    {$ENDIF}
    tkInteger, tkEnumeration :
      Begin
      {$IFDEF WST_DELPHI}
        if ( ATypeInfo^.Kind = tkEnumeration ) and
           ( GetTypeData(ATypeInfo)^.BaseType^ = TypeInfo(Boolean) )
        then begin
          boolData := False;
          GetBool(ATypeInfo,locName,boolData);
          Boolean(AData) := boolData;
        end else begin
      {$ENDIF}
          enumData := 0;
          If ( ATypeInfo^.Kind = tkInteger ) Then
            GetInt64(ATypeInfo,locName,enumData)
          Else
            GetEnum(ATypeInfo,locName,enumData);
          Case GetTypeData(ATypeInfo)^.OrdType Of
            otSByte : ShortInt(AData)    := enumData;
            otUByte : Byte(AData)        := enumData;
            otSWord : SmallInt(AData)    := enumData;
            otUWord : Word(AData)        := enumData;
            otSLong : LongInt(AData)     := enumData;
            otULong : LongWord(AData)    := enumData;
          End;
      {$IFDEF WST_DELPHI}
        end;
      {$ENDIF}
      End;
    tkFloat :
      Begin
        floatDt := 0;
        GetFloat(ATypeInfo,locName,floatDt);
        Case GetTypeData(ATypeInfo)^.FloatType Of
          ftSingle : Single(AData)    := floatDt;
          ftDouble : Double(AData)    := floatDt;
          ftExtended : Extended(AData)    := floatDt;
          ftCurr : Currency(AData)    := floatDt;
{$IFDEF HAS_COMP}
          ftComp : Comp(AData)    := floatDt;
{$ENDIF}
        End;
      End;
  End;
end;

function TJsonRpcBaseFormatter.ReadBuffer(
  const AName : string;
  out AResBuffer : string
) : Boolean;
var
  locName : string;
  locBuffer : TJSONData;
begin
  locName := AName;
  Result := GetDataBuffer(locName,locBuffer);
  if Result then
    AResBuffer := locBuffer.AsJSON;
end;

procedure TJsonRpcBaseFormatter.WriteBuffer(const AValue: string);
var
  locStream : TStream;
  locParser : TJSONParser;
  locObject : TJSONData;
begin
  locParser := nil;
  locStream := TStringStream.Create(AValue);
  try
    locParser := TJSONParser.Create(locStream);
    locObject := locParser.Parse();
    try
      case StackTop().ScopeObject.JSONType() of
        jtObject : TJSONObject(StackTop().ScopeObject).Add('__buffer__',locObject);
        jtArray  : TJSONArray(StackTop().ScopeObject).Add(locObject);
        else
          Error('Invalid JSON buffer : Object or Array expected.');
      end;
    except
      FreeAndNil(locObject);
      raise;
    end;
  finally
    locParser.Free();
    locStream.Free();
  end;
end;

procedure TJsonRpcBaseFormatter.SaveToStream(AStream : TStream);
var
  locBuffer : string;
begin
  if Assigned(FRootData) then begin
    locBuffer := FRootData.AsJSON;
    AStream.WriteBuffer(locBuffer[1],Length(locBuffer));
  end;
end;

procedure TJsonRpcBaseFormatter.LoadFromStream(AStream : TStream);
var
  locParser : TJSONParser;
  locObject : TJSONData;
begin
  ClearStack();
  FSerializationStyle := Low(TSerializationStyle);
  locParser := TJSONParser.Create(AStream);
  try
    locObject := locParser.Parse();
    if Assigned(locObject) then begin
      FreeAndNil(FRootData); // it will be set in PushStack()
      case locObject.JSONType() of
        jtObject : PushStack(locObject,stObject);
        jtArray  : PushStack(locObject,stArray);
        else
          Error('Invalid JSON buffer : Object or Array expected.');
      end;
    end else begin
      FreeAndNil(FRootData);
    end;
  finally
    locParser.Free();
  end;
end;

procedure TJsonRpcBaseFormatter.Error(const AMsg : string);
begin
  raise EJsonRpcException.Create(AMsg);
end;

procedure TJsonRpcBaseFormatter.Error(const AMsg : string; const AArgs : array of const);
begin
  raise EJsonRpcException.CreateFmt(AMsg,AArgs);
end;

constructor TJsonRpcBaseFormatter.Create();
begin
  inherited Create();
  FStack := TObjectStack.Create();
end;

destructor TJsonRpcBaseFormatter.Destroy();
begin
  FStack.Free();
  FreeAndNil(FRootData);
  inherited Destroy();
end;

{ TStackItem }

function TStackItem.GetItemCount() : Integer;
begin
  Result := FScopeObject.Count;
end;

constructor TStackItem.Create(AScopeObject : TJSONData; AScopeType : TScopeType);
begin
  FScopeObject := AScopeObject;
  FScopeType := AScopeType;
end;

{$IFDEF HAS_QWORD}
function TStackItem.CreateUInt64Buffer(
  const AName  : string;
  const AValue : QWord
) : TJSONData;
begin
  Result := CreateFloatBuffer(AName,AValue);
end;
{$ENDIF HAS_QWORD}

{ TObjectStackItem }

function TObjectStackItem.GetDataObject() : TJSONObject;
begin
  Result := TJSONObject(ScopeObject);
end;

constructor TObjectStackItem.Create(AScopeObject : TJSONObject);
begin
  inherited Create(AScopeObject,stObject);
end;

function TObjectStackItem.FindNode(var ANodeName : string) : TJSONData;
begin
  Result := FindObject(GetDataObject(),ANodeName);
end;

function TObjectStackItem.CreateStringBuffer(
  const AName  : string;
  const AValue : TJSONStringType
) : TJSONData;
var
  locObj : TJSONObject;
  i : PtrInt;
begin
  locObj := GetDataObject();
  Result := FindObject(locObj,AName);
  if ( Result = nil ) then begin
    i := locObj.Add(AName,AValue);
    Result := locObj.Items[i];
  end else begin
    Result.AsString := AValue;
  end;
end;

function TObjectStackItem.CreateIntBuffer(
  const AName : string;
  const AValue : TJsonInteger
) : TJSONData;
var
  locObj : TJSONObject;
  i : PtrInt;
begin
  locObj := GetDataObject();
  Result := FindObject(locObj,AName);
  if ( Result = nil ) then begin
    i := locObj.Add(AName,AValue);
    Result := locObj.Items[i];
  end else begin
    Result.AsInteger := AValue;
  end;
end;

function TObjectStackItem.CreateInt64Buffer(
  const AName : string;  
  const AValue : Int64
) : TJSONData;  
{$IFDEF WST_HAS_JSON_INT64}     
var
  locObj : TJSONObject;
  i : PtrInt;
begin
  locObj := GetDataObject();
  Result := FindObject(locObj,AName);
  if ( Result = nil ) then begin
    i := locObj.Add(AName,AValue);
    Result := locObj.Items[i];
  end else begin
    Result.AsInt64 := AValue;
  end;   
end;
{$ELSE WST_HAS_JSON_INT64}
begin
  Result := CreateFloatBuffer(AName,AValue);
end;
{$ENDIF WST_HAS_JSON_INT64}

function TObjectStackItem.CreateFloatBuffer(
  const AName : string;
  const AValue : TJSONFloat
) : TJSONData;
var
  locObj : TJSONObject;
  i : PtrInt;
begin
  locObj := GetDataObject();
  Result := FindObject(locObj,AName);
  if ( Result = nil ) then begin
    i := locObj.Add(AName,AValue);
    Result := locObj.Items[i];
  end else begin
    Result.AsFloat := AValue;
  end;
end;

function TObjectStackItem.CreateBoolBuffer(
  const AName : string;
  const AValue : Boolean
) : TJSONData;
var
  locObj : TJSONObject;
  i : PtrInt;
begin
  locObj := GetDataObject();
  Result := FindObject(locObj,AName);
  if ( Result = nil ) then begin
    i := locObj.Add(AName,AValue);
    Result := locObj.Items[i];
  end else begin
    Result.AsBoolean := AValue;
  end;
end;

function TObjectStackItem.CreateObjectBuffer(const AName : string) : TJSONObject;
var
  locObj : TJSONObject;
  locIndex : Integer;
begin
  locObj := GetDataObject();
  locIndex := locObj.IndexOfName(AName);
  if ( locIndex = -1 ) then begin
    Result := TJSONObject.Create();
    locObj.Add(AName,Result);
  end else begin
    Result := locObj.Items[locIndex] as TJSONObject;
  end;
end;

function TObjectStackItem.CreateArrayBuffer(const AName : string) : TJSONArray;
var
  locObj : TJSONObject;
begin
  locObj := GetDataObject();
  Result := FindObject(locObj,AName) as TJSONArray;
  if ( Result = nil ) then begin
    Result := TJSONArray.Create();
    locObj.Add(AName,Result);
  end;
end;

function TObjectStackItem.NilItem(AItem : TJSONData) : TJSONData;
var
  locPos : PtrInt;
begin
  locPos := GetDataObject().IndexOf(AItem);
  if ( locPos < 0 ) then
    raise EJsonRpcException.Create('Can not "Nil" an object not owned.');
  Result := TJSONNull.Create();
  try
    GetDataObject().Items[locPos] := Result;
  except
    FreeAndNil(Result);
    raise;
  end;
end;

function TObjectStackItem.GetScopeItemNames(const AReturnList : TStrings) : Integer;
var
  i, c : PtrInt;
  locObj : TJSONObject;
begin
  AReturnList.Clear();
  locObj := GetDataObject();
  c := locObj.Count;
  for i := 0 to Pred(c) do begin
    AReturnList.Add(locObj.Names[i]);
  end;
  Result := AReturnList.Count;
end;

{ TArrayStackItem }

function TArrayStackItem.GetDataObject() : TJSONArray;
begin
  Result := TJSONArray(ScopeObject);
end;

constructor TArrayStackItem.Create(AScopeObject : TJSONArray);
begin
  inherited Create(AScopeObject,stArray);
end;

function TArrayStackItem.FindNode(var ANodeName : string) : TJSONData;
begin
  if ( FIndex >= GetDataObject().Count ) then
    raise EJsonRpcException.CreateFmt('Index out of bound : %d; Node Name = "%s".',[FIndex,ANodeName]);
  Result:= GetDataObject().Items[FIndex];
  Inc(FIndex);
end;

function TArrayStackItem.CreateStringBuffer(const AName : string; const AValue : TJSONStringType) : TJSONData;
begin
  Result := GetDataObject().Items[GetDataObject().Add(AValue)];
end;

function TArrayStackItem.CreateIntBuffer(const AName : string; const AValue : TJsonInteger) : TJSONData;
begin
  Result := GetDataObject().Items[GetDataObject().Add(AValue)];
end;

function TArrayStackItem.CreateInt64Buffer(
  const AName : string;  
  const AValue : Int64
) : TJSONData;  
begin
{$IFDEF WST_HAS_JSON_INT64} 
  Result := GetDataObject().Items[GetDataObject().Add(AValue)];
{$ELSE WST_HAS_JSON_INT64} 
  Result := CreateFloatBuffer(AName,AValue);
{$ENDIF WST_HAS_JSON_INT64} 
end;

function TArrayStackItem.CreateFloatBuffer(const AName : string; const AValue : TJSONFloat) : TJSONData;
begin
  Result := GetDataObject().Items[GetDataObject().Add(AValue)];
end;

function TArrayStackItem.CreateBoolBuffer(const AName : string; const AValue : Boolean) : TJSONData;
begin
  Result := GetDataObject().Items[GetDataObject().Add(AValue)];
end;

function TArrayStackItem.CreateObjectBuffer(const AName : string) : TJSONObject;
begin
  Result := TJSONObject.Create();
  try
    GetDataObject().Add(Result);
  except
    FreeAndNil(Result);
    raise;
  end;
end;

function TArrayStackItem.CreateArrayBuffer(const AName : string) : TJSONArray;
begin
  Result := TJSONArray.Create();
  try
    GetDataObject().Add(Result);
  except
    FreeAndNil(Result);
    raise;
  end;
end;

function TArrayStackItem.NilItem(AItem : TJSONData) : TJSONData;
var
  locPos : PtrInt;
begin
  locPos := GetDataObject().IndexOf(AItem);
  if ( locPos < 0 ) then
    raise EJsonRpcException.Create('Can not "Nil" an object not owned.');
  Result := TJSONNull.Create();
  try
    GetDataObject().Items[locPos] := Result;
  except
    FreeAndNil(Result);
    raise;
  end;
end;

function TArrayStackItem.GetScopeItemNames(const AReturnList : TStrings) : Integer;
var
  i : PtrInt;
begin
  AReturnList.Clear();
  for i := 1 to GetDataObject().Count do
    AReturnList.Add('i');
  Result := AReturnList.Count;
end;

{ TNullStackItem }

procedure TNullStackItem.RaiseNotApplicable();
begin
  raise EJsonRpcException.Create('Operation not applicable at a NULL object.');
end;

constructor TNullStackItem.Create(AScopeObject : TJSONNull);
begin
  inherited Create(AScopeObject,stNilScope);
end;

function TNullStackItem.FindNode(var ANodeName : string) : TJSONData;
begin
  Result := nil;
end;

{$WARNINGS OFF} {$HINTS OFF}
function TNullStackItem.CreateStringBuffer(const AName : string; const AValue : TJSONStringType) : TJSONData;
begin
  RaiseNotApplicable();
end;

function TNullStackItem.CreateIntBuffer(const AName : string; const AValue : TJsonInteger) : TJSONData;
begin
  RaiseNotApplicable();
end;

function TNullStackItem.CreateInt64Buffer(const AName : string; const AValue : Int64) : TJSONData;  
begin
  RaiseNotApplicable();   
end;

function TNullStackItem.CreateFloatBuffer(const AName : string; const AValue : TJSONFloat) : TJSONData;
begin
  RaiseNotApplicable();
end;

function TNullStackItem.CreateBoolBuffer(const AName : string; const AValue : Boolean) : TJSONData;
begin
  RaiseNotApplicable();
end;

function TNullStackItem.CreateObjectBuffer(const AName : string) : TJSONObject;
begin
  RaiseNotApplicable();
end;

function TNullStackItem.CreateArrayBuffer(const AName : string) : TJSONArray;
begin
  RaiseNotApplicable();
end;

function TNullStackItem.NilItem(AItem : TJSONData) : TJSONData;
begin
  RaiseNotApplicable();
end;
{$WARNINGS ON} {$HINTS ON}

function TNullStackItem.GetScopeItemNames(const AReturnList : TStrings) : Integer;
begin
  Result := 0;
end;

end.

