Quick.Options.pas 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667
  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. Sleep(0);
  375. fFileMonitor.Enabled := laststate;
  376. end;
  377. end
  378. else fSerializer.Save(fFilename,fSections);
  379. end;
  380. procedure TOptionsContainer.SetReloadIfFileChanged(const Value: Boolean);
  381. begin
  382. if Value = fReloadIfFileChanged then Exit;
  383. fReloadIfFileChanged := Value;
  384. if Assigned(fFileMonitor) then fFileMonitor.Free;
  385. if fReloadIfFileChanged then CreateFileMonitor;
  386. end;
  387. { TOptions }
  388. function TOptions.ConfigureOptions<T>(aOptionsFunc: TConfigureOptionsProc<T>): IOptionsValidator;
  389. var
  390. value : TValue;
  391. begin
  392. Result := TOptionsValidator.Create(Self);
  393. if Assigned(aOptionsFunc) then
  394. begin
  395. value := Self;
  396. aOptionsFunc(value.AsType<T>);
  397. end;
  398. end;
  399. constructor TOptions.Create;
  400. begin
  401. fName := '';
  402. fHideOptions := False;
  403. end;
  404. procedure TOptions.DefaultValues;
  405. begin
  406. //nothing
  407. end;
  408. procedure TOptions.DoValidateOptions;
  409. var
  410. ivalidator : IOptionsValidator;
  411. begin
  412. ivalidator := TOptionsValidator.Create(Self);
  413. ivalidator.ValidateOptions;
  414. end;
  415. procedure TOptions.ValidateOptions;
  416. begin
  417. try
  418. DoValidateOptions;
  419. except
  420. on E : Exception do
  421. begin
  422. raise EOptionConfigureError.CreateFmt('Validation Options Error : %s',[e.Message]);
  423. end;
  424. end;
  425. end;
  426. { TOptionsValidator }
  427. procedure TOptionsValidator.ValidateObject(aObj : TObject);
  428. var
  429. ctx : TRttiContext;
  430. rtype : TRttiType;
  431. rprop : TRttiProperty;
  432. attrib : TCustomAttribute;
  433. rvalue : TValue;
  434. begin
  435. rtype := ctx.GetType(aObj.ClassInfo);
  436. for rprop in rtype.GetProperties do
  437. begin
  438. //check only published properties
  439. if rprop.Visibility = TMemberVisibility.mvPublished then
  440. begin
  441. //check validation option attributes
  442. for attrib in rprop.GetAttributes do
  443. begin
  444. if attrib is Required then ValidateRequired(aObj,rprop)
  445. else if attrib is StringLength then ValidateStringLength(aObj,rprop,StringLength(attrib))
  446. else if attrib is Range then ValidateRange(aObj,rprop,Range(attrib));
  447. end;
  448. rvalue := rprop.GetValue(aObj);
  449. if not rvalue.IsEmpty then
  450. begin
  451. case rvalue.Kind of
  452. tkClass : ValidateObject(rvalue.AsObject);
  453. tkDynArray : ValidateArray(rvalue);
  454. end;
  455. end;
  456. end;
  457. end;
  458. end;
  459. constructor TOptionsValidator.Create(aOptions: TOptions);
  460. begin
  461. fOptions := aOptions;
  462. end;
  463. procedure TOptionsValidator.ValidateOptions;
  464. begin
  465. ValidateObject(fOptions);
  466. end;
  467. procedure TOptionsValidator.ValidateArray(aValue : TValue);
  468. type
  469. PPByte = ^PByte;
  470. var
  471. ctx : TRttiContext;
  472. rDynArray : TRttiDynamicArrayType;
  473. itvalue : TValue;
  474. i : Integer;
  475. begin
  476. rDynArray := ctx.GetType(aValue.TypeInfo) as TRTTIDynamicArrayType;
  477. for i := 0 to aValue.GetArrayLength - 1 do
  478. begin
  479. TValue.Make(PPByte(aValue.GetReferenceToRawData)^ + rDynArray.ElementType.TypeSize * i, rDynArray.ElementType.Handle,itvalue);
  480. if not itvalue.IsEmpty then
  481. begin
  482. case itvalue.Kind of
  483. tkClass : ValidateObject(itvalue.AsObject);
  484. tkDynArray : ValidateArray(itvalue);
  485. end;
  486. end;
  487. end;
  488. end;
  489. procedure TOptionsValidator.ValidateRange(const aInstance : TObject; aProperty: TRttiProperty; aValidation : Range);
  490. var
  491. value : TValue;
  492. msg : string;
  493. begin
  494. value := aProperty.GetValue(aInstance);
  495. if not value.IsEmpty then
  496. begin
  497. if value.Kind = tkFloat then
  498. begin
  499. if (value.AsExtended < aValidation.Min) or (value.AsExtended > aValidation.Max) then
  500. begin
  501. 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])
  502. else msg := aValidation.ErrorMsg;
  503. raise EOptionValidationError.Create(msg);
  504. end;
  505. end
  506. else if value.Kind in [tkInteger,tkInt64] then
  507. begin
  508. if (value.AsInt64 < aValidation.Min) or (value.AsInt64 > aValidation.Max) then
  509. begin
  510. 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)])
  511. else msg := aValidation.ErrorMsg;
  512. raise EOptionValidationError.Create(msg);
  513. end;
  514. end;
  515. end;
  516. end;
  517. procedure TOptionsValidator.ValidateRequired(const aInstance : TObject; aProperty: TRttiProperty);
  518. begin
  519. if aProperty.GetValue(aInstance).IsEmpty then raise EOptionValidationError.CreateFmt('Option %s "%s.%s" is required',[fOptions.Name,aInstance.ClassName,aProperty.Name]);
  520. end;
  521. procedure TOptionsValidator.ValidateStringLength(const aInstance : TObject; aProperty: TRttiProperty; aValidation : StringLength);
  522. var
  523. value : TValue;
  524. msg : string;
  525. begin
  526. value := aProperty.GetValue(aInstance);
  527. if (not value.IsEmpty) and (value.AsString.Length > aValidation.MaxLength) then
  528. begin
  529. if aValidation.ErrorMsg.IsEmpty then msg := Format('Option %s "%s.%s" exceeds max length (%d)',[fOptions.Name,aInstance.ClassName,aProperty.Name,aValidation.MaxLength])
  530. else msg := aValidation.ErrorMsg;
  531. raise EOptionValidationError.Create(msg);
  532. end;
  533. end;
  534. { Range }
  535. constructor Range.Create(aMin, aMax: Integer; const aErrorMsg : string = '');
  536. begin
  537. fRangeMin := aMin;
  538. fRangeMax := aMax;
  539. fErrorMsg := aErrorMsg;
  540. end;
  541. constructor Range.Create(aMin, aMax: Double; const aErrorMsg: string);
  542. begin
  543. fRangeMin := aMin;
  544. fRangeMax := aMax;
  545. fErrorMsg := aErrorMsg;
  546. end;
  547. { StringLength }
  548. constructor StringLength.Create(aMaxLength: Integer; const aErrorMsg : string = '');
  549. begin
  550. fMaxLength := aMaxLength;
  551. fErrorMsg := aErrorMsg;
  552. end;
  553. { TOptionValue<T> }
  554. constructor TOptionValue<T>.Create(aValue: T);
  555. begin
  556. fValue := aValue;
  557. end;
  558. function TOptionValue<T>.GetSectionValue: T;
  559. begin
  560. Result := fValue;
  561. end;
  562. { TOptions<T> }
  563. function TOptions<T>.ConfigureOptions(aOptionsFunc: TConfigureOptionsProc<T>): IOptionsValidator;
  564. begin
  565. if Assigned(aOptionsFunc) then Result := fOptions.ConfigureOptions<T>(aOptionsFunc)
  566. else Result := TOptionsValidator.Create(fOptions);
  567. end;
  568. constructor TOptions<T>.Create(aOptions: T);
  569. begin
  570. fOptions := aOptions;
  571. end;
  572. { TOptionsBuilder<T> }
  573. constructor TOptionsBuilder<T>.Create;
  574. begin
  575. fOptions := (PTypeInfo(TypeInfo(T)).TypeData.ClassType.Create) as T;
  576. end;
  577. function TOptionsBuilder<T>.Options: T;
  578. begin
  579. Result := fOptions;
  580. end;
  581. end.