libis-tools 1.0.5-java

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 (71) hide show
  1. checksums.yaml +7 -0
  2. data/.coveralls.yml +2 -0
  3. data/.gitignore +16 -0
  4. data/.rspec +2 -0
  5. data/.travis.yml +40 -0
  6. data/Gemfile +7 -0
  7. data/README.md +202 -0
  8. data/Rakefile +11 -0
  9. data/bin/libis_tool +5 -0
  10. data/lib/libis-tools.rb +1 -0
  11. data/lib/libis/tools.rb +25 -0
  12. data/lib/libis/tools/assert.rb +52 -0
  13. data/lib/libis/tools/checksum.rb +106 -0
  14. data/lib/libis/tools/cli/cli_helper.rb +189 -0
  15. data/lib/libis/tools/cli/reorg.rb +416 -0
  16. data/lib/libis/tools/command.rb +133 -0
  17. data/lib/libis/tools/command_line.rb +23 -0
  18. data/lib/libis/tools/config.rb +147 -0
  19. data/lib/libis/tools/config_file.rb +85 -0
  20. data/lib/libis/tools/csv.rb +38 -0
  21. data/lib/libis/tools/deep_struct.rb +71 -0
  22. data/lib/libis/tools/extend/array.rb +16 -0
  23. data/lib/libis/tools/extend/empty.rb +7 -0
  24. data/lib/libis/tools/extend/hash.rb +147 -0
  25. data/lib/libis/tools/extend/kernel.rb +25 -0
  26. data/lib/libis/tools/extend/ostruct.rb +3 -0
  27. data/lib/libis/tools/extend/roo.rb +91 -0
  28. data/lib/libis/tools/extend/string.rb +94 -0
  29. data/lib/libis/tools/extend/struct.rb +29 -0
  30. data/lib/libis/tools/extend/symbol.rb +8 -0
  31. data/lib/libis/tools/logger.rb +130 -0
  32. data/lib/libis/tools/mets_dnx.rb +61 -0
  33. data/lib/libis/tools/mets_file.rb +504 -0
  34. data/lib/libis/tools/mets_objects.rb +547 -0
  35. data/lib/libis/tools/parameter.rb +372 -0
  36. data/lib/libis/tools/spreadsheet.rb +196 -0
  37. data/lib/libis/tools/temp_file.rb +42 -0
  38. data/lib/libis/tools/thread_safe.rb +31 -0
  39. data/lib/libis/tools/version.rb +5 -0
  40. data/lib/libis/tools/xml_document.rb +583 -0
  41. data/libis-tools.gemspec +55 -0
  42. data/spec/assert_spec.rb +65 -0
  43. data/spec/checksum_spec.rb +68 -0
  44. data/spec/command_spec.rb +90 -0
  45. data/spec/config_file_spec.rb +83 -0
  46. data/spec/config_spec.rb +113 -0
  47. data/spec/csv_spec.rb +159 -0
  48. data/spec/data/test-headers.csv +2 -0
  49. data/spec/data/test-headers.tsv +2 -0
  50. data/spec/data/test-noheaders.csv +1 -0
  51. data/spec/data/test-noheaders.tsv +1 -0
  52. data/spec/data/test.data +9 -0
  53. data/spec/data/test.xlsx +0 -0
  54. data/spec/data/test.xml +8 -0
  55. data/spec/data/test.yml +2 -0
  56. data/spec/data/test_config.yml +15 -0
  57. data/spec/deep_struct_spec.rb +138 -0
  58. data/spec/logger_spec.rb +165 -0
  59. data/spec/mets_file_spec.rb +223 -0
  60. data/spec/parameter_container_spec.rb +152 -0
  61. data/spec/parameter_spec.rb +148 -0
  62. data/spec/spec_helper.rb +29 -0
  63. data/spec/spreadsheet_spec.rb +1820 -0
  64. data/spec/temp_file_spec.rb +76 -0
  65. data/spec/test.xsd +20 -0
  66. data/spec/thread_safe_spec.rb +64 -0
  67. data/spec/xmldocument_spec.rb +421 -0
  68. data/test/test_helper.rb +7 -0
  69. data/test/webservices/test_ca_item_info.rb +59 -0
  70. data/test/webservices/test_ca_search.rb +35 -0
  71. metadata +437 -0
