LCL: implemented TMask, MatchesMask, added Masks docs

git-svn-id: trunk@11592 -
This commit is contained in:
tombo 2007-07-22 14:17:13 +00:00
parent d53bbc8fee
commit 77381cc8d4
10 changed files with 1089 additions and 111 deletions

4
.gitattributes vendored
View File

@ -1051,6 +1051,7 @@ docs/xml/lcl/lmessages.xml svneol=LF#text/xml eol=lf
docs/xml/lcl/lresources.xml svneol=LF#text/xml eol=lf
docs/xml/lcl/maps.xml svneol=native#text/plain
docs/xml/lcl/maskedit.xml svneol=LF#text/xml eol=lf
docs/xml/lcl/masks.xml svneol=LF#text/xml eol=lf
docs/xml/lcl/menus.xml svneol=LF#text/xml eol=lf
docs/xml/lcl/pairsplitter.xml svneol=LF#text/xml eol=lf
docs/xml/lcl/postscriptcanvas.xml svneol=LF#text/xml eol=lf
@ -2845,6 +2846,7 @@ lcl/lmessages.pp svneol=native#text/pascal
lcl/lresources.pp svneol=native#text/pascal
lcl/maps.pp svneol=native#text/pascal
lcl/maskedit.pp svneol=native#text/pascal
lcl/masks.pas svneol=native#text/pascal
lcl/menus.pp svneol=native#text/pascal
lcl/nonwin32/README.txt svneol=native#text/plain
lcl/nonwin32/messages.pp svneol=native#text/pascal
@ -2890,6 +2892,8 @@ lcl/tests/test4_3listview.lpr svneol=native#text/plain
lcl/tests/test5_1asyncprocess.lpi svneol=native#text/plain
lcl/tests/test5_1asyncprocess.lpr svneol=native#text/plain
lcl/tests/test5_1worker.pas svneol=native#text/plain
lcl/tests/test6_1masks.lpi svneol=native#text/plain
lcl/tests/test6_1masks.lpr svneol=native#text/plain
lcl/textstrings.pas svneol=native#text/pascal
lcl/themes.pas svneol=native#text/pascal
lcl/tmschema.pas svneol=native#text/pascal

408
docs/xml/lcl/masks.xml Normal file
View File

