#region CPL License /* Nuclex Framework Copyright (C) 2002-2009 Nuclex Development Labs This library is free software; you can redistribute it and/or modify it under the terms of the IBM Common Public License as published by the IBM Corporation; either version 1.0 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the IBM Common Public License for more details. You should have received a copy of the IBM Common Public License along with this library */ #endregion using System; using System.Collections.Generic; using System.Net; using System.Net.Sockets; using System.Threading; using System.Text; using Nuclex.Networking.Exceptions; using Nuclex.Support.Tracking; using Nuclex.Support.Scheduling; namespace Nuclex.Audio.Metadata.Requests { /// Retrieves a list of the servers known musical categories internal class CddbCategoryListRequest : Request, IAbortable, IProgressReporter { /// Triggered when the status of the process changes public event EventHandler AsyncProgressChanged; /// Initializes a new CDDB category list request /// /// Protocol used to communication with the CDDB server /// public CddbCategoryListRequest(CddbProtocol protocol) { this.protocol = protocol; } /// Aborts the running process. Can be called from any thread. /// /// The receiver should honor the abort request and stop whatever it is /// doing as soon as possible. The method does not impose any requirement /// on the timeliness of the reaction of the running process, but implementers /// are advised to not ignore the abort request and urged to try and design /// their code in such a way that it can be stopped in a reasonable time /// (eg. within 1 second of the abort request). /// public void AsyncAbort() { if(this.protocol != null) { CddbProtocol theProtocol = this.protocol; this.protocol = null; theProtocol.Dispose(); } } /// Starts the asynchronous execution of the request public void Start() { ThreadPool.QueueUserWorkItem(new WaitCallback(execute)); } /// /// Allows the specific request implementation to re-throw an exception if /// the background process finished unsuccessfully /// protected override void ReraiseExceptions() { if(this.exception != null) { throw this.exception; } } /// /// Allows the specific request implementation to re-throw an exception if /// the background process finished unsuccessfully /// protected override string[] GatherResults() { return this.categories; } /// Triggers the AsyncProgressChanged event /// New progress to report to the event subscribers protected virtual void OnProgressAchieved(float progress) { EventHandler copy = AsyncProgressChanged; if(copy != null) { copy(this, new ProgressReportEventArgs(progress)); } } /// Called asynchronously to execute the request /// Not used private void execute(object state) { lock(this.protocol.SyncRoot) { try { // Issue the command to the CDDB server this.protocol.SendLine("cddb lscat", 5000); // The first reply will indicate the status of the request. string statusLine = this.protocol.ReceiveLine(5000); int statusCode = CddbProtocol.GetStatusCode(statusLine); // Process the response according to its status code switch(statusCode) { // Request was accepted and genre list follows case 210: { this.categories = receiveCategoryList(); break; } // No success code, find out what exactly went wrong default: { this.exception = exceptionFromGenreListStatus( statusCode, statusLine.Substring((statusLine.Length >= 4) ? 4 : 3) ); break; } } } catch(Exception exception) { this.exception = exception; } } OnAsyncEnded(); } /// Receives the list of known categories from the server /// The category list as an array of strings private string[] receiveCategoryList() { List categoryList = new List(); for(; ; ) { string line = this.protocol.ReceiveLine(5000); if(line == ".") { break; } categoryList.Add(line); } // All genres received, convert the list into an array that can be returned // to the owner of the request. return categoryList.ToArray(); } /// /// Generates an exception from the status code in the reply to /// the genre list request /// /// Status code provided by the server /// Response returned by the server /// /// The exception for the server status code or null if the status code /// indicates success /// private static Exception exceptionFromGenreListStatus(int statusCode, string message) { switch(statusCode) { default: { return new BadResponseException( string.Format( "Bad response from CDDB server: invalid status code '{0}', " + "server message is '{1}'", statusCode, message ) ); } } } /// Exception that has occured during asynchronous processing private volatile Exception exception; /// Protocol used to communicate with the CDDB server private volatile CddbProtocol protocol; /// Categories returned by the CDDB server private string[] categories; } } // namespace Nuclex.Audio.Metadata.Requests