libis-tools 1.0.5-java

Sign up to get free protection for your applications and to get access to all the features.
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