cldrxml.pas 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703
  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(AText : DOMString; AWeigthKind : TReorderWeigthKind);overload;
  207. var
  208. u4str : UCS4String;
  209. k : Integer;
  210. kp : PReorderUnit;
  211. begin
  212. u4str := WideStringToUCS4String(AText);
  213. EnsureElementLength(elementActualCount+1);
  214. kp := @statement^.Elements[elementActualCount];
  215. k := Length(u4str) - 1{null terminated};
  216. SetLength(kp^.Characters,k);
  217. for k := 0 to k - 1 do
  218. kp^.Characters[k] := u4str[k];
  219. kp^.WeigthKind:= AWeigthKind;
  220. elementActualCount := elementActualCount + 1;
  221. end;}
  222. procedure AddElement(
  223. const AChars : array of UCS4Char;
  224. const AWeigthKind : TReorderWeigthKind;
  225. const AContext : DOMString
  226. );overload;
  227. var
  228. kp : PReorderUnit;
  229. k : Integer;
  230. begin
  231. EnsureElementLength(elementActualCount+1);
  232. kp := @statement^.Elements[elementActualCount];
  233. SetLength(kp^.Characters,Length(AChars));
  234. for k := 0 to Length(AChars) - 1 do
  235. kp^.Characters[k] := AChars[k];
  236. kp^.WeigthKind := AWeigthKind;
  237. elementActualCount := elementActualCount + 1;
  238. if (AContext <> '') then
  239. kp^.Context := DomString2UnicodeCodePointArray(AContext);
  240. end;
  241. procedure ReadChars(
  242. ANode : TDOMNode;
  243. APos : Integer;
  244. var AChars : UCS4String
  245. );
  246. var
  247. t : TDOMNode;
  248. u4str : UCS4String;
  249. s : DOMString;
  250. begin
  251. if not ANode.HasChildNodes() then begin
  252. SetLength(AChars,1);
  253. AChars[0] := Ord(UnicodeChar(' '));
  254. exit;
  255. //raise Exception.CreateFmt(sCodePointExpected + ANode.ClassName,[APos]);
  256. end;
  257. t := ANode.FindNode(s_CODEPOINT);
  258. if (t = nil) then begin
  259. if (ANode.ChildNodes.Count <> 1) then
  260. raise Exception.CreateFmt(sUniqueChildNodeExpected,[APos]);
  261. t := ANode.ChildNodes[0];
  262. if not t.InheritsFrom(TDOMText) then
  263. raise Exception.CreateFmt(sTextNodeChildExpected,[APos,(t.NodeName+'('+t.ClassName+')')]);
  264. s := TDOMText(t).Data;
  265. if (Length(s) = 1) then begin
  266. SetLength(AChars,1);
  267. AChars[0] := Ord(s[1]);
  268. end else begin
  269. u4str := WideStringToUCS4String(s);
  270. AChars := u4str;
  271. SetLength(AChars,Length(AChars)-1);
  272. end;
  273. end else begin
  274. t := t.Attributes.GetNamedItem(s_HEX);
  275. if (t = nil) then
  276. raise Exception.CreateFmt(sHexAttributeExpected,[APos]);
  277. SetLength(AChars,1);
  278. AChars[0] := StrToInt('$'+t.NodeValue);
  279. end
  280. end;
  281. procedure AddPrefixChars(const APrefix : array of UCS4Char; var ADest : TUnicodeCodePointArray);
  282. var
  283. k : Integer;
  284. begin
  285. k := Length(ADest);
  286. SetLength(ADest,(k+Length(APrefix)));
  287. Move(ADest[0],ADest[k+1],(SizeOf(k*ADest[0])));
  288. for k := 0 to k - 1 do
  289. ADest[k] := APrefix[k];
  290. end;
  291. function ReadNextItem(const APos : Integer) : Integer;
  292. var
  293. n, t : TDOMNode;
  294. contextStr : DOMString;
  295. w : TReorderWeigthKind;
  296. isSimpleCharTag : Boolean;
  297. simpleCharTag : AnsiChar;
  298. last : PReorderUnit;
  299. u4str : UCS4String;
  300. k : Integer;
  301. begin
  302. contextStr := '';
  303. Result := APos;
  304. n := list[APos];
  305. isSimpleCharTag := (Length(n.NodeName) = 1) and (Ord(n.NodeName[1])<=127);
  306. if isSimpleCharTag then begin
  307. simpleCharTag := AnsiChar(n.NodeName[1]);
  308. if (simpleCharTag = 'x') then begin
  309. inBlock := True;
  310. n := n.FirstChild;
  311. if (n.NodeName = s_CONTEXT) then begin
  312. if n.HasChildNodes() then begin
  313. t := n.FirstChild;
  314. if (t.NodeType = TEXT_NODE) then
  315. contextStr := TDOMText(t).Data;
  316. end;
  317. n := n.NextSibling;
  318. end;
  319. isSimpleCharTag := (Length(n.NodeName) = 1) and (Ord(n.NodeName[1])<=127);
  320. if isSimpleCharTag then
  321. simpleCharTag := AnsiChar(n.NodeName[1]);
  322. end;
  323. end;
  324. if isSimpleCharTag and (simpleCharTag in ['p','s','t','i']) then begin
  325. w := CharToReorderWeigthKind(AnsiChar(n.NodeName[1]));
  326. ReadChars(n,APos,u4str);
  327. AddElement(u4str,w,contextStr);
  328. Result := Result + 1;
  329. if not inBlock then
  330. exit;
  331. last := @statement^.Elements[elementActualCount-1];
  332. n := n.NextSibling;
  333. if (n <> nil) and (n.NodeName = s_EXTEND) then begin
  334. ReadChars(n,APos,u4str);
  335. SetLength(last^.ExpansionChars,Length(u4str));
  336. for k := 0 to Length(u4str) - 1 do
  337. last^.ExpansionChars[k] := u4str[k];
  338. end;
  339. exit;
  340. end;
  341. if (Length(n.NodeName) = 2) and (n.NodeName[2] = 'c') and
  342. (Ord(n.NodeName[1])<=127) and (AnsiChar(n.NodeName[1]) in ['p','s','t','i'])
  343. then begin
  344. w := CharToReorderWeigthKind(AnsiChar(n.NodeName[1]));
  345. ReadChars(n,APos,u4str);
  346. for k := Low(u4str) to High(u4str) do
  347. AddElement(u4str[k],w,contextStr);
  348. Result := Result + 1;
  349. exit;
  350. end;
  351. raise Exception.CreateFmt(sCaseNothandled,[n.NodeName,APos]);
  352. end;
  353. var
  354. i, c : Integer;
  355. n : TDOMNode;
  356. begin
  357. Result := False;
  358. inBlock := False;
  359. elementActualCount := 0;
  360. if (AStartPosition <= 0) then
  361. startPosition := 0
  362. else
  363. startPosition := AStartPosition;
  364. i := startPosition;
  365. list := ARules.ChildNodes;
  366. c := list.Count;
  367. if (c <= i) then
  368. exit;
  369. statement := AStatement;
  370. statement^.Clear();
  371. n := list[i];
  372. i := parse_reset();
  373. while (i < c) do begin
  374. n := list[i];
  375. if (n.NodeName = s_RESET) then
  376. Break;
  377. i := ReadNextItem(i);
  378. end;
  379. SetLength(statement^.Elements,elementActualCount);
  380. Result := (i > startPosition);
  381. if Result then
  382. ANextPos := i;
  383. end;
  384. procedure ParseInitialDocument(ASequence : POrderedCharacters; ADoc : TDOMDocument);
  385. var
  386. n : TDOMNode;
  387. rulesElement : TDOMElement;
  388. i, c, nextPost : Integer;
  389. statement : TReorderSequence;
  390. p : PReorderUnit;
  391. begin
  392. n := ADoc.DocumentElement.FindNode(s_RULES);
  393. if (n = nil) then
  394. raise Exception.Create(sRulesNodeNotFound);
  395. rulesElement := n as TDOMElement;
  396. c := rulesElement.ChildNodes.Count;
  397. ASequence^.Clear();
  398. SetLength(ASequence^.Data,c+100);
  399. nextPost := 0;
  400. i := 0;
  401. while (i < c) do begin
  402. statement.Clear();
  403. if not ParseStatement(rulesElement,i,@statement,nextPost) then
  404. Break;
  405. i := nextPost;
  406. try
  407. ASequence^.ApplyStatement(@statement);
  408. except
  409. on e : Exception do begin
  410. e.Message := Format('%s Position = %d',[e.Message,i]);
  411. raise;
  412. end;
  413. end;
  414. end;
  415. if (ASequence^.ActualLength > 0) then begin
  416. p := @ASequence^.Data[0];
  417. for i := 0 to ASequence^.ActualLength - 1 do begin
  418. p^.Changed := False;
  419. Inc(p);
  420. end;
  421. end;
  422. end;
  423. procedure ParseInitialDocument(ASequence : POrderedCharacters; AFileName : string);
  424. var
  425. doc : TXMLDocument;
  426. begin
  427. ReadXMLFile(doc,AFileName);
  428. try
  429. ParseInitialDocument(ASequence,doc);
  430. finally
  431. doc.Free();
  432. end;
  433. end;
  434. function EvaluateXPathStr(const AExpression : string; AContextNode : TDOMNode): DOMString;
  435. var
  436. xv : TXPathVariable;
  437. begin
  438. xv := EvaluateXPathExpression(AExpression,AContextNode);
  439. try
  440. if (xv <> nil) then
  441. Result := xv.AsText
  442. else
  443. Result := '';
  444. finally
  445. xv.Free();
  446. end;
  447. end;
  448. function ParseDeletion(
  449. const APattern : DOMString;
  450. ASequence : PReorderSequence
  451. ) : Integer;
  452. var
  453. r : array of TReorderUnit;
  454. c, i : Integer;
  455. uset : TUnicodeSet;
  456. it : TUnicodeSet.TIterator;
  457. p : PReorderUnit;
  458. begin
  459. if (APattern = '') then
  460. exit(0);
  461. it := nil;
  462. uset := TUnicodeSet.Create();
  463. try
  464. uset.AddPattern(APattern);
  465. it := uset.CreateIterator();
  466. c := 0;
  467. it.Reset();
  468. while it.MoveNext() do begin
  469. Inc(c);
  470. end;
  471. SetLength(r,c);
  472. p := @r[0];
  473. i := 0;
  474. it.Reset();
  475. while it.MoveNext() do begin
  476. p^.Clear();
  477. p^.WeigthKind := TReorderWeigthKind.Deletion;
  478. p^.Characters := Copy(it.GetCurrent());
  479. Inc(p);
  480. Inc(i);
  481. end;
  482. ASequence^.Clear();
  483. ASequence^.Elements := r;
  484. finally
  485. it.Free();
  486. uset.Free();
  487. end;
  488. SetLength(r,0);
  489. end;
  490. procedure ParseCollationItem(
  491. ACollationNode : TDOMElement;
  492. AItem : TCldrCollationItem;
  493. AMode : TCldrParserMode
  494. );
  495. var
  496. n : TDOMNode;
  497. rulesElement : TDOMElement;
  498. i, c, nextPos : Integer;
  499. statementList : TReorderSequenceArray;
  500. sal : Integer;//statement actual length
  501. statement : PReorderSequence;
  502. s : DOMString;
  503. begin
  504. AItem.TypeName := ACollationNode.GetAttribute(s_TYPE);
  505. AItem.Base := EvaluateXPathStr('base',ACollationNode);
  506. AItem.Backwards := (EvaluateXPathStr('settings/@backwards',ACollationNode) = 'on');
  507. if AItem.Backwards then
  508. AItem.ChangedFields := AItem.ChangedFields + [TCollationField.BackWard];
  509. AItem.Rules := nil;
  510. if (AMode = TCldrParserMode.FullParsing) then begin
  511. SetLength(statementList,15);
  512. sal := 0;
  513. statement := @statementList[0];
  514. s := EvaluateXPathStr('suppress_contractions',ACollationNode);
  515. if (s <> '') then begin
  516. if (ParseDeletion(s,statement) > 0) then begin
  517. Inc(sal);
  518. Inc(statement);
  519. end else begin
  520. statement^.Clear();
  521. end;
  522. end;
  523. n := ACollationNode.FindNode(s_RULES);
  524. if (n <> nil) then begin
  525. rulesElement := n as TDOMElement;
  526. c := rulesElement.ChildNodes.Count;
  527. nextPos := 0;
  528. i := 0;
  529. while (i < c) do begin
  530. statement^.Clear();
  531. if not ParseStatement(rulesElement,i,statement,nextPos) then
  532. Break;
  533. i := nextPos;
  534. Inc(statement);
  535. Inc(sal);
  536. if (sal >= Length(statementList)) then begin
  537. SetLength(statementList,(sal*2));
  538. statement := @statementList[(sal-1)];
  539. end;
  540. end;
  541. end;
  542. SetLength(statementList,sal);
  543. AItem.Rules := statementList;
  544. end;
  545. end;
  546. procedure ParseCollationDocument(
  547. ADoc : TDOMDocument;
  548. ACollation : TCldrCollation;
  549. AMode : TCldrParserMode
  550. );
  551. var
  552. n : TDOMNode;
  553. collationsElement : TDOMElement;
  554. i, c : Integer;
  555. item : TCldrCollationItem;
  556. nl : TDOMNodeList;
  557. begin
  558. n := ADoc.DocumentElement.FindNode(s_COLLATIONS);
  559. if (n = nil) then
  560. raise Exception.Create(sCollationsNodeNotFound);
  561. collationsElement := n as TDOMElement;
  562. ACollation.Clear();
  563. ACollation.Language := EvaluateXPathStr('identity/language/@type',ADoc.DocumentElement);
  564. ACollation.Version := EvaluateXPathStr('identity/version/@number',ADoc.DocumentElement);
  565. ACollation.DefaultType := EvaluateXPathStr('collations/default/@type',ADoc.DocumentElement);
  566. if collationsElement.HasChildNodes() then begin
  567. nl := collationsElement.ChildNodes;
  568. c := nl.Count;
  569. item := nil;
  570. try
  571. for i := 0 to c - 1 do begin
  572. n := nl[i];
  573. if (n.NodeName = s_COLLATION) then begin
  574. item := TCldrCollationItem.Create();
  575. ParseCollationItem((n as TDOMElement),item,AMode);
  576. ACollation.Add(item);
  577. item := nil;
  578. end
  579. end;
  580. except
  581. FreeAndNil(item);
  582. raise;
  583. end;
  584. end;
  585. end;
  586. procedure ParseCollationDocument(
  587. ADoc : TDOMDocument;
  588. ACollation : TCldrCollationItem;
  589. AType : string
  590. );
  591. var
  592. xv : TXPathVariable;
  593. begin
  594. xv := EvaluateXPathExpression(Format('collations/collation[@type=%s]',[QuotedStr(AType)]),ADoc.DocumentElement);
  595. try
  596. if (xv.AsNodeSet.Count = 0) then
  597. raise Exception.CreateFmt(sCollationTypeNotFound,[AType]);
  598. ACollation.Clear();
  599. ParseCollationItem((TDOMNode(xv.AsNodeSet[0]) as TDOMElement),ACollation,TCldrParserMode.FullParsing);
  600. finally
  601. xv.Free();
  602. end
  603. end;
  604. function ReadXMLFile(f: TStream) : TXMLDocument;
  605. var
  606. src : TXMLInputSource;
  607. parser: TDOMParser;
  608. begin
  609. src := TXMLInputSource.Create(f);
  610. Result := TXMLDocument.Create;
  611. parser := TDOMParser.Create();
  612. try
  613. parser.Options.IgnoreComments := True;
  614. parser.Parse(src, Result);
  615. finally
  616. src.Free();
  617. parser.Free;
  618. end;
  619. end;
  620. function ReadXMLFile(const AFilename: String) : TXMLDocument;
  621. var
  622. FileStream: TStream;
  623. begin
  624. Result := nil;
  625. FileStream := TFileStream.Create(AFilename, fmOpenRead+fmShareDenyWrite);
  626. try
  627. Result := ReadXMLFile(FileStream);
  628. finally
  629. FileStream.Free;
  630. end;
  631. end;
  632. procedure ParseCollationDocument(
  633. const AFileName : string;
  634. ACollation : TCldrCollation;
  635. AMode : TCldrParserMode
  636. );
  637. var
  638. doc : TXMLDocument;
  639. begin
  640. doc := ReadXMLFile(AFileName);
  641. try
  642. ParseCollationDocument(doc,ACollation,AMode);
  643. ACollation.LocalID := ExtractFileName(ChangeFileExt(AFileName,''));
  644. finally
  645. doc.Free();
  646. end;
  647. end;
  648. procedure ParseCollationDocument(
  649. const AFileName : string;
  650. ACollation : TCldrCollationItem;
  651. AType : string
  652. );
  653. var
  654. doc : TXMLDocument;
  655. begin
  656. doc := ReadXMLFile(AFileName);
  657. try
  658. ParseCollationDocument(doc,ACollation,AType);
  659. finally
  660. doc.Free();
  661. end;
  662. end;
  663. end.