Quick.Options.pas 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676
  1. { ***************************************************************************
  2. Copyright (c) 2015-2020 Kike Pérez
  3. Unit : Quick.Options
  4. Description : Configuration group settings
  5. Author : Kike Pérez
  6. Version : 1.0
  7. Created : 18/10/2019
  8. Modified : 27/02/2020
  9. This file is part of QuickLib: https://github.com/exilon/QuickLib
  10. ***************************************************************************
  11. Licensed under the Apache License, Version 2.0 (the "License");
  12. you may not use this file except in compliance with the License.
  13. You may obtain a copy of the License at
  14. http://www.apache.org/licenses/LICENSE-2.0
  15. Unless required by applicable law or agreed to in writing, software
  16. distributed under the License is distributed on an "AS IS" BASIS,
  17. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  18. See the License for the specific language governing permissions and
  19. limitations under the License.
  20. *************************************************************************** }
  21. unit Quick.Options;
  22. {$i QuickLib.inc}
  23. interface
  24. uses
  25. Classes,
  26. RTTI,
  27. System.TypInfo,
  28. System.SysUtils,
  29. System.Generics.Collections,
  30. Quick.RTTI.Utils,
  31. Quick.Commons,
  32. Quick.FileMonitor;
  33. type
  34. Required = class(TCustomAttribute);
  35. TValidationCustomAttribute = class(TCustomAttribute)
  36. protected
  37. fErrorMsg : string;
  38. public
  39. property ErrorMsg : string read fErrorMsg write fErrorMsg;
  40. end;
  41. Range = class(TValidationCustomAttribute)
  42. private
  43. fRangeMin : Double;
  44. fRangeMax : Double;
  45. public
  46. constructor Create(aMin, aMax : Integer; const aErrorMsg : string = ''); overload;
  47. constructor Create(aMin, aMax : Double; const aErrorMsg : string = ''); overload;
  48. property Min : Double read fRangeMin write fRangeMax;
  49. property Max : Double read fRangeMax write fRangeMax;
  50. end;
  51. StringLength = class(TValidationCustomAttribute)
  52. private
  53. fMaxLength : Integer;
  54. public
  55. constructor Create(aMaxLength : Integer; const aErrorMsg : string = '');
  56. property MaxLength : Integer read fMaxLength write fMaxLength;
  57. end;
  58. TOptions = class;
  59. TConfigureOptionsProc<T : TOptions> = reference to procedure(aOptions : T);
  60. IOptionsValidator = interface
  61. ['{C6A09F78-8E34-4689-B943-83620437B9EF}']
  62. procedure ValidateOptions;
  63. end;
  64. TOptions = class(TInterfacedObject,IOptionsValidator)
  65. private
  66. fName : string;
  67. fHideOptions : Boolean;
  68. procedure DoValidateOptions; virtual;
  69. public
  70. constructor Create; virtual;
  71. property Name : string read fName write fName;
  72. property HideOptions : Boolean read fHideOptions write fHideOptions;
  73. procedure DefaultValues; virtual;
  74. function ConfigureOptions<T : TOptions>(aOptionsFunc : TConfigureOptionsProc<T>) : IOptionsValidator;
  75. procedure ValidateOptions;
  76. end;
  77. TOptionsValidator = class(TInterfacedObject,IOptionsValidator)
  78. private
  79. fOptions : TOptions;
  80. public
  81. constructor Create(aOptions : TOptions);
  82. procedure ValidateRequired(const aInstance : TObject; aProperty: TRttiProperty);
  83. procedure ValidateStringLength(const aInstance : TObject; aProperty: TRttiProperty; aValidation : StringLength);
  84. procedure ValidateRange(const aInstance : TObject; aProperty: TRttiProperty; aValidation : Range);
  85. procedure ValidateObject(aObj : TObject);
  86. procedure ValidateArray(aValue : TValue);
  87. procedure ValidateOptions;
  88. end;
  89. TOptions<T : TOptions> = record
  90. private
  91. fOptions : T;
  92. public
  93. constructor Create(aOptions : T);
  94. function ConfigureOptions(aOptionsFunc : TConfigureOptionsProc<T>) : IOptionsValidator;
  95. end;
  96. IOptions<T : TOptions> = interface
  97. ['{2779F946-2692-4F74-88AD-F35F5137057A}']
  98. function GetSectionValue : T;
  99. property Value : T read GetSectionValue;
  100. end;
  101. TOptionsClass = class of TOptions;
  102. IOptionsContainer = interface
  103. ['{A593C8BB-53CF-4AA4-9641-BF974E45CBD1}']
  104. function AddSection(aOption : TOptionsClass; const aOptionsName : string = '') : TOptions;
  105. function GetOptions(aOptionClass : TOptionsClass): TOptions;
  106. function GetSection(aOptionsSection : TOptionsClass; var vOptions : TOptions) : Boolean; overload;
  107. procedure AddOption(aOption : TOptions);
  108. function ExistsSection(aOption : TOptionsClass; const aSectionName : string = '') : Boolean;
  109. end;
  110. TSectionList = TObjectList<TOptions>;
  111. IOptionsSerializer = interface
  112. ['{7DECE203-4AAE-4C9D-86C8-B3D583DF7C8B}']
  113. function Load(const aFilename : string; aSections : TSectionList; aFailOnSectionNotExists : Boolean) : Boolean;
  114. procedure Save(const aFilename : string; aSections : TSectionList);
  115. function GetFileSectionNames(const aFilename : string; out oSections : TArray<string>) : Boolean;
  116. end;
  117. TOptionsSerializer = class(TInterfacedObject,IOptionsSerializer)
  118. public
  119. function Load(const aFilename : string; aSections : TSectionList; aFailOnSectionNotExists : Boolean) : Boolean; virtual; abstract;
  120. procedure Save(const aFilename : string; aSections : TSectionList); virtual; abstract;
  121. function GetFileSectionNames(const aFilename : string; out oSections : TArray<string>) : Boolean; virtual; abstract;
  122. end;
  123. TFileModifiedEvent = reference to procedure;
  124. TLoadConfigEvent = reference to procedure;
  125. TOptionValue<T : TOptions> = class(TInterfacedObject,IOptions<T>)
  126. private
  127. fValue : T;
  128. function GetSectionValue : T;
  129. public
  130. constructor Create(aValue : T);
  131. property Value : T read GetSectionValue;
  132. end;
  133. TOptionsContainer = class(TInterfacedObject,IOptionsContainer)
  134. private
  135. fFilename : string;
  136. fSerializer : IOptionsSerializer;
  137. fSections : TSectionList;
  138. fFileMonitor : TFileMonitor;
  139. fOnFileModified : TFileModifiedEvent;
  140. fLoaded : Boolean;
  141. fReloadIfFileChanged : Boolean;
  142. fOnConfigLoaded : TLoadConfigEvent;
  143. fOnConfigReloaded : TLoadConfigEvent;
  144. procedure CreateFileMonitor;
  145. procedure FileModifiedNotify(MonitorNotify : TMonitorNotify);
  146. procedure SetReloadIfFileChanged(const Value: Boolean);
  147. function GetOptions(aOptionClass : TOptionsClass): TOptions; overload;
  148. function GetOptions(aIndex : Integer) : TOptions; overload;
  149. function GetSection(aOptionsSection : TOptionsClass; var vOptions : TOptions) : Boolean; overload;
  150. public
  151. constructor Create(const aFilename : string; aOptionsSerializer : IOptionsSerializer; aReloadIfFileChanged : Boolean = False);
  152. destructor Destroy; override;
  153. property FileName : string read fFilename write fFilename;
  154. property ReloadIfFileChanged : Boolean read fReloadIfFileChanged write SetReloadIfFileChanged;
  155. property IsLoaded : Boolean read fLoaded;
  156. function ExistsSection(aOption : TOptionsClass; const aSectionName : string = '') : Boolean; overload;
  157. function ExistsSection<T : TOptions>(const aSectionName : string = '') : Boolean; overload;
  158. property OnFileModified : TFileModifiedEvent read fOnFileModified write fOnFileModified;
  159. property OnConfigLoaded : TLoadConfigEvent read fOnConfigLoaded write fOnConfigLoaded;
  160. property OnConfigReloaded : TLoadConfigEvent read fOnConfigReloaded write fOnConfigReloaded;
  161. property Items[aOptionClass : TOptionsClass] : TOptions read GetOptions; default;
  162. property Items[aIndex : Integer] : TOptions read GetOptions; default;
  163. function AddSection(aOption : TOptionsClass; const aSectionName : string = '') : TOptions; overload;
  164. function AddSection<T : TOptions>(const aSectionName : string = '') : TOptions<T>; overload;
  165. procedure AddOption(aOption : TOptions);
  166. function GetSectionInterface<T : TOptions> : IOptions<T>;
  167. function GetSection<T : TOptions>(const aSectionName : string = '') : T; overload;
  168. function GetFileSectionNames(out oSections : TArray<string>) : Boolean;
  169. function Count : Integer;
  170. procedure Load(aFailOnSectionNotExists : Boolean = False);
  171. procedure Save;
  172. end;
  173. IOptionsBuilder<T : TOptions> = interface
  174. ['{1A1DC9A9-7F2D-4CC4-A772-6C7DBAB34424}']
  175. function Options : T;
  176. end;
  177. TOptionsBuilder<T : TOptions> = class(TInterfacedObject,IOptionsBuilder<T>)
  178. protected
  179. fOptions : T;
  180. public
  181. constructor Create;
  182. function Options : T;
  183. end;
  184. EOptionConfigureError = class(Exception);
  185. EOptionLoadError = class(Exception);
  186. EOptionSaveError = class(Exception);
  187. EOptionValidationError = class(Exception);
  188. implementation
  189. { TOptionsContainer }
  190. constructor TOptionsContainer.Create(const aFilename : string; aOptionsSerializer : IOptionsSerializer; aReloadIfFileChanged : Boolean = False);
  191. begin
  192. fSerializer := aOptionsSerializer;
  193. fSections := TSectionList.Create(False);
  194. fFilename := aFilename;
  195. fLoaded := False;
  196. fReloadIfFileChanged := aReloadIfFileChanged;
  197. if aReloadIfFileChanged then CreateFileMonitor;
  198. end;
  199. procedure TOptionsContainer.CreateFileMonitor;
  200. begin
  201. fFileMonitor := TQuickFileMonitor.Create;
  202. fFileMonitor.FileName := fFilename;
  203. fFileMonitor.Interval := 2000;
  204. fFileMonitor.Notifies := [TMonitorNotify.mnFileModified];
  205. fFileMonitor.OnFileChange := FileModifiedNotify;
  206. fFileMonitor.Enabled := True;
  207. end;
  208. destructor TOptionsContainer.Destroy;
  209. var
  210. option : TOptions;
  211. begin
  212. if Assigned(fFileMonitor) then fFileMonitor.Free;
  213. fSerializer := nil;
  214. for option in fSections do
  215. begin
  216. if option.RefCount = 0 then option.Free;
  217. end;
  218. fSections.Free;
  219. inherited;
  220. end;
  221. function TOptionsContainer.ExistsSection(aOption: TOptionsClass;const aSectionName: string): Boolean;
  222. var
  223. option : TOptions;
  224. begin
  225. Result := False;
  226. for option in fSections do
  227. begin
  228. if CompareText(option.ClassName,aOption.ClassName) = 0 then
  229. begin
  230. if (not aSectionName.IsEmpty) and (CompareText(option.Name,aSectionName) = 0) then Exit(True);
  231. end;
  232. end;
  233. end;
  234. function TOptionsContainer.ExistsSection<T>(const aSectionName: string): Boolean;
  235. begin
  236. Result := GetSection<T>(aSectionName) <> nil;
  237. end;
  238. procedure TOptionsContainer.FileModifiedNotify(MonitorNotify: TMonitorNotify);
  239. begin
  240. if MonitorNotify = TMonitorNotify.mnFileModified then
  241. begin
  242. if Assigned(fOnFileModified) then fOnFileModified;
  243. if fReloadIfFileChanged then
  244. begin
  245. Load(False);
  246. end;
  247. end;
  248. end;
  249. procedure TOptionsContainer.AddOption(aOption: TOptions);
  250. begin
  251. if aOption.Name.IsEmpty then aOption.Name := Copy(aOption.ClassName,2,aOption.ClassName.Length);
  252. fSections.Add(aOption);
  253. end;
  254. function TOptionsContainer.AddSection(aOption : TOptionsClass; const aSectionName : string = '') : TOptions;
  255. var
  256. option : TOptions;
  257. begin
  258. //if section already exists, returns it
  259. option := Self.GetOptions(aOption);
  260. if option <> nil then Exit(option);
  261. option := aOption.Create;
  262. if aSectionName.IsEmpty then option.Name := Copy(aOption.ClassName,2,aOption.ClassName.Length)
  263. else option.Name := aSectionName;
  264. fSections.Add(option);
  265. Result := option;
  266. end;
  267. function TOptionsContainer.AddSection<T>(const aSectionName: string): TOptions<T>;
  268. var
  269. option : TOptions;
  270. begin
  271. //if section already exists, returns it
  272. option := Self.GetSection<T>(aSectionName);
  273. if option <> nil then Exit(TOptions<T>(option));
  274. //new section
  275. option := TRTTI.CreateInstance<T>;
  276. if aSectionName.IsEmpty then option.Name := Copy(T.ClassName,2,T.ClassName.Length)
  277. else option.Name := aSectionName;
  278. fSections.Add(option);
  279. Result.Create(option);
  280. end;
  281. function TOptionsContainer.Count: Integer;
  282. begin
  283. Result := fSections.Count;
  284. end;
  285. function TOptionsContainer.GetFileSectionNames(out oSections : TArray<string>) : Boolean;
  286. begin
  287. Result := fSerializer.GetFileSectionNames(fFilename,oSections);
  288. end;
  289. function TOptionsContainer.GetOptions(aIndex: Integer): TOptions;
  290. begin
  291. Result := fSections[aIndex];
  292. end;
  293. function TOptionsContainer.GetSection(aOptionsSection: TOptionsClass; var vOptions: TOptions): Boolean;
  294. var
  295. option : TOptions;
  296. begin
  297. Result := False;
  298. for option in fSections do
  299. begin
  300. if option is TOptionsClass then
  301. begin
  302. vOptions := option as TOptionsClass;
  303. Exit;
  304. end;
  305. end;
  306. end;
  307. function TOptionsContainer.GetOptions(aOptionClass : TOptionsClass) : TOptions;
  308. var
  309. option : TOptions;
  310. begin
  311. Result := nil;
  312. for option in fSections do
  313. begin
  314. if option is aOptionClass then Result := option as TOptionsClass;
  315. end;
  316. end;
  317. function TOptionsContainer.GetSection<T>(const aSectionName : string = '') : T;
  318. var
  319. option : TOptions;
  320. begin
  321. Result := nil;
  322. for option in fSections do
  323. begin
  324. if option is T then
  325. begin
  326. if (aSectionName.IsEmpty) or (CompareText(option.Name,aSectionName) = 0) then
  327. begin
  328. Result := option as T;
  329. Exit;
  330. end;
  331. end;
  332. end;
  333. end;
  334. function TOptionsContainer.GetSectionInterface<T>: IOptions<T>;
  335. begin
  336. Result := TOptionValue<T>.Create(Self.GetSection<T>);
  337. end;
  338. procedure TOptionsContainer.Load(aFailOnSectionNotExists : Boolean = False);
  339. var
  340. option : TOptions;
  341. begin
  342. if FileExists(fFilename) then
  343. begin
  344. if not fSerializer.Load(fFilename,fSections,aFailOnSectionNotExists) then Save;
  345. if not fLoaded then
  346. begin
  347. fLoaded := True;
  348. if Assigned(fOnConfigLoaded) then fOnConfigLoaded;
  349. end
  350. else if Assigned(fOnConfigReloaded) then fOnConfigReloaded;
  351. end
  352. else
  353. begin
  354. //if not exists file get default values
  355. for option in fSections do option.DefaultValues;
  356. //creates default file
  357. Save;
  358. end;
  359. end;
  360. procedure TOptionsContainer.Save;
  361. var
  362. laststate : Boolean;
  363. begin
  364. //disable filemonitor to avoid detect manual save as a external file change
  365. if fReloadIfFileChanged then
  366. begin
  367. laststate := fFileMonitor.Enabled;
  368. fFileMonitor.Enabled := False;
  369. try
  370. //save config file
  371. fSerializer.Save(fFilename,fSections);
  372. finally
  373. //set last state
  374. fFileMonitor.Enabled := laststate;
  375. end;
  376. end
  377. else fSerializer.Save(fFilename,fSections);
  378. end;
  379. procedure TOptionsContainer.SetReloadIfFileChanged(const Value: Boolean);
  380. begin
  381. if Value = fReloadIfFileChanged then Exit;
  382. fReloadIfFileChanged := Value;
  383. if Assigned(fFileMonitor) then fFileMonitor.Free;
  384. if fReloadIfFileChanged then CreateFileMonitor;
  385. end;
  386. { TOptions }
  387. function TOptions.ConfigureOptions<T>(aOptionsFunc: TConfigureOptionsProc<T>): IOptionsValidator;
  388. var
  389. value : TValue;
  390. begin
  391. Result := TOptionsValidator.Create(Self);
  392. if Assigned(aOptionsFunc) then
  393. begin
  394. value := Self;
  395. aOptionsFunc(value.AsType<T>);
  396. end;
  397. end;
  398. constructor TOptions.Create;
  399. begin
  400. fName := '';
  401. fHideOptions := False;
  402. end;
  403. procedure TOptions.DefaultValues;
  404. begin
  405. //nothing
  406. end;
  407. procedure TOptions.DoValidateOptions;
  408. var
  409. ivalidator : IOptionsValidator;
  410. begin
  411. ivalidator := TOptionsValidator.Create(Self);
  412. ivalidator.ValidateOptions;
  413. end;
  414. procedure TOptions.ValidateOptions;
  415. begin
  416. try
  417. DoValidateOptions;
  418. except
  419. on E : Exception do
  420. begin
  421. raise EOptionConfigureError.CreateFmt('Validation Options Error : %s',[e.Message]);
  422. end;
  423. end;
  424. end;
  425. { TOptionsValidator }
  426. procedure TOptionsValidator.ValidateObject(aObj : TObject);
  427. var
  428. ctx : TRttiContext;
  429. rtype : TRttiType;
  430. rprop : TRttiProperty;
  431. attrib : TCustomAttribute;
  432. rvalue : TValue;
  433. begin
  434. ctx := TRttiContext.Create;
  435. try
  436. rtype := ctx.GetType(aObj.ClassInfo);
  437. for rprop in rtype.GetProperties do
  438. begin
  439. //check only published properties
  440. if rprop.Visibility = TMemberVisibility.mvPublished then
  441. begin
  442. //check validation option attributes
  443. for attrib in rprop.GetAttributes do
  444. begin
  445. if attrib is Required then ValidateRequired(aObj,rprop)
  446. else if attrib is StringLength then ValidateStringLength(aObj,rprop,StringLength(attrib))
  447. else if attrib is Range then ValidateRange(aObj,rprop,Range(attrib));
  448. end;
  449. rvalue := rprop.GetValue(aObj);
  450. if not rvalue.IsEmpty then
  451. begin
  452. case rvalue.Kind of
  453. tkClass : ValidateObject(rvalue.AsObject);
  454. tkDynArray : ValidateArray(rvalue);
  455. end;
  456. end;
  457. end;
  458. end;
  459. finally
  460. ctx.Free;
  461. end;
  462. end;
  463. constructor TOptionsValidator.Create(aOptions: TOptions);
  464. begin
  465. fOptions := aOptions;
  466. end;
  467. procedure TOptionsValidator.ValidateOptions;
  468. begin
  469. ValidateObject(fOptions);
  470. end;
  471. procedure TOptionsValidator.ValidateArray(aValue : TValue);
  472. type
  473. PPByte = ^PByte;
  474. var
  475. ctx : TRttiContext;
  476. rDynArray : TRttiDynamicArrayType;
  477. itvalue : TValue;
  478. i : Integer;
  479. begin
  480. ctx := TRttiContext.Create;
  481. try
  482. rDynArray := ctx.GetType(aValue.TypeInfo) as TRTTIDynamicArrayType;
  483. for i := 0 to aValue.GetArrayLength - 1 do
  484. begin
  485. TValue.Make(PPByte(aValue.GetReferenceToRawData)^ + rDynArray.ElementType.TypeSize * i, rDynArray.ElementType.Handle,itvalue);
  486. if not itvalue.IsEmpty then
  487. begin
  488. case itvalue.Kind of
  489. tkClass : ValidateObject(itvalue.AsObject);
  490. tkDynArray : ValidateArray(itvalue);
  491. end;
  492. end;
  493. end;
  494. finally
  495. ctx.Free;
  496. end;
  497. end;
  498. procedure TOptionsValidator.ValidateRange(const aInstance : TObject; aProperty: TRttiProperty; aValidation : Range);
  499. var
  500. value : TValue;
  501. msg : string;
  502. begin
  503. value := aProperty.GetValue(aInstance);
  504. if not value.IsEmpty then
  505. begin
  506. if value.Kind = tkFloat then
  507. begin
  508. if (value.AsExtended < aValidation.Min) or (value.AsExtended > aValidation.Max) then
  509. begin
  510. if aValidation.ErrorMsg.IsEmpty then msg := Format('Option %s "%s.%s" exceeds predefined range (%2f - %2f)',[fOptions.Name,aInstance.ClassName,aProperty.Name,aValidation.Min,aValidation.Max])
  511. else msg := aValidation.ErrorMsg;
  512. raise EOptionValidationError.Create(msg);
  513. end;
  514. end
  515. else if value.Kind in [tkInteger,tkInt64] then
  516. begin
  517. if (value.AsInt64 < aValidation.Min) or (value.AsInt64 > aValidation.Max) then
  518. begin
  519. if aValidation.ErrorMsg.IsEmpty then msg := Format('Option %s "%s.%s" exceeds predefined range (%d - %d)',[fOptions.Name,aInstance.ClassName,aProperty.Name,Round(aValidation.Min),Round(aValidation.Max)])
  520. else msg := aValidation.ErrorMsg;
  521. raise EOptionValidationError.Create(msg);
  522. end;
  523. end;
  524. end;
  525. end;
  526. procedure TOptionsValidator.ValidateRequired(const aInstance : TObject; aProperty: TRttiProperty);
  527. begin
  528. if aProperty.GetValue(aInstance).IsEmpty then raise EOptionValidationError.CreateFmt('Option %s "%s.%s" is required',[fOptions.Name,aInstance.ClassName,aProperty.Name]);
  529. end;
  530. procedure TOptionsValidator.ValidateStringLength(const aInstance : TObject; aProperty: TRttiProperty; aValidation : StringLength);
  531. var
  532. value : TValue;
  533. msg : string;
  534. begin
  535. value := aProperty.GetValue(aInstance);
  536. if (not value.IsEmpty) and (value.AsString.Length > aValidation.MaxLength) then
  537. begin
  538. if aValidation.ErrorMsg.IsEmpty then msg := Format('Option %s "%s.%s" exceeds max length (%d)',[fOptions.Name,aInstance.ClassName,aProperty.Name,aValidation.MaxLength])
  539. else msg := aValidation.ErrorMsg;
  540. raise EOptionValidationError.Create(msg);
  541. end;
  542. end;
  543. { Range }
  544. constructor Range.Create(aMin, aMax: Integer; const aErrorMsg : string = '');
  545. begin
  546. fRangeMin := aMin;
  547. fRangeMax := aMax;
  548. fErrorMsg := aErrorMsg;
  549. end;
  550. constructor Range.Create(aMin, aMax: Double; const aErrorMsg: string);
  551. begin
  552. fRangeMin := aMin;
  553. fRangeMax := aMax;
  554. fErrorMsg := aErrorMsg;
  555. end;
  556. { StringLength }
  557. constructor StringLength.Create(aMaxLength: Integer; const aErrorMsg : string = '');
  558. begin
  559. fMaxLength := aMaxLength;
  560. fErrorMsg := aErrorMsg;
  561. end;
  562. { TOptionValue<T> }
  563. constructor TOptionValue<T>.Create(aValue: T);
  564. begin
  565. fValue := aValue;
  566. end;
  567. function TOptionValue<T>.GetSectionValue: T;
  568. begin
  569. Result := fValue;
  570. end;
  571. { TOptions<T> }
  572. function TOptions<T>.ConfigureOptions(aOptionsFunc: TConfigureOptionsProc<T>): IOptionsValidator;
  573. begin
  574. if Assigned(aOptionsFunc) then Result := fOptions.ConfigureOptions<T>(aOptionsFunc)
  575. else Result := TOptionsValidator.Create(fOptions);
  576. end;
  577. constructor TOptions<T>.Create(aOptions: T);
  578. begin
  579. fOptions := aOptions;
  580. end;
  581. { TOptionsBuilder<T> }
  582. constructor TOptionsBuilder<T>.Create;
  583. begin
  584. fOptions := (PTypeInfo(TypeInfo(T)).TypeData.ClassType.Create) as T;
  585. end;
  586. function TOptionsBuilder<T>.Options: T;
  587. begin
  588. Result := fOptions;
  589. end;
  590. end.