{ *********************************************************************** }
{                                                                         }
{ Delphi Visual Component Library                                         }
{                                                                         }
{ Copyright (c) 1995-2004 Borland Software Corporation                    }
{                                                                         }
{ *********************************************************************** }

unit Borland.Vcl.Masks;

interface

uses System.Text.RegularExpressions, SysUtils;

type
  EMaskException = class(Exception);

  TMask = class
  private
    FMask: System.Text.RegularExpressions.RegEx;
  public
    constructor Create(const MaskValue: string);
    destructor Destroy; override;
    function Matches(const Filename: string): Boolean;
  end;

function MatchesMask(const Filename, Mask: string): Boolean;

implementation

uses
  StrUtils, RTLConsts;

procedure InvalidMask(const Mask: string);
begin
  raise EMaskException.Create(Format(SInvalidMask, [Mask]));
  // raise EMaskException.CreateResFmt(SInvalidMask, [Mask]);
end;

function ConvertMaskToRegularExpression(const Mask: string): string;
var
  I, CurPos, Len: Integer;

  procedure CheckPos(I, Len: Integer; const Mask: string);
  begin
    if I = Len - 1 then
      InvalidMask(Mask);
  end;

begin
  Result := '';
  CurPos := 0;
  Len := Length(Mask);
  while CurPos < Len do
  begin
    I := FindDelimiter('*?[', Mask, CurPos + 1) - 1; // Do not localize
    if I >= CurPos then
    begin
      if I > CurPos then
        Result := Result +
          System.Text.RegularExpressions.RegEx.Escape(Copy(Mask, CurPos + 1, I - CurPos));
      case Mask[I + 1] of         // add one because pascal strings are 1-offset
        '?' : Result := Result + '.';
        '*' : Result := Result + '.*';
        '[' :
          begin
            CheckPos(I, Len, Mask);
            if Mask[I + 2] = '!' then
            begin
              Result := Result + '[^';
              Inc(I);
              CheckPos(I, Len, Mask);
            end
            else
              Result := Result + '[';
            CurPos := I + 1;
            while Mask[I + 1] <> ']' do
            begin
              I := FindDelimiter(']-', Mask, CurPos + 1) - 1; // Do not localize
              if I < 0 then
                InvalidMask(Mask);
              Result := Result +
                System.Text.RegularExpressions.RegEx.Escape(Copy(Mask, CurPos + 1, I - CurPos));
              if Mask[I + 1] = '-' then
              begin
                CheckPos(I, Len, Mask);
                Result := Result + '-';
                CurPos := I + 1;
              end;
            end;
            Result := Result + ']';
          end;
      end;
      CurPos := I + 1;
    end
    else Break;
  end;
  if CurPos < Len then
    Result := Result +
      System.Text.RegularExpressions.RegEx.Escape(Copy(Mask, CurPos + 1, Len - CurPos));
end;

{ TMask }

constructor TMask.Create(const MaskValue: string);
begin
  inherited Create;
  FMask := System.Text.RegularExpressions.RegEx.Create(
    ConvertMaskToRegularExpression(MaskValue),
    System.Text.RegularExpressions.RegexOptions.IgnoreCase);
end;

destructor TMask.Destroy;
begin
  FMask.Free; // Finalize the mask
end;

function TMask.Matches(const Filename: string): Boolean;
var
  Match : System.Text.RegularExpressions.Match;
  Groups : System.Text.RegularExpressions.GroupCollection;
  Group : System.Text.RegularExpressions.Group;
  Capture : System.Text.RegularExpressions.Capture;
  I, J: Integer;
begin
  Result := False;
  Match := FMask.Match(FileName);
  Groups := Match.Groups;
  for I := 0 to Groups.Count - 1 do
  begin
    Group := Groups.get_Item(I);
    if Group.Success then
    begin
      for J := 0 to Group.Captures.Count - 1 do
      begin
        Capture := Group.Captures.Item[J];
        if SameText(Capture.Value, Filename) then
        begin
          Result := True;
          Exit;
        end;
      end;
    end;
  end;
end;

function MatchesMask(const Filename, Mask: string): Boolean;
var
  CMask: TMask;
begin
  CMask := TMask.Create(Mask);
  Result := CMask.Matches(Filename);
end;

end.
