Browse Source

--- Merging r30827 into '.':
U packages/fcl-web/src/base/restbase.pp
U packages/fcl-web/src/base/restcodegen.pp
--- Recording mergeinfo for merge of r30827 into '.':
U .
--- Merging r30833 into '.':
U packages/fcl-web/src/base/fpwebclient.pp
--- Recording mergeinfo for merge of r30833 into '.':
G .
--- Merging r30834 into '.':
U packages/fcl-web/src/base/fphttpwebclient.pp
--- Recording mergeinfo for merge of r30834 into '.':
G .
--- Merging r30843 into '.':
G packages/fcl-web/src/base/fpwebclient.pp
--- Recording mergeinfo for merge of r30843 into '.':
G .
--- Merging r30868 into '.':
G packages/fcl-web/src/base/restbase.pp
G packages/fcl-web/src/base/restcodegen.pp
--- Recording mergeinfo for merge of r30868 into '.':
G .
--- Merging r31015 into '.':
U packages/fcl-web/src/base/httpdefs.pp
--- Recording mergeinfo for merge of r31015 into '.':
G .
--- Merging r31016 into '.':
U packages/fcl-web/src/base/custcgi.pp
--- Recording mergeinfo for merge of r31016 into '.':
G .
--- Merging r31017 into '.':
G packages/fcl-web/src/base/restbase.pp
--- Recording mergeinfo for merge of r31017 into '.':
G .
--- Merging r31018 into '.':
G packages/fcl-web/src/base/custcgi.pp
U packages/fcl-web/src/base/cgiprotocol.pp
--- Recording mergeinfo for merge of r31018 into '.':
G .
--- Merging r31020 into '.':
G packages/fcl-web/src/base/fpwebclient.pp
--- Recording mergeinfo for merge of r31020 into '.':
G .
--- Merging r31023 into '.':
G packages/fcl-web/src/base/restbase.pp
--- Recording mergeinfo for merge of r31023 into '.':
G .

# revisions: 30827,30833,30834,30843,30868,31015,31016,31017,31018,31020,31023

git-svn-id: branches/fixes_3_0@31090 -

marco 10 years ago
parent
commit
cd2f723385

+ 22 - 21
packages/fcl-web/src/base/cgiprotocol.pp

@@ -5,7 +5,7 @@ unit cgiprotocol;
 interface
 
 Const
-  CGIVarCount = 44 ;
+  CGIVarCount = 45 ;
 
 Type
   TCGIVarArray = Array[1..CGIVarCount] of String;
@@ -36,28 +36,29 @@ Const
     { 22 } 'HTTP_REFERER',
     { 23 } 'HTTP_USER_AGENT',
     { 24 } 'HTTP_COOKIE',
+    { 25 } 'HTTP_IF_NONE_MATCH',
 
      // Additional Apache vars
