unit ufJsonFormat;
{$INCLUDE 'Filters.inc'}

interface

uses
  SysUtils, Classes,
  uJSON,
  uFilter, uMessages;

type
  TFilterJsonFormat = class(TFilter)
  protected
    function LoadContent(const Content: string): TJSON;
  public
    class function ID: string; override;
    class function Name: string; override;
    class function Priority: TFilter.TPriority; override;
    procedure Clear; override;
    function IsSupported(const FileName, Content: string): boolean; override;
    function Execute(const FileName, Content: string): string; override;
  end;

implementation

{ TFilterJsonFormat }

procedure TFilterJsonFormat.Clear;
begin
  inherited;
  Description := GetMsg(MFilterJSONFormatDescription);
  FileMasks.Add('*.json');
end;

function TFilterJsonFormat.Execute(const FileName, Content: string): string;
var
  JSON: TJSON;
begin
  JSON := LoadContent(Content);
  try
    Result := JSON.AsJSon(True, False);
  finally
    JSONFree(JSON);
  end;
end;

class function TFilterJsonFormat.ID: string;
begin
  Result := 'json';
end;

function TFilterJsonFormat.IsSupported(const FileName, Content: string): boolean;
type
  TState = (stStart, stBOM, stStartingWhiteSpace);
  TSupported = (suUnknown, suNo, suYes);
var
  Supported: TSupported;
  State: TState;
  i, n: integer;
begin
  Result := False;
  if inherited IsSupported(FileName, Content) then
  begin
    try
      Supported := suUnknown;
      n := Length(Content);
      // File must start with an optional BOM, optional whitespace, '{'
      begin
        State := stStart;
        for i := 1 to n do
        begin
          case Ord(Content[i]) of
            $feff:
              if State = stStart then
                State := stBOM
              else
                Supported := suNo;
            0, 9, 10, 13, 32:
              State := stStartingWhiteSpace;
            Ord('{'):
              Supported := suYes;
            else
              Supported := suNo;
          end;
          if Supported <> suUnknown then
            Break;
        end;
      end;
      // File must end with '}', optional whitespace
      if Supported = suYes then
      begin
        Supported := suUnknown;
        for i := n downto 1 do
        begin
          case Ord(Content[i]) of
            0, 9, 10, 13, 32:
              ;
            Ord('}'):
              Supported := suYes;
            else
              Supported := suNo;
          end;
          if Supported <> suUnknown then
            Break;
        end;
      end;
      Result := (Supported = suYes);
    except
      Result := False;
    end;
  end;
end;

function TFilterJsonFormat.LoadContent(const Content: string): TJSON;
begin
  Result := JSONCreate(Content);
end;

class function TFilterJsonFormat.Name: string;
begin
  Result := GetMsg(MFilterJSONFormatName);
end;

class function TFilterJsonFormat.Priority: TFilter.TPriority;
begin
  Result := prLow;
end;

initialization
  TRegisteredFilters.Add(TFilterJsonFormat);

end.
