rbrainz 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (84) hide show
  1. data/LICENSE +25 -0
  2. data/README +11 -0
  3. data/Rakefile +110 -0
  4. data/TODO +24 -0
  5. data/doc/README.rdoc +68 -0
  6. data/examples/getartist.rb +54 -0
  7. data/lib/rbrainz.rb +11 -0
  8. data/lib/rbrainz/data/countrynames.rb +259 -0
  9. data/lib/rbrainz/data/languagenames.rb +404 -0
  10. data/lib/rbrainz/data/scriptnames.rb +64 -0
  11. data/lib/rbrainz/model.rb +48 -0
  12. data/lib/rbrainz/model/alias.rb +23 -0
  13. data/lib/rbrainz/model/artist.rb +54 -0
  14. data/lib/rbrainz/model/disc.rb +30 -0
  15. data/lib/rbrainz/model/entity.rb +72 -0
  16. data/lib/rbrainz/model/incomplete_date.rb +76 -0
  17. data/lib/rbrainz/model/label.rb +55 -0
  18. data/lib/rbrainz/model/mbid.rb +82 -0
  19. data/lib/rbrainz/model/release.rb +50 -0
  20. data/lib/rbrainz/model/release_event.rb +34 -0
  21. data/lib/rbrainz/model/track.rb +27 -0
  22. data/lib/rbrainz/webservice.rb +6 -0
  23. data/lib/rbrainz/webservice/filter.rb +132 -0
  24. data/lib/rbrainz/webservice/includes.rb +150 -0
  25. data/lib/rbrainz/webservice/mbxml.rb +404 -0
  26. data/lib/rbrainz/webservice/query.rb +61 -0
  27. data/lib/rbrainz/webservice/webservice.rb +69 -0
  28. data/test/lib/test_entity.rb +58 -0
  29. data/test/lib/testing_helper.rb +13 -0
  30. data/test/test-data/README +13 -0
  31. data/test/test-data/invalid/artist/basic_1.xml +12 -0
  32. data/test/test-data/invalid/artist/basic_2.xml +12 -0
  33. data/test/test-data/invalid/artist/empty_1.xml +0 -0
  34. data/test/test-data/invalid/artist/empty_2.xml +3 -0
  35. data/test/test-data/invalid/artist/empty_3.xml +7 -0
  36. data/test/test-data/invalid/artist/search_result_1.xml +11 -0
  37. data/test/test-data/valid/artist/Tchaikovsky-1.xml +90 -0
  38. data/test/test-data/valid/artist/Tori_Amos_1.xml +8 -0
  39. data/test/test-data/valid/artist/Tori_Amos_2.xml +53 -0
  40. data/test/test-data/valid/artist/Tori_Amos_3.xml +25 -0
  41. data/test/test-data/valid/artist/Tori_Amos_4.xml +14 -0
  42. data/test/test-data/valid/artist/Tori_Amos_5.xml +13 -0
  43. data/test/test-data/valid/artist/empty_1.xml +7 -0
  44. data/test/test-data/valid/artist/empty_2.xml +11 -0
  45. data/test/test-data/valid/artist/search_result_1.xml +19 -0
  46. data/test/test-data/valid/label/Atlantic_Records_1.xml +9 -0
  47. data/test/test-data/valid/label/Atlantic_Records_2.xml +11 -0
  48. data/test/test-data/valid/label/search_result_1.xml +14 -0
  49. data/test/test-data/valid/release/Highway_61_Revisited_1.xml +68 -0
  50. data/test/test-data/valid/release/Little_Earthquakes_1.xml +24 -0
  51. data/test/test-data/valid/release/Little_Earthquakes_2.xml +73 -0
  52. data/test/test-data/valid/release/Mission_Impossible_2.xml +155 -0
  53. data/test/test-data/valid/release/Under_the_Pink_1.xml +16 -0
  54. data/test/test-data/valid/release/Under_the_Pink_2.xml +14 -0
  55. data/test/test-data/valid/release/Under_the_Pink_3.xml +16 -0
  56. data/test/test-data/valid/release/search_result_1.xml +29 -0
  57. data/test/test-data/valid/track/Silent_All_These_Years_1.xml +7 -0
  58. data/test/test-data/valid/track/Silent_All_These_Years_2.xml +21 -0
  59. data/test/test-data/valid/track/Silent_All_These_Years_3.xml +16 -0
  60. data/test/test-data/valid/track/Silent_All_These_Years_4.xml +30 -0
  61. data/test/test-data/valid/track/Silent_All_These_Years_5.xml +20 -0
  62. data/test/test-data/valid/track/search_result_1.xml +45 -0
  63. data/test/test-data/valid/user/User_1.xml +15 -0
  64. data/test/test_alias.rb +50 -0
  65. data/test/test_artist.rb +132 -0
  66. data/test/test_artist_filter.rb +36 -0
  67. data/test/test_artist_includes.rb +63 -0
  68. data/test/test_disc.rb +38 -0
  69. data/test/test_incomplete_date.rb +60 -0
  70. data/test/test_label.rb +129 -0
  71. data/test/test_label_filter.rb +36 -0
  72. data/test/test_label_includes.rb +55 -0
  73. data/test/test_mbid.rb +99 -0
  74. data/test/test_mbxml.rb +368 -0
  75. data/test/test_query.rb +24 -0
  76. data/test/test_release.rb +161 -0
  77. data/test/test_release_event.rb +67 -0
  78. data/test/test_release_filter.rb +57 -0
  79. data/test/test_release_includes.rb +73 -0
  80. data/test/test_track.rb +102 -0
  81. data/test/test_track_filter.rb +57 -0
  82. data/test/test_track_includes.rb +61 -0
  83. data/test/test_webservice.rb +23 -0
  84. metadata +138 -0
