maskutils.pp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576
  1. {
  2. /***************************************************************************
  3. maskutils.pas
  4. ---------
  5. ***************************************************************************/
  6. *****************************************************************************
  7. * *
  8. * This file is part of the Lazarus Component Library (LCL) *
  9. * *
  10. * See the file COPYING.modifiedLGPL, included in this distribution, *
  11. * for details about the copyright. *
  12. * *
  13. * This program is distributed in the hope that it will be useful, *
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of *
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
  16. * *
  17. *****************************************************************************
  18. Author: Boguslaw Brandys
  19. Abstract:
  20. FormatMaskText implementation
  21. }
  22. unit maskutils;
  23. {$mode objfpc}{$H+}
  24. {.$define DebugMaskUtils}
  25. interface
  26. uses
  27. Classes
  28. ,SysUtils
  29. {$ifdef DebugMaskUtils}
  30. ,lclproc
  31. {$endif};
  32. function FormatMaskText(const EditMask: string; const Value: string): string;
  33. function FormatMaskInput(const EditMask: string): string;
  34. type
  35. TStepState =
  36. (
  37. stLeading, //? not used currently
  38. stUpper, //use uppercase
  39. stLower, //use lowercase
  40. stSpecial, //use escape character
  41. stArbitrary //put arbitrary character
  42. );
  43. TParseState = set of TStepState;
  44. { TMaskUtils }
  45. type
  46. TMaskUtils = class(TObject)
  47. private
  48. FValue: string;
  49. SourcePosition,Position : Integer;
  50. FEditMask,FMask : string;
  51. SourceVal,ExitVal : string;
  52. Matched : Boolean;
  53. MissChar : Char;
  54. State : TParseState;
  55. procedure EvaluateExit;
  56. procedure EvaluateMissing;
  57. procedure DoFillRest;
  58. procedure DoLiteral;
  59. procedure DoLiteralInputMask;
  60. procedure DoToken;
  61. procedure DoTokenInputMask;
  62. procedure DoUpper;
  63. procedure DoLower;
  64. procedure DoNumeric(Required : Boolean);
  65. procedure DoAlpha(Required : Boolean);
  66. procedure DoAlphaNumeric(Required : Boolean);
  67. procedure DoNumericPlusMinus;
  68. procedure DoArbitrary(Required : Boolean);
  69. procedure DoTime;
  70. procedure DoDate;
  71. function GetInputMask: string;
  72. procedure SetMask(const AValue: string);
  73. procedure SetValue(const AValue: string);
  74. protected
  75. procedure RaiseError;
  76. procedure ExtractMask;
  77. function MaskPtr : Char;
  78. function SourcePtr : Char;
  79. public
  80. function ValidateInput : string;
  81. property Mask : string read FEditMask write SetMask;
  82. property Value : string read FValue write SetValue;
  83. property InputMask : string read GetInputMask;
  84. end;
  85. implementation
  86. resourcestring
  87. //exInvalidMaskValue = 'Input mask value incorrect';
  88. exInvalidMaskValue = 'FormatMaskText function failed!';
  89. //replace above text when all bugs will be fixed!
  90. function IsNumeric(const C : Char) : Boolean;
  91. begin
  92. Result := C In ['0'..'9'];
  93. end;
  94. function IsAlpha(const C : Char) : Boolean;
  95. begin
  96. //Fix it later if better way is possible
  97. Result := AnsiUpperCase(C) <> AnsiLowerCase(C);
  98. end;
  99. function IsToken(const C : Char) : Boolean;
  100. begin
  101. Result := C In ['!','>','<','\','L','l','A','a','C','c','0','9','#',':',
  102. '/',';'];
  103. end;
  104. { TMaskUtils }
  105. procedure TMaskUtils.SetMask(const AValue: string);
  106. begin
  107. if FEditMask = AValue then Exit;
  108. FEditMask := AValue;
  109. ExtractMask;
  110. end;
  111. procedure TMaskUtils.SetValue(const AValue: string);
  112. begin
  113. if SourceVal=AValue then exit;
  114. SourceVal := AValue;
  115. end;
  116. function TMaskUtils.ValidateInput : string;
  117. begin
  118. {Prepare}
  119. ExitVal := '';
  120. Position := 1;
  121. SourcePosition := 1;
  122. State := [];
  123. {Process}
  124. while (Position <= Length(FMask)) do
  125. begin
  126. if (IsToken(MaskPtr) and not (stSpecial In State)) then
  127. DoToken
  128. else
  129. DoLiteral;
  130. Inc(Position);
  131. end;
  132. DoFillRest;
  133. Result := ExitVal;
  134. end;
  135. procedure TMaskUtils.EvaluateMissing;
  136. begin
  137. ExitVal := ExitVal + MissChar;
  138. Inc(SourcePosition);
  139. end;
  140. procedure TMaskUtils.RaiseError;inline;
  141. begin
  142. if SourcePosition > Length(SourceVal) then
  143. EvaluateMissing
  144. else
  145. raise Exception.CreateFmtHelp(exInvalidMaskValue,[],Position);
  146. end;
  147. function TMaskUtils.MaskPtr : Char;
  148. begin
  149. Result := FMask[Position];
  150. end;
  151. function TMaskUtils.SourcePtr : Char;
  152. begin
  153. if SourcePosition <= Length(SourceVal) then
  154. Result := SourceVal[SourcePosition]
  155. else Result := #0;
  156. end;
  157. {Extract mask from input parameter}
  158. procedure TMaskUtils.ExtractMask;
  159. var
  160. P : Integer;
  161. s : string;
  162. begin
  163. { TODO: Implement clear, UTF8 compliant parsing ? }
  164. MissChar := #32;
  165. Matched := false;
  166. s := Copy(FEditMask,1,Length(FEditMask));
  167. P := LastDelimiter(';',s);
  168. if P = 0 then FMask := s
  169. else
  170. begin
  171. MissChar := PChar(Copy(s,P+1,1))^;
  172. Delete(s,P,2);
  173. P := LastDelimiter(';',s);
  174. Matched := (Copy(s,P+1,1) <> '0');
  175. Delete(s,P,2);
  176. FMask := s;
  177. end;
  178. end;
  179. procedure TMaskUtils.EvaluateExit;
  180. begin
  181. if stUpper in State then
  182. ExitVal := ExitVal + UpperCase(SourcePtr)
  183. else
  184. if stLower in State then
  185. ExitVal := ExitVal + LowerCase(SourcePtr)
  186. else
  187. ExitVal := ExitVal + SourcePtr;
  188. Inc(SourcePosition);
  189. end;
  190. procedure TMaskUtils.DoUpper;
  191. begin
  192. {$ifdef DebugMaskUtils}
  193. DebugLn(['DoUpper',',Position=',Position]);
  194. {$endif}
  195. if stLower in State then
  196. Exclude(State,stLower)
  197. else
  198. Include(State,stUpper);
  199. {Ugly check for '<>' sequence. Is that required ?}
  200. if (Position > 1) and (FMask[Position-1] = '<') then
  201. begin
  202. Exclude(State,stLower);
  203. Exclude(State,stUpper);
  204. end;
  205. end;
  206. procedure TMaskUtils.DoLower;
  207. begin
  208. {$ifdef DebugMaskUtils}
  209. DebugLn(['DoLower',',Position=',Position]);
  210. {$endif}
  211. if stUpper in State then
  212. Exclude(State,stUpper)
  213. else
  214. Include(State,stLower);
  215. end;
  216. procedure TMaskUtils.DoAlphaNumeric(Required : Boolean);
  217. begin
  218. {$ifdef DebugMaskUtils}
  219. DebugLn(['DoAlphaNumeric',',Position=',Position]);
  220. {$endif}
  221. if Required then
  222. begin
  223. if (IsAlpha(SourcePtr) or IsNumeric(SourcePtr)) then EvaluateExit
  224. else
  225. RaiseError;
  226. end
  227. else
  228. begin
  229. if (IsAlpha(SourcePtr) or IsNumeric(SourcePtr)) then EvaluateExit
  230. else
  231. EvaluateMissing;
  232. end;
  233. end;
  234. procedure TMaskUtils.DoArbitrary(Required : Boolean);
  235. begin
  236. {$ifdef DebugMaskUtils}
  237. DebugLn(['DoArbitrary',',Position=',Position]);
  238. {$endif}
  239. Include(State,stArbitrary);
  240. if Required then
  241. begin
  242. if Position > Length(SourceVal) then RaiseError;
  243. end
  244. else
  245. begin
  246. if Position > Length(SourceVal) then EvaluateMissing
  247. else
  248. EvaluateExit;
  249. end;
  250. end;
  251. procedure TMaskUtils.DoNumeric(Required : Boolean);
  252. begin
  253. {$ifdef DebugMaskUtils}
  254. DebugLn(['DoNumeric',',Position=',Position]);
  255. {$endif}
  256. if Required then
  257. begin
  258. if IsNumeric(SourcePtr) then EvaluateExit
  259. else
  260. RaiseError;
  261. end
  262. else
  263. begin
  264. if IsNumeric(SourcePtr) then EvaluateExit
  265. else
  266. EvaluateMissing;
  267. end;
  268. end;
  269. procedure TMaskUtils.DoNumericPlusMinus;
  270. begin
  271. {$ifdef DebugMaskUtils}
  272. DebugLn(['DoNumericPlusMinus',',Position=',Position]);
  273. {$endif}
  274. if (IsNumeric(SourcePtr)) or
  275. (SourcePtr = '+') or
  276. (SourcePtr = '-') then
  277. EvaluateExit
  278. else
  279. EvaluateMissing;
  280. end;
  281. procedure TMaskUtils.DoTime;
  282. begin
  283. {$ifdef DebugMaskUtils}
  284. DebugLn(['DoTime',',Position=',Position]);
  285. {$endif}
  286. ExitVal := ExitVal + TimeSeparator;
  287. end;
  288. procedure TMaskUtils.DoDate;
  289. begin
  290. {$ifdef DebugMaskUtils}
  291. DebugLn(['DoDate',',Position=',Position]);
  292. {$endif}
  293. ExitVal := ExitVal + DateSeparator;
  294. end;
  295. function TMaskUtils.GetInputMask: string;
  296. begin
  297. {Prepare}
  298. ExitVal := '';
  299. Position := 1;
  300. State := [];
  301. {Process}
  302. while (Position <= Length(FMask)) do
  303. begin
  304. if (IsToken(MaskPtr) and not (stSpecial In State)) then
  305. DoTokenInputMask
  306. else
  307. DoLiteralInputMask;
  308. Inc(Position);
  309. end;
  310. Result := ExitVal;
  311. end;
  312. procedure TMaskUtils.DoAlpha(Required : Boolean);
  313. begin
  314. {$ifdef DebugMaskUtils}
  315. DebugLn(['DoAlpha',',Position=',Position]);
  316. {$endif}
  317. if Required then
  318. begin
  319. if IsAlpha(SourcePtr) then
  320. EvaluateExit
  321. else
  322. RaiseError;
  323. end
  324. else
  325. begin
  326. if IsAlpha(SourcePtr) then
  327. EvaluateExit
  328. else
  329. EvaluateMissing;
  330. end;
  331. end;
  332. procedure TMaskUtils.DoToken;
  333. begin
  334. if stArbitrary in State then Exclude(State,stArbitrary);
  335. case MaskPtr of
  336. '!' : Include(State,stLeading);
  337. '>' : DoUpper;
  338. '<' : DoLower;
  339. '\' : Include(State,stSpecial);
  340. 'L' : DoAlpha(true);
  341. 'l' : DoAlpha(false);
  342. 'A' : DoAlphaNumeric(true);
  343. 'a' : DoAlphaNumeric(false);
  344. 'C' : DoArbitrary(true);
  345. 'c' : DoArbitrary(false);
  346. '0' : DoNumeric(true);
  347. '9' : DoNumeric(false);
  348. '#' : DoNumericPlusMinus;
  349. ':' : DoTime;
  350. '/' : DoDate;
  351. end;
  352. end;
  353. procedure TMaskUtils.DoTokenInputMask;
  354. begin
  355. case MaskPtr of
  356. '!',
  357. '>',
  358. '<' : ;{nothing}
  359. '\' : Include(State,stSpecial);
  360. 'L',
  361. 'l',
  362. 'A',
  363. 'a',
  364. 'C',
  365. 'c',
  366. '0',
  367. '9',
  368. '#' : ExitVal := ExitVal + MissChar;
  369. ':' : DoTime;
  370. '/' : DoDate;
  371. end;
  372. end;
  373. procedure TMaskUtils.DoLiteral;
  374. begin
  375. {$ifdef DebugMaskUtils}
  376. DebugLn(['DoLiteral',',Position=',Position]);
  377. {$endif}
  378. if stSpecial in State then
  379. Exclude(State,stSpecial);
  380. if Matched and (MaskPtr <> SourcePtr) then
  381. RaiseError;
  382. if Matched or not (IsAlpha(SourcePtr) or IsNumeric(SourcePtr)) then
  383. Inc(SourcePosition);
  384. ExitVal := ExitVal + MaskPtr;
  385. end;
  386. procedure TMaskUtils.DoLiteralInputMask;
  387. begin
  388. if stSpecial in State then
  389. Exclude(State,stSpecial);
  390. ExitVal := ExitVal + MaskPtr;
  391. end;
  392. procedure TMaskUtils.DoFillRest;
  393. var
  394. i : Integer;
  395. begin
  396. {Fill rest of exit value because source is longer then mask
  397. and the last mask character permit arbitrary char.
  398. Compatibility with delphi}
  399. if (stArbitrary in State) then
  400. begin
  401. i := Length(SourceVal) - Length(FMask);
  402. while i >= 0 do
  403. begin
  404. EvaluateExit;
  405. Dec(i);
  406. end;
  407. end;
  408. end;
  409. function FormatMaskText(const EditMask: string; const Value: string): string;
  410. var
  411. msk : TMaskUtils;
  412. begin
  413. Result := '';
  414. msk := TMaskUtils.Create;
  415. try
  416. msk.Mask := EditMask;
  417. msk.Value := Value;
  418. Result := msk.ValidateInput;
  419. finally
  420. msk.Free;
  421. end;
  422. end;
  423. {Returns preprocessed mask (without escape characters, with currect locale date
  424. and time separators) }
  425. function FormatMaskInput(const EditMask: string): string;
  426. var
  427. msk : TMaskUtils;
  428. begin
  429. Result := '';
  430. msk := TMaskUtils.Create;
  431. try
  432. msk.Mask := EditMask;
  433. Result := msk.InputMask;
  434. finally
  435. msk.Free;
  436. end;
  437. end;
  438. end.