dcxmlconfig.pas 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820
  1. {
  2. Double Commander
  3. -------------------------------------------------------------------------
  4. Implementation of configuration file in XML.
  5. Based on XmlConf from fcl-xml package.
  6. Copyright (C) 2010 Przemyslaw Nagay ([email protected])
  7. Copyright (C) 2013-2023 Alexander Koblov ([email protected])
  8. This program is free software; you can redistribute it and/or modify
  9. it under the terms of the GNU General Public License as published by
  10. the Free Software Foundation; either version 2 of the License, or
  11. (at your option) any later version.
  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. See the
  15. GNU General Public License for more details.
  16. You should have received a copy of the GNU General Public License
  17. along with this program; if not, write to the Free Software
  18. Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  19. }
  20. unit DCXmlConfig;
  21. {$mode objfpc}{$H+}
  22. interface
  23. uses
  24. Classes, SysUtils, Laz2_DOM, Laz2_XMLRead, Laz2_XMLWrite;
  25. type
  26. // Define type aliases so we don't have to include DOM if we want to use config.
  27. TXmlNode = TDOMNode;
  28. TXmlPath = DOMString;
  29. { TXmlConfig }
  30. TXmlConfig = class
  31. private
  32. FFileName: String;
  33. FDoc: TXMLDocument;
  34. function GetRootNode: TXmlNode;
  35. procedure SplitPathToNodeAndAttr(const Path: DOMString; out NodePath: DOMString; out AttrName: DOMString);
  36. public
  37. constructor Create; virtual;
  38. constructor Create(const AFileName: String; AutoLoad: Boolean = False); virtual;
  39. destructor Destroy; override;
  40. procedure Clear;
  41. function AddNode(const RootNode: TDOMNode; const ValueName: DOMString): TDOMNode;
  42. procedure DeleteNode(const RootNode: TDOMNode; const Path: DOMString);
  43. procedure DeleteNode(const Node: TDOMNode);
  44. procedure ClearNode(const Node: TDOMNode);
  45. function FindNode(const RootNode: TDOMNode; const Path: DOMString; bCreate: Boolean = False): TDOMNode;
  46. function GetContent(const Node: TDOMNode): String;
  47. function IsEmpty: Boolean;
  48. procedure SetContent(const Node: TDOMNode; const AValue: String);
  49. // ------------------------------------------------------------------------
  50. function GetAttr(const RootNode: TDOMNode; const Path: DOMString; const ADefault: String): String;
  51. function GetAttr(const RootNode: TDOMNode; const Path: DOMString; const ADefault: Boolean): Boolean;
  52. function GetAttr(const RootNode: TDOMNode; const Path: DOMString; const ADefault: Integer): Integer;
  53. function GetAttr(const RootNode: TDOMNode; const Path: DOMString; const ADefault: Int64): Int64;
  54. function GetAttr(const RootNode: TDOMNode; const Path: DOMString; const ADefault: Double): Double;
  55. function GetValue(const RootNode: TDOMNode; const Path: DOMString; const ADefault: String): String;
  56. function GetValue(const RootNode: TDOMNode; const Path: DOMString; const ADefault: Boolean): Boolean;
  57. function GetValue(const RootNode: TDOMNode; const Path: DOMString; const ADefault: Integer): Integer;
  58. function GetValue(const RootNode: TDOMNode; const Path: DOMString; const ADefault: Int64): Int64;
  59. function GetValue(const RootNode: TDOMNode; const Path: DOMString; const ADefault: Double): Double;
  60. function GetValue(const RootNode: TDOMNode; const Path: DOMString; constref ADefault: TRect): TRect;
  61. // The Try... functions return True if the attribute/node was found and only then set AValue.
  62. function TryGetAttr(const RootNode: TDOMNode; const Path: DOMString; out AValue: String): Boolean;
  63. function TryGetAttr(const RootNode: TDOMNode; const Path: DOMString; out AValue: Boolean): Boolean;
  64. function TryGetAttr(const RootNode: TDOMNode; const Path: DOMString; out AValue: Integer): Boolean;
  65. function TryGetAttr(const RootNode: TDOMNode; const Path: DOMString; out AValue: Int64): Boolean;
  66. function TryGetAttr(const RootNode: TDOMNode; const Path: DOMString; out AValue: Double): Boolean;
  67. function TryGetValue(const RootNode: TDOMNode; const Path: DOMString; out AValue: String): Boolean;
  68. function TryGetValue(const RootNode: TDOMNode; const Path: DOMString; out AValue: Boolean): Boolean;
  69. function TryGetValue(const RootNode: TDOMNode; const Path: DOMString; out AValue: Integer): Boolean;
  70. function TryGetValue(const RootNode: TDOMNode; const Path: DOMString; out AValue: Int64): Boolean;
  71. function TryGetValue(const RootNode: TDOMNode; const Path: DOMString; out AValue: Double): Boolean;
  72. // ------------------------------------------------------------------------
  73. // AddValue functions always add a new node.
  74. procedure AddValue(const RootNode: TDOMNode; const ValueName: DOMString; const AValue: String);
  75. procedure AddValue(const RootNode: TDOMNode; const ValueName: DOMString; const AValue: Boolean);
  76. procedure AddValue(const RootNode: TDOMNode; const ValueName: DOMString; const AValue: Integer);
  77. procedure AddValue(const RootNode: TDOMNode; const ValueName: DOMString; const AValue: Int64);
  78. procedure AddValue(const RootNode: TDOMNode; const ValueName: DOMString; const AValue: Double);
  79. procedure AddValueDef(const RootNode: TDOMNode; const ValueName: DOMString; const AValue, DefaultValue: String);
  80. procedure AddValueDef(const RootNode: TDOMNode; const ValueName: DOMString; const AValue, DefaultValue: Boolean);
  81. procedure AddValueDef(const RootNode: TDOMNode; const ValueName: DOMString; const AValue, DefaultValue: Integer);
  82. procedure AddValueDef(const RootNode: TDOMNode; const ValueName: DOMString; const AValue, DefaultValue: Int64);
  83. procedure AddValueDef(const RootNode: TDOMNode; const ValueName: DOMString; const AValue, DefaultValue: Double);
  84. // SetValue functions can only set values for unique paths.
  85. procedure SetAttr(const RootNode: TDOMNode; const Path: DOMString; const AValue: String);
  86. procedure SetAttr(const RootNode: TDOMNode; const Path: DOMString; const AValue: Boolean);
  87. procedure SetAttr(const RootNode: TDOMNode; const Path: DOMString; const AValue: Integer);
  88. procedure SetAttr(const RootNode: TDOMNode; const Path: DOMString; const AValue: Int64);
  89. procedure SetAttr(const RootNode: TDOMNode; const Path: DOMString; const AValue: Double);
  90. procedure SetValue(const RootNode: TDOMNode; const Path: DOMString; const AValue: String);
  91. procedure SetValue(const RootNode: TDOMNode; const Path: DOMString; const AValue: Boolean);
  92. procedure SetValue(const RootNode: TDOMNode; const Path: DOMString; const AValue: Integer);
  93. procedure SetValue(const RootNode: TDOMNode; const Path: DOMString; const AValue: Int64);
  94. procedure SetValue(const RootNode: TDOMNode; const Path: DOMString; const AValue: Double);
  95. procedure SetValue(const RootNode: TDOMNode; const Path: DOMString; constref AValue: TRect);
  96. // ------------------------------------------------------------------------
  97. procedure GetFont(const aNode: TXmlNode; Path: TXmlPath;
  98. var Name: String; var Size: Integer; var Style, Quality: Integer;
  99. const DefName: String; const DefSize: Integer; const DefStyle, DefQuality: Integer);
  100. procedure SetFont(const aNode: TXmlNode; Path: TXmlPath;
  101. const Name: String; const Size: Integer; const Style, Quality: Integer);
  102. // ------------------------------------------------------------------------
  103. procedure ReadFromFile(const AFilename: String);
  104. procedure ReadFromStream(AStream: TStream);
  105. procedure WriteToFile(const AFilename: String);
  106. procedure WriteToStream(AStream: TStream);
  107. function Load: Boolean;
  108. function LoadBypassingErrors: Boolean;
  109. function Save: Boolean;
  110. {en
  111. Get path of form "<RootNodeName>/<Child1NodeName>/<Child2NodeName>...".
  112. }
  113. function GetPathFromNode(aNode: TDOMNode): String;
  114. property FileName: String read FFileName write FFileName;
  115. property RootNode: TXmlNode read GetRootNode;
  116. end;
  117. EXmlConfigEmpty = class(EFilerError);
  118. EXmlConfigNotFound = class(EFilerError);
  119. implementation
  120. uses
  121. LazLogger, DCBasicTypes, DCOSUtils, DCClassesUtf8, URIParser;
  122. const
  123. XML_READER_FLAGS = [xrfAllowSpecialCharsInAttributeValue];
  124. const
  125. BoolStrings: array[Boolean] of DOMString = ('False', 'True');
  126. constructor TXmlConfig.Create;
  127. begin
  128. Clear;
  129. end;
  130. constructor TXmlConfig.Create(const AFileName: String; AutoLoad: Boolean);
  131. begin
  132. FFileName := AFileName;
  133. if not (AutoLoad and LoadBypassingErrors) then
  134. Clear;
  135. end;
  136. destructor TXmlConfig.Destroy;
  137. begin
  138. FreeAndNil(FDoc);
  139. inherited Destroy;
  140. end;
  141. procedure TXmlConfig.Clear;
  142. begin
  143. FreeAndNil(FDoc);
  144. FDoc := TXMLDocument.Create;
  145. FDoc.AppendChild(FDoc.CreateElement(ApplicationName));
  146. end;
  147. function TXmlConfig.GetRootNode: TXmlNode;
  148. begin
  149. Result := FDoc.DocumentElement;
  150. end;
  151. // ------------------------------------------------------------------------
  152. function TXmlConfig.GetAttr(const RootNode: TDOMNode; const Path: DOMString; const ADefault: String): String;
  153. begin
  154. if not TryGetAttr(RootNode, Path, Result) then
  155. Result := ADefault;
  156. end;
  157. function TXmlConfig.GetAttr(const RootNode: TDOMNode; const Path: DOMString; const ADefault: Boolean): Boolean;
  158. begin
  159. if not TryGetAttr(RootNode, Path, Result) then
  160. Result := ADefault;
  161. end;
  162. function TXmlConfig.GetAttr(const RootNode: TDOMNode; const Path: DOMString; const ADefault: Integer): Integer;
  163. begin
  164. if not TryGetAttr(RootNode, Path, Result) then
  165. Result := ADefault;
  166. end;
  167. function TXmlConfig.GetAttr(const RootNode: TDOMNode; const Path: DOMString; const ADefault: Int64): Int64;
  168. begin
  169. if not TryGetAttr(RootNode, Path, Result) then
  170. Result := ADefault;
  171. end;
  172. function TXmlConfig.GetAttr(const RootNode: TDOMNode; const Path: DOMString; const ADefault: Double): Double;
  173. begin
  174. if not TryGetAttr(RootNode, Path, Result) then
  175. Result := ADefault;
  176. end;
  177. function TXmlConfig.TryGetAttr(const RootNode: TDOMNode; const Path: DOMString; out AValue: String): Boolean;
  178. var
  179. Node: TDOMNode;
  180. Attr: TDOMAttr;
  181. NodePath, AttrName: DOMString;
  182. begin
  183. SplitPathToNodeAndAttr(Path, NodePath, AttrName);
  184. if NodePath <> EmptyStr then
  185. begin
  186. Node := FindNode(RootNode, NodePath, False);
  187. if not Assigned(Node) then
  188. Exit(False);
  189. end
  190. else
  191. Node := RootNode;
  192. Attr := TDOMElement(Node).GetAttributeNode(AttrName);
  193. Result := Assigned(Attr);
  194. if Result then
  195. AValue := Attr.Value;
  196. end;
  197. function TXmlConfig.TryGetAttr(const RootNode: TDOMNode; const Path: DOMString; out AValue: Boolean): Boolean;
  198. var
  199. sValue: String;
  200. begin
  201. Result := TryGetAttr(RootNode, Path, sValue);
  202. if Result then
  203. begin
  204. if SameText(sValue, 'TRUE') then
  205. AValue := True
  206. else if SameText(sValue, 'FALSE') then
  207. AValue := False
  208. else
  209. Result := False; // If other text then return not found.
  210. end;
  211. end;
  212. function TXmlConfig.TryGetAttr(const RootNode: TDOMNode; const Path: DOMString; out AValue: Integer): Boolean;
  213. var
  214. sValue: String;
  215. begin
  216. Result := TryGetAttr(RootNode, Path, sValue) and TryStrToInt(sValue, AValue);
  217. end;
  218. function TXmlConfig.TryGetAttr(const RootNode: TDOMNode; const Path: DOMString; out AValue: Int64): Boolean;
  219. var
  220. sValue: String;
  221. begin
  222. Result := TryGetAttr(RootNode, Path, sValue) and TryStrToInt64(sValue, AValue);
  223. end;
  224. function TXmlConfig.TryGetAttr(const RootNode: TDOMNode; const Path: DOMString; out AValue: Double): Boolean;
  225. var
  226. sValue: String;
  227. begin
  228. Result := TryGetAttr(RootNode, Path, sValue) and TryStrToFloat(sValue, AValue);
  229. end;
  230. function TXmlConfig.GetValue(const RootNode: TDOMNode; const Path: DOMString; const ADefault: String): String;
  231. var
  232. Node: TDOMNode;
  233. begin
  234. Node := FindNode(RootNode, Path, False);
  235. if Assigned(Node) then
  236. Result := Node.TextContent
  237. else
  238. Result := ADefault;
  239. end;
  240. function TXmlConfig.IsEmpty: Boolean;
  241. begin
  242. Result := RootNode.ChildNodes.Count = 0;
  243. end;
  244. function TXmlConfig.GetValue(const RootNode: TDOMNode; const Path: DOMString; const ADefault: Boolean): Boolean;
  245. var
  246. sValue: String;
  247. begin
  248. sValue := GetValue(RootNode, Path, '');
  249. if SameText(sValue, 'TRUE') then
  250. Result := True
  251. else if SameText(sValue, 'FALSE') then
  252. Result := False
  253. else
  254. Result := ADefault;
  255. end;
  256. function TXmlConfig.GetValue(const RootNode: TDOMNode; const Path: DOMString; const ADefault: Integer): Integer;
  257. begin
  258. Result := StrToIntDef(GetValue(RootNode, Path, ''), ADefault);
  259. end;
  260. function TXmlConfig.GetValue(const RootNode: TDOMNode; const Path: DOMString; const ADefault: Int64): Int64;
  261. begin
  262. Result := StrToInt64Def(GetValue(RootNode, Path, ''), ADefault);
  263. end;
  264. function TXmlConfig.GetValue(const RootNode: TDOMNode; const Path: DOMString; const ADefault: Double): Double;
  265. begin
  266. Result := StrToFloatDef(GetValue(RootNode, Path, ''), ADefault);
  267. end;
  268. function TXmlConfig.GetValue(const RootNode: TDOMNode; const Path: DOMString;
  269. constref ADefault: TRect): TRect;
  270. var
  271. I: Integer;
  272. ARect: TStringArray;
  273. begin
  274. ARect:= GetValue(RootNode, Path, '').Split(['|']);
  275. if Length(ARect) <> 4 then
  276. Result:= ADefault
  277. else begin
  278. for I:= 0 to 3 do
  279. Result.Vector[I]:= StrToIntDef(ARect[I], ADefault.Vector[I]);
  280. end;
  281. end;
  282. function TXmlConfig.TryGetValue(const RootNode: TDOMNode; const Path: DOMString; out AValue: String): Boolean;
  283. var
  284. Node: TDOMNode;
  285. begin
  286. Node := FindNode(RootNode, Path, False);
  287. Result := Assigned(Node);
  288. if Result then
  289. AValue := Node.TextContent;
  290. end;
  291. function TXmlConfig.TryGetValue(const RootNode: TDOMNode; const Path: DOMString; out AValue: Boolean): Boolean;
  292. var
  293. sValue: String;
  294. begin
  295. Result := TryGetValue(RootNode, Path, sValue);
  296. if Result then
  297. begin
  298. if SameText(sValue, 'TRUE') then
  299. AValue := True
  300. else if SameText(sValue, 'FALSE') then
  301. AValue := False
  302. else
  303. Result := False; // If other text then return not found.
  304. end;
  305. end;
  306. function TXmlConfig.TryGetValue(const RootNode: TDOMNode; const Path: DOMString; out AValue: Integer): Boolean;
  307. var
  308. sValue: String;
  309. begin
  310. Result := TryGetValue(RootNode, Path, sValue) and TryStrToInt(sValue, AValue);
  311. end;
  312. function TXmlConfig.TryGetValue(const RootNode: TDOMNode; const Path: DOMString; out AValue: Int64): Boolean;
  313. var
  314. sValue: String;
  315. begin
  316. Result := TryGetValue(RootNode, Path, sValue) and TryStrToInt64(sValue, AValue);
  317. end;
  318. function TXmlConfig.TryGetValue(const RootNode: TDOMNode; const Path: DOMString; out AValue: Double): Boolean;
  319. var
  320. sValue: String;
  321. begin
  322. Result := TryGetValue(RootNode, Path, sValue) and TryStrToFloat(sValue, AValue);
  323. end;
  324. // ----------------------------------------------------------------------------
  325. procedure TXmlConfig.AddValue(const RootNode: TDOMNode; const ValueName: DOMString; const AValue: String);
  326. var
  327. Node: TDOMNode;
  328. begin
  329. Node := RootNode.AppendChild(FDoc.CreateElement(ValueName));
  330. Node.TextContent := AValue;
  331. end;
  332. procedure TXmlConfig.AddValueDef(const RootNode: TDOMNode; const ValueName: DOMString; const AValue, DefaultValue: Boolean);
  333. begin
  334. if AValue <> DefaultValue then
  335. AddValue(RootNode, ValueName, AValue);
  336. end;
  337. procedure TXmlConfig.AddValueDef(const RootNode: TDOMNode; const ValueName: DOMString; const AValue, DefaultValue: Double);
  338. begin
  339. if AValue <> DefaultValue then
  340. AddValue(RootNode, ValueName, AValue);
  341. end;
  342. procedure TXmlConfig.AddValueDef(const RootNode: TDOMNode; const ValueName: DOMString; const AValue, DefaultValue: Int64);
  343. begin
  344. if AValue <> DefaultValue then
  345. AddValue(RootNode, ValueName, AValue);
  346. end;
  347. procedure TXmlConfig.AddValueDef(const RootNode: TDOMNode; const ValueName: DOMString; const AValue, DefaultValue: Integer);
  348. begin
  349. if AValue <> DefaultValue then
  350. AddValue(RootNode, ValueName, AValue);
  351. end;
  352. procedure TXmlConfig.AddValueDef(const RootNode: TDOMNode; const ValueName: DOMString; const AValue, DefaultValue: String);
  353. begin
  354. if AValue <> DefaultValue then
  355. AddValue(RootNode, ValueName, AValue);
  356. end;
  357. procedure TXmlConfig.AddValue(const RootNode: TDOMNode; const ValueName: DOMString; const AValue: Boolean);
  358. begin
  359. AddValue(RootNode, ValueName, BoolStrings[AValue]);
  360. end;
  361. procedure TXmlConfig.AddValue(const RootNode: TDOMNode; const ValueName: DOMString; const AValue: Integer);
  362. begin
  363. AddValue(RootNode, ValueName, IntToStr(AValue));
  364. end;
  365. procedure TXmlConfig.AddValue(const RootNode: TDOMNode; const ValueName: DOMString; const AValue: Int64);
  366. begin
  367. AddValue(RootNode, ValueName, IntToStr(AValue));
  368. end;
  369. procedure TXmlConfig.AddValue(const RootNode: TDOMNode; const ValueName: DOMString; const AValue: Double);
  370. begin
  371. AddValue(RootNode, ValueName, FloatToStr(AValue));
  372. end;
  373. procedure TXmlConfig.SetAttr(const RootNode: TDOMNode; const Path: DOMString; const AValue: String);
  374. var
  375. Node: TDOMNode;
  376. NodePath, AttrName: DOMString;
  377. begin
  378. SplitPathToNodeAndAttr(Path, NodePath, AttrName);
  379. if NodePath <> EmptyStr then
  380. begin
  381. Node := FindNode(RootNode, NodePath, True);
  382. TDOMElement(Node)[AttrName] := AValue;
  383. end
  384. else
  385. TDOMElement(RootNode)[AttrName] := AValue;
  386. end;
  387. procedure TXmlConfig.SetAttr(const RootNode: TDOMNode; const Path: DOMString; const AValue: Boolean);
  388. begin
  389. SetAttr(RootNode, Path, BoolStrings[AValue]);
  390. end;
  391. procedure TXmlConfig.SetAttr(const RootNode: TDOMNode; const Path: DOMString; const AValue: Integer);
  392. begin
  393. SetAttr(RootNode, Path, IntToStr(AValue));
  394. end;
  395. procedure TXmlConfig.SetAttr(const RootNode: TDOMNode; const Path: DOMString; const AValue: Int64);
  396. begin
  397. SetAttr(RootNode, Path, IntToStr(AValue));
  398. end;
  399. procedure TXmlConfig.SetAttr(const RootNode: TDOMNode; const Path: DOMString; const AValue: Double);
  400. begin
  401. SetAttr(RootNode, Path, FloatToStr(AValue));
  402. end;
  403. procedure TXmlConfig.SetValue(const RootNode: TDOMNode; const Path: DOMString; const AValue: String);
  404. var
  405. Node: TDOMNode;
  406. begin
  407. Node := FindNode(RootNode, Path, True);
  408. Node.TextContent := AValue;
  409. end;
  410. procedure TXmlConfig.SetValue(const RootNode: TDOMNode; const Path: DOMString; const AValue: Boolean);
  411. begin
  412. SetValue(RootNode, Path, BoolStrings[AValue]);
  413. end;
  414. procedure TXmlConfig.SetValue(const RootNode: TDOMNode; const Path: DOMString; const AValue: Integer);
  415. begin
  416. SetValue(RootNode, Path, IntToStr(AValue));
  417. end;
  418. procedure TXmlConfig.SetValue(const RootNode: TDOMNode; const Path: DOMString; const AValue: Int64);
  419. begin
  420. SetValue(RootNode, Path, IntToStr(AValue));
  421. end;
  422. procedure TXmlConfig.SetValue(const RootNode: TDOMNode; const Path: DOMString; const AValue: Double);
  423. begin
  424. SetValue(RootNode, Path, FloatToStr(AValue));
  425. end;
  426. procedure TXmlConfig.SetValue(const RootNode: TDOMNode; const Path: DOMString;
  427. constref AValue: TRect);
  428. var
  429. S: String;
  430. begin
  431. S:= IntToStr(AValue.Vector[0]) + '|' + IntToStr(AValue.Vector[1]) + '|' +
  432. IntToStr(AValue.Vector[2]) + '|' + IntToStr(AValue.Vector[3]);
  433. SetValue(RootNode, Path, S);
  434. end;
  435. // ----------------------------------------------------------------------------
  436. procedure TXmlConfig.ReadFromFile(const AFilename: String);
  437. var
  438. FileStream: TStream;
  439. TmpDoc: TXMLDocument;
  440. begin
  441. FileStream := TFileStreamEx.Create(AFilename, fmOpenRead or fmShareDenyWrite);
  442. try
  443. if FileStream.Size = 0 then
  444. raise EXmlConfigEmpty.Create('');
  445. ReadXMLFile(TmpDoc, FileStream, FilenameToURI(AFilename), XML_READER_FLAGS);
  446. if TmpDoc.DocumentElement.NodeName <> ApplicationName then
  447. raise EXMLReadError.Create('Root element is not <' + ApplicationName + '>.');
  448. FDoc.Free;
  449. FDoc := TmpDoc;
  450. finally
  451. FileStream.Free;
  452. end;
  453. end;
  454. procedure TXmlConfig.ReadFromStream(AStream: TStream);
  455. var
  456. TmpDoc: TXMLDocument;
  457. begin
  458. if AStream.Size = 0 then
  459. raise EXmlConfigEmpty.Create('');
  460. ReadXMLFile(TmpDoc, AStream, XML_READER_FLAGS);
  461. FDoc.Free;
  462. FDoc := TmpDoc;
  463. end;
  464. procedure TXmlConfig.WriteToFile(const AFilename: String);
  465. var
  466. FileStream: TFileStreamEx;
  467. begin
  468. FileStream := TFileStreamEx.Create(AFilename, fmCreate or fmShareDenyWrite);
  469. try
  470. WriteToStream(FileStream);
  471. FileStream.Flush;
  472. finally
  473. FileStream.Free;
  474. end;
  475. end;
  476. procedure TXmlConfig.WriteToStream(AStream: TStream);
  477. var
  478. Position: Int64;
  479. MemoryStream: TMemoryStream;
  480. begin
  481. MemoryStream:= TMemoryStream.Create;
  482. try
  483. WriteXMLFile(FDoc, MemoryStream);
  484. Position:= AStream.Position;
  485. AStream.Size:= MemoryStream.Size;
  486. AStream.Position:= Position;
  487. MemoryStream.SaveToStream(AStream);
  488. finally
  489. MemoryStream.Free;
  490. end;
  491. end;
  492. function TXmlConfig.Load: Boolean;
  493. begin
  494. Result := False;
  495. if FFileName = '' then
  496. Exit;
  497. if not mbFileExists(FileName) then
  498. raise EXmlConfigNotFound.Create('');
  499. if not mbFileAccess(FileName, fmOpenRead or fmShareDenyWrite) then
  500. raise EFOpenError.Create(SysErrorMessage(GetLastOSError));
  501. ReadFromFile(FileName);
  502. Result := True;
  503. end;
  504. function TXmlConfig.LoadBypassingErrors: Boolean;
  505. var
  506. ErrMsg: String;
  507. begin
  508. try
  509. Result := Load;
  510. except
  511. on e: Exception do
  512. begin
  513. ErrMsg := 'Error loading configuration file ' + FileName;
  514. if e.Message <> EmptyStr then
  515. ErrMsg := ErrMsg + ': ' + e.Message;
  516. DebugLogger.DebugLn(ErrMsg);
  517. Result := False;
  518. end;
  519. end;
  520. end;
  521. function TXmlConfig.Save: Boolean;
  522. var
  523. AFileName: String;
  524. dwAttr: TFileAttrs;
  525. bFileExists: Boolean;
  526. sTmpConfigFileName: String;
  527. begin
  528. Result := False;
  529. if FFileName = '' then
  530. Exit;
  531. dwAttr := mbFileGetAttr(FileName);
  532. bFileExists := (dwAttr <> faInvalidAttributes) and (not FPS_ISDIR(dwAttr));
  533. if bFileExists and FPS_ISLNK(dwAttr) then
  534. AFileName := mbReadAllLinks(FileName)
  535. else begin
  536. AFileName := FileName;
  537. end;
  538. // Write to temporary file and if successfully written rename to proper name.
  539. if (not bFileExists) or mbFileAccess(AFileName, fmOpenWrite or fmShareDenyWrite) then
  540. begin
  541. sTmpConfigFileName := GetTempName(AFileName);
  542. try
  543. WriteToFile(sTmpConfigFileName);
  544. if bFileExists then begin
  545. mbFileCopyAttr(AFileName, sTmpConfigFileName, [caoCopyOwnership, caoCopyPermissions]);
  546. end;
  547. if not mbRenameFile(sTmpConfigFileName, AFileName) then
  548. begin
  549. mbDeleteFile(sTmpConfigFileName);
  550. DebugLogger.Debugln('Cannot save configuration file ', FileName);
  551. end
  552. else
  553. Result := True;
  554. except
  555. on e: EStreamError do
  556. begin
  557. mbDeleteFile(sTmpConfigFileName);
  558. DebugLogger.Debugln('Error saving configuration file ', FileName, ': ' + e.Message);
  559. end;
  560. end;
  561. end
  562. else
  563. begin
  564. DebugLogger.Debugln('Cannot save configuration file ', FileName, ' - check permissions');
  565. end;
  566. end;
  567. procedure TXmlConfig.SplitPathToNodeAndAttr(const Path: DOMString; out NodePath: DOMString; out AttrName: DOMString);
  568. var
  569. AttrSepPos: Integer;
  570. begin
  571. // Last part of the path is the attr name.
  572. AttrSepPos := Length(Path);
  573. while (AttrSepPos > 0) and (Path[AttrSepPos] <> '/') do
  574. Dec(AttrSepPos);
  575. if (AttrSepPos = 0) or (AttrSepPos = Length(Path)) then
  576. begin
  577. NodePath := EmptyStr;
  578. AttrName := Path;
  579. end
  580. else
  581. begin
  582. NodePath := Copy(Path, 1, AttrSepPos - 1);
  583. AttrName := Copy(Path, AttrSepPos + 1, Length(Path) - AttrSepPos);
  584. end;
  585. end;
  586. function TXmlConfig.AddNode(const RootNode: TDOMNode; const ValueName: DOMString): TDOMNode;
  587. begin
  588. Result := RootNode.AppendChild(FDoc.CreateElement(ValueName));
  589. end;
  590. procedure TXmlConfig.DeleteNode(const RootNode: TDOMNode; const Path: DOMString);
  591. begin
  592. DeleteNode(FindNode(RootNode, Path, False));
  593. end;
  594. procedure TXmlConfig.DeleteNode(const Node: TDOMNode);
  595. begin
  596. if Assigned(Node) and Assigned(Node.ParentNode) then
  597. Node.ParentNode.DetachChild(Node);
  598. end;
  599. procedure TXmlConfig.ClearNode(const Node: TDOMNode);
  600. var
  601. Attr: TDOMAttr;
  602. begin
  603. while Assigned(Node.FirstChild) do
  604. Node.RemoveChild(Node.FirstChild);
  605. if Node.HasAttributes then
  606. begin
  607. Attr := TDOMAttr(Node.Attributes[0]);
  608. while Assigned(Attr) do
  609. begin
  610. TDOMElement(Node).RemoveAttributeNode(Attr);
  611. Attr := TDOMAttr(Attr.NextSibling);
  612. end;
  613. end;
  614. end;
  615. function TXmlConfig.FindNode(const RootNode: TDOMNode; const Path: DOMString; bCreate: Boolean = False): TDOMNode;
  616. var
  617. StartPos, EndPos: Integer;
  618. PathLen: Integer;
  619. Child: TDOMNode;
  620. function CompareDOMStrings(const s1, s2: DOMPChar; l1, l2: integer): integer;
  621. var i: integer;
  622. begin
  623. Result:=l1-l2;
  624. i:=0;
  625. while (i<l1) and (Result=0) do begin
  626. Result:=ord(s1[i])-ord(s2[i]);
  627. inc(i);
  628. end;
  629. end;
  630. begin
  631. Result := RootNode;
  632. PathLen := Length(Path);
  633. if PathLen = 0 then
  634. Exit;
  635. StartPos := 1;
  636. while Assigned(Result) do
  637. begin
  638. EndPos := StartPos;
  639. while (EndPos <= PathLen) and (Path[EndPos] <> '/') do
  640. Inc(EndPos);
  641. Child := Result.FirstChild;
  642. while Assigned(Child) and not ((Child.NodeType = ELEMENT_NODE)
  643. and (0 = CompareDOMStrings(DOMPChar(Child.NodeName), @Path[StartPos],
  644. Length(Child.NodeName), EndPos-StartPos))) do
  645. Child := Child.NextSibling;
  646. if not Assigned(Child) and bCreate then
  647. begin
  648. Child := FDoc.CreateElementBuf(@Path[StartPos], EndPos-StartPos);
  649. Result.AppendChild(Child);
  650. end;
  651. Result := Child;
  652. StartPos := EndPos + 1;
  653. if StartPos > PathLen then
  654. Break;
  655. end;
  656. end;
  657. function TXmlConfig.GetContent(const Node: TDOMNode): String;
  658. begin
  659. Result := Node.TextContent;
  660. end;
  661. procedure TXmlConfig.SetContent(const Node: TDOMNode; const AValue: String);
  662. begin
  663. Node.TextContent := AValue;
  664. end;
  665. function TXmlConfig.GetPathFromNode(aNode: TDOMNode): String;
  666. begin
  667. Result := aNode.NodeName;
  668. aNode := aNode.ParentNode;
  669. while Assigned(aNode) and (aNode <> RootNode) do
  670. begin
  671. Result := aNode.NodeName + '/' + Result;
  672. aNode := aNode.ParentNode;
  673. end;
  674. end;
  675. procedure TXmlConfig.GetFont(const aNode: TXmlNode; Path: TXmlPath; var
  676. Name: String; var Size: Integer; var Style, Quality: Integer;
  677. const DefName: String; const DefSize: Integer; const DefStyle,
  678. DefQuality: Integer);
  679. begin
  680. if Path <> '' then
  681. Path := Path + '/';
  682. Name := GetValue(aNode, Path + 'Name', DefName);
  683. Size := GetValue(aNode, Path + 'Size', DefSize);
  684. Style := GetValue(aNode, Path + 'Style', DefStyle);
  685. Quality := GetValue(aNode, Path + 'Quality', DefQuality);
  686. end;
  687. procedure TXmlConfig.SetFont(const aNode: TXmlNode; Path: TXmlPath;
  688. const Name: String; const Size: Integer; const Style, Quality: Integer);
  689. begin
  690. if Path <> '' then
  691. Path := Path + '/';
  692. SetValue(aNode, Path + 'Name', Name);
  693. SetValue(aNode, Path + 'Size', Size);
  694. SetValue(aNode, Path + 'Style', Style);
  695. SetValue(aNode, Path + 'Quality', Quality);
  696. end;
  697. end.