-    { 25 } 'HTTP_CONNECTION',
-    { 26 } 'HTTP_ACCEPT_LANGUAGE',
-    { 27 } 'HTTP_HOST',
-    { 28 } 'SERVER_SIGNATURE',
-    { 29 } 'SERVER_ADDR',
-    { 30 } 'DOCUMENT_ROOT',
-    { 31 } 'SERVER_ADMIN',
-    { 32 } 'SCRIPT_FILENAME',
-    { 33 } 'REMOTE_PORT',
-    { 34 } 'REQUEST_URI',
-    { 35 } 'CONTENT',
-    { 36 } 'HTTP_X_REQUESTED_WITH',
-    { 37 } 'HTTP_AUTHORIZATION',
-    { 38 } 'SCRIPT_URI',
-    { 39 } 'SCRIPT_URL',
-    { 40 } 'CONTEXT_DOCUMENT_ROOT',
-    { 41 } 'CONTEXT_PREFIX',
-    { 42 } 'HTTP_CACHE_CONTROL',
-    { 43 } 'HTTP_PRAGMA',
-    { 44 } 'REQUEST_SCHEME'
+    { 26 } 'HTTP_CONNECTION',
+    { 27 } 'HTTP_ACCEPT_LANGUAGE',
+    { 28 } 'HTTP_HOST',
+    { 29 } 'SERVER_SIGNATURE',
+    { 30 } 'SERVER_ADDR',
+    { 31 } 'DOCUMENT_ROOT',
+    { 32 } 'SERVER_ADMIN',
+    { 33 } 'SCRIPT_FILENAME',
+    { 34 } 'REMOTE_PORT',
+    { 35 } 'REQUEST_URI',
+    { 36 } 'CONTENT',
+    { 37 } 'HTTP_X_REQUESTED_WITH',
+    { 38 } 'HTTP_AUTHORIZATION',
+    { 39 } 'SCRIPT_URI',
+    { 40 } 'SCRIPT_URL',
+    { 41 } 'CONTEXT_DOCUMENT_ROOT',
+    { 42 } 'CONTEXT_PREFIX',
+    { 43 } 'HTTP_CACHE_CONTROL',
+    { 44 } 'HTTP_PRAGMA',
+    { 45 } 'REQUEST_SCHEME'
     );
 
 Function IndexOfCGIVar(AVarName: String): Integer;

+ 23 - 21
packages/fcl-web/src/base/custcgi.pp

@@ -188,27 +188,28 @@ Const
     { 22: 'HTTP_REFERER'           } (h:hhReferer; v : hvUnknown),
     { 23: 'HTTP_USER_AGENT'        } (h:hhUserAgent; v : hvUnknown),
     { 24: 'HTTP_COOKIE'            } (h:hhUnknown; v : hvCookie),