@ -0,0 +1,408 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<fpdoc-descriptions>
<package name="lcl">
<!--
====================================================================
Masks
====================================================================
-->
<module name="Masks">
<short>This unit contains classes for mask matching.</short>
<descr>
</descr>
<!-- unresolved type reference Visibility: default -->
<element name="Classes">
<short></short>
<descr>
</descr>
<seealso>
</seealso>
</element>
<!-- unresolved type reference Visibility: default -->
<element name="SysUtils">
<short></short>
<descr>
</descr>
<seealso>
</seealso>
</element>
<!-- unresolved type reference Visibility: default -->
<element name="Contnrs">
<short></short>
<descr>
</descr>
<seealso>
</seealso>
</element>
<!-- enumeration type Visibility: default -->
<element name="TMaskCharType">
<short></short>
<descr>
</descr>
<seealso>
</seealso>
</element>
<!-- enumeration value Visibility: default -->
<element name="TMaskCharType.mcChar">
<short></short>
</element>
<!-- enumeration value Visibility: default -->
<element name="TMaskCharType.mcCharSet">
<short></short>
</element>
<!-- enumeration value Visibility: default -->
<element name="TMaskCharType.mcAnyChar">
<short></short>
</element>
<!-- enumeration value Visibility: default -->
<element name="TMaskCharType.mcAnyText">
<short></short>
</element>
<!-- set type Visibility: default -->
<element name="TCharSet">
<short></short>
<descr>
</descr>
<seealso>
</seealso>
</element>
<!-- pointer type Visibility: default -->
<element name="PCharSet">
<short></short>
<descr>
</descr>
<seealso>
</seealso>
</element>
<!-- record type Visibility: default -->
<element name="TMaskChar">
<short></short>
<descr>
</descr>
<seealso>
</seealso>
</element>
<!-- variable Visibility: default -->
<element name="TMaskChar.CharValue">
<short></short>
<descr>
</descr>
<seealso>
</seealso>
</element>
<!-- variable Visibility: default -->
<element name="TMaskChar.Negative">
<short></short>
<descr>
</descr>
<seealso>
</seealso>
</element>
<!-- variable Visibility: default -->
<element name="TMaskChar.SetValue">
<short></short>
<descr>
</descr>
<seealso>
</seealso>
</element>
<!-- record type Visibility: default -->
<element name="TMaskString">
<short></short>
<descr>
</descr>
<seealso>
</seealso>
</element>
<!-- variable Visibility: default -->
<element name="TMaskString.MinLength">
<short></short>
<descr>
</descr>
<seealso>
</seealso>
</element>
<!-- variable Visibility: default -->
<element name="TMaskString.MaxLength">
<short></short>
<descr>
</descr>
<seealso>
</seealso>
</element>
<!-- variable Visibility: default -->
<element name="TMaskString.Chars">
<short></short>
<descr>
</descr>
<seealso>
</seealso>
</element>
<!-- object Visibility: default -->
<element name="TMask">
<short>The TMask class represents a mask.</short>
<descr>
</descr>
<errors>
</errors>
<seealso>
</seealso>
</element>
<!-- variable Visibility: private -->
<element name="TMask.FMask">
<short></short>
<descr>
</descr>
<seealso>
</seealso>
</element>
<!-- constructor Visibility: public -->
<element name="TMask.Create">
<short>Creates new mask for matching.</short>
<descr><p>Creates new mask for matching. Matching is case-insensitive. The mask consist of literal elements such as:</p>
<ul>
<li>characters</li>
<li>character sets - each character set starts with "[" and ends with "]", contains list of single characters or range denoted with "-".</li>
<li>wild cards "?" for any character</li>
<li>wild cards "*" for any string</li>
</ul>
</descr>
<errors>
</errors>
<seealso>
</seealso>
</element>
<!-- argument Visibility: default -->
<element name="TMask.Create.AValue">
<short></short>
</element>
<!-- destructor Visibility: public -->
<element name="TMask.Destroy">
<short></short>
<descr>
</descr>
<errors>
</errors>
<seealso>
</seealso>
</element>
<!-- function Visibility: public -->
<element name="TMask.Matches">
<short>If the file name matches the mask.</short>
<descr>
</descr>
<errors>
</errors>
<seealso>
</seealso>
</element>
<!-- function result Visibility: default -->
<element name="TMask.Matches.Result">
<short></short>
</element>
<!-- argument Visibility: default -->
<element name="TMask.Matches.AFileName">
<short></short>
</element>
<!-- object Visibility: default -->
<element name="TMaskList">
<short>The TMaskList class represents a list of masks.</short>
<descr>
</descr>
<errors>
</errors>
<seealso>
<link id="TMask"/>
</seealso>
</element>
<!-- variable Visibility: private -->
<element name="TMaskList.FMasks">
<short></short>
<descr>
</descr>
<seealso>
</seealso>
</element>
<!-- function Visibility: private -->
<element name="TMaskList.GetItem">
<short></short>
<descr>
</descr>
<errors>
</errors>
<seealso>
</seealso>
</element>
<!-- function result Visibility: default -->
<element name="TMaskList.GetItem.Result">
<short></short>
</element>
<!-- argument Visibility: default -->
<element name="TMaskList.GetItem.Index">
<short></short>
</element>
<!-- constructor Visibility: public -->
<element name="TMaskList.Create">
<short>Creates new list of masks from passed value, each item is seperated by separator.</short>
<descr>
</descr>
<errors>
</errors>
<seealso>
<link id="TMask.Create"/>
</seealso>
</element>
<!-- argument Visibility: default -->
<element name="TMaskList.Create.AValue">
<short></short>
</element>
<!-- argument Visibility: default -->
<element name="TMaskList.Create.ASeparator">
<short></short>
</element>
<!-- destructor Visibility: public -->
<element name="TMaskList.Destroy">
<short></short>
<descr>
</descr>
<errors>
</errors>
<seealso>
</seealso>
</element>
<!-- function Visibility: public -->
<element name="TMaskList.Matches">
<short>If the file name matches at least one of mask list items.</short>
<descr>
</descr>
<errors>
</errors>
<seealso>
</seealso>
</element>
<!-- function result Visibility: default -->
<element name="TMaskList.Matches.Result">
<short></short>
</element>
<!-- argument Visibility: default -->
<element name="TMaskList.Matches.AFileName">
<short></short>
</element>
<!-- property Visibility: public -->
<element name="TMaskList.Items">
<short>The items of mask list.</short>
<descr>
</descr>
<seealso>
</seealso>
</element>
<!-- argument Visibility: default -->
<element name="TMaskList.Items.Index">
<short></short>
</element>
<!-- function Visibility: default -->
<element name="MatchesMask">
<short>If the file name matches the passed mask.</short>
<descr>
</descr>
<errors>
</errors>
<seealso>
<link id="TMask.Create"/>
</seealso>
</element>
<!-- function result Visibility: default -->
<element name="MatchesMask.Result">
<short></short>
</element>
<!-- argument Visibility: default -->
<element name="MatchesMask.FileName">
<short></short>
</element>
<!-- argument Visibility: default -->
<element name="MatchesMask.Mask">
<short></short>
</element>
<!-- function Visibility: default -->
<element name="MatchesMaskList">
<short>If the file name matches at least one of passed masks sepearated by separator.</short>
<descr>
</descr>
<errors>
</errors>
<seealso>
<link id="TMask.Create"/>
</seealso>
</element>
<!-- function result Visibility: default -->
<element name="MatchesMaskList.Result">
<short></short>
</element>
<!-- argument Visibility: default -->
<element name="MatchesMaskList.FileName">
<short></short>
</element>
<!-- argument Visibility: default -->
<element name="MatchesMaskList.Mask">
<short></short>
</element>
<!-- argument Visibility: default -->
<element name="MatchesMaskList.Separator">
<short></short>
</element>
</module> <!-- Masks -->
</package>
</fpdoc-descriptions>