@@ -0,0 +1,61 @@
1
+ module Libis
2
+ module Tools
3
+ class MetsFile
4
+
5
+ # Base class for a DNX section in the METS file. Each {DnxSection} derived class has a unique tag which will
6
+ # become the id of the <section> element.
7
+ class DnxSection < OpenStruct
8
+
9
+ # Instance method to access the class tag for DNX.
10
+ def tag
11
+ _tag = self.class.name.split('::').last
12
+ _tag[0] = _tag[0].downcase
13
+ _tag
14
+ end
15
+ end
16
+
17
+ # Specialized DNX section.
18
+ class ObjectCharacteristics < DnxSection; end
19
+ # Specialized DNX section.
20
+ class GeneralIECharacteristics < DnxSection; end
21
+ # Specialized DNX section.
22
+ class GeneralRepCharacteristics < DnxSection; end
23
+ # Specialized DNX section.
24
+ class GeneralFileCharacteristics < DnxSection; end
25
+ # Specialized DNX section.
26
+ class RetentionPolicy < DnxSection; end
27
+ # Specialized DNX section.
28
+ class FileFixity < DnxSection; end
29
+ # Specialized DNX section.
30
+ class AccessRightsPolicy < DnxSection; end
31
+ # Specialized DNX section.
32
+ class WebHarvesting < DnxSection; end
33
+ # Specialized DNX section.
34
+ class Collection < DnxSection; end
35
+ # Specialized DNX section.
36
+ class PreservationLevel < DnxSection; end
37
+ # Specialized DNX section.
38
+ class EnvironmentDependencies < DnxSection; end
39
+ # Specialized DNX section.
40
+ class EnvHardwareRegistry < DnxSection; end
41
+ # Specialized DNX section.
42
+ class EnvSoftwareRegistry < DnxSection; end
43
+ # Specialized DNX section.
44
+ class EnvironmentHardware < DnxSection; end
45
+ # Specialized DNX section.
46
+ class EnvironmentSoftware < DnxSection; end
47
+ # Specialized DNX section.
48
+ class Relationship < DnxSection; end
49
+ # Specialized DNX section.
50
+ class Environment < DnxSection; end
51
+ # Specialized DNX section.
52
+ class Inhibitors < DnxSection; end
53
+ # Specialized DNX section.
54
+ class SignatureInformation < DnxSection; end
55
+ # Specialized DNX section.
56
+ class CreatingApplication < DnxSection; end
57
+
58
+ end
59
+ end
60
+ end
61
+
@@ -0,0 +1,504 @@
1
+ # encoding: utf-8
2
+ require 'ostruct'
3
+
4
+ require 'libis/tools/extend/hash'
5
+ require_relative 'xml_document'
6
+ require_relative 'thread_safe'
7
+ require_relative 'mets_dnx'
8
+ require_relative 'mets_objects'
9
+
10
+ module Libis
11
+ module Tools
12
+
13
+ # noinspection RubyResolve
14
+
15
+ # This class supports creating METS files in a friendlier way than by creating an XML file.
16
+ #
17
+ # There are sub-classes that represent {::Libis::Tools::MetsFile::Representation}s,
18
+ # {::Libis::Tools::MetsFile::File}s, {::Libis::Tools::MetsFile::Div}isions and {::Libis::Tools::MetsFile::Map}s.
19
+ # These are simple container classes that take care of assigning proper ids and accept most known attributes.
20
+ # Each of the container classes have a corresponding method on the METS class that takes a Hash with attributes
21
+ # and returns the created object.
22
+ #
23
+ # {::Libis::Tools::MetsFile::Div} and {::Libis::Tools::MetsFile::File} instances can be added to a
24
+ # {::Libis::Tools::MetsFile::Div} instance and a Div can be associated with a
25
+ # {::Libis::Tools::MetsFile::Representation}, thus creating a structmap.
26
+ #
27
+ # The {#amd_info=} method on the {MetsFile} class sets the main amd parameters and
28
+ #
29
+ # With the help of the {DnxSection} class and it's derived classes, the container classes can generate the amd
30
+ # sections for the METS file.
31
+ class MetsFile
32
+ include ::Libis::Tools::ThreadSafe
33
+
34
+ # Keeps track of {::Libis::Tools::MetsFile::Representation}s created
35
+ attr_reader :representations
36
+ # Keeps track of {::Libis::Tools::MetsFile::File}s created
37
+ attr_reader :files
38
+ # Keeps track of {::Libis::Tools::MetsFile::Div}s created
39
+ attr_reader :divs
40
+ # Keeps track of {::Libis::Tools::MetsFile::Map}s created
41
+ attr_reader :maps
42
+
43
+ # noinspection RubyConstantNamingConvention
44
+
45
+ # Namespace constants for METS XML
46
+ NS = {
47
+ mets: 'http://www.loc.gov/METS/',
48
+ dc: 'http://purl.org/dc/elements/1.1/',
49
+ dnx: 'http://www.exlibrisgroup.com/dps/dnx',
50
+ xlin: 'http://www.w3.org/1999/xlink',
51
+ }
52
+
53
+ # Creates an initializes a new {MetsFile} instance
54
+ def initialize
55
+ @representations = {}
56
+ @files = {}
57
+ @divs = {}
58
+ @maps = {}
59
+ @dnx = {}
60
+ @dc_record = nil
61
+ @id_map = {}
62
+ end
63
+
64
+ # Reads an existing METS XML file and parses into a large Hash structure for inspection.
65
+ #
66
+ # It will not immediately allow you to create a {MetsFile} instance from it, but with some inspection and
67
+ # knowledge of METS file structure it should be possible to recreate a similar file using the result.
68
+ #
69
+ # The returned Hash has the following structure:
70
+ #
71
+ # * :amd - the general AMD section with subsections
72
+ # * :dmd - the general DMD section with the DC record(s)
73
+ # Each amd section has one or more subsections with keys :tech, :rights, :source or :digiprov. Each
74
+ # subsection is a Hash with section id as key and an array as value. For each <record> element a Hash is
75
+ # added to the array with <key@id> as key and <key> content as value.
76
+ #
77
+ # @param [String,Hash,::Libis::Tools::XmlDocument, Nokogiri::XML::Document] xml XML file in any of the listed formats.
78
+ # @return [Hash] The parsed information.
79
+ def self.parse(xml)
80
+ xml_doc = case xml
81
+ when String
82
+ Libis::Tools::XmlDocument.parse(xml).document
83
+ when Hash
84
+ Libis::Tools::XmlDocument.from_hash(xml).document
85
+ when Libis::Tools::XmlDocument
86
+ xml.document
87
+ when Nokogiri::XML::Document
88
+ xml
89
+ else
90
+ raise ArgumentError, "Libis::Tools::MetsFile#parse does not accept input of type #{xml.class}"
91
+ end
92
+
93
+ dmd_sec = xml_doc.root.xpath('mets:dmdSec', NS).inject({}) do |hash_dmd, dmd|
94
+ hash_dmd[dmd[:ID]] = dmd.xpath('.//dc:record', NS).first.children.inject({}) do |h, c|
95
+ h[c.name] = c.content
96
+ h
97
+ end
98
+ hash_dmd
99
+ end
100
+ amd_sec = xml_doc.root.xpath('mets:amdSec', NS).inject({}) do |hash_amd, amd|
101
+ hash_amd[amd[:ID]] = [:tech, :rights, :source, :digiprov].inject({}) do |hash_sec, sec|
102
+ md = amd.xpath("mets:#{sec}MD", NS).first
103
+ return hash_sec unless md
104
+ # hash_sec[sec] = md.xpath('mets:mdWrap/dnx:dnx/dnx:section', NS).inject({}) do |hash_md, dnx_sec|
105
+ hash_sec[sec] = md.xpath('.//dnx:section', NS).inject({}) do |hash_md, dnx_sec|
106
+ hash_md[dnx_sec[:id]] = dnx_sec.xpath('dnx:record', NS).inject([]) do |records, dnx_record|
107
+ records << dnx_record.xpath('dnx:key', NS).inject({}) do |record_hash, key|
108
+ record_hash[key[:id]] = key.content
109
+ record_hash
110
+ end
111
+ records
112
+ end
113
+ hash_md
114
+ end
115
+ hash_sec
116
+ end
117
+ hash_amd
118
+ end
119
+ rep_sec = xml_doc.root.xpath('.//mets:fileGrp', NS).inject({}) do |hash_rep, rep|
120
+ hash_rep[rep[:ID]] = {
121
+ amd: amd_sec[rep[:ADMID]],
122
+ dmd: amd_sec[rep[:DMDID]]
123
+ }.cleanup.merge(
124
+ rep.xpath('mets:file', NS).inject({}) do |hash_file, file|
125
+ hash_file[file[:ID]] = {
126
+ group: file[:GROUPID],
127
+ amd: amd_sec[file[:ADMID]],
128
+ dmd: dmd_sec[file[:DMDID]],
129
+ }.cleanup
130
+ hash_file
131
+ end
132
+ )
133
+ hash_rep
134
+ end
135
+ {amd: amd_sec['ie-amd'],
136
+ dmd: dmd_sec['ie-dmd'],
137
+ }.cleanup.merge(
138
+ xml_doc.root.xpath('.//mets:structMap[@TYPE="PHYSICAL"]', NS).inject({}) do |hash_map, map|
139
+ rep_id = map[:ID].gsub(/-\d+$/, '')
140
+ rep = rep_sec[rep_id]
141
+ div_parser = lambda do |div|
142
+ if div[:TYPE] == 'FILE'
143
+ file_id = div.xpath('mets:fptr').first[:FILEID]
144
+ {
145
+ id: file_id
146
+ }.merge rep[file_id]
147
+ else
148
+ div.children.inject({}) do |hash, child|
149
+ # noinspection RubyScope
150
+ hash[child[:LABEL]] = div_parser.call(child)
151
+ hash
152
+ end
153
+ end
154
+ end
155
+ hash_map[map.xpath('mets:div').first[:LABEL]] = {
156
+ id: rep_id,
157
+ amd: rep_sec[rep_id][:amd],
158
+ dmd: rep_sec[rep_id][:dmd],
159
+ }.cleanup.merge(
160
+ map.xpath('mets:div', NS).inject({}) do |hash, div|
161
+ hash[div[:LABEL]] = div_parser.call(div)
162
+ end
163
+ )
164
+ hash_map
165
+ end
166
+ )
167
+ end
168
+
169
+ # Sets the DC record for the global amd section.
170
+ #
171
+ # @param [String] xml Serialized Dublin Core XML file
172
+ def dc_record=(xml)
173
+ @dc_record = xml
174
+ end
175
+
176
+ # Sets the attributes for the global amd section.
177
+ #
178
+ # @param [Hash] hash name, value pairs for the DNX sections. Each will go into it's appropriate AMD and DNX
179
+ # sections automatically.
180
+ # The following names are currently supported:
181
+ # * status
182
+ # * entity_type
183
+ # * user_a
184
+ # * user_b
185
+ # * user_c
186
+ # * submission_reason
187
+ # * retention_id - RentionPolicy ID
188
+ # * harvest_url
189
+ # * harvest_id
190
+ # * harvest_target
191
+ # * harvest_group
192
+ # * harvest_date
193
+ # * harvest_time
194
+ # * collection_id - Collection ID where the IE should be added to
195
+ # * access_right - AccessRight ID
196
+ # * source_metadata - Array with hashes like {type: 'MyXML', data: '<xml ....>'}
197
+ def amd_info=(hash)
198
+ tech_data = []
199
+ data = {
200
+ groupID: hash[:group_id]
201
+ }.cleanup
202
+ tech_data << ObjectCharacteristics.new(data) unless data.empty?
203
+ data = {
204
+ status: hash[:status],
205
+ IEEntityType: hash[:entity_type],
206
+ UserDefinedA: hash[:user_a],
207
+ UserDefinedB: hash[:user_b],
208
+ UserDefinedC: hash[:user_c],
209
+ submissionReason: hash[:submission_reason],
210
+ }.cleanup
211
+ tech_data << GeneralIECharacteristics.new(data) unless data.empty?
212
+ data = {
213
+ policyId: hash[:retention_id],
214
+ }.cleanup
215
+ tech_data << RetentionPolicy.new(data) unless data.empty?
216
+ data = {
217
+ primarySeedURL: hash[:harvest_url],
218
+ WCTIdentifier: hash[:harvest_id],
219
+ targetName: hash[:harvest_target],
220
+ group: hash[:harvest_group],
221
+ harvestDate: hash[:harvest_date],
222
+ harvestTime: hash[:harvest_time],
223
+ }.cleanup
224
+ tech_data << WebHarvesting.new(data) unless data.empty?
225
+ data = {
226
+ collectionId: hash[:collection_id]
227
+ }.cleanup
228
+ tech_data << Collection.new(data) unless data.empty?
229
+ @dnx[:tech] = tech_data unless tech_data.empty?
230
+ data = {
231
+ policyId: hash[:access_right]
232
+ }.cleanup
233
+ rights_data = []
234
+ rights_data << AccessRightsPolicy.new(data) unless data.empty?
235
+ @dnx[:rights] = rights_data unless rights_data.empty?
236
+ (hash[:source_metadata] || []).each_with_index do |metadata, i|
237
+ @dnx["source-#{metadata[:type].to_s.upcase}-#{i+1}"] = metadata[:data]
238
+ end
239
+ end
240
+
241
+ def get_id(klass)
242
+ self.mutex.synchronize do
243
+ @id_map[klass] = (@id_map[klass] ? @id_map[klass] + 1 : 1)
244
+ return @id_map[klass]
245
+ end
246
+ end
247
+
248
+ # Create a new representation. See {::Libis::Tools::MetsFile::Representation} for supported Hash keys.
249
+ # @param [Hash] hash
250
+ # @return [Libis::Tools::MetsFile::Representation]
251
+ def representation(hash = {})
252
+ rep = ::Libis::Tools::MetsFile::Representation.new
253
+ rep.set_id get_id(::Libis::Tools::MetsFile::Representation)
254
+ rep.set_from_hash hash
255
+ @representations[rep.id] = rep
256
+ end
257
+
258
+ # Create a new division. See {Div} for supported Hash keys.
259
+ # @param [Hash] hash
260
+ # @return [Libis::Tools::MetsFile::Div]
261
+ def div(hash = {})
262
+ div = Libis::Tools::MetsFile::Div.new
263
+ div.set_id get_id(::Libis::Tools::MetsFile::Div)
264
+ div.set_from_hash hash
265
+ @divs[div.id] = div
266
+ end
267
+
268
+ # Create a new file. See {File} for supported Hash keys.
269
+ # @param [Hash] hash
270
+ # @return [Libis::Tools::MetsFile::File]
271
+ def file(hash = {})
272
+ file = Libis::Tools::MetsFile::File.new
273
+ file.set_id get_id(::Libis::Tools::MetsFile::File)
274
+ file.set_from_hash hash
275
+ @files[file.id] = file
276
+ end
277
+
278
+ # Create a new structmap.
279
+ # @param [Libis::Tools::MetsFile::Representation] rep
280
+ # @param [Libis::Tools::MetsFile::Div] div
281
+ # @param [Boolean] logical if true, create a logical structmap; if false (default): a physical structmap.
282
+ # @return [Libis::Tools::MetsFile::Map]
283
+ def map(rep, div, logical = false)
284
+ map = Libis::Tools::MetsFile::Map.new
285
+ map.set_id get_id(::Libis::Tools::MetsFile::Map)
286
+ map.representation = rep
287
+ map.div = div
288
+ map.is_logical = logical
289
+ @maps[map.id] = map
290
+ end
291
+
292
+ # Create the METS XML document.
293
+ # @return [Libis::Tools::XmlDocument]
294
+ def xml_doc
295
+ ::Libis::Tools::XmlDocument.build do |xml|
296
+ xml[:mets].mets(
297
+ 'xmlns:mets' => NS[:mets],
298
+ ) {
299
+ add_dmd(xml)
300
+ add_amd(xml)
301
+ add_filesec(xml)
302
+ add_struct_map(xml)
303
+ }
304
+ end
305
+ end
306
+
307
+ protected
308
+
309
+ # ID for the DMD section of a representation, division or file
310
+ def dmd_id(id)
311
+ "#{id}-dmd"
312
+ end
313
+
314
+ # ID for the AMD section of a representation, division or file
315
+ def amd_id(id)
316
+ "#{id}-amd"
317
+ end
318
+
319
+ # Helper method to create the XML DMD sections
320
+ def add_dmd(xml, object = nil)
321
+ case object
322
+ when NilClass
323
+ add_dmd_section(xml, 'ie', @dc_record)
324
+ # @representations.values.each { |rep| add_dmd(xml, rep) }
325
+ @files.values.each { |file| add_dmd(xml, file) }
326
+ when Libis::Tools::MetsFile::File
327
+ add_dmd_section(xml, object.xml_id, object.dc_record)
328
+ # when Representation
329
+ # add_dmd_section(xml, object.xml_id, object.dc_record)
330
+ else
331
+ raise RuntimeError, "Unsupported object type: #{object.class}"
332
+ end
333
+ end
334
+
335
+ # Helper method to create the XML AMD sections
336
+ def add_amd(xml, object = nil)
337
+ case object
338
+ when NilClass
339
+ raise RuntimeError, 'No IE amd info present.' unless @dnx
340
+ add_amd_section(xml, 'ie', @dnx)
341
+ @representations.values.each { |rep| add_amd(xml, rep) }
342
+ @files.values.each { |file| add_amd(xml, file) }
343
+ when Libis::Tools::MetsFile::File
344
+ add_amd_section(xml, object.xml_id, object.amd)
345
+ when Libis::Tools::MetsFile::Representation
346
+ add_amd_section(xml, object.xml_id, object.amd)
347
+ else
348
+ raise RuntimeError, "Unsupported object type: #{object.class}"
349
+ end
350
+ end
351
+
352
+ # Helper method to create the XML file section
353
+ def add_filesec(xml, object = nil, representation = nil)
354
+ case object
355
+ when NilClass
356
+ xml[:mets].fileSec {
357
+ @representations.values.each { |rep| add_filesec(xml, rep) }
358
+ }
359
+ when Libis::Tools::MetsFile::Representation
360
+ h = {
361
+ ID: object.xml_id,
362
+ USE: object.usage_type,
363
+ ADMID: amd_id(object.xml_id),
364
+ # DDMID: dmd_id(object.xml_id),
365
+ }.cleanup
366
+ xml[:mets].fileGrp(h) {
367
+ @files.values.each { |obj| add_filesec(xml, obj, object) }
368
+ }
369
+ when Libis::Tools::MetsFile::File
370
+ if object.representation == representation
371
+ h = {
372
+ ID: object.xml_id,
373
+ MIMETYPE: object.mimetype,
374
+ ADMID: amd_id(object.xml_id),
375
+ GROUPID: object.make_group_id,
376
+ }.cleanup
377
+ h[:DMDID] = dmd_id(object.xml_id) if object.dc_record
378
+
379
+ xml[:mets].file(h) {
380
+ # noinspection RubyStringKeysInHashInspection
381
+ xml[:mets].FLocat(
382
+ LOCTYPE: 'URL',
383
+ 'xmlns:xlin' => NS[:xlin],
384
+ 'xlin:href' => object.target_location,
385
+ )
386
+ }
387
+ end
388
+ else
389
+ raise RuntimeError, "Unsupported object type: #{object.class}"
390
+ end
391
+ end
392
+
393
+ # Helper method to create the Structmap
394
+ def add_struct_map(xml, object = nil)
395
+ case object
396
+ when NilClass
397
+ @maps.values.each do |map|
398
+ xml[:mets].structMap(
399
+ ID: "#{map.representation.xml_id}-1",
400
+ TYPE: (map.is_logical ? 'LOGICAL' : 'PHYSICAL'),
401
+ ) {
402
+ xml[:mets].div(LABEL: map.representation.label) {
403
+ add_struct_map(xml, map.div) if map.div
404
+ }
405
+ }
406
+ end
407
+ when Libis::Tools::MetsFile::Div
408
+ h = {
409
+ LABEL: object.label,
410
+ }.cleanup
411
+ xml[:mets].div(h) {
412
+ object.files.each { |file| add_struct_map(xml, file) }
413
+ object.divs.each { |div| add_struct_map(xml, div) }
414
+ }
415
+ when Libis::Tools::MetsFile::File
416
+ h = {
417
+ LABEL: object.label,
418
+ TYPE: 'FILE',
419
+ }.cleanup
420
+ xml[:mets].div(h) {
421
+ xml[:mets].fptr(FILEID: object.xml_id)
422
+ }
423
+ else
424
+ raise RuntimeError, "Unsupported object type: #{object.class}"
425
+ end
426
+
427
+ end
428
+
429
+ # Helper method to create a single XML DMD section
430
+ def add_dmd_section(xml, id, dc_record = nil)
431
+ return if dc_record.nil?
432
+ xml[:mets].dmdSec(ID: dmd_id(id)) {
433
+ xml[:mets].mdWrap(MDTYPE: 'DC') {
434
+ xml[:mets].xmlData {
435
+ xml[:dc] << dc_record
436
+ }
437
+ }
438
+ }
439
+ end
440
+
441
+ # Helper method to create a single AMD section
442
+ def add_amd_section(xml, id, dnx_sections = {})
443
+ xml[:mets].amdSec(ID: amd_id(id)) {
444
+ dnx_sections.each do |section_type, data|
445
+ if section_type.to_s =~ /^source-(.*)-\d+$/
446
+ xml[:mets].send('sourceMD', ID: "#{amd_id(id)}-#{section_type.to_s}") {
447
+ xml[:mets].mdWrap(MDTYPE: $1) {
448
+ xml[:mets].xmlData {
449
+ xml << data
450
+ }
451
+ }
452
+ }
453
+ else
454
+ xml[:mets].send("#{section_type}MD", ID: "#{amd_id(id)}-#{section_type.to_s}") {
455
+ xml[:mets].mdWrap(MDTYPE: 'OTHER', OTHERMDTYPE: 'dnx') {
456
+ xml[:mets].xmlData {
457
+ add_dnx_sections(xml, data)
458
+ }
459
+ }
460
+ }
461
+ end
462
+ end
463
+ }
464
+ end
465
+
466
+ # Helper method to create the XML DNX sections
467
+ def add_dnx_sections(xml, section_data)
468
+ section_data ||= []
469
+ xml.dnx(xmlns: NS[:dnx]) {
470
+ (section_data).each do |section|
471
+ xml.section(id: section.tag) {
472
+ records = section[:array] || [section]
473
+ records.each do |data|
474
+ xml.record {
475
+ data.each_pair do |key, value|
476
+ next if value.nil?
477
+ xml.key(value, id: key)
478
+ end
479
+ }
480
+ end
481
+ }
482
+ end
483
+ }
484
+ end
485
+
486
+ # Helper method to parse a XML div
487
+ def parse_div(div, rep)
488
+ if div[:TYPE] == 'FILE'
489
+ file_id = div.children.first[:FILEID]
490
+ {
491
+ id: file_id
492
+ }.merge rep[file_id]
493
+ else
494
+ div.children.inject({}) do |hash, child|
495
+ hash[child[:LABEL]] = parse_div child, rep
496
+ hash
497
+ end
498
+ end
499
+ end
500
+
501
+ end
502
+
503
+ end
504
+ end