delphimvcframework/samples/SwaggerSampleApi/DelphiUnit.pas

626 lines
16 KiB
ObjectPascal
Raw Normal View History

2019-07-29 14:33:28 +02:00
unit DelphiUnit;
interface
uses
2019-07-29 16:01:27 +02:00
classes,
system.json,
System.SysUtils,
System.Rtti,
System.TypInfo,
2019-08-02 03:44:19 +02:00
System.Generics.Collections,
System.Generics.Defaults
2019-07-29 16:01:27 +02:00
;
2019-07-29 14:33:28 +02:00
type
TUnitTypeDefinition = class;
TUnitFieldDefinition = class
private
FFieldName: string;
FFieldType: string;
FAttributes: TStringList;
FDescription: string;
public
property FieldName: string read FFieldName write FFieldName;
property FieldType: string read FFieldType write FFieldType;
property Description: string read FDescription write FDescription;
procedure AddAttribute(const inAttribute: string);
function GenerateInterface: string;
constructor Create;
destructor Destroy; override;
end;
TUnitParameter = class
private
FFlags: TParamFlags;
FType: TUnitTypeDefinition;
FParamName: string;
public
property ParamName: string read FParamName write FParamName;
property Flags: TParamFlags read FFlags write FFlags;
property ParamType: TUnitTypeDefinition read FType write FType;
end;
TUnitMethod = class
private
FAttributes: TStringList;
FMethodKind: TMethodKind;
FVisibility: TMemberVisibility;
FName: string;
FIsStatic: Boolean;
FIsClassMethod: Boolean;
FReturnType: TUnitTypeDefinition;
FParams: TObjectList<TUnitParameter>;
FVars: TObjectList<TUnitParameter>;
FContent: TStringList;
2019-08-02 03:44:19 +02:00
function MethodKindToDelphiString(var LHasReturn: Boolean): string;
procedure ParametersToDelphiString(var LParamString: string);
procedure MethodLocalVarsToDelphiString(LFuncSL: TStringList);
function GetIsConstructor: Boolean;
function GetIsDestructor: Boolean;
2019-07-29 14:33:28 +02:00
public
property Content: TStringList read FContent write FContent;
property MethodKind: TMethodKind read FMethodKind write FMethodKind;
property Visibility: TMemberVisibility read FVisibility write FVisibility;
property Name: string read FName write FName;
2019-08-02 03:44:19 +02:00
property IsConstructor: Boolean read GetIsConstructor;
property IsDestructor: Boolean read GetIsDestructor;
2019-07-29 14:33:28 +02:00
property IsClassMethod: Boolean read FIsClassMethod write FIsClassMethod;
// Static: No 'Self' parameter
property IsStatic: Boolean read FIsStatic write FIsStatic;
property ReturnType: TUnitTypeDefinition read FReturnType write FReturnType;
function GetParameters: TArray<TUnitParameter>;
procedure AddParameter(param: TUnitParameter);
procedure AddLocalVariable(inVar: TUnitParameter);
procedure AddAttribute(const inAttribute: string);
function GenerateInterface: string;
function GenerateImplementation(inOnType: TUnitTypeDefinition): string;
constructor Create;
destructor Destroy; override;
end;
TUnitTypeDefinition = class
private
FTypeName: string;
FTypeInheritedFrom: string;
FAttributes: TStringList;
public
Fields: TObjectList<TUnitFieldDefinition>;
FMethods: TObjectList<TUnitMethod>;
property TypeName: string read FTypeName write FTypeName;
property TypeInherited: string read FTypeInheritedFrom write FTypeInheritedFrom;
function GetMethods(): TArray<TUnitMethod>;
procedure AddAttribute(const inAttribute: string);
function GenerateInterface: string;
constructor Create;
destructor Destroy; override;
end;
TDelphiUnit = class
private
FInterfaceUses: TStringList;
FImplementationUses: TStringList;
FUnitName: string;
2019-08-02 03:44:19 +02:00
FTitle: String;
FDescription: string;
FLicense: string;
2019-07-29 14:33:28 +02:00
public
TypeDefinitions: TObjectList<TUnitTypeDefinition>;
function GenerateInterfaceSectionStart: string; virtual;
function GenerateInterfaceUses: string; virtual;
function GenerateImplementationSectionStart: string; virtual;
function GenerateImplementationUses: string; virtual;
public
property UnitFile: string read FUnitName write FUnitName;
2019-08-02 03:44:19 +02:00
property Title: String read FTitle write FTitle;
property Description: string read FDescription write FDescription;
property License: string read FLicense write FLicense;
2019-07-29 14:33:28 +02:00
procedure AddInterfaceUnit(const inFilename: string); virtual;
procedure AddImplementationUnit(const inFilename: string); virtual;
procedure AddType(inTypeInfo: TUnitTypeDefinition);
2019-08-02 03:44:19 +02:00
procedure SortTypeDefinitions;
function Generate: string;
2019-07-29 14:33:28 +02:00
constructor Create; virtual;
destructor Destroy; override;
end;
implementation
2019-08-02 03:44:19 +02:00
function DelphiVarName(const inVarname: string):string;
begin
Result := inVarname;
if Result.ToLower = 'type' then
Result := '&' + Result
else if Result.ToLower = 'file' then
Result := '&' + Result;
end;
2019-07-29 14:33:28 +02:00
{ TDelphiUnit }
procedure TDelphiUnit.AddImplementationUnit(const inFilename: string);
var
IntIndex : Integer;
begin
IntIndex := FInterfaceUses.IndexOf(inFilename);
if IntIndex < 0 then
FImplementationUses.Add(inFilename);
end;
procedure TDelphiUnit.AddInterfaceUnit(const inFilename: string);
var
ImpIndex : Integer;
begin
ImpIndex := FImplementationUses.IndexOf(inFilename);
if ImpIndex >= 0 then
FImplementationUses.Delete(ImpIndex);
FInterfaceUses.Add(inFilename);
end;
procedure TDelphiUnit.AddType(inTypeInfo: TUnitTypeDefinition);
begin
TypeDefinitions.Add(inTypeInfo);
end;
constructor TDelphiUnit.Create;
begin
FInterfaceUses := TStringList.Create;
FInterfaceUses.Duplicates := dupIgnore;
FImplementationUses := TStringList.Create;
FImplementationUses.Duplicates := dupIgnore;
TypeDefinitions := TObjectList<TUnitTypeDefinition>.Create;
end;
destructor TDelphiUnit.Destroy;
begin
FreeAndNil(FInterfaceUses);
FreeAndNil(FImplementationUses);
FreeAndNil(TypeDefinitions);
inherited;
end;
function TDelphiUnit.GenerateImplementationSectionStart: string;
var
2019-07-29 16:01:27 +02:00
LImplementationSection: TStringList;
2019-07-29 14:33:28 +02:00
begin
2019-07-29 16:01:27 +02:00
LImplementationSection := TStringList.Create;
2019-07-29 14:33:28 +02:00
try
2019-07-29 16:01:27 +02:00
LImplementationSection.Add('');
LImplementationSection.Add('implementation');
LImplementationSection.Add('');
Result := LImplementationSection.Text;
2019-07-29 14:33:28 +02:00
finally
2019-07-29 16:01:27 +02:00
FreeAndNil(LImplementationSection);
2019-07-29 14:33:28 +02:00
end;
end;
function TDelphiUnit.GenerateImplementationUses: string;
var
2019-07-29 16:01:27 +02:00
LUsesSL: TStringList;
2019-07-29 14:33:28 +02:00
i: Integer;
begin
2019-07-29 16:01:27 +02:00
LUsesSL := TStringList.Create;
2019-07-29 14:33:28 +02:00
try
if FImplementationUses.Count > 0 then
begin
2019-07-29 16:01:27 +02:00
LUsesSL.Add('uses');
2019-07-29 14:33:28 +02:00
for i := 0 to FImplementationUses.Count - 1 do
begin
if i = 0 then
2019-07-29 16:01:27 +02:00
LUsesSL.Add(' ' + FImplementationUses[i])
2019-07-29 14:33:28 +02:00
else
2019-07-29 16:01:27 +02:00
LUsesSL.Add(' , ' + FImplementationUses[i]);
2019-07-29 14:33:28 +02:00
end;
2019-07-29 16:01:27 +02:00
LUsesSL.Add(' ;');
2019-07-29 14:33:28 +02:00
end;
2019-07-29 16:01:27 +02:00
LUsesSL.Add('');
Result := LUsesSL.Text;
2019-07-29 14:33:28 +02:00
finally
2019-07-29 16:01:27 +02:00
FreeAndNil(LUsesSL);
2019-07-29 14:33:28 +02:00
end;
end;
function TDelphiUnit.GenerateInterfaceSectionStart: string;
var
2019-07-29 16:01:27 +02:00
LInterfaceSection: TStringList;
2019-07-29 14:33:28 +02:00
begin
2019-07-29 16:01:27 +02:00
LInterfaceSection := TStringList.Create;
2019-07-29 14:33:28 +02:00
try
2019-07-29 16:01:27 +02:00
LInterfaceSection.Add('unit ' + UnitFile + ';');
LInterfaceSection.Add('');
LInterfaceSection.Add('interface');
LInterfaceSection.Add('');
Result := LInterfaceSection.Text;
2019-07-29 14:33:28 +02:00
finally
2019-07-29 16:01:27 +02:00
FreeAndNil(LInterfaceSection);
2019-07-29 14:33:28 +02:00
end;
end;
function TDelphiUnit.GenerateInterfaceUses: string;
var
2019-07-29 16:01:27 +02:00
LUsesSL: TStringList;
2019-07-29 14:33:28 +02:00
i: Integer;
begin
2019-07-29 16:01:27 +02:00
LUsesSL := TStringList.Create;
2019-07-29 14:33:28 +02:00
try
if FInterfaceUses.Count > 0 then
begin
2019-07-29 16:01:27 +02:00
LUsesSL.Add('uses');
2019-07-29 14:33:28 +02:00
for i := 0 to FInterfaceUses.Count - 1 do
begin
if i = 0 then
2019-07-29 16:01:27 +02:00
LUsesSL.Add(' ' + FInterfaceUses[i])
2019-07-29 14:33:28 +02:00
else
2019-07-29 16:01:27 +02:00
LUsesSL.Add(' , ' + FInterfaceUses[i]);
2019-07-29 14:33:28 +02:00
end;
2019-07-29 16:01:27 +02:00
LUsesSL.Add(' ;');
2019-07-29 14:33:28 +02:00
end;
2019-07-29 16:01:27 +02:00
LUsesSL.Add('');
Result := LUsesSL.Text;
2019-07-29 14:33:28 +02:00
finally
2019-07-29 16:01:27 +02:00
FreeAndNil(LUsesSL);
2019-07-29 14:33:28 +02:00
end;
end;
2019-08-02 03:44:19 +02:00
function TDelphiUnit.Generate:string;
var
i: Integer;
j: Integer;
LMethod: TUnitMethod;
LMvcFile: TStringList;
begin
LMvcFile := TStringList.Create;
try
LMvcFile.Add(GenerateInterfaceSectionStart);
LMvcFile.Add(GenerateInterfaceUses);
LMvcFile.Add('(*');
LMvcFile.Add('Title: ' + Title);
LMvcFile.Add('Description: ' + Description);
LMvcFile.Add('License: ' + License);
LMvcFile.Add('*)');
LMvcFile.Add('');
LMvcFile.Add('type');
SortTypeDefinitions;
for i := 0 to TypeDefinitions.Count - 1 do
begin
LMvcFile.Add(TypeDefinitions[i].GenerateInterface);
end;
LMvcFile.Add(GenerateImplementationSectionStart);
LMvcFile.Add(GenerateImplementationUses);
LMvcFile.Add('');
for j := 0 to TypeDefinitions.Count - 1 do
begin
for LMethod in TypeDefinitions[j].GetMethods do
begin
LMvcFile.Add(LMethod.GenerateImplementation(TypeDefinitions[j]));
end;
end;
LMvcFile.Add('end.');
Result := LMvcFile.Text;
finally
FreeAndNil(LMvcFile);
end;
end;
procedure TDelphiUnit.SortTypeDefinitions;
begin
{ TODO : Make this much more advanced to handle dependency ordering of declarations }
TypeDefinitions.Sort(TComparer<TUnitTypeDefinition>.Construct(function (const L, R: TUnitTypeDefinition): integer
begin
if L.TypeName = 'TMyMVCController' then
Result := 1
else if R.TypeName = 'TMyMVCController' then
Result := -1
else
Result := CompareText(L.TypeName, R.TypeName);
end));
end;
2019-07-29 14:33:28 +02:00
{ TTypeDefinition }
procedure TUnitTypeDefinition.AddAttribute(const inAttribute: string);
begin
FAttributes.Add(inAttribute);
end;
constructor TUnitTypeDefinition.Create;
begin
FAttributes := TStringList.Create;
Fields := TObjectList<TUnitFieldDefinition>.Create;
FMethods := TObjectList<TUnitMethod>.Create;
end;
destructor TUnitTypeDefinition.Destroy;
begin
FreeAndNil(FAttributes);
FreeAndNil(Fields);
FreeAndNil(FMethods);
inherited;
end;
function TUnitTypeDefinition.GenerateInterface: string;
var
2019-07-29 16:01:27 +02:00
LInterfaceSL: TStringList;
2019-07-29 14:33:28 +02:00
i: Integer;
j: Integer;
begin
2019-07-29 16:01:27 +02:00
LInterfaceSL := TStringList.Create;
2019-07-29 14:33:28 +02:00
try
for i := 0 to FAttributes.Count - 1 do
begin
2019-07-29 16:01:27 +02:00
LInterfaceSL.Add(FAttributes[i]);
2019-07-29 14:33:28 +02:00
end;
if TypeInherited.Length > 0 then
2019-07-29 16:01:27 +02:00
LInterfaceSL.Add(' ' + TypeName + ' = class(' + TypeInherited + ')')
2019-07-29 14:33:28 +02:00
else
2019-07-29 16:01:27 +02:00
LInterfaceSL.Add(' ' + TypeName + ' = class');
2019-07-29 14:33:28 +02:00
for j := 0 to Fields.Count - 1 do
begin
2019-07-29 16:01:27 +02:00
LInterfaceSL.Add(Fields[j].GenerateInterface);
2019-07-29 14:33:28 +02:00
end;
for j := 0 to FMethods.Count - 1 do
begin
2019-07-29 16:01:27 +02:00
LInterfaceSL.Add(TrimRight(FMethods[j].GenerateInterface));
LInterfaceSL.Add('');
2019-07-29 14:33:28 +02:00
end;
2019-07-29 16:01:27 +02:00
LInterfaceSL.Add(' end;');
2019-07-29 14:33:28 +02:00
2019-07-29 16:01:27 +02:00
Result := LInterfaceSL.Text;
2019-07-29 14:33:28 +02:00
finally
2019-07-29 16:01:27 +02:00
FreeAndNil(LInterfaceSL);
2019-07-29 14:33:28 +02:00
end;
end;
function TUnitTypeDefinition.GetMethods: TArray<TUnitMethod>;
var
i: Integer;
begin
SetLength(Result, FMethods.Count);
for i := 0 to FMethods.Count - 1 do
begin
Result[i] := FMethods[i];
end;
end;
{ TFieldDefinition }
procedure TUnitFieldDefinition.AddAttribute(const inAttribute: string);
begin
FAttributes.Add(inAttribute);
end;
constructor TUnitFieldDefinition.Create;
begin
FAttributes := TStringList.Create;
end;
destructor TUnitFieldDefinition.Destroy;
begin
FreeAndNil(FAttributes);
inherited;
end;
function TUnitFieldDefinition.GenerateInterface: string;
var
2019-08-02 03:44:19 +02:00
i : Integer;
SL : TStringList;
LType : string;
2019-07-29 14:33:28 +02:00
begin
SL := TStringList.Create;
try
2019-08-02 03:44:19 +02:00
LType := FFieldType;
2019-07-29 14:33:28 +02:00
for i := 0 to FAttributes.Count - 1 do
begin
SL.Add(' ' + FAttributes[i]);
end;
2019-08-02 03:44:19 +02:00
2019-07-29 14:33:28 +02:00
if Description.Length > 0 then
SL.Add(' [MVCDoc(' + QuotedStr(Description) + ')]');
2019-08-02 03:44:19 +02:00
SL.Add(' ' + DelphiVarName(FFieldName) + ' : ' + LType + ';');
2019-07-29 14:33:28 +02:00
Result := SL.Text;
finally
FreeAndNil(SL);
end;
end;
{ TUnitMethod }
procedure TUnitMethod.AddAttribute(const inAttribute: string);
begin
FAttributes.Add(inAttribute);
end;
procedure TUnitMethod.AddLocalVariable(inVar: TUnitParameter);
begin
FVars.Add(inVar);
end;
procedure TUnitMethod.AddParameter(param: TUnitParameter);
begin
FParams.Add(param);
end;
constructor TUnitMethod.Create;
begin
FParams := TObjectList<TUnitParameter>.Create;
FAttributes := TStringList.Create;
FVars := TObjectList<TUnitParameter>.Create;
FContent := TStringList.Create;
end;
destructor TUnitMethod.Destroy;
begin
FreeAndNil(FParams);
FreeAndNil(FAttributes);
FreeAndNil(FVars);
FreeAndNil(FContent);
inherited;
end;
2019-08-02 03:44:19 +02:00
procedure TUnitMethod.MethodLocalVarsToDelphiString(LFuncSL: TStringList);
var
i: Integer;
begin
if FVars.Count > 0 then
begin
LFuncSL.Add('var');
for i := 0 to FVars.Count - 1 do
begin
LFuncSL.Add(' ' + FVars[i].ParamName + ' : ' + FVars[i].ParamType.TypeName + ';');
end;
end;
end;
procedure TUnitMethod.ParametersToDelphiString(var LParamString: string);
2019-07-29 14:33:28 +02:00
var
2019-07-29 16:01:27 +02:00
LParam: TUnitParameter;
LParamFlagString: string;
2019-08-02 03:44:19 +02:00
LParamName: string;
begin
LParamString := '(';
for LParam in GetParameters do
begin
LParamFlagString := '';
if pfConst in LParam.Flags then
LParamFlagString := 'const'
else if pfVar in LParam.Flags then
LParamFlagString := 'var'
else if pfOut in LParam.Flags then
LParamFlagString := 'out'
else if pfArray in LParam.Flags then
LParamFlagString := 'array of';
if LParamFlagString.Length > 0 then
LParamFlagString := LParamFlagString + ' ';
LParamName := DelphiVarName(LParam.ParamName);
LParamString := LParamString + LParamFlagString + LParamName + ': ' + LParam.FType.FTypeName + '; ';
end;
if LParamString.EndsWith('; ') then
LParamString := LParamString.Remove(LParamString.Length - 2);
LParamString := LParamString + ')';
if LParamString = '()' then
LParamString := '';
end;
function TUnitMethod.MethodKindToDelphiString(var LHasReturn: Boolean): string;
2019-07-29 14:33:28 +02:00
begin
case MethodKind of
mkProcedure:
2019-08-02 03:44:19 +02:00
Result := 'procedure';
2019-07-29 14:33:28 +02:00
mkFunction:
begin
2019-08-02 03:44:19 +02:00
Result := 'function';
2019-07-29 16:01:27 +02:00
LHasReturn := True;
2019-07-29 14:33:28 +02:00
end;
mkDestructor:
2019-08-02 03:44:19 +02:00
Result := 'destructor';
2019-07-29 14:33:28 +02:00
mkConstructor:
2019-08-02 03:44:19 +02:00
Result := 'constructor';
2019-07-29 14:33:28 +02:00
mkClassFunction:
begin
2019-08-02 03:44:19 +02:00
Result := 'class function';
2019-07-29 16:01:27 +02:00
LHasReturn := True;
2019-07-29 14:33:28 +02:00
end;
mkClassProcedure:
2019-08-02 03:44:19 +02:00
Result := 'class procedure';
2019-07-29 14:33:28 +02:00
mkClassConstructor:
2019-08-02 03:44:19 +02:00
Result := 'class constructor';
2019-07-29 14:33:28 +02:00
mkClassDestructor:
2019-08-02 03:44:19 +02:00
Result := 'class destructor';
2019-07-29 14:33:28 +02:00
else
2019-08-02 03:44:19 +02:00
Result := 'unknown';
2019-07-29 14:33:28 +02:00
end;
2019-08-02 03:44:19 +02:00
end;
function TUnitMethod.GenerateImplementation(inOnType: TUnitTypeDefinition): string;
var
LProcTypeString: string;
LHasReturn: Boolean;
LParamString: string;
LClassNameProcIn: string;
LFuncSL: TStringList;
begin
LHasReturn := False;
LClassNameProcIn := '';
LProcTypeString := MethodKindToDelphiString(LHasReturn);
2019-07-29 14:33:28 +02:00
if Assigned(inOnType) then
2019-07-29 16:01:27 +02:00
LClassNameProcIn := inOnType.TypeName + '.';
2019-08-02 03:44:19 +02:00
ParametersToDelphiString(LParamString);
2019-07-29 14:33:28 +02:00
2019-07-29 16:01:27 +02:00
if LHasReturn then
Result := LProcTypeString + ' ' + LClassNameProcIn + FName + LParamString + ': ' + ReturnType.FTypeName + ';'
2019-07-29 14:33:28 +02:00
else
2019-07-29 16:01:27 +02:00
Result := LProcTypeString + ' ' + LClassNameProcIn + FName + LParamString + ';';
2019-07-29 14:33:28 +02:00
2019-07-29 16:01:27 +02:00
LFuncSL := TStringList.Create;
2019-07-29 14:33:28 +02:00
try
2019-07-29 16:01:27 +02:00
LFuncSL.Text := Result;
2019-08-02 03:44:19 +02:00
MethodLocalVarsToDelphiString(LFuncSL);
2019-07-29 16:01:27 +02:00
LFuncSL.Add('begin');
LFuncSL.Add(Content.Text);
LFuncSL.Add('end;');
2019-07-29 14:33:28 +02:00
2019-07-29 16:01:27 +02:00
Result := LFuncSL.Text;
2019-07-29 14:33:28 +02:00
finally
2019-07-29 16:01:27 +02:00
FreeAndNil(LFuncSL);
2019-07-29 14:33:28 +02:00
end;
end;
function TUnitMethod.GenerateInterface: string;
var
2019-07-29 16:01:27 +02:00
LProcTypeString: string;
LHasReturn: Boolean;
LParamString: string;
LAttributeString: string;
2019-07-29 14:33:28 +02:00
begin
2019-07-29 16:01:27 +02:00
LHasReturn := False;
2019-07-29 14:33:28 +02:00
2019-08-02 03:44:19 +02:00
LProcTypeString := MethodKindToDelphiString(LHasReturn);
2019-07-29 14:33:28 +02:00
2019-08-02 03:44:19 +02:00
ParametersToDelphiString(LParamString);
2019-07-29 14:33:28 +02:00
2019-07-29 16:01:27 +02:00
if LHasReturn then
Result := ' ' + LProcTypeString + ' ' + FName + LParamString + ': ' + ReturnType.FTypeName + ';'
2019-07-29 14:33:28 +02:00
else
2019-07-29 16:01:27 +02:00
Result := ' ' + LProcTypeString + ' ' + FName + LParamString + ';';
2019-07-29 14:33:28 +02:00
2019-07-29 16:01:27 +02:00
LAttributeString := FAttributes.Text;
Result := LAttributeString + Result;
2019-07-29 14:33:28 +02:00
end;
2019-08-02 03:44:19 +02:00
function TUnitMethod.GetIsConstructor: Boolean;
begin
Result := MethodKind = mkConstructor;
end;
function TUnitMethod.GetIsDestructor: Boolean;
begin
Result := MethodKind = mkDestructor;
end;
2019-07-29 14:33:28 +02:00
function TUnitMethod.GetParameters: TArray<TUnitParameter>;
var
i: Integer;
begin
setLength(Result, FParams.Count);
for i := 0 to FParams.Count - 1 do
begin
Result[i] := FParams[i];
end;
end;
end.