Exemple #1
0
        /// <summary>
        /// Default constructor
        /// </summary>
        public FileFilter(FileFilterType DefaultType = FileFilterType.Exclude)
        {
            RootNode = new FileFilterNode(null, "");

            DefaultNode      = new FileFilterNode(RootNode, "...");
            DefaultNode.Type = DefaultType;
        }
Exemple #2
0
        /// <summary>
        /// Returns the highest possible rule number which can match the given list of input tokens, assuming that the list of input tokens is incomplete.
        /// </summary>
        /// <param name="CurrentNode">The current node being checked</param>
        /// <param name="Tokens">The tokens to match</param>
        /// <param name="TokenIdx">Current token index</param>
        /// <param name="CurrentBestRuleNumber">The highest rule number seen so far. Used to optimize tree traversals.</param>
        /// <returns>New highest rule number</returns>
        int HighestPossibleIncludeMatch(FileFilterNode CurrentNode, string[] Tokens, int TokenIdx, int CurrentBestRuleNumber)
        {
            // If we've matched all the input tokens, check if this rule is better than any other we've seen
            if (TokenIdx == Tokens.Length)
            {
                return(Math.Max(CurrentBestRuleNumber, CurrentNode.MaxIncludeRuleNumber));
            }

            // Test all the branches for one that matches
            int BestRuleNumber = CurrentBestRuleNumber;

            if (CurrentNode.MaxIncludeRuleNumber > BestRuleNumber)
            {
                foreach (FileFilterNode Branch in CurrentNode.Branches)
                {
                    if (Branch.Pattern == "...")
                    {
                        if (Branch.MaxIncludeRuleNumber > BestRuleNumber)
                        {
                            BestRuleNumber = Branch.MaxIncludeRuleNumber;
                        }
                    }
                    else
                    {
                        if (Branch.IsMatch(Tokens[TokenIdx]))
                        {
                            BestRuleNumber = HighestPossibleIncludeMatch(Branch, Tokens, TokenIdx + 1, BestRuleNumber);
                        }
                    }
                }
            }
            return(BestRuleNumber);
        }
Exemple #3
0
 /// <summary>
 /// Default constructor.
 /// </summary>
 public FileFilterNode(FileFilterNode InParent, string InPattern)
 {
     Parent     = InParent;
     Pattern    = InPattern;
     RuleNumber = -1;
     Type       = FileFilterType.Exclude;
 }
Exemple #4
0
        /// <summary>
        /// Determines whether it's possible for anything within the given folder name to match the filter. Useful to early out of recursive file searches.
        /// </summary>
        /// <param name="FolderName">File to match</param>
        /// <returns>True if the file passes the filter</returns>
        public bool PossiblyMatches(string FolderName)
        {
            string[] Tokens = FolderName.Trim('/', '\\').Split('/', '\\');

            FileFilterNode MatchingNode = FindMatchingNode(RootNode, Tokens.Union(new string[] { "" }).ToArray(), 0, DefaultNode);

            return(MatchingNode.Type == FileFilterType.Include || HighestPossibleIncludeMatch(RootNode, Tokens, 0, MatchingNode.RuleNumber) > MatchingNode.RuleNumber);
        }
Exemple #5
0
        /// <summary>
        /// Determines whether the given file matches the filter
        /// </summary>
        /// <param name="FileName">File to match</param>
        /// <returns>True if the file passes the filter</returns>
        public bool Matches(string FileName)
        {
            string[] Tokens = FileName.TrimStart('/', '\\').Split('/', '\\');

            FileFilterNode MatchingNode = FindMatchingNode(RootNode, Tokens, 0, DefaultNode);

            return(MatchingNode.Type == FileFilterType.Include);
        }
