diff --git a/Sources/MpcNET/Commands/Database/FindAndSearchCommands.cs b/Sources/MpcNET/Commands/Database/FindAndSearchCommands.cs
index 26abfbb..23a06e3 100644
--- a/Sources/MpcNET/Commands/Database/FindAndSearchCommands.cs
+++ b/Sources/MpcNET/Commands/Database/FindAndSearchCommands.cs
@@ -7,10 +7,244 @@
namespace MpcNET.Commands.Database
{
using System.Collections.Generic;
+ using System.ComponentModel;
using System.Linq;
using MpcNET.Tags;
using MpcNET.Types;
+ ///
+ /// Operators available for filters in the protocol
+ ///
+ public enum FilterOperator
+ {
+ ///
+ /// Equal (==)
+ ///
+ [Description("==")]
+ Equal,
+ ///
+ /// Different (!=)
+ ///
+ [Description("!=")]
+ Different,
+ ///
+ /// Contains (contains)
+ ///
+ [Description("contains")]
+ Contains,
+ ///
+ /// Mask (=~)
+ ///
+ [Description("=~")]
+ Mask,
+ ///
+ /// None ("")
+ ///
+ [Description("")]
+ None,
+ }
+
+ ///
+ ///
+ public static class EnumHelper
+ {
+ ///
+ /// of the enum
+ ///
+ ///
+ public static string GetDescription(this T enumValue)
+ where T : struct, System.IConvertible
+ {
+ if (!typeof(T).IsEnum)
+ return null;
+
+ string description = enumValue.ToString();
+ System.Reflection.FieldInfo fieldInfo = enumValue.GetType().GetField(enumValue.ToString());
+
+ if (fieldInfo != null)
+ {
+ object[] attributes = fieldInfo.GetCustomAttributes(typeof(DescriptionAttribute), true);
+ if (attributes != null && attributes.Length > 0)
+ description = ((DescriptionAttribute)attributes[0]).Description;
+ }
+
+ return description;
+ }
+ }
+
+ ///
+ /// Filter interface specialized by filters in the protocol
+ ///
+ public abstract class IFilter
+ {
+ ///
+ /// Name of the filter
+ ///
+ public string Name;
+ ///
+ /// Operator of the filter
+ ///
+ public FilterOperator Operator;
+ ///
+ /// Value to test
+ ///
+ public string Value;
+ ///
+ /// If the expression is negated
+ ///
+ public bool Negation;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Name of the filter
+ /// Operator of the filter
+ /// Value to test
+ /// If the expression is negated
+ public IFilter(string Name, FilterOperator Operator, string Value, bool Negation = false)
+ {
+ this.Name = Name;
+ this.Operator = Operator;
+ this.Value = Value;
+ this.Negation = Negation;
+ }
+
+ ///
+ /// Gets the formatted command
+ ///
+ public string GetFormattedCommand()
+ {
+ string make = "";
+
+ if (Negation)
+ make += "(!";
+
+ make += "(" + Name + " ";
+ make += Operator.GetDescription();
+ make += " " + "\\\"" + Value + "\\\"" + ")";
+
+ if (Negation)
+ make += ")";
+
+ return make;
+ }
+ }
+
+ ///
+ /// Filter "file"
+ ///
+ public class FilterFile : IFilter
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Operator of the filter
+ /// Value to test
+ /// If the expression is negated
+ public FilterFile(string Value, FilterOperator Operator, bool Negation = false) : base("file", Operator, Value, Negation)
+ {
+ if (Operator != FilterOperator.Equal)
+ throw new System.ArgumentException("Operator is not compatible: for \"File\" use FilterOperator.Equal.");
+
+ this.Name = "file";
+ this.Value = Value;
+ this.Operator = Operator;
+ this.Negation = Negation;
+ }
+ }
+
+ ///
+ /// Filter "TAG"
+ ///
+ public class FilterTag : IFilter
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The tag.
+ /// Operator of the filter
+ /// Value to test
+ /// If the expression is negated
+ public FilterTag(ITag Tag, string Value, FilterOperator Operator, bool Negation = false) : base(Tag.Value, Operator, Value, Negation)
+ {
+ if (Operator != FilterOperator.Equal && Operator != FilterOperator.Different && Operator != FilterOperator.Contains)
+ throw new System.ArgumentException("Operator is not compatible: for \"TAG\" use FilterOperator.Equal, FilterOperator.Different or FilterOperator.Contains.");
+
+ this.Value = Value;
+ this.Operator = Operator;
+ this.Negation = Negation;
+ }
+ }
+
+ ///
+ /// Filter "base" (not the interface base class, the base command: restrict search songs to a directory)
+ ///
+ public class FilterBase : IFilter
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Operator of the filter
+ /// Value to test
+ /// If the expression is negated
+ public FilterBase(string Value, FilterOperator Operator, bool Negation = false) : base("base", Operator, Value, Negation)
+ {
+ if (Operator != FilterOperator.None)
+ throw new System.ArgumentException("Operator is not compatible: for \"base\" use FilterOperator.None.");
+
+ this.Name = "base";
+ this.Value = Value;
+ this.Operator = Operator;
+ this.Negation = Negation;
+ }
+ }
+
+ ///
+ /// Filter "modified-since"
+ ///
+ public class FilterModifiedSince : IFilter
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Operator of the filter
+ /// Value to test
+ /// If the expression is negated
+ public FilterModifiedSince(string Value, FilterOperator Operator, bool Negation = false) : base("modified-since", Operator, Value, Negation)
+ {
+ if (Operator != FilterOperator.None)
+ throw new System.ArgumentException("Operator is not compatible: for \"ModifiedSince\" use FilterOperator.None.");
+
+ this.Name = "modified-since";
+ this.Value = Value;
+ this.Operator = Operator;
+ this.Negation = Negation;
+ }
+ }
+
+ ///
+ /// Filter "AudioFormat"
+ ///
+ public class FilterAudioFormat : IFilter
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Operator of the filter
+ /// Value to test
+ /// If the expression is negated
+ public FilterAudioFormat(string Value, FilterOperator Operator, bool Negation = false) : base("AudioFormat", Operator, Value, Negation)
+ {
+ if (Operator != FilterOperator.Equal && Operator != FilterOperator.Mask)
+ throw new System.ArgumentException("Operator is not compatible: for \"AudioFormat\" use FilterOperator.Equal or FilterOperator.Mask.");
+
+ this.Name = "AudioFormat";
+ this.Value = Value;
+ this.Operator = Operator;
+ this.Negation = Negation;
+ }
+ }
+
///
/// Finds songs in the database that contain "searchText".
/// Since MPD 0.21, search syntax is now (TAG == 'VALUE').
@@ -44,6 +278,22 @@ namespace MpcNET.Commands.Database
/// End of the portion of the results desired
public SearchCommand(List> filters, int windowStart = -1, int windowEnd = -1) : base(filters, windowStart, windowEnd) { }
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Filter
+ /// Start of the portion of the results desired
+ /// End of the portion of the results desired
+ public SearchCommand(IFilter filter, int windowStart = -1, int windowEnd = -1) : base(filter, windowStart, windowEnd) { }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// List of filters
+ /// Start of the portion of the results desired
+ /// End of the portion of the results desired
+ public SearchCommand(List filters, int windowStart = -1, int windowEnd = -1) : base(filters, windowStart, windowEnd) { }
+
}
///
@@ -79,6 +329,21 @@ namespace MpcNET.Commands.Database
/// End of the portion of the results desired
public SearchAddCommand(List> filters, int windowStart = -1, int windowEnd = -1) : base(filters, windowStart, windowEnd) { }
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Filter
+ /// Start of the portion of the results desired
+ /// End of the portion of the results desired
+ public SearchAddCommand(IFilter filter, int windowStart = -1, int windowEnd = -1) : base(filter, windowStart, windowEnd) { }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// List of filters
+ /// Start of the portion of the results desired
+ /// End of the portion of the results desired
+ public SearchAddCommand(List filters, int windowStart = -1, int windowEnd = -1) : base(filters, windowStart, windowEnd) { }
}
///
@@ -114,15 +379,31 @@ namespace MpcNET.Commands.Database
/// End of the portion of the results desired
public FindCommand(List> filters, int windowStart = -1, int windowEnd = -1) : base(filters, windowStart, windowEnd) { }
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Filter
+ /// Start of the portion of the results desired
+ /// End of the portion of the results desired
+ public FindCommand(IFilter filter, int windowStart = -1, int windowEnd = -1) : base(filter, windowStart, windowEnd) { }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// List of filters
+ /// Start of the portion of the results desired
+ /// End of the portion of the results desired
+ public FindCommand(List filters, int windowStart = -1, int windowEnd = -1) : base(filters, windowStart, windowEnd) { }
+
}
-
///
/// Base class for find/search commands.
///
public abstract class BaseFilterCommand : IMpcCommand>
{
- private readonly List> filters;
+ private readonly List> tagFilters;
+ private readonly List completeFilters;
private readonly int _start;
private readonly int _end;
@@ -145,8 +426,8 @@ namespace MpcNET.Commands.Database
/// End of the portion of the results desired
public BaseFilterCommand(ITag tag, string searchText, int windowStart = -1, int windowEnd = -1)
{
- this.filters = new List>();
- this.filters.Add(new KeyValuePair(tag, searchText));
+ tagFilters = new List>();
+ tagFilters.Add(new KeyValuePair(tag, searchText));
_start = windowStart;
_end = windowEnd;
@@ -160,7 +441,36 @@ namespace MpcNET.Commands.Database
/// End of the portion of the results desired
public BaseFilterCommand(List> filters, int windowStart = -1, int windowEnd = -1)
{
- this.filters = filters;
+ tagFilters = filters;
+
+ _start = windowStart;
+ _end = windowEnd;
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Filter
+ /// Start of the portion of the results desired
+ /// End of the portion of the results desired
+ public BaseFilterCommand(IFilter filters, int windowStart = -1, int windowEnd = -1)
+ {
+ completeFilters = new List();
+ completeFilters.Add(filters);
+
+ _start = windowStart;
+ _end = windowEnd;
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// List of filters
+ /// Start of the portion of the results desired
+ /// End of the portion of the results desired
+ public BaseFilterCommand(List filters, int windowStart = -1, int windowEnd = -1)
+ {
+ completeFilters = filters;
_start = windowStart;
_end = windowEnd;
@@ -170,14 +480,27 @@ namespace MpcNET.Commands.Database
/// Serializes the command.
///
///
- /// The serialize command.
+ /// The serialized command.
///
public string Serialize()
{
- var serializedFilters = string.Join(" AND ",
- filters.Select(x => $"({x.Key.Value} {Operand} {escape(x.Value)})")
- );
- var cmd = $@"{CommandName} ""({serializedFilters})""";
+ string cmd = "";
+
+ if (tagFilters != null)
+ {
+ var serializedFilters = string.Join(" AND ",
+ tagFilters.Select(x => $"({x.Key.Value} {Operand} {escape(x.Value)})")
+ );
+ cmd = $@"{CommandName} ""({serializedFilters})""";
+ }
+
+ if (completeFilters != null)
+ {
+ var serializedFilters = string.Join(" AND ",
+ completeFilters.Select(x => $"{x.GetFormattedCommand()}")
+ );
+ cmd = $@"{CommandName} ""({serializedFilters})""";
+ }
if (_start > -1)
{