(*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations
 * under the License.
 *)

unit Thrift;

interface

uses
  SysUtils,
  Thrift.Utils,
  Thrift.Exception,
  Thrift.Protocol;

const
  Version = '0.24.0';

type
  TException = Thrift.Exception.TException; // compatibility alias

  TApplicationExceptionSpecializedClass = class of TApplicationExceptionSpecialized;

  TApplicationException = class( TException, IBase, ISupportsToString)
  public
    type
{$SCOPEDENUMS ON}
      TExceptionType = (
        Unknown,
        UnknownMethod,
        InvalidMessageType,
        WrongMethodName,
        BadSequenceID,
        MissingResult,
        InternalError,
        ProtocolError,
        InvalidTransform,
        InvalidProtocol,
        UnsupportedClientType
      );
{$SCOPEDENUMS OFF}
  strict private
    FExceptionType : TExceptionType;

  strict protected
    function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
    function _AddRef: Integer; stdcall;
    function _Release: Integer; stdcall;

  strict protected
    constructor HiddenCreate(const Msg: string);
    class function GetSpecializedExceptionType(AType: TExceptionType): TApplicationExceptionSpecializedClass;

  public
    // purposefully hide inherited constructor
    class function Create(const Msg: string): TApplicationException; overload; deprecated 'Use specialized TApplicationException types (or regenerate from IDL)';
    class function Create: TApplicationException; overload; deprecated 'Use specialized TApplicationException types (or regenerate from IDL)';
    class function Create( AType: TExceptionType): TApplicationException; overload; deprecated 'Use specialized TApplicationException types (or regenerate from IDL)';
    class function Create( AType: TExceptionType; const msg: string): TApplicationException; overload; deprecated 'Use specialized TApplicationException types (or regenerate from IDL)';

    function Type_: TExceptionType; virtual;

    procedure IBase_Read( const iprot: IProtocol);
    procedure IBase.Read = IBase_Read;

    class function Read( const iprot: IProtocol): TApplicationException;
    procedure Write( const oprot: IProtocol );
  end;

  // Needed to remove deprecation warning
  TApplicationExceptionSpecialized = class abstract (TApplicationException)
  strict protected
    class function GetType: TApplicationException.TExceptionType;  virtual; abstract;
  public
    constructor Create(const Msg: string);
    function Type_: TApplicationException.TExceptionType; override;
  end;

  TApplicationExceptionUnknown = class (TApplicationExceptionSpecialized)
  strict protected
    class function GetType: TApplicationException.TExceptionType;  override;
  end;

  TApplicationExceptionUnknownMethod = class (TApplicationExceptionSpecialized)
  strict protected
    class function GetType: TApplicationException.TExceptionType;  override;
  end;

  TApplicationExceptionInvalidMessageType = class (TApplicationExceptionSpecialized)
  strict protected
    class function GetType: TApplicationException.TExceptionType;  override;
  end;

  TApplicationExceptionWrongMethodName = class (TApplicationExceptionSpecialized)
  strict protected
    class function GetType: TApplicationException.TExceptionType;  override;
  end;

  TApplicationExceptionBadSequenceID = class (TApplicationExceptionSpecialized)
  strict protected
    class function GetType: TApplicationException.TExceptionType;  override;
  end;

  TApplicationExceptionMissingResult = class (TApplicationExceptionSpecialized)
  strict protected
    class function GetType: TApplicationException.TExceptionType;  override;
  end;

  TApplicationExceptionInternalError = class (TApplicationExceptionSpecialized)
  strict protected
    class function GetType: TApplicationException.TExceptionType;  override;
  end;

  TApplicationExceptionProtocolError = class (TApplicationExceptionSpecialized)
  strict protected
    class function GetType: TApplicationException.TExceptionType;  override;
  end;

  TApplicationExceptionInvalidTransform = class (TApplicationExceptionSpecialized)
  strict protected
    class function GetType: TApplicationException.TExceptionType;  override;
  end;

  TApplicationExceptionInvalidProtocol = class (TApplicationExceptionSpecialized)
  strict protected
    class function GetType: TApplicationException.TExceptionType;  override;
  end;

  TApplicationExceptionUnsupportedClientType = class (TApplicationExceptionSpecialized)
  strict protected
    class function GetType: TApplicationException.TExceptionType;  override;
  end;



implementation

{ TApplicationException }

constructor TApplicationException.HiddenCreate(const Msg: string);
begin
  inherited Create(Msg);
end;

class function TApplicationException.Create(const Msg: string): TApplicationException;
begin
  Result := TApplicationExceptionUnknown.Create(Msg);
end;

class function TApplicationException.Create: TApplicationException;
begin
  Result := TApplicationExceptionUnknown.Create('');
end;

class function TApplicationException.Create( AType: TExceptionType): TApplicationException;
begin
{$WARN SYMBOL_DEPRECATED OFF}
  Result := Create(AType, '');
{$WARN SYMBOL_DEPRECATED DEFAULT}
end;

class function TApplicationException.Create( AType: TExceptionType; const msg: string): TApplicationException;
begin
  Result := GetSpecializedExceptionType(AType).Create(msg);
end;


function TApplicationException.QueryInterface(const IID: TGUID; out Obj): HResult;
begin
  if GetInterface(IID, Obj)
  then result := S_OK
  else result := E_NOINTERFACE;
end;

function TApplicationException._AddRef: Integer;
begin
  result := -1;    // not refcounted
end;

function TApplicationException._Release: Integer;
begin
  result := -1;    // not refcounted
end;


function TApplicationException.Type_: TExceptionType;
begin
  result := FExceptionType;