View File

@ -35,7 +35,7 @@ interface
{$endif}
uses
Classes, SysUtils, StdCtrls, FileUtil;
Classes, SysUtils, StdCtrls, FileUtil, Masks;
Type
@ -162,7 +162,7 @@ begin
faAnyFile, Info)=0
then
Repeat
if FileInFilenameMasks(Info.Name,Mask) then begin
if MatchesMaskList(Info.Name,Mask) then begin
Added:=false;
AddFile(ftReadOnly,faReadOnly);
AddFile(ftHidden,faHidden);

View File

@ -78,7 +78,6 @@ function CreateAbsoluteSearchPath(const SearchPath, BaseDirectory: string): stri
function CreateRelativePath(const Filename, BaseDirectory: string): string;
function FileIsInPath(const Filename, Path: string): boolean;
function FileIsInDirectory(const Filename, Directory: string): boolean;
function FileInFilenameMasks(const Filename, Masks: string): boolean;
// file search
type

View File

@ -1046,111 +1046,6 @@ begin
and (CompareFilenames(ExpDir,LeftStr(ExpFile,p))=0);
end;
{------------------------------------------------------------------------------
function FileInFilenameMasks(const Filename, Masks: string): boolean;
Checks if 'Filename' fits to one of the mask in 'Masks'.
Note: It checks the whole Filename. So, for example /somewhere/unit1.pas does
not fit the mask 'unit*.pas', but it will fit '*.pas'.
Masks is delimited by semicolon.
Masks allows asterisk (*) for arbitrary text and question mark (?) for one
arbitrary character.
Examples:
'*.pas;*.pp;*.inc'
'*.tar.*'
'lazarus*.xpm'
------------------------------------------------------------------------------}
function FileInFilenameMasks(const Filename, Masks: string): boolean;
var
TrimmedFile: String;
MasksLen: Integer;
MaskStartPos: Integer;
MaskEndPos: Integer;
MaskPos: LongInt;
FilePos: Integer;
FileLen: Integer;
MaskChar: Char;
begin
Result:=false;
if (Filename='') or (Masks='') then exit;
TrimmedFile:=TrimFilename(Filename); // only cleanup, do not expand
if TrimmedFile='' then exit;
// try each Mask
MasksLen:=length(Masks);
FileLen:=length(TrimmedFile);
MaskEndPos:=1;
repeat
// find next Mask
MaskStartPos:=MaskEndPos;
while (MaskStartPos<=MasksLen) and (Masks[MaskStartPos]=';') do
inc(MaskStartPos);
if MaskStartPos>MasksLen then exit; // no mask -> end
MaskEndPos:=MaskStartPos+1;
while (MaskEndPos<=MasksLen) and (Masks[MaskEndPos]<>';') do
inc(MaskEndPos);
// check if mask fits to filename
MaskPos:=MaskStartPos;
FilePos:=1;
while MaskPos<MaskEndPos do begin
MaskChar:=Masks[MaskPos];
case MaskChar of
'?': begin
// skip one character
if FilePos>FileLen then break;
inc(FilePos);
inc(MaskPos);
end;
'*': begin
// Anything. Will be handled by the 'else' part below
inc(MaskPos);
if MaskPos=MaskEndPos then
FilePos:=FileLen+1;
end;
else
begin
if FilePos>FileLen then break;
{$IFDEF WINDOWS}
if UpperCaseTable[byte(MaskChar)]
=UpperCaseTable[byte(TrimmedFile[FilePos])] then
{$ELSE}
if MaskChar=TrimmedFile[FilePos] then
{$ENDIF}
begin
// character fits
inc(MaskPos);
inc(FilePos);
end else begin
//character does not fit
// -> go back to last astersik (*)
while (MaskPos>MaskStartPos) and (Masks[MaskPos-1]<>'*') do begin
dec(MaskPos);
dec(FilePos);
end;
if (MaskPos=MaskStartPos) then begin
// there was no asterisk (*) => the filename does not fit this mask
break;
end else begin
// there is an asterisk (*) => try the next position
inc(FilePos);
end;
end;
end;
end;
end;
if (MaskPos=MaskEndPos) and (FilePos>FileLen) then begin
// found
Result:=true;
exit;
end;
until false;
end;
{------------------------------------------------------------------------------
function CopyFile(const SrcFilename, DestFilename: string): boolean;
------------------------------------------------------------------------------}