@@ -0,0 +1,50 @@
1
+ # $Id: release.rb 15 2007-05-23 14:31:36Z phw $
2
+ # Copyright (c) 2007, Philipp Wolfer
3
+ # All rights reserved.
4
+ # See LICENSE for permissions.
5
+
6
+ require 'rbrainz/model/entity'
7
+ require 'rbrainz/model/release_event'
8
+ require 'rbrainz/model/disc'
9
+
10
+ module MusicBrainz
11
+ module Model
12
+
13
+ # A release in the MusicBrainz DB.
14
+ #
15
+ # See http://musicbrainz.org/doc/Release.
16
+ class Release < Entity
17
+
18
+ TYPE_ALBUM = NS_MMD_1 + 'Album'
19
+ TYPE_AUDIOBOOK = NS_MMD_1 + 'Audiobook'
20
+ TYPE_BOOTLEG = NS_MMD_1 + 'Bootleg'
21
+ TYPE_COMPILATION = NS_MMD_1 + 'Compilation'
22
+ TYPE_EP = NS_MMD_1 + 'EP'
23
+ TYPE_INTERVIEW = NS_MMD_1 + 'Interview'
24
+ TYPE_LIVE = NS_MMD_1 + 'Live'
25
+ TYPE_NONE = NS_MMD_1 + 'None'
26
+ TYPE_OFFICIAL = NS_MMD_1 + 'Official'
27
+ TYPE_OTHER = NS_MMD_1 + 'Other'
28
+ TYPE_PROMOTION = NS_MMD_1 + 'Promotion'
29
+ TYPE_PSEUDO_RELEASE = NS_MMD_1 + 'Pseudo-Release'
30
+ TYPE_REMIX = NS_MMD_1 + 'Remix'
31
+ TYPE_SINGLE = NS_MMD_1 + 'Single'
32
+ TYPE_SOUNDTRACK = NS_MMD_1 + 'Soundtrack'
33
+ TYPE_SPOKENWORD = NS_MMD_1 + 'Spokenword'
34
+
35
+ attr_accessor :title, :types, :asin, :artist,
36
+ :text_language, :text_script
37
+
38
+ attr_accessor :tracks, :release_events, :discs
39
+
40
+ def initialize
41
+ @tracks = Array.new
42
+ @release_events = Array.new
43
+ @discs = Array.new
44
+ @types = Array.new
45
+ end
46
+
47
+ end
48
+
49
+ end
50
+ end
@@ -0,0 +1,34 @@
1
+ # $Id: release_event.rb 9 2007-05-21 21:16:34Z phw $
2
+ # Copyright (c) 2007, Philipp Wolfer
3
+ # All rights reserved.
4
+ # See LICENSE for permissions.
5
+
6
+ require 'rbrainz/model/label'
7
+
8
+ module MusicBrainz
9
+ module Model
10
+
11
+ # A release event in the MusicBrainz DB.
12
+ #
13
+ # See http://musicbrainz.org/doc/ReleaseEvent.
14
+ class ReleaseEvent
15
+
16
+ attr_accessor :country, :catalog_number,
17
+ :barcode, :label
18
+
19
+ attr_reader :date
20
+
21
+ # Set the date the release took place.
22
+ #
23
+ # Should be an IncompleteDate object or
24
+ # a date string, which will get converted
25
+ # into an IncompleteDate.
26
+ def date=(date)
27
+ date = IncompleteDate.new date unless date.is_a? IncompleteDate
28
+ @date = date
29
+ end
30
+
31
+ end
32
+
33
+ end
34
+ end
@@ -0,0 +1,27 @@
1
+ # $Id: track.rb 13 2007-05-22 22:59:57Z phw $
2
+ # Copyright (c) 2007, Philipp Wolfer
3
+ # All rights reserved.
4
+ # See LICENSE for permissions.
5
+
6
+ require 'rbrainz/model/entity'
7
+
8
+ module MusicBrainz
9
+ module Model
10
+
11
+ # A track in the MusicBrainz DB.
12
+ #
13
+ # See http://musicbrainz.org/doc/Track.
14
+ class Track < Entity
15
+
16
+ attr_accessor :title, :duration, :artist,
17
+ :puids, :releases
18
+
19
+ def initialize
20
+ @puids = Array.new
21
+ @releases = Array.new
22
+ end
23
+
24
+ end
25
+
26
+ end
27
+ end
@@ -0,0 +1,6 @@
1
+ # $Id: webservice.rb 4 2007-05-21 02:04:26Z phw $
2
+ # Copyright (c) 2007, Philipp Wolfer
3
+ # All rights reserved.
4
+ # See LICENSE for permissions.
5
+
6
+ require 'rbrainz/webservice/query'
@@ -0,0 +1,132 @@
1
+ # $Id: filter.rb 4 2007-05-21 02:04:26Z phw $
2
+ # Copyright (c) 2007, Philipp Wolfer
3
+ # All rights reserved.
4
+ # See LICENSE for permissions.
5
+
6
+ require 'uri'
7
+
8
+ module MusicBrainz
9
+ module Webservice
10
+
11
+ # Base class for all filter classes.
12
+ class AbstractFilter
13
+
14
+ # The parameter +filter+ is a hash with filter options.
15
+ # The the concrete classes for a description of those
16
+ # options.
17
+ def initialize(filter)
18
+ raise 'Tried to initialize abstract class.'
19
+ end
20
+
21
+ # Returns the filter list as a query string
22
+ # (without leading +&+).
23
+ def to_s
24
+ @filter.to_a.map {|name, value|
25
+ '%s=%s' % [URI.escape(name.to_s), URI.escape(value.to_s)]
26
+ }.join('&')
27
+ end
28
+
29
+ end
30
+
31
+ class ArtistFilter < AbstractFilter
32
+
33
+ # The parameter +filter+ is a hash with filter options:
34
+ # [:name] Fetch a list of artists with a matching name.
35
+ # [:limit] The maximum number of artists returned. Defaults
36
+ # to 25, the maximum allowed value is 100.
37
+ def initialize(filter)
38
+ @filter = Hash.new
39
+ @filter[:name] = filter[:name] if filter[:name]
40
+ @filter[:limit] = filter[:limit] if filter[:limit]
41
+ end
42
+
43
+ end
44
+
45
+ class ReleaseFilter < AbstractFilter
46
+
47
+ # The parameter +filter+ is a hash with filter options:
48
+ # [:title] Fetch a list of releases with a matching title.
49
+ # [:discid] Fetch all releases matching to the given DiscID.
50
+ # [:artist] The returned releases should match the given artist name.
51
+ # [:artistid] The returned releases should match the given artist ID
52
+ # (36 character ASCII representation). If this is given,
53
+ # the artist parameter is ignored.
54
+ # [:releasetypes] The returned releases must match all of the given
55
+ # release types. This is a list of space separated values
56
+ # like Official, Bootleg, Album, Compilation, etc.
57
+ # [:count] Number of tracks in the release.
58
+ # [:date] Earliest release date for the release.
59
+ # [:asin] The Amazon ASIN.
60
+ # [:lang] The language for this release.
61
+ # [:script] The script used in this release.
62
+ # [:limit] The maximum number of tracks returned. Defaults
63
+ # to 25, the maximum allowed value is 100.
64
+ def initialize(filter)
65
+ @filter = Hash.new
66
+ @filter[:title] = filter[:title] if filter[:title]
67
+ @filter[:discid] = filter[:discid] if filter[:discid]
68
+ @filter[:artist] = filter[:artist] if filter[:artist]
69
+ @filter[:artistid] = filter[:artistid] if filter[:artistid]
70
+ @filter[:releasetypes] = filter[:releasetypes] if filter[:releasetypes]
71
+ @filter[:count] = filter[:count] if filter[:count]
72
+ @filter[:date] = filter[:date] if filter[:date]
73
+ @filter[:asin] = filter[:asin] if filter[:asin]
74
+ @filter[:lang] = filter[:lang] if filter[:lang]
75
+ @filter[:script] = filter[:script] if filter[:script]
76
+ @filter[:limit] = filter[:limit] if filter[:limit]
77
+ end
78
+
79
+ end
80
+
81
+ class TrackFilter < AbstractFilter
82
+
83
+ # The parameter +filter+ is a hash with filter options:
84
+ # [:title] Fetch a list of tracks with a matching title.
85
+ # [:artist] The returned tracks have to match the given
86
+ # artist name.
87
+ # [:release] The returned tracks have to match the given
88
+ # release title.
89
+ # [:duration] The length of the track in milliseconds
90
+ # [:tracknum] The track number
91
+ # [:artistid] The artist's MBID. If this is given, the artist
92
+ # parameter is ignored.
93
+ # [:releaseid] The release's MBID. If this is given, the release
94
+ # parameter is ignored.
95
+ # [:puid] The returned tracks have to match the given PUID.
96
+ # [:count] Number of tracks on the release.
97
+ # [:releasetype] The type of the release this track appears on
98
+ # [:limit] The maximum number of tracks returned. Defaults
99
+ # to 25, the maximum allowed value is 100.
100
+ def initialize(filter)
101
+ @filter = Hash.new
102
+ @filter[:title] = filter[:title] if filter[:title]
103
+ @filter[:artist] = filter[:artist] if filter[:artist]
104
+ @filter[:release] = filter[:release] if filter[:release]
105
+ @filter[:duration] = filter[:duration] if filter[:duration]
106
+ @filter[:tracknum] = filter[:tracknum] if filter[:tracknum]
107
+ @filter[:artistid] = filter[:artistid] if filter[:artistid]
108
+ @filter[:releaseid] = filter[:releaseid] if filter[:releaseid]
109
+ @filter[:puid] = filter[:puid] if filter[:puid]
110
+ @filter[:count] = filter[:count] if filter[:count]
111
+ @filter[:releasetype] = filter[:releasetype] if filter[:releasetype]
112
+ @filter[:limit] = filter[:limit] if filter[:limit]
113
+ end
114
+
115
+ end
116
+
117
+ class LabelFilter < AbstractFilter
118
+
119
+ # The parameter +filter+ is a hash with filter options:
120
+ # [:name] Fetch a list of labels with a matching name.
121
+ # [:limit] The maximum number of labels returned. Defaults
122
+ # to 25, the maximum allowed value is 100.
123
+ def initialize(filter)
124
+ @filter = Hash.new
125
+ @filter[:name] = filter[:name] if filter[:name]
126
+ @filter[:limit] = filter[:limit] if filter[:limit]
127
+ end
128
+
129
+ end
130
+
131
+ end
132
+ end
@@ -0,0 +1,150 @@
1
+ # $Id: includes.rb 4 2007-05-21 02:04:26Z phw $
2
+ # Copyright (c) 2007, Philipp Wolfer
3
+ # All rights reserved.
4
+ # See LICENSE for permissions.
5
+
6
+ require 'uri'
7
+
8
+ module MusicBrainz
9
+ module Webservice
10
+
11
+ # Base class for all include classes.
12
+ class AbstractIncludes
13
+
14
+ def initialize(includes)
15
+ raise 'Tried to initialize abstract class.'
16
+ end
17
+
18
+ def to_s
19
+ if @parameters.size > 0
20
+ return 'inc=' + URI.escape(@parameters.join(' '))
21
+ else
22
+ return ''
23
+ end
24
+ end
25
+
26
+ end
27
+
28
+ class ArtistIncludes < AbstractIncludes
29
+
30
+ # Includes is a hash with the following fields:
31
+ # [:aliases] Include aliases (boolean).
32
+ # [:releases] Array of release types that should be included
33
+ # in the result. All releases of the artist that match
34
+ # all of those types will be included. Use the constants
35
+ # defined in Model::Releases for the release types.
36
+ # [:va_releases] Array of release types. All various artist releases
37
+ # the artist appears on and that match all of those
38
+ # types will be included. Use the constants
39
+ # defined in Model::Releases for the release types.
40
+ # [:artist_rels] Include artist relationships (boolean).
41
+ # [:release_rels] Include release relationships (boolean).
42
+ # [:track_rels] Include track relationships (boolean).
43
+ # [:label_rels] Include label relationships (boolean).
44
+ # [:url_rels] Include url relationships (boolean).
45
+ #
46
+ # TODO: Check release types. It's possible that :releases
47
+ # and :va_releases can't be used in parallel.
48
+ def initialize(includes)
49
+ @parameters = Array.new
50
+ @parameters << 'aliases' if includes[:aliases]
51
+ @parameters << 'artist-rels' if includes[:artist_rels]
52
+ @parameters << 'release-rels' if includes[:release_rels]
53
+ @parameters << 'track-rels' if includes[:track_rels]
54
+ @parameters << 'label-rels' if includes[:label_rels]
55
+ @parameters << 'url-rels' if includes[:url_rels]
56
+
57
+ includes[:releases].each {|release_type|
58
+ @parameters << 'sa-' + release_type.to_s
59
+ } if includes[:releases]
60
+
61
+ includes[:va_releases].each {|release_type|
62
+ @parameters << 'va-' + release_type.to_s
63
+ } if includes[:va_releases]
64
+ end
65
+
66
+ end
67
+
68
+ class ReleaseIncludes < AbstractIncludes
69
+
70
+ # Includes is a hash with the following fields:
71
+ # [:artist] Include track artist (boolean).
72
+ # [:counts] Includes the track number (boolean).
73
+ # [:release_events] Includes the release events (boolean).
74
+ # [:discs] Include the disc IDs (boolean).
75
+ # [:tracks] Include the release tracks (boolean).
76
+ # [:labels] Include the labels under which the release
77
+ # was publlished (boolean).
78
+ # [:artist_rels] Include artist relationships (boolean).
79
+ # [:release_rels] Include release relationships (boolean).
80
+ # [:track_rels] Include track relationships (boolean).
81
+ # [:label_rels] Include label relationships (boolean).
82
+ # [:url_rels] Include url relationships (boolean).
83
+ # [:track_level_rels] Include the relationships for the
84
+ # single tracks as well (boolean).
85
+ def initialize(includes)
86
+ @parameters = Array.new
87
+ @parameters << 'artist' if includes[:artist]
88
+ @parameters << 'counts' if includes[:counts]
89
+ @parameters << 'release-events' if includes[:release_events]
90
+ @parameters << 'discs' if includes[:discs]
91
+ @parameters << 'tracks' if includes[:tracks]
92
+ @parameters << 'labels' if includes[:labels]
93
+ @parameters << 'artist-rels' if includes[:artist_rels]
94
+ @parameters << 'release-rels' if includes[:release_rels]
95
+ @parameters << 'track-rels' if includes[:track_rels]
96
+ @parameters << 'label-rels' if includes[:label_rels]
97
+ @parameters << 'url-rels' if includes[:url_rels]
98
+ @parameters << 'track-level-rels' if includes[:track_level_rels]
99
+ end
100
+
101
+ end
102
+
103
+ class TrackIncludes < AbstractIncludes
104
+
105
+ # Includes is a hash with the following fields:
106
+ # [:artist] Include track artist (boolean).
107
+ # [:releases] Includes releases the track appears on (boolean).
108
+ # [:puids] Include the track's PUIDs (boolean).
109
+ # [:artist_rels] Include artist relationships (boolean).
110
+ # [:release_rels] Include release relationships (boolean).
111
+ # [:track_rels] Include track relationships (boolean).
112
+ # [:label_rels] Include label relationships (boolean).
113
+ # [:url_rels] Include url relationships (boolean).
114
+ def initialize(includes)
115
+ @parameters = Array.new
116
+ @parameters << 'artist' if includes[:artist]
117
+ @parameters << 'releases' if includes[:releases]
118
+ @parameters << 'puids' if includes[:puids]
119
+ @parameters << 'artist-rels' if includes[:artist_rels]
120
+ @parameters << 'release-rels' if includes[:release_rels]
121
+ @parameters << 'track-rels' if includes[:track_rels]
122
+ @parameters << 'label-rels' if includes[:label_rels]
123
+ @parameters << 'url-rels' if includes[:url_rels]
124
+ end
125
+
126
+ end
127
+
128
+ class LabelIncludes < AbstractIncludes
129
+
130
+ # Includes is a hash with the following fields:
131
+ # [:aliases] Include aliases (boolean).
132
+ # [:artist_rels] Include artist relationships (boolean).
133
+ # [:release_rels] Include release relationships (boolean).
134
+ # [:track_rels] Include track relationships (boolean).
135
+ # [:label_rels] Include label relationships (boolean).
136
+ # [:url_rels] Include url relationships (boolean).
137
+ def initialize(includes)
138
+ @parameters = Array.new
139
+ @parameters << 'aliases' if includes[:aliases]
140
+ @parameters << 'artist-rels' if includes[:artist_rels]
141
+ @parameters << 'release-rels' if includes[:release_rels]
142
+ @parameters << 'track-rels' if includes[:track_rels]
143
+ @parameters << 'label-rels' if includes[:label_rels]
144
+ @parameters << 'url-rels' if includes[:url_rels]
145
+ end
146
+
147
+ end
148
+
149
+ end
150
+ end
@@ -0,0 +1,404 @@
1
+ # $Id: mbxml.rb 17 2007-05-23 16:51:11Z phw $
2
+ # Copyright (c) 2007, Philipp Wolfer
3
+ # All rights reserved.
4
+ # See LICENSE for permissions.
5
+
6
+ require 'rbrainz/model'
7
+ require 'rexml/document'
8
+ include REXML
9
+
10
+ module MusicBrainz
11
+ module Webservice
12
+
13
+ # Class to read the XML data returned by the MusicBrainz
14
+ # webservice and create the corresponding model classes.
15
+ # The class understands the MusicBrainz XML Metadata Version 1.0
16
+ # schema.
17
+ # See http://musicbrainz.org/doc/MusicBrainzXMLMetaData for more
18
+ # information on the MusicBrainz XML Metadata schema.
19
+ class MBXML
20
+
21
+ def initialize(xml)
22
+ @document = Document.new(xml)
23
+
24
+ # Already loaded artists, releases, tracks
25
+ # and labels will get cached in these variables
26
+ # to link to them if they occure multiple times
27
+ # inside the same document.
28
+ @artists = Hash.new
29
+ @releases = Hash.new
30
+ @tracks = Hash.new
31
+ @labels = Hash.new
32
+ end
33
+
34
+ # Read the XML string and create an entity model
35
+ # for the given entity type if it is present in
36
+ # the document.
37
+ # Returns nil if no entity of the given type is present.
38
+ def get_entity(entity_type)
39
+ # Search for the first occuring node of type entity which is a child node
40
+ # of the metadata element.
41
+ entity = @document.elements["//[local-name()='metadata' and namespace-uri()='%s']/%s[1]" %
42
+ [Model::NS_MMD_1, entity_type]]
43
+
44
+ unless entity.nil? or entity.is_a? REXML::Text
45
+ case entity.name
46
+ when 'artist'
47
+ return create_artist(entity)
48
+ when 'release'
49
+ return create_release(entity)
50
+ when 'track'
51
+ return create_track(entity)
52
+ when 'label'
53
+ return create_label(entity)
54
+ end
55
+ else
56
+ return nil
57
+ end
58
+ end
59
+
60
+ # Read the XML string and create a list of entity
61
+ # models for the given entity type. Ther must be
62
+ # an entity-list element as a child of the metadata
63
+ # element in the document.
64
+ # Returns nil if no entity list of the given type is present.
65
+ # Returns an empty array if the list is empty.
66
+ def get_entity_list(entity_type)
67
+ # Search for the first occuring node of type entity which is a child node
68
+ # of the metadata element.
69
+ entity_list = @document.elements["//[local-name()='metadata' and namespace-uri()='%s']/%s-list[1]" %
70
+ [Model::NS_MMD_1, entity_type]]
71
+
72
+ unless entity_list.nil? or entity_list.is_a? REXML::Text
73
+ list = Array.new
74
+
75
+ case entity_list.name
76
+ when 'artist-list'
77
+ read_artist_list(entity_list) {|model|
78
+ list << model
79
+ }
80
+ return list
81
+ when 'release-list'
82
+ read_release_list(entity_list) {|model|
83
+ list << model
84
+ }
85
+ return list
86
+ when 'track-list'
87
+ read_track_list(entity_list) {|model|
88
+ list << model
89
+ }
90
+ return list
91
+ when 'label-list'
92
+ read_label_list(entity_list) {|model|
93
+ list << model
94
+ }
95
+ return list
96
+ end
97
+
98
+ if iterator
99
+ list = Array.new
100
+ iterator.each {|model|
101
+ raise model.inspect
102
+ list << model
103
+ }
104
+ return list
105
+ end
106
+ end
107
+ return nil
108
+ end
109
+
110
+ private
111
+
112
+ # Iterate over a list of artists.
113
+ #
114
+ # The node must be of the type <em>artist-list</em>.
115
+ def read_artist_list(node)
116
+ node.elements.each('artist') {|child|
117
+ yield create_artist(child)
118
+ }
119
+ end
120
+
121
+ # Create an +Artist+ object from the given artist node.
122
+ #
123
+ # TODO: relation list
124
+ def create_artist(node)
125
+ if node.attributes['id'] and @artists[node.attributes['id']]
126
+ artist = @artists[node.attributes['id']]
127
+ else
128
+ artist = Model::Artist.new
129
+ @artists[node.attributes['id']] = artist
130
+ end
131
+
132
+ # Read all defined data fields
133
+ artist.id = node.attributes['id']
134
+ artist.type = MBXML.add_metadata_namespace(node.attributes['type']) if node.attributes['type']
135
+
136
+ artist.name = node.elements['name'].text if node.elements['name']
137
+ artist.sort_name = node.elements['sort-name'].text if node.elements['sort-name']
138
+ artist.disambiguation = node.elements['disambiguation'].text if node.elements['disambiguation']
139
+
140
+ if life_span = node.elements['life-span']
141
+ if life_span.attributes['begin']
142
+ artist.begin_date = Model::IncompleteDate.new life_span.attributes['begin']
143
+ end
144
+ if life_span.attributes['end']
145
+ artist.end_date = Model::IncompleteDate.new life_span.attributes['end']
146
+ end
147
+ end
148
+
149
+ # Read the alias list
150
+ if node.elements['alias-list']
151
+ read_alias_list(node.elements['alias-list']) {|artist_alias|
152
+ artist.aliases << artist_alias
153
+ }
154
+ end
155
+
156
+ # Read the release list
157
+ if node.elements['release-list']
158
+ read_release_list(node.elements['release-list']) {|release|
159
+ release.artist = artist unless release.artist
160
+ artist.releases << release
161
+ }
162
+ end
163
+
164
+ return artist
165
+ end
166
+
167
+ # Iterate over a list of releases.
168
+ #
169
+ # The node must be of the type <em>release-list</em>.
170
+ def read_release_list(node)
171
+ node.elements.each('release') {|child|
172
+ yield create_release(child)
173
+ }
174
+ end
175
+
176
+ # Create a +Release+ object from the given release node.
177
+ #
178
+ # TODO: PUID list
179
+ # TODO: relation list
180
+ def create_release(node)
181
+ if node.attributes['id'] and @releases[node.attributes['id']]
182
+ release = @releases[node.attributes['id']]
183
+ else
184
+ release = Model::Release.new
185
+ @releases[node.attributes['id']] = release
186
+ end
187
+
188
+ # Read all defined data fields
189
+ release.id = node.attributes['id']
190
+ release.title = node.elements['title'].text if node.elements['title']
191
+ release.asin = node.elements['asin'].text if node.elements['asin']
192
+ release.artist = create_artist(node.elements['artist']) if node.elements['artist']
193
+
194
+ # Read the types
195
+ node.attributes['type'].split(' ').each {|type|
196
+ release.types << MBXML.add_metadata_namespace(type)
197
+ } if node.attributes['type']
198
+
199
+ # Read the text representation information.
200
+ if text_representation = node.elements['text-representation']
201
+ release.text_language = text_representation.attributes['language']
202
+ release.text_script = text_representation.attributes['script']
203
+ end
204
+
205
+ # Read the track list
206
+ if node.elements['track-list']
207
+ read_track_list(node.elements['track-list']) {|track|
208
+ track.artist = release.artist unless track.artist
209
+ track.releases << release
210
+ release.tracks << track
211
+ }
212
+ end
213
+
214
+ # Read the release event list
215
+ if node.elements['release-event-list']
216
+ read_release_event_list(node.elements['release-event-list']) {|event|
217
+ release.release_events << event
218
+ }
219
+ end
220
+
221
+ # Read the disc list
222
+ if node.elements['disc-list']
223
+ read_disc_list(node.elements['disc-list']) {|disc|
224
+ release.discs << disc
225
+ }
226
+ end
227
+
228
+ return release
229
+ end
230
+
231
+ # Iterate over a list of tracks.
232
+ #
233
+ # The node must be of the type <em>track-list</em>.
234
+ def read_track_list(node)
235
+ node.elements.each('track') {|child|
236
+ yield create_track(child)
237
+ }
238
+ end
239
+
240
+ # Create a +Track+ object from the given track node.
241
+ #
242
+ # TODO: relation list
243
+ def create_track(node)
244
+ if node.attributes['id'] and @tracks[node.attributes['id']]
245
+ track = @tracks[node.attributes['id']]
246
+ else
247
+ track = Model::Track.new
248
+ @tracks[node.attributes['id']] = track
249
+ end
250
+
251
+ # Read all defined data fields
252
+ track.id = node.attributes['id']
253
+ track.title = node.elements['title'].text if node.elements['title']
254
+ track.duration = node.elements['duration'].text.to_i if node.elements['duration']
255
+ track.artist = create_artist(node.elements['artist']) if node.elements['artist']
256
+
257
+ # Read the release list
258
+ if node.elements['release-list']
259
+ read_release_list(node.elements['release-list']) {|release|
260
+ release.tracks << track
261
+ track.releases << release
262
+ }
263
+ end
264
+
265
+ # Read the PUID list
266
+ if node.elements['puid-list']
267
+ read_puid_list(node.elements['puid-list']) {|puid|
268
+ track.puids << puid
269
+ }
270
+ end
271
+
272
+ return track
273
+ end
274
+
275
+ # Iterate over a list of labels.
276
+ #
277
+ # The node must be of the type <em>label-list</em>.
278
+ def read_label_list(node)
279
+ node.elements.each('label') {|child|
280
+ yield create_label(child)
281
+ }
282
+ end
283
+
284
+ # Create a +Label+ object from the given label node.
285
+ #
286
+ # TODO: Relations
287
+ def create_label(node)
288
+ if node.attributes['id'] and @labels[node.attributes['id']]
289
+ label = @labels[node.attributes['id']]
290
+ else
291
+ label = Model::Label.new
292
+ @labels[node.attributes['id']] = label
293
+ end
294
+
295
+ # Read all defined data fields
296
+ label.id = node.attributes['id']
297
+ label.type = MBXML.add_metadata_namespace(node.attributes['type']) if node.attributes['type']
298
+
299
+ label.name = node.elements['name'].text if node.elements['name']
300
+ label.sort_name = node.elements['sort-name'].text if node.elements['sort-name']
301
+ label.code = node.elements['label-code'].text if node.elements['label-code']
302
+ label.disambiguation = node.elements['disambiguation'].text if node.elements['disambiguation']
303
+ label.country = node.elements['country'].text if node.elements['country']
304
+
305
+ if life_span = node.elements['life-span']
306
+ if life_span.attributes['begin']
307
+ label.founding_date = Model::IncompleteDate.new life_span.attributes['begin']
308
+ end
309
+ if life_span.attributes['end']
310
+ label.dissolving_date = Model::IncompleteDate.new life_span.attributes['end']
311
+ end
312
+ end
313
+
314
+ # Read the alias list
315
+ if node.elements['release-list']
316
+ read_release_list(node.elements['release-list']) {|release|
317
+ label.releases << release
318
+ }
319
+ end
320
+
321
+ return label
322
+ end
323
+
324
+ # Iterate over a list of aliases.
325
+ #
326
+ # The node must be of the type <em>alias-list</em>.
327
+ def read_alias_list(node)
328
+ node.elements.each('alias') {|child|
329
+ yield create_alias(child)
330
+ }
331
+ end
332
+
333
+ # Create an +Alias+ object from the given alias node.
334
+ def create_alias(node)
335
+ alias_model = Model::Alias.new
336
+ alias_model.name = node.text
337
+ alias_model.type = node.attributes['type']
338
+ alias_model.script = node.attributes['script']
339
+ return alias_model
340
+ end
341
+
342
+ # Iterate over a list of release events.
343
+ #
344
+ # The node must be of the type <em>release-event-list</em>.
345
+ def read_release_event_list(node)
346
+ node.elements.each('event') {|child|
347
+ yield create_release_event(child)
348
+ }
349
+ end
350
+
351
+ # Create an +ReleaseEvent+ object from the given release event node.
352
+ def create_release_event(node)
353
+ event = Model::ReleaseEvent.new
354
+
355
+ # Read all defined data fields
356
+ if node.attributes['date']
357
+ event.date = Model::IncompleteDate.new node.attributes['date']
358
+ end
359
+ event.country = node.attributes['country']
360
+ event.catalog_number = node.attributes['catalog-number']
361
+ event.barcode = node.attributes['barcode']
362
+ event.label = create_label(node.elements['label']) if node.elements['label']
363
+
364
+ return event
365
+ end
366
+
367
+ # Iterate over a list of PUIDs.
368
+ #
369
+ # The node must be of the type <em>puid-list</em>.
370
+ def read_puid_list(node)
371
+ node.elements.each('puid') {|child|
372
+ yield child.attributes['id']
373
+ }
374
+ end
375
+
376
+ # Iterate over a list of discs.
377
+ #
378
+ # The node must be of the type <em>disc-list</em>.
379
+ def read_disc_list(node)
380
+ node.elements.each('disc') {|child|
381
+ disc = Model::Disc.new
382
+ disc.id = child.attributes['id']
383
+ disc.sectors = child.attributes['sectors'].to_i
384
+ yield disc
385
+ }
386
+ end
387
+
388
+ # Helper method which will return the given property
389
+ # extended by the metadata namespace. If the property
390
+ # already includes the namespace it will be returned
391
+ # unchanged.
392
+ def self.add_metadata_namespace(metadata_property)
393
+ regex = Regexp.new("/#{Model::NS_MMD_1}[a-z-]/i")
394
+ unless metadata_property =~ regex
395
+ return Model::NS_MMD_1 + metadata_property
396
+ else
397
+ return metadata_property
398
+ end
399
+ end
400
+
401
+ end
402
+
403
+ end
404
+ end