using System.Collections.Generic;
using System.IO;
namespace SabreTools.Matching
{
///
/// A set of content matches that work together
///
public class ContentMatchSet : IMatchSet
{
///
public List Matchers { get; }
///
public string SetName { get; }
///
/// Function to get a content version
///
///
/// A content version method takes the file path, the file contents,
/// and a list of found positions and returns a single string. That
/// string is either a version string, in which case it will be appended
/// to the match name, or `null`, in which case it will cause
/// the match name to be omitted.
///
public GetArrayVersion? GetArrayVersion { get; }
///
/// Function to get a content version
///
///
/// A content version method takes the file path, the file contents,
/// and a list of found positions and returns a single string. That
/// string is either a version string, in which case it will be appended
/// to the match name, or `null`, in which case it will cause
/// the match name to be omitted.
///
public GetStreamVersion? GetStreamVersion { get; }
#region Generic Constructors
///
/// Constructor
///
/// ContentMatch representing the comparisons
/// Unique name for the set
public ContentMatchSet(ContentMatch needle, string setName)
: this([needle], setName) { }
///
/// Constructor
///
/// List of ContentMatch objects representing the comparisons
/// Unique name for the set
///
/// Thrown if is empty.
///
public ContentMatchSet(List needles, string setName)
{
// Validate the inputs
if (needles.Count == 0)
throw new InvalidDataException(nameof(needles));
Matchers = needles;
SetName = setName;
GetArrayVersion = null;
GetStreamVersion = null;
}
#endregion
#region Array Constructors
///
/// Constructor
///
/// ContentMatch representing the comparisons
/// Delegate for deriving a version on match of an array
/// Unique name for the set
public ContentMatchSet(ContentMatch needle, GetArrayVersion getVersion, string setName)
: this([needle], getVersion, setName) { }
///
/// Constructor
///
/// List of ContentMatch objects representing the comparisons
/// Delegate for deriving a version on match of an array
/// Unique name for the set
///
/// Thrown if is empty.
///
public ContentMatchSet(List needles, GetArrayVersion getVersion, string setName)
{
// Validate the inputs
if (needles.Count == 0)
throw new InvalidDataException(nameof(needles));
Matchers = needles;
SetName = setName;
GetArrayVersion = getVersion;
GetStreamVersion = null;
}
#endregion
#region Stream Constructors
///
/// Constructor
///
/// ContentMatch representing the comparisons
/// Delegate for deriving a version on match of a Stream
/// Unique name for the set
public ContentMatchSet(ContentMatch needle, GetStreamVersion getVersion, string setName)
: this([needle], getVersion, setName) { }
///
/// Constructor
///
/// List of ContentMatch objects representing the comparisons
/// Delegate for deriving a version on match of a Stream
/// Unique name for the set
///
/// Thrown if is empty.
///
public ContentMatchSet(List needles, GetStreamVersion getVersion, string setName)
{
// Validate the inputs
if (needles.Count == 0)
throw new InvalidDataException(nameof(needles));
Matchers = needles;
SetName = setName;
GetArrayVersion = null;
GetStreamVersion = getVersion;
}
#endregion
#region Array Matching
///
/// Determine whether all content matches pass
///
/// Array to search
/// List of matching positions, if any
public List MatchesAll(byte[]? stack)
{
// If either set is null or empty
if (stack is null || stack.Length == 0 || Matchers.Count == 0)
return [];
// Initialize the position list
var positions = new List();
// Loop through all content matches and make sure all pass
foreach (var contentMatch in Matchers)
{
int position = contentMatch.Match(stack);
if (position < 0)
return [];
positions.Add(position);
}
return positions;
}
///
/// Determine whether any content matches pass
///
/// Array to search
/// First matching position on success, -1 on error
public int MatchesAny(byte[]? stack)
{
// If either set is null or empty
if (stack is null || stack.Length == 0 || Matchers.Count == 0)
return -1;
// Loop through all content matches and make sure all pass
foreach (var contentMatch in Matchers)
{
int position = contentMatch.Match(stack);
if (position >= 0)
return position;
}
return -1;
}
#endregion
#region Stream Matching
///
/// Determine whether all content matches pass
///
/// Stream to search
/// List of matching positions, if any
public List MatchesAll(Stream? stack)
{
// If either set is null or empty
if (stack is null || stack.Length == 0 || Matchers.Count == 0)
return [];
// Initialize the position list
var positions = new List();
// Loop through all content matches and make sure all pass
foreach (var contentMatch in Matchers)
{
int position = contentMatch.Match(stack);
if (position < 0)
return [];
positions.Add(position);
}
return positions;
}
///
/// Determine whether any content matches pass
///
/// Stream to search
/// First matching position on success, -1 on error
public int MatchesAny(Stream? stack)
{
// If either set is null or empty
if (stack is null || stack.Length == 0 || Matchers.Count == 0)
return -1;
// Loop through all content matches and make sure all pass
foreach (var contentMatch in Matchers)
{
int position = contentMatch.Match(stack);
if (position >= 0)
return position;
}
return -1;
}
#endregion
}
}