THRIFT-1993 Factory to create instances from known (generated) interface types with Delphi

Patch: Jens Geyer
diff --git a/lib/delphi/src/Thrift.TypeRegistry.pas b/lib/delphi/src/Thrift.TypeRegistry.pas
new file mode 100644
index 0000000..1b863d2
--- /dev/null
+++ b/lib/delphi/src/Thrift.TypeRegistry.pas
@@ -0,0 +1,84 @@
+(*

+ * 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.TypeRegistry;

+

+interface

+

+uses

+  Generics.Collections;

+

+type

+  TFactoryMethod<T> = function:T;

+

+  TypeRegistry = class

+  private

+    class var FTypeInfoToFactoryLookup : TDictionary<Pointer, Pointer>;

+  public

+    class constructor Create;

+    class destructor Destroy;

+    class procedure RegisterTypeFactory<F>(const aFactoryMethod: TFactoryMethod<F>);

+    class function  Construct<F>: F;

+  end;

+

+implementation

+

+uses

+  TypInfo;

+

+{ TypeRegistration }

+

+class constructor TypeRegistry.Create;

+begin

+  FTypeInfoToFactoryLookup := TDictionary<Pointer, Pointer>.Create;

+end;

+

+class destructor TypeRegistry.Destroy;

+begin

+  FTypeInfoToFactoryLookup.Free;

+end;

+

+class procedure TypeRegistry.RegisterTypeFactory<F>(const aFactoryMethod: TFactoryMethod<F>);

+var

+  TypeInfo     : Pointer;

+begin

+  TypeInfo := System.TypeInfo(F);

+

+  if (TypeInfo <> nil) and (PTypeInfo(TypeInfo).Kind = tkInterface)

+  then FTypeInfoToFactoryLookup.AddOrSetValue(TypeInfo, @aFactoryMethod);

+end;

+

+class function TypeRegistry.Construct<F>: F;

+var

+  TypeInfo     : PTypeInfo;

+  Factory      : Pointer;

+begin

+  Result := default(F);

+

+  TypeInfo := System.TypeInfo(F);

+

+  if Assigned(TypeInfo) and (TypeInfo.Kind = tkInterface)

+  then begin

+    if FTypeInfoToFactoryLookup.TryGetValue(TypeInfo, Factory)

+    then Result := TFactoryMethod<F>(Factory)();

+  end;

+end;

+

+

+end.

diff --git a/lib/delphi/test/codegen/run-Pascal-Codegen-Tests.bat.tmpl b/lib/delphi/test/codegen/run-Pascal-Codegen-Tests.bat.tmpl
index 8d25eae..6ccd260 100644
--- a/lib/delphi/test/codegen/run-Pascal-Codegen-Tests.bat.tmpl
+++ b/lib/delphi/test/codegen/run-Pascal-Codegen-Tests.bat.tmpl
@@ -58,7 +58,7 @@
 echo.
 echo Generating code, please wait ...
 cd "%TARGET%"
-for %%a in (*.thrift) do "%BIN%\thrift.exe" -v --gen delphi:ansistr_binary "%%a" >> "%LOGFILE%"
+for %%a in (*.thrift) do "%BIN%\thrift.exe" -v --gen delphi:ansistr_binary,register_types "%%a" 2>> "%LOGFILE%"
 REM * for %%a in (*.thrift) do "%BIN%\thrift.exe" -v --gen cpp "%%a" >> NUL:
 cmd /c start notepad "%LOGFILE%"
 cd ..
diff --git a/lib/delphi/test/typeregistry/TestTypeRegistry.dpr b/lib/delphi/test/typeregistry/TestTypeRegistry.dpr
new file mode 100644
index 0000000..64d5771
--- /dev/null
+++ b/lib/delphi/test/typeregistry/TestTypeRegistry.dpr
@@ -0,0 +1,89 @@
+(*
+ * 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.
+ *)
+
+program TestTypeRegistry;
+
+{$APPTYPE CONSOLE}
+
+uses
+  Classes, Windows, SysUtils, Generics.Collections, TypInfo,
+  Thrift in '..\..\src\Thrift.pas',
+  Thrift.Transport in '..\..\src\Thrift.Transport.pas',
+  Thrift.Protocol in '..\..\src\Thrift.Protocol.pas',
+  Thrift.Protocol.JSON in '..\..\src\Thrift.Protocol.JSON.pas',
+  Thrift.Collections in '..\..\src\Thrift.Collections.pas',
+  Thrift.Server in '..\..\src\Thrift.Server.pas',
+  Thrift.Console in '..\..\src\Thrift.Console.pas',
+  Thrift.Utils in '..\..\src\Thrift.Utils.pas',
+  Thrift.Serializer in '..\..\src\Thrift.Serializer.pas',
+  Thrift.Stream in '..\..\src\Thrift.Stream.pas',
+  Thrift.TypeRegistry in '..\..\src\Thrift.TypeRegistry.pas',
+  DebugProtoTest;
+
+type
+  Tester<T : IInterface> = class
+  public
+    class procedure Test;
+  end;
+
+class procedure Tester<T>.Test;
+var instance : T;
+    name : string;
+begin
+  instance := TypeRegistry.Construct<T>;
+  name := GetTypeName(TypeInfo(T));
+  if instance <> nil

+  then Writeln( name, ' = ok')

+  else begin

+    Writeln( name, ' = failed');

+    raise Exception.Create( 'Test with '+name+' failed!');

+  end;

+end;
+
+begin
+  Writeln('Testing ...');
+  Tester<IDoubles>.Test;
+  Tester<IOneOfEach>.Test;

+  Tester<IBonk>.Test;

+  Tester<INesting>.Test;

+  Tester<IHolyMoley>.Test;

+  Tester<IBackwards>.Test;

+  Tester<IEmpty>.Test;

+  Tester<IWrapper>.Test;

+  Tester<IRandomStuff>.Test;

+  Tester<IBase64>.Test;

+  Tester<ICompactProtoTestStruct>.Test;

+  Tester<ISingleMapTestStruct>.Test;

+  Tester<IBlowUp>.Test;

+  Tester<IReverseOrderStruct>.Test;

+  Tester<IStructWithSomeEnum>.Test;

+  Tester<ITestUnion>.Test;

+  Tester<ITestUnionMinusStringField>.Test;

+  Tester<IComparableUnion>.Test;

+  Tester<IStructWithAUnion>.Test;

+  Tester<IPrimitiveThenStruct>.Test;

+  Tester<IStructWithASomemap>.Test;

+  Tester<IBigFieldIdStruct>.Test;

+  Tester<IBreaksRubyCompactProtocol>.Test;

+  Tester<ITupleProtocolTestStruct>.Test;

+  Writeln('Completed.');

+

+

+end.

+