123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545 |
- unit odatacodegen;
- {$mode objfpc}{$H+}
- interface
- uses
- Classes, SysUtils, pastree, restcodegen, inifiles;
- Type
- EEDMX2PasConverter = Class(Exception);
- // Extra set of keywords to take into account when cleaning a property name.
- TExtraKeyWords = (ekwNone,ekwObject,ekwEntity,ekwEntitySet,ekwEntityContainer,ekwservice);
- TODataVersion = (ODataV2,ODataV4);
- TEnumerationMode = (emScoped,emPrefixTypeName,emPlain);
- { TPropertySetter }
- TPropertyFlag = (pfRequired,pfNavigation, pfIndexed, pfReadOnly, pfNeedSetter, pfNeedGetter, pfInkey);
- TPropertyFlags = Set of TPropertyFlag;
- TResultType = (rtNone,rtSimple,rtObject,rtArraySimple,rtArrayObject);
- // Specialized TPasElement classes.
- // Using these tells the code generator what kind of code it must generate for an identifier.
- TPropertySetter = Class(TPasProcedure)
- private
- FProp: TPasElement;
- Public
- Property TheProperty : TPasElement Read FProp Write FProp;
- end;
- TPropertyGetter = Class(TPasFunction)
- private
- FProp: TPasElement;
- Public
- Property TheProperty : TPasElement Read FProp Write FProp;
- end;
- TGetRestKind = Class(TPasProcedure);
- TObjectRestKind = Class(TPasClassFunction);
- TExportPropertyName = class(TPasClassFunction);
- TCreateContainer = Class(TPasFunction);
- TCreateEntitySet = Class(TPasFunction);
- TEntityClassFunction = Class(TPasClassFunction);
- TGetNavigationProperty = Class(TPasFunction);
- TGetSingleton = Class(TPasFunction);
- TGetContainedSingleton = Class(TPasFunction);
- TKeyAsURLPart = Class(TPasFunction);
- TEntityMethod = Class(TPasFunction);
- TSetArrayLength = Class(TPasProcedure);
- TGetStream = Class(TPasProcedure);
- TSetStream = Class(TPasProcedure);
- TBoundFunction = Class(TPasFunction);
- TBoundActionProc = Class(TPasProcedure);
- TBoundActionFunc = Class(TPasFunction);
- TUnBoundFunction = Class(TPasFunction)
- private
- FPath: STring;
- Public
- Property ExportPath : STring Read FPath Write FPath;
- end;
- TUnBoundActionFunc = Class(TPasFunction)
- private
- FPath: STring;
- Public
- Property ExportPath : STring Read FPath Write FPath;
- end;
- TUnBoundActionProc = Class(TPasProcedure)
- private
- FPath: STring;
- Public
- Property ExportPath : STring Read FPath Write FPath;
- end;
- TEntityGet = Class(TEntityMethod);
- TEntityList = Class(TEntityMethod);
- TEntityListAll = Class(TEntityList);
- TEntityPut = Class(TEntityMethod);
- TEntityPatch = Class(TEntityMethod);
- TEntityPost = Class(TEntityMethod);
- TEntityDelete = Class(TEntityMethod);
- TServiceClass = Class(TPasClassType);
- TComplexClass = Class(TPasClassType);
- TEntityClass = Class(TPasClassType);
- TEntityContainerClass = Class(TPasClassType);
- TEntitySetClass = Class(TPasClassType);
- { TODataCodeGenerator }
- TODataCodeGenerator = class(TRestCodeGenerator)
- private
- FAliases: TStrings;
- FBaseComplexType: String;
- FBaseEntityContainerType: String;
- FBaseEntitySetType: String;
- FBaseEntityType: String;
- FBaseServiceType: String;
- FEnumerationMode: TEnumerationMode;
- FFieldPrefix: String;
- FSchemaAncestor: String;
- FServiceSuffix: String;
- FReservedWords : TStringList;
- FIdentifierMap : TStrings;
- procedure SetAliases(AValue: TStrings);
- function GetReservedWords: TStrings;
- procedure SetReservedWords(AValue: TStrings);
- Protected
- procedure EmitOptions; virtual;
- function ConvertTypeToStringExpr(const ExprName, ExprType: String): String;
- Function GetResultType(Const aType: String; Out AElementType : String): TResultType;
- function GetBaseClassName(El: TPasClassType): String;
- Procedure RegisterBaseTypes; virtual;
- function IsSimpleType(const aType: String): Boolean;
- function FlattenName(const AName: String): String;
- procedure WriteProcedureDecl(P: TPasProcedure);
- function CleanPropertyName(const AName: String; UseExtra: TExtraKeyWords): string;
- function CleanPropertyName(const AName: UnicodeString; UseExtra: TExtraKeyWords): string;
- Function CountProperties(C: TPasClassType): Integer;
- Property IdentifierMap : TStrings Read FIdentifierMap;
- Public
- Constructor Create(AOwner : TComponent); override;
- Destructor destroy; override;
- Class Function WTOA(Const S : UnicodeString) : String;
- Function is26Only(P: TPasProcedure): Boolean;
- Function BaseUnits : String; override;
- Class function IndentStrings(S: TStrings; aindent: Integer): string;
- Class Function ODataVersion : TODataVersion; virtual; abstract;
- Published
- Property BaseComplexType : String Read FBaseComplexType Write FBaseComplexType;
- Property BaseEntityType : String Read FBaseEntityType Write FBaseEntityType;
- Property BaseEntityContainerType : String Read FBaseEntityContainerType Write FBaseEntityContainerType;
- Property BaseServiceType : String Read FBaseServiceType Write FBaseServiceType;
- Property BaseEntitySetType : String Read FBaseEntitySetType Write FBaseEntitySetType;
- Property Aliases : TStrings Read FAliases Write SetAliases;
- Property SchemaAncestor : String Read FSchemaAncestor Write FSchemaAncestor;
- Property FieldPrefix: String Read FFieldPrefix Write FFieldPrefix;
- Property ServiceSuffix : String Read FServiceSuffix Write FServiceSuffix;
- property EnumerationMode : TEnumerationMode Read FEnumerationMode Write FEnumerationMode;
- Property ReservedWords : TStrings Read GetReservedWords Write SetReservedWords;
- end;
- implementation
- { TODataCodeGenerator }
- procedure TODataCodeGenerator.SetAliases(AValue: TStrings);
- begin
- if FAliases=AValue then Exit;
- FAliases.Assign(AValue);
- end;
- constructor TODataCodeGenerator.Create(AOwner: TComponent);
- begin
- inherited Create(AOwner);
- BaseClassName:='TODataObject';
- BaseComplexType:='TODataObject';
- BaseEntityType:='TODataEntity';
- BaseEntityContainerType:='TODataEntityContainer';
- BaseServiceType:='TODataService';
- BaseEntitySetType:='TODataEntitySet';
- SchemaAncestor:='TObject';
- FieldPrefix:='F';
- ServiceSuffix:='_';
- FAliases:=TStringlist.Create;
- FIdentifierMap:=THashedStringList.Create;
- end;
- destructor TODataCodeGenerator.destroy;
- begin
- FreeAndNil(FAliases);
- FreeAndNil(FReservedWords);
- FreeAndNil(FIdentifierMap);
- Inherited;
- end;
- function TODataCodeGenerator.BaseUnits: String;
- begin
- Result:='fpjson, restbase, odatabase, odataservice';
- end;
- function TODataCodeGenerator.GetReservedWords: TStrings;
- begin
- if (FReservedWords=Nil) then
- begin
- FReservedWords:=TStringList.Create;
- FReservedWords.Sorted:=True;
- end;
- Result:=FReservedWords;
- end;
- procedure TODataCodeGenerator.SetReservedWords(AValue: TStrings);
- begin
- if AValue=FReservedwords then exit;
- if AValue.Count=0 then
- FreeAndNil(FReservedWords)
- else
- ReservedWords.Assign(AValue);
- end;
- class function TODataCodeGenerator.WTOA(const S: UnicodeString): String;
- begin
- Result:=AnsiString(S);
- end;
- function TODataCodeGenerator.is26Only(P: TPasProcedure): Boolean;
- begin
- Result:=P is TSetArrayLength;
- end;
- class function TODataCodeGenerator.IndentStrings(S: TStrings; aindent: Integer
- ): string;
- Var
- I,CurrLen,CurrPos : Integer;
- begin
- Result:='';
- CurrLen:=0;
- CurrPos:=0;
- For I:=0 to S.Count-1 do
- begin
- CurrLen:=Length(S[i]);
- If (CurrLen+CurrPos)>72 then
- begin
- Result:=Result+LineEnding+StringOfChar(' ',aIndent);
- CurrPos:=aIndent;
- end;
- Result:=Result+S[i];
- CurrPos:=CurrPos+CurrLen;
- end;
- end;
- procedure TODataCodeGenerator.WriteProcedureDecl(P : TPasProcedure);
- Var
- S : TStrings;
- R: TPasResultElement;
- T : String;
- B : Boolean;
- begin
- S:=TStringList.Create;
- try
- S.Add(P.TypeName+' '+P.Name);
- P.ProcType.GetArguments(S);
- if P.ProcType.InheritsFrom(TPasFunctionType) then
- If Assigned(TPasFunctionType(P.ProcType).ResultEl) then
- begin
- R:=TPasFunctionType(P.ProcType).ResultEl;
- T:=' : ';
- If (R.ResultType.Name<>'') then
- T:=T+R.ResultType.Name
- else
- T:=T+R.ResultType.GetDeclaration(False);
- S.Add(T);
- end;
- P.GetModifiers(S);
- B:=Is26Only(P);
- if B then
- AddLn('{$IFDEF VER2_6}');
- AddLn(IndentStrings(S,Length(S[0]))+';');
- if B then
- AddLn('{$ENDIF VER2_6}');
- finally
- S.Free;
- end;
- end;
- function TODataCodeGenerator.CleanPropertyName(const AName: String;
- UseExtra: TExtraKeyWords): string;
- Const
- // Pascal keywords
- KW=';absolute;and;array;asm;begin;case;const;constructor;destructor;div;do;'+
- 'downto;else;end;file;for;function;goto;if;implementation;in;inherited;'+
- 'inline;interface;label;mod;nil;not;object;of;on;operator;or;packed;'+
- 'procedure;program;record;reintroduce;repeat;self;set;shl;shr;string;then;'+
- '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;length;setlength;result;';
- // Reserved words (methods)
- RWComponent = ';post;component;name;notification;componentcount;';
- RWOdataObject = 'destroy;loadPropertyfromjson;makekeystring;allowadditionalproperties;odataannotations;odataannotationvalues;odataannotationcount;';
- RWEntity = 'baseurl;keyasurlpart;delete;basepath;post;put;patch;';
- RWEntitySet = RWComponent+'getbaseurl;checkservice;checkcontainer;notification;getsingle;getmulti;containerurl;containedpath;service;objectrestkind;entityclass;getservice;container;';
- RWEntityContainer = RWComponent+'checkservice;objectrestkind;entitycontainername;defaultservice;createentityset;service;';
- RWService = RWComponent+'dolog;composeurl;service;jsontoodataerror;resptoerror;objectrestkind;servicename;registerservice;registerentitycontainers;addtoquery;'+
- 'queryparamstostring;servicecall;getstream;setstream;arrayservicecall;getmulti;createentitycontainer;getentityclass;onlog;webclient;serviceurl;apineedsauth;'+
- 'odatarequestheaders;lastresponseheaders;odatametadata;';
- Var
- I : Integer;
- RW : String;
- begin
- Result:=Aname;
- For I:=Length(Result) downto 1 do
- If Not ((Upcase(Result[i]) in ['_','A'..'Z'])
- or ((I>1) and (Result[i] in (['0'..'9'])))) then
- Delete(Result,i,1);
- if Pos(';'+lowercase(Result)+';',KW)<>0 then
- Result:='_'+Result;
- if UseExtra<>ekwNone then
- begin
- Case useExtra of
- ekwObject : RW:=RWOdataObject;
- ekwEntity : RW:=RWEntity;
- ekwEntitySet : RW:=RWEntitySet;
- ekwEntityContainer : RW:=RWEntityContainer;
- ekwservice : RW:=RWService;
- end;
- if Pos(';'+lowercase(Result)+';',RW)<>0 then
- Result:='_'+Result;
- if Assigned(FReservedWords) then
- if FReservedWords.IndexOf(Result)<>-1 then
- Result:='_'+Result;
- end;
- end;
- function TODataCodeGenerator.CleanPropertyName(const AName: UnicodeString;
- UseExtra: TExtraKeyWords): string;
- begin
- Result:=CleanpropertyName(WTOA(AName),UseExtra);
- end;
- function TODataCodeGenerator.FlattenName(const AName: String): String;
- begin
- Result:=StringReplace(AName,'.','_',[rfReplaceAll]);
- end;
- function TODataCodeGenerator.IsSimpleType(const aType: String): Boolean;
- begin
- Case LowerCase(aType) of
- 'boolean': Result:=True;
- 'byte' : Result:=True;
- 'tsbyte': Result:=True;
- 'shortint': Result:=True;
- 'int16': Result:=True;
- 'smallint': Result:=True;
- 'word': Result:=True;
- 'int32': Result:=True;
- 'integer': Result:=True;
- 'cardinal': Result:=True;
- 'dword': Result:=True;
- 'int64': Result:=True;
- 'qwordl': Result:=True;
- 'tint16': Result:=True;
- 'tint32': Result:=True;
- 'tint64': Result:=True;
- 'string': Result:=True;
- 'guidstring': Result:=True;
- 'tguidstring': Result:=True;
- 'double': Result:=True;
- 'single': Result:=True;
- else
- Result:=False;
- end;
- end;
- procedure TODataCodeGenerator.RegisterBaseTypes;
- Const
- TypeCount = 68;
- BaseTypes : Array[1..TypeCount,1..2] of String =
- (('Edm.Byte','Byte'), ('Collection(Edm.Byte)','TByteArray'),
- ('Edm.SByte','TSByte'), ('Collection(Edm.SByte)','TShortintArray'),
- ('Edm.int16','TInt16'), ('Collection(Edm.int16)','TInt16Array'),
- ('Edm.int32','TInt32'), ('Collection(Edm.int32)','TInt32Array'),
- ('Edm.int64','int64'), ('Collection(Edm.int64)','TInt64Array'),
- ('Edm.string','string'), ('Collection(Edm.string)','TStringArray'),
- ('Edm.Guid','TGUIDString'), ('Collection(Edm.guid)','TGuidStringArray'),
- ('Edm.Duration','TDuration'), ('Collection(Edm.Duration)','TStringArray'),
- ('Edm.Boolean','boolean'), ('Collection(Edm.boolean)','TBooleanArray'),
- ('Edm.Date','TDate'), ('Collection(Edm.Date)','TDateArray'),
- ('Edm.DateTime','TDateTime'), ('Collection(Edm.DateTime)','TDateTimeArray'),
- ('Edm.Time','TTime'), ('Collection(Edm.Time)','TTimeArray'),
- ('Edm.TimeOfDay','TTimeOfDay'), ('Collection(Edm.TimeOfDay)','TTimeOfDayArray'),
- ('Edm.DateTimeOffset','TDateTime'), ('Collection(Edm.DateTimeOffcset)','TDateTimeArray'),
- ('Edm.Decimal','double'), ('Collection(Edm.Decimal)','TDoubleArray'),
- ('Edm.Double','Double'), ('Collection(Edm.Double)','TDoubleArray'),
- ('Edm.Single','Single'), ('Collection(Edm.Single)','TSingleArray'),
- ('Edm.Binary','TBinary'), ('Collection(Edm.Binary)','TBinaryArray'),
- ('Edm.Stream','TStream'), ('Collection(Edm.Stream)','TByteArrayArray'),
- ('Edm.Geography','TGeography'), ('Collection(Edm.Geography)','TGeographyArray'),
- ('Edm.GeographyPoint','TGeographyPoint'), ('Collection(Edm.GeographyPoint)','TGeographyPointArray'),
- ('Edm.GeographyPolygon','TGeographyPolygon'), ('Collection(Edm.GeographyPolygon)','TGeographyPolygonArray'),
- ('Edm.GeographyLineString','TGeographyLineString'), ('Collection(Edm.GeographyLineString)','TGeographyLineStringArray'),
- ('Edm.GeographyMultiPoint','TGeographyMultiPoint'), ('Collection(Edm.GeographyMultiPoint)','TGeographyMultiPointArray'),
- ('Edm.GeographyMultiString','TGeographyMultiLineString'), ('Collection(Edm.GeographyMultiLineString)','TGeographyMultiLineStringArray'),
- ('Edm.GeographyMultiPolygon','TGeographyMultiPolygon'), ('Collection(Edm.GeographyMultiPolygon)','TGeographyMultiPolygonArray'),
- ('Edm.Geometry','TGeometry'), ('Collection(Edm.Geometry)','TGeometryArray'),
- ('Edm.GeometryPoint','TGeometryPoint'), ('Collection(Edm.GeometryPoint)','TGeometryPointArray'),
- ('Edm.GeometryPolygon','TGeometryPolygon'), ('Collection(Edm.GeometryPolygon)','TGeometryPolygonArray'),
- ('Edm.GeometryLineString','TGeometryLineString'), ('Collection(Edm.GeometryLineString)','TGeometryLineStringArray'),
- ('Edm.GeometryMultiPoint','TGeometryMultiPoint'), ('Collection(Edm.GeometryMultiPoint)','TGeometryMultiPointArray'),
- ('Edm.GeometryMultiString','TGeometryMultiLineString'), ('Collection(Edm.GeometryMultiLineString)','TGeometryMultiLineStringArray'),
- ('Edm.GeometryMultiPolygon','TGeometryMultiPolygon'), ('Collection(Edm.GeometryMultiPolygon)','TGeometryMultiPolygonArray'),
- ('Edm.GeographyCollection','TGeographyArray'), ('Edm.GeometryCollection','TGeometryArray')
- );
- Var
- I : integer;
- begin
- For I:=1 to TypeCount do
- FIdentifierMap.Add(LowerCase(BaseTypes[I,1])+'='+BaseTypes[I,2]);
- end;
- function TODataCodeGenerator.GetBaseClassName(El: TPasClassType): String;
- begin
- Result:='';
- if Assigned(EL.AncestorType) then
- Result:=EL.AncestorType.Name;
- if (Result='') then
- begin
- if EL.InheritsFrom(TServiceClass) then
- Result:=BaseServiceType
- else if EL.InheritsFrom(TEntityContainerClass) then
- Result:=BaseEntityContainerType
- else if EL.InheritsFrom(TEntitySetClass) then
- Result:=BaseEntitySetType
- else if EL.InheritsFrom(TEntityClass) then
- Result:=BaseEntityType
- else if EL.InheritsFrom(TComplexClass) then
- Result:=BaseComplexType
- else
- Result:=BaseClassName;
- end;
- end;
- function TODataCodeGenerator.CountProperties(C: TPasClassType): Integer;
- Var
- I : Integer;
- begin
- Result:=0;
- While (C<>Nil) do
- begin
- For I:=0 to C.Members.Count-1 do
- If TObject(C.Members[i]) is TPasProperty then
- Inc(Result);
- if C.AncestorType is TPasClassType then
- C:=C.AncestorType as TPasClassType
- else
- C:=Nil;
- end;
- end;
- function TODataCodeGenerator.GetResultType(const aType: String; out
- AElementType: String): TResultType;
- Var
- P : Integer;
- EN : String;
- begin
- P:=Pos('array',lowercase(aType));
- if (aType='') then
- Result:=rtNone
- else if IsSimpleType(AType) then
- Result:=rtSimple
- else if P>0 then
- begin
- AElementType:=Copy(atype,1,P-1);
- EN:=AElementType;
- if (EN<>'') and (EN[1]='T') then
- Delete(EN,1,1);
- if IsSimpleType(EN) then
- begin
- Result:=rtArraySimple;
- AElementType:=EN;
- end
- else
- Result:=rtArrayObject;
- end
- else
- Result:=rtObject;
- end;
- function TODataCodeGenerator.ConvertTypeToStringExpr(const ExprName,
- ExprType: String): String;
- begin
- Case LowerCase(ExprType) of
- 'boolean' : Result:='BoolToStr('+ExprName+',''true'',''false'')';
- 'byte' : Result:='IntToStr('+ExprName+')';
- 'tsbyte': Result:='IntToStr('+ExprName+')';
- 'int16': Result:='IntToStr('+ExprName+')';
- 'int32': Result:='IntToStr('+ExprName+')';
- 'int64': Result:='IntToStr('+ExprName+')';
- 'tint16': Result:='IntToStr('+ExprName+')';
- 'tint32': Result:='IntToStr('+ExprName+')';
- 'tint64': Result:='IntToStr('+ExprName+')';
- 'string': Result:='TODataObject.MakeKeyString('+ExprName+')';
- 'tguidstring': Result:='TODataObject.MakeKeyString('+ExprName+')';
- 'tdatetime': Result:='FormatDateTime(''yyyy-mm-dd"T"hhmmss'','+ExprName+')';
- 'double': Result:='FloatToStr('+ExprName+')';
- 'single': Result:='FloatToStr('+ExprName+')';
- 'tbinary' : Result:='BinaryToString('+ExprName+')';
- else
- Raise EEDMX2PasConverter.CreateFmt('GET : Unsupported key type "%s" for %s',[ExprType,ExprName]);
- end;
- end;
- procedure TODataCodeGenerator.EmitOptions;
- Var
- I : Integer;
- S : String;
- begin
- Addln('(*');
- IncIndent;
- Addln('Options used to generate: ');
- Str(ODataVersion,S);
- Addln('OData version : '+S);
- Addln('BasecomplexType : '+BaseComplexType);
- Addln('BaseEntityType : '+BaseEntityType);
- Addln('BaseEntityContainerType : '+BaseEntityContainerType);
- Addln('BaseServiceType : '+BaseServiceType);
- Addln('BaseEntitySetType : '+BaseEntitySetType);
- For I:=0 to Aliases.Count-1 do
- Addln('Aliases[%d] : %s',[i,Aliases[i]]);
- Addln('SchemaAncestor : '+SchemaAncestor);
- Addln('FieldPrefix : '+FieldPrefix);
- Addln('ServiceSuffix : '+ServiceSuffix);
- Str(EnumerationMode,S);
- Addln('EnumerationMode : '+S);
- decIndent;
- Addln('*)');
- end;
- end.
|