View File

@ -30,7 +30,7 @@ interface
uses
Classes, SysUtils, LCLProc, FileUtil, LCLStrConsts, Dialogs,
LazConfigStorage, HelpIntfs;
LazConfigStorage, HelpIntfs, Masks;
type
{ THelpQueryItem }
@ -2279,7 +2279,7 @@ begin
end;
//debugln('THelpDBISourceDirectory.FileMatches FileMask="',FileMask,'"');
if (FileMask<>'')
and (not FileInFilenameMasks(ExtractFilename(AFilename),FileMask)) then exit;
and (not MatchesMaskList(ExtractFilename(AFilename),FileMask)) then exit;
//debugln('THelpDBISourceDirectory.FileMatches Success');
Result:=true;
end;

View File

@ -134,7 +134,7 @@ ResourceString
// StringHashList, LResource, Menus, ExtCtrls, ImgList, Spin
// StdCtrls, Calendar, CustomTimer, Forms, Grids, LCLProc, Controls, ComCtrls,
// ExtDlgs, EditBtn
// ExtDlgs, EditBtn, Masks
sInvalidActionRegistration = 'Invalid action registration';
sInvalidActionUnregistration = 'Invalid action unregistration';
sInvalidActionEnumeration = 'Invalid action enumeration';
@ -147,6 +147,7 @@ ResourceString
sInvalidImageSize = 'Invalid image size';
sDuplicateMenus = 'Duplicate menus';
sCannotFocus = 'Cannot focus a disabled or invisible window';
sInvalidCharSet = 'The char set in mask "%s" is not valid!';
rsListMustBeEmpty = 'List must be empty';
rsInvalidPropertyValue = 'Invalid property value';

373
lcl/masks.pas Normal file
View File

