THRIFT-4862 better ToString() support for enums and container types
Client: Delphi
Patch: Jens Geyer

This closes #1795
diff --git a/lib/delphi/src/Thrift.Utils.pas b/lib/delphi/src/Thrift.Utils.pas
index 7e57863..46e238c 100644
--- a/lib/delphi/src/Thrift.Utils.pas
+++ b/lib/delphi/src/Thrift.Utils.pas
@@ -25,12 +25,19 @@
 
 uses
   {$IFDEF OLD_UNIT_NAMES}
-  Classes, Windows, SysUtils, Character, SyncObjs;
+  Classes, Windows, SysUtils, Character, SyncObjs, TypInfo, Rtti;
   {$ELSE}
-  System.Classes, Winapi.Windows, System.SysUtils, System.Character, System.SyncObjs;
+  System.Classes, Winapi.Windows, System.SysUtils, System.Character,
+  System.SyncObjs, System.TypInfo, System.Rtti;
   {$ENDIF}
 
 type
+  ISupportsToString = interface
+    ['{AF71C350-E0CD-4E94-B77C-0310DC8227FF}']
+    function ToString : string;
+  end;
+
+
   IOverlappedHelper = interface
     ['{A1832EFA-2E02-4884-8F09-F0A0277157FA}']
     function Overlapped : TOverlapped;
@@ -55,6 +62,13 @@
   end;
 
 
+  TThriftStringBuilder = class( TStringBuilder)
+  public
+    function Append(const Value: TBytes): TStringBuilder; overload;
+    function Append(const Value: ISupportsToString): TStringBuilder; overload;
+  end;
+
+
   Base64Utils = class sealed
   public
     class function Encode( const src : TBytes; srcOff, len : Integer; dst : TBytes; dstOff : Integer) : Integer; static;
@@ -68,6 +82,16 @@
     class function IsLowSurrogate( const c : Char) : Boolean; static; inline;
   end;
 
+  EnumUtils<T> = class sealed
+  public
+    class function ToString(const value : Integer) : string;  reintroduce; static; inline;
+  end;
+
+  StringUtils<T> = class sealed
+  public
+    class function ToString(const value : T) : string;  reintroduce; static; inline;
+  end;
+
 
 {$IFDEF Win64}
 function InterlockedExchangeAdd64( var Addend : Int64; Value : Int64) : Int64;  
@@ -256,4 +280,51 @@
 {$ENDIF}
 
 
+{ EnumUtils<T> }
+
+class function EnumUtils<T>.ToString(const value : Integer) : string;
+var pType : PTypeInfo;
+begin
+  pType := PTypeInfo(TypeInfo(T));
+  if Assigned(pType) and (pType^.Kind = tkEnumeration)
+  then result := GetEnumName(pType,value)
+  else result := IntToStr(Ord(value));
+end;
+
+
+{ StringUtils<T> }
+
+class function StringUtils<T>.ToString(const value : T) : string;
+var pType : PTypeInfo;
+    base  : ISupportsToString;
+begin
+  pType := PTypeInfo(TypeInfo(T));
+  if Assigned(pType) then begin
+    case pType^.Kind of
+      tkInterface : begin
+        if Supports(IInterface(value), ISupportsToString, base) then begin
+          result := base.toString;
+          Exit;
+        end;
+      end;
+    end;
+  end;
+
+  result := TValue.From<T>(value).ToString;
+end;
+
+
+{ TThriftStringBuilder }
+
+function TThriftStringBuilder.Append(const Value: TBytes): TStringBuilder;
+begin
+  Result := Append( string( RawByteString(Value)) );
+end;
+
+function TThriftStringBuilder.Append( const Value: ISupportsToString): TStringBuilder;
+begin
+  Result := Append( Value.ToString );
+end;
+
+
 end.