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

            DefaultNode      = new FileFilterNode(RootNode, "...");
            DefaultNode.Type = DefaultType;
        }
예제 #2
0
		/// <summary>
		/// Default constructor
		/// </summary>
		public FileFilter(FileFilterType DefaultType = FileFilterType.Exclude)
		{
			RootNode = new FileFilterNode(null, "");

			DefaultNode = new FileFilterNode(RootNode, "...");
			DefaultNode.Type = DefaultType;
		}
예제 #3
0
 /// <summary>
 /// Default constructor.
 /// </summary>
 public FileFilterNode(FileFilterNode InParent, string InPattern)
 {
     Parent     = InParent;
     Pattern    = InPattern;
     RuleNumber = -1;
     Type       = FileFilterType.Exclude;
 }
예제 #4
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);
        }
예제 #5
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);
        }
예제 #6
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);
        }
예제 #7
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);
        }
예제 #8
0
		/// <summary>
		/// Default constructor.
		/// </summary>
		public FileFilterNode(FileFilterNode InParent, string InPattern)
		{
			Parent = InParent;
			Pattern = InPattern;
			RuleNumber = -1;
			Type = FileFilterType.Exclude;
		}
예제 #9
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;
		}
예제 #10
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;
		}
예제 #11
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="bInclude">Whether to include or exclude files matching this rule</param>
		public void AddRule(string Pattern, FileFilterType Type)
		{
			string NormalizedPattern = Pattern.Replace('\\', '/');

			// We don't want a slash at the start, but if there was not one specified, it's not anchored to the root of the tree.
			if (NormalizedPattern.StartsWith("/"))
			{
				NormalizedPattern = NormalizedPattern.Substring(1);
			}
			else if(!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;
				}
			}
		}
예제 #12
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="bInclude">Whether to include or exclude files matching this rule</param>
        public void AddRule(string Pattern, FileFilterType Type)
        {
            string NormalizedPattern = Pattern.Replace('\\', '/');

            // We don't want a slash at the start, but if there was not one specified, it's not anchored to the root of the tree.
            if (NormalizedPattern.StartsWith("/"))
            {
                NormalizedPattern = NormalizedPattern.Substring(1);
            }
            else if (!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;
                }
            }
        }