1.9.4... too many small changes to comment

This commit is contained in:
chudov
2009-01-17 04:09:38 +00:00
parent dc9168dcf9
commit 399573c1be
56 changed files with 5188 additions and 3645 deletions

View File

@@ -1,82 +1,82 @@
// MusicBrainzObject.cs
//
// Copyright (c) 2008 Scott Peterson <lunchtimemama@gmail.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Net;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Xml;
namespace MusicBrainz
{
public abstract class MusicBrainzObject
{
#region Private Fields
static DateTime last_accessed;
static readonly TimeSpan min_interval = new TimeSpan (0, 0, 1); // 1 second
static readonly object server_mutex = new object ();
static readonly string [] rels_params = new string [] {
"artist-rels",
"release-rels",
"track-rels",
"label-rels",
"url-rels"
};
bool all_data_loaded;
bool all_rels_loaded;
string id;
byte score;
ReadOnlyCollection<Relation<Artist>> artist_rels;
ReadOnlyCollection<Relation<Release>> release_rels;
ReadOnlyCollection<Relation<Track>> track_rels;
ReadOnlyCollection<Relation<Label>> label_rels;
ReadOnlyCollection<UrlRelation> url_rels;
#endregion
#region Constructors
internal MusicBrainzObject (string id, string parameters)
{
all_data_loaded = true;
CreateFromId (id, parameters ?? CreateInc ());
}
internal MusicBrainzObject (XmlReader reader, bool all_rels_loaded)
{
this.all_rels_loaded = all_rels_loaded;
CreateFromXml (reader);
}
#endregion
// MusicBrainzObject.cs
//
// Copyright (c) 2008 Scott Peterson <lunchtimemama@gmail.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Net;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Xml;
namespace MusicBrainz
{
public abstract class MusicBrainzObject
{
#region Private Fields
static DateTime last_accessed;
static readonly TimeSpan min_interval = new TimeSpan (1000000); // 0.1 second
static readonly object server_mutex = new object ();
static readonly string [] rels_params = new string [] {
"artist-rels",
"release-rels",
"track-rels",
"label-rels",
"url-rels"
};
bool all_data_loaded;
bool all_rels_loaded;
string id;
byte score;
ReadOnlyCollection<Relation<Artist>> artist_rels;
ReadOnlyCollection<Relation<Release>> release_rels;
ReadOnlyCollection<Relation<Track>> track_rels;
ReadOnlyCollection<Relation<Label>> label_rels;
ReadOnlyCollection<UrlRelation> url_rels;
#endregion
#region Constructors
internal MusicBrainzObject (string id, string parameters)
{
all_data_loaded = true;
CreateFromId (id, parameters ?? CreateInc ());
}
internal MusicBrainzObject (XmlReader reader, bool all_rels_loaded)
{
this.all_rels_loaded = all_rels_loaded;
CreateFromXml (reader);
}
#endregion
#region Private Methods
string CreateInc ()
@@ -84,51 +84,51 @@ namespace MusicBrainz
StringBuilder builder = new StringBuilder ();
CreateIncCore (builder);
return builder.ToString ();
}
void CreateFromId (string id, string parameters)
{
XmlProcessingClosure (
CreateUrl (UrlExtension, id, parameters),
delegate (XmlReader reader) {
reader.ReadToFollowing ("metadata");
reader.Read ();
CreateFromXml (reader.ReadSubtree ());
reader.Close ();
}
);
}
void CreateFromXml (XmlReader reader)
{
reader.Read ();
id = reader ["id"];
byte.TryParse (reader ["ext:score"], out score);
ProcessAttributes (reader);
while (reader.Read () && reader.NodeType != XmlNodeType.EndElement) {
if (reader.Name == "relation-list") {
all_rels_loaded = true;
switch (reader ["target-type"]) {
case "Artist":
artist_rels = CreateRelation<Artist> (reader.ReadSubtree ());
break;
case "Release":
release_rels = CreateRelation<Release> (reader.ReadSubtree ());
break;
case "Track":
track_rels = CreateRelation<Track> (reader.ReadSubtree ());
break;
case "Label":
label_rels = CreateRelation<Label> (reader.ReadSubtree ());
break;
}
void CreateFromId (string id, string parameters)
{
XmlProcessingClosure (
CreateUrl (UrlExtension, id, parameters),
delegate (XmlReader reader) {
reader.ReadToFollowing ("metadata");
reader.Read ();
CreateFromXml (reader.ReadSubtree ());
reader.Close ();
}
);
}
void CreateFromXml (XmlReader reader)
{
reader.Read ();
id = reader ["id"];
byte.TryParse (reader ["ext:score"], out score);
ProcessAttributes (reader);
while (reader.Read () && reader.NodeType != XmlNodeType.EndElement) {
if (reader.Name == "relation-list") {
all_rels_loaded = true;
switch (reader ["target-type"]) {
case "Artist":
artist_rels = CreateRelation<Artist> (reader.ReadSubtree ());
break;
case "Release":
release_rels = CreateRelation<Release> (reader.ReadSubtree ());
break;
case "Track":
track_rels = CreateRelation<Track> (reader.ReadSubtree ());
break;
case "Label":
label_rels = CreateRelation<Label> (reader.ReadSubtree ());
break;
case "Url":
url_rels = CreateUrlRelation (reader.ReadSubtree ());
break;
}
} else
ProcessXml (reader.ReadSubtree ());
}
reader.Close ();
url_rels = CreateUrlRelation (reader.ReadSubtree ());
break;
}
} else
ProcessXml (reader.ReadSubtree ());
}
reader.Close ();
}
void ProcessXml (XmlReader reader)
@@ -136,92 +136,92 @@ namespace MusicBrainz
reader.Read ();
ProcessXmlCore (reader);
reader.Close ();
}
#endregion
#region Protected
internal bool AllDataLoaded {
get { return all_data_loaded; }
}
internal bool AllRelsLoaded {
get { return all_rels_loaded; }
set { all_rels_loaded = value; }
}
internal virtual void CreateIncCore (StringBuilder builder)
{
if (!all_rels_loaded)
AppendIncParameters (builder, rels_params);
}
internal static void AppendIncParameters (StringBuilder builder, string parameter)
{
builder.Append (builder.Length == 0 ? "&inc=" : "+");
builder.Append (parameter);
}
internal static void AppendIncParameters (StringBuilder builder, string parameter1, string parameter2)
{
builder.Append (builder.Length == 0 ? "&inc=" : "+");
builder.Append (parameter1);
builder.Append ('+');
builder.Append (parameter2);
}
internal static void AppendIncParameters (StringBuilder builder, string [] parameters)
{
foreach (string parameter in parameters)
AppendIncParameters (builder, parameter);
}
internal void LoadMissingData ()
{
if (!all_data_loaded) {
LoadMissingDataCore ();
all_data_loaded = true;
}
}
internal void LoadMissingDataCore (MusicBrainzObject obj)
{
if (!all_rels_loaded) {
artist_rels = obj.GetArtistRelations ();
release_rels = obj.GetReleaseRelations ();
track_rels = obj.GetTrackRelations ();
label_rels = obj.GetLabelRelations ();
url_rels = obj.GetUrlRelations ();
}
}
internal T GetPropertyOrNull<T> (ref T field_reference) where T : class
{
if (field_reference == null) LoadMissingData ();
return field_reference;
}
internal T GetPropertyOrDefault<T> (ref T? field_reference) where T : struct
{
return GetPropertyOrDefault (ref field_reference, default (T));
}
internal T GetPropertyOrDefault<T> (ref T? field_reference, T default_value) where T : struct
{
if (field_reference == null) LoadMissingData ();
return field_reference ?? default_value;
}
internal ReadOnlyCollection<T> GetPropertyOrNew<T> (ref ReadOnlyCollection<T> field_reference)
{
return GetPropertyOrNew (ref field_reference, true);
}
internal ReadOnlyCollection<T> GetPropertyOrNew<T> (ref ReadOnlyCollection<T> field_reference, bool condition)
{
if (field_reference == null && condition) LoadMissingData ();
return field_reference ?? new ReadOnlyCollection<T> (new T [0]);
}
#endregion
#region Protected
internal bool AllDataLoaded {
get { return all_data_loaded; }
}
internal bool AllRelsLoaded {
get { return all_rels_loaded; }
set { all_rels_loaded = value; }
}
internal virtual void CreateIncCore (StringBuilder builder)
{
if (!all_rels_loaded)
AppendIncParameters (builder, rels_params);
}
internal static void AppendIncParameters (StringBuilder builder, string parameter)
{
builder.Append (builder.Length == 0 ? "&inc=" : "+");
builder.Append (parameter);
}
internal static void AppendIncParameters (StringBuilder builder, string parameter1, string parameter2)
{
builder.Append (builder.Length == 0 ? "&inc=" : "+");
builder.Append (parameter1);
builder.Append ('+');
builder.Append (parameter2);
}
internal static void AppendIncParameters (StringBuilder builder, string [] parameters)
{
foreach (string parameter in parameters)
AppendIncParameters (builder, parameter);
}
internal void LoadMissingData ()
{
if (!all_data_loaded) {
LoadMissingDataCore ();
all_data_loaded = true;
}
}
internal void LoadMissingDataCore (MusicBrainzObject obj)
{
if (!all_rels_loaded) {
artist_rels = obj.GetArtistRelations ();
release_rels = obj.GetReleaseRelations ();
track_rels = obj.GetTrackRelations ();
label_rels = obj.GetLabelRelations ();
url_rels = obj.GetUrlRelations ();
}
}
internal T GetPropertyOrNull<T> (ref T field_reference) where T : class
{
if (field_reference == null) LoadMissingData ();
return field_reference;
}
internal T GetPropertyOrDefault<T> (ref T? field_reference) where T : struct
{
return GetPropertyOrDefault (ref field_reference, default (T));
}
internal T GetPropertyOrDefault<T> (ref T? field_reference, T default_value) where T : struct
{
if (field_reference == null) LoadMissingData ();
return field_reference ?? default_value;
}
internal ReadOnlyCollection<T> GetPropertyOrNew<T> (ref ReadOnlyCollection<T> field_reference)
{
return GetPropertyOrNew (ref field_reference, true);
}
internal ReadOnlyCollection<T> GetPropertyOrNew<T> (ref ReadOnlyCollection<T> field_reference, bool condition)
{
if (field_reference == null && condition) LoadMissingData ();
return field_reference ?? new ReadOnlyCollection<T> (new T [0]);
}
internal virtual void ProcessXmlCore (XmlReader reader)
@@ -231,101 +231,101 @@ namespace MusicBrainz
internal virtual void ProcessAttributes (XmlReader reader)
{
}
internal abstract void LoadMissingDataCore ();
internal abstract string UrlExtension { get; }
#endregion
#region Public
public virtual string Id {
get { return id; }
}
public virtual byte Score {
get { return score; }
}
public virtual ReadOnlyCollection<Relation<Artist>> GetArtistRelations ()
{
return GetPropertyOrNew (ref artist_rels, !all_rels_loaded);
}
public virtual ReadOnlyCollection<Relation<Release>> GetReleaseRelations ()
{
return GetPropertyOrNew (ref release_rels, !all_rels_loaded);
}
public virtual ReadOnlyCollection<Relation<Track>> GetTrackRelations ()
{
return GetPropertyOrNew (ref track_rels, !all_rels_loaded);
}
public virtual ReadOnlyCollection<Relation<Label>> GetLabelRelations ()
{
return GetPropertyOrNew (ref label_rels, !all_rels_loaded);
}
public virtual ReadOnlyCollection<UrlRelation> GetUrlRelations ()
{
return GetPropertyOrNew (ref url_rels, !all_rels_loaded);
}
public override bool Equals (object obj)
{
return this == obj as MusicBrainzObject;
}
public static bool operator ==(MusicBrainzObject obj1, MusicBrainzObject obj2)
}
internal abstract void LoadMissingDataCore ();
internal abstract string UrlExtension { get; }
#endregion
#region Public
public virtual string Id {
get { return id; }
}
public virtual byte Score {
get { return score; }
}
public virtual ReadOnlyCollection<Relation<Artist>> GetArtistRelations ()
{
return GetPropertyOrNew (ref artist_rels, !all_rels_loaded);
}
public virtual ReadOnlyCollection<Relation<Release>> GetReleaseRelations ()
{
return GetPropertyOrNew (ref release_rels, !all_rels_loaded);
}
public virtual ReadOnlyCollection<Relation<Track>> GetTrackRelations ()
{
return GetPropertyOrNew (ref track_rels, !all_rels_loaded);
}
public virtual ReadOnlyCollection<Relation<Label>> GetLabelRelations ()
{
return GetPropertyOrNew (ref label_rels, !all_rels_loaded);
}
public virtual ReadOnlyCollection<UrlRelation> GetUrlRelations ()
{
return GetPropertyOrNew (ref url_rels, !all_rels_loaded);
}
public override bool Equals (object obj)
{
return this == obj as MusicBrainzObject;
}
public static bool operator ==(MusicBrainzObject obj1, MusicBrainzObject obj2)
{
if (Object.ReferenceEquals (obj1, null)) {
return Object.ReferenceEquals (obj2, null);
}
return !Object.ReferenceEquals (obj2, null) && obj1.GetType () == obj2.GetType () && obj1.Id == obj2.Id;
}
public static bool operator !=(MusicBrainzObject obj1, MusicBrainzObject obj2)
{
return !(obj1 == obj2);
}
public override int GetHashCode ()
{
return (GetType ().Name + Id).GetHashCode ();
}
#endregion
#region Static
static ReadOnlyCollection<Relation<T>> CreateRelation<T> (XmlReader reader) where T : MusicBrainzObject
{
List<Relation<T>> relations = new List<Relation<T>> ();
while (reader.ReadToFollowing ("relation")) {
string type = reader ["type"];
RelationDirection direction = RelationDirection.Forward;
string direction_string = reader ["direction"];
if (direction_string != null && direction_string == "backward")
direction = RelationDirection.Backward;
string begin = reader ["begin"];
string end = reader ["end"];
string attributes_string = reader ["attributes"];
string [] attributes = attributes_string == null
? null : attributes_string.Split (' ');
reader.Read ();
relations.Add (new Relation<T> (
type,
ConstructMusicBrainzObjectFromXml<T> (reader.ReadSubtree ()),
direction,
begin,
end,
attributes));
}
reader.Close ();
return relations.AsReadOnly ();
}
return !Object.ReferenceEquals (obj2, null) && obj1.GetType () == obj2.GetType () && obj1.Id == obj2.Id;
}
public static bool operator !=(MusicBrainzObject obj1, MusicBrainzObject obj2)
{
return !(obj1 == obj2);
}
public override int GetHashCode ()
{
return (GetType ().Name + Id).GetHashCode ();
}
#endregion
#region Static
static ReadOnlyCollection<Relation<T>> CreateRelation<T> (XmlReader reader) where T : MusicBrainzObject
{
List<Relation<T>> relations = new List<Relation<T>> ();
while (reader.ReadToFollowing ("relation")) {
string type = reader ["type"];
RelationDirection direction = RelationDirection.Forward;
string direction_string = reader ["direction"];
if (direction_string != null && direction_string == "backward")
direction = RelationDirection.Backward;
string begin = reader ["begin"];
string end = reader ["end"];
string attributes_string = reader ["attributes"];
string [] attributes = attributes_string == null
? null : attributes_string.Split (' ');
reader.Read ();
relations.Add (new Relation<T> (
type,
ConstructMusicBrainzObjectFromXml<T> (reader.ReadSubtree ()),
direction,
begin,
end,
attributes));
}
reader.Close ();
return relations.AsReadOnly ();
}
static ReadOnlyCollection<UrlRelation> CreateUrlRelation (XmlReader reader)
@@ -348,138 +348,141 @@ namespace MusicBrainz
attributes));
}
return url_rels.AsReadOnly ();
}
static string CreateUrl (string url_extension, int limit, int offset, string parameters)
{
StringBuilder builder = new StringBuilder ();
if (limit != 25) {
builder.Append ("&limit=");
builder.Append (limit);
}
if (offset != 0) {
builder.Append ("&offset=");
builder.Append (offset);
}
builder.Append (parameters);
return CreateUrl (url_extension, string.Empty, builder.ToString ());
}
static string CreateUrl (string url_extension, string id, string parameters)
{
StringBuilder builder = new StringBuilder (
MusicBrainzService.ServiceUrl.AbsoluteUri.Length + id.Length + parameters.Length + 9);
builder.Append (MusicBrainzService.ServiceUrl.AbsoluteUri);
builder.Append (url_extension);
builder.Append ('/');
builder.Append (id);
builder.Append ("?type=xml");
builder.Append (parameters);
return builder.ToString ();
}
static void XmlProcessingClosure (string url, XmlProcessingDelegate code)
{
Monitor.Enter (server_mutex);
// Don't access the MB server twice within a second
TimeSpan time = DateTime.Now - last_accessed;
if (min_interval > time)
Thread.Sleep ((min_interval - time).Milliseconds);
}
static string CreateUrl (string url_extension, int limit, int offset, string parameters)
{
StringBuilder builder = new StringBuilder ();
if (limit != 25) {
builder.Append ("&limit=");
builder.Append (limit);
}
if (offset != 0) {
builder.Append ("&offset=");
builder.Append (offset);
}
builder.Append (parameters);
return CreateUrl (url_extension, string.Empty, builder.ToString ());
}
static string CreateUrl (string url_extension, string id, string parameters)
{
StringBuilder builder = new StringBuilder (
MusicBrainzService.ServiceUrl.AbsoluteUri.Length + id.Length + parameters.Length + 9);
builder.Append (MusicBrainzService.ServiceUrl.AbsoluteUri);
builder.Append (url_extension);
builder.Append ('/');
builder.Append (id);
builder.Append ("?type=xml");
builder.Append (parameters);
return builder.ToString ();
}
static void XmlProcessingClosure (string url, XmlProcessingDelegate code)
{
Monitor.Enter (server_mutex);
// Don't access the MB server twice within a second
if (last_accessed != null)
{
TimeSpan time = DateTime.Now - last_accessed;
if (min_interval > time)
Thread.Sleep((min_interval - time).Milliseconds);
}
WebRequest request = WebRequest.Create (url);
bool cache_implemented = false;
try {
request.CachePolicy = MusicBrainzService.CachePolicy;
cache_implemented = true;
} catch (NotImplementedException) {}
HttpWebResponse response = null;
try {
response = (HttpWebResponse)request.GetResponse ();
} catch (WebException e) {
response = (HttpWebResponse)e.Response;
}
if (response == null) throw new MusicBrainzNotFoundException ();
switch (response.StatusCode) {
case HttpStatusCode.BadRequest:
Monitor.Exit (server_mutex);
throw new MusicBrainzInvalidParameterException ();
case HttpStatusCode.Unauthorized:
Monitor.Exit (server_mutex);
throw new MusicBrainzUnauthorizedException ();
case HttpStatusCode.NotFound:
Monitor.Exit (server_mutex);
throw new MusicBrainzNotFoundException ();
}
WebRequest request = WebRequest.Create (url);
bool cache_implemented = false;
try {
request.CachePolicy = MusicBrainzService.CachePolicy;
cache_implemented = true;
} catch (NotImplementedException) {}
HttpWebResponse response = null;
try {
response = (HttpWebResponse)request.GetResponse ();
} catch (WebException e) {
response = (HttpWebResponse)e.Response;
}
if (response == null) throw new MusicBrainzNotFoundException ();
switch (response.StatusCode) {
case HttpStatusCode.BadRequest:
Monitor.Exit (server_mutex);
throw new MusicBrainzInvalidParameterException ();
case HttpStatusCode.Unauthorized:
Monitor.Exit (server_mutex);
throw new MusicBrainzUnauthorizedException ();
case HttpStatusCode.NotFound:
Monitor.Exit (server_mutex);
throw new MusicBrainzNotFoundException ();
}
bool from_cache = cache_implemented && response.IsFromCache;
if (from_cache) Monitor.Exit (server_mutex);
MusicBrainzService.OnXmlRequest (url, from_cache);
// Should we read the stream into a memory stream and run the XmlReader off of that?
code (new XmlTextReader (response.GetResponseStream ()));
response.Close ();
if (!from_cache) {
last_accessed = DateTime.Now;
Monitor.Exit (server_mutex);
}
}
#endregion
#region Query
internal static string CreateLuceneParameter (string query)
{
StringBuilder builder = new StringBuilder (query.Length + 7);
builder.Append ("&query=");
Utils.PercentEncode (builder, query);
return builder.ToString ();
}
internal static List<T> Query<T> (string url_extension,
int limit, int offset,
string parameters,
out int? count) where T : MusicBrainzObject
{
int count_value = 0;
List<T> results = new List<T> ();
XmlProcessingClosure (
CreateUrl (url_extension, limit, offset, parameters),
delegate (XmlReader reader) {
reader.ReadToFollowing ("metadata");
reader.Read ();
int.TryParse (reader ["count"], out count_value);
while (reader.Read () && reader.NodeType == XmlNodeType.Element)
results.Add (ConstructMusicBrainzObjectFromXml<T> (reader.ReadSubtree ()));
reader.Close ();
}
);
count = count_value == 0 ? results.Count : count_value;
return results;
}
static T ConstructMusicBrainzObjectFromXml<T> (XmlReader reader) where T : MusicBrainzObject
{
ConstructorInfo constructor = typeof (T).GetConstructor (
BindingFlags.NonPublic | BindingFlags.Instance,
null,
new Type [] { typeof (XmlReader) },
null);
return (T)constructor.Invoke (new object [] {reader});
}
#endregion
}
internal delegate void XmlProcessingDelegate (XmlReader reader);
}
if (from_cache) Monitor.Exit (server_mutex);
MusicBrainzService.OnXmlRequest (url, from_cache);
// Should we read the stream into a memory stream and run the XmlReader off of that?
code (new XmlTextReader (response.GetResponseStream ()));
response.Close ();
if (!from_cache) {
last_accessed = DateTime.Now;
Monitor.Exit (server_mutex);
}
}
#endregion
#region Query
internal static string CreateLuceneParameter (string query)
{
StringBuilder builder = new StringBuilder (query.Length + 7);
builder.Append ("&query=");
Utils.PercentEncode (builder, query);
return builder.ToString ();
}
internal static List<T> Query<T> (string url_extension,
int limit, int offset,
string parameters,
out int? count) where T : MusicBrainzObject
{
int count_value = 0;
List<T> results = new List<T> ();
XmlProcessingClosure (
CreateUrl (url_extension, limit, offset, parameters),
delegate (XmlReader reader) {
reader.ReadToFollowing ("metadata");
reader.Read ();
int.TryParse (reader ["count"], out count_value);
while (reader.Read () && reader.NodeType == XmlNodeType.Element)
results.Add (ConstructMusicBrainzObjectFromXml<T> (reader.ReadSubtree ()));
reader.Close ();
}
);
count = count_value == 0 ? results.Count : count_value;
return results;
}
static T ConstructMusicBrainzObjectFromXml<T> (XmlReader reader) where T : MusicBrainzObject
{
ConstructorInfo constructor = typeof (T).GetConstructor (
BindingFlags.NonPublic | BindingFlags.Instance,
null,
new Type [] { typeof (XmlReader) },
null);
return (T)constructor.Invoke (new object [] {reader});
}
#endregion
}
internal delegate void XmlProcessingDelegate (XmlReader reader);
}