end;


class function TApplicationException.GetSpecializedExceptionType(AType: TExceptionType): TApplicationExceptionSpecializedClass;
begin
  case AType of
    TExceptionType.UnknownMethod:         Result := TApplicationExceptionUnknownMethod;
    TExceptionType.InvalidMessageType:    Result := TApplicationExceptionInvalidMessageType;
    TExceptionType.WrongMethodName:       Result := TApplicationExceptionWrongMethodName;
    TExceptionType.BadSequenceID:         Result := TApplicationExceptionBadSequenceID;
    TExceptionType.MissingResult:         Result := TApplicationExceptionMissingResult;
    TExceptionType.InternalError:         Result := TApplicationExceptionInternalError;
    TExceptionType.ProtocolError:         Result := TApplicationExceptionProtocolError;
    TExceptionType.InvalidTransform:      Result := TApplicationExceptionInvalidTransform;
    TExceptionType.InvalidProtocol:       Result := TApplicationExceptionInvalidProtocol;
    TExceptionType.UnsupportedClientType: Result := TApplicationExceptionUnsupportedClientType;
  else
    ASSERT( TExceptionType.Unknown = aType);
    Result := TApplicationExceptionUnknown;
  end;
end;


procedure TApplicationException.IBase_Read( const iprot: IProtocol);
var
  field : TThriftField;
  struc : TThriftStruct;
begin
  struc := iprot.ReadStructBegin;
  while ( True ) do
  begin
    field := iprot.ReadFieldBegin;
    if ( field.Type_ = TType.Stop) then begin
      Break;
    end;

    case field.Id of
      1 : begin
        if ( field.Type_ = TType.String_) then begin
          Exception(Self).Message := iprot.ReadString;
        end else begin
          TProtocolUtil.Skip( iprot, field.Type_ );
        end;
      end;

      2 : begin
        if ( field.Type_ = TType.I32) then begin
          FExceptionType := TExceptionType( iprot.ReadI32 );
        end else begin
          TProtocolUtil.Skip( iprot, field.Type_ );
        end;
      end else begin
        TProtocolUtil.Skip( iprot, field.Type_);
      end;
    end;
    iprot.ReadFieldEnd;
  end;
  iprot.ReadStructEnd;
end;


class function TApplicationException.Read( const iprot: IProtocol): TApplicationException;
var instance : TApplicationException;
    base : IBase;
begin
  instance := TApplicationException.CreateFmt('',[]);
  try
    if Supports( instance, IBase, base) then try
      base.Read(iprot);
    finally
      base := nil;  // clear ref before free
    end;

    result := GetSpecializedExceptionType(instance.Type_).Create( Exception(instance).Message);
  finally
    instance.Free;
  end;
end;

procedure TApplicationException.Write( const oprot: IProtocol);
var
  struc : TThriftStruct;
  field : TThriftField;
begin
  Init(struc, 'TApplicationException');
  Init(field);

  oprot.WriteStructBegin( struc );
  if Message <> '' then begin
    field.Name := 'message';
    field.Type_ := TType.String_;
    field.Id := 1;
    oprot.WriteFieldBegin( field );
    oprot.WriteString( Message );
    oprot.WriteFieldEnd;
  end;

  field.Name := 'type';
  field.Type_ := TType.I32;
  field.Id := 2;
  oprot.WriteFieldBegin(field);
  oprot.WriteI32(Integer(Type_));
  oprot.WriteFieldEnd();
  oprot.WriteFieldStop();
  oprot.WriteStructEnd();
end;

{ TApplicationExceptionSpecialized }

constructor TApplicationExceptionSpecialized.Create(const Msg: string);
begin
  inherited HiddenCreate(Msg);
end;

function TApplicationExceptionSpecialized.Type_: TApplicationException.TExceptionType;
begin
  result := GetType;
end;


{ specialized TApplicationExceptions }

class function TApplicationExceptionUnknownMethod.GetType : TApplicationException.TExceptionType;
begin
  result := TExceptionType.UnknownMethod;
end;

class function TApplicationExceptionInvalidMessageType.GetType : TApplicationException.TExceptionType;
begin
  result := TExceptionType.InvalidMessageType;
end;

class function TApplicationExceptionWrongMethodName.GetType : TApplicationException.TExceptionType;
begin
  result := TExceptionType.WrongMethodName;
end;

class function TApplicationExceptionBadSequenceID.GetType : TApplicationException.TExceptionType;
begin
  result := TExceptionType.BadSequenceID;
end;

class function TApplicationExceptionMissingResult.GetType : TApplicationException.TExceptionType;
begin
  result := TExceptionType.MissingResult;
end;

class function TApplicationExceptionInternalError.GetType : TApplicationException.TExceptionType;
begin
  result := TExceptionType.InternalError;
end;

class function TApplicationExceptionProtocolError.GetType : TApplicationException.TExceptionType;
begin
  result := TExceptionType.ProtocolError;
end;

class function TApplicationExceptionInvalidTransform.GetType : TApplicationException.TExceptionType;
begin
  result := TExceptionType.InvalidTransform;
end;

class function TApplicationExceptionInvalidProtocol.GetType : TApplicationException.TExceptionType;
begin
  result := TExceptionType.InvalidProtocol;
end;

class function TApplicationExceptionUnsupportedClientType.GetType : TApplicationException.TExceptionType;
begin
  result := TExceptionType.UnsupportedClientType;
end;

class function TApplicationExceptionUnknown.GetType : TApplicationException.TExceptionType;
begin
  result := TExceptionType.Unknown;
end;


end.
