dtdmodel.pp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466
  1. {
  2. This file is part of the Free Component Library
  3. Object model for DTD.
  4. Copyright (c) 2010 by Sergei Gorelkin, [email protected]
  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 dtdmodel;
  12. {$ifdef fpc}
  13. {$MODE objfpc}{$H+}
  14. {$endif}
  15. interface
  16. uses
  17. Classes, SysUtils, xmlutils;
  18. type
  19. TCPType = (ctName, ctChoice, ctSeq);
  20. TCPQuant = (cqOnce, cqZeroOrOnce, cqZeroOrMore, cqOnceOrMore);
  21. TContentParticle = class(TObject)
  22. private
  23. FParent: TContentParticle;
  24. FChildren: TFPList;
  25. FIndex: Integer;
  26. FDef: TObject;
  27. FCPType: TCPType;
  28. FCPQuant: TCPQuant;
  29. function GetChildCount: Integer;
  30. function GetChild(Index: Integer): TContentParticle;
  31. public
  32. destructor Destroy; override;
  33. function Add: TContentParticle;
  34. function IsRequired: Boolean;
  35. function FindFirst(aDef: TObject): TContentParticle;
  36. function FindNext(aDef: TObject; ChildIdx: Integer): TContentParticle;
  37. function MoreRequired(ChildIdx: Integer): Boolean;
  38. property ChildCount: Integer read GetChildCount;
  39. property Children[Index: Integer]: TContentParticle read GetChild;
  40. property Def: TObject read FDef write FDef;
  41. property CPType: TCPType read FCPType write FCPType;
  42. property CPQuant: TCPQuant read FCPQuant write FCPQuant;
  43. end;
  44. TDTDObject = class(TObject)
  45. private
  46. FExternallyDeclared: Boolean;
  47. public
  48. property ExternallyDeclared: Boolean read FExternallyDeclared write FExternallyDeclared;
  49. end;
  50. TAttrDefault = (
  51. adImplied,
  52. adDefault,
  53. adRequired,
  54. adFixed
  55. );
  56. TAttributeDef = class(TDTDObject)
  57. private
  58. FData: PNodeData;
  59. FDataType: TAttrDataType;
  60. FDefault: TAttrDefault;
  61. FIndex: Cardinal;
  62. FIsNamespaceDecl: Boolean;
  63. FEnumeration: array of XMLString;
  64. public
  65. constructor Create(aName: PHashItem; aColonPos: Integer);
  66. destructor Destroy; override;
  67. function AddEnumToken(Buf: PWideChar; Len: Integer): Boolean;
  68. function HasEnumToken(const aValue: XMLString): Boolean;
  69. function ValidateSyntax(const aValue: XMLString; Namespaces: Boolean): Boolean;
  70. property Data: PNodeData read FData;
  71. property Default: TAttrDefault read FDefault write FDefault;
  72. property DataType: TAttrDataType read FDataType write FDataType;
  73. property Index: Cardinal read FIndex;
  74. property IsNamespaceDecl: Boolean read FIsNamespaceDecl;
  75. end;
  76. TElementContentType = (
  77. ctUndeclared,
  78. ctAny,
  79. ctEmpty,
  80. ctMixed,
  81. ctChildren
  82. );
  83. TElementDecl = class(TDTDObject)
  84. private
  85. FAttrDefs: TFPList;
  86. FNeedsDefaultPass: Boolean;
  87. FHasRequiredAtts: Boolean;
  88. function GetAttrDefCount: Integer;
  89. function AttrDefByIndex(index: Integer): TAttributeDef;
  90. public
  91. ContentType: TElementContentType;
  92. IDAttr: TAttributeDef;
  93. NotationAttr: TAttributeDef;
  94. RootCP: TContentParticle;
  95. destructor Destroy; override;
  96. function GetAttrDef(aName: PHashItem): TAttributeDef;
  97. procedure AddAttrDef(aDef: TAttributeDef);
  98. property AttrDefCount: Integer read GetAttrDefCount;
  99. property AttrDefs[index: Integer]: TAttributeDef read AttrDefByIndex;
  100. property NeedsDefaultPass: Boolean read FNeedsDefaultPass;
  101. property HasRequiredAtts: Boolean read FHasRequiredAtts;
  102. end;
  103. TEntityDecl = class(TDTDObject)
  104. public
  105. FName: XMLString; // TODO: change to PHashItem
  106. FInputEncoding: XMLString;
  107. FXMLEncoding: XMLString;
  108. FPublicID: XMLString;
  109. FSystemID: XMLString;
  110. FNotationName: XMLString;
  111. FURI: XMLString;
  112. FReplacementText: XMLString;
  113. FXMLVersion: TXMLVersion;
  114. FPrefetched: Boolean;
  115. FResolved: Boolean;
  116. FOnStack: Boolean;
  117. FBetweenDecls: Boolean;
  118. FIsPE: Boolean;
  119. FStartLocation: TLocation;
  120. FCharCount: Cardinal;
  121. end;
  122. TNotationDecl = class(TDTDObject)
  123. public
  124. FName: XMLString;
  125. FPublicID: XMLString;
  126. FSystemID: XMLString;
  127. FURI: XMLString;
  128. end;
  129. TDTDModel = class
  130. private
  131. FRefCount: Integer;
  132. FNameTable: THashTable;
  133. FEntities: THashTable;
  134. FNotations: THashTable;
  135. function GetEntities: THashTable;
  136. function GetNotations: THashTable;
  137. public
  138. FName: XMLString;
  139. FSystemID: XMLString;
  140. FPublicID: XMLString;
  141. FInternalSubset: XMLString;
  142. constructor Create(aNameTable: THashTable);
  143. destructor Destroy; override;
  144. function Reference: TDTDModel;
  145. procedure Release;
  146. property Entities: THashTable read GetEntities;
  147. property Notations: THashTable read GetNotations;
  148. end;
  149. implementation
  150. { TDTDModel }
  151. function TDTDModel.GetEntities: THashTable;
  152. begin
  153. if FEntities = nil then
  154. FEntities := THashTable.Create(256, True);
  155. Result := FEntities;
  156. end;
  157. function TDTDModel.GetNotations: THashTable;
  158. begin
  159. if FNotations = nil then
  160. FNotations := THashTable.Create(256, True);
  161. Result := FNotations;
  162. end;
  163. constructor TDTDModel.Create(aNameTable: THashTable);
  164. begin
  165. FNameTable := aNameTable;
  166. FRefCount := 1;
  167. end;
  168. destructor TDTDModel.Destroy;
  169. begin
  170. FEntities.Free;
  171. FNotations.Free;
  172. inherited Destroy;
  173. end;
  174. function TDTDModel.Reference: TDTDModel;
  175. begin
  176. Inc(FRefCount);
  177. Result := Self;
  178. end;
  179. procedure TDTDModel.Release;
  180. begin
  181. if Assigned(Self) then
  182. begin
  183. Dec(FRefCount);
  184. if FRefCount = 0 then
  185. self.Destroy;
  186. end;
  187. end;
  188. { TContentParticle }
  189. function TContentParticle.Add: TContentParticle;
  190. begin
  191. if FChildren = nil then
  192. FChildren := TFPList.Create;
  193. Result := TContentParticle.Create;
  194. Result.FParent := Self;
  195. Result.FIndex := FChildren.Add(Result);
  196. end;
  197. destructor TContentParticle.Destroy;
  198. var
  199. I: Integer;
  200. begin
  201. if Assigned(FChildren) then
  202. for I := FChildren.Count-1 downto 0 do
  203. TObject(FChildren[I]).Free;
  204. FChildren.Free;
  205. inherited Destroy;
  206. end;
  207. function TContentParticle.GetChild(Index: Integer): TContentParticle;
  208. begin
  209. Result := TContentParticle(FChildren[Index]);
  210. end;
  211. function TContentParticle.GetChildCount: Integer;
  212. begin
  213. if Assigned(FChildren) then
  214. Result := FChildren.Count
  215. else
  216. Result := 0;
  217. end;
  218. function TContentParticle.IsRequired: Boolean;
  219. var
  220. I: Integer;
  221. begin
  222. Result := (CPQuant = cqOnce) or (CPQuant = cqOnceOrMore);
  223. // do not return True if all children are optional
  224. if (CPType <> ctName) and Result then
  225. begin
  226. for I := 0 to ChildCount-1 do
  227. begin
  228. Result := Children[I].IsRequired;
  229. if Result then Exit;
  230. end;
  231. end;
  232. end;
  233. function TContentParticle.MoreRequired(ChildIdx: Integer): Boolean;
  234. var
  235. I: Integer;
  236. begin
  237. Result := False;
  238. if CPType = ctSeq then
  239. begin
  240. for I := ChildIdx + 1 to ChildCount-1 do
  241. begin
  242. Result := Children[I].IsRequired;
  243. if Result then Exit;
  244. end;
  245. end;
  246. if Assigned(FParent) then
  247. Result := FParent.MoreRequired(FIndex);
  248. end;
  249. function TContentParticle.FindFirst(aDef: TObject): TContentParticle;
  250. var
  251. I: Integer;
  252. begin
  253. Result := nil;
  254. case CPType of
  255. ctSeq:
  256. for I := 0 to ChildCount-1 do with Children[I] do
  257. begin
  258. Result := FindFirst(aDef);
  259. if Assigned(Result) or IsRequired then
  260. Exit;
  261. end;
  262. ctChoice:
  263. for I := 0 to ChildCount-1 do with Children[I] do
  264. begin
  265. Result := FindFirst(aDef);
  266. if Assigned(Result) then
  267. Exit;
  268. end;
  269. else // ctName
  270. if aDef = Self.Def then
  271. Result := Self
  272. end;
  273. end;
  274. function TContentParticle.FindNext(aDef: TObject;
  275. ChildIdx: Integer): TContentParticle;
  276. var
  277. I: Integer;
  278. begin
  279. Result := nil;
  280. if CPType = ctSeq then // search sequence to its end
  281. begin
  282. for I := ChildIdx + 1 to ChildCount-1 do with Children[I] do
  283. begin
  284. Result := FindFirst(aDef);
  285. if (Result <> nil) or IsRequired then
  286. Exit;
  287. end;
  288. end;
  289. if (CPQuant = cqZeroOrMore) or (CPQuant = cqOnceOrMore) then
  290. Result := FindFirst(aDef);
  291. if (Result = nil) and Assigned(FParent) then
  292. Result := FParent.FindNext(aDef, FIndex);
  293. end;
  294. { TElementDecl }
  295. function TElementDecl.GetAttrDefCount: Integer;
  296. begin
  297. if Assigned(FAttrDefs) then
  298. Result := FAttrDefs.Count
  299. else
  300. Result := 0;
  301. end;
  302. function TElementDecl.AttrDefByIndex(index: Integer): TAttributeDef;
  303. begin
  304. if Assigned(FAttrDefs) then
  305. Result := TAttributeDef(FAttrDefs[index])
  306. else
  307. Result := nil;
  308. end;
  309. destructor TElementDecl.Destroy;
  310. var
  311. i: Integer;
  312. begin
  313. RootCP.Free;
  314. if Assigned(FAttrDefs) then
  315. begin
  316. for i := FAttrDefs.Count-1 downto 0 do
  317. TObject(FAttrDefs.List^[i]).Free;
  318. FAttrDefs.Free;
  319. end;
  320. inherited Destroy;
  321. end;
  322. function TElementDecl.GetAttrDef(aName: PHashItem): TAttributeDef;
  323. var
  324. i: Integer;
  325. begin
  326. if Assigned(FAttrDefs) then
  327. begin
  328. for i := 0 to FAttrDefs.Count-1 do
  329. begin
  330. Result := TAttributeDef(FAttrDefs.List^[i]);
  331. if Result.FData^.FQName = aName then
  332. Exit;
  333. end;
  334. end;
  335. Result := nil;
  336. end;
  337. procedure TElementDecl.AddAttrDef(aDef: TAttributeDef);
  338. begin
  339. if FAttrDefs = nil then
  340. FAttrDefs := TFPList.Create;
  341. aDef.FIndex := FAttrDefs.Add(aDef);
  342. if aDef.Default in [adRequired, adDefault, adFixed] then
  343. FNeedsDefaultPass := True;
  344. if aDef.Default = adRequired then
  345. FHasRequiredAtts := True;
  346. end;
  347. { TAttributeDef }
  348. constructor TAttributeDef.Create(aName: PHashItem; aColonPos: Integer);
  349. begin
  350. New(FData);
  351. FillChar(FData^, sizeof(TNodeData), 0);
  352. FData^.FIsDefault := True;
  353. FData^.FQName := aName;
  354. FData^.FColonPos := aColonPos;
  355. FData^.FTypeInfo := Self;
  356. FIsNamespaceDecl := ((Length(aName^.Key) = 5) or (aColonPos = 6)) and
  357. (Pos(XMLString('xmlns'), aName^.Key) = 1);
  358. end;
  359. destructor TAttributeDef.Destroy;
  360. var
  361. curr, tmp: PNodeData;
  362. begin
  363. curr := FData;
  364. while Assigned(curr) do
  365. begin
  366. tmp := curr^.FNext;
  367. Dispose(curr);
  368. curr := tmp;
  369. end;
  370. inherited Destroy;
  371. end;
  372. function TAttributeDef.AddEnumToken(Buf: PWideChar; Len: Integer): Boolean;
  373. var
  374. I, L: Integer;
  375. begin
  376. // TODO: this implementaion is the slowest possible...
  377. Result := False;
  378. L := Length(FEnumeration);
  379. for I := 0 to L-1 do
  380. begin
  381. if (Len = Length(FEnumeration[i])) and
  382. CompareMem(Pointer(FEnumeration[i]), Buf, Len*sizeof(WideChar)) then
  383. Exit;
  384. end;
  385. SetLength(FEnumeration, L+1);
  386. SetString(FEnumeration[L], Buf, Len);
  387. Result := True;
  388. end;
  389. function TAttributeDef.HasEnumToken(const aValue: XMLString): Boolean;
  390. var
  391. I: Integer;
  392. begin
  393. Result := True;
  394. if Length(FEnumeration) = 0 then
  395. Exit;
  396. for I := 0 to Length(FEnumeration)-1 do
  397. begin
  398. if FEnumeration[I] = aValue then
  399. Exit;
  400. end;
  401. Result := False;
  402. end;
  403. function TAttributeDef.ValidateSyntax(const aValue: XMLString; Namespaces: Boolean): Boolean;
  404. begin
  405. case FDataType of
  406. dtId, dtIdRef, dtEntity: Result := IsXmlName(aValue) and
  407. ((not Namespaces) or (Pos(WideChar(':'), aValue) = 0));
  408. dtIdRefs, dtEntities: Result := IsXmlNames(aValue) and
  409. ((not Namespaces) or (Pos(WideChar(':'), aValue) = 0));
  410. dtNmToken: Result := IsXmlNmToken(aValue) and HasEnumToken(aValue);
  411. dtNmTokens: Result := IsXmlNmTokens(aValue);
  412. // IsXmlName() not necessary - enum is never empty and contains valid names
  413. dtNotation: Result := HasEnumToken(aValue);
  414. else
  415. Result := True;
  416. end;
  417. end;
  418. end.