xmlread.pp 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317
  1. {
  2. $Id$
  3. This file is part of the Free Component Library
  4. XML reading routines.
  5. Copyright (c) 1999-2003 by Sebastian Guenther, [email protected]
  6. See the file COPYING.FPC, included in this distribution,
  7. for details about the copyright.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  11. **********************************************************************}
  12. unit XMLRead;
  13. interface
  14. uses SysUtils, Classes, DOM;
  15. type
  16. EXMLReadError = class(Exception);
  17. procedure ReadXMLFile(var ADoc: TXMLDocument; const AFilename: String);
  18. overload;
  19. procedure ReadXMLFile(var ADoc: TXMLDocument; var f: File); overload;
  20. procedure ReadXMLFile(var ADoc: TXMLDocument; var f: TStream); overload;
  21. procedure ReadXMLFile(var ADoc: TXMLDocument; var f: TStream;
  22. const AFilename: String); overload;
  23. procedure ReadXMLFragment(AParentNode: TDOMNode; const AFilename: String);
  24. overload;
  25. procedure ReadXMLFragment(AParentNode: TDOMNode; var f: File); overload;
  26. procedure ReadXMLFragment(AParentNode: TDOMNode; var f: TStream); overload;
  27. procedure ReadXMLFragment(AParentNode: TDOMNode; var f: TStream;
  28. const AFilename: String); overload;
  29. procedure ReadDTDFile(var ADoc: TXMLDocument; const AFilename: String);
  30. overload;
  31. procedure ReadDTDFile(var ADoc: TXMLDocument; var f: File); overload;
  32. procedure ReadDTDFile(var ADoc: TXMLDocument; var f: TStream); overload;
  33. procedure ReadDTDFile(var ADoc: TXMLDocument; var f: TStream;
  34. const AFilename: String); overload;
  35. // =======================================================
  36. implementation
  37. const
  38. Letter = ['A'..'Z', 'a'..'z'];
  39. Digit = ['0'..'9'];
  40. PubidChars: set of Char = [' ', #13, #10, 'a'..'z', 'A'..'Z', '0'..'9',
  41. '-', '''', '(', ')', '+', ',', '.', '/', ':', '=', '?', ';', '!', '*',
  42. '#', '@', '$', '_', '%'];
  43. WhitespaceChars: set of Char = [#9, #10, #13, ' '];
  44. NmToken: set of Char = Letter + Digit + ['.', '-', '_', ':'];
  45. type
  46. TXMLReaderDocument = class(TXMLDocument)
  47. public
  48. procedure SetDocType(ADocType: TDOMDocumentType);
  49. end;
  50. TXMLReaderDocumentType = class(TDOMDocumentType)
  51. public
  52. constructor Create(ADocument: TXMLReaderDocument);
  53. property Name: DOMString read FNodeName write FNodeName;
  54. end;
  55. TSetOfChar = set of Char;
  56. TXMLReader = class
  57. protected
  58. buf, BufStart: PChar;
  59. Filename: String;
  60. procedure RaiseExc(descr: String);
  61. function SkipWhitespace: Boolean;
  62. procedure ExpectWhitespace;
  63. procedure ExpectString(s: String);
  64. function CheckFor(s: PChar): Boolean;
  65. function GetString(ValidChars: TSetOfChar): String;
  66. function GetName(var s: String): Boolean;
  67. function ExpectName: String; // [5]
  68. procedure ExpectAttValue(attr: TDOMAttr); // [10]
  69. function ExpectPubidLiteral: String; // [12]
  70. function ParseComment(AOwner: TDOMNode): Boolean; // [15]
  71. function ParsePI: Boolean; // [16]
  72. procedure ExpectProlog; // [22]
  73. function ParseEq: Boolean; // [25]
  74. procedure ExpectEq;
  75. procedure ParseMisc(AOwner: TDOMNode); // [27]
  76. function ParseMarkupDecl: Boolean; // [29]
  77. function ParseCharData(AOwner: TDOMNode): Boolean; // [14]
  78. function ParseCDSect(AOwner: TDOMNode): Boolean; // [18]
  79. function ParseElement(AOwner: TDOMNode): Boolean; // [39]
  80. procedure ExpectElement(AOwner: TDOMNode);
  81. function ParseReference(AOwner: TDOMNode): Boolean; // [67]
  82. procedure ExpectReference(AOwner: TDOMNode);
  83. function ParsePEReference: Boolean; // [69]
  84. function ParseExternalID: Boolean; // [75]
  85. procedure ExpectExternalID;
  86. function ParseEncodingDecl: String; // [80]
  87. procedure ResolveEntities(RootNode: TDOMNode);
  88. public
  89. doc: TDOMDocument;
  90. procedure ProcessXML(ABuf: PChar; AFilename: String); // [1]
  91. procedure ProcessFragment(AOwner: TDOMNode; ABuf: PChar; AFilename: String);
  92. procedure ProcessDTD(ABuf: PChar; AFilename: String); // ([29])
  93. end;
  94. procedure TXMLReaderDocument.SetDocType(ADocType: TDOMDocumentType);
  95. begin
  96. FDocType := ADocType;
  97. end;
  98. constructor TXMLReaderDocumentType.Create(ADocument: TXMLReaderDocument);
  99. begin
  100. inherited Create(ADocument);
  101. end;
  102. procedure TXMLReader.RaiseExc(descr: String);
  103. var
  104. apos: PChar;
  105. x, y: Integer;
  106. begin
  107. // find out the line in which the error occured
  108. apos := BufStart;
  109. x := 1;
  110. y := 1;
  111. while apos < buf do begin
  112. if apos[0] = #10 then begin
  113. Inc(y);
  114. x := 1;
  115. end else
  116. Inc(x);
  117. Inc(apos);
  118. end;
  119. raise EXMLReadError.Create('In ' + Filename + ' (line ' + IntToStr(y) + ' pos ' +
  120. IntToStr(x) + '): ' + descr);
  121. end;
  122. function TXMLReader.SkipWhitespace: Boolean;
  123. begin
  124. Result := False;
  125. while buf[0] in WhitespaceChars do
  126. begin
  127. Inc(buf);
  128. Result := True;
  129. end;
  130. end;
  131. procedure TXMLReader.ExpectWhitespace;
  132. begin
  133. if not SkipWhitespace then
  134. RaiseExc('Expected whitespace');
  135. end;
  136. procedure TXMLReader.ExpectString(s: String);
  137. var
  138. i: Integer;
  139. s2: PChar;
  140. s3: String;
  141. begin
  142. for i := 1 to Length(s) do
  143. if buf[i - 1] <> s[i] then begin
  144. GetMem(s2, Length(s) + 1);
  145. StrLCopy(s2, buf, Length(s));
  146. s3 := StrPas(s2);
  147. FreeMem(s2);
  148. RaiseExc('Expected "' + s + '", found "' + s3 + '"');
  149. end;
  150. Inc(buf, Length(s));
  151. end;
  152. function TXMLReader.CheckFor(s: PChar): Boolean;
  153. begin
  154. if buf[0] = #0 then begin
  155. Result := False;
  156. exit;
  157. end;
  158. if StrLComp(buf, s, StrLen(s)) = 0 then begin
  159. Inc(buf, StrLen(s));
  160. Result := True;
  161. end else
  162. Result := False;
  163. end;
  164. function TXMLReader.GetString(ValidChars: TSetOfChar): String;
  165. begin
  166. SetLength(Result, 0);
  167. while buf[0] in ValidChars do begin
  168. Result := Result + buf[0];
  169. Inc(buf);
  170. end;
  171. end;
  172. {$IFDEF FPC}
  173. {$IFNDEF VER1_0}
  174. {$DEFINE UsesFPCWidestrings}
  175. {$ENDIF}
  176. {$ENDIF}
  177. {$IFDEF UsesFPCWidestrings}
  178. procedure SimpleWide2AnsiMove(source:pwidechar;dest:pchar;len:longint);
  179. var
  180. i : longint;
  181. begin
  182. for i:=1 to len do
  183. begin
  184. if word(source^)<256 then
  185. dest^:=char(word(source^))
  186. else
  187. dest^:='?';
  188. inc(dest);
  189. inc(source);
  190. end;
  191. end;
  192. procedure SimpleAnsi2WideMove(source:pchar;dest:pwidechar;len:longint);
  193. var
  194. i : longint;
  195. begin
  196. for i:=1 to len do
  197. begin
  198. dest^:=widechar(byte(source^));
  199. inc(dest);
  200. inc(source);
  201. end;
  202. end;
  203. const
  204. WideStringManager: TWideStringManager = (
  205. Wide2AnsiMove: @SimpleWide2AnsiMove;
  206. Ansi2WideMove: @SimpleAnsi2WideMove
  207. );
  208. {$ENDIF}
  209. procedure TXMLReader.ProcessXML(ABuf: PChar; AFilename: String); // [1]
  210. {$IFDEF UsesFPCWidestrings}
  211. var
  212. OldWideStringManager: TWideStringManager;
  213. {$ENDIF}
  214. begin
  215. buf := ABuf;
  216. BufStart := ABuf;
  217. Filename := AFilename;
  218. {$IFDEF UsesFPCWidestrings}
  219. SetWideStringManager(WideStringManager, OldWideStringManager);
  220. try
  221. {$ENDIF}
  222. doc := TXMLReaderDocument.Create;
  223. ExpectProlog;
  224. ExpectElement(doc);
  225. ParseMisc(doc);
  226. {$IFDEF UsesFPCWidestrings}
  227. finally
  228. SetWideStringManager(OldWideStringManager);
  229. end;
  230. {$ENDIF}
  231. if buf[0] <> #0 then
  232. RaiseExc('Text after end of document element found');
  233. end;
  234. procedure TXMLReader.ProcessFragment(AOwner: TDOMNode; ABuf: PChar;
  235. AFilename: String);
  236. {$IFDEF UsesFPCWidestrings}
  237. var
  238. OldWideStringManager: TWideStringManager;
  239. {$ENDIF}
  240. begin
  241. buf := ABuf;
  242. BufStart := ABuf;
  243. Filename := AFilename;
  244. {$IFDEF UsesFPCWidestrings}
  245. SetWideStringManager(WideStringManager, OldWideStringManager);
  246. try
  247. {$ENDIF}
  248. SkipWhitespace;
  249. while ParseCharData(AOwner) or ParseCDSect(AOwner) or ParsePI or
  250. ParseComment(AOwner) or ParseElement(AOwner) or
  251. ParseReference(AOwner) do
  252. SkipWhitespace;
  253. {$IFDEF UsesFPCWidestrings}
  254. finally
  255. SetWideStringManager(OldWideStringManager);
  256. end;
  257. {$ENDIF}
  258. end;
  259. function TXMLReader.GetName(var s: String): Boolean; // [5]
  260. begin
  261. SetLength(s, 0);
  262. if not (buf[0] in (Letter + ['_', ':'])) then begin
  263. Result := False;
  264. exit;
  265. end;
  266. s := buf[0];
  267. Inc(buf);
  268. s := s + GetString(Letter + ['0'..'9', '.', '-', '_', ':']);
  269. Result := True;
  270. end;
  271. function TXMLReader.ExpectName: String; // [5]
  272. begin
  273. if not (buf[0] in (Letter + ['_', ':'])) then
  274. RaiseExc('Expected letter, "_" or ":" for name, found "' + buf[0] + '"');
  275. Result := buf[0];
  276. Inc(buf);
  277. Result := Result + GetString(Letter + ['0'..'9', '.', '-', '_', ':']);
  278. end;
  279. procedure TXMLReader.ExpectAttValue(attr: TDOMAttr); // [10]
  280. var
  281. s: String;
  282. procedure FlushStringBuffer;
  283. begin
  284. if Length(s) > 0 then
  285. begin
  286. attr.AppendChild(doc.CreateTextNode(s));
  287. SetLength(s, 0);
  288. end;
  289. end;
  290. var
  291. StrDel: array[0..1] of Char; // String delimiter
  292. begin
  293. if (buf[0] <> '''') and (buf[0] <> '"') then
  294. RaiseExc('Expected quotation marks');
  295. StrDel[0] := buf[0];
  296. StrDel[1] := #0;
  297. Inc(buf);
  298. SetLength(s, 0);
  299. while not CheckFor(StrDel) do
  300. if buf[0] = '&' then
  301. begin
  302. FlushStringBuffer;
  303. ParseReference(attr);
  304. end else
  305. begin
  306. s := s + buf[0];
  307. Inc(buf);
  308. end;
  309. FlushStringBuffer;
  310. ResolveEntities(Attr);
  311. end;
  312. function TXMLReader.ExpectPubidLiteral: String;
  313. begin
  314. SetLength(Result, 0);
  315. if CheckFor('''') then begin
  316. GetString(PubidChars - ['''']);
  317. ExpectString('''');
  318. end else if CheckFor('"') then begin
  319. GetString(PubidChars - ['"']);
  320. ExpectString('"');
  321. end else
  322. RaiseExc('Expected quotation marks');
  323. end;
  324. function TXMLReader.ParseComment(AOwner: TDOMNode): Boolean; // [15]
  325. var
  326. comment: String;
  327. begin
  328. if CheckFor('<!--') then begin
  329. SetLength(comment, 0);
  330. while (buf[0] <> #0) and (buf[1] <> #0) and
  331. ((buf[0] <> '-') or (buf[1] <> '-')) do begin
  332. comment := comment + buf[0];
  333. Inc(buf);
  334. end;
  335. AOwner.AppendChild(doc.CreateComment(comment));
  336. ExpectString('-->');
  337. Result := True;
  338. end else
  339. Result := False;
  340. end;
  341. function TXMLReader.ParsePI: Boolean; // [16]
  342. var
  343. checkbuf: array[0..3] of char;
  344. begin
  345. if CheckFor('<?') then begin
  346. StrLCopy(checkbuf, buf, 4);
  347. if UpperCase(StrPas(checkbuf)) = 'XML ' then
  348. RaiseExc('"<?xml" processing instruction not allowed here');
  349. ExpectName;
  350. if SkipWhitespace then
  351. while (buf[0] <> #0) and (buf[1] <> #0) and not
  352. ((buf[0] = '?') and (buf[1] = '>')) do Inc(buf);
  353. ExpectString('?>');
  354. Result := True;
  355. end else
  356. Result := False;
  357. end;
  358. procedure TXMLReader.ExpectProlog; // [22]
  359. procedure ParseVersionNum;
  360. begin
  361. if doc.InheritsFrom(TXMLDocument) then
  362. TXMLDocument(doc).XMLVersion :=
  363. GetString(['a'..'z', 'A'..'Z', '0'..'9', '_', '.', ':', '-']);
  364. end;
  365. procedure ParseDoctypeDecls;
  366. begin
  367. repeat
  368. SkipWhitespace;
  369. until not (ParseMarkupDecl or ParsePEReference);
  370. ExpectString(']');
  371. end;
  372. var
  373. DocType: TXMLReaderDocumentType;
  374. begin
  375. if CheckFor('<?xml') then
  376. begin
  377. // '<?xml' VersionInfo EncodingDecl? SDDecl? S? '?>'
  378. // VersionInfo: S 'version' Eq (' VersionNum ' | " VersionNum ")
  379. SkipWhitespace;
  380. ExpectString('version');
  381. ParseEq;
  382. if buf[0] = '''' then
  383. begin
  384. Inc(buf);
  385. ParseVersionNum;
  386. ExpectString('''');
  387. end else if buf[0] = '"' then
  388. begin
  389. Inc(buf);
  390. ParseVersionNum;
  391. ExpectString('"');
  392. end else
  393. RaiseExc('Expected single or double quotation mark');
  394. // EncodingDecl?
  395. ParseEncodingDecl;
  396. // SDDecl?
  397. SkipWhitespace;
  398. if CheckFor('standalone') then
  399. begin
  400. ExpectEq;
  401. if buf[0] = '''' then
  402. begin
  403. Inc(buf);
  404. if not (CheckFor('yes''') or CheckFor('no''')) then
  405. RaiseExc('Expected ''yes'' or ''no''');
  406. end else if buf[0] = '''' then
  407. begin
  408. Inc(buf);
  409. if not (CheckFor('yes"') or CheckFor('no"')) then
  410. RaiseExc('Expected "yes" or "no"');
  411. end;
  412. SkipWhitespace;
  413. end;
  414. ExpectString('?>');
  415. end;
  416. // Check for "Misc*"
  417. ParseMisc(doc);
  418. // Check for "(doctypedecl Misc*)?" [28]
  419. if CheckFor('<!DOCTYPE') then
  420. begin
  421. DocType := TXMLReaderDocumentType.Create(doc as TXMLReaderDocument);
  422. if doc.InheritsFrom(TXMLReaderDocument) then
  423. TXMLReaderDocument(doc).SetDocType(DocType);
  424. SkipWhitespace;
  425. DocType.Name := ExpectName;
  426. SkipWhitespace;
  427. if CheckFor('[') then
  428. begin
  429. ParseDoctypeDecls;
  430. SkipWhitespace;
  431. ExpectString('>');
  432. end else if not CheckFor('>') then
  433. begin
  434. ParseExternalID;
  435. SkipWhitespace;
  436. if CheckFor('[') then
  437. begin
  438. ParseDoctypeDecls;
  439. SkipWhitespace;
  440. end;
  441. ExpectString('>');
  442. end;
  443. ParseMisc(doc);
  444. end;
  445. end;
  446. function TXMLReader.ParseEq: Boolean; // [25]
  447. var
  448. savedbuf: PChar;
  449. begin
  450. savedbuf := buf;
  451. SkipWhitespace;
  452. if buf[0] = '=' then begin
  453. Inc(buf);
  454. SkipWhitespace;
  455. Result := True;
  456. end else begin
  457. buf := savedbuf;
  458. Result := False;
  459. end;
  460. end;
  461. procedure TXMLReader.ExpectEq;
  462. begin
  463. if not ParseEq then
  464. RaiseExc('Expected "="');
  465. end;
  466. // Parse "Misc*":
  467. // Misc ::= Comment | PI | S
  468. procedure TXMLReader.ParseMisc(AOwner: TDOMNode); // [27]
  469. begin
  470. repeat
  471. SkipWhitespace;
  472. until not (ParseComment(AOwner) or ParsePI);
  473. end;
  474. function TXMLReader.ParseMarkupDecl: Boolean; // [29]
  475. function ParseElementDecl: Boolean; // [45]
  476. procedure ExpectChoiceOrSeq; // [49], [50]
  477. procedure ExpectCP; // [48]
  478. begin
  479. if CheckFor('(') then
  480. ExpectChoiceOrSeq
  481. else
  482. ExpectName;
  483. if CheckFor('?') then
  484. else if CheckFor('*') then
  485. else if CheckFor('+') then;
  486. end;
  487. var
  488. delimiter: Char;
  489. begin
  490. SkipWhitespace;
  491. ExpectCP;
  492. SkipWhitespace;
  493. delimiter := #0;
  494. while not CheckFor(')') do begin
  495. if delimiter = #0 then begin
  496. if (buf[0] = '|') or (buf[0] = ',') then
  497. delimiter := buf[0]
  498. else
  499. RaiseExc('Expected "|" or ","');
  500. Inc(buf);
  501. end else
  502. ExpectString(delimiter);
  503. SkipWhitespace;
  504. ExpectCP;
  505. end;
  506. end;
  507. begin
  508. if CheckFor('<!ELEMENT') then begin
  509. ExpectWhitespace;
  510. ExpectName;
  511. ExpectWhitespace;
  512. // Get contentspec [46]
  513. if CheckFor('EMPTY') then
  514. else if CheckFor('ANY') then
  515. else if CheckFor('(') then begin
  516. SkipWhitespace;
  517. if CheckFor('#PCDATA') then begin
  518. // Parse Mixed section [51]
  519. SkipWhitespace;
  520. if not CheckFor(')') then
  521. repeat
  522. ExpectString('|');
  523. SkipWhitespace;
  524. ExpectName;
  525. until CheckFor(')*');
  526. end else begin
  527. // Parse Children section [47]
  528. ExpectChoiceOrSeq;
  529. if CheckFor('?') then
  530. else if CheckFor('*') then
  531. else if CheckFor('+') then;
  532. end;
  533. end else
  534. RaiseExc('Invalid content specification');
  535. SkipWhitespace;
  536. ExpectString('>');
  537. Result := True;
  538. end else
  539. Result := False;
  540. end;
  541. function ParseAttlistDecl: Boolean; // [52]
  542. var
  543. attr: TDOMAttr;
  544. begin
  545. if CheckFor('<!ATTLIST') then begin
  546. ExpectWhitespace;
  547. ExpectName;
  548. SkipWhitespace;
  549. while not CheckFor('>') do begin
  550. ExpectName;
  551. ExpectWhitespace;
  552. // Get AttType [54], [55], [56]
  553. if CheckFor('CDATA') then
  554. else if CheckFor('ID') then
  555. else if CheckFor('IDREF') then
  556. else if CheckFor('IDREFS') then
  557. else if CheckFor('ENTITTY') then
  558. else if CheckFor('ENTITIES') then
  559. else if CheckFor('NMTOKEN') then
  560. else if CheckFor('NMTOKENS') then
  561. else if CheckFor('NOTATION') then begin // [57], [58]
  562. ExpectWhitespace;
  563. ExpectString('(');
  564. SkipWhitespace;
  565. ExpectName;
  566. SkipWhitespace;
  567. while not CheckFor(')') do begin
  568. ExpectString('|');
  569. SkipWhitespace;
  570. ExpectName;
  571. SkipWhitespace;
  572. end;
  573. end else if CheckFor('(') then begin // [59]
  574. SkipWhitespace;
  575. GetString(Nmtoken);
  576. SkipWhitespace;
  577. while not CheckFor(')') do begin
  578. ExpectString('|');
  579. SkipWhitespace;
  580. GetString(Nmtoken);
  581. SkipWhitespace;
  582. end;
  583. end else
  584. RaiseExc('Invalid tokenized type');
  585. ExpectWhitespace;
  586. // Get DefaultDecl [60]
  587. if CheckFor('#REQUIRED') then
  588. else if CheckFor('#IMPLIED') then
  589. else begin
  590. if CheckFor('#FIXED') then
  591. SkipWhitespace;
  592. attr := doc.CreateAttribute('');
  593. ExpectAttValue(attr);
  594. end;
  595. SkipWhitespace;
  596. end;
  597. Result := True;
  598. end else
  599. Result := False;
  600. end;
  601. function ParseEntityDecl: Boolean; // [70]
  602. var
  603. NewEntity: TDOMEntity;
  604. function ParseEntityValue: Boolean; // [9]
  605. var
  606. strdel: array[0..1] of Char;
  607. begin
  608. if (buf[0] <> '''') and (buf[0] <> '"') then begin
  609. Result := False;
  610. exit;
  611. end;
  612. strdel[0] := buf[0];
  613. strdel[1] := #0;
  614. Inc(buf);
  615. while not CheckFor(strdel) do
  616. if ParsePEReference then
  617. else if ParseReference(NewEntity) then
  618. else begin
  619. Inc(buf); // Normal haracter
  620. end;
  621. Result := True;
  622. end;
  623. begin
  624. if CheckFor('<!ENTITY') then begin
  625. ExpectWhitespace;
  626. if CheckFor('%') then begin // [72]
  627. ExpectWhitespace;
  628. NewEntity := doc.CreateEntity(ExpectName);
  629. ExpectWhitespace;
  630. // Get PEDef [74]
  631. if ParseEntityValue then
  632. else if ParseExternalID then
  633. else
  634. RaiseExc('Expected entity value or external ID');
  635. end else begin // [71]
  636. NewEntity := doc.CreateEntity(ExpectName);
  637. ExpectWhitespace;
  638. // Get EntityDef [73]
  639. if ParseEntityValue then
  640. else begin
  641. ExpectExternalID;
  642. // Get NDataDecl [76]
  643. ExpectWhitespace;
  644. ExpectString('NDATA');
  645. ExpectWhitespace;
  646. ExpectName;
  647. end;
  648. end;
  649. SkipWhitespace;
  650. ExpectString('>');
  651. Result := True;
  652. end else
  653. Result := False;
  654. end;
  655. function ParseNotationDecl: Boolean; // [82]
  656. begin
  657. if CheckFor('<!NOTATION') then begin
  658. ExpectWhitespace;
  659. ExpectName;
  660. ExpectWhitespace;
  661. if ParseExternalID then
  662. else if CheckFor('PUBLIC') then begin // [83]
  663. ExpectWhitespace;
  664. ExpectPubidLiteral;
  665. end else
  666. RaiseExc('Expected external or public ID');
  667. SkipWhitespace;
  668. ExpectString('>');
  669. Result := True;
  670. end else
  671. Result := False;
  672. end;
  673. begin
  674. Result := False;
  675. while ParseElementDecl or ParseAttlistDecl or ParseEntityDecl or
  676. ParseNotationDecl or ParsePI or ParseComment(doc) or SkipWhitespace do
  677. Result := True;
  678. end;
  679. procedure TXMLReader.ProcessDTD(ABuf: PChar; AFilename: String);
  680. begin
  681. buf := ABuf;
  682. BufStart := ABuf;
  683. Filename := AFilename;
  684. doc := TXMLReaderDocument.Create;
  685. ParseMarkupDecl;
  686. {
  687. if buf[0] <> #0 then begin
  688. WriteLn('=== Unparsed: ===');
  689. //WriteLn(buf);
  690. WriteLn(StrLen(buf), ' chars');
  691. end;
  692. }
  693. end;
  694. function TXMLReader.ParseCharData(AOwner: TDOMNode): Boolean; // [14]
  695. var
  696. s: String;
  697. i: Integer;
  698. begin
  699. SetLength(s, 0);
  700. while not (buf[0] in [#0, '<', '&']) do
  701. begin
  702. s := s + buf[0];
  703. Inc(buf);
  704. end;
  705. if Length(s) > 0 then
  706. begin
  707. // Check if s has non-whitespace content
  708. i := Length(s);
  709. while (i > 0) and (s[i] in WhitespaceChars) do
  710. Dec(i);
  711. if i > 0 then
  712. AOwner.AppendChild(doc.CreateTextNode(s));
  713. Result := True;
  714. end else
  715. Result := False;
  716. end;
  717. function TXMLReader.ParseCDSect(AOwner: TDOMNode): Boolean; // [18]
  718. var
  719. cdata: String;
  720. begin
  721. if CheckFor('<![CDATA[') then
  722. begin
  723. SetLength(cdata, 0);
  724. while not CheckFor(']]>') do
  725. begin
  726. cdata := cdata + buf[0];
  727. Inc(buf);
  728. end;
  729. AOwner.AppendChild(doc.CreateCDATASection(cdata));
  730. Result := True;
  731. end else
  732. Result := False;
  733. end;
  734. function TXMLReader.ParseElement(AOwner: TDOMNode): Boolean; // [39] [40] [44]
  735. var
  736. NewElem: TDOMElement;
  737. var
  738. IsEmpty: Boolean;
  739. name: String;
  740. oldpos: PChar;
  741. attr: TDOMAttr;
  742. begin
  743. oldpos := buf;
  744. if CheckFor('<') then
  745. begin
  746. if not GetName(name) then
  747. begin
  748. buf := oldpos;
  749. Result := False;
  750. exit;
  751. end;
  752. NewElem := doc.CreateElement(name);
  753. AOwner.AppendChild(NewElem);
  754. SkipWhitespace;
  755. IsEmpty := False;
  756. while True do
  757. begin
  758. if CheckFor('/>') then
  759. begin
  760. IsEmpty := True;
  761. break;
  762. end;
  763. if CheckFor('>') then
  764. break;
  765. // Get Attribute [41]
  766. attr := doc.CreateAttribute(ExpectName);
  767. NewElem.Attributes.SetNamedItem(attr);
  768. ExpectEq;
  769. ExpectAttValue(attr);
  770. SkipWhitespace;
  771. end;
  772. if not IsEmpty then
  773. begin
  774. // Get content
  775. SkipWhitespace;
  776. while ParseCharData(NewElem) or ParseCDSect(NewElem) or ParsePI or
  777. ParseComment(NewElem) or ParseElement(NewElem) or
  778. ParseReference(NewElem) do;
  779. // Get ETag [42]
  780. ExpectString('</');
  781. if ExpectName <> name then
  782. RaiseExc('Unmatching element end tag (expected "</' + name + '>")');
  783. SkipWhitespace;
  784. ExpectString('>');
  785. end;
  786. ResolveEntities(NewElem);
  787. Result := True;
  788. end else
  789. Result := False;
  790. end;
  791. procedure TXMLReader.ExpectElement(AOwner: TDOMNode);
  792. begin
  793. if not ParseElement(AOwner) then
  794. RaiseExc('Expected element');
  795. end;
  796. function TXMLReader.ParsePEReference: Boolean; // [69]
  797. begin
  798. if CheckFor('%') then begin
  799. ExpectName;
  800. ExpectString(';');
  801. Result := True;
  802. end else
  803. Result := False;
  804. end;
  805. function TXMLReader.ParseReference(AOwner: TDOMNode): Boolean; // [67] [68]
  806. begin
  807. if not CheckFor('&') then begin
  808. Result := False;
  809. exit;
  810. end;
  811. if CheckFor('#') then begin // Test for CharRef [66]
  812. if CheckFor('x') then begin
  813. // !!!: there must be at least one digit
  814. while buf[0] in ['0'..'9', 'a'..'f', 'A'..'F'] do Inc(buf);
  815. end else
  816. // !!!: there must be at least one digit
  817. while buf[0] in ['0'..'9'] do Inc(buf);
  818. end else
  819. AOwner.AppendChild(doc.CreateEntityReference(ExpectName));
  820. ExpectString(';');
  821. Result := True;
  822. end;
  823. procedure TXMLReader.ExpectReference(AOwner: TDOMNode);
  824. begin
  825. if not ParseReference(AOwner) then
  826. RaiseExc('Expected reference ("&Name;" or "%Name;")');
  827. end;
  828. function TXMLReader.ParseExternalID: Boolean; // [75]
  829. function GetSystemLiteral: String;
  830. begin
  831. SetLength(Result, 0);
  832. if buf[0] = '''' then begin
  833. Inc(buf);
  834. while (buf[0] <> '''') and (buf[0] <> #0) do begin
  835. Result := Result + buf[0];
  836. Inc(buf);
  837. end;
  838. ExpectString('''');
  839. end else if buf[0] = '"' then begin
  840. Inc(buf);
  841. while (buf[0] <> '"') and (buf[0] <> #0) do begin
  842. Result := Result + buf[0];
  843. Inc(buf);
  844. end;
  845. ExpectString('"');
  846. end;
  847. end;
  848. begin
  849. if CheckFor('SYSTEM') then begin
  850. ExpectWhitespace;
  851. GetSystemLiteral;
  852. Result := True;
  853. end else if CheckFor('PUBLIC') then begin
  854. ExpectWhitespace;
  855. ExpectPubidLiteral;
  856. ExpectWhitespace;
  857. GetSystemLiteral;
  858. Result := True;
  859. end else
  860. Result := False;
  861. end;
  862. procedure TXMLReader.ExpectExternalID;
  863. begin
  864. if not ParseExternalID then
  865. RaiseExc('Expected external ID');
  866. end;
  867. function TXMLReader.ParseEncodingDecl: String; // [80]
  868. function ParseEncName: String;
  869. begin
  870. if not (buf[0] in ['A'..'Z', 'a'..'z']) then
  871. RaiseExc('Expected character (A-Z, a-z)');
  872. Result := buf[0];
  873. Inc(buf);
  874. Result := Result + GetString(['A'..'Z', 'a'..'z', '0'..'9', '.', '_', '-']);
  875. end;
  876. begin
  877. SetLength(Result, 0);
  878. SkipWhitespace;
  879. if CheckFor('encoding') then begin
  880. ExpectEq;
  881. if buf[0] = '''' then begin
  882. Inc(buf);
  883. Result := ParseEncName;
  884. ExpectString('''');
  885. end else if buf[0] = '"' then begin
  886. Inc(buf);
  887. Result := ParseEncName;
  888. ExpectString('"');
  889. end;
  890. end;
  891. end;
  892. { Currently, this method will only resolve the entities which are
  893. predefined in XML: }
  894. procedure TXMLReader.ResolveEntities(RootNode: TDOMNode);
  895. procedure ReplaceEntityRef(EntityNode: TDOMNode; const Replacement: String);
  896. var
  897. PrevSibling, NextSibling: TDOMNode;
  898. begin
  899. PrevSibling := EntityNode.PreviousSibling;
  900. NextSibling := EntityNode.NextSibling;
  901. if Assigned(PrevSibling) and (PrevSibling.NodeType = TEXT_NODE) then
  902. begin
  903. TDOMCharacterData(PrevSibling).AppendData(Replacement);
  904. RootNode.RemoveChild(EntityNode);
  905. if Assigned(NextSibling) and (NextSibling.NodeType = TEXT_NODE) then
  906. begin
  907. TDOMCharacterData(PrevSibling).AppendData(
  908. TDOMCharacterData(NextSibling).Data);
  909. RootNode.RemoveChild(NextSibling);
  910. end
  911. end else
  912. if Assigned(NextSibling) and (NextSibling.NodeType = TEXT_NODE) then
  913. begin
  914. TDOMCharacterData(NextSibling).InsertData(0, Replacement);
  915. RootNode.RemoveChild(EntityNode);
  916. end else
  917. RootNode.ReplaceChild(Doc.CreateTextNode(Replacement), EntityNode);
  918. end;
  919. var
  920. Node, NextSibling: TDOMNode;
  921. begin
  922. Node := RootNode.FirstChild;
  923. while Assigned(Node) do
  924. begin
  925. NextSibling := Node.NextSibling;
  926. if Node.NodeType = ENTITY_REFERENCE_NODE then
  927. if Node.NodeName = 'amp' then
  928. ReplaceEntityRef(Node, '&')
  929. else if Node.NodeName = 'apos' then
  930. ReplaceEntityRef(Node, '''')
  931. else if Node.NodeName = 'gt' then
  932. ReplaceEntityRef(Node, '>')
  933. else if Node.NodeName = 'lt' then
  934. ReplaceEntityRef(Node, '<')
  935. else if Node.NodeName = 'quot' then
  936. ReplaceEntityRef(Node, '"');
  937. Node := NextSibling;
  938. end;
  939. end;
  940. procedure ReadXMLFile(var ADoc: TXMLDocument; var f: File);
  941. var
  942. Reader: TXMLReader;
  943. buf: PChar;
  944. BufSize: LongInt;
  945. begin
  946. ADoc := nil;
  947. BufSize := FileSize(f) + 1;
  948. if BufSize <= 1 then
  949. exit;
  950. GetMem(buf, BufSize);
  951. try
  952. BlockRead(f, buf^, BufSize - 1);
  953. buf[BufSize - 1] := #0;
  954. Reader := TXMLReader.Create;
  955. try
  956. Reader.ProcessXML(buf, TFileRec(f).name);
  957. ADoc := TXMLDocument(Reader.doc);
  958. finally
  959. Reader.Free;
  960. end;
  961. finally
  962. FreeMem(buf);
  963. end;
  964. end;
  965. procedure ReadXMLFile(var ADoc: TXMLDocument; var f: TStream;
  966. const AFilename: String);
  967. var
  968. Reader: TXMLReader;
  969. buf: PChar;
  970. begin
  971. ADoc := nil;
  972. if f.Size = 0 then
  973. exit;
  974. GetMem(buf, f.Size + 1);
  975. try
  976. f.Read(buf^, f.Size);
  977. buf[f.Size] := #0;
  978. Reader := TXMLReader.Create;
  979. try
  980. Reader.ProcessXML(buf, AFilename);
  981. ADoc := TXMLDocument(Reader.doc);
  982. finally
  983. Reader.Free;
  984. end;
  985. finally
  986. FreeMem(buf);
  987. end;
  988. end;
  989. procedure ReadXMLFile(var ADoc: TXMLDocument; var f: TStream);
  990. begin
  991. ReadXMLFile(ADoc, f, '<Stream>');
  992. end;
  993. procedure ReadXMLFile(var ADoc: TXMLDocument; const AFilename: String);
  994. var
  995. Stream: TStream;
  996. begin
  997. ADoc := nil;
  998. Stream := TFileStream.Create(AFilename, fmOpenRead);
  999. try
  1000. ReadXMLFile(ADoc, Stream, AFilename);
  1001. finally
  1002. Stream.Free;
  1003. end;
  1004. end;
  1005. procedure ReadXMLFragment(AParentNode: TDOMNode; var f: File);
  1006. var
  1007. Reader: TXMLReader;
  1008. buf: PChar;
  1009. BufSize: LongInt;
  1010. begin
  1011. BufSize := FileSize(f) + 1;
  1012. if BufSize <= 1 then
  1013. exit;
  1014. GetMem(buf, BufSize);
  1015. try
  1016. BlockRead(f, buf^, BufSize - 1);
  1017. buf[BufSize - 1] := #0;
  1018. Reader := TXMLReader.Create;
  1019. try
  1020. Reader.Doc := AParentNode.OwnerDocument;
  1021. Reader.ProcessFragment(AParentNode, buf, TFileRec(f).name);
  1022. finally
  1023. Reader.Free;
  1024. end;
  1025. finally
  1026. FreeMem(buf);
  1027. end;
  1028. end;
  1029. procedure ReadXMLFragment(AParentNode: TDOMNode; var f: TStream;
  1030. const AFilename: String);
  1031. var
  1032. Reader: TXMLReader;
  1033. buf: PChar;
  1034. begin
  1035. if f.Size = 0 then
  1036. exit;
  1037. GetMem(buf, f.Size + 1);
  1038. try
  1039. f.Read(buf^, f.Size);
  1040. buf[f.Size] := #0;
  1041. Reader := TXMLReader.Create;
  1042. Reader.Doc := AParentNode.OwnerDocument;
  1043. try
  1044. Reader.ProcessFragment(AParentNode, buf, AFilename);
  1045. finally
  1046. Reader.Free;
  1047. end;
  1048. finally
  1049. FreeMem(buf);
  1050. end;
  1051. end;
  1052. procedure ReadXMLFragment(AParentNode: TDOMNode; var f: TStream);
  1053. begin
  1054. ReadXMLFragment(AParentNode, f, '<Stream>');
  1055. end;
  1056. procedure ReadXMLFragment(AParentNode: TDOMNode; const AFilename: String);
  1057. var
  1058. Stream: TStream;
  1059. begin
  1060. Stream := TFileStream.Create(AFilename, fmOpenRead);
  1061. try
  1062. ReadXMLFragment(AParentNode, Stream, AFilename);
  1063. finally
  1064. Stream.Free;
  1065. end;
  1066. end;
  1067. procedure ReadDTDFile(var ADoc: TXMLDocument; var f: File);
  1068. var
  1069. Reader: TXMLReader;
  1070. buf: PChar;
  1071. BufSize: LongInt;
  1072. begin
  1073. ADoc := nil;
  1074. BufSize := FileSize(f) + 1;
  1075. if BufSize <= 1 then
  1076. exit;
  1077. GetMem(buf, BufSize);
  1078. try
  1079. BlockRead(f, buf^, BufSize - 1);
  1080. buf[BufSize - 1] := #0;
  1081. Reader := TXMLReader.Create;
  1082. try
  1083. Reader.ProcessDTD(buf, TFileRec(f).name);
  1084. ADoc := TXMLDocument(Reader.doc);
  1085. finally
  1086. Reader.Free;
  1087. end;
  1088. finally
  1089. FreeMem(buf);
  1090. end;
  1091. end;
  1092. procedure ReadDTDFile(var ADoc: TXMLDocument; var f: TStream;
  1093. const AFilename: String);
  1094. var
  1095. Reader: TXMLReader;
  1096. buf: PChar;
  1097. begin
  1098. ADoc := nil;
  1099. if f.Size = 0 then
  1100. exit;
  1101. GetMem(buf, f.Size + 1);
  1102. try
  1103. f.Read(buf^, f.Size);
  1104. buf[f.Size] := #0;
  1105. Reader := TXMLReader.Create;
  1106. try
  1107. Reader.ProcessDTD(buf, AFilename);
  1108. ADoc := TXMLDocument(Reader.doc);
  1109. finally
  1110. Reader.Free;
  1111. end;
  1112. finally
  1113. FreeMem(buf);
  1114. end;
  1115. end;
  1116. procedure ReadDTDFile(var ADoc: TXMLDocument; var f: TStream);
  1117. begin
  1118. ReadDTDFile(ADoc, f, '<Stream>');
  1119. end;
  1120. procedure ReadDTDFile(var ADoc: TXMLDocument; const AFilename: String);
  1121. var
  1122. Stream: TStream;
  1123. begin
  1124. ADoc := nil;
  1125. Stream := TFileStream.Create(AFilename, fmOpenRead);
  1126. try
  1127. ReadDTDFile(ADoc, Stream, AFilename);
  1128. finally
  1129. Stream.Free;
  1130. end;
  1131. end;
  1132. end.
  1133. {
  1134. $Log$
  1135. Revision 1.10 2003-12-01 23:59:12 sg
  1136. * Added support for main branch to be able to read and write at least
  1137. ISO8859-1 encoded files correctly. A much improved solution will be
  1138. provided when the mainbranch RTL fully supports Unicode/WideStrings.
  1139. Revision 1.9 2003/11/04 20:00:46 michael
  1140. + Fixed processing instruction parsing. <?xml is not allowed but <?xml-XXX is
  1141. Revision 1.8 2003/01/15 21:59:55 sg
  1142. * the units DOM, XMLRead and XMLWrite now compile with Delphi without
  1143. modifications as well
  1144. Revision 1.7 2002/09/21 19:22:38 sg
  1145. * Added procedures to process XML fragments only (e.g. for merging them
  1146. into an existing DOM document)
  1147. Revision 1.6 2002/09/07 15:15:29 peter
  1148. * old logs removed and tabs fixed
  1149. }