#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