2
0

cldrxml.pas 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686
  1. { Parser of the CLDR collation xml files.
  2. Copyright (c) 2013 by Inoussa OUEDRAOGO
  3. The source code is distributed under the Library GNU
  4. General Public License with the following modification:
  5. - object files and libraries linked into an application may be
  6. distributed without source code.
  7. If you didn't receive a copy of the file COPYING, contact:
  8. Free Software Foundation
  9. 675 Mass Ave
  10. Cambridge, MA 02139
  11. USA
  12. This program is distributed in the hope that it will be useful,
  13. but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  15. }
  16. unit cldrxml;
  17. {$mode objfpc}{$H+}
  18. {$TypedAddress on}
  19. interface
  20. uses
  21. Classes, SysUtils, DOM,
  22. cldrhelper;
  23. procedure ParseInitialDocument(ASequence : POrderedCharacters; ADoc : TDOMDocument);overload;
  24. procedure ParseInitialDocument(ASequence : POrderedCharacters; AFileName : string);overload;
  25. procedure ParseCollationDocument(
  26. ADoc : TDOMDocument;
  27. ACollation : TCldrCollation;
  28. AMode : TCldrParserMode
  29. );overload;
  30. procedure ParseCollationDocument(
  31. const AFileName : string;
  32. ACollation : TCldrCollation;
  33. AMode : TCldrParserMode
  34. );overload;
  35. procedure ParseCollationDocument(
  36. const AFileName : string;
  37. ACollation : TCldrCollationItem;
  38. AType : string
  39. );overload;
  40. procedure ParseCollationDocument(
  41. ADoc : TDOMDocument;
  42. ACollation : TCldrCollationItem;
  43. AType : string
  44. );overload;
  45. resourcestring
  46. sCaseNothandled = 'This case is not handled : "%s", Position = %d.';
  47. sCodePointExpected = 'Code Point node expected as child at this position "%d".';
  48. sCollationsNodeNotFound = '"collations" node not found.';
  49. sCollationTypeNotFound = 'collation "Type" not found : "%s".';
  50. sHexAttributeExpected = '"hex" attribute expected at this position "%d".';
  51. sInvalidResetClause = 'Invalid "Reset" clause.';
  52. sNodeNameAssertMessage = 'Expected NodeName "%s", got "%s".';
  53. sRulesNodeNotFound = '"rules" node not found.';
  54. sTextNodeChildExpected = '(Child) text node expected at this position "%d", but got "%s".';
  55. sUniqueChildNodeExpected = 'Unique child node expected at this position "%d".';
  56. sUnknownResetLogicalPosition = 'Unknown reset logical position : "%s".';
  57. implementation
  58. uses
  59. typinfo, XMLRead, XPath, Helper, unicodeset;
  60. const
  61. s_AT = 'at';
  62. //s_BEFORE = 'before';
  63. s_CODEPOINT = 'codepoint';
  64. s_COLLATION = 'collation';
  65. s_COLLATIONS = 'collations';
  66. s_CONTEXT = 'context';
  67. //s_DEFAULT = 'default';
  68. s_EXTEND = 'extend';
  69. s_HEX = 'hex';
  70. s_POSITION = 'position';
  71. s_RESET = 'reset';
  72. s_RULES = 'rules';
  73. //s_STANDART = 'standard';
  74. s_TYPE = 'type';
  75. procedure CheckNodeName(ANode : TDOMNode; const AExpectedName : DOMString);
  76. begin
  77. if (ANode.NodeName <> AExpectedName) then
  78. raise Exception.CreateFmt(sNodeNameAssertMessage,[AExpectedName,ANode.NodeName]);
  79. end;
  80. function CharToReorderWeigthKind(const AChar : Char) : TReorderWeigthKind;inline;
  81. begin
  82. case AChar of
  83. 'p' : Result := TReorderWeigthKind.PriMary;
  84. 's' : Result := TReorderWeigthKind.Secondary;
  85. 't' : Result := TReorderWeigthKind.Tertiary;
  86. 'i' : Result := TReorderWeigthKind.Identity;
  87. else
  88. Result := TReorderWeigthKind.Identity;
  89. end;
  90. end;
  91. function DomString2UnicodeCodePointArray(const AValue : DOMString): TUnicodeCodePointArray;
  92. var
  93. u4str : UCS4String;
  94. k : Integer;
  95. begin
  96. if (Length(AValue) = 0) then
  97. exit(nil);
  98. if (Length(AValue) = 1) then begin
  99. SetLength(Result,1);
  100. Result[0] := Ord(AValue[1])
  101. end else begin
  102. u4str := WideStringToUCS4String(AValue);
  103. k := Length(u4str) - 1; // remove the last #0
  104. SetLength(Result,k);
  105. for k := 0 to k - 1 do
  106. Result[k] := u4str[k];
  107. end;
  108. end;
  109. function TryStrToLogicalReorder(
  110. const AValue : string;
  111. out AResult : TReorderLogicalReset
  112. ) : Boolean;
  113. var
  114. s : string;
  115. i : Integer;
  116. begin
  117. s := StringReplace(AValue,' ','',[rfReplaceAll]);
  118. s := StringReplace(s,'_','',[rfReplaceAll]);
  119. i := GetEnumValue(TypeInfo(TReorderLogicalReset),s);
  120. Result := (i > -1);
  121. if Result then
  122. AResult := TReorderLogicalReset(i);
  123. end;
  124. function ParseStatement(
  125. ARules : TDOMElement;
  126. AStartPosition : Integer;
  127. AStatement : PReorderSequence;
  128. var ANextPos : Integer
  129. ) : Boolean;
  130. var
  131. startPosition : Integer;
  132. statement : PReorderSequence;
  133. elementActualCount : Integer;
  134. list : TDOMNodeList;
  135. inBlock : Boolean;
  136. procedure SkipComments();
  137. begin
  138. while (startPosition < list.Count) do begin
  139. if (list[startPosition].NodeType <> COMMENT_NODE) then
  140. Break;
  141. Inc(startPosition);
  142. end;
  143. end;
  144. function parse_reset() : Integer;
  145. var
  146. n, t : TDOMNode;
  147. s : string;
  148. logicalPos : TReorderLogicalReset;
  149. begin
  150. SkipComments();
  151. n := list[startPosition];
  152. CheckNodeName(n,s_RESET);
  153. if n.HasChildNodes() then begin
  154. n := n.FirstChild;
  155. if (n.NodeType = TEXT_NODE) then begin
  156. statement^.Reset := DomString2UnicodeCodePointArray(Trim(TDOMText(n).Data));
  157. Result := startPosition+1;
  158. end else begin
  159. if not TryStrToLogicalReorder(n.NodeName,logicalPos) then
  160. raise Exception.CreateFmt(sUnknownResetLogicalPosition,[n.NodeName]);
  161. statement^.LogicalPosition := logicalPos;
  162. Result := startPosition+1;
  163. end;
  164. end else if not n.HasChildNodes() then begin
  165. if (list[startPosition+1].NodeName = s_POSITION) then begin
  166. s := list[startPosition+1].Attributes.GetNamedItem(s_AT).NodeValue;
  167. if not TryStrToLogicalReorder(s,logicalPos) then
  168. raise Exception.CreateFmt(sUnknownResetLogicalPosition,[s]);
  169. statement^.LogicalPosition := logicalPos;
  170. Result := startPosition+2;
  171. end else begin
  172. t := list[startPosition+1];
  173. {if (t.NodeType <> TEXT_NODE) then
  174. raise Exception.CreateFmt(sTextNodeChildExpected,[(startPosition+1),(t.NodeName+'('+t.ClassName+')')]);}
  175. if (t.NodeType = TEXT_NODE) then
  176. statement^.Reset := DomString2UnicodeCodePointArray(Trim(TDOMText(t).Data))
  177. else
  178. statement^.Reset := DomString2UnicodeCodePointArray(' ');
  179. Result := startPosition+2;
  180. end;
  181. end;
  182. if (statement^.LogicalPosition = TReorderLogicalReset.None) and
  183. (Length(statement^.Reset) = 0)
  184. then
  185. raise Exception.Create(sInvalidResetClause);
  186. end;
  187. procedure EnsureElementLength(const ALength : Integer);
  188. var
  189. k, d : Integer;
  190. begin
  191. k := Length(statement^.Elements);
  192. if (k < ALength) then begin
  193. k := ALength;
  194. if (k = 0) then begin
  195. k := 50;
  196. end else begin
  197. if (k < 10) then
  198. d := 10
  199. else
  200. d := 2;
  201. k := k * d;
  202. end;
  203. SetLength(statement^.Elements,k);
  204. end;
  205. end;
  206. procedure AddElement(
  207. const AChars : array of UCS4Char;
  208. const AWeigthKind : TReorderWeigthKind;
  209. const AContext : DOMString
  210. );overload;
  211. var
  212. kp : PReorderUnit;
  213. k : Integer;
  214. begin
  215. EnsureElementLength(elementActualCount+1);
  216. kp := @statement^.Elements[elementActualCount];
  217. SetLength(kp^.Characters,Length(AChars));
  218. for k := 0 to Length(AChars) - 1 do
  219. kp^.Characters[k] := AChars[k];
  220. kp^.WeigthKind := AWeigthKind;
  221. elementActualCount := elementActualCount + 1;
  222. if (AContext <> '') then
  223. kp^.Context := DomString2UnicodeCodePointArray(AContext);
  224. end;
  225. procedure ReadChars(
  226. ANode : TDOMNode;
  227. APos : Integer;
  228. var AChars : UCS4String
  229. );
  230. var
  231. t : TDOMNode;
  232. u4str : UCS4String;
  233. s : DOMString;
  234. begin
  235. if not ANode.HasChildNodes() then begin
  236. SetLength(AChars,1);
  237. AChars[0] := Ord(UnicodeChar(' '));
  238. exit;
  239. //raise Exception.CreateFmt(sCodePointExpected + ANode.ClassName,[APos]);
  240. end;
  241. t := ANode.FindNode(s_CODEPOINT);
  242. if (t = nil) then begin
  243. if (ANode.ChildNodes.Count <> 1) then
  244. raise Exception.CreateFmt(sUniqueChildNodeExpected,[APos]);
  245. t := ANode.ChildNodes[0];
  246. if not t.InheritsFrom(TDOMText) then
  247. raise Exception.CreateFmt(sTextNodeChildExpected,[APos,(t.NodeName+'('+t.ClassName+')')]);
  248. s := TDOMText(t).Data;
  249. if (Length(s) = 1) then begin
  250. SetLength(AChars,1);
  251. AChars[0] := Ord(s[1]);
  252. end else begin
  253. u4str := WideStringToUCS4String(s);
  254. AChars := u4str;
  255. SetLength(AChars,Length(AChars)-1);
  256. end;
  257. end else begin
  258. t := t.Attributes.GetNamedItem(s_HEX);
  259. if (t = nil) then
  260. raise Exception.CreateFmt(sHexAttributeExpected,[APos]);
  261. SetLength(AChars,1);
  262. AChars[0] := StrToInt('$'+t.NodeValue);
  263. end
  264. end;
  265. procedure AddPrefixChars(const APrefix : array of UCS4Char; var ADest : TUnicodeCodePointArray);
  266. var
  267. k : Integer;
  268. begin
  269. k := Length(ADest);
  270. SetLength(ADest,(k+Length(APrefix)));
  271. Move(ADest[0],ADest[k+1],(SizeOf(k*ADest[0])));
  272. for k := 0 to k - 1 do
  273. ADest[k] := APrefix[k];
  274. end;
  275. function ReadNextItem(const APos : Integer) : Integer;
  276. var
  277. n, t : TDOMNode;
  278. contextStr : DOMString;
  279. w : TReorderWeigthKind;
  280. isSimpleCharTag : Boolean;
  281. simpleCharTag : AnsiChar;
  282. last : PReorderUnit;
  283. u4str : UCS4String;
  284. k : Integer;
  285. begin
  286. contextStr := '';
  287. Result := APos;
  288. n := list[APos];
  289. isSimpleCharTag := (Length(n.NodeName) = 1) and (Ord(n.NodeName[1])<=127);
  290. if isSimpleCharTag then begin
  291. simpleCharTag := AnsiChar(n.NodeName[1]);
  292. if (simpleCharTag = 'x') then begin
  293. inBlock := True;
  294. n := n.FirstChild;
  295. if (n.NodeName = s_CONTEXT) then begin
  296. if n.HasChildNodes() then begin
  297. t := n.FirstChild;
  298. if (t.NodeType = TEXT_NODE) then
  299. contextStr := TDOMText(t).Data;
  300. end;
  301. n := n.NextSibling;
  302. end;
  303. isSimpleCharTag := (Length(n.NodeName) = 1) and (Ord(n.NodeName[1])<=127);
  304. if isSimpleCharTag then
  305. simpleCharTag := AnsiChar(n.NodeName[1]);
  306. end;
  307. end;
  308. if isSimpleCharTag and (simpleCharTag in ['p','s','t','i']) then begin
  309. w := CharToReorderWeigthKind(AnsiChar(n.NodeName[1]));
  310. ReadChars(n,APos,u4str);
  311. AddElement(u4str,w,contextStr);
  312. Result := Result + 1;
  313. if not inBlock then
  314. exit;
  315. last := @statement^.Elements[elementActualCount-1];
  316. n := n.NextSibling;
  317. if (n <> nil) and (n.NodeName = s_EXTEND) then begin
  318. ReadChars(n,APos,u4str);
  319. SetLength(last^.ExpansionChars,Length(u4str));
  320. for k := 0 to Length(u4str) - 1 do
  321. last^.ExpansionChars[k] := u4str[k];
  322. end;
  323. exit;
  324. end;
  325. if (Length(n.NodeName) = 2) and (n.NodeName[2] = 'c') and
  326. (Ord(n.NodeName[1])<=127) and (AnsiChar(n.NodeName[1]) in ['p','s','t','i'])
  327. then begin
  328. w := CharToReorderWeigthKind(AnsiChar(n.NodeName[1]));
  329. ReadChars(n,APos,u4str);
  330. for k := Low(u4str) to High(u4str) do
  331. AddElement(u4str[k],w,contextStr);
  332. Result := Result + 1;
  333. exit;
  334. end;
  335. raise Exception.CreateFmt(sCaseNothandled,[n.NodeName,APos]);
  336. end;
  337. var
  338. i, c : Integer;
  339. n : TDOMNode;
  340. begin
  341. Result := False;
  342. inBlock := False;
  343. elementActualCount := 0;
  344. if (AStartPosition <= 0) then
  345. startPosition := 0
  346. else
  347. startPosition := AStartPosition;
  348. i := startPosition;
  349. list := ARules.ChildNodes;
  350. c := list.Count;
  351. if (c <= i) then
  352. exit;
  353. statement := AStatement;
  354. statement^.Clear();
  355. n := list[i];
  356. i := parse_reset();
  357. while (i < c) do begin
  358. n := list[i];
  359. if (n.NodeName = s_RESET) then
  360. Break;
  361. i := ReadNextItem(i);
  362. end;
  363. SetLength(statement^.Elements,elementActualCount);
  364. Result := (i > startPosition);
  365. if Result then
  366. ANextPos := i;
  367. end;
  368. procedure ParseInitialDocument(ASequence : POrderedCharacters; ADoc : TDOMDocument);
  369. var
  370. n : TDOMNode;
  371. rulesElement : TDOMElement;
  372. i, c, nextPost : Integer;
  373. statement : TReorderSequence;
  374. p : PReorderUnit;
  375. begin
  376. n := ADoc.DocumentElement.FindNode(s_RULES);
  377. if (n = nil) then
  378. raise Exception.Create(sRulesNodeNotFound);
  379. rulesElement := n as TDOMElement;
  380. c := rulesElement.ChildNodes.Count;
  381. ASequence^.Clear();
  382. SetLength(ASequence^.Data,c+100);
  383. nextPost := 0;
  384. i := 0;
  385. while (i < c) do begin
  386. statement.Clear();
  387. if not ParseStatement(rulesElement,i,@statement,nextPost) then
  388. Break;
  389. i := nextPost;
  390. try
  391. ASequence^.ApplyStatement(@statement);
  392. except
  393. on e : Exception do begin
  394. e.Message := Format('%s Position = %d',[e.Message,i]);
  395. raise;
  396. end;
  397. end;
  398. end;
  399. if (ASequence^.ActualLength > 0) then begin
  400. p := @ASequence^.Data[0];
  401. for i := 0 to ASequence^.ActualLength - 1 do begin
  402. p^.Changed := False;
  403. Inc(p);
  404. end;
  405. end;
  406. end;
  407. procedure ParseInitialDocument(ASequence : POrderedCharacters; AFileName : string);
  408. var
  409. doc : TXMLDocument;
  410. begin
  411. ReadXMLFile(doc,AFileName);
  412. try
  413. ParseInitialDocument(ASequence,doc);
  414. finally
  415. doc.Free();
  416. end;
  417. end;
  418. function EvaluateXPathStr(const AExpression : string; AContextNode : TDOMNode): DOMString;
  419. var
  420. xv : TXPathVariable;
  421. begin
  422. xv := EvaluateXPathExpression(AExpression,AContextNode);
  423. try
  424. if (xv <> nil) then
  425. Result := xv.AsText
  426. else
  427. Result := '';
  428. finally
  429. xv.Free();
  430. end;
  431. end;
  432. function ParseDeletion(
  433. const APattern : DOMString;
  434. ASequence : PReorderSequence
  435. ) : Integer;
  436. var
  437. r : array of TReorderUnit;
  438. c, i : Integer;
  439. uset : TUnicodeSet;
  440. it : TUnicodeSet.TIterator;
  441. p : PReorderUnit;
  442. begin
  443. if (APattern = '') then
  444. exit(0);
  445. it := nil;
  446. uset := TUnicodeSet.Create();
  447. try
  448. uset.AddPattern(APattern);
  449. it := uset.CreateIterator();
  450. c := 0;
  451. it.Reset();
  452. while it.MoveNext() do begin
  453. Inc(c);
  454. end;
  455. SetLength(r,c);
  456. p := @r[0];
  457. i := 0;
  458. it.Reset();
  459. while it.MoveNext() do begin
  460. p^.Clear();
  461. p^.WeigthKind := TReorderWeigthKind.Deletion;
  462. p^.Characters := Copy(it.GetCurrent());
  463. Inc(p);
  464. Inc(i);
  465. end;
  466. ASequence^.Clear();
  467. ASequence^.Elements := r;
  468. finally
  469. it.Free();
  470. uset.Free();
  471. end;
  472. SetLength(r,0);
  473. end;
  474. procedure ParseCollationItem(
  475. ACollationNode : TDOMElement;
  476. AItem : TCldrCollationItem;
  477. AMode : TCldrParserMode
  478. );
  479. var
  480. n : TDOMNode;
  481. rulesElement : TDOMElement;
  482. i, c, nextPos : Integer;
  483. statementList : TReorderSequenceArray;
  484. sal : Integer;//statement actual length
  485. statement : PReorderSequence;
  486. s : DOMString;
  487. begin
  488. AItem.TypeName := ACollationNode.GetAttribute(s_TYPE);
  489. AItem.Base := EvaluateXPathStr('base',ACollationNode);
  490. AItem.Backwards := (EvaluateXPathStr('settings/@backwards',ACollationNode) = 'on');
  491. if AItem.Backwards then
  492. AItem.ChangedFields := AItem.ChangedFields + [TCollationField.BackWard];
  493. AItem.Rules := nil;
  494. if (AMode = TCldrParserMode.FullParsing) then begin
  495. SetLength(statementList,15);
  496. sal := 0;
  497. statement := @statementList[0];
  498. s := EvaluateXPathStr('suppress_contractions',ACollationNode);
  499. if (s <> '') then begin
  500. if (ParseDeletion(s,statement) > 0) then begin
  501. Inc(sal);
  502. Inc(statement);
  503. end else begin
  504. statement^.Clear();
  505. end;
  506. end;
  507. n := ACollationNode.FindNode(s_RULES);
  508. if (n <> nil) then begin
  509. rulesElement := n as TDOMElement;
  510. c := rulesElement.ChildNodes.Count;
  511. nextPos := 0;
  512. i := 0;
  513. while (i < c) do begin
  514. statement^.Clear();
  515. if not ParseStatement(rulesElement,i,statement,nextPos) then
  516. Break;
  517. i := nextPos;
  518. Inc(statement);
  519. Inc(sal);
  520. if (sal >= Length(statementList)) then begin
  521. SetLength(statementList,(sal*2));
  522. statement := @statementList[(sal-1)];
  523. end;
  524. end;
  525. end;
  526. SetLength(statementList,sal);
  527. AItem.Rules := statementList;
  528. end;
  529. end;
  530. procedure ParseCollationDocument(
  531. ADoc : TDOMDocument;
  532. ACollation : TCldrCollation;
  533. AMode : TCldrParserMode
  534. );
  535. var
  536. n : TDOMNode;
  537. collationsElement : TDOMElement;
  538. i, c : Integer;
  539. item : TCldrCollationItem;
  540. nl : TDOMNodeList;
  541. begin
  542. n := ADoc.DocumentElement.FindNode(s_COLLATIONS);
  543. if (n = nil) then
  544. raise Exception.Create(sCollationsNodeNotFound);
  545. collationsElement := n as TDOMElement;
  546. ACollation.Clear();
  547. ACollation.Language := EvaluateXPathStr('identity/language/@type',ADoc.DocumentElement);
  548. ACollation.Version := EvaluateXPathStr('identity/version/@number',ADoc.DocumentElement);
  549. ACollation.DefaultType := EvaluateXPathStr('collations/default/@type',ADoc.DocumentElement);
  550. if collationsElement.HasChildNodes() then begin
  551. nl := collationsElement.ChildNodes;
  552. c := nl.Count;
  553. item := nil;
  554. try
  555. for i := 0 to c - 1 do begin
  556. n := nl[i];
  557. if (n.NodeName = s_COLLATION) then begin
  558. item := TCldrCollationItem.Create();
  559. ParseCollationItem((n as TDOMElement),item,AMode);
  560. ACollation.Add(item);
  561. item := nil;
  562. end
  563. end;
  564. except
  565. FreeAndNil(item);
  566. raise;
  567. end;
  568. end;
  569. end;
  570. procedure ParseCollationDocument(
  571. ADoc : TDOMDocument;
  572. ACollation : TCldrCollationItem;
  573. AType : string
  574. );
  575. var
  576. xv : TXPathVariable;
  577. begin
  578. xv := EvaluateXPathExpression(Format('collations/collation[@type=%s]',[QuotedStr(AType)]),ADoc.DocumentElement);
  579. try
  580. if (xv.AsNodeSet.Count = 0) then
  581. raise Exception.CreateFmt(sCollationTypeNotFound,[AType]);
  582. ACollation.Clear();
  583. ParseCollationItem((TDOMNode(xv.AsNodeSet[0]) as TDOMElement),ACollation,TCldrParserMode.FullParsing);
  584. finally
  585. xv.Free();
  586. end
  587. end;
  588. function ReadXMLFile(f: TStream) : TXMLDocument;
  589. var
  590. src : TXMLInputSource;
  591. parser: TDOMParser;
  592. begin
  593. src := TXMLInputSource.Create(f);
  594. Result := TXMLDocument.Create;
  595. parser := TDOMParser.Create();
  596. try
  597. parser.Options.IgnoreComments := True;
  598. parser.Parse(src, Result);
  599. finally
  600. src.Free();
  601. parser.Free;
  602. end;
  603. end;
  604. function ReadXMLFile(const AFilename: String) : TXMLDocument;
  605. var
  606. FileStream: TStream;
  607. begin
  608. Result := nil;
  609. FileStream := TFileStream.Create(AFilename, fmOpenRead+fmShareDenyWrite);
  610. try
  611. Result := ReadXMLFile(FileStream);
  612. finally
  613. FileStream.Free;
  614. end;
  615. end;
  616. procedure ParseCollationDocument(
  617. const AFileName : string;
  618. ACollation : TCldrCollation;
  619. AMode : TCldrParserMode
  620. );
  621. var
  622. doc : TXMLDocument;
  623. begin
  624. doc := ReadXMLFile(AFileName);
  625. try
  626. ParseCollationDocument(doc,ACollation,AMode);
  627. ACollation.LocalID := ExtractFileName(ChangeFileExt(AFileName,''));
  628. finally
  629. doc.Free();
  630. end;
  631. end;
  632. procedure ParseCollationDocument(
  633. const AFileName : string;
  634. ACollation : TCldrCollationItem;
  635. AType : string
  636. );
  637. var
  638. doc : TXMLDocument;
  639. begin
  640. doc := ReadXMLFile(AFileName);
  641. try
  642. ParseCollationDocument(doc,ACollation,AType);
  643. finally
  644. doc.Free();
  645. end;
  646. end;
  647. end.