{
    Copyright (c) 2013 by Yury Sidorov and the FPC Development Team

    JSON output of a PPU File

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    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.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

 ****************************************************************************}

unit ppujson;
{$mode objfpc}{$H+}

interface

uses
  SysUtils, Classes, ppuout;

type
  { TPpuJsonOutput }

  TPpuJsonOutput = class(TPpuOutput)
  private
    FNeedDelim: array of boolean;
    function JsonStr(const s: string): string;
    procedure BeforeWriteElement;
    procedure WriteAttr(const AName, AValue: string);
  protected
    procedure WriteObjectStart(const AName: string; Def: TPpuDef); override;
    procedure WriteObjectEnd(const AName: string; Def: TPpuDef); override;
    procedure WriteArrayStart(const AName: string); override;
    procedure WriteArrayEnd(const AName: string); override;
    procedure WriteStr(const AName, AValue: string); override;
    procedure WriteInt(const AName: string; AValue: Int64; Signed: boolean); override;
    procedure WriteFloat(const AName: string; AValue: extended); override;
    procedure WriteBool(const AName: string; AValue: boolean); override;
    procedure WriteNull(const AName: string); override;
  public
    constructor Create(OutFileHandle: THandle); override;
    procedure IncI; override;
    procedure DecI; override;
  end;

implementation

{ TPpuJsonOutput }

function TPpuJsonOutput.JsonStr(const s: string): string;
var
  ws: widestring;
  ps: PWideChar;
  pd: PAnsiChar;
  i, slen, dlen, dpos: integer;

  procedure _AddChar(c: ansichar);
  begin
    if dpos = dlen then begin
      dlen:=dlen*2;
      SetLength(Result, dlen);
      pd:=PAnsiChar(Result) + dpos;
    end;
    pd^:=c;
    Inc(pd);
    Inc(dpos);
  end;

var
  c: widechar;
  ss: shortstring;
begin
  ws:=UTF8Decode(s);
  ps:=PWideChar(ws);
  slen:=Length(ws);
  dlen:=slen + 2;
  SetLength(Result, dlen);
  pd:=PAnsiChar(Result);
  dpos:=0;
  _AddChar('"');
  while slen > 0 do begin
    c:=ps^;
    case c of
      '"', '\', '/':
        begin
          _AddChar('\');
          _AddChar(c);
        end;
      #8:
        begin
          _AddChar('\');
          _AddChar('b');
        end;
      #9:
        begin
          _AddChar('\');
          _AddChar('t');
        end;
      #10:
        begin
          _AddChar('\');
          _AddChar('n');
        end;
      #13:
        begin
          _AddChar('\');
          _AddChar('r');
        end;
      #12:
        begin
          _AddChar('\');
          _AddChar('f');
        end;
      else
        if (c < #32) or (c > #127) then begin
          _AddChar('\');
          _AddChar('u');
          ss:=hexStr(integer(c), 4);
          for i:=1 to 4 do
            _AddChar(ss[i]);
        end
        else
          _AddChar(c);
    end;
    Inc(ps);
    Dec(slen);
  end;
  _AddChar('"');
  SetLength(Result, dpos);
end;

procedure TPpuJsonOutput.BeforeWriteElement;
begin
  if FNeedDelim[Indent] then
    WriteLn(',');
  FNeedDelim[Indent]:=True;
end;

procedure TPpuJsonOutput.WriteAttr(const AName, AValue: string);
begin
  BeforeWriteElement;
  if AName <> '' then
    Write(Format('"%s": %s', [AName, AValue]))
  else
    Write(AValue);
end;

procedure TPpuJsonOutput.WriteStr(const AName, AValue: string);
begin
  WriteAttr(AName, JsonStr(AValue));
end;

procedure TPpuJsonOutput.WriteInt(const AName: string; AValue: Int64; Signed: boolean);
begin
  if Signed then
    WriteAttr(AName, IntToStr(AValue))
  else
    WriteAttr(AName, IntToStr(QWord(AValue)));
end;

procedure TPpuJsonOutput.WriteFloat(const AName: string; AValue: extended);
var
  s: string;
begin
  Str(AValue, s);
  WriteAttr(AName, s);
end;

procedure TPpuJsonOutput.WriteBool(const AName: string; AValue: boolean);
begin
  if AValue then
    WriteAttr(AName, 'true')
  else
    WriteAttr(AName, 'false');
end;

procedure TPpuJsonOutput.WriteNull(const AName: string);
begin
  WriteAttr(AName, 'null');
end;

procedure TPpuJsonOutput.WriteArrayStart(const AName: string);
begin
  WriteAttr(AName, '[');
  WriteLn;
  inherited;
end;

procedure TPpuJsonOutput.WriteArrayEnd(const AName: string);
begin
  inherited;
  Write(']');
end;

procedure TPpuJsonOutput.WriteObjectStart(const AName: string; Def: TPpuDef);
begin
  WriteAttr(AName, '{');
  WriteLn;
  inherited;
end;

procedure TPpuJsonOutput.WriteObjectEnd(const AName: string; Def: TPpuDef);
begin
  inherited;
  Write('}');
end;

constructor TPpuJsonOutput.Create(OutFileHandle: THandle);
begin
  inherited Create(OutFileHandle);
  SetLength(FNeedDelim, 10);
  FNeedDelim[0]:=False;
end;

procedure TPpuJsonOutput.IncI;
begin
  inherited IncI;
  if Length(FNeedDelim) >= Indent then
    SetLength(FNeedDelim, Indent + 1);
  FNeedDelim[Indent]:=False;
end;

procedure TPpuJsonOutput.DecI;
begin
  if FNeedDelim[Indent] then
    WriteLn;
  inherited DecI;
end;

end.

