using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text;
using System.Text.RegularExpressions;
namespace Libmpc
{
///
/// The MpdResponse class parses the response to an MPD command in it's most
/// basic structure.
///
public class MpdResponse : IEnumerable>
{
private const string OK = "OK";
private const string ACK = "ACK";
private static readonly Regex LINE_REGEX = new Regex("^(?[A-Za-z]*):[ ]{0,1}(?.*)$");
private readonly bool isError;
private readonly int errorCode;
private readonly int commandListNum;
private readonly string currentCommand;
private readonly string errorMessage;
private readonly ReadOnlyCollection message;
private Dictionary dictionary = null;
///
/// If the response denotes an error in the last command.
///
public bool IsError { get { return this.isError; } }
///
/// The error code if an error occured.
///
public int ErrorCode { get { return this.errorCode; } }
///
/// If an error occured the index of the invalid command in a command list.
///
public int CommandListNum { get { return this.commandListNum; } }
///
/// The command executed.
///
public string CurrentCommand { get { return this.currentCommand; } }
///
/// The description of the error, if occured.
///
public string ErrorMessage { get { return this.errorMessage; } }
///
/// The lines of the response message.
///
public ReadOnlyCollection Message { get { return this.message; } }
///
/// The value of an attribute in the MPD response.
///
/// The name of the attribute.
/// The value of the attribute
public string this[string key]
{
get
{
if (this.dictionary == null)
{
this.dictionary = new Dictionary();
foreach (string line in this.message)
{
Match match = LINE_REGEX.Match(line);
if (match.Success)
this.dictionary[match.Result("$key")] = match.Result("$value");
}
}
return this.dictionary[key];
}
}
///
/// The number of lines in the response message.
///
public int Count { get { return this.message.Count; } }
///
/// A line in the MPD response as KeyValuePair. If the message cannot be separated
/// into key and value according to the MPD protocol spec, a KeyValuePair is returned
/// with the key null and the value the whole text of the line.
///
/// The index of the line.
/// The requested line as KeyValuePair.
public KeyValuePair this[int line]
{
get
{
Match match = LINE_REGEX.Match(this.message[line]);
if (match.Success)
return new KeyValuePair(match.Result("${key}"), match.Result("${value}"));
else
return new KeyValuePair(null, this.message[line]);
}
}
///
/// Creates a new MpdResponse from a list of lines in case no error occured.
///
/// The response to an MPD command.
public MpdResponse( ReadOnlyCollection message )
{
if (message == null)
throw new ArgumentNullException("message");
this.isError = false;
this.errorCode = -1;
this.commandListNum = 0;
this.currentCommand = null;
this.errorMessage = null;
this.message = message;
}
///
/// Creates a new MpdResponse in case an error occured.
///
/// The code of the error.
/// The index of the command which raised the error.
/// The command that raised the error.
/// The message describing the error.
/// The text of the standard MPD response.
public MpdResponse( int errorCode, int commandListNum, string currentCommand, string errorMessage, ReadOnlyCollection message)
{
if (currentCommand == null)
throw new ArgumentNullException("currentCommand");
if (errorMessage == null)
throw new ArgumentNullException("errorMessage");
if (message == null)
throw new ArgumentNullException("message");
this.isError = true;
this.errorCode = errorCode;
this.commandListNum = commandListNum;
this.currentCommand = currentCommand;
this.errorMessage = errorMessage;
this.message = message;
}
///
/// Returns the values in all lines with the given attribute.
///
/// The attribute who's values are reguested.
/// The values in all lines with the given attribute.
public List getAttributeValueList(string attribute)
{
List ret = new List();
foreach (string line in this.message)
{
Match match = LINE_REGEX.Match(line);
if (match.Success)
{
string key = match.Result("${key}");
if( ( key != null ) && key.Equals( attribute ) )
{
string value = match.Result("${value}");
if( value != null )
ret.Add( value );
}
}
}
return ret;
}
///
/// Returns only the value parts in all key/value pairs in the response.
///
/// The list of values in all key/value pairs in the response.
public List getValueList()
{
List ret = new List();
foreach (string line in this.message)
{
Match match = LINE_REGEX.Match(line);
if (match.Success)
{
string value = match.Result("${value}");
if (value != null)
ret.Add(value);
}
}
return ret;
}
///
/// Builds the response text of the MPD server from the object.
///
/// The response text of the MPD server from the object.
public override string ToString()
{
StringBuilder builder = new StringBuilder();
foreach (string line in this.message)
builder.AppendLine(line);
if (this.isError)
{
builder.Append(ACK);
builder.Append(" [");
builder.Append(this.errorMessage);
builder.Append('@');
builder.Append(this.commandListNum);
builder.Append("] {");
builder.Append(this.currentCommand);
builder.Append("} ");
builder.Append(this.errorMessage);
//ACK [50@1] {play} song doesn't exist: "10240"
}
else
builder.Append(OK);
return builder.ToString();
}
#region IEnumerable> Members
///
/// Returns an enumerator for all KeyValuePairs in the MPD response.
///
/// An enumerator for all KeyValuePairs in the MPD response.
IEnumerator> IEnumerable>.GetEnumerator()
{
return new MpdResponseEnumerator(this);
}
#endregion
#region IEnumerable Members
///
/// Returns an enumerator for all KeyValuePairs in the MPD response.
///
/// An enumerator for all KeyValuePairs in the MPD response.
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return new MpdResponseEnumerator(this);
}
#endregion
}
///
/// A class for enumerating over the KeyValuePairs in the response.
///
public class MpdResponseEnumerator :IEnumerator>
{
private readonly MpdResponse response;
private int position = -1;
private KeyValuePair current;
///
/// Creates a new MpdResponseEnumerator.
///
/// The response to enumerate over.
protected internal MpdResponseEnumerator(MpdResponse response)
{
this.response = response;
}
#region IEnumerator> Members
///
/// Returns the current element of the enumerator.
///
KeyValuePair IEnumerator>.Current
{
get { return this.current; }
}
#endregion
#region IDisposable Members
void IDisposable.Dispose()
{
this.position = -1;
}
#endregion
#region IEnumerator Members
///
/// Returns the current element of the enumerator.
///
object System.Collections.IEnumerator.Current
{
get { return this.current; }
}
///
/// Moves the enumerator to the next KeyValuePair in the MPD response.
///
/// If the enumerator has any values left.
bool System.Collections.IEnumerator.MoveNext()
{
this.position++;
if (this.position < this.response.Count)
{
this.current = this.response[this.position];
return true;
}
else
return false;
}
///
/// Sets the enumerator to it's initial state.
///
void System.Collections.IEnumerator.Reset()
{
this.position = -1;
}
#endregion
}
}