pas2js/tools/html2form/formgen.pas
2020-08-29 07:10:28 +00:00

1129 lines
28 KiB
ObjectPascal

{
This file is part of the Pas2JS toolchain
Copyright (c) 2020 by Michael Van Canneyt
This unit implements a HTML to pascal class converter.
See the file COPYING.FPC, included in this distribution,
for details about the copyright.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
**********************************************************************}
unit formgen;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, sax, sax_html, pascodegen, fpjson, jsonparser;
Type
TLogEvent = Procedure (Sender : TObject; Const Msg : String) of object;
{ TFormElement }
TFormElement = Class(TCollectionItem)
private
FHTMLID: String;
FName: String;
FType: String;
FEvents : TStrings;
function GetEvents: TStrings;
function getName: String;
procedure SetEvents(AValue: TStrings);
Public
Destructor Destroy; override;
Function HasEvents : Boolean;
Procedure Assign(Source : TPersistent); override;
Published
Property Name : String Read getName Write FName;
Property HTMLID : String Read FHTMLID Write FHTMLID;
Property ElementType : String Read FType Write FType;
Property Events : TStrings Read GetEvents Write SetEvents;
end;
{ TFormElementList }
TFormElementList = CLass(TCollection)
private
function GetEl(aIndex : Integer): TFormElement;
Public
Function Add(Const aName : string) : TFormElement;
Function IndexOf(Const aName : string) : Integer;
Function Find(Const aName : string) : TFormElement;
Property Elements[aIndex : Integer] : TFormElement Read GetEl; default;
end;
TAttributeOperation = (aoNotPresent,aoPresent,aoEqual,aoNotEqual,aoContains);
{ TAttributeCondition }
TAttributeCondition = Class(TCollectionItem)
private
FAttribute: String;
FOperation: TAttributeOperation;
FValue: String;
Public
Procedure LoadFromJSON(aName : String; aValue: TJSONData);
function IsMatch(aValue: String): Boolean;
Property Attribute : String Read FAttribute Write FAttribute;
Property Operation : TAttributeOperation Read FOperation Write FOperation;
Property Value : String Read FValue Write FValue;
end;
{ TAttributeConditionList }
TAttributeConditionList = Class(TCollection)
private
function GetC(aIndex : Integer): TAttributeCondition;
Public
Procedure LoadFromJSON(aJSON : TJSONObject);
Function IsMatch(Attrs: TSAXAttributes): Boolean;
Property Conditions[aIndex : Integer] : TAttributeCondition Read GetC; default;
end;
(* // Structure of accepted JSON
[
{
"class" : "TWebComboBox",
"tag" : "input",
"attrs" : {
name0 : null, // name0 Not present
name1 : "value", // name1 equals value
name2 ; "-value", // name2 does not equal value
name3 : "~value" // name3 contains value
}
}
]
*)
{ THTMLElementMap }
THTMLElementMap = Class(TCollectionItem)
private
FConditionList : TAttributeConditionList;
FControlClass: String;
FTag: String;
function GetAttrConditionList: TAttributeConditionList;
Protected
Function CreateConditionList : TAttributeConditionList; virtual;
Public
Destructor Destroy; override;
Procedure LoadFromJSON(aJSON : TJSONObject);
Function HasConditions : Boolean;
Function IsMatch(aTag: SAXString; Attrs: TSAXAttributes): Boolean;
Property Tag : String Read FTag Write FTag;
Property ControlClass : String Read FControlClass Write FControlClass;
Property Attributes : TAttributeConditionList Read GetAttrConditionList;
end;
{ THTMLElementMapList }
THTMLElementMapList = Class(TCollection)
private
function GetM(aIndex : Integer): THTMLElementMap;
Public
Procedure LoadFromFile(Const aFileName : String);
Procedure LoadFromStream(aStream : TStream); virtual;
Procedure LoadFromJSON(aJSON : TJSONArray); virtual;
Function IndexOfMap(aTag: SAXString; Attrs: TSAXAttributes): Integer;
Function FindMap(aTag: SAXString; Attrs: TSAXAttributes): THTMLElementMap;
Property Maps[aIndex : Integer] : THTMLElementMap Read GetM; default;
end;
{ THTMLToFormELements }
THTMLToFormELements = class(TComponent)
private
FBelowID: String;
FDefaultElements: Boolean;
FExcludeIDS: TStrings;
FFormElements: TFormElementList;
FLevel : Integer;
FMap: THTMLElementMapList;
FOnLog: TLogEvent;
function MakeValidName(aID: string): string;
procedure SetExcludeIDS(AValue: TStrings);
procedure SetFormElements(AValue: TFormElementList);
protected
Procedure DoLog(Const Msg : String);
Procedure DoLog(Const Fmt : String; Args : Array of const);
function CreateHTMLElementMapList: THTMLElementMapList; virtual;
procedure GetEvents(aEl: TFormElement; Atts: TSAXAttributes); virtual;
procedure DoEndElement(Sender: TObject; const {%H-}NamespaceURI, {%H-}LocalName,
{%H-}QName: SAXString); virtual;
procedure DoStartElement(Sender: TObject; const {%H-}NamespaceURI, LocalName,
{%H-}QName: SAXString; Atts: TSAXAttributes); virtual;
function Maptype(aTag: SAXString; Atts: TSAXAttributes): String; virtual;
Class Function CreateElementList : TFormElementList; virtual;
Property Level : Integer Read FLevel Write FLevel;
Public
Constructor Create(aOwner : TComponent); override;
Destructor Destroy; override;
Procedure Clear;
Procedure LoadFromStream(aInput : TStream);
Procedure LoadFromFile(Const aFileName : String);
Property FormElements : TFormElementList Read FFormElements Write SetFormElements;
Property BelowID : String Read FBelowID Write FBelowID;
Property ExcludeIDS : TStrings Read FExcludeIDS Write SetExcludeIDS;
Property Map : THTMLElementMapList Read FMap;
Property DefaultElements : Boolean Read FDefaultElements Write FDefaultElements;
Property OnLog : TLogEvent Read FOnLog Write FOnLog;
end;
{ TFormCodeGen }
TSpecialMethod = (smConstructor,smBindElements,smBindElementEvents);
TSpecialMethods = Set of TSpecialMethod;
TFormOption = (foEvents,foFormFile,foBindInConstructor);
TFormOptions = Set of TFormOption;
{ TFormFileCodeGen }
TFormFileCodeGen = Class(TPascalCodeGenerator)
private
FElementHeight: Word;
FElementHSpacing: Word;
FElementVSpacing: Word;
FElementWidth: Word;
FDoEvents: Boolean;
FFormClassName: String;
FFormElements: TFormElementList;
FIDProperty: String;
FLeft: Word;
FMaxHeight: Word;
FMaxWidth: Word;
FTop: Word;
Protected
function GetFormName(const aClassName: string): String; virtual;
procedure GenerateElements; virtual;
procedure EmitElementEvents(El: TFormElement); virtual;
procedure EmitElementProps(El: TFormElement); virtual;
procedure NextPosition; virtual;
Property ELeft : Word Read FLeft Write FLeft;
Property ETop : Word Read FTop Write FTop;
Public
Constructor Create(aOwner : TComponent);override;
Procedure Execute;
Property FormElements: TFormElementList read FFormElements write FFormElements;
Property FormClassName : String read FFormClassName write FFormClassName;
Property DoEvents : Boolean read FDoEvents write FDoEvents;
Property IDProperty : String Read FIDProperty Write FIDProperty;
Property ElementHeight : Word Read FElementHeight Write FElementHeight;
Property ElementWidth : Word Read FElementWidth Write FElementWidth;
Property MaxWidth : Word Read FMaxWidth Write FMaxWidth;
Property MaxHeight : Word Read FMaxHeight Write FMaxHeight;
Property ElementHSpacing : Word Read FElementHSpacing Write FElementHSpacing;
Property ElementVSpacing : Word Read FElementVSpacing Write FElementVSpacing;
end;
TFormCodeGen = Class(TPascalCodeGenerator)
private
FAddMethods: TSpecialMethods;
FConstructorArgs: String;
FEventModifiers: String;
FEventSignature: string;
FFormClassName: string;
FFormElements: TFormElementList;
fFormFileGenerator: TFormFileCodeGen;
FFormSource: Tstrings;
FGetElementFunction: string;
FOptions: TFormOptions;
FOverrideMethods: TSpecialMethods;
FParentClassName: string;
FVirtualMethods: TSpecialMethods;
procedure SetFormElements(AValue: TFormElementList);
Protected
function BaseUnits : String; override;
Function CreateHTMLToFormELements: THTMLToFormELements; virtual;
Class Function CreateElementList : TFormElementList; virtual;
procedure EmitFormFile; virtual;
function CreateFormFileGen : TFormFileCodeGen; virtual;
procedure EmitFormElement(aEL: TFormElement); virtual;
procedure EmitFormEvents(aEL: TFormElement);virtual;
procedure EmitImplementation; virtual;
procedure EmitPublicSection; virtual;
procedure EmitPublishedSection; virtual;
procedure EmitFormBindElements; virtual;
procedure EmitFormBindEvents; virtual;
procedure EmitFormConstructor; virtual;
function VirtualOverride(M: TSpecialMethod; const Decl: String): string; virtual;
Public
Constructor Create(aOwner : TComponent); override;
Destructor Destroy; override;
class function Pretty(const S: String): string; virtual;
class procedure GetEventNameAndHandler(const S,aFieldName: String; out aName, aHandler: string);
Procedure Execute;
Property FormFileGenerator : TFormFileCodeGen Read fFormFileGenerator Write FFormFileGenerator;
Property FormElements : TFormElementList Read FFormElements Write SetFormElements;
Property FormClassName : string Read FFormClassName Write FFormClassName;
Property ParentClassName : string Read FParentClassName Write FParentClassName;
Property GetElementFunction : string Read FGetElementFunction Write FGetElementFunction;
Property EventSignature: string Read FEventSignature Write FEventSignature;
Property EventModifiers : String Read FEventModifiers Write FEventModifiers;
Property ConstructorArgs : String Read FConstructorArgs Write FConstructorArgs;
Property Options : TFormOptions Read FOptions Write FOptions;
Property AddMethods : TSpecialMethods Read FAddMethods Write FAddMethods;
Property OverrideMethods : TSpecialMethods Read FOverrideMethods Write FOverrideMethods;
Property VirtualMethods : TSpecialMethods Read FVirtualMethods Write FVirtualMethods;
Property FormSource : Tstrings Read FFormSource;
end;
implementation
{ TFormFileCodeGen }
function TFormFileCodeGen.GetFormName(const aClassName: string): String;
begin
Result:=aClassName;
if SameText(Copy(Result,1,1),'T') then
Delete(Result,1,1);
end;
(*
procedure TFormFileCodeGen.LoadFromStream(const AStream: TStream);
begin
if aStream=Nil then exit;
end;
*)
constructor TFormFileCodeGen.Create(aOwner: TComponent);
begin
inherited Create(aOwner);
IDProperty:='ElementID';
ElementHeight:=24;
ElementWidth:=72;
ElementVSpacing:=8;
ElementHSpacing:=16;
MaxWidth:=800;
MaxHeight:=600;
end;
procedure TFormFileCodeGen.NextPosition;
begin
ELeft:=ELeft+ElementWidth+ElementHSpacing;
if ELeft+ElementWidth>=MaxWidth then
begin
ELeft:=8;
ETop:=ETop+ElementHeight+ElementVSpacing;
end;
end;
procedure TFormFileCodeGen.EmitElementProps(El : TFormElement);
begin
AddLn('Top = %d',[ETop]);
AddLn('Left = %d',[ELeft]);
Addln('Width = %d',[ElementWidth]);
Addln('Height = %d',[ElementHeight]);
addLn('%s = ''%s''',[IDProperty,El.Name]);
end;
procedure TFormFileCodeGen.EmitElementEvents(El : TFormElement);
Var
S,EN,EH : String;
begin
For S in El.Events do
begin
TFormCodeGen.GetEventNameAndHandler(S,El.Name,EN,EH);
Addln('%s = %s',[EN,EH]);
end;
end;
procedure TFormFileCodeGen.GenerateElements;
Var
I : Integer;
El : TFormElement;
begin
For I:=0 to FormElements.Count-1 do
begin
el:=FormElements[i];
With El do
begin
Addln('object %s: %s',[Name,ElementType]);
Indent;
EmitElementProps(EL);
if DoEvents then
EmitElementEvents(El);
Undent;
AddLn('end');
NextPosition;
end;
end;
end;
procedure TFormFileCodeGen.Execute;
begin
ETop:=8;
ELeft:=8;
AddLn('object %s : %s',[GetFormName(FormClassName),FormClassName]);
Indent;
AddLn('Width = %d',[MaxWidth]);
AddLn('Height = %d',[MaxHeight]);
GenerateElements;
Undent;
AddLn('end');
end;
{ THTMLElementMapList }
function THTMLElementMapList.GetM(aIndex : Integer): THTMLElementMap;
begin
Result:=Items[aIndex] as THTMLElementMap;
end;
procedure THTMLElementMapList.LoadFromFile(const aFileName: String);
Var
F : TFileStream;
begin
F:=TFileStream.Create(aFileName,fmOpenRead or fmShareDenyWrite);
try
LoadFromStream(F);
finally
F.Free;
end;
end;
procedure THTMLElementMapList.LoadFromStream(aStream: TStream);
Var
D : TJSONData;
begin
D:=GetJSON(aStream);
try
if D is TJSONArray then
LoadFromJSON(D as TJSONArray);
finally
D.Free;
end;
end;
procedure THTMLElementMapList.LoadFromJSON(aJSON: TJSONArray);
Var
E : TJSONEnum;
begin
For E in aJSON do
if E.Value is TJSONObject then
(Add as THTMLElementMap).LoadFromJSON(e.Value as TJSONObject);
end;
function THTMLElementMapList.IndexOfMap(aTag: SAXString; Attrs: TSAXAttributes
): Integer;
begin
Result:=0;
While (Result<Count) and Not GetM(Result).IsMatch(aTag,Attrs) do
Inc(Result);
if Result=Count then
Result:=-1;
end;
function THTMLElementMapList.FindMap(aTag: SAXString; Attrs: TSAXAttributes
): THTMLElementMap;
Var
Idx : Integer;
begin
Idx:=IndexOfMap(aTag,Attrs);
If Idx=-1 then
Result:=Nil
else
Result:=GetM(Idx);
end;
{ THTMLElementMap }
function THTMLElementMap.GetAttrConditionList: TAttributeConditionList;
begin
If FConditionList=Nil then
FConditionList:=CreateConditionList;
Result:=FConditionList
end;
function THTMLElementMap.CreateConditionList: TAttributeConditionList;
begin
Result:=TAttributeConditionList.Create(TAttributeCondition);
end;
destructor THTMLElementMap.Destroy;
begin
FreeAndNil(FConditionList);
inherited Destroy;
end;
procedure THTMLElementMap.LoadFromJSON(aJSON: TJSONObject);
Var
A : TJSONObject;
begin
FTag:=aJSON.Get('tag','');
ControlClass:=aJSON.Get('class','');
A:=aJSON.Get('attrs',TJSONObject(Nil));
If Assigned(A) then
Attributes.LoadFromJSON(A);
end;
function THTMLElementMap.HasConditions: Boolean;
begin
Result:=Assigned(FConditionList) and (FConditionList.Count>0);
end;
function THTMLElementMap.IsMatch(aTag: SAXString; Attrs: TSAXAttributes): Boolean;
begin
Result:=SameText(UTF8Encode(aTag),FTag);
if Result and HasConditions then
Result:=Attributes.IsMatch(Attrs);
end;
{ TAttributeConditionList }
function TAttributeConditionList.GetC(aIndex : Integer): TAttributeCondition;
begin
Result:=TAttributeCondition(Items[aIndex]);
end;
procedure TAttributeConditionList.LoadFromJSON(aJSON: TJSONObject);
Var
E : TJSONEnum;
A : TAttributeCondition;
begin
For E in aJSON do
begin
A:=Add as TAttributeCondition;
A.LoadFromJSON(E.Key,E.Value);
end;
end;
function TAttributeConditionList.IsMatch(Attrs: TSAXAttributes): Boolean;
function GetIndex(const aName: SAXString): Integer;
begin
Result := Attrs.Length-1;
while (Result>=0) and not SameText(UTF8Encode(Attrs.LocalNames[Result]),UTF8Encode(aName)) do
Dec(Result);
end;
Var
I,Idx : Integer;
A : TAttributeCondition;
begin
Result:=True;
I:=0;
While Result and (I<Count) do
begin
A:=GetC(I);
Idx:=GetIndex(UTF8Decode(A.Attribute));
if A.Operation=aoNotPresent then
Result:=Idx<0
else
Result:=A.IsMatch(UTF8Encode(Attrs.GetValue(Idx)));
Inc(I);
end;
end;
{ TAttributeCondition }
procedure TAttributeCondition.LoadFromJSON(aName: String; aValue: TJSONData);
Var
S : TJSONStringType;
C : Char;
begin
Attribute:=aName;
if aValue.JSONType=jtNull then
Operation:=aoNotPresent
else if aValue.JSONType=jtBoolean then
begin
if aValue.AsBoolean then
Operation:=aoPresent
else
Operation:=aoNotPresent
end
else
begin
S:=aValue.AsString;
If S<>'' then
C:=S[1]
else
C:=#0;
Case C of
'-' : Operation:=aoNotEqual;
'~' : Operation:=aoContains;
else
Operation:=aoEqual;
Value:=S;
end;
if Operation in [aoNotEqual,aoContains] then
Value:=Copy(S,2,Length(S)-1);
end;
end;
function TAttributeCondition.IsMatch(aValue: String): Boolean;
begin
Case Operation of
aoPresent : Result:=True;
aoNotEqual : Result:=Not SameText(aValue,Value);
aoEqual : Result:=SameText(aValue,Value);
aoContains : Result:=Pos(LowerCase(Value),LowerCase(aValue))>0;
end;
end;
{ THTMLToFormELements }
procedure THTMLToFormELements.SetFormElements(AValue: TFormElementList);
begin
if FFormElements=AValue then Exit;
FFormElements:=AValue;
end;
procedure THTMLToFormELements.DoLog(const Msg: String);
begin
if Assigned(FOnLog) then
FOnLog(Self,Msg);
end;
procedure THTMLToFormELements.DoLog(const Fmt: String; Args: array of const);
begin
DoLog(Format(Fmt,Args));
end;
function THTMLToFormELements.Maptype(aTag: SAXString; Atts: TSAXAttributes): String;
var
t : string;
m : THTMLElementMap;
begin
Result:='';
if Map.Count>0 then
begin
M:=Map.FindMap(aTag,Atts);
if Assigned(m) then
Exit(M.ControlClass)
else if not DefaultElements then
begin
DoLog('Could not map tag %s',[aTag]);
Exit;
end;
end;
t:=lowercase(Utf8Encode(aTag));
case t of
'input' : Result:='TJSHTMLInputElement';
'button' : Result:='TJSHTMLButtonElement';
'select' : Result:='TJSHTMLSelectElement';
'textarea' : Result:='TJSHTMLTextAreaElement';
'option' : Result:='';
else
Result:='TJSHTMLElement';
end;
end;
function THTMLToFormELements.MakeValidName(aID: string): string;
Var
C : Char;
begin
Result:='';
for C in aID do
if C in ['_','a'..'z','A'..'Z','0'..'9'] then
Result:=Result+C
else
Result:=Result+'_';
end;
procedure THTMLToFormELements.SetExcludeIDS(AValue: TStrings);
begin
if FExcludeIDS=AValue then Exit;
FExcludeIDs.AddStrings(AValue,True);
end;
procedure THTMLToFormELements.DoStartElement(Sender: TObject;
const NamespaceURI, LocalName, QName: SAXString; Atts: TSAXAttributes);
Var
aID,aType : String;
El : TFormElement;
begin
if Not Assigned(atts) then exit;
aID:=UTF8Encode(Atts.GetValue('','id'));
if (aID='') or (FExcludeIDS.IndexOf(aID)>=0) then
exit;
if (Level=0) and (BelowID=aID) then
Level:=1
else if (BelowID<>'') and (Level<=0) then
Exit;
aType:=MapType(LocalName,Atts);
if aType='' then
DoLog('Ignoring tag %s with id %s',[LocalName,aID])
else
begin
El:=FormElements.Add(MakeValidName(aID));
EL.ElementType:=aType;
EL.HTMLID:=aId;
GetEvents(El,Atts);
end
end;
procedure THTMLToFormELements.GetEvents(aEl : TFormElement; Atts : TSAXAttributes);
Var
I,aLen : Integer;
aName : string;
begin
for I:=0 to Atts.Length-1 do
begin
aName:=UTF8Encode(Atts.GetLocalName(i));
aLen:=Length(aName);
if (aLen>3) and (Copy(aName,1,1)='_') and (Copy(aName,aLen,1)='_') then
aEl.Events.Add(Copy(aName,2,aLen-2)+'='+UTF8Encode(Atts.GetValue(i)));
end;
end;
procedure THTMLToFormELements.DoEndElement(Sender: TObject; const NamespaceURI,
LocalName, QName: SAXString);
begin
if Level>0 then
Dec(FLevel);
end;
class function THTMLToFormELements.CreateElementList: TFormElementList;
begin
Result:=TFormElementList.Create(TFormElement);
end;
function THTMLToFormELements.CreateHTMLElementMapList: THTMLElementMapList;
begin
Result:=THTMLElementMapList.Create(THTMLElementMap);
end;
constructor THTMLToFormELements.Create(aOwner: TComponent);
begin
inherited Create(aOwner);
FMap:=CreateHTMLElementMapList;
FFormElements:=CreateElementList;
FExcludeIDS:=TStringList.Create;
TStringList(FExcludeIDS).Sorted:=True;
end;
destructor THTMLToFormELements.Destroy;
begin
FreeAndNil(FMap);
FreeAndNil(FExcludeIDS);
FreeAndNil(FFormElements);
inherited Destroy;
end;
procedure THTMLToFormELements.Clear;
begin
FFormElements.Clear;
end;
procedure THTMLToFormELements.LoadFromStream(aInput: TStream);
var
MyReader : THTMLReader;
begin
MyReader:=THTMLReader.Create;
Try
MyReader.OnStartElement:=@DoStartElement;
MyReader.OnEndElement:=@DoEndElement;
MyReader.ParseStream(aInput);
finally
FreeAndNil(MyReader);
end;
end;
procedure THTMLToFormELements.LoadFromFile(const aFileName: String);
var
F : TFileStream;
begin
F:=TFileStream.Create(aFileName,fmOpenRead or fmShareDenyWrite);
try
LoadFromStream(F);
finally
F.Free;
end;
end;
{ TFormCodeGen }
procedure TFormCodeGen.SetFormElements(AValue: TFormElementList);
begin
if FFormElements=AValue then Exit;
FFormElements.Assign(AValue);
end;
function TFormCodeGen.BaseUnits: String;
begin
Result:='js, web';
end;
class function TFormCodeGen.CreateElementList: TFormElementList;
begin
Result:=TFormElementList.Create(TFormElement);
end;
constructor TFormCodeGen.Create(aOwner: TComponent);
begin
inherited Create(aOwner);
// Assume class is TComponent descendant
FConstructorArgs:='aOwner : TComponent';
FFormElements:=CreateElementList;
FormClassName:='TMyForm';
ParentClassName:='TComponent';
EventSignature:='Event : TJSEvent';
EventModifiers:='virtual; abstract;';
GetElementFunction:='document.getelementByID';
AddMethods:=[smConstructor,smBindElements,smBindElementEvents];
VirtualMethods:=[smBindElementEvents,smBindElements];
OverrideMethods:=[smConstructor];
Options:=[foBindInConstructor];
fFormFileGenerator:=CreateFormFileGen;
FFormSource:=TStringList.Create;
end;
destructor TFormCodeGen.Destroy;
begin
FreeAndNil(FFormSource);
FreeAndNil(fFormFileGenerator) ;
FreeAndNil(FFormElements);
inherited Destroy;
end;
procedure TFormCodeGen.EmitFormElement(aEL : TFormElement);
begin
With aEl do
AddLn('%s : %s;',[Name,ElementType]) ;
end;
procedure TFormCodeGen.EmitFormEvents(aEL : TFormElement);
Var
S,EN,EH : String;
begin
if not aEl.HasEvents then
exit;
For S in aEl.Events do
begin
GetEventNameAndHandler(S,aEl.Name,EN,EH);
Addln('Procedure %s(%s); %s',[EH, EventSignature,EventModifiers]);
end;
end;
procedure TFormCodeGen.EmitPublishedSection;
var
I : Integer;
begin
For I:=0 to FormElements.Count-1 do
EmitFormElement(FormElements[i]);
if foEvents in Options then
For I:=0 to FormElements.Count-1 do
EmitFormEvents(FormElements[i]);
end;
function TFormCodeGen.VirtualOverride(M: TSpecialMethod; const Decl: String): string;
begin
Result:=Decl;
if M in OverrideMethods then
Result:=Result+' override;'
else if M in VirtualMethods then
Result:=Result+' virtual;'
end;
procedure TFormCodeGen.EmitPublicSection;
begin
if smConstructor in AddMethods then
Addln(VirtualOverride(smConstructor,'Constructor create('+ConstructorArgs+');'));
if smBindElements in AddMethods then
Addln(VirtualOverride(smBindElements, 'Procedure BindElements;'));
if (smBindElementEvents in AddMethods) and (foEvents in Options) then
Addln(VirtualOverride(smBindElementEvents,'Procedure BindElementEvents;'));
end;
procedure TFormCodeGen.Execute;
begin
Source.Clear;
Addln('unit %s;',[OutputUnitName]);
CreateHeader;
Addln('Type');
Indent;
ClassHeader(FormClassName);
AddLn('%s = class(%s) ',[FormClassName,ParentClassName]);
Addln('Published');
Indent;
EmitPublishedSection;
Undent;
Addln('Public');
Indent;
EmitPublicSection;
Undent;
Addln('end;');
Undent;
Addln('');
Addln('implementation');
AddLn('');
if (foFormFile in Options) then
begin
EmitFormFile;
AddLn('');
AddLn('{$R *.dfm}');
AddLn('');
end;
ClassHeader(FormClassName);
EmitImplementation;
AddLn('');
AddLn('end.');
end;
procedure TFormCodeGen.EmitFormFile;
begin
FormFileGenerator.FormElements:=Self.FormElements;
FormFileGenerator.DoEvents:=foEvents in Options;
FormFileGenerator.FormClassName:=Self.FormClassName;
FormFileGenerator.Execute;
FormSource.Assign(FormFileGenerator.Source);
end;
function TFormCodeGen.CreateFormFileGen: TFormFileCodeGen;
begin
Result:=TFormFileCodeGen.Create(Nil);
end;
function TFormCodeGen.CreateHTMLToFormELements: THTMLToFormELements;
begin
Result:=THTMLToFormELements.Create(Self);
end;
procedure TFormCodeGen.EmitFormConstructor;
begin
Addln('');
Addln('Constructor %s.create(aOwner : TComponent);',[FormClassName]);
if not (foBindInConstructor in Options) then
SimpleMethodBody(['Inherited;'])
else
begin
if foEvents in Options then
SimpleMethodBody(['Inherited;','BindElements;','BindElementEvents;'])
else
SimpleMethodBody(['Inherited;','BindElements;']);
end;
Addln('');
end;
procedure TFormCodeGen.EmitImplementation;
begin
if smConstructor in AddMethods then
EmitFormConstructor;
if (smBindElements in AddMethods) then
EmitFormBindElements;
if (foEvents in Options) and Not (foFormFile in Options) and (smBindElementEvents in AddMethods) then
EmitFormBindEvents;
end;
procedure TFormCodeGen.EmitFormBindElements;
var
I : integer;
El : TFormElement;
begin
Addln('');
Addln('Procedure %s.BindElements;',[FormClassName]);
Addln('');
AddLn('begin');
Indent;
if smBindElements in OverrideMethods then
AddLn('inherited;');
For I:=0 to FormElements.Count-1 do
begin
el:=FormElements[i];
With El do
Addln('%s:=%s(%s(''%s''));',[Name,ElementType,GetElementFunction,HTMLID]);
end;
Undent;
Addln('end;');
Addln('');
end;
class function TFormCodeGen.Pretty(const S: String): string;
begin
Result:=UpperCase(Copy(S,1,1))+LowerCase(Copy(S,2,Length(S)-1));
end;
class procedure TFormCodeGen.GetEventNameAndHandler(const S,
aFieldName: String; out aName, aHandler: string);
Var
P : Integer;
begin
P:=Pos('=',S);
if (P=0) then
P:=Length(S)+1;
aName:=Copy(S,1,P-1);
aHandler:=Copy(S,P+1,Length(S)-P);
if AHandler='' then
aHandler:=aFieldName+Pretty(aName);
// Writeln(aFieldName,': ',S,' -> ',aName,' & ',aHandler);
end;
procedure TFormCodeGen.EmitFormBindEvents;
var
I : integer;
El : TFormElement;
S,EN,EH : String;
begin
Addln('Procedure %s.BindElementEvents;',[FormClassName]);
Addln('');
AddLn('begin');
Indent;
if smBindElementEvents in OverrideMethods then
AddLn('inherited;');
For I:=0 to FormElements.Count-1 do
begin
el:=FormElements[i];
With El do
if HasEvents then
For S in El.Events do
begin
GetEventNameAndHandler(S,Name,EN,EH);
Addln('%s.AddEventListener(''%s'',@%s);',[Name,EN,EH]);
end;
end;
Undent;
Addln('end;');
end;
{ TFormElementList }
function TFormElementList.GetEl(aIndex : Integer): TFormElement;
begin
Result:=Items[aIndex] as TFormElement;
end;
function TFormElementList.Add(const aName: string): TFormElement;
begin
if IndexOf(aName)<>-1 then
Raise Exception.CreateFmt('Duplicate name : %s' ,[aName]);
Result:=(Inherited Add) as TFormElement;
Result.Name:=aName;
end;
function TFormElementList.IndexOf(const aName: string): Integer;
begin
Result:=Count-1;
While (Result>=0) and Not SameText(aName,GetEl(Result).Name) do
Dec(Result);
end;
function TFormElementList.Find(const aName: string): TFormElement;
var
Idx : Integer;
begin
Idx:=IndexOf(aName);
if Idx>=0 then
Result:=GetEl(Idx)
else
Result:=Nil;
end;
{ TFormElement }
function TFormElement.GetEvents: TStrings;
begin
If (FEvents=Nil) then
FEvents:=TStringList.Create;
Result:=FEvents;
end;
function TFormElement.getName: String;
begin
Result:=FName;
if Result='' then
Result:=HTMLID;
end;
procedure TFormElement.SetEvents(AValue: TStrings);
begin
If AValue=FEVents then exit;
Events.Assign(aValue);
end;
destructor TFormElement.Destroy;
begin
FreeAndNil(FEvents);
inherited Destroy;
end;
function TFormElement.HasEvents: Boolean;
begin
Result:=Assigned(FEvents) and (FEvents.Count>0);
end;
procedure TFormElement.Assign(Source: TPersistent);
Var
FE : TFormElement absolute Source;
begin
if Source is TFormElement then
begin
FHTMLID:=FE.HTMLID;
FName:=FE.FName;
FType:=FE.FType;
if FE.HasEvents then
Events:=FE.Events;
end
else
inherited Assign(Source);
end;
end.