+    { 25: 'HTTP_IF_NONE_MATCH      } (h:hhIfNoneMatch; v : hvUnknown),
      // Additional Apache vars
-    { 25: 'HTTP_CONNECTION'        } (h:hhConnection; v : hvUnknown),
-    { 26: 'HTTP_ACCEPT_LANGUAGE'   } (h:hhAcceptLanguage; v : hvUnknown),
-    { 27: 'HTTP_HOST'              } (h:hhHost; v : hvUnknown),
-    { 28: 'SERVER_SIGNATURE'       } (h:hhUnknown; v : hvUnknown),
-    { 29: 'SERVER_ADDR'            } (h:hhUnknown; v : hvUnknown),
-    { 30: 'DOCUMENT_ROOT'          } (h:hhUnknown; v : hvUnknown),
-    { 31: 'SERVER_ADMIN'           } (h:hhUnknown; v : hvUnknown),
-    { 32: 'SCRIPT_FILENAME'        } (h:hhUnknown; v : hvUnknown),
-    { 33: 'REMOTE_PORT'            } (h:hhUnknown; v : hvUnknown),
-    { 34: 'REQUEST_URI'            } (h:hhUnknown; v : hvUnknown),
-    { 35: 'CONTENT'                } (h:hhUnknown; v : hvContent),
-    { 36: 'XHTTPREQUESTEDWITH'     } (h:hhUnknown; v : hvXRequestedWith),
-    { 37: 'HTTP_AUTHORIZATION'     } (h:hhAuthorization; v : hvUnknown),
-    { 38: 'SCRIPT_URI'             } (h:hhUnknown; v : hvUnknown),
-    { 39: 'SCRIPT_URL'             } (h:hhUnknown; v : hvURL),
-    { 40: 'CONTEXT_DOCUMENT_ROOT'  } (h:hhUnknown; v : hvUnknown),
-    { 41: 'CONTEXT_PREFIX'         } (h:hhUnknown; v : hvUnknown),
-    { 42: 'HTTP_CACHE_CONTROL'     } (h:hhCacheControl; v : hvUnknown),
-    { 43: 'HTTP_PRAGMA'            } (h:hhPragma; v : hvUnknown),
-    { 44: 'REQUEST_SCHEME'         } (h:hhUnknown; v : hvUnknown)
+    { 26: 'HTTP_CONNECTION'        } (h:hhConnection; v : hvUnknown),
+    { 27: 'HTTP_ACCEPT_LANGUAGE'   } (h:hhAcceptLanguage; v : hvUnknown),
+    { 28: 'HTTP_HOST'              } (h:hhHost; v : hvUnknown),
+    { 29: 'SERVER_SIGNATURE'       } (h:hhUnknown; v : hvUnknown),
+    { 30: 'SERVER_ADDR'            } (h:hhUnknown; v : hvUnknown),
+    { 31: 'DOCUMENT_ROOT'          } (h:hhUnknown; v : hvUnknown),
+    { 32: 'SERVER_ADMIN'           } (h:hhUnknown; v : hvUnknown),
+    { 33: 'SCRIPT_FILENAME'        } (h:hhUnknown; v : hvUnknown),
+    { 34: 'REMOTE_PORT'            } (h:hhUnknown; v : hvUnknown),
+    { 35: 'REQUEST_URI'            } (h:hhUnknown; v : hvUnknown),
+    { 36: 'CONTENT'                } (h:hhUnknown; v : hvContent),
+    { 37: 'XHTTPREQUESTEDWITH'     } (h:hhUnknown; v : hvXRequestedWith),
+    { 38: 'HTTP_AUTHORIZATION'     } (h:hhAuthorization; v : hvUnknown),
+    { 39: 'SCRIPT_URI'             } (h:hhUnknown; v : hvUnknown),
+    { 40: 'SCRIPT_URL'             } (h:hhUnknown; v : hvURL),
+    { 41: 'CONTEXT_DOCUMENT_ROOT'  } (h:hhUnknown; v : hvUnknown),
+    { 42: 'CONTEXT_PREFIX'         } (h:hhUnknown; v : hvUnknown),
+    { 43: 'HTTP_CACHE_CONTROL'     } (h:hhCacheControl; v : hvUnknown),
+    { 44: 'HTTP_PRAGMA'            } (h:hhPragma; v : hvUnknown),
+    { 45: 'REQUEST_SCHEME'         } (h:hhUnknown; v : hvUnknown)
   );
 
 procedure TCgiHandler.GetCGIVarList(List: TStrings);
@@ -370,8 +371,9 @@ begin
           V:=HTTPDecode(V);
         SetHTTPVariable(M.V,V)
         end;
-      end
+      end;
     end;
+  ReadContent;
 end;
 
 procedure TCGIRequest.ReadContent;

+ 13 - 7
packages/fcl-web/src/base/fphttpwebclient.pp

@@ -29,6 +29,7 @@ Type
   Private
     FHTTP : TFPHTTPClient;
   Public
+    function GetHeaders: TStrings;override;
     Constructor Create(AHTTP : TFPHTTPClient);
     Destructor Destroy; override;
   end;
@@ -44,7 +45,6 @@ Type
     Function GetStatusText : String; override;
   Public
     Constructor Create(AHTTP : TFPHTTPRequest);
-    Destructor Destroy; override;
   end;
 
   { TFPHTTPWebClient }
@@ -61,6 +61,10 @@ uses dateutils;
 
 { TFPHTTPRequest }
 
+function TFPHTTPRequest.GetHeaders: TStrings;
+begin
+  Result:=FHTTP.RequestHeaders;
+end;
 
 constructor TFPHTTPRequest.Create(AHTTP: TFPHTTPClient);
 begin
@@ -105,17 +109,13 @@ begin
   FHTTP:=AHTTP.FHTTP;
 end;
 
-Destructor TFPHTTPResponse.Destroy;
-begin
-  FreeAndNil(FHTTP);
-  inherited Destroy;
-end;
 
 { TFPHTTPWebClient }
 
 Function TFPHTTPWebClient.DoCreateRequest: TWebClientRequest;
 begin
   Result:=TFPHTTPRequest.Create(TFPHTTPClient.Create(Self));
