rbrainz 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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