Exemple #6
0
        /// <summary>
        /// Finds the node which matches a given list of tokens.
        /// </summary>
        /// <param name="CurrentNode"></param>
        /// <param name="Tokens"></param>
        /// <param name="TokenIdx"></param>
        /// <param name="CurrentBestNode"></param>
        /// <returns></returns>
        FileFilterNode FindMatchingNode(FileFilterNode CurrentNode, string[] Tokens, int TokenIdx, FileFilterNode CurrentBestNode)
        {
            // If we've matched all the input tokens, check if this rule is better than any other we've seen
            if (TokenIdx == Tokens.Length)
            {
                return((CurrentNode.RuleNumber > CurrentBestNode.RuleNumber) ? CurrentNode : CurrentBestNode);
            }

            // If there is no rule under the current node which is better than the current best node, early out
            if (CurrentNode.MaxIncludeRuleNumber <= CurrentBestNode.RuleNumber && CurrentNode.MaxExcludeRuleNumber <= CurrentBestNode.RuleNumber)
            {
                return(CurrentBestNode);
            }

            // Test all the branches for one that matches
            FileFilterNode BestNode = CurrentBestNode;

            foreach (FileFilterNode Branch in CurrentNode.Branches)
            {
                if (Branch.Pattern == "...")
                {
                    for (int NextTokenIdx = Tokens.Length; NextTokenIdx >= TokenIdx; NextTokenIdx--)
                    {
                        BestNode = FindMatchingNode(Branch, Tokens, NextTokenIdx, BestNode);
                    }
                }
                else
                {
                    if (Branch.IsMatch(Tokens[TokenIdx]))
                    {
                        BestNode = FindMatchingNode(Branch, Tokens, TokenIdx + 1, BestNode);
                    }
                }
            }
            return(BestNode);
        }
Exemple #7
0
        /// <summary>
        /// Adds an include or exclude rule to the filter
        /// </summary>
        /// <param name="Pattern">The pattern which the rule should match</param>
        /// <param name="Type">Whether to include or exclude files matching this rule</param>
        public void AddRule(string Pattern, FileFilterType Type)
        {
            string NormalizedPattern = Pattern.Replace('\\', '/');

            // Remove the slash from the start of the pattern. Any exclude pattern that doesn't contain a directory separator is assumed to apply to any directory (eg. *.cpp), otherwise it's
            // taken relative to the root.
            if (NormalizedPattern.StartsWith("/"))
            {
                NormalizedPattern = NormalizedPattern.Substring(1);
            }
            else if (!NormalizedPattern.Contains("/") && !NormalizedPattern.StartsWith("..."))
            {
                NormalizedPattern = ".../" + NormalizedPattern;
            }

            // All directories indicate a wildcard match
            if (NormalizedPattern.EndsWith("/"))
            {
                NormalizedPattern += "...";
            }

            // Replace any directory wildcards mid-string
            for (int Idx = NormalizedPattern.IndexOf("..."); Idx != -1; Idx = NormalizedPattern.IndexOf("...", Idx))
            {
                if (Idx > 0 && NormalizedPattern[Idx - 1] != '/')
                {
                    NormalizedPattern = NormalizedPattern.Insert(Idx, "*/");
                    Idx++;
                }

                Idx += 3;

                if (Idx < NormalizedPattern.Length && NormalizedPattern[Idx] != '/')
                {
                    NormalizedPattern = NormalizedPattern.Insert(Idx, "/*");
                    Idx += 2;
                }
            }

            // Split the pattern into fragments
            string[] BranchPatterns = NormalizedPattern.Split('/');

            // Add it into the tree
            FileFilterNode LastNode = RootNode;

            foreach (string BranchPattern in BranchPatterns)
            {
                FileFilterNode NextNode = LastNode.Branches.FirstOrDefault(x => x.Pattern == BranchPattern);
                if (NextNode == null)
                {
                    NextNode = new FileFilterNode(LastNode, BranchPattern);
                    LastNode.Branches.Add(NextNode);
                }
                LastNode = NextNode;
            }

            // We've reached the end of the pattern, so mark it as a leaf node
            Rules.Add(LastNode);
            LastNode.RuleNumber = Rules.Count - 1;
            LastNode.Type       = Type;

            // Update the maximums along that path
            for (FileFilterNode UpdateNode = LastNode; UpdateNode != null; UpdateNode = UpdateNode.Parent)
            {
                if (Type == FileFilterType.Include)
                {
                    UpdateNode.MaxIncludeRuleNumber = LastNode.RuleNumber;
                }
                else
                {
                    UpdateNode.MaxExcludeRuleNumber = LastNode.RuleNumber;
                }
            }
        }