123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185 |
- {
- This file is part of the Free Component Library
- Copyright (c) 2024 by Michael Van Canneyt [email protected]
- JSON Schema - pascal types and helpers
- 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 fpjson.schema.pascaltypes;
- {$mode ObjFPC}{$H+}
- interface
- uses
- {$IFDEF FPC_DOTTEDUNITS}
- System.Classes, System.SysUtils, System.Contnrs, System.StrUtils,
- {$ELSE}
- Classes, SysUtils, contnrs, StrUtils,
- {$ENDIF}
- fpjson.schema.types,
- fpjson.schema.schema;
- Type
- ESchemaData = Class(EJSONSchema);
- TPascalTypeData = Class;
- TSchemaCodeGenLogEvent = Procedure (aType : TEventType; const Msg : String) of object;
- TDependencyType = (dtNone,dtDirect,dtIndirect);
- TDependencyTypes = set of TDependencyType;
- TNameType = (ntSchema,ntPascal,ntInterface,ntImplementation,ntSerializer);
- TNameTypes = set of TNameType;
- TSerializeType = (stSerialize,stDeserialize);
- TSerializeTypes = set of TSerializeType;
- TPascalType = (ptUnknown,
- ptBoolean, // Boolean
- ptInteger, // 32-bit integer
- ptInt64, // 64-bit integer
- ptDateTime, // TDateTime
- ptFloat32, // Single
- ptFloat64, // Double
- ptString, // String
- ptEnum, // Enumerated
- ptJSON, // TJSONData (empty schema object)
- ptAnonStruct, // Anonymous Class/Record (schema object with properties)
- ptSchemaStruct, // Named Class/Record
- ptArray // Array of...
- );
- TPascalTypes = Set of TPascalType;
- // Aliases
- TPropertyType = TPascalType;
- TPropertyTypes = TPascalTypes;
- { TPascalProperty }
- TPascalPropertyData = class(TObject)
- private
- FSchemaName: string;
- FElementType: TPropertyType;
- FEnumValues: TStrings;
- FPascalName: string;
- FSchema: TJSONSchema;
- FTypeNames: Array[TNameType] of string ;
- FElementTypeNames: Array[TNameType] of string ;
- FPropertyType: TPropertyType;
- FTypeData: TPascalTypeData;
- function GetElementTypeNames(aType : TNameType): String;
- function GetFallBackTypeName(aPropertyType: TPropertyType): string;
- function GetPascalTypeName: String;
- procedure SetElementTypeNames(aType : TNameType; AValue: String);
- procedure SetEnumValues(AValue: TStrings);
- Function GetTypeName(aType: TNameType) : String;
- procedure SetTypeName(aType: TNameType; aValue : String);
- Public
- Constructor Create(const aSchemaName, aPascalName : string);
- Destructor Destroy; override;
- // schema Name of the property
- Property SchemaName : string Read FSchemaName Write FSchemaName;
- // Pascal Name of the property
- Property PascalName : string Read FPascalName Write FPascalName;
- // Indexed access to all kind of type names
- Property TypeNames [aType : TNameType] : String Read GetTypeName Write SetTypeName;
- // Type of the property
- Property PropertyType : TPropertyType Read FPropertyType Write FPropertyType;
- // If Type is ptEnum, the values. Without _empty_.
- Property EnumValues : TStrings Read FEnumValues Write SetEnumValues;
- // Pascal type name for the property (same as TypeNames[ntPascal])
- Property PascalTypeName : String Index ntPascal Read GetTypeName Write SetTypeName;
- // PropertyType = ptArray : The array element type
- Property ElementType : TPropertyType Read FElementType Write FElementType;
- // PropertyType = ptArray : The array element type name (same as ElementTypeNames[ntPascal])
- Property ElementTypeName : String Index ntPascal Read GetElementTypeNames Write SetElementTypeNames;
- // PropertyType = ptArray : The array element type names
- Property ElementTypeNames[aType : TNameType] : String Read GetElementTypeNames Write SetElementTypeNames;
- // PropertyType = ptSchemaStruct: The type data for that component.
- // PropertyType = ptArray and elType=ptSchemaStruct
- Property TypeData : TPascalTypeData Read FTypeData Write FTypeData;
- // The JSON Schema for this property
- Property Schema : TJSONSchema Read FSchema Write FSchema;
- end;
- { TPascalTypeData }
- TPascalTypeData = class(TObject)
- private
- FElementTypeData: TPascalTypeData;
- FSchemaName: String;
- FImplementationName: String;
- FIndex: Integer;
- FInterfaceName: String;
- FInterfaceUUID: String;
- FPascalName: String;
- FSchema: TJSONSChema;
- FDependencies : TFPObjectList;
- FSerializerName: String;
- FSerializeTypes: TSerializeTypes;
- FSorted : Boolean;
- FProperties : TFPObjectList;
- FType: TPascalType;
- function GetDependency(aIndex : Integer): TPascalTypeData;
- function GetDependencyCount: Integer;
- function GetImplementationName: String;
- function GetInterfaceName: String;
- function GetInterfaceUUID: String;
- function GetProperty(aIndex : Integer): TPascalPropertyData;
- function GetPropertyCount: Integer;
- function GetSerializerName: String;
- Protected
- function CreateProperty(const aSchemaName, aPascalName: string): TPascalPropertyData; virtual;
- Public
- class function ExtractFirstType(aSchema: TJSONSchema): TSchemaSimpleType;
- Public
- Constructor Create(aIndex : integer; aType : TPascalType; const aSchemaName,aPascalName : String; aSchema : TJSONSchema);
- destructor Destroy; override;
- // Sort the properties.
- Procedure SortProperties;
- // Index of property using schema name
- Function IndexOfProperty(const aSchemaName: string) : Integer;
- // Index of property using Pascal name
- Function IndexOfPascalProperty(const aPascalName: string) : Integer;
- // Find property by schema name.
- Function FindProperty(const aName: string) : TPascalPropertyData;
- // Add a property. The pascal name must not yet exist.
- Function AddProperty(const aSchemaName,aPascalName : String) : TPascalPropertyData;
- // Return the requested name
- function GetTypeName(aNameType : TNameType) : string;
- // Check whether this component depends on given component. If recurse is true, check all intermediary structures as well.
- function DependsOn(aData : TPascalTypeData; Recurse : Boolean) : TDependencyType;
- // Add aData as a type this type depends on.
- Procedure AddDependency(aData : TPascalTypeData);
- // Component has array-typed property ?
- Function HasArrayProperty : Boolean;
- // Component has object-typed property ? (SchemaComponentsonly = False -> also return array of string etc.)
- function HasObjectProperty(aSchemaComponentsOnly: Boolean): Boolean;
- // Components his component depends on
- Property Dependency[aIndex : Integer] : TPascalTypeData Read GetDependency;
- // Number of Components his component depends on
- Property DependencyCount : Integer Read GetDependencyCount;
- // Indexed access to Properties
- Property Properties[aIndex : Integer] : TPascalPropertyData Read GetProperty; default;
- // Number of properties
- Property PropertyCount : Integer Read GetPropertyCount;
- // Pascal type name for DTO (can be influenced by map). Default is schema name with prefix/suffix
- Property PascalName : String Read FPascalName;
- // Schema name.
- Property SchemaName : String Read FSchemaName;
- // Interface name. Default Pascal name + 'Intf'
- Property InterfaceName : String Read GetInterfaceName Write FInterfaceName;
- // Interface UUID.
- Property InterfaceUUID : String Read GetInterfaceUUID Write FInterfaceUUID;
- // Implemention class name. Default Pascal name + 'Obj'
- Property ImplementationName : String Read GetImplementationName write FImplementationName;
- // Name of serialized helper. Default is Pascal name + 'Serializer'
- Property SerializerName : String Read GetSerializerName Write FSerializerName;
- // Do we need to serialize/deserialize ?
- Property SerializeTypes : TSerializeTypes Read FSerializeTypes Write FSerializeTypes;
- // Schema of this component.
- Property Schema: TJSONSChema Read FSchema;
- // Was this element sorted ?
- Property Sorted : Boolean Read FSorted Write FSorted;
- // PascalType
- Property Pascaltype : TPascalType Read FType;
- // For arrays, a pointer to the element type
- Property ElementTypeData : TPascalTypeData Read FElementTypeData Write FElementTypeData;
- end;
- { TPascalTypeDataList }
- TPascalTypeDataList = Class(TFPObjectList)
- private
- function GetTypes(aIndex : Integer): TPascalTypeData;
- Public
- Procedure Add(aItem : TPascalTypeData); reintroduce;
- Property Types[aIndex : Integer] : TPascalTypeData Read GetTypes; default;
- end;
- TKeywordEscapeMode = (kemAmpersand,kemSuffix,kemPrefix);
- { TSchemaData }
- TSchemaData = class(TObject)
- private
- FKeywordEscapeMode: TKeywordEscapeMode;
- FTypeList : TPascalTypeDataList;
- FAliasList : TPascalTypeDataList;
- FTypeMap : TFPObjectHashTable;
- FArrayTypePrefix: string;
- FArrayTypeSuffix: string;
- FDelphiTypes: Boolean;
- FInterfaceTypePrefix: String;
- FObjectTypePrefix: string;
- FObjectTypeSuffix: string;
- FOnLog: TSchemaCodeGenLogEvent;
- FUseEnums: Boolean;
- function GetSchemaType(aIndex : Integer): TPascalTypeData;
- function GetSchemaTypeCount: Integer;
- protected
- // Logging
- procedure DoLog(Const aType : TEventType; const aMessage : String);
- procedure DoLog(Const aType : TEventType; const aFmt : String; aArgs : Array of const);
- // Override this to finish creating a type.
- procedure FinishAutoCreatedType(aName: string; aType: TPascalTypeData; lElementTypeData: TPascalTypeData); virtual;
- // Override this to determine the type name of a pascal property
- function GetSchemaTypeAndName(aType: TPascalTypeData; aSchema: TJSONSchema; out aPropType: TPascalType; aNameType: TNameType=ntPascal): String; virtual;
- // Add a new type to the type map.
- procedure AddToTypeMap(const aSchemaName: String; aData : TPascalTypeData); virtual; overload;
- // Get pascal type name based on schema name
- function SchemaNameToNameType(const aName: string; aNameType: TNameType): string; virtual;
- // Take JSONPointer reference and find pascal type data for it.
- function GetPascalTypeDataFromRef(const aRef: String): TPascalTypeData; virtual;
- // Find schema pascal type data. If AllowCreate is true, type data for Enum,Array and object properties will be created.
- function GetSchemaTypeData(aType: TPascalTypeData; lSchema: TJSONSchema; AllowCreate: Boolean=False): TPascalTypeData;
- // Add a type to the alias list
- Procedure AddAliasType(aType : TPascalTypeData); virtual;
- // Sort types in dependency order
- procedure SortTypes;
- Public
- Constructor Create; virtual;
- Destructor Destroy; override;
- // Create aliases for known simple types
- procedure DefineStandardPascalTypes;
- // Is the word a pascal keyword ?
- class function IsKeyWord(const aWord : String) : Boolean;
- // Escape the word if it is a pascal keyword ?
- function EscapeKeyWord(const aWord : string) : string;
- // Get the pascal name based on schema name
- function GetTypeMap(const aName : string): String;
- // Return index of named schema type (name as in OpenApi). Return -1 if not found.
- function IndexOfSchemaType(const aSchemaName: String): integer;
- // Find Pascal type data based on schema type name.
- function FindSchemaTypeData(const aSchemaName: String; aFormat : String = ''): TPascalTypeData;
- // Extract simple type from schema
- Function GetSchemaType(aSchema : TJSONSchema) : TSchemaSimpleType;
- // Extract element type from schema
- Function GetArrayElementType(aSchema : TJSONSchema) : TSchemaSimpleType;
- // Used when creating a new type. Override to create a descendant;
- function CreatePascalType(aIndex: integer; aType : TPascalType; const aSchemaName, aPascalName: String; aSchema: TJSONSchema): TPascalTypeData; virtual;
- // Add a type to the list
- Procedure AddType(const aSchemaName: String; aType : TPascalTypeData); virtual;
- // Add a type definition to the type map.
- procedure AddAliasToTypeMap(aType: TPascalType; const aAlias, aSchemaTypeName, aPascalTypeName: String; aSchema: TJSONSchema); overload;
- // Add a property to a type
- function AddTypeProperty(aType: TPascalTypeData; lProp: TJSONSchema; aName : string = ''; Recurse : Boolean = True): TPascalPropertyData;
- // Add properties to structured pascal type from aSchema. if aSchema = nil then use aType.Schema
- Procedure AddPropertiesToType(aType : TPascalTypeData; aSchema: TJSONSchema = Nil; Recurse : Boolean = True);
- // For all types, fill the depency list: contains all structured types on which the type depends (recursively).
- procedure CheckDependencies; virtual;
- // Number of types
- Property TypeCount : Integer Read GetSchemaTypeCount;
- // Indexed access to all types.
- Property Types[aIndex : Integer] : TPascalTypeData Read GetSchemaType; default;
- // Map schema type to pascal type.
- Property TypeMap[aSchemaName : string] : String Read GetTypeMap;
- // prefix for object definitions. Default T
- Property ObjectTypePrefix : string Read FObjectTypePrefix Write FObjectTypePrefix;
- // prefix for object definitions. Default empty
- Property ObjectTypeSuffix : string Read FObjectTypeSuffix Write FObjectTypeSuffix;
- // Prefix for Dto Objects
- Property InterfaceTypePrefix : String Read FInterfaceTypePrefix Write FInterfaceTypePrefix;
- // Prefix for array types
- Property ArrayTypePrefix : string Read FArrayTypePrefix Write FArrayTypePrefix;
- // Suffix for array types. Default Array
- Property ArrayTypeSuffix : string Read FArrayTypeSuffix Write FArrayTypeSuffix;
- // Use delphi types: TArray<X> instead of Array of X
- Property DelphiTypes : Boolean Read FDelphiTypes Write FDelphiTypes;
- // Use enums for enumerateds (default is to keep them as strings)
- Property UseEnums : Boolean Read FUseEnums Write FUseEnums;
- // Log callback
- Property OnLog : TSchemaCodeGenLogEvent Read FOnLog Write FOnLog;
- // how to escape keywords
- Property KeywordEscapeMode : TKeywordEscapeMode Read FKeywordEscapeMode Write FKeywordEscapeMode;
- end;
- implementation
- function CompareTypeDataOnName(Item1, Item2: Pointer): Integer;
- var
- lType1 : TPascalTypeData absolute Item1;
- lType2 : TPascalTypeData absolute Item2;
- begin
- Result:=CompareText(lType1.SchemaName,lType2.SchemaName);
- end;
- function CompareProperties(Item1, Item2: Pointer): Integer;
- var
- lParam1 : TPascalPropertyData absolute Item1;
- lParam2 : TPascalPropertyData absolute Item2;
-
- begin
- Result:=CompareText(lParam1.PascalName,lParam2.PascalName);
- end;
- { TPascalPropertyData }
- procedure TPascalPropertyData.SetEnumValues(AValue: TStrings);
- begin
- if FEnumValues=AValue then Exit;
- FEnumValues.Assign(AValue);
- end;
- function TPascalPropertyData.GetPascalTypeName: String;
- begin
- Result:=GetTypeName(ntPascal);
- end;
- function TPascalPropertyData.GetElementTypeNames(aType : TNameType): String;
- begin
- Result:=FElementTypeNames[aType];
- if Result<>'' then
- exit;
- if (PropertyType=ptArray) then
- begin
- if (ElementType=ptSchemaStruct) then
- Exit(TypeData.GetTypeName(aType));
- Result:=GetFallBackTypeName(ElementType);
- end;
- end;
- procedure TPascalPropertyData.SetElementTypeNames(aType : TNameType; AValue: String);
- begin
- FElementTypeNames[aType]:=aValue;
- end;
- constructor TPascalPropertyData.Create(const aSchemaName, aPascalName: string);
- begin
- FSchemaName:=aSchemaName;
- FPascalName:=aPascalName;
- FEnumValues:=TStringList.Create;
- end;
- destructor TPascalPropertyData.Destroy;
- begin
- FreeAndNil(FEnumValues);
- inherited Destroy;
- end;
- function TPascalPropertyData.GetTypeName(aType: TNameType): String;
- begin
- Result:=FTypeNames[aType];
- if Result<>'' then
- exit;
- if Assigned(FTypeData) then
- Exit(FTypeData.GetTypeName(aType));
- // Fallback
- Result:=GetFallBackTypeName(FPropertyType);
- end;
- function TPascalPropertyData.GetFallBackTypeName(aPropertyType: TPropertyType): string;
- begin
- Case aPropertyType of
- ptUnknown : Raise ESchemaData.CreateFmt('Unknown property type for property "%s"',[PascalName]);
- ptBoolean : Result:='boolean';
- ptInteger : Result:='integer';
- ptInt64 : Result:='Int64';
- ptDateTime : Result:='TDateTime';
- ptFloat32 : Result:='single';
- ptFloat64 : Result:='double';
- ptString : Result:='string';
- ptEnum : Raise ESchemaData.CreateFmt('Unknown name for enumerated property "%s"',[PascalName]);
- ptJSON : Result := 'string';
- ptAnonStruct : Raise ESchemaData.CreateFmt('Unknown name for structured property "%s"',[PascalName]);
- ptSchemaStruct : Raise ESchemaData.CreateFmt('Unknown name for schema-typed property "%s"',[PascalName]);
- end;
- end;
- procedure TPascalPropertyData.SetTypeName(aType: TNameType; aValue: String);
- begin
- FTypeNames[aType]:=aValue;
- end;
- function TPascalTypeData.GetDependencyCount: Integer;
- begin
- Result:=0;
- if Assigned(FDependencies) then
- Result:=FDependencies.Count;
- end;
- function TPascalTypeData.GetImplementationName: String;
- begin
- Result:=FImplementationName;
- if Result='' then
- begin
- Result:='T'+StringReplace(SchemaName,'Dto','',[rfIgnoreCase]);
- Result:=Result+'Obj';
- end;
- end;
- function TPascalTypeData.GetInterfaceName: String;
- begin
- Result:=FInterfaceName;
- if Result='' then
- Result:='I'+SchemaName;
- end;
- function TPascalTypeData.GetInterfaceUUID: String;
- begin
- if FInterfaceUUID='' then
- FInterfaceUUID:=TGUID.NewGuid.ToString(False);
- Result:=FInterfaceUUID;
- end;
- function TPascalTypeData.GetProperty(aIndex : Integer): TPascalPropertyData;
- begin
- Result:=TPascalPropertyData(FProperties[aIndex]);
- end;
- function TPascalTypeData.GetPropertyCount: Integer;
- begin
- Result:=FProperties.Count;
- end;
- function TPascalTypeData.GetSerializerName: String;
- begin
- Result:=FSerializerName;
- If Result='' then
- Result:=PascalName+'Serializer';
- end;
- function TPascalTypeData.CreateProperty(const aSchemaName,aPascalName: string): TPascalPropertyData;
- begin
- Result:=TPascalPropertyData.Create(aSchemaName,aPascalName);
- end;
- procedure TPascalTypeData.SortProperties;
- begin
- FProperties.Sort(@CompareProperties);
- end;
- function TPascalTypeData.GetDependency(aIndex : Integer): TPascalTypeData;
- begin
- if Assigned(FDependencies) then
- Result:=TPascalTypeData(FDependencies[aIndex])
- else
- Raise EListError.CreateFmt('List index out of bounds: %d',[aIndex]);
- end;
- constructor TPascalTypeData.Create(aIndex: integer; aType: TPascalType; const aSchemaName, aPascalName: String; aSchema: TJSONSchema
- );
- begin
- FIndex:=aIndex;
- FSchema:=ASchema;
- FSchemaName:=aSchemaName;
- FPascalName:=aPascalName;
- FSerializeTypes:=[stSerialize,stDeserialize];
- FProperties:=TFPObjectList.Create(True);
- FType:=aType;
- end;
- destructor TPascalTypeData.Destroy;
- begin
- FreeAndNil(FProperties);
- FreeAndNil(FDependencies);
- Inherited;
- end;
- function TPascalTypeData.IndexOfProperty(const aSchemaName: string): Integer;
- begin
- Result:=FProperties.Count-1;
- While (Result>=0) and Not SameText(GetProperty(Result).SchemaName,aSchemaName) do
- Dec(Result);
- end;
- function TPascalTypeData.IndexOfPascalProperty(const aPascalName: string): Integer;
- begin
- Result:=FProperties.Count-1;
- While (Result>=0) and Not SameText(GetProperty(Result).PascalName,aPascalName) do
- Dec(Result);
- end;
- function TPascalTypeData.FindProperty(const aName: string): TPascalPropertyData;
- var
- Idx : Integer;
- begin
- Idx:=IndexOfProperty(aName);
- If Idx=-1 then
- Result:=Nil
- else
- Result:=GetProperty(Idx);
- end;
- function TPascalTypeData.AddProperty(const aSchemaName, aPascalName: String): TPascalPropertyData;
- begin
- if IndexOfPascalProperty(aPascalName)<>-1 then
- Raise ESchemaData.CreateFmt('Duplicate property name : %s',[aPascalName]);
- Result:=CreateProperty(aSchemaName,aPascalName);
- FProperties.Add(Result);
- end;
- function TPascalTypeData.GetTypeName(aNameType: TNameType): string;
- begin
- Case aNameType of
- ntSchema: Result:=SchemaName;
- ntPascal: Result:=PascalName;
- ntInterface : Result:=InterfaceName;
- ntImplementation : Result:=ImplementationName;
- ntSerializer : Result:=SerializerName
- end;
- end;
- function TPascalTypeData.DependsOn(aData: TPascalTypeData; Recurse: Boolean): TDependencyType;
- var
- I : Integer;
- begin
- Result:=dtNone;
- if Not Assigned(FDependencies) then
- exit;
- For I:=0 to DependencyCount-1 do
- if (Dependency[i]=aData) then
- exit(dtDirect);
- if not Recurse then
- exit;
- For I:=0 to DependencyCount-1 do
- if (Dependency[i].DependsOn(aData,True)<>dtNone) then
- Exit(dtIndirect);
- end;
- procedure TPascalTypeData.AddDependency(aData: TPascalTypeData);
- begin
- if FDependencies=Nil then
- FDependencies:=TFPObjectList.Create(False);
- FDependencies.Add(aData);
- end;
- procedure TSchemaData.CheckDependencies;
- procedure CheckProps(lTop,aData : TPascalTypeData);
- var
- lPropData : TPascalTypeData;
- I : Integer;
- begin
- For I:=0 to aData.PropertyCount-1 do
- begin
- lPropData:=aData.Properties[I].TypeData;
- if Assigned(lPropData) then
- begin
- Case lPropData.Pascaltype of
- ptAnonStruct,ptSchemaStruct:
- begin
- lTop.AddDependency(lPropData);
- CheckProps(lTop,lPropData);
- end;
- ptArray:
- begin
- lPropData:=lPropData.ElementTypeData;
- if assigned(lPropData) and (lPropData.PascalType in [ptAnonStruct,ptSchemaStruct]) then
- begin
- lTop.AddDependency(lPropData);
- CheckProps(lTop,lPropData);
- end;
- end
- else
- ;
- end;
- end;
- end;
- end;
- var
- I : Integer;
- lData : TPascalTypeData;
- begin
- For I:=0 to TypeCount-1 do
- begin
- lData:=Types[I];
- CheckProps(lData,lData);
- end;
- end;
- class function TPascalTypeData.ExtractFirstType(aSchema : TJSONSchema): TSchemaSimpleType;
- begin
- Result:=aSchema.Validations.GetFirstType;
- end;
- function TPascalTypeData.HasArrayProperty: Boolean;
- var
- I : integer;
- begin
- Result:=False;
- if not Assigned(FSchema) then exit;
- For I:=0 to Schema.Properties.Count-1 do
- if (ExtractFirstType(Schema.Properties[i])=sstArray) then
- exit(True);
- end;
- function TPascalTypeData.HasObjectProperty(aSchemaComponentsOnly : Boolean): Boolean;
- var
- I : integer;
- lProp : TJSONSchema;
- begin
- Result:=False;
- if not Assigned(FSchema) then exit;
- For I:=0 to Schema.Properties.Count-1 do
- begin
- lProp:=Schema.Properties[i];
- if (lProp.Ref<>'') then
- exit(True);
- if (ExtractFirstType(lProp)=sstObject) and not aSchemaComponentsOnly then
- exit(True);
- end;
- end;
- { TPascalTypeDataList }
- function TPascalTypeDataList.GetTypes(aIndex : Integer): TPascalTypeData;
- begin
- Result:=TPascalTypeData(Items[aIndex]);
- end;
- procedure TPascalTypeDataList.Add(aItem: TPascalTypeData);
- begin
- Inherited Add(aItem);
- end;
- { TSchemaData }
- function TSchemaData.GetSchemaTypeCount: Integer;
- begin
- Result:=FTypeList.Count;
- end;
- function TSchemaData.GetSchemaType(aIndex : Integer): TPascalTypeData;
- begin
- Result:=FTypeList[aIndex];
- end;
- procedure TSchemaData.DoLog(const aType: TEventType; const aMessage: String);
- begin
- If Assigned(FOnLog) then
- FOnLog(aType,aMessage);
- end;
- procedure TSchemaData.DoLog(const aType: TEventType; const aFmt: String; aArgs: array of const);
- begin
- If Assigned(FOnLog) then
- FOnLog(aType,Format(aFmt,aArgs));
- end;
- // Find requested name type in API types, based on openAPI name.
- function TSchemaData.SchemaNameToNameType(const aName: string; aNameType: TNameType): string;
- var
- lType : TPascalTypeData;
- begin
- lType:=FindSchemaTypeData(aName);
- if Assigned(lType) then
- Result:=lType.GetTypeName(aNameType)
- else
- Result:=aName;
- end;
- function TSchemaData.GetPascalTypeDataFromRef(const aRef : String): TPascalTypeData;
- var
- P : Integer;
- lName : String;
- begin
- P:=RPos('/',aRef);
- if P=0 then
- P:=RPos('#',aRef);
- if p=0 then
- lName:=aRef
- else
- lName:=Copy(aRef,P+1,Length(aRef)-P);
- Result:=FindSchemaTypeData(lName);
- end;
- procedure TSchemaData.AddAliasType(aType: TPascalTypeData);
- begin
- FAliasList.Add(aType);
- end;
- // Determine the PascalType and pascal type name of the given schema
- function TSchemaData.GetSchemaTypeAndName(aType: TPascalTypeData; aSchema: TJSONSchema; out aPropType: TPascalType; aNameType : TNameType = ntPascal): String;
- var
- lTypeData : TPascalTypeData;
- begin
- lTypeData:=GetSchemaTypeData(aType,aSchema);
- if lTypeData=Nil then
- begin
- aPropType:=ptUnknown;
- Result:='';
- end
- else
- begin
- aPropType:=lTypeData.Pascaltype;
- Result:=lTypeData.GetTypeName(aNameType);
- end;
- end;
- Procedure TSchemaData.FinishAutoCreatedType(aName : string; aType: TPascalTypeData; lElementTypeData: TPascalTypeData);
- begin
- AddType(aName,aType);
- Case aType.Pascaltype of
- ptAnonStruct:
- AddPropertiesToType(aType,aType.Schema,True);
- ptArray:
- aType.FElementTypeData:=lElementTypeData;
- end;
- end;
- function TSchemaData.GetSchemaTypeData(aType: TPascalTypeData; lSchema: TJSONSchema; AllowCreate : Boolean = False) : TPascalTypeData;
- var
- lType : TSchemaSimpleType;
- lName,lBaseName,lPascalName : string;
- lFormat : String;
- lElTypeData : TPascalTypeData;
- begin
- LType:=lSchema.Validations.GetFirstType;
- Result:=Nil;
- if lSchema.Ref<>'' then
- Result:=GetPascalTypeDataFromRef(lSchema.Ref)
- else
- begin
- lName:='';
- lFormat:='';
- Case lType of
- sstNone: ;
- sstNull: ;
- sstBoolean :
- lName:='boolean';
- sstInteger :
- begin
- lName:='integer';
- lFormat:=lSchema.Validations.Format;
- end;
- sstNumber:
- begin
- lName:='number';
- end;
- sstString:
- begin
- if IndexText(lSchema.Validations.Format,['date','time','date-time'])>=0 then
- begin
- lName:='string';
- lFormat:=lSchema.Validations.Format;
- end
- else if UseEnums and lSchema.Validations.HasKeywordData(jskEnum) and (lSchema.Validations.Enum.Count>0) then
- begin
- if assigned(aType) then
- lBaseName:=aType.GetTypeName(ntSchema)+'_'+lSchema.Name
- else
- lBaseName:='T'+lSchema.Name;
- lName:='('+lBaseName+')';
- Result:=FindSchemaTypeData(lName);
- if (Result=Nil) and allowCreate then
- begin
- Result:=CreatePascalType(-1,ptEnum,lName,'T'+lBaseName,lSchema);
- FinishAutoCreatedType(lName,Result,Nil);
- end;
- end
- else
- begin
- lName:='string';
- end;
- end;
- sstArray:
- begin
- lElTypeData:=GetSchemaTypeData(Nil,lSchema.Items[0]);
- // Data.FindSchemaTypeData('Array of string')
- lPascalName:=ArrayTypePrefix+lElTypeData.PascalName+ArrayTypeSuffix;
- lName:='['+lElTypeData.SchemaName+']';
- Result:=FindSchemaTypeData(lName);
- if Result<>Nil then
- lName:='';
- if (Result=Nil) and AllowCreate then
- begin
- Result:=CreatePascalType(-1,ptArray,lName,lPascalName,lSchema);
- FinishAutoCreatedType(lName,Result,lElTypeData);
- lName:='';
- end;
- end;
- sstObject:
- begin
- if lSchema.Properties.Count=0 then
- lName:='JSON'
- else
- begin
- if assigned(aType) then
- lBaseName:=aType.GetTypeName(ntSchema)+'_'+lSchema.Name
- else
- lBaseName:='Nested_'+lSchema.Name;
- lName:='{'+lBaseName+'}';
- lPascalName:='T'+lBaseName;
- Result:=FindSchemaTypeData(lName);
- if (Result=Nil) and AllowCreate then
- begin
- Result:=CreatePascalType(-1,ptAnonStruct,lName,lPascalName,lSchema);
- FinishAutoCreatedType(lName,Result,lElTypeData);
- lName:='';
- end;
- end;
- end;
- sstAny:
- lname:='any';
- end;
- if lName<>'' then
- Result:=FindSchemaTypeData(lName,lFormat);
- end;
- end;
- // Add a property to the type using the schema
- function TSchemaData.AddTypeProperty(aType: TPascalTypeData; lProp: TJSONSchema; aName: string; Recurse: Boolean
- ): TPascalPropertyData;
- var
- lTypeName, lName : string;
- lType,lEltype : TPropertyType;
- I : Integer;
- lPropTypeData : TPascaltypeData;
- begin
- lName:=aName;
- if lName='' then
- lName:=EscapeKeyWord(lProp.Name);
- if lProp.Validations.TypesCount>1 then
- Raise ESchemaData.CreateFmt('Creating property for schema with multiple types ("%s") is not supported',[lName]);
- if (lProp.Validations.GetFirstType=sstArray) then
- if (lProp.Items.Count<>1) then
- Raise ESchemaData.CreateFmt('Creating array property for schema with multiple item types ("%s") is not supported',[lName])
- else if (lProp.Items.Count<1) then
- Raise ESchemaData.CreateFmt('Creating array property for schema without item types ("%s") is not supported',[lName]);
- lPropTypeData:=GetSchemaTypeData(aType,lProp,Recurse);
- if lPropTypeData=Nil then
- Raise ESchemaData.CreateFmt('Unknown property type for property %s',[lName]);
- lType:=lPropTypeData.Pascaltype;
- lTypeName:=lPropTypeData.GetTypeName(ntPascal);
- Result:=aType.AddProperty(lProp.Name,lName);
- Result.Schema:=lProp;
- Result.PropertyType:=lType;
- Result.TypeData:=lPropTypeData;
- Result.PascalTypeName:=lPropTypeData.GetTypeName(ntPascal);
- if (lType=ptEnum) then
- begin
- for I:=0 to lProp.Validations.Enum.Count-1 do
- Result.EnumValues.Add(EscapeKeyWord(lProp.Validations.Enum.Items[I].AsString));
- end;
- if (lType=ptArray) then
- begin
- Result.PascalTypeName:=lTypeName;
- if (lProp.Items[0].Ref<>'') then
- begin
- Result.ElementType:=ptSchemaStruct;
- Result.TypeData:=GetPascalTypeDataFromRef(lProp.Items[0].Ref);
- if Result.TypeData=Nil then
- Raise ESchemaData.CreateFmt('No typedata for property %s element type (Ref: %s)',[Result.PascalName,lProp.Items[0].Ref]);
- Result.ElementTypeName:=Result.TypeData.PascalName;
- end
- else
- begin
- Result.ElementTypeName:=GetSchemaTypeAndName(Nil,lProp.Items[0],lEltype);
- Result.ElementType:=lElType;
- end;
- Result.TypeNames[ntInterface]:=GetSchemaTypeAndName(Nil,lProp,lelType,ntInterface);
- Result.TypeNames[ntImplementation]:=GetSchemaTypeAndName(Nil,lProp,lElType,ntImplementation);
- end;
- end;
- procedure TSchemaData.AddPropertiesToType(aType: TPascalTypeData; aSchema: TJSONSchema; Recurse: Boolean);
- var
- I : Integer;
- lSchema : TJSONSchema;
- begin
- lSchema:=aSchema;
- if lSchema=Nil then
- lSchema:=aType.Schema;
- for I:=0 to lSchema.Properties.Count-1 do
- AddTypeProperty(aType,lSchema.Properties[i],'',Recurse);
- end;
- function TSchemaData.CreatePascalType(aIndex: integer; aType : TPascalType; const aSchemaName, aPascalName: String; aSchema: TJSONSchema): TPascalTypeData;
- begin
- Result:=TPascalTypeData.Create(aIndex,aType,aSchemaName,aPascalName,aSchema);
- end;
- procedure TSchemaData.AddAliasToTypeMap(aType : TPascalType; const aAlias,aSchemaTypeName, aPascalTypeName: String; aSchema: TJSONSchema);
- var
- lType : TPascalTypeData;
- begin
- lType:=CreatePascalType(-1,aType,aSchemaTypeName,aPascalTypeName,aSchema);
- if not (aType in [ptSchemaStruct,ptAnonStruct,ptArray]) then
- lType.InterfaceName:=aPascalTypeName;
- AddToTypeMap(aAlias,lType);
- AddAliasType(lType);
- end;
- constructor TSchemaData.Create;
- begin
- FTypeMap:=TFPObjectHashTable.Create(False);
- FTypeList:=TPascalTypeDataList.Create(True);
- FAliasList:=TPascalTypeDataList.Create(True);
- FObjectTypePrefix:='T';
- FObjectTypeSuffix:='';
- FInterfaceTypePrefix:='I';
- FArrayTypeSuffix:='Array';
- FArrayTypePrefix:='';
- FKeywordEscapeMode:=kemSuffix;
- end;
- destructor TSchemaData.Destroy;
- begin
- FreeAndNil(FTypeList);
- FreeAndNil(FAliasList);
- FreeAndNil(FTypeMap);
- inherited Destroy;
- end;
- procedure TSchemaData.DefineStandardPascalTypes;
- begin
- // typename--format
- AddAliasToTypeMap(ptInteger,'integer','integer','integer',Nil);
- AddAliasToTypeMap(ptInteger,'integer--int32','integer','integer',Nil);
- AddAliasToTypeMap(ptInt64,'integer--int64','integer','int64',Nil);
- AddAliasToTypeMap(ptString,'string','string','string',Nil);
- AddAliasToTypeMap(ptDateTime,'string--date','string','TDateTime',Nil);
- AddAliasToTypeMap(ptDateTime,'string--time','string','TDateTime',Nil);
- AddAliasToTypeMap(ptDateTime,'string--date-time','string','TDateTime',Nil);
- AddAliasToTypeMap(ptBoolean,'boolean','boolean','boolean',Nil);
- AddAliasToTypeMap(ptFloat64,'number','number','double',Nil);
- AddAliasToTypeMap(ptJSON,'JSON','object','string',Nil);
- AddAliasToTypeMap(ptJSON,'any','object','string',Nil);
- AddAliasToTypeMap(ptArray,'[string]','[string]','TStringDynArray',Nil);
- AddAliasToTypeMap(ptArray,'[integer]','[integer]','TIntegerDynArray',Nil);
- AddAliasToTypeMap(ptArray,'[integer--int64]','[integer--int64]','TInt64DynArray',Nil);
- AddAliasToTypeMap(ptArray,'[number]','[number]','TDoubleDynArray',Nil);
- AddAliasToTypeMap(ptArray,'[boolean]','[boolean]','TBooleanDynArray',Nil);
- end;
- class function TSchemaData.IsKeyWord(const aWord: String): Boolean;
- Const
- 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;';
- begin
- Result:=Pos(';'+lowercase(aWord)+';',KW)<>0;
- end;
- function TSchemaData.EscapeKeyWord(const aWord: string): string;
- begin
- Result:=aWord;
- if IsKeyWord(Result) then
- case KeywordEscapeMode of
- kemSuffix : Result:=Result+'_';
- kemPrefix : Result:='_'+Result;
- kemAmpersand : Result:='&'+Result;
- end;
- end;
- function TSchemaData.GetTypeMap(const aName: string): String;
- begin
- Result:=SchemaNameToNameType(aName,ntPascal);
- end;
- // Find Pascal type data based on schema name
- function TSchemaData.FindSchemaTypeData(const aSchemaName: String; aFormat: String): TPascalTypeData;
- var
- lName : string;
- begin
- lName:=aSchemaName;
- if aFormat<>'' then
- lName:=lName+'--'+aFormat;
- Result:=TPascalTypeData(FTypeMap.Items[lName]);
- end;
- function TSchemaData.IndexOfSchemaType(const aSchemaName: String): integer;
- begin
- Result:=FTypeList.Count-1;
- While (Result>=0) and (GetSchemaType(Result).SchemaName<>aSchemaName) do
- Dec(Result);
- end;
- function TSchemaData.GetSchemaType(aSchema: TJSONSchema): TSchemaSimpleType;
- begin
- if aSchema=Nil then
- Result:=sstNone
- else
- Result:=TPascalTypeData.ExtractFirstType(aSchema);
- end;
- function TSchemaData.GetArrayElementType(aSchema: TJSONSchema): TSchemaSimpleType;
- begin
- Result:=sstNone;
- if GetSchemaType(aSchema)=sstArray then
- Result:=GetSchemaType(aSchema.Items[0]);
- end;
- procedure TSchemaData.AddType(const aSchemaName: String; aType: TPascalTypeData);
- begin
- FTypeList.Add(aType);
- addToTypeMap(aSchemaName,aType);
- end;
- procedure TSchemaData.AddToTypeMap(const aSchemaName: String; aData: TPascalTypeData);
- begin
- if FTypeMap.Items[aSchemaName]=Nil then
- FTypeMap.Add(aSchemaName,aData);
- end;
- procedure TSchemaData.SortTypes;
- Procedure AddToList(aList : TPascalTypeDataList; aType : TPascalTypeData);
- var
- I : integer;
- begin
- if aType.Sorted then
- exit;
- for I:=0 to aType.DependencyCount-1 do
- AddToList(aList,aType.Dependency[i]);
- aList.Add(aType);
- aType.Sorted:=True;
- end;
- var
- lTmpList,lSortedList : TPascalTypeDataList;
- i : integer;
- begin
- FTypeList.Sort(@CompareTypeDataOnName);
- lSortedList:=TPascalTypeDataList.Create(False);
- try
- lTmpList:=lSortedList;
- For I:=0 to FTypeList.Count-1 do
- AddToList(lSortedList,TPascalTypeData(FTypeList[i]));
- lTmpList:=FTypeList;
- FTypeList:=lSortedList;
- FTypeList.OwnsObjects:=True;
- lSortedList:=lTmpList;
- lSortedList.OwnsObjects:=False;
- finally
- lSortedList.Free;
- end;
- end;
- end.
|