123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599 |
- {
- /***************************************************************************
- maskutils.pas
- ---------
- ***************************************************************************/
- *****************************************************************************
- * *
- * This file is part of the Lazarus Component Library (LCL) *
- * *
- * See the file COPYING.modifiedLGPL, included in this distribution, *
- * for details about the copyright. *
- * *
- * This program is distributed in the hope that it will be useful, *
- * but WITHOUT ANY WARRANTY; without even the implied warranty of *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
- * *
- *****************************************************************************
-
- Author: Boguslaw Brandys
-
- Abstract:
- FormatMaskText implementation
-
- }
- unit maskutils;
- {$mode objfpc}{$H+}
- {.$define DebugMaskUtils}
- interface
- uses
- Classes
- ,SysUtils
- {$ifdef DebugMaskUtils}
- ,lclproc
- {$endif};
- function FormatMaskText(const EditMask: string; const Value: string): string;
- function FormatMaskInput(const EditMask: string): string;
- function MaskDoFormatText(const EditMask: string; const Value: string; Blank: Char): string;
- type
- TStepState =
- (
- stLeading, //? not used currently
- stUpper, //use uppercase
- stLower, //use lowercase
- stSpecial, //use escape character
- stArbitrary //put arbitrary character
- );
- TParseState = set of TStepState;
- { TMaskUtils }
- type
- TMaskUtils = class(TObject)
- private
- FValue: string;
- SourcePosition,Position : Integer;
- FEditMask,FMask : string;
- SourceVal,ExitVal : string;
- FMatched : Boolean;
- FMissChar : Char;
- State : TParseState;
- procedure EvaluateExit;
- procedure EvaluateMissing;
- procedure DoFillRest;
- procedure DoLiteral;
- procedure DoLiteralInputMask;
- procedure DoToken;
- procedure DoTokenInputMask;
- procedure DoUpper;
- procedure DoLower;
- procedure DoNumeric(Required : Boolean);
- procedure DoAlpha(Required : Boolean);
- procedure DoAlphaNumeric(Required : Boolean);
- procedure DoNumericPlusMinus;
- procedure DoArbitrary(Required : Boolean);
- procedure DoTime;
- procedure DoDate;
- function GetInputMask: string;
- procedure SetMask(const AValue: string);
- procedure SetValue(const AValue: string);
- protected
- procedure RaiseError;
- procedure ExtractMask;
- function MaskPtr : Char;
- function SourcePtr : Char;
- property Matched: Boolean read FMatched write FMatched;
- property MissChar: Char read FMissChar write FMissChar;
- public
- function ValidateInput : string;
- property Mask : string read FEditMask write SetMask;
- property Value : string read FValue write SetValue;
- property InputMask : string read GetInputMask;
- end;
- implementation
- resourcestring
- //exInvalidMaskValue = 'Input mask value incorrect';
- exInvalidMaskValue = 'FormatMaskText function failed!';
- //replace above text when all bugs will be fixed!
- function IsNumeric(const C : Char) : Boolean;
- begin
- Result := C In ['0'..'9'];
- end;
- function IsAlpha(const C : Char) : Boolean;
- begin
- //Fix it later if better way is possible
- Result := AnsiUpperCase(C) <> AnsiLowerCase(C);
- end;
- function IsToken(const C : Char) : Boolean;
- begin
- Result := C In ['!','>','<','\','L','l','A','a','C','c','0','9','#',':',
- '/',';'];
- end;
- { TMaskUtils }
- procedure TMaskUtils.SetMask(const AValue: string);
- begin
- if FEditMask = AValue then Exit;
- FEditMask := AValue;
- ExtractMask;
- end;
- procedure TMaskUtils.SetValue(const AValue: string);
- begin
- if SourceVal=AValue then exit;
- SourceVal := AValue;
- end;
- function TMaskUtils.ValidateInput : string;
- begin
- {Prepare}
- ExitVal := '';
- Position := 1;
- SourcePosition := 1;
- State := [];
- {Process}
- while (Position <= Length(FMask)) do
- begin
- if (IsToken(MaskPtr) and not (stSpecial In State)) then
- DoToken
- else
- DoLiteral;
- Inc(Position);
- end;
- DoFillRest;
- Result := ExitVal;
- end;
- procedure TMaskUtils.EvaluateMissing;
- begin
- ExitVal := ExitVal + MissChar;
- Inc(SourcePosition);
- end;
- procedure TMaskUtils.RaiseError;inline;
- begin
- if SourcePosition > Length(SourceVal) then
- EvaluateMissing
- else
- raise Exception.CreateFmtHelp(exInvalidMaskValue,[],Position);
- end;
- function TMaskUtils.MaskPtr : Char;
- begin
- Result := FMask[Position];
- end;
- function TMaskUtils.SourcePtr : Char;
- begin
- if SourcePosition <= Length(SourceVal) then
- Result := SourceVal[SourcePosition]
- else Result := #0;
- end;
- {Extract mask from input parameter}
- procedure TMaskUtils.ExtractMask;
- var
- P : Integer;
- s : string;
- begin
- { TODO: Implement clear, UTF8 compliant parsing ? }
- MissChar := #32;
- Matched := false;
- s := Copy(FEditMask,1,Length(FEditMask));
- P := LastDelimiter(';',s);
- if P = 0 then FMask := s
- else
- begin
- MissChar := PChar(Copy(s,P+1,1))^;
- Delete(s,P,2);
- P := LastDelimiter(';',s);
- Matched := (Copy(s,P+1,1) <> '0');
- Delete(s,P,2);
- FMask := s;
- end;
- end;
- procedure TMaskUtils.EvaluateExit;
- begin
- if stUpper in State then
- ExitVal := ExitVal + UpperCase(SourcePtr)
- else
- if stLower in State then
- ExitVal := ExitVal + LowerCase(SourcePtr)
- else
- ExitVal := ExitVal + SourcePtr;
- Inc(SourcePosition);
- end;
- procedure TMaskUtils.DoUpper;
- begin
- {$ifdef DebugMaskUtils}
- DebugLn(['DoUpper',',Position=',Position]);
- {$endif}
- if stLower in State then
- Exclude(State,stLower)
- else
- Include(State,stUpper);
- {Ugly check for '<>' sequence. Is that required ?}
- if (Position > 1) and (FMask[Position-1] = '<') then
- begin
- Exclude(State,stLower);
- Exclude(State,stUpper);
- end;
- end;
- procedure TMaskUtils.DoLower;
- begin
- {$ifdef DebugMaskUtils}
- DebugLn(['DoLower',',Position=',Position]);
- {$endif}
- if stUpper in State then
- Exclude(State,stUpper)
- else
- Include(State,stLower);
- end;
- procedure TMaskUtils.DoAlphaNumeric(Required : Boolean);
- begin
- {$ifdef DebugMaskUtils}
- DebugLn(['DoAlphaNumeric',',Position=',Position]);
- {$endif}
- if Required then
- begin
- if (IsAlpha(SourcePtr) or IsNumeric(SourcePtr)) then EvaluateExit
- else
- RaiseError;
- end
- else
- begin
- if (IsAlpha(SourcePtr) or IsNumeric(SourcePtr)) then EvaluateExit
- else
- EvaluateMissing;
- end;
- end;
- procedure TMaskUtils.DoArbitrary(Required : Boolean);
- begin
- {$ifdef DebugMaskUtils}
- DebugLn(['DoArbitrary',',Position=',Position]);
- {$endif}
- Include(State,stArbitrary);
- if Required then
- begin
- if Position > Length(SourceVal) then RaiseError;
- end
- else
- begin
- if Position > Length(SourceVal) then EvaluateMissing
- else
- EvaluateExit;
- end;
- end;
- procedure TMaskUtils.DoNumeric(Required : Boolean);
- begin
- {$ifdef DebugMaskUtils}
- DebugLn(['DoNumeric',',Position=',Position]);
- {$endif}
- if Required then
- begin
- if IsNumeric(SourcePtr) then EvaluateExit
- else
- RaiseError;
- end
- else
- begin
- if IsNumeric(SourcePtr) then EvaluateExit
- else
- EvaluateMissing;
- end;
- end;
- procedure TMaskUtils.DoNumericPlusMinus;
- begin
- {$ifdef DebugMaskUtils}
- DebugLn(['DoNumericPlusMinus',',Position=',Position]);
- {$endif}
- if (IsNumeric(SourcePtr)) or
- (SourcePtr = '+') or
- (SourcePtr = '-') then
- EvaluateExit
- else
- EvaluateMissing;
- end;
- procedure TMaskUtils.DoTime;
- begin
- {$ifdef DebugMaskUtils}
- DebugLn(['DoTime',',Position=',Position]);
- {$endif}
- ExitVal := ExitVal + TimeSeparator;
- end;
- procedure TMaskUtils.DoDate;
- begin
- {$ifdef DebugMaskUtils}
- DebugLn(['DoDate',',Position=',Position]);
- {$endif}
- ExitVal := ExitVal + DateSeparator;
- end;
- function TMaskUtils.GetInputMask: string;
- begin
- {Prepare}
- ExitVal := '';
- Position := 1;
- State := [];
- {Process}
- while (Position <= Length(FMask)) do
- begin
- if (IsToken(MaskPtr) and not (stSpecial In State)) then
- DoTokenInputMask
- else
- DoLiteralInputMask;
- Inc(Position);
- end;
- Result := ExitVal;
- end;
- procedure TMaskUtils.DoAlpha(Required : Boolean);
- begin
- {$ifdef DebugMaskUtils}
- DebugLn(['DoAlpha',',Position=',Position]);
- {$endif}
- if Required then
- begin
- if IsAlpha(SourcePtr) then
- EvaluateExit
- else
- RaiseError;
- end
- else
- begin
- if IsAlpha(SourcePtr) then
- EvaluateExit
- else
- EvaluateMissing;
- end;
- end;
- procedure TMaskUtils.DoToken;
- begin
- if stArbitrary in State then Exclude(State,stArbitrary);
- case MaskPtr of
- '!' : Include(State,stLeading);
- '>' : DoUpper;
- '<' : DoLower;
- '\' : Include(State,stSpecial);
- 'L' : DoAlpha(true);
- 'l' : DoAlpha(false);
- 'A' : DoAlphaNumeric(true);
- 'a' : DoAlphaNumeric(false);
- 'C' : DoArbitrary(true);
- 'c' : DoArbitrary(false);
- '0' : DoNumeric(true);
- '9' : DoNumeric(false);
- '#' : DoNumericPlusMinus;
- ':' : DoTime;
- '/' : DoDate;
- end;
- end;
- procedure TMaskUtils.DoTokenInputMask;
- begin
- case MaskPtr of
- '!',
- '>',
- '<' : ;{nothing}
- '\' : Include(State,stSpecial);
- 'L',
- 'l',
- 'A',
- 'a',
- 'C',
- 'c',
- '0',
- '9',
- '#' : ExitVal := ExitVal + MissChar;
- ':' : DoTime;
- '/' : DoDate;
- end;
- end;
- procedure TMaskUtils.DoLiteral;
- begin
- {$ifdef DebugMaskUtils}
- DebugLn(['DoLiteral',',Position=',Position]);
- {$endif}
- if stSpecial in State then
- Exclude(State,stSpecial);
- if Matched and (MaskPtr <> SourcePtr) then
- RaiseError;
- if Matched or not (IsAlpha(SourcePtr) or IsNumeric(SourcePtr)) then
- Inc(SourcePosition);
- ExitVal := ExitVal + MaskPtr;
- end;
- procedure TMaskUtils.DoLiteralInputMask;
- begin
- if stSpecial in State then
- Exclude(State,stSpecial);
- ExitVal := ExitVal + MaskPtr;
- end;
- procedure TMaskUtils.DoFillRest;
- var
- i : Integer;
- begin
- {Fill rest of exit value because source is longer then mask
- and the last mask character permit arbitrary char.
- Compatibility with delphi}
- if (stArbitrary in State) then
- begin
- i := Length(SourceVal) - Length(FMask);
- while i >= 0 do
- begin
- EvaluateExit;
- Dec(i);
- end;
- end;
- end;
- function FormatMaskText(const EditMask: string; const Value: string): string;
- var
- msk : TMaskUtils;
- begin
- Result := '';
- msk := TMaskUtils.Create;
- try
- msk.Mask := EditMask;
- msk.Value := Value;
- Result := msk.ValidateInput;
- finally
- msk.Free;
- end;
- end;
- {Returns preprocessed mask (without escape characters, with currect locale date
- and time separators) }
- function FormatMaskInput(const EditMask: string): string;
- var
- msk : TMaskUtils;
- begin
- Result := '';
- msk := TMaskUtils.Create;
- try
- msk.Mask := EditMask;
- Result := msk.InputMask;
- finally
- msk.Free;
- end;
- end;
- {
- Format Value string using EditMask, dont use 2d and 3d fields of EditMask,
- set own Blank char and Matched = False
- }
- function MaskDoFormatText(const EditMask: string; const Value: string; Blank: Char): string;
- var
- msk : TMaskUtils;
- begin
- Result := '';
- msk := TMaskUtils.Create;
- try
- msk.Mask := EditMask;
- msk.Value := Value;
- msk.Matched := False;
- msk.MissChar := Blank;
- Result := msk.ValidateInput;
- finally
- msk.Free;
- end;
- end;
- end.
|