+  Result.Headers.NameValueSeparator:=':';
 end;
 
 Function TFPHTTPWebClient.DoHTTPMethod(Const AMethod, AURL: String;
@@ -129,7 +129,6 @@ Var
 begin
   U:=AURL;
   H:=TFPHTTPRequest(ARequest).FHTTP;
-  TFPHTTPRequest(ARequest).FHTTP:=Nil;
   S:=ARequest.ParamsAsQuery;
   if (S<>'') then
     begin
@@ -139,6 +138,13 @@ begin
     end;
   Result:=TFPHTTPResponse.Create(ARequest as TFPHTTPRequest);
   try
+    if Assigned(ARequest.Content) and (ARequest.Headers.IndexOfName('Content-length')<0) then
+      H.AddHeader('Content-length',IntToStr(ARequest.Content.size));
+    if ARequest.Content.Size>0 then
+      begin
+      H.RequestBody:=ARequest.Content;
+      H.RequestBody.Position:=0;
+      end;
     H.HTTPMethod(AMethod,U,Result.Content,[]); // Will rais an exception
   except
     FreeAndNil(Result);

+ 15 - 3
packages/fcl-web/src/base/fpwebclient.pp

@@ -46,6 +46,7 @@ Type
   TWebClientRequest = Class(TRequestResponse)
   Private
     FExtraParams : TStrings;
+    FResponseStream: TStream;
   Protected
     function GetExtraParams: TStrings; virtual;
   Public
@@ -54,7 +55,7 @@ Type
     // Query Parameters to include in request
     Property Params : TStrings Read GetExtraParams;
     // If you want the response to go to this stream, set this in the request
-    Property ResponseContent : TStream Read FStream Write FStream;
+    Property ResponseContent : TStream Read FResponseStream Write FResponseStream;
   end;
 
 
@@ -116,6 +117,8 @@ Type
     // Must create a request.
     Function DoCreateRequest : TWebClientRequest; virtual; abstract;
   Public
+    Destructor Destroy; override;
+
     // Executes the HTTP method AMethod on AURL. Raises an exception on error.
     // On success, TWebClientResponse is returned. It must be freed by the caller.
     Function ExecuteRequest(Const AMethod,AURL : String; ARequest : TWebClientRequest) : TWebClientResponse;
@@ -165,13 +168,13 @@ begin
 end;
 
 
-Destructor TWebClientRequest.Destroy;
+destructor TWebClientRequest.Destroy;
 begin
   FreeAndNil(FExtraParams);
   inherited Destroy;
 end;
 
-Function TWebClientRequest.ParamsAsQuery: String;
+function TWebClientRequest.ParamsAsQuery: String;
 
 Var
   N,V : String;
@@ -232,6 +235,12 @@ begin
     end;
 end;
 
+destructor TAbstractWebClient.Destroy;
+begin
+  LogFile:='';
+  inherited Destroy;
+end;
+
 procedure TAbstractWebClient.LogRequest(AMethod, AURL: String;
   ARequest: TWebClientRequest);
 
@@ -336,7 +345,10 @@ Function TRequestResponse.GetContentAsString: String;
 begin
   SetLength(Result,Content.Size);
   if (Length(Result)>0) then
+    begin
+    Content.Position:=0;
     Content.ReadBuffer(Result[1],Length(Result));
+    end;
 end;
 
 end.

+ 1 - 1
packages/fcl-web/src/base/httpdefs.pp

@@ -356,7 +356,7 @@ type
     property Date: String Index Ord(hhDate) Read GetHeaderValue Write SetHeaderValue;
     property Expires: String Index Ord(hhExpires) Read GetHeaderValue Write SetHeaderValue;
     property From: String Index Ord(hhFrom) Read GetHeaderValue Write SetHeaderValue;
-    Property Host : String Index Ord(hhHost) Read GetFieldValue Write SetFieldValue;
+    Property Host : String Index Ord(hhHost) Read GetHeaderValue Write SetHeaderValue;
     property IfModifiedSince: String Index Ord(hhIfModifiedSince) Read GetHeaderValue Write SetHeaderValue;
     property LastModified: String Index Ord(hhLastModified) Read GetHeaderValue Write SetHeaderValue;
     property Location: String Index Ord(hhLocation) Read GetHeaderValue Write SetHeaderValue;

+ 83 - 8
packages/fcl-web/src/base/restbase.pp

@@ -24,12 +24,15 @@ uses
 Type
   ERESTAPI = Class(Exception);
   TStringArray = Array of string;
+  TStringArrayArray = Array of TStringArray;
   TUnicodeStringArray = Array of UnicodeString;
   TIntegerArray = Array of Integer;
   TInt64Array = Array of Int64;
   TInt32Array = Array of Integer;
   TFloatArray = Array of TJSONFloat;
+  TFloatArrayArray = Array of TFloatArray;
   TDoubleArray = Array of TJSONFloat;
+  TDoubleArrayArray = Array of TDoubleArray;
   TDateTimeArray = Array of TDateTime;
   TBooleanArray = Array of boolean;
   TChildType = (ctArray,ctObject);
@@ -57,6 +60,13 @@ Type
     procedure SetObjectOptions(AValue: TObjectOptions);
     Function GetAdditionalProperties : TJSONObject;
   protected
+{$ifdef ver2_6}
+    // Version 2.6.4 has a bug for i386 where the array cannot be set through RTTI.
+    // This is a helper method that sets the length of the array to the desired length,
+    // After which the new array pointer is read again.
+    // AName is guaranteed to be lowercase
+    Procedure SetArrayLength(const AName : String; ALength : Longint); virtual;
+{$endif}
     Procedure MarkPropertyChanged(AIndex : Integer);
     Function IsDateTimeProp(Info : PTypeInfo) : Boolean;
     Function DateTimePropType(Info : PTypeInfo) : TDateTimeType;
@@ -115,6 +125,19 @@ Type
   end;
   TBaseObjectClass = Class of TBaseObject;
   TObjectArray =  Array of TBaseObject;
+  TObjectArrayArray =  Array of TObjectArray;
+
+  TBaseListEnumerator = class
+  private
+    FList: TFPObjectList;
+    FPosition: Integer;
+  public
+    constructor Create(AList: TFPObjectList);
+    function GetCurrent: TBaseObject; virtual;
+    function MoveNext: Boolean;
+    property Current: TBaseObject read GetCurrent;
+  end;
+  TBaseListEnumeratorClass = Class of TBaseListEnumerator;
 
   { TBaseObjectList }
 
@@ -125,9 +148,11 @@ Type
     function GetO(Aindex : Integer): TBaseObject;
     procedure SetO(Aindex : Integer; AValue: TBaseObject);
     Class Function ObjectClass : TBaseObjectClass; virtual;
+    Function DoCreateEnumerator(AEnumClass : TBaseListEnumeratorClass) : TBaseListEnumerator;
   Public
     Constructor Create(AOptions : TObjectOptions = DefaultObjectOptions); Override;
     Destructor Destroy; override;
+    function GetEnumerator : TBaseListEnumerator;
     Function AddObject(Const AKind : String) : TBaseObject; virtual;
     Property Objects [Aindex : Integer] : TBaseObject Read GetO Write SetO; default;
   end;
@@ -198,7 +223,7 @@ Var
 function DateTimeToRFC3339(ADate :TDateTime):string;
 
 begin
-  Result:=FormatDateTime('yyyy-mm-dd"T"hh:nn:ss.zzz"Z"',ADate);
+  Result:=FormatDateTime('yyyy-mm-dd"T"hh":"nn":"ss"."zzz"Z"',ADate);
 end;
 
 function DateToRFC3339(ADate: TDateTime): string;
@@ -209,7 +234,7 @@ end;
 function TimeToRFC3339(ADate :TDateTime):string;
 
 begin
-  Result:=FormatDateTime('hh:nn:ss.zzz',ADate);
+  Result:=FormatDateTime('hh":"nn":"ss"."zzz',ADate);
 end;
 
 
@@ -443,24 +468,35 @@ begin
   FList[AIndex]:=AValue;
 end;
 
-Class Function TBaseObjectList.ObjectClass: TBaseObjectClass;
+class function TBaseObjectList.ObjectClass: TBaseObjectClass;
 begin
   Result:=TBaseObject;
 end;
 
-Constructor TBaseObjectList.Create(AOptions : TObjectOptions = DefaultObjectOptions);
+function TBaseObjectList.DoCreateEnumerator(AEnumClass: TBaseListEnumeratorClass
+  ): TBaseListEnumerator;
+begin
+  Result:=AEnumClass.Create(FList);
+end;
+
+constructor TBaseObjectList.Create(AOptions: TObjectOptions);
 begin
   inherited Create(AOptions);
   FList:=TFPObjectList.Create;
 end;
 
-Destructor TBaseObjectList.Destroy;
+destructor TBaseObjectList.Destroy;
 begin
   FreeAndNil(FList);
   inherited Destroy;
 end;
 
-Function TBaseObjectList.AddObject(const AKind : String): TBaseObject;
+function TBaseObjectList.GetEnumerator: TBaseListEnumerator;
+begin
+  Result:=TBaseListEnumerator.Create(FList);
+end;
+
+function TBaseObjectList.AddObject(const AKind: String): TBaseObject;
 
 Var
   C : TBaseObjectClass;
@@ -475,6 +511,24 @@ begin
   FList.Add(Result);
 end;
 
+constructor TBAseListEnumerator.Create(AList: TFPObjectList);
+begin
+  inherited Create;
+  FList := AList;
+  FPosition := -1;
+end;
+
+function TBaseListEnumerator.GetCurrent: TBaseObject;
+begin
+  Result := TBaseObject(FList[FPosition]);
+end;
+
+function TBaseListEnumerator.MoveNext: Boolean;
+begin
+  Inc(FPosition);
+  Result := FPosition < FList.Count;
+end;
+
 { TBaseObject }
 
 function TBaseObject.GetDynArrayProp(P: PPropInfo): Pointer;
@@ -482,6 +536,7 @@ begin
   Result:=Pointer(GetObjectProp(Self,P));
 end;
 
+
 procedure TBaseObject.SetDynArrayProp(P: PPropInfo; AValue: Pointer);
 begin
   SetObjectProp(Self,P,TObject(AValue));
@@ -598,7 +653,7 @@ Var
   I : Integer;
   PA : ^pdynarraytypeinfo;
   ET : PTypeInfo;
-  AN : String;
+  LPN,AN : String;
   AP : Pointer;
   S : TJSONSchema;
 
@@ -640,13 +695,26 @@ begin
         FreeAndNil(O[i]);
       end;
     // Clear array
+{$ifdef ver2_6}
+    LPN:=Lowercase(P^.Name);
+    SetArrayLength(LPN,0);
+{$else}
     I:=0;
     DynArraySetLength(AP,P^.PropType,1,@i);
+{$endif}
     // Now, set new length
     I:=AValue.Count;
     // Writeln(ClassName,' (Array) Setting length of array property ',P^.Name,' (type: ',P^.PropType^.Name,')  to ',AValue.Count);
+{$ifdef ver2_6}
+    // Workaround for bug in 2.6.4 that cannot set the array prop correctly.
+    // Call helper routine and re-get array value
+    SetArrayLength(LPN,i);
+    AP:=GetObjectProp(Self,P);
+{$else}
     DynArraySetLength(AP,P^.PropType,1,@i);
+    I:=Length(TObjectArray(AP));
     SetDynArrayProp(P,AP);
+{$endif}
     // Fill in all elements
     For I:=0 to AValue.Count-1 do
       begin
@@ -1061,6 +1129,13 @@ begin
   Result:=fAdditionalProperties
 end;
 
+{$IFDEF VER2_6}
+procedure TBaseObject.SetArrayLength(Const AName: String; ALength: Longint);
+begin
+  Raise ERestAPI.CreateFmt('Unknown Array %s',[AName]);
+end;
+{$ENDIF}
+
 class function TBaseObject.AllowAdditionalProperties: Boolean;
 begin
   Result:=False;
@@ -1081,7 +1156,7 @@ Const
        'to;type;unit;until;uses;var;while;with;xor;dispose;exit;false;new;true;'+
        'as;class;dispinterface;except;exports;finalization;finally;initialization;'+
        'inline;is;library;on;out;packed;property;raise;resourcestring;threadvar;try;'+
-       'private;published;';
+       'private;published;length;setlength;';
 Var
   I : Integer;
 

+ 25 - 1
packages/fcl-web/src/base/restcodegen.pp

@@ -22,20 +22,27 @@ uses
   Classes, SysUtils;
 
 Type
-
+  TCodegenLogType = (cltInfo);
+  TCodegenLogTypes = Set of TCodegenLogType;
+  TCodeGeneratorLogEvent = Procedure (Sender : TObject; LogType : TCodegenLogType; Const Msg : String) of object;
   { TRestCodeGenerator }
 
   TRestCodeGenerator = Class(TComponent)
   Private
+    FAddTimeStamp: Boolean;
     FBaseClassName: String;
+    FBaseListClassName: String;
     FClassPrefix: String;
     FExtraUnits: String;
     FLicenseText: TStrings;
+    FOnLog: TCodeGeneratorLogEvent;
     FOutputUnitName: String;
     FSource : TStrings;
     Findent : String;
   Protected
     // Source manipulation
+    Procedure DoLog(Const Msg : String; AType : TCodegenLogType = cltInfo);
+    Procedure DoLog(Const Fmt : String; Args : Array of const; AType : TCodegenLogType = cltInfo);
     Procedure CreateHeader; virtual;
     Procedure IncIndent;
     Procedure DecIndent;
@@ -62,10 +69,13 @@ Type
     Property Source : TStrings Read FSource;
   Published
     Property BaseClassName : String Read FBaseClassName Write FBaseClassName;
+    Property BaseListClassName : String Read FBaseListClassName Write FBaseListClassName;
     Property OutputUnitName : String Read FOutputUnitName Write FOutputUnitName;
     Property ExtraUnits : String Read FExtraUnits Write FExtraUnits;
     Property ClassPrefix : String Read FClassPrefix Write FClassPrefix;
     Property LicenseText : TStrings Read FLicenseText;
+    Property OnLog : TCodeGeneratorLogEvent Read FOnLog Write FOnlog;
+    Property AddTimeStamp : Boolean Read FAddTimeStamp Write FAddTimeStamp;
   end;
 
 implementation
@@ -203,6 +213,18 @@ begin
   end;
 end;
 
+procedure TRestCodeGenerator.DoLog(const Msg: String; AType: TCodegenLogType);
+begin
+  If Assigned(FOnLog) then
+    FOnLog(Self,Atype,Msg);
+end;
+
+procedure TRestCodeGenerator.DoLog(const Fmt: String; Args: array of const;
+  AType: TCodegenLogType);
+begin
+  DoLog(Format(Fmt,Args),AType);
+end;
+
 procedure TRestCodeGenerator.CreateHeader;
 
 Var
@@ -211,6 +233,8 @@ Var
 begin
   if LicenseText.Count>0 then
     Comment(LicenseText);
+  if AddTimeStamp then
+    Comment('Generated on: '+DateTimeToStr(Now));
   addln('{$MODE objfpc}');
   addln('{$H+}');
   addln('');