fpjson.schema.pascaltypes.pp 38 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244
  1. {
  2. This file is part of the Free Component Library
  3. Copyright (c) 2024 by Michael Van Canneyt [email protected]
  4. JSON Schema - pascal types and helpers
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. unit fpjson.schema.pascaltypes;
  12. {$mode ObjFPC}{$H+}
  13. interface
  14. uses
  15. {$IFDEF FPC_DOTTEDUNITS}
  16. System.Classes, System.SysUtils, System.Contnrs, System.StrUtils,
  17. {$ELSE}
  18. Classes, SysUtils, contnrs, StrUtils,
  19. {$ENDIF}
  20. fpjson.schema.types,
  21. fpjson.schema.schema;
  22. Type
  23. ESchemaData = Class(EJSONSchema);
  24. TPascalTypeData = Class;
  25. TSchemaCodeGenLogEvent = Procedure (aType : TEventType; const Msg : String) of object;
  26. TDependencyType = (dtNone,dtDirect,dtIndirect);
  27. TDependencyTypes = set of TDependencyType;
  28. TNameType = (ntSchema,ntPascal,ntInterface,ntImplementation,ntSerializer);
  29. TNameTypes = set of TNameType;
  30. TSerializeType = (stSerialize,stDeserialize);
  31. TSerializeTypes = set of TSerializeType;
  32. TPascalType = (ptUnknown,
  33. ptBoolean, // Boolean
  34. ptInteger, // 32-bit integer
  35. ptInt64, // 64-bit integer
  36. ptDateTime, // TDateTime
  37. ptFloat32, // Single
  38. ptFloat64, // Double
  39. ptString, // String
  40. ptEnum, // Enumerated
  41. ptJSON, // TJSONData (empty schema object)
  42. ptAnonStruct, // Anonymous Class/Record (schema object with properties)
  43. ptSchemaStruct, // Named Class/Record
  44. ptArray // Array of...
  45. );
  46. TPascalTypes = Set of TPascalType;
  47. // Aliases
  48. TPropertyType = TPascalType;
  49. TPropertyTypes = TPascalTypes;
  50. { TPascalProperty }
  51. { TPascalPropertyData }
  52. TPascalPropertyData = class(TObject)
  53. private
  54. FSchemaName: string;
  55. FElementType: TPropertyType;
  56. FEnumValues: TStrings;
  57. FPascalName: string;
  58. FSchema: TJSONSchema;
  59. FTypeNames: Array[TNameType] of string ;
  60. FElementTypeNames: Array[TNameType] of string ;
  61. FPropertyType: TPropertyType;
  62. FTypeData: TPascalTypeData;
  63. function GetElementTypeNames(aType : TNameType): String;
  64. function GetFallBackTypeName(aPropertyType: TPropertyType): string;
  65. function GetPascalTypeName: String;
  66. procedure SetElementTypeNames(aType : TNameType; AValue: String);
  67. procedure SetEnumValues(AValue: TStrings);
  68. Function GetTypeName(aType: TNameType) : String;
  69. procedure SetTypeData(AValue: TPascalTypeData);
  70. procedure SetTypeName(aType: TNameType; aValue : String);
  71. Public
  72. Constructor Create(const aSchemaName, aPascalName : string);
  73. Destructor Destroy; override;
  74. // schema Name of the property
  75. Property SchemaName : string Read FSchemaName Write FSchemaName;
  76. // Pascal Name of the property
  77. Property PascalName : string Read FPascalName Write FPascalName;
  78. // Indexed access to all kind of type names
  79. Property TypeNames [aType : TNameType] : String Read GetTypeName Write SetTypeName;
  80. // Type of the property
  81. Property PropertyType : TPropertyType Read FPropertyType Write FPropertyType;
  82. // If Type is ptEnum, the values. Without _empty_.
  83. Property EnumValues : TStrings Read FEnumValues Write SetEnumValues;
  84. // Pascal type name for the property (same as TypeNames[ntPascal])
  85. Property PascalTypeName : String Index ntPascal Read GetTypeName Write SetTypeName;
  86. // PropertyType = ptArray : The array element type
  87. Property ElementType : TPropertyType Read FElementType Write FElementType;
  88. // PropertyType = ptArray : The array element type name (same as ElementTypeNames[ntPascal])
  89. Property ElementTypeName : String Index ntPascal Read GetElementTypeNames Write SetElementTypeNames;
  90. // PropertyType = ptArray : The array element type names
  91. Property ElementTypeNames[aType : TNameType] : String Read GetElementTypeNames Write SetElementTypeNames;
  92. // PropertyType = ptSchemaStruct: The type data for that component.
  93. // PropertyType = ptArray and elType=ptSchemaStruct
  94. Property TypeData : TPascalTypeData Read FTypeData Write SetTypeData;
  95. // The JSON Schema for this property
  96. Property Schema : TJSONSchema Read FSchema Write FSchema;
  97. end;
  98. { TPascalTypeData }
  99. TPascalTypeData = class(TObject)
  100. private
  101. FElementTypeData: TPascalTypeData;
  102. FSchemaName: String;
  103. FImplementationName: String;
  104. FIndex: Integer;
  105. FInterfaceName: String;
  106. FInterfaceUUID: String;
  107. FPascalName: String;
  108. FSchema: TJSONSChema;
  109. FDependencies : TFPObjectList;
  110. FSerializerName: String;
  111. FSerializeTypes: TSerializeTypes;
  112. FSorted : Boolean;
  113. FProperties : TFPObjectList;
  114. FType: TPascalType;
  115. function GetDependency(aIndex : Integer): TPascalTypeData;
  116. function GetDependencyCount: Integer;
  117. function GetImplementationName: String;
  118. function GetInterfaceName: String;
  119. function GetInterfaceUUID: String;
  120. function GetProperty(aIndex : Integer): TPascalPropertyData;
  121. function GetPropertyCount: Integer;
  122. function GetSerializerName: String;
  123. Protected
  124. function CreateProperty(const aSchemaName, aPascalName: string): TPascalPropertyData; virtual;
  125. Public
  126. class function ExtractFirstType(aSchema: TJSONSchema): TSchemaSimpleType;
  127. Public
  128. Constructor Create(aIndex : integer; aType : TPascalType; const aSchemaName,aPascalName : String; aSchema : TJSONSchema);
  129. destructor Destroy; override;
  130. // Sort the properties.
  131. Procedure SortProperties;
  132. // Index of property using schema name
  133. Function IndexOfProperty(const aSchemaName: string) : Integer;
  134. // Index of property using Pascal name
  135. Function IndexOfPascalProperty(const aPascalName: string) : Integer;
  136. // Find property by schema name.
  137. Function FindProperty(const aName: string) : TPascalPropertyData;
  138. // Add a property. The pascal name must not yet exist.
  139. Function AddProperty(const aSchemaName,aPascalName : String) : TPascalPropertyData;
  140. // Return the requested name
  141. function GetTypeName(aNameType : TNameType) : string;
  142. // Check whether this component depends on given component. If recurse is true, check all intermediary structures as well.
  143. function DependsOn(aData : TPascalTypeData; Recurse : Boolean) : TDependencyType;
  144. // Add aData as a type this type depends on.
  145. Procedure AddDependency(aData : TPascalTypeData);
  146. // Component has array-typed property ?
  147. Function HasArrayProperty : Boolean;
  148. // Component has object-typed property ? (SchemaComponentsonly = False -> also return array of string etc.)
  149. function HasObjectProperty(aSchemaComponentsOnly: Boolean): Boolean;
  150. // Components his component depends on
  151. Property Dependency[aIndex : Integer] : TPascalTypeData Read GetDependency;
  152. // Number of Components his component depends on
  153. Property DependencyCount : Integer Read GetDependencyCount;
  154. // Indexed access to Properties
  155. Property Properties[aIndex : Integer] : TPascalPropertyData Read GetProperty; default;
  156. // Number of properties
  157. Property PropertyCount : Integer Read GetPropertyCount;
  158. // Pascal type name for DTO (can be influenced by map). Default is schema name with prefix/suffix
  159. Property PascalName : String Read FPascalName;
  160. // Schema name.
  161. Property SchemaName : String Read FSchemaName;
  162. // Interface name. Default Pascal name + 'Intf'
  163. Property InterfaceName : String Read GetInterfaceName Write FInterfaceName;
  164. // Interface UUID.
  165. Property InterfaceUUID : String Read GetInterfaceUUID Write FInterfaceUUID;
  166. // Implemention class name. Default Pascal name + 'Obj'
  167. Property ImplementationName : String Read GetImplementationName write FImplementationName;
  168. // Name of serialized helper. Default is Pascal name + 'Serializer'
  169. Property SerializerName : String Read GetSerializerName Write FSerializerName;
  170. // Do we need to serialize/deserialize ?
  171. Property SerializeTypes : TSerializeTypes Read FSerializeTypes Write FSerializeTypes;
  172. // Schema of this component.
  173. Property Schema: TJSONSChema Read FSchema;
  174. // Was this element sorted ?
  175. Property Sorted : Boolean Read FSorted Write FSorted;
  176. // PascalType
  177. Property Pascaltype : TPascalType Read FType;
  178. // For arrays, a pointer to the element type
  179. Property ElementTypeData : TPascalTypeData Read FElementTypeData Write FElementTypeData;
  180. end;
  181. { TPascalTypeDataList }
  182. TPascalTypeDataList = Class(TFPObjectList)
  183. private
  184. function GetTypes(aIndex : Integer): TPascalTypeData;
  185. Public
  186. Procedure Add(aItem : TPascalTypeData); reintroduce;
  187. Property Types[aIndex : Integer] : TPascalTypeData Read GetTypes; default;
  188. end;
  189. TKeywordEscapeMode = (kemAmpersand,kemSuffix,kemPrefix);
  190. { TSchemaData }
  191. TSchemaData = class(TObject)
  192. private
  193. FKeywordEscapeMode: TKeywordEscapeMode;
  194. FTypeList : TPascalTypeDataList;
  195. FAliasList : TPascalTypeDataList;
  196. FTypeMap : TFPObjectHashTable;
  197. FArrayTypePrefix: string;
  198. FArrayTypeSuffix: string;
  199. FDelphiTypes: Boolean;
  200. FInterfaceTypePrefix: String;
  201. FObjectTypePrefix: string;
  202. FObjectTypeSuffix: string;
  203. FOnLog: TSchemaCodeGenLogEvent;
  204. FUseEnums: Boolean;
  205. function GetSchemaType(aIndex : Integer): TPascalTypeData;
  206. function GetSchemaTypeCount: Integer;
  207. protected
  208. // Logging
  209. procedure DoLog(Const aType : TEventType; const aMessage : String);
  210. procedure DoLog(Const aType : TEventType; const aFmt : String; aArgs : Array of const);
  211. // Override this to finish creating a type.
  212. procedure FinishAutoCreatedType(aName: string; aType: TPascalTypeData; lElementTypeData: TPascalTypeData); virtual;
  213. // Override this to determine the type name of a pascal property
  214. function GetSchemaTypeAndName(aType: TPascalTypeData; aSchema: TJSONSchema; out aPropType: TPascalType; aNameType: TNameType=ntPascal): String; virtual;
  215. // Add a new type to the type map.
  216. procedure AddToTypeMap(const aSchemaName: String; aData : TPascalTypeData); virtual; overload;
  217. // Get pascal type name based on schema name
  218. function SchemaNameToNameType(const aName: string; aNameType: TNameType): string; virtual;
  219. // Take JSONPointer reference and find pascal type data for it.
  220. function GetPascalTypeDataFromRef(const aRef: String): TPascalTypeData; virtual;
  221. // Find schema pascal type data. If AllowCreate is true, type data for Enum,Array and object properties will be created.
  222. function GetSchemaTypeData(aType: TPascalTypeData; lSchema: TJSONSchema; AllowCreate: Boolean=False): TPascalTypeData;
  223. // Add a type to the alias list
  224. Procedure AddAliasType(aType : TPascalTypeData); virtual;
  225. // Sanitize identifier
  226. function Sanitize(const aName : string) : String;
  227. // Sort types in dependency order
  228. procedure SortTypes;
  229. Public
  230. Constructor Create; virtual;
  231. Destructor Destroy; override;
  232. // Create aliases for known simple types
  233. procedure DefineStandardPascalTypes;
  234. // Is the word a pascal keyword ?
  235. class function IsKeyWord(const aWord : String) : Boolean;
  236. // Escape the word if it is a pascal keyword ?
  237. function EscapeKeyWord(const aWord : string) : string;
  238. // Get the pascal name based on schema name
  239. function GetTypeMap(const aName : string): String;
  240. // Return index of named schema type (name as in OpenApi). Return -1 if not found.
  241. function IndexOfSchemaType(const aSchemaName: String): integer;
  242. // Find Pascal type data based on schema type name.
  243. function FindSchemaTypeData(const aSchemaName: String; aFormat : String = ''): TPascalTypeData;
  244. // Extract simple type from schema
  245. Function GetSchemaType(aSchema : TJSONSchema) : TSchemaSimpleType;
  246. // Extract element type from schema
  247. Function GetArrayElementType(aSchema : TJSONSchema) : TSchemaSimpleType;
  248. // Used when creating a new type. Override to create a descendant;
  249. function CreatePascalType(aIndex: integer; aType : TPascalType; const aSchemaName, aPascalName: String; aSchema: TJSONSchema): TPascalTypeData; virtual;
  250. // Add a type to the list
  251. Procedure AddType(const aSchemaName: String; aType : TPascalTypeData); virtual;
  252. // Add a type definition to the type map.
  253. function AddAliasToTypeMap(aType: TPascalType; const aAlias, aSchemaTypeName, aPascalTypeName: String; aSchema: TJSONSchema) : TPascalTypeData; overload;
  254. // Add a property to a type
  255. function AddTypeProperty(aType: TPascalTypeData; lProp: TJSONSchema; aName : string = ''; Recurse : Boolean = True): TPascalPropertyData;
  256. // Add properties to structured pascal type from aSchema. if aSchema = nil then use aType.Schema
  257. Procedure AddPropertiesToType(aType : TPascalTypeData; aSchema: TJSONSchema = Nil; Recurse : Boolean = True);
  258. // For all types, fill the depency list: contains all structured types on which the type depends (recursively).
  259. procedure CheckDependencies; virtual;
  260. // Number of types
  261. Property TypeCount : Integer Read GetSchemaTypeCount;
  262. // Indexed access to all types.
  263. Property Types[aIndex : Integer] : TPascalTypeData Read GetSchemaType; default;
  264. // Map schema type to pascal type.
  265. Property TypeMap[aSchemaName : string] : String Read GetTypeMap;
  266. // prefix for object definitions. Default T
  267. Property ObjectTypePrefix : string Read FObjectTypePrefix Write FObjectTypePrefix;
  268. // prefix for object definitions. Default empty
  269. Property ObjectTypeSuffix : string Read FObjectTypeSuffix Write FObjectTypeSuffix;
  270. // Prefix for Dto Objects
  271. Property InterfaceTypePrefix : String Read FInterfaceTypePrefix Write FInterfaceTypePrefix;
  272. // Prefix for array types
  273. Property ArrayTypePrefix : string Read FArrayTypePrefix Write FArrayTypePrefix;
  274. // Suffix for array types. Default Array
  275. Property ArrayTypeSuffix : string Read FArrayTypeSuffix Write FArrayTypeSuffix;
  276. // Use delphi types: TArray<X> instead of Array of X
  277. Property DelphiTypes : Boolean Read FDelphiTypes Write FDelphiTypes;
  278. // Use enums for enumerateds (default is to keep them as strings)
  279. Property UseEnums : Boolean Read FUseEnums Write FUseEnums;
  280. // Log callback
  281. Property OnLog : TSchemaCodeGenLogEvent Read FOnLog Write FOnLog;
  282. // how to escape keywords
  283. Property KeywordEscapeMode : TKeywordEscapeMode Read FKeywordEscapeMode Write FKeywordEscapeMode;
  284. end;
  285. implementation
  286. function CompareTypeDataOnName(Item1, Item2: Pointer): Integer;
  287. var
  288. lType1 : TPascalTypeData absolute Item1;
  289. lType2 : TPascalTypeData absolute Item2;
  290. begin
  291. Result:=CompareText(lType1.SchemaName,lType2.SchemaName);
  292. end;
  293. function CompareProperties(Item1, Item2: Pointer): Integer;
  294. var
  295. lParam1 : TPascalPropertyData absolute Item1;
  296. lParam2 : TPascalPropertyData absolute Item2;
  297. begin
  298. Result:=CompareText(lParam1.PascalName,lParam2.PascalName);
  299. end;
  300. { TPascalPropertyData }
  301. procedure TPascalPropertyData.SetEnumValues(AValue: TStrings);
  302. begin
  303. if FEnumValues=AValue then Exit;
  304. FEnumValues.Assign(AValue);
  305. end;
  306. function TPascalPropertyData.GetPascalTypeName: String;
  307. begin
  308. Result:=GetTypeName(ntPascal);
  309. end;
  310. function TPascalPropertyData.GetElementTypeNames(aType : TNameType): String;
  311. begin
  312. Result:=FElementTypeNames[aType];
  313. if Result<>'' then
  314. exit;
  315. if (PropertyType=ptArray) then
  316. begin
  317. if (ElementType=ptSchemaStruct) then
  318. Exit(TypeData.GetTypeName(aType));
  319. Result:=GetFallBackTypeName(ElementType);
  320. end;
  321. end;
  322. procedure TPascalPropertyData.SetElementTypeNames(aType : TNameType; AValue: String);
  323. begin
  324. FElementTypeNames[aType]:=aValue;
  325. end;
  326. constructor TPascalPropertyData.Create(const aSchemaName, aPascalName: string);
  327. begin
  328. FSchemaName:=aSchemaName;
  329. FPascalName:=aPascalName;
  330. FEnumValues:=TStringList.Create;
  331. end;
  332. destructor TPascalPropertyData.Destroy;
  333. begin
  334. FreeAndNil(FEnumValues);
  335. inherited Destroy;
  336. end;
  337. function TPascalPropertyData.GetTypeName(aType: TNameType): String;
  338. begin
  339. Result:=FTypeNames[aType];
  340. if Result<>'' then
  341. exit;
  342. if Assigned(FTypeData) then
  343. Exit(FTypeData.GetTypeName(aType));
  344. // Fallback
  345. Result:=GetFallBackTypeName(FPropertyType);
  346. end;
  347. procedure TPascalPropertyData.SetTypeData(AValue: TPascalTypeData);
  348. begin
  349. if FTypeData=AValue then Exit;
  350. FTypeData:=AValue;
  351. if Assigned(FTypeData) then
  352. FElementType:=FTypeData.Pascaltype;
  353. end;
  354. function TPascalPropertyData.GetFallBackTypeName(aPropertyType: TPropertyType): string;
  355. begin
  356. Case aPropertyType of
  357. ptUnknown : Raise ESchemaData.CreateFmt('Unknown property type for property "%s"',[PascalName]);
  358. ptBoolean : Result:='boolean';
  359. ptInteger : Result:='integer';
  360. ptInt64 : Result:='Int64';
  361. ptDateTime : Result:='TDateTime';
  362. ptFloat32 : Result:='single';
  363. ptFloat64 : Result:='double';
  364. ptString : Result:='string';
  365. ptEnum : Raise ESchemaData.CreateFmt('Unknown name for enumerated property "%s"',[PascalName]);
  366. ptJSON : Result := 'string';
  367. ptAnonStruct : Raise ESchemaData.CreateFmt('Unknown name for structured property "%s"',[PascalName]);
  368. ptSchemaStruct : Raise ESchemaData.CreateFmt('Unknown name for schema-typed property "%s"',[PascalName]);
  369. end;
  370. end;
  371. procedure TPascalPropertyData.SetTypeName(aType: TNameType; aValue: String);
  372. begin
  373. FTypeNames[aType]:=aValue;
  374. end;
  375. function TPascalTypeData.GetDependencyCount: Integer;
  376. begin
  377. Result:=0;
  378. if Assigned(FDependencies) then
  379. Result:=FDependencies.Count;
  380. end;
  381. function TPascalTypeData.GetImplementationName: String;
  382. begin
  383. Result:=FImplementationName;
  384. if Result='' then
  385. begin
  386. if PascalType in [ptSchemaStruct,ptAnonStruct] then
  387. begin
  388. Result:='T'+StringReplace(SchemaName,'Dto','',[rfIgnoreCase]);
  389. Result:=Result+'Obj';
  390. end
  391. else
  392. Result:=PascalName;
  393. end;
  394. end;
  395. function TPascalTypeData.GetInterfaceName: String;
  396. begin
  397. Result:=FInterfaceName;
  398. if Result='' then
  399. begin
  400. if Pascaltype in [ptAnonStruct,ptSchemaStruct] then
  401. Result:='I'+SchemaName
  402. else
  403. Result:=PascalName;
  404. end;
  405. end;
  406. function TPascalTypeData.GetInterfaceUUID: String;
  407. begin
  408. if FInterfaceUUID='' then
  409. FInterfaceUUID:=TGUID.NewGuid.ToString(False);
  410. Result:=FInterfaceUUID;
  411. end;
  412. function TPascalTypeData.GetProperty(aIndex : Integer): TPascalPropertyData;
  413. begin
  414. Result:=TPascalPropertyData(FProperties[aIndex]);
  415. end;
  416. function TPascalTypeData.GetPropertyCount: Integer;
  417. begin
  418. Result:=FProperties.Count;
  419. end;
  420. function TPascalTypeData.GetSerializerName: String;
  421. begin
  422. Result:=FSerializerName;
  423. If Result='' then
  424. Result:=PascalName+'Serializer';
  425. end;
  426. function TPascalTypeData.CreateProperty(const aSchemaName,aPascalName: string): TPascalPropertyData;
  427. begin
  428. Result:=TPascalPropertyData.Create(aSchemaName,aPascalName);
  429. end;
  430. procedure TPascalTypeData.SortProperties;
  431. begin
  432. FProperties.Sort(@CompareProperties);
  433. end;
  434. function TPascalTypeData.GetDependency(aIndex : Integer): TPascalTypeData;
  435. begin
  436. if Assigned(FDependencies) then
  437. Result:=TPascalTypeData(FDependencies[aIndex])
  438. else
  439. Raise EListError.CreateFmt('List index out of bounds: %d',[aIndex]);
  440. end;
  441. constructor TPascalTypeData.Create(aIndex: integer; aType: TPascalType; const aSchemaName, aPascalName: String; aSchema: TJSONSchema
  442. );
  443. begin
  444. FIndex:=aIndex;
  445. FSchema:=ASchema;
  446. FSchemaName:=aSchemaName;
  447. FPascalName:=aPascalName;
  448. FSerializeTypes:=[stSerialize,stDeserialize];
  449. FProperties:=TFPObjectList.Create(True);
  450. FType:=aType;
  451. end;
  452. destructor TPascalTypeData.Destroy;
  453. begin
  454. FreeAndNil(FProperties);
  455. FreeAndNil(FDependencies);
  456. Inherited;
  457. end;
  458. function TPascalTypeData.IndexOfProperty(const aSchemaName: string): Integer;
  459. begin
  460. Result:=FProperties.Count-1;
  461. While (Result>=0) and Not SameText(GetProperty(Result).SchemaName,aSchemaName) do
  462. Dec(Result);
  463. end;
  464. function TPascalTypeData.IndexOfPascalProperty(const aPascalName: string): Integer;
  465. begin
  466. Result:=FProperties.Count-1;
  467. While (Result>=0) and Not SameText(GetProperty(Result).PascalName,aPascalName) do
  468. Dec(Result);
  469. end;
  470. function TPascalTypeData.FindProperty(const aName: string): TPascalPropertyData;
  471. var
  472. Idx : Integer;
  473. begin
  474. Idx:=IndexOfProperty(aName);
  475. If Idx=-1 then
  476. Result:=Nil
  477. else
  478. Result:=GetProperty(Idx);
  479. end;
  480. function TPascalTypeData.AddProperty(const aSchemaName, aPascalName: String): TPascalPropertyData;
  481. begin
  482. if IndexOfPascalProperty(aPascalName)<>-1 then
  483. Raise ESchemaData.CreateFmt('Duplicate property name : %s',[aPascalName]);
  484. Result:=CreateProperty(aSchemaName,aPascalName);
  485. FProperties.Add(Result);
  486. end;
  487. function TPascalTypeData.GetTypeName(aNameType: TNameType): string;
  488. begin
  489. Case aNameType of
  490. ntSchema: Result:=SchemaName;
  491. ntPascal: Result:=PascalName;
  492. ntInterface : Result:=InterfaceName;
  493. ntImplementation : Result:=ImplementationName;
  494. ntSerializer : Result:=SerializerName
  495. end;
  496. end;
  497. function TPascalTypeData.DependsOn(aData: TPascalTypeData; Recurse: Boolean): TDependencyType;
  498. var
  499. I : Integer;
  500. begin
  501. Result:=dtNone;
  502. if Not Assigned(FDependencies) then
  503. exit;
  504. For I:=0 to DependencyCount-1 do
  505. if (Dependency[i]=aData) then
  506. exit(dtDirect);
  507. if not Recurse then
  508. exit;
  509. For I:=0 to DependencyCount-1 do
  510. if (Dependency[i].DependsOn(aData,True)<>dtNone) then
  511. Exit(dtIndirect);
  512. end;
  513. procedure TPascalTypeData.AddDependency(aData: TPascalTypeData);
  514. begin
  515. if FDependencies=Nil then
  516. FDependencies:=TFPObjectList.Create(False);
  517. FDependencies.Add(aData);
  518. end;
  519. procedure TSchemaData.CheckDependencies;
  520. procedure CheckProps(lTop,aData : TPascalTypeData);
  521. var
  522. lProp : TPascalPropertyData;
  523. lPropData : TPascalTypeData;
  524. I : Integer;
  525. begin
  526. For I:=0 to aData.PropertyCount-1 do
  527. begin
  528. lProp:=aData.Properties[I];
  529. lPropData:=lProp.TypeData; // Array element or struct depending on type.
  530. if Assigned(lPropData) then
  531. begin
  532. Case lPropData.Pascaltype of
  533. ptAnonStruct,ptSchemaStruct:
  534. begin
  535. lTop.AddDependency(lPropData);
  536. CheckProps(lTop,lPropData);
  537. end;
  538. ptArray:
  539. begin
  540. lPropData:=lPropData.ElementTypeData;
  541. if assigned(lPropData) and (lPropData.PascalType in [ptAnonStruct,ptSchemaStruct]) then
  542. begin
  543. lTop.AddDependency(lPropData);
  544. CheckProps(lTop,lPropData);
  545. end;
  546. end
  547. else
  548. ;
  549. end;
  550. end;
  551. end;
  552. end;
  553. var
  554. I : Integer;
  555. lData : TPascalTypeData;
  556. begin
  557. For I:=0 to TypeCount-1 do
  558. begin
  559. lData:=Types[I];
  560. CheckProps(lData,lData);
  561. end;
  562. end;
  563. class function TPascalTypeData.ExtractFirstType(aSchema : TJSONSchema): TSchemaSimpleType;
  564. begin
  565. Result:=aSchema.Validations.GetFirstType;
  566. end;
  567. function TPascalTypeData.HasArrayProperty: Boolean;
  568. var
  569. I : integer;
  570. begin
  571. Result:=False;
  572. if not Assigned(FSchema) then exit;
  573. For I:=0 to Schema.Properties.Count-1 do
  574. if (ExtractFirstType(Schema.Properties[i])=sstArray) then
  575. exit(True);
  576. end;
  577. function TPascalTypeData.HasObjectProperty(aSchemaComponentsOnly : Boolean): Boolean;
  578. var
  579. I : integer;
  580. lProp : TJSONSchema;
  581. begin
  582. Result:=False;
  583. if not Assigned(FSchema) then exit;
  584. For I:=0 to Schema.Properties.Count-1 do
  585. begin
  586. lProp:=Schema.Properties[i];
  587. if (lProp.Ref<>'') then
  588. exit(True);
  589. if (ExtractFirstType(lProp)=sstObject) and not aSchemaComponentsOnly then
  590. exit(True);
  591. end;
  592. end;
  593. { TPascalTypeDataList }
  594. function TPascalTypeDataList.GetTypes(aIndex : Integer): TPascalTypeData;
  595. begin
  596. Result:=TPascalTypeData(Items[aIndex]);
  597. end;
  598. procedure TPascalTypeDataList.Add(aItem: TPascalTypeData);
  599. begin
  600. Inherited Add(aItem);
  601. end;
  602. { TSchemaData }
  603. function TSchemaData.GetSchemaTypeCount: Integer;
  604. begin
  605. Result:=FTypeList.Count;
  606. end;
  607. function TSchemaData.GetSchemaType(aIndex : Integer): TPascalTypeData;
  608. begin
  609. Result:=FTypeList[aIndex];
  610. end;
  611. procedure TSchemaData.DoLog(const aType: TEventType; const aMessage: String);
  612. begin
  613. If Assigned(FOnLog) then
  614. FOnLog(aType,aMessage);
  615. end;
  616. procedure TSchemaData.DoLog(const aType: TEventType; const aFmt: String; aArgs: array of const);
  617. begin
  618. If Assigned(FOnLog) then
  619. FOnLog(aType,Format(aFmt,aArgs));
  620. end;
  621. // Find requested name type in API types, based on openAPI name.
  622. function TSchemaData.SchemaNameToNameType(const aName: string; aNameType: TNameType): string;
  623. var
  624. lType : TPascalTypeData;
  625. begin
  626. lType:=FindSchemaTypeData(aName);
  627. if Assigned(lType) then
  628. Result:=lType.GetTypeName(aNameType)
  629. else
  630. Result:=aName;
  631. end;
  632. function TSchemaData.GetPascalTypeDataFromRef(const aRef : String): TPascalTypeData;
  633. var
  634. P : Integer;
  635. lName : String;
  636. begin
  637. P:=RPos('/',aRef);
  638. if P=0 then
  639. P:=RPos('#',aRef);
  640. if p=0 then
  641. lName:=aRef
  642. else
  643. lName:=Copy(aRef,P+1,Length(aRef)-P);
  644. Result:=FindSchemaTypeData(lName);
  645. end;
  646. procedure TSchemaData.AddAliasType(aType: TPascalTypeData);
  647. begin
  648. FAliasList.Add(aType);
  649. end;
  650. function TSchemaData.Sanitize(const aName: string): String;
  651. var
  652. i : integer;
  653. lRes : string;
  654. begin
  655. lRes:=aName;
  656. UniqueString(lRes);
  657. For I:=1 to Length(lRes) do
  658. if not (lRes[i] in ['a'..'z','A'..'Z','0'..'9','_']) then
  659. lRes[i]:='_';
  660. Result:=lRes;
  661. end;
  662. // Determine the PascalType and pascal type name of the given schema
  663. function TSchemaData.GetSchemaTypeAndName(aType: TPascalTypeData; aSchema: TJSONSchema; out aPropType: TPascalType; aNameType : TNameType = ntPascal): String;
  664. var
  665. lTypeData : TPascalTypeData;
  666. begin
  667. lTypeData:=GetSchemaTypeData(aType,aSchema);
  668. if lTypeData=Nil then
  669. begin
  670. aPropType:=ptUnknown;
  671. Result:='';
  672. end
  673. else
  674. begin
  675. aPropType:=lTypeData.Pascaltype;
  676. Result:=lTypeData.GetTypeName(aNameType);
  677. end;
  678. end;
  679. procedure TSchemaData.FinishAutoCreatedType(aName: string; aType: TPascalTypeData; lElementTypeData: TPascalTypeData);
  680. begin
  681. AddType(aName,aType);
  682. Case aType.Pascaltype of
  683. ptAnonStruct:
  684. AddPropertiesToType(aType,aType.Schema,True);
  685. ptArray:
  686. aType.FElementTypeData:=lElementTypeData;
  687. end;
  688. end;
  689. function TSchemaData.GetSchemaTypeData(aType: TPascalTypeData; lSchema: TJSONSchema; AllowCreate : Boolean = False) : TPascalTypeData;
  690. var
  691. lType : TSchemaSimpleType;
  692. lName,lBaseName,lPascalName : string;
  693. lFormat : String;
  694. lElTypeData : TPascalTypeData;
  695. begin
  696. LType:=lSchema.Validations.GetFirstType;
  697. Result:=Nil;
  698. if lSchema.Ref<>'' then
  699. Result:=GetPascalTypeDataFromRef(lSchema.Ref)
  700. else
  701. begin
  702. lName:='';
  703. lFormat:='';
  704. Case lType of
  705. sstNone: ;
  706. sstNull: ;
  707. sstBoolean :
  708. lName:='boolean';
  709. sstInteger :
  710. begin
  711. lName:='integer';
  712. lFormat:=lSchema.Validations.Format;
  713. end;
  714. sstNumber:
  715. begin
  716. lName:='number';
  717. end;
  718. sstString:
  719. begin
  720. if IndexText(lSchema.Validations.Format,['date','time','date-time'])>=0 then
  721. begin
  722. lName:='string';
  723. lFormat:=lSchema.Validations.Format;
  724. end
  725. else if UseEnums and lSchema.Validations.HasKeywordData(jskEnum) and (lSchema.Validations.Enum.Count>0) then
  726. begin
  727. if assigned(aType) then
  728. lBaseName:=aType.GetTypeName(ntSchema)+'_'+lSchema.Name
  729. else
  730. lBaseName:='T'+lSchema.Name;
  731. lName:='('+lBaseName+')';
  732. Result:=FindSchemaTypeData(lName);
  733. if (Result=Nil) and allowCreate then
  734. begin
  735. Result:=CreatePascalType(-1,ptEnum,lName,'T'+lBaseName,lSchema);
  736. FinishAutoCreatedType(lName,Result,Nil);
  737. end;
  738. end
  739. else
  740. begin
  741. lName:='string';
  742. end;
  743. end;
  744. sstArray:
  745. begin
  746. lElTypeData:=GetSchemaTypeData(Nil,lSchema.Items[0]);
  747. lPascalName:=Sanitize(ArrayTypePrefix+lElTypeData.PascalName+ArrayTypeSuffix);
  748. lName:='['+lElTypeData.SchemaName;
  749. if lSchema.Items[0].Validations.HasKeywordData(jskformat) then
  750. lName:=lName+'--'+lSchema.Items[0].Validations.Format;
  751. lName:=LName+']';
  752. Result:=FindSchemaTypeData(lName);
  753. if Result<>Nil then
  754. lName:='';
  755. if (Result=Nil) and AllowCreate then
  756. begin
  757. Result:=CreatePascalType(-1,ptArray,lName,lPascalName,lSchema);
  758. FinishAutoCreatedType(lName,Result,lElTypeData);
  759. lName:='';
  760. end;
  761. end;
  762. sstObject:
  763. begin
  764. if lSchema.Properties.Count=0 then
  765. lName:='JSON'
  766. else
  767. begin
  768. if assigned(aType) then
  769. lBaseName:=aType.GetTypeName(ntSchema)+'_'+lSchema.Name
  770. else
  771. lBaseName:='Nested_'+lSchema.Name;
  772. lName:='{'+lBaseName+'}';
  773. lPascalName:=ObjectTypePrefix+Sanitize(lBaseName);
  774. Result:=FindSchemaTypeData(lName);
  775. if (Result=Nil) and AllowCreate then
  776. begin
  777. Result:=CreatePascalType(-1,ptAnonStruct,lName,lPascalName,lSchema);
  778. FinishAutoCreatedType(lName,Result,lElTypeData);
  779. lName:='';
  780. end;
  781. end;
  782. end;
  783. sstAny:
  784. lname:='any';
  785. end;
  786. if lName<>'' then
  787. Result:=FindSchemaTypeData(lName,lFormat);
  788. end;
  789. end;
  790. // Add a property to the type using the schema
  791. function TSchemaData.AddTypeProperty(aType: TPascalTypeData; lProp: TJSONSchema; aName: string; Recurse: Boolean
  792. ): TPascalPropertyData;
  793. var
  794. lTypeName, lName : string;
  795. lType,lEltype : TPropertyType;
  796. I : Integer;
  797. lPropTypeData : TPascaltypeData;
  798. begin
  799. lName:=aName;
  800. if lName='' then
  801. lName:=EscapeKeyWord(Sanitize(lProp.Name));
  802. DoLog(etInfo,'Adding property name %s to %s',[lName,aType.PascalName]);
  803. if lProp.Validations.TypesCount>1 then
  804. Raise ESchemaData.CreateFmt('Creating property for schema with multiple types ("%s") is not supported',[lName]);
  805. if (lProp.Validations.GetFirstType=sstArray) then
  806. if (lProp.Items.Count<>1) then
  807. Raise ESchemaData.CreateFmt('Creating array property for schema with multiple item types ("%s") is not supported',[lName])
  808. else if (lProp.Items.Count<1) then
  809. Raise ESchemaData.CreateFmt('Creating array property for schema without item types ("%s") is not supported',[lName]);
  810. lPropTypeData:=GetSchemaTypeData(aType,lProp,Recurse);
  811. if lPropTypeData=Nil then
  812. Raise ESchemaData.CreateFmt('Unknown property type for property %s',[lName]);
  813. lType:=lPropTypeData.Pascaltype;
  814. lTypeName:=lPropTypeData.GetTypeName(ntPascal);
  815. Result:=aType.AddProperty(lProp.Name,lName);
  816. Result.Schema:=lProp;
  817. Result.PropertyType:=lType;
  818. Result.TypeData:=lPropTypeData;
  819. Result.PascalTypeName:=lPropTypeData.GetTypeName(ntPascal);
  820. if (lType=ptEnum) then
  821. begin
  822. for I:=0 to lProp.Validations.Enum.Count-1 do
  823. Result.EnumValues.Add(EscapeKeyWord(lProp.Validations.Enum.Items[I].AsString));
  824. end;
  825. if (lType=ptArray) then
  826. begin
  827. Result.PascalTypeName:=lTypeName;
  828. if (lProp.Items[0].Ref<>'') then
  829. begin
  830. Result.ElementType:=ptSchemaStruct;
  831. Result.TypeData:=GetPascalTypeDataFromRef(lProp.Items[0].Ref);
  832. if Result.TypeData=Nil then
  833. Raise ESchemaData.CreateFmt('No typedata for property %s element type (Ref: %s)',[Result.PascalName,lProp.Items[0].Ref]);
  834. Result.ElementTypeName:=Result.TypeData.PascalName;
  835. end
  836. else
  837. begin
  838. Result.ElementTypeName:=GetSchemaTypeAndName(Nil,lProp.Items[0],lEltype);
  839. Result.ElementType:=lElType;
  840. end;
  841. Result.TypeNames[ntInterface]:=GetSchemaTypeAndName(Nil,lProp,lelType,ntInterface);
  842. Result.TypeNames[ntImplementation]:=GetSchemaTypeAndName(Nil,lProp,lElType,ntImplementation);
  843. end;
  844. end;
  845. procedure TSchemaData.AddPropertiesToType(aType: TPascalTypeData; aSchema: TJSONSchema; Recurse: Boolean);
  846. var
  847. I : Integer;
  848. lSchema : TJSONSchema;
  849. begin
  850. lSchema:=aSchema;
  851. if lSchema=Nil then
  852. lSchema:=aType.Schema;
  853. for I:=0 to lSchema.Properties.Count-1 do
  854. AddTypeProperty(aType,lSchema.Properties[i],'',Recurse);
  855. end;
  856. function TSchemaData.CreatePascalType(aIndex: integer; aType : TPascalType; const aSchemaName, aPascalName: String; aSchema: TJSONSchema): TPascalTypeData;
  857. begin
  858. Result:=TPascalTypeData.Create(aIndex,aType,aSchemaName,aPascalName,aSchema);
  859. end;
  860. function TSchemaData.AddAliasToTypeMap(aType: TPascalType; const aAlias, aSchemaTypeName, aPascalTypeName: String;
  861. aSchema: TJSONSchema): TPascalTypeData;
  862. var
  863. lType : TPascalTypeData;
  864. begin
  865. lType:=CreatePascalType(-1,aType,aSchemaTypeName,aPascalTypeName,aSchema);
  866. if not (aType in [ptSchemaStruct,ptAnonStruct,ptArray]) then
  867. lType.InterfaceName:=aPascalTypeName;
  868. AddToTypeMap(aAlias,lType);
  869. AddAliasType(lType);
  870. Result:=lType;
  871. end;
  872. constructor TSchemaData.Create;
  873. begin
  874. FTypeMap:=TFPObjectHashTable.Create(False);
  875. FTypeList:=TPascalTypeDataList.Create(True);
  876. FAliasList:=TPascalTypeDataList.Create(True);
  877. FObjectTypePrefix:='T';
  878. FObjectTypeSuffix:='';
  879. FInterfaceTypePrefix:='I';
  880. FArrayTypeSuffix:='Array';
  881. FArrayTypePrefix:='';
  882. FKeywordEscapeMode:=kemSuffix;
  883. end;
  884. destructor TSchemaData.Destroy;
  885. begin
  886. FreeAndNil(FTypeList);
  887. FreeAndNil(FAliasList);
  888. FreeAndNil(FTypeMap);
  889. inherited Destroy;
  890. end;
  891. procedure TSchemaData.DefineStandardPascalTypes;
  892. var
  893. lArr,lElem : TPascalTypeData;
  894. begin
  895. // typename--format
  896. lElem:=AddAliasToTypeMap(ptInteger,'integer','integer','integer',Nil);
  897. lArr:=AddAliasToTypeMap(ptArray,'[integer]','[integer]','TIntegerDynArray',Nil);
  898. lArr.ElementTypeData:=lElem;
  899. AddAliasToTypeMap(ptInteger,'integer--int32','integer','integer',Nil);
  900. lElem:=AddAliasToTypeMap(ptInt64,'integer--int64','integer','int64',Nil);
  901. lArr:=AddAliasToTypeMap(ptArray,'[integer--int64]','[integer--int64]','TInt64DynArray',Nil);
  902. lArr.ElementTypeData:=lElem;
  903. lElem:=AddAliasToTypeMap(ptString,'string','string','string',Nil);
  904. lArr:=AddAliasToTypeMap(ptArray,'[string]','[string]','TStringDynArray',Nil);
  905. lArr.ElementTypeData:=lElem;
  906. AddAliasToTypeMap(ptDateTime,'string--date','string','TDateTime',Nil);
  907. AddAliasToTypeMap(ptDateTime,'string--time','string','TDateTime',Nil);
  908. AddAliasToTypeMap(ptDateTime,'string--date-time','string','TDateTime',Nil);
  909. lElem:=AddAliasToTypeMap(ptFloat64,'number','number','double',Nil);
  910. lArr:=AddAliasToTypeMap(ptArray,'[number]','[number]','TDoubleDynArray',Nil);
  911. lArr.ElementTypeData:=lElem;
  912. AddAliasToTypeMap(ptJSON,'JSON','object','string',Nil);
  913. AddAliasToTypeMap(ptJSON,'any','object','string',Nil);
  914. lElem:=AddAliasToTypeMap(ptBoolean,'boolean','boolean','boolean',Nil);
  915. lArr:=AddAliasToTypeMap(ptArray,'[boolean]','[boolean]','TBooleanDynArray',Nil);
  916. lArr.ElementTypeData:=lElem;
  917. end;
  918. class function TSchemaData.IsKeyWord(const aWord: String): Boolean;
  919. Const
  920. KW=';absolute;and;array;asm;begin;case;const;constructor;destructor;div;do;'+
  921. 'downto;else;end;file;for;function;goto;if;implementation;in;inherited;'+
  922. 'inline;interface;label;mod;nil;not;object;of;on;operator;or;packed;'+
  923. 'procedure;program;record;reintroduce;repeat;self;set;shl;shr;string;then;'+
  924. 'to;type;unit;until;uses;var;while;with;xor;dispose;exit;false;new;true;'+
  925. 'as;class;dispinterface;except;exports;finalization;finally;initialization;'+
  926. 'inline;is;library;on;out;packed;property;raise;resourcestring;threadvar;try;'+
  927. 'private;published;length;setlength;';
  928. begin
  929. Result:=Pos(';'+lowercase(aWord)+';',KW)<>0;
  930. end;
  931. function TSchemaData.EscapeKeyWord(const aWord: string): string;
  932. begin
  933. Result:=aWord;
  934. if IsKeyWord(Result) then
  935. case KeywordEscapeMode of
  936. kemSuffix : Result:=Result+'_';
  937. kemPrefix : Result:='_'+Result;
  938. kemAmpersand : Result:='&'+Result;
  939. end;
  940. end;
  941. function TSchemaData.GetTypeMap(const aName: string): String;
  942. begin
  943. Result:=SchemaNameToNameType(aName,ntPascal);
  944. end;
  945. // Find Pascal type data based on schema name
  946. function TSchemaData.FindSchemaTypeData(const aSchemaName: String; aFormat: String): TPascalTypeData;
  947. var
  948. lName : string;
  949. begin
  950. lName:=aSchemaName;
  951. if aFormat<>'' then
  952. lName:=lName+'--'+aFormat;
  953. Result:=TPascalTypeData(FTypeMap.Items[lName]);
  954. end;
  955. function TSchemaData.IndexOfSchemaType(const aSchemaName: String): integer;
  956. begin
  957. Result:=FTypeList.Count-1;
  958. While (Result>=0) and (GetSchemaType(Result).SchemaName<>aSchemaName) do
  959. Dec(Result);
  960. end;
  961. function TSchemaData.GetSchemaType(aSchema: TJSONSchema): TSchemaSimpleType;
  962. begin
  963. if aSchema=Nil then
  964. Result:=sstNone
  965. else
  966. Result:=TPascalTypeData.ExtractFirstType(aSchema);
  967. end;
  968. function TSchemaData.GetArrayElementType(aSchema: TJSONSchema): TSchemaSimpleType;
  969. begin
  970. Result:=sstNone;
  971. if GetSchemaType(aSchema)=sstArray then
  972. Result:=GetSchemaType(aSchema.Items[0]);
  973. end;
  974. procedure TSchemaData.AddType(const aSchemaName: String; aType: TPascalTypeData);
  975. begin
  976. FTypeList.Add(aType);
  977. addToTypeMap(aSchemaName,aType);
  978. end;
  979. procedure TSchemaData.AddToTypeMap(const aSchemaName: String; aData: TPascalTypeData);
  980. begin
  981. if FTypeMap.Items[aSchemaName]=Nil then
  982. FTypeMap.Add(aSchemaName,aData);
  983. end;
  984. procedure TSchemaData.SortTypes;
  985. Procedure AddToList(aList : TPascalTypeDataList; aType : TPascalTypeData);
  986. var
  987. I : integer;
  988. begin
  989. if aType.Sorted then
  990. exit;
  991. for I:=0 to aType.DependencyCount-1 do
  992. AddToList(aList,aType.Dependency[i]);
  993. aList.Add(aType);
  994. aType.Sorted:=True;
  995. end;
  996. var
  997. lTmpList,lSortedList : TPascalTypeDataList;
  998. i : integer;
  999. begin
  1000. FTypeList.Sort(@CompareTypeDataOnName);
  1001. lSortedList:=TPascalTypeDataList.Create(False);
  1002. try
  1003. lTmpList:=lSortedList;
  1004. For I:=0 to FTypeList.Count-1 do
  1005. AddToList(lSortedList,TPascalTypeData(FTypeList[i]));
  1006. lTmpList:=FTypeList;
  1007. FTypeList:=lSortedList;
  1008. FTypeList.OwnsObjects:=True;
  1009. lSortedList:=lTmpList;
  1010. lSortedList.OwnsObjects:=False;
  1011. finally
  1012. lSortedList.Free;
  1013. end;
  1014. end;
  1015. end.