From 9ee2f5af12b0746774f7d8c48c4e30d03c7b1d45 Mon Sep 17 00:00:00 2001 From: Daniele Teti Date: Mon, 16 Dec 2024 12:28:18 +0100 Subject: [PATCH] updated template pro --- ...VCFramework.View.Renderers.WebStencils.pas | 38 +++-- sources/TemplatePro.pas | 157 ++++++++---------- 2 files changed, 95 insertions(+), 100 deletions(-) diff --git a/sources/MVCFramework.View.Renderers.WebStencils.pas b/sources/MVCFramework.View.Renderers.WebStencils.pas index b0b9f48c..5fdccca8 100644 --- a/sources/MVCFramework.View.Renderers.WebStencils.pas +++ b/sources/MVCFramework.View.Renderers.WebStencils.pas @@ -173,7 +173,7 @@ procedure TMVCWebStencilsViewEngine.OnGetValue(Sender: TObject; const AObjectNam lValue: TValue; begin AHandled := False; - if ViewModel.TryGetValue(AObjectName, lValue) then + if (ViewModel <> nil) and ViewModel.TryGetValue(AObjectName, lValue) then begin AReplaceText := GetTValueVarAsString(lValue, AObjectName, TWebStencilsProcessor(Sender)); AHandled := True; @@ -205,17 +205,33 @@ procedure TMVCWebStencilsViewEngine.RegisterWSFunctions(WSProcessor: TWebStencil end) as IInvokable, 'json', 'json', '', True, 'Serialize an object to JSON', nil)); - TBindingMethodsFactory.RegisterMethod( - TMethodDescription.Create( - MakeInvokable(function(Args: TArray): IValue - begin - if Length(Args) <> 1 then + TBindingMethodsFactory.RegisterMethod( + TMethodDescription.Create( + MakeInvokable(function(Args: TArray): IValue begin - raise EWebStencilsException.Create('Expected 1 parameter, got ' + Length(Args).ToString); - end; - Result := TValueWrapper.Create(TMVCWebStencilsViewEngine.GetTValueVarAsString(Args[0].GetValue, '', nil)); - end), - 'ValueOf', 'ValueOf', '', True, 'ValueOf returns the inner value of a nullable as string - the non-nullable types are returned as-is', nil)); + if Length(Args) <> 1 then + begin + raise EWebStencilsException.Create('Expected 1 parameter, got ' + Length(Args).ToString); + end; + Result := TValueWrapper.Create(TMVCWebStencilsViewEngine.GetTValueVarAsString(Args[0].GetValue, '', nil)); + end), + 'ValueOf', 'ValueOf', '', True, 'ValueOf returns the inner value of a nullable as string - the non-nullable types are returned as-is', nil)); + + TBindingMethodsFactory.RegisterMethod( + TMethodDescription.Create( + MakeInvokable(function(Args: TArray): IValue + begin + if Length(Args) <> 1 then + begin + raise EWebStencilsException.Create('Expected 1 parameter, got ' + Length(Args).ToString); + end; + if (ViewModel <> nil) and ViewModel.ContainsKey(Args[0].GetValue.AsString) then + Result := TValueWrapper.Create(True) + else + Result := TValueWrapper.Create(False); + end), + 'Defined', 'Defined', '', True, 'Defined returns true if variable is defined', nil)); + finally TMonitor.Exit(gWSLock); diff --git a/sources/TemplatePro.pas b/sources/TemplatePro.pas index 05de3d5b..836ad68f 100644 --- a/sources/TemplatePro.pas +++ b/sources/TemplatePro.pas @@ -58,15 +58,14 @@ TIfThenElseIndex = record IfIndex, ElseIndex: Int64; end; - TTokenType = (ttContent, ttInclude, ttFor, ttEndFor, ttIfThen, ttBoolExpression, ttElse, ttEndIf, ttStartTag, - ttComment, ttJump, ttBlock, ttEndBlock, ttContinue, ttLiteralString, ttEndTag, ttValue, ttFilterName, - ttFilterParameter, ttLineBreak, ttSystemVersion, ttExit, ttEOF, ttInfo); + TTokenType = (ttContent, ttInclude, ttFor, ttEndFor, ttIfThen, ttBoolExpression, ttElse, ttEndIf, ttStartTag, ttComment, ttJump, ttBlock, + ttEndBlock, ttContinue, ttLiteralString, ttEndTag, ttValue, ttFilterName, ttFilterParameter, ttLineBreak, ttSystemVersion, ttExit, + ttEOF, ttInfo); const - TOKEN_TYPE_DESCR: array [Low(TTokenType) .. High(TTokenType)] of string = ('ttContent', 'ttInclude', 'ttFor', - 'ttEndFor', 'ttIfThen', 'ttBoolExpression', 'ttElse', 'ttEndIf', 'ttStartTag', 'ttComment', 'ttJump', 'ttBlock', - 'ttEndBlock', 'ttContinue', 'ttLiteralString', 'ttEndTag', 'ttValue', 'ttFilterName', 'ttFilterParameter', - 'ttLineBreak', 'ttSystemVersion', 'ttExit', 'ttEOF', 'ttInfo'); + TOKEN_TYPE_DESCR: array [Low(TTokenType) .. High(TTokenType)] of string = ('ttContent', 'ttInclude', 'ttFor', 'ttEndFor', 'ttIfThen', + 'ttBoolExpression', 'ttElse', 'ttEndIf', 'ttStartTag', 'ttComment', 'ttJump', 'ttBlock', 'ttEndBlock', 'ttContinue', 'ttLiteralString', + 'ttEndTag', 'ttValue', 'ttFilterName', 'ttFilterParameter', 'ttLineBreak', 'ttSystemVersion', 'ttExit', 'ttEOF', 'ttInfo'); const { ttInfo value1 can be: } @@ -97,8 +96,7 @@ TFilterParameter = record Value2: String; Ref1: Int64; Ref2: Int64; { in case of tokentype = filter, contains the integer value, if any } - class function Create(TokType: TTokenType; Value1: String; Value2: String; Ref1: Int64 = -1; Ref2: Int64 = -1) - : TToken; static; + class function Create(TokType: TTokenType; Value1: String; Value2: String; Ref1: Int64 = -1; Ref2: Int64 = -1): TToken; static; function TokenTypeAsString: String; function ToString: String; procedure SaveToBytes(const aBytes: TBinaryWriter); @@ -115,8 +113,7 @@ TBlockAddress = record TComparandType = (ctEQ, ctNE, ctGT, ctGE, ctLT, ctLE); TTProTemplateFunction = function(const aValue: TValue; const aParameters: TArray): TValue; - TTProTemplateAnonFunction = reference to function(const aValue: TValue; - const aParameters: TArray): TValue; + TTProTemplateAnonFunction = reference to function(const aValue: TValue; const aParameters: TArray): TValue; TTProVariablesInfo = (viSimpleType, viObject, viDataSet, viListOfObject, viJSONObject, viIterable); TTProVariablesInfos = set of TTProVariablesInfo; @@ -138,8 +135,7 @@ TTProVariables = class(TObjectDictionary) constructor Create; end; - TTProCompiledTemplateGetValueEvent = reference to procedure(const DataSource, Members: string; var Value: TValue; - var Handled: Boolean); + TTProCompiledTemplateGetValueEvent = reference to procedure(const DataSource, Members: string; var Value: TValue; var Handled: Boolean); PTProFormatSettings = ^TFormatSettings; @@ -208,11 +204,9 @@ TTProCompiledTemplate = class(TInterfacedObject, ITProCompiledTemplate) function ExecuteFilter(aFunctionName: string; var aParameters: TArray; aValue: TValue; const aVarNameWhereShoudBeApplied: String): TValue; procedure CheckParNumber(const aHowManyPars: Integer; const aParameters: TArray); overload; - procedure CheckParNumber(const aMinParNumber, aMaxParNumber: Integer; - const aParameters: TArray); overload; + procedure CheckParNumber(const aMinParNumber, aMaxParNumber: Integer; const aParameters: TArray); overload; function GetPseudoVariable(const VarIterator: Integer; const PseudoVarName: String): TValue; overload; - function IsAnIterator(const VarName: String; out DataSourceName: String; - out CurrentIterator: TLoopStackItem): Boolean; + function IsAnIterator(const VarName: String; out DataSourceName: String; out CurrentIterator: TLoopStackItem): Boolean; function GetOnGetValue: TTProCompiledTemplateGetValueEvent; function EvaluateValue(var Idx: Int64; out MustBeEncoded: Boolean): TValue; procedure SetOnGetValue(const Value: TTProCompiledTemplateGetValueEvent); @@ -220,8 +214,8 @@ TTProCompiledTemplate = class(TInterfacedObject, ITProCompiledTemplate) function GetFormatSettings: PTProFormatSettings; procedure SetFormatSettings(const Value: PTProFormatSettings); class procedure InternalDumpToFile(const FileName: String; const aTokens: TList); - function ComparandOperator(const aComparandType: TComparandType; const aValue: TValue; - const aParameters: TArray; const aLocaleFormatSettings: TFormatSettings): TValue; + function ComparandOperator(const aComparandType: TComparandType; const aValue: TValue; const aParameters: TArray; + const aLocaleFormatSettings: TFormatSettings): TValue; public destructor Destroy; override; function Render: String; @@ -252,16 +246,16 @@ TTProCompiler = class function MatchSymbol(const aSymbol: string): Boolean; function MatchSpace: Boolean; function MatchString(out aStringValue: string): Boolean; - procedure InternalMatchFilter(lIdentifier: String; var lStartVerbatim: Int64; const CurrToken: TTokenType; - aTokens: TList; const lRef2: Integer); + procedure InternalMatchFilter(lIdentifier: String; var lStartVerbatim: Int64; const CurrToken: TTokenType; aTokens: TList; + const lRef2: Integer); function GetFunctionParameters: TArray; function CreateFilterParameterToken(const FilterParameter: PFilterParameter): TToken; procedure Error(const aMessage: string); function Step: Char; function CurrentChar: Char; function GetSubsequentText: String; - procedure InternalCompileIncludedTemplate(const aTemplate: string; const aTokens: TList; - const aFileNameRefPath: String; const aCompilerOptions: TTProCompilerOptions); + procedure InternalCompileIncludedTemplate(const aTemplate: string; const aTokens: TList; const aFileNameRefPath: String; + const aCompilerOptions: TTProCompilerOptions); procedure ProcessJumps(const aTokens: TList); procedure Compile(const aTemplate: string; const aTokens: TList; const aFileNameRefPath: String); overload; constructor Create(const aEncoding: TEncoding; const aOptions: TTProCompilerOptions = []); overload; @@ -270,8 +264,7 @@ TTProCompiler = class public function Compile(const aTemplate: string; const aFileNameRefPath: String = ''): ITProCompiledTemplate; overload; constructor Create(aEncoding: TEncoding = nil); overload; - class function CompileAndRender(const aTemplate: string; const VarNames: TArray; - const VarValues: TArray): String; + class function CompileAndRender(const aTemplate: string; const VarNames: TArray; const VarValues: TArray): String; end; ITProWrappedList = interface @@ -290,13 +283,12 @@ TTProConfiguration = class sealed protected class procedure RegisterHandlers(const TemplateProCompiledTemplate: ITProCompiledTemplate); public - class property OnContextConfiguration: TTProCompiledTemplateEvent read fOnContextConfiguration - write fOnContextConfiguration; + class property OnContextConfiguration: TTProCompiledTemplateEvent read fOnContextConfiguration write fOnContextConfiguration; end; function HTMLEncode(s: string): string; -function HandleTemplateSectionStateMachine(const aTokenValue1: String; - var aTemplateSectionType: TTProTemplateSectionType; out aErrorMessage: String): Boolean; +function HandleTemplateSectionStateMachine(const aTokenValue1: String; var aTemplateSectionType: TTProTemplateSectionType; + out aErrorMessage: String): Boolean; implementation @@ -344,8 +336,7 @@ TTProDuckTypedList = class(TInterfacedObject, ITProWrappedList) procedure GetItemAsTValue(const AIndex: Integer; out aValue: TValue); function GetItem(const AIndex: Integer): TObject; class function CanBeWrappedAsList(const AObjectAsDuck: TObject): Boolean; overload; static; - class function CanBeWrappedAsList(const AObjectAsDuck: TObject; out AMVCList: ITProWrappedList): Boolean; - overload; static; + class function CanBeWrappedAsList(const AObjectAsDuck: TObject; out AMVCList: ITProWrappedList): Boolean; overload; static; class function CanBeWrappedAsList(const AInterfaceAsDuck: IInterface): Boolean; overload; static; class function Wrap(const AObjectAsDuck: TObject): ITProWrappedList; static; end; @@ -360,8 +351,8 @@ function WrapAsList(const AObject: TObject): ITProWrappedList; procedure FunctionError(const aFunctionName, aErrMessage: string); begin - raise ETProRenderException.Create(Format('[%1:s] %0:s (error in filter call for function [%1:s])', - [aErrMessage, aFunctionName]))at ReturnAddress; + raise ETProRenderException.Create(Format('[%1:s] %0:s (error in filter call for function [%1:s])', [aErrMessage, aFunctionName])) + at ReturnAddress; end; function TTProCompiledTemplate.ComparandOperator(const aComparandType: TComparandType; const aValue: TValue; @@ -387,8 +378,7 @@ function TTProCompiledTemplate.ComparandOperator(const aComparandType: TComparan ctLE: Result := aLeftValue <= aRightValue; else - raise ETProRenderException.Create('Invalid Comparand Type: ' + TRttiEnumerationType.GetName - (aComparandType)); + raise ETProRenderException.Create('Invalid Comparand Type: ' + TRttiEnumerationType.GetName(aComparandType)); end; end; @@ -427,8 +417,7 @@ function TTProCompiledTemplate.ComparandOperator(const aComparandType: TComparan ctLE: Result := aValue.AsInt64 <= lInt64Value; else - raise ETProRenderException.Create('Invalid Comparand Type: ' + TRttiEnumerationType.GetName - (aComparandType)); + raise ETProRenderException.Create('Invalid Comparand Type: ' + TRttiEnumerationType.GetName(aComparandType)); end; end; tkFloat: @@ -497,8 +486,7 @@ function TTProCompiledTemplate.ComparandOperator(const aComparandType: TComparan ctLE: Result := aValue.AsExtended <= lExtendedValue; else - raise ETProRenderException.Create('Invalid Comparand Type: ' + TRttiEnumerationType.GetName - (aComparandType)); + raise ETProRenderException.Create('Invalid Comparand Type: ' + TRttiEnumerationType.GetName(aComparandType)); end end; end; @@ -561,8 +549,7 @@ function TTProCompiledTemplate.GetDataSetFieldAsTValue(const aDataSet: TDataSet; ftBoolean: Result := lField.AsBoolean; else - Error('Invalid data type for field "%s": %s', - [FieldName, TRttiEnumerationType.GetName(lField.DataType)]); + Error('Invalid data type for field "%s": %s', [FieldName, TRttiEnumerationType.GetName(lField.DataType)]); end; end; @@ -858,15 +845,13 @@ function TTProCompiledTemplate.GetTValueVarAsString(const Value: PValue; const V end; -procedure TTProCompiledTemplate.AddFilter(const FunctionName: string; - const AnonFunctionImpl: TTProTemplateAnonFunction); +procedure TTProCompiledTemplate.AddFilter(const FunctionName: string; const AnonFunctionImpl: TTProTemplateAnonFunction); begin InitTemplateAnonFunctions; fTemplateAnonFunctions.Add(FunctionName.ToLower, AnonFunctionImpl); end; -procedure TTProCompiledTemplate.CheckParNumber(const aMinParNumber, aMaxParNumber: Integer; - const aParameters: TArray); +procedure TTProCompiledTemplate.CheckParNumber(const aMinParNumber, aMaxParNumber: Integer; const aParameters: TArray); var lParNumber: Integer; begin @@ -918,7 +903,7 @@ procedure TTProCompiler.InternalMatchFilter(lIdentifier: String; var lStartVerba if not MatchEndTag then begin - Error('Expected end tag "' + END_TAG + '" near ' + GetSubsequentText); + Error('Expected end tag "' + END_TAG + '"'); end; lStartVerbatim := fCharIndex; aTokens.Add(TToken.Create(CurrToken, lIdentifier, '', lFilterParamsCount, lRef2)); @@ -1033,7 +1018,7 @@ function TTProCompiler.MatchVariable(var aIdentifier: string): Boolean; lTmp := ''; if not MatchVariable(lTmp) then begin - Error('Expected identifier after "' + aIdentifier + '" - got ' + GetSubsequentText); + Error('Expected identifier after "' + aIdentifier + '"'); end; aIdentifier := aIdentifier + '.' + lTmp; end; @@ -1144,8 +1129,7 @@ function TTProCompiler.MatchSymbol(const aSymbol: string): Boolean; lSavedCharIndex := fCharIndex; lSymbolIndex := 0; lSymbolLength := Length(aSymbol); - while (fInputString.Chars[fCharIndex].ToLower = aSymbol.Chars[lSymbolIndex].ToLower) and - (lSymbolIndex < lSymbolLength) do + while (fInputString.Chars[fCharIndex].ToLower = aSymbol.Chars[lSymbolIndex].ToLower) and (lSymbolIndex < lSymbolLength) do begin Inc(fCharIndex); Inc(lSymbolIndex); @@ -1268,8 +1252,7 @@ procedure TTProCompiler.Compile(const aTemplate: string; const aTokens: TList 0 then begin lLastToken := ttContent; - aTokens.Add(TToken.Create(lLastToken, fInputString.Substring(lStartVerbatim, - lEndVerbatim - lStartVerbatim), '')); + aTokens.Add(TToken.Create(lLastToken, fInputString.Substring(lStartVerbatim, lEndVerbatim - lStartVerbatim), '')); end; aTokens.Add(TToken.Create(ttEOF, '', '')); Break; @@ -1302,8 +1285,7 @@ procedure TTProCompiler.Compile(const aTemplate: string; const aTokens: TList 0 then begin lLastToken := ttContent; - aTokens.Add(TToken.Create(lLastToken, fInputString.Substring(lStartVerbatim, - lEndVerbatim - lStartVerbatim), '')); + aTokens.Add(TToken.Create(lLastToken, fInputString.Substring(lStartVerbatim, lEndVerbatim - lStartVerbatim), '')); end; if CurrentChar = START_TAG[1] then @@ -1343,7 +1325,7 @@ procedure TTProCompiler.Compile(const aTemplate: string; const aTokens: TList); @@ -1915,6 +1894,12 @@ function TTProCompiler.GetSubsequentText: String; I: Integer; begin Result := CurrentChar; + if Result = #0 then + begin + Result := ''; + end + else + begin Step; I := 0; while (CurrentChar <> #0) and (CurrentChar <> END_TAG[1]) and (I < 20) do @@ -1923,17 +1908,16 @@ function TTProCompiler.GetSubsequentText: String; Step; Inc(I); end; - Result := Result.QuotedString('"'); + end; end; -procedure TTProCompiledTemplate.CheckParNumber(const aHowManyPars: Integer; - const aParameters: TArray); +procedure TTProCompiledTemplate.CheckParNumber(const aHowManyPars: Integer; const aParameters: TArray); begin CheckParNumber(aHowManyPars, aHowManyPars, aParameters); end; -function TTProCompiledTemplate.ExecuteFilter(aFunctionName: string; var aParameters: TArray; - aValue: TValue; const aVarNameWhereShoudBeApplied: String): TValue; +function TTProCompiledTemplate.ExecuteFilter(aFunctionName: string; var aParameters: TArray; aValue: TValue; + const aVarNameWhereShoudBeApplied: String): TValue; var lDateValue: TDateTime; lDateFilterFormatSetting: TFormatSettings; @@ -1946,8 +1930,7 @@ function TTProCompiledTemplate.ExecuteFilter(aFunctionName: string; var aParamet lNullableDate: NullableTDate; lValue, lVarValue: TValue; lExtendedValue: Extended; - procedure CheckParamType(const FunctionName: String; const FilterParameter: PFilterParameter; - const Types: TFilterParameterTypes); + procedure CheckParamType(const FunctionName: String; const FilterParameter: PFilterParameter; const Types: TFilterParameterTypes); begin if not(FilterParameter.ParType in Types) then begin @@ -2570,8 +2553,7 @@ function TToken.TokenTypeAsString: String; function TToken.ToString: String; begin - Result := Format('%15s | Ref1: %8d | Ref2: %8d | Val1: %-25s| Val2: %-25s', - [TokenTypeAsString, Ref1, Ref2, Value1, Value2]); + Result := Format('%15s | Ref1: %8d | Ref2: %8d | Val1: %-25s| Val2: %-25s', [TokenTypeAsString, Ref1, Ref2, Value1, Value2]); end; { TTProCompiledTemplate } @@ -2634,8 +2616,7 @@ destructor TTProCompiledTemplate.Destroy; inherited; end; -procedure TTProCompiledTemplate.DoOnGetValue(const DataSource, Members: string; var Value: TValue; - var Handled: Boolean); +procedure TTProCompiledTemplate.DoOnGetValue(const DataSource, Members: string; var Value: TValue; var Handled: Boolean); begin Handled := False; if Assigned(fOnGetValue) then @@ -2918,8 +2899,8 @@ function TTProCompiledTemplate.Render: String; begin if fTokens[lIdx].Value1 <> TEMPLATEPRO_VERSION then begin - Error('Compiled template has been compiled with a different version. Expected ' + TEMPLATEPRO_VERSION + - ' got ' + fTokens[lIdx].Value1); + Error('Compiled template has been compiled with a different version. Expected ' + TEMPLATEPRO_VERSION + ' got ' + + fTokens[lIdx].Value1); end; end; ttContinue: @@ -3181,8 +3162,8 @@ function TTProCompiledTemplate.GetVarAsTValue(const aName: string): TValue; if lIsAnIterator then begin if lHasMember then - Result := TTProRTTIUtils.GetProperty(WrapAsList(lVariable.VarValue.AsObject) - .GetItem(lCurrentIterator.IteratorPosition), lVarMembers) + Result := TTProRTTIUtils.GetProperty(WrapAsList(lVariable.VarValue.AsObject).GetItem(lCurrentIterator.IteratorPosition), + lVarMembers) else Result := WrapAsList(lVariable.VarValue.AsObject).GetItem(lCurrentIterator.IteratorPosition); end @@ -3263,8 +3244,8 @@ class procedure TTProCompiledTemplate.InternalDumpToFile(const FileName: String; end; end; -function TTProCompiledTemplate.IsAnIterator(const VarName: String; out DataSourceName: String; - out CurrentIterator: TLoopStackItem): Boolean; +function TTProCompiledTemplate.IsAnIterator(const VarName: String; out DataSourceName: String; out CurrentIterator: TLoopStackItem) + : Boolean; var I: Integer; begin @@ -3636,8 +3617,7 @@ procedure TTProCompiledTemplate.SetData(const Name: String; Value: TValue); tkInteger, tkString, tkUString, tkFloat, tkEnumeration: GetVariables.Add(Name, TVarDataSource.Create(Value, [viSimpleType])); else - raise ETProException.Create('Invalid type for variable "' + Name + '": ' + TRttiEnumerationType.GetName - (Value.Kind)); + raise ETProException.Create('Invalid type for variable "' + Name + '": ' + TRttiEnumerationType.GetName(Value.Kind)); end; end; @@ -3743,8 +3723,7 @@ class function TTProDuckTypedList.CanBeWrappedAsList(const AObjectAsDuck: TObjec Result := CanBeWrappedAsList(AObjectAsDuck, lList); end; -class function TTProDuckTypedList.CanBeWrappedAsList(const AObjectAsDuck: TObject; - out AMVCList: ITProWrappedList): Boolean; +class function TTProDuckTypedList.CanBeWrappedAsList(const AObjectAsDuck: TObject; out AMVCList: ITProWrappedList): Boolean; var List: ITProWrappedList; begin @@ -3919,8 +3898,8 @@ class function TBlockAddress.Create(BeginBlockAddress, EndBlockAddress: Int64): Result.EndBlockAddress := EndBlockAddress; end; -function HandleTemplateSectionStateMachine(const aTokenValue1: String; - var aTemplateSectionType: TTProTemplateSectionType; out aErrorMessage: String): Boolean; +function HandleTemplateSectionStateMachine(const aTokenValue1: String; var aTemplateSectionType: TTProTemplateSectionType; + out aErrorMessage: String): Boolean; begin Result := True; if aTokenValue1 = STR_BEGIN_OF_LAYOUT then