@ -0,0 +1,373 @@
{
/***************************************************************************
masks.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. *
* *
*****************************************************************************
}
unit Masks;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, Contnrs;
type
TMaskCharType = (mcChar, mcCharSet, mcAnyChar, mcAnyText);
TCharSet = set of Char;
PCharSet = ^TCharSet;
TMaskChar = record
case CharType: TMaskCharType of
mcChar: (CharValue: Char);
mcCharSet: (Negative: Boolean; SetValue: PCharSet);
mcAnyChar, mcAnyText: ();
end;
TMaskString = record
MinLength: Integer;
MaxLength: Integer;
Chars: Array of TMaskChar;
end;
{ TMask }
TMask = class
private
FMask: TMaskString;
public
constructor Create(const AValue: String);
destructor Destroy; override;
function Matches(const AFileName: String): Boolean;
end;
{ TMaskList }
TMaskList = class
private
FMasks: TObjectList;
function GetItem(Index: Integer): TMask;
public
constructor Create(const AValue: String; ASeparator: Char = ';');
destructor Destroy; override;
function Matches(const AFileName: String): Boolean;
property Items[Index: Integer]: TMask read GetItem;
end;
function MatchesMask(const FileName, Mask: String): Boolean;
function MatchesMaskList(const FileName, Mask: String; Separator: Char = ';'): Boolean;
implementation
uses LCLProc, LCLStrConsts;
function MatchesMask(const FileName, Mask: String): Boolean;
var
AMask: TMask;
begin
AMask := TMask.Create(Mask);
try
Result := AMask.Matches(FileName);
finally
AMask.Free;
end;
end;
function MatchesMaskList(const FileName, Mask: String; Separator: Char): Boolean;
var
AMaskList: TMaskList;
begin
AMaskList := TMaskList.Create(Mask, Separator);
try
Result := AMaskList.Matches(FileName);
finally
AMaskList.Free;
end;
end;
{ TMask }
constructor TMask.Create(const AValue: String);
var
I: Integer;
SkipAnyText: Boolean;
procedure CharSetError;
begin
raise EConvertError.CreateFmt(sInvalidCharSet, [AValue]);
end;
procedure AddAnyText;
begin
if SkipAnyText then
begin
Inc(I);
Exit;
end;
SetLength(FMask.Chars, Length(FMask.Chars) + 1);
FMask.Chars[High(FMask.Chars)].CharType := mcAnyText;
FMask.MaxLength := MaxInt;
SkipAnyText := True;
Inc(I);
end;
procedure AddAnyChar;
begin
SkipAnyText := False;
SetLength(FMask.Chars, Length(FMask.Chars) + 1);
FMask.Chars[High(FMask.Chars)].CharType := mcAnyChar;
Inc(FMask.MinLength);
if FMask.MaxLength < MaxInt then Inc(FMask.MaxLength);
Inc(I);
end;
procedure AddCharSet;
var
CharSet: TCharSet;
Valid: Boolean;
C, Last: Char;
begin
SkipAnyText := False;
SetLength(FMask.Chars, Length(FMask.Chars) + 1);
FMask.Chars[High(FMask.Chars)].CharType := mcCharSet;
Inc(I);
if (I <= Length(AValue)) and (AValue[I] = '!') then
begin
FMask.Chars[High(FMask.Chars)].Negative := True;
Inc(I);
end
else FMask.Chars[High(FMask.Chars)].Negative := False;
Last := '-';
CharSet := [];
Valid := False;
while I <= Length(AValue) do
begin
case AValue[I] of
'-':
begin
if Last = '-' then CharSetError;
Inc(I);
if (I > Length(AValue)) then CharSetError;
//DebugLn('Set: ' + Last + '-' + UpCase(AValue[I]));
for C := Last to UpCase(AValue[I]) do Include(CharSet, C);
Inc(I);
end;
']':
begin
Valid := True;
Break;
end;
else
begin
Last := UpCase(AValue[I]);
Include(CharSet, Last);
Inc(I);
end;
end;
end;
if (not Valid) or (CharSet = []) then CharSetError;
New(FMask.Chars[High(FMask.Chars)].SetValue);
FMask.Chars[High(FMask.Chars)].SetValue^ := CharSet;
Inc(FMask.MinLength);
if FMask.MaxLength < MaxInt then Inc(FMask.MaxLength);
Inc(I);
end;
procedure AddChar;
begin
SkipAnyText := False;
SetLength(FMask.Chars, Length(FMask.Chars) + 1);
with FMask.Chars[High(FMask.Chars)] do
begin
CharType := mcChar;
CharValue := UpCase(AValue[I]);
end;
Inc(FMask.MinLength);
if FMask.MaxLength < MaxInt then Inc(FMask.MaxLength);
Inc(I);
end;
begin
SetLength(FMask.Chars, 0);
FMask.MinLength := 0;
FMask.MaxLength := 0;
SkipAnyText := False;
I := 1;
while I <= Length(AValue) do
begin
case AValue[I] of
'*': AddAnyText;
'?': AddAnyChar;
'[': AddCharSet;
else AddChar;
end;
end;
end;
destructor TMask.Destroy;
var
I: Integer;
begin
for I := 0 to High(FMask.Chars) do
if FMask.Chars[I].CharType = mcCharSet then
Dispose(FMask.Chars[I].SetValue);
inherited Destroy;
end;
function TMask.Matches(const AFileName: String): Boolean;
var
L: Integer;
S: String;
function MatchToEnd(MaskIndex, CharIndex: Integer): Boolean;
var
I, J: Integer;
begin
Result := False;
for I := MaskIndex to High(FMask.Chars) do
begin
case FMask.Chars[I].CharType of
mcChar:
begin
if CharIndex > L then Exit;
//DebugLn('Match ' + S[CharIndex] + '<?>' + FMask.Chars[I].CharValue);
if S[CharIndex] <> FMask.Chars[I].CharValue then Exit;
Inc(CharIndex);
end;
mcCharSet:
begin
if CharIndex > L then Exit;
if FMask.Chars[I].Negative xor
(S[CharIndex] in FMask.Chars[I].SetValue^) then Inc(CharIndex)
else Exit;
end;
mcAnyChar:
begin
if CharIndex > L then Exit;
Inc(CharIndex);
end;
mcAnyText:
begin
if I = High(FMask.Chars) then
begin
Result := True;
Exit;
end;
for J := CharIndex to L do
if MatchToEnd(I + 1, J) then
begin
Result := True;
Exit;
end;
end;
end;
end;
Result := CharIndex > L;
end;
begin
Result := False;
L := Length(AFileName);
if L = 0 then
begin
if FMask.MinLength = 0 then Result := True;
Exit;
end;
if (L < FMask.MinLength) or (L > FMask.MaxLength) then Exit;
S := UpperCase(AFileName);
Result := MatchToEnd(0, 1);
end;
{ TMaskList }
function TMaskList.GetItem(Index: Integer): TMask;
begin
Result := TMask(FMasks.Items[Index]);
end;
constructor TMaskList.Create(const AValue: String; ASeparator: Char);
var
S: TStringList;
I: Integer;
begin
FMasks := TObjectList.Create(True);
S := TStringList.Create;
try
S.Delimiter := ASeparator;
S.DelimitedText := AValue;
for I := 0 to S.Count - 1 do
FMasks.Add(TMask.Create(S[I]));
finally
S.Free;
end;
end;
destructor TMaskList.Destroy;
begin
FMasks.Free;
inherited Destroy;
end;
function TMaskList.Matches(const AFileName: String): Boolean;
var
I: Integer;
begin
Result := False;
for I := 0 to FMasks.Count - 1 do
begin
if TMask(FMasks.Items[I]).Matches(AFileName) then
begin
Result := True;
Exit;
end;
end;
end;
end.

122
lcl/tests/test6_1masks.lpi Normal file
View File

@ -0,0 +1,122 @@
<?xml version="1.0"?>
<CONFIG>
<ProjectOptions>
<PathDelim Value="\"/>
<Version Value="5"/>
<General>
<MainUnit Value="0"/>
<IconPath Value="./"/>
<TargetFileExt Value=".exe"/>
<Title Value="fpcunitproject1"/>
<ActiveEditorIndexAtStart Value="0"/>
</General>
<VersionInfo>
<ProjectVersion Value=""/>
<Language Value=""/>
<CharSet Value=""/>
</VersionInfo>
<PublishOptions>
<Version Value="2"/>
<IgnoreBinaries Value="False"/>
<IncludeFileFilter Value="*.(pas|pp|inc|lfm|lpr|lrs|lpi|lpk|sh|xml)"/>
<ExcludeFileFilter Value="*.(bak|ppu|ppw|o|so);*~;backup"/>
</PublishOptions>
<RunParams>
<local>
<FormatVersion Value="1"/>
<LaunchingApplication PathPlusParams="/usr/X11R6/bin/xterm -T 'Lazarus Run Output' -e $(LazarusDir)/tools/runwait.sh $(TargetCmdLine)"/>
</local>
</RunParams>
<RequiredPackages Count="3">
<Item1>
<PackageName Value="LCL"/>
</Item1>
<Item2>
<PackageName Value="FPCUnitTestRunner"/>
</Item2>
<Item3>
<PackageName Value="FCL"/>
</Item3>
</RequiredPackages>
<Units Count="7">
<Unit0>
<Filename Value="test6_1masks.lpr"/>
<IsPartOfProject Value="True"/>
<UnitName Value="test6_1masks"/>
<CursorPos X="21" Y="1"/>
<TopLine Value="1"/>
<EditorIndex Value="0"/>
<UsageCount Value="20"/>
<Loaded Value="True"/>
</Unit0>
<Unit1>
<Filename Value="..\..\..\test\TMask\testcase1.pas"/>
<IsPartOfProject Value="True"/>
<UnitName Value="TestCase1"/>
<CursorPos X="1" Y="7"/>
<TopLine Value="1"/>
<UsageCount Value="20"/>
</Unit1>
<Unit2>
<Filename Value="..\..\..\test\TMask\masks.pas"/>
<IsPartOfProject Value="True"/>
<UnitName Value="Masks"/>
<CursorPos X="1" Y="67"/>
<TopLine Value="83"/>
<UsageCount Value="20"/>
</Unit2>
<Unit3>
<Filename Value="..\..\..\fpcbeta\fpcsrc\packages\fcl-fpcunit\src\fpcunit.pp"/>
<UnitName Value="fpcunit"/>
<CursorPos X="3" Y="57"/>
<TopLine Value="43"/>
<UsageCount Value="10"/>
</Unit3>
<Unit4>
<Filename Value="..\..\..\fpcbeta\fpcsrc\rtl\objpas\sysutils\sysutilh.inc"/>
<CursorPos X="48" Y="132"/>
<TopLine Value="122"/>
<UsageCount Value="10"/>
</Unit4>
<Unit5>
<Filename Value="..\..\components\fpcunit\guitestrunner.pas"/>
<ComponentName Value="GUITestRunner"/>
<HasResources Value="True"/>
<UnitName Value="GuiTestRunner"/>
<CursorPos X="8" Y="39"/>
<TopLine Value="25"/>
<UsageCount Value="10"/>
</Unit5>
<Unit6>
<Filename Value="..\masks.pas"/>
<UnitName Value="Masks"/>
<CursorPos X="1" Y="83"/>
<TopLine Value="69"/>
<EditorIndex Value="1"/>
<UsageCount Value="10"/>
<Loaded Value="True"/>
</Unit6>
</Units>
<JumpHistory Count="0" HistoryIndex="-1"/>
</ProjectOptions>
<CompilerOptions>
<Version Value="5"/>
<PathDelim Value="\"/>
<CodeGeneration>
<Generate Value="Faster"/>
</CodeGeneration>
<Other>
<CompilerPath Value="$(CompPath)"/>
</Other>
</CompilerOptions>
<Debugging>
<Exceptions Count="2">
<Item1>
<Name Value="ECodetoolError"/>
</Item1>
<Item2>
<Name Value="EFOpenError"/>
</Item2>
</Exceptions>
</Debugging>
</CONFIG>

176
lcl/tests/test6_1masks.lpr Normal file
View File

@ -0,0 +1,176 @@
{
*****************************************************************************
* *
* This file is part of the Lazarus Component Library (LCL) *
* *
* See the file COPYING.LCL, 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. *
* *
*****************************************************************************
LCL Test 6_1
Mask creating and matching test.
}
program test6_1masks;
{$mode objfpc}{$H+}
uses
Interfaces, Forms, GuiTestRunner, Masks,
Classes, SysUtils, fpcunit, testutils, testregistry;
type
{ TTestMask }
TTestMask = class(TTestCase)
private
FS, FMask: String;
procedure Test;
protected
procedure TestMask(const S, Mask: String; Result: Boolean);
procedure TestMaskException(const S, Mask: String; AFail: Boolean);
published
procedure TestNil;
procedure TestAnyText;
procedure TestAnyChar;
procedure TestCharSet;
procedure TestMaskSyntax;
end;
procedure TTestMask.Test;
begin
MatchesMask(FS, FMask);
end;
procedure TTestMask.TestMask(const S, Mask: String; Result: Boolean);
begin
AssertEquals(S + ' match ' + Mask + ': ', Result, MatchesMask(S, Mask));
end;
procedure TTestMask.TestMaskException(const S, Mask: String; AFail: Boolean);
begin
FS := S;
FMask := Mask;
if AFail then
AssertException('Invalid syntax: ' + S + ' match ' + Mask + ': ', EConvertError, @Test)
else
try
Test;
except
Fail('Invalid syntax: ' + S + ' match ' + Mask);
end;
end;
procedure TTestMask.TestMaskSyntax;
begin
TestMaskException('', '', False);
TestMaskException('', 'a', False);
TestMaskException('', '?', False);
TestMaskException('', '*', False);
TestMaskException('', '[a]', False);
TestMaskException('', '[a-b]', False);
TestMaskException('', '[!a-b]', False);
TestMaskException('', '[abc]', False);
TestMaskException('', '[abc-fgh]', False);
TestMaskException('', '[a------h]', False);
TestMaskException('', '**', False);
TestMaskException('', 'aa', False);
TestMaskException('', 'a*', False);
TestMaskException('', '*a', False);
TestMaskException('', '*?', False);
TestMaskException('', '[', True);
TestMaskException('', '[a', True);
TestMaskException('', '[]', True);
TestMaskException('', '[!]', True);
TestMaskException('', '[-]', True);
TestMaskException('', '[a-]', True);
TestMaskException('', '[-a]', True);
TestMaskException('', '[--a]', True);
end;
procedure TTestMask.TestNil;
begin
TestMask('', '', True);
TestMask('', '*', True);
TestMask('', '?', False);
TestMask('', 'a', False);
TestMask('', '[a]', False);
end;
procedure TTestMask.TestAnyText;
begin
TestMask('abc', '*', True);
TestMask('abc', 'a*', True);
TestMask('abc', '*c', True);
TestMask('abc', '*a*', True);
TestMask('abc', '*b*', True);
TestMask('abc', '*c*', True);
TestMask('abc', 'a*c', True);
TestMask('abc', '*bc', True);
TestMask('abc', 'ab*', True);
TestMask('abcde', '*', True);
TestMask('abcde', 'a*e', True);
TestMask('abcde', 'a*b*e', True);
TestMask('abcde', 'a*d*e', True);
TestMask('abcde', 'a*c*e', True);
TestMask('abcde', 'a*b*e', True);
TestMask('abc', '*b', False);
TestMask('abc', 'b*', False);
TestMask('abc', '*a', False);
TestMask('abc', 'c*', False);
TestMask('abc', 'ab*d', False);
TestMask('abcde', 'a*d', False);
TestMask('abcde', 'a*c*d', False);
TestMask('abcde', 'b*d*e', False);
end;
procedure TTestMask.TestAnyChar;
begin
TestMask('abc', '?bc', True);
TestMask('abc', '?b?', True);
TestMask('abc', '???', True);
TestMask('abc', '?*?', True);
TestMask('abc', '?*??', True);
TestMask('abc', '?*?*?', True);
TestMask('abc', 'a?', False);
TestMask('abc', 'abc?', False);
TestMask('abc', '?abc', False);
TestMask('abc', '??*??', False);
TestMask('abc', '?*?*??', False);
end;
procedure TTestMask.TestCharSet;
begin
TestMask('c', '[c]', True);
TestMask('c', '[!b]', True);
TestMask('c', '[a-c]', True);
TestMask('c', '[a-d]', True);
TestMask('c', '[!a-b]', True);
TestMask('c', '[abc]', True);
TestMask('c', '[a]', False);
TestMask('c', '[!c]', False);
TestMask('c', '[a-b]', False);
TestMask('c', '[abd]', False);
end;
begin
RegisterTest(TTestMask);
Application.Initialize;
Application.CreateForm(TGuiTestRunner, TestRunner);
Application.Run;
end.