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 } }