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.Collections.pas b/lib/delphi/src/Thrift.Collections.pas
index b2206cb..2e13724 100644
--- a/lib/delphi/src/Thrift.Collections.pas
+++ b/lib/delphi/src/Thrift.Collections.pas
@@ -22,7 +22,7 @@
interface
uses
- Generics.Collections, Generics.Defaults, Thrift.Utils;
+ SysUtils, Generics.Collections, Generics.Defaults, Thrift.Utils;
type
@@ -30,11 +30,11 @@
TArray<T> = array of T;
{$IFEND}
- IThriftContainer = interface
- ['{93DEF5A0-D162-461A-AB22-5B4EE0734050}']
- function ToString: string;
+ IThriftContainer = interface( ISupportsToString)
+ ['{E05C0F9D-A4F5-491D-AADA-C926B4BDB6E4}']
end;
+
IThriftDictionary<TKey,TValue> = interface(IThriftContainer)
['{25EDD506-F9D1-4008-A40F-5940364B7E46}']
function GetEnumerator: TEnumerator<TPair<TKey,TValue>>;
@@ -64,7 +64,7 @@
property Values: TDictionary<TKey,TValue>.TValueCollection read GetValues;
end;
- TThriftDictionaryImpl<TKey,TValue> = class( TInterfacedObject, IThriftDictionary<TKey,TValue>)
+ TThriftDictionaryImpl<TKey,TValue> = class( TInterfacedObject, IThriftDictionary<TKey,TValue>, IThriftContainer, ISupportsToString)
private
FDictionaly : TDictionary<TKey,TValue>;
protected
@@ -95,6 +95,7 @@
public
constructor Create(ACapacity: Integer = 0);
destructor Destroy; override;
+ function ToString : string; override;
end;
IThriftList<T> = interface(IThriftContainer)
@@ -140,7 +141,7 @@
property Items[Index: Integer]: T read GetItem write SetItem; default;
end;
- TThriftListImpl<T> = class( TInterfacedObject, IThriftList<T>)
+ TThriftListImpl<T> = class( TInterfacedObject, IThriftList<T>, IThriftContainer, ISupportsToString)
private
FList : TList<T>;
protected
@@ -186,6 +187,7 @@
public
constructor Create;
destructor Destroy; override;
+ function ToString : string; override;
end;
IHashSet<TValue> = interface(IThriftContainer)
@@ -202,7 +204,7 @@
function Remove( const item: TValue ): Boolean;
end;
- THashSetImpl<TValue> = class( TInterfacedObject, IHashSet<TValue>)
+ THashSetImpl<TValue> = class( TInterfacedObject, IHashSet<TValue>, IThriftContainer, ISupportsToString)
private
FDictionary : IThriftDictionary<TValue,Integer>;
FIsReadOnly: Boolean;
@@ -219,6 +221,7 @@
function Remove( const item: TValue ): Boolean;
public
constructor Create;
+ function ToString : string; override;
end;
implementation
@@ -287,6 +290,28 @@
end;
end;
+function THashSetImpl<TValue>.ToString : string;
+var elm : TValue;
+ sb : TThriftStringBuilder;
+ first : Boolean;
+begin
+ sb := TThriftStringBuilder.Create('{');
+ try
+ first := TRUE;
+ for elm in FDictionary.Keys do begin
+ if first
+ then first := FALSE
+ else sb.Append(', ');
+
+ sb.Append( StringUtils<TValue>.ToString(elm));
+ end;
+ sb.Append('}');
+ Result := sb.ToString;
+ finally
+ sb.Free;
+ end;
+end;
+
{ TThriftDictionaryImpl<TKey, TValue> }
procedure TThriftDictionaryImpl<TKey, TValue>.Add(const Key: TKey;
@@ -393,6 +418,32 @@
{$IFEND}
end;
+function TThriftDictionaryImpl<TKey, TValue>.ToString : string;
+var pair : TPair<TKey, TValue>;
+ sb : TThriftStringBuilder;
+ first : Boolean;
+begin
+ sb := TThriftStringBuilder.Create('{');
+ try
+ first := TRUE;
+ for pair in FDictionaly do begin
+ if first
+ then first := FALSE
+ else sb.Append(', ');
+
+ sb.Append( '(');
+ sb.Append( StringUtils<TKey>.ToString(pair.Key));
+ sb.Append(' => ');
+ sb.Append( StringUtils<TValue>.ToString(pair.Value));
+ sb.Append(')');
+ end;
+ sb.Append('}');
+ Result := sb.ToString;
+ finally
+ sb.Free;
+ end;
+end;
+
procedure TThriftDictionaryImpl<TKey, TValue>.TrimExcess;
begin
FDictionaly.TrimExcess;
@@ -611,6 +662,28 @@
{$IFEND}
end;
+function TThriftListImpl<T>.ToString : string;
+var elm : T;
+ sb : TThriftStringBuilder;
+ first : Boolean;
+begin
+ sb := TThriftStringBuilder.Create('{');
+ try
+ first := TRUE;
+ for elm in FList do begin
+ if first
+ then first := FALSE
+ else sb.Append(', ');
+
+ sb.Append( StringUtils<T>.ToString(elm));
+ end;
+ sb.Append('}');
+ Result := sb.ToString;
+ finally
+ sb.Free;
+ end;
+end;
+
procedure TThriftListImpl<T>.TrimExcess;
begin
FList.TrimExcess;
diff --git a/lib/delphi/src/Thrift.Protocol.pas b/lib/delphi/src/Thrift.Protocol.pas
index 36509ca..609dfc6 100644
--- a/lib/delphi/src/Thrift.Protocol.pas
+++ b/lib/delphi/src/Thrift.Protocol.pas
@@ -29,6 +29,7 @@
Contnrs,
Thrift.Exception,
Thrift.Stream,
+ Thrift.Utils,
Thrift.Collections,
Thrift.Transport;
@@ -111,12 +112,6 @@
function GetProtocol( const trans: ITransport): IProtocol;
end;
- TThriftStringBuilder = class( TStringBuilder)
- public
- function Append(const Value: TBytes): TStringBuilder; overload;
- function Append(const Value: IThriftContainer): TStringBuilder; overload;
- end;
-
TProtocolException = class( TException)
public
const // TODO(jensg): change into enum
@@ -292,9 +287,8 @@
constructor Create( trans: ITransport );
end;
- IBase = interface
- ['{08D9BAA8-5EAA-410F-B50B-AC2E6E5E4155}']
- function ToString: string;
+ IBase = interface( ISupportsToString)
+ ['{AFF6CECA-5200-4540-950E-9B89E0C1C00C}']
procedure Read( const iprot: IProtocol);
procedure Write( const iprot: IProtocol);
end;
@@ -1034,19 +1028,6 @@
inherited HiddenCreate(Msg);
end;
-{ TThriftStringBuilder }
-
-function TThriftStringBuilder.Append(const Value: TBytes): TStringBuilder;
-begin
- Result := Append( string( RawByteString(Value)) );
-end;
-
-function TThriftStringBuilder.Append(
- const Value: IThriftContainer): TStringBuilder;
-begin
- Result := Append( Value.ToString );
-end;
-
{ TBinaryProtocolImpl.TFactory }
constructor TBinaryProtocolImpl.TFactory.Create(AStrictRead, AStrictWrite: Boolean);
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.