libis-tools 0.9.1

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 (46) 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 +37 -0
  6. data/Gemfile +7 -0
  7. data/LICENSE.txt +22 -0
  8. data/README.md +289 -0
  9. data/Rakefile +6 -0
  10. data/lib/libis-tools.rb +1 -0
  11. data/lib/libis/tools.rb +16 -0
  12. data/lib/libis/tools/assert.rb +41 -0
  13. data/lib/libis/tools/checksum.rb +84 -0
  14. data/lib/libis/tools/command.rb +40 -0
  15. data/lib/libis/tools/config.rb +160 -0
  16. data/lib/libis/tools/dc_record.rb +47 -0
  17. data/lib/libis/tools/extend/empty.rb +7 -0
  18. data/lib/libis/tools/extend/hash.rb +107 -0
  19. data/lib/libis/tools/extend/ostruct.rb +3 -0
  20. data/lib/libis/tools/extend/string.rb +85 -0
  21. data/lib/libis/tools/extend/struct.rb +29 -0
  22. data/lib/libis/tools/logger.rb +71 -0
  23. data/lib/libis/tools/mets_file.rb +575 -0
  24. data/lib/libis/tools/parameter.rb +172 -0
  25. data/lib/libis/tools/sharepoint_mapping.rb +118 -0
  26. data/lib/libis/tools/sharepoint_record.rb +260 -0
  27. data/lib/libis/tools/version.rb +5 -0
  28. data/lib/libis/tools/xml_document.rb +574 -0
  29. data/libis-tools.gemspec +39 -0
  30. data/spec/assert_spec.rb +65 -0
  31. data/spec/checksum_spec.rb +132 -0
  32. data/spec/command_spec.rb +68 -0
  33. data/spec/config_spec.rb +86 -0
  34. data/spec/data/test.data +9 -0
  35. data/spec/data/test.xml +8 -0
  36. data/spec/data/test.yml +1 -0
  37. data/spec/logger_spec.rb +107 -0
  38. data/spec/parameter_container_spec.rb +83 -0
  39. data/spec/parameter_spec.rb +139 -0
  40. data/spec/spec_helper.rb +12 -0
  41. data/spec/test.xsd +20 -0
  42. data/spec/xmldocument_spec.rb +413 -0
  43. data/test/test_helper.rb +7 -0
  44. data/test/webservices/test_ca_item_info.rb +59 -0
  45. data/test/webservices/test_ca_search.rb +35 -0
  46. metadata +244 -0
@@ -0,0 +1,3 @@
1
+ # encoding: utf-8
2
+ require 'ostruct'
3
+ require 'backports/2.0.0/stdlib/ostruct'
@@ -0,0 +1,85 @@
1
+ class String
2
+
3
+ def blank?
4
+ self == ''
5
+ end unless method_defined? :blank?
6
+
7
+ def sort_form
8
+ result = []
9
+ matcher = /^(\D*)(\d*)(.*)$/
10
+ self.split('.').each { |s|
11
+ while !s.empty? and (x = matcher.match s)
12
+ a = x[1].to_s.strip
13
+ b = a.gsub(/[ _]/, '')
14
+ result << [b.downcase, b, a]
15
+ result << x[2].to_i
16
+ s = x[3]
17
+ end
18
+ }
19
+ result
20
+ end unless method_defined? :sort_form
21
+
22
+ def underscore
23
+ self.gsub(/::/, '/').
24
+ gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2').
25
+ gsub(/([a-z\d])([A-Z])/, '\1_\2').
26
+ tr('-', '_').
27
+ downcase
28
+ end unless method_defined? :underscore
29
+
30
+ def quote
31
+ '\"' + self.gsub(/"/) { |s| '\\' + s[0] } + '\"'
32
+ end unless method_defined? :quote
33
+
34
+ def escape_for_regexp
35
+ self.gsub(/[\.\+\*\(\)\{\}\|\/\\\^\$"']/) { |s| '\\' + s[0].to_s }
36
+ end
37
+
38
+ def escape_for_string
39
+ self.gsub(/"/) { |s| '\\' + s[0].to_s }
40
+ end
41
+
42
+ def escape_for_cmd
43
+ self.gsub(/"/) { |s| '\\\\\\' + s[0].to_s }
44
+ end
45
+
46
+ def escape_for_sql
47
+ self.gsub(/'/) { |s| ($` == '' || $' == '' ? '' : '\'') + s[0].to_s }
48
+ end
49
+
50
+ def dot_net_clean
51
+ self.gsub /^(\d+|error|float|string);\\?#/, ''
52
+ end
53
+
54
+ def remove_whitespace
55
+ self.gsub(/\s/, '_')
56
+ end
57
+
58
+ def encode_visual(regex = nil)
59
+ regex ||= /\W/
60
+ self.gsub(regex) { |c| '_x' + '%04x' % c.unpack('U')[0] + '_'}
61
+ end unless method_defined? :encode_visual
62
+
63
+ def decode_visual
64
+ self.gsub(/_x([0-9a-f]{4})_/i) { [$1.to_i(16)].pack('U') }
65
+ end unless method_defined? :decode_visual
66
+
67
+ def align_left
68
+ string = dup
69
+ relevant_lines = string.split(/\r\n|\r|\n/).select { |line| line.size > 0 }
70
+ indentation_levels = relevant_lines.map do |line|
71
+ match = line.match(/^( +)[^ ]+/)
72
+ match ? match[1].size : 0
73
+ end
74
+ indentation_level = indentation_levels.min
75
+ string.gsub! /^#{' ' * indentation_level}/, '' if indentation_level > 0
76
+ string
77
+ end unless method_defined? :align_left
78
+
79
+ end
80
+
81
+ class NilClass
82
+ def blank?
83
+ true
84
+ end
85
+ end
@@ -0,0 +1,29 @@
1
+ # encoding: utf-8
2
+ require 'json'
3
+ require 'backports/rails/hash'
4
+ require 'backports/2.0.0/struct'
5
+
6
+ class Struct
7
+ def to_hash
8
+ members.inject({}) {|h,m| h[m] = send(m); h}
9
+ end unless method_defined? :to_hash
10
+
11
+ def set(h = {})
12
+ h.symbolize_keys!
13
+ members.each {|m| send("#{m}=", h[m]) if h.key?(m)}
14
+ self
15
+ end unless method_defined? :set
16
+
17
+ def self.from_hash(h)
18
+ h.symbolize_keys!
19
+ members.inject(new) {|o,m| o[m] = h[m] if h.key?(m); o}
20
+ end unless respond_to? :from_hash
21
+
22
+ def to_json
23
+ to_hash.to_json
24
+ end unless method_defined? :to_json
25
+
26
+ def self.from_json(j)
27
+ from_hash(JSON.parse(j))
28
+ end unless respond_to? :from_json
29
+ end
@@ -0,0 +1,71 @@
1
+ # encoding: utf-8
2
+
3
+ require 'backports'
4
+ require 'libis/tools/config'
5
+ require 'libis/tools/extend/string'
6
+
7
+ module Libis
8
+ module Tools
9
+
10
+ # The Logger module adds logging functionality to any class.
11
+ #
12
+ # Just include the ::Libis::Tools::Logger module and the methods debug, info, warn, error and fatal will be
13
+ # available to the class instance. Each method takes a message argument and optional extra parameters.
14
+ #
15
+ # The methods all call the {#message} method with the logging level as first argument and the supplied arguments
16
+ # appended.
17
+ module Logger
18
+
19
+ def self.included(klass)
20
+ klass.class_eval do
21
+
22
+ def debug(msg, *args)
23
+ return if (self.options[:quiet] rescue false)
24
+ message ::Logger::DEBUG, msg, *args
25
+ end
26
+
27
+ def info(msg, *args)
28
+ return if (self.options[:quiet] rescue false)
29
+ message ::Logger::INFO, msg, *args
30
+ end
31
+
32
+ def warn(msg, *args)
33
+ return if (self.options[:quiet] rescue false)
34
+ message ::Logger::WARN, msg, *args
35
+ end
36
+
37
+ def error(msg, *args)
38
+ message ::Logger::ERROR, msg, *args
39
+ end
40
+
41
+ def fatal(msg, *args)
42
+ message ::Logger::FATAL, msg, *args
43
+ end
44
+
45
+ # The method that performs the code logging action.
46
+ #
47
+ # If extra arguments are supplied, the message string is expected to be a format specification string and the
48
+ # extra arguments will be applied to it.
49
+ #
50
+ # This default message method implementation uses the logger of ::Libis::Tools::Config. If an 'appname'
51
+ # parameter is defined in the Config object, it will be used as program name by the logger, otherwise the
52
+ # class name is taken.
53
+ #
54
+ # @param [{::Logger::Severity}] severity
55
+ # @param [String] msg message string
56
+ # @param [Object] args optional list of extra arguments
57
+ def message(severity, msg, *args)
58
+ message_text = (msg % args rescue ((msg + ' - %s') % args.to_s))
59
+ appname = Config.appname
60
+ appname = self.name if self.respond_to? :name
61
+ appname = self.class.name if appname.blank?
62
+ Config.logger.add(severity, message_text, appname)
63
+ end
64
+
65
+ end
66
+ end
67
+
68
+ end
69
+
70
+ end
71
+ end
@@ -0,0 +1,575 @@
1
+ # encoding: utf-8
2
+ require 'ostruct'
3
+
4
+ require 'libis/tools/extend/hash'
5
+ require_relative 'xml_document'
6
+
7
+ module Libis
8
+ module Tools
9
+
10
+ # noinspection RubyResolve
11
+ # noinspection RubyClassVariableUsageInspection
12
+ class MetsFile
13
+
14
+ module IdContainer
15
+
16
+ def set_from_hash(h)
17
+ h.each { |k, v| send "#{k}=", v }
18
+ end
19
+
20
+ def id
21
+ return @id if @id
22
+ @id = self.class.instance_variable_get('@id') || 1
23
+ self.class.instance_variable_set('@id', @id + 1)
24
+ @id
25
+ end
26
+
27
+ def to_s
28
+ "#{self.class}:\n" +
29
+ self.instance_variables.map do |var|
30
+ v = self.instance_variable_get(var)
31
+ v = "#{v.class}-#{v.id}" if v.is_a? IdContainer
32
+ v = v.map do |x|
33
+ x.is_a?(IdContainer) ? "#{x.class}-#{x.id}" : x.to_s
34
+ end.join(',') if v.is_a? Array
35
+ " - #{var.to_s.gsub(/^@/, '')}: #{v}"
36
+ end.join("\n")
37
+ end
38
+
39
+ end
40
+
41
+ class Representation
42
+ include IdContainer
43
+
44
+ attr_accessor :label, :preservation_type, :usage_type, :dc_record
45
+
46
+ def xml_id
47
+ "rep#{id}"
48
+ end
49
+
50
+ def amd
51
+ dnx = {}
52
+ tech_data = []
53
+ data = {
54
+ preservationType: preservation_type,
55
+ usageType: usage_type,
56
+ # RevisionNumber: 1,
57
+ # DigitalOriginal: true,
58
+ }.cleanup
59
+ tech_data << TechGeneralRep.new(data) unless data.empty?
60
+ dnx[:tech] = tech_data unless tech_data.empty?
61
+ dnx
62
+ end
63
+
64
+ end
65
+
66
+ class File
67
+ include IdContainer
68
+
69
+ attr_accessor :label, :location, :target_location, :mimetype, :entity_type, :representation, :dc_record
70
+
71
+ def xml_id
72
+ "fid#{id}"
73
+ end
74
+
75
+ def group_id
76
+ "grp#{master.id rescue id}"
77
+ end
78
+
79
+ def master
80
+ @master ||= nil
81
+ end
82
+
83
+ def master=(file)
84
+ @master = file
85
+ end
86
+
87
+ def manifestations
88
+ @manifestations ||= Array.new
89
+ end
90
+
91
+ def add_manifestation(file)
92
+ manifestations << file
93
+ file.master = self
94
+ end
95
+
96
+ def orig_name
97
+ ::File.basename(location)
98
+ end
99
+
100
+ def orig_path
101
+ ::File.dirname(location)
102
+ end
103
+
104
+ def target
105
+ if target_location.nil?
106
+ return "#{xml_id}#{::File.extname(location)}"
107
+ end
108
+ target_location
109
+ end
110
+
111
+ def amd
112
+ dnx = {}
113
+ tech_data = []
114
+ data = {
115
+ label: label,
116
+ fileMIMEType: mimetype,
117
+ fileOriginalName: orig_name,
118
+ fileOriginalPath: orig_path,
119
+ FileEntityType: entity_type,
120
+ # fileSizeBytes: size,
121
+ }.cleanup
122
+ tech_data << TechGeneralFile.new(data) unless data.empty?
123
+ # data = {
124
+ # fixityType: fixity_type,
125
+ # fixityValue: fixity_value,
126
+ # }.cleanup
127
+ # tech_data << TechFixity.new(data) unless data.empty?
128
+ dnx[:tech] = tech_data unless tech_data.empty?
129
+ dnx
130
+ end
131
+
132
+ end
133
+
134
+ class Div
135
+ include IdContainer
136
+
137
+ attr_accessor :label
138
+
139
+ def xml_id
140
+ "div-#{id}"
141
+ end
142
+
143
+ def children
144
+ files + divs
145
+ end
146
+
147
+ def files
148
+ @files ||= Array.new
149
+ end
150
+
151
+ def divs
152
+ @divs ||= Array.new
153
+ end
154
+
155
+ def <<(obj)
156
+ case obj
157
+ when File
158
+ files << obj
159
+ when Div
160
+ divs << obj
161
+ else
162
+ raise RuntimeError, "Child object type not supported: #{obj.class}"
163
+ end
164
+ end
165
+ end
166
+
167
+ class Map
168
+ include IdContainer
169
+
170
+ attr_accessor :representation, :div
171
+
172
+ def xml_id
173
+ "smap-#{id}"
174
+ end
175
+
176
+ end
177
+
178
+ class DnxSection < OpenStruct
179
+ def self.tag(value = nil)
180
+ var_name = '@tag'
181
+ if value.nil?
182
+ instance_variable_get(var_name)
183
+ else
184
+ instance_variable_set(var_name, value)
185
+ end
186
+ end
187
+
188
+ def tag
189
+ self.class.tag
190
+ end
191
+ end
192
+
193
+ class TechGeneralIE < DnxSection
194
+ tag 'generalIECharacteristics'
195
+ end
196
+
197
+ class TechGeneralRep < DnxSection
198
+ tag 'generalRepCharacteristics'
199
+ end
200
+
201
+ class TechGeneralFile < DnxSection
202
+ tag 'generalFileCharacteristics'
203
+ end
204
+
205
+ class RetentionPeriod < DnxSection
206
+ tag 'retentionPolicy'
207
+ end
208
+
209
+ class TechFixity < DnxSection
210
+ tag 'fileFixity'
211
+ end
212
+
213
+ class Rights < DnxSection
214
+ tag 'accessRightsPolicy'
215
+ end
216
+
217
+ attr_reader :representations, :files, :divs, :maps
218
+
219
+ # noinspection RubyConstantNamingConvention
220
+ NS = {
221
+ mets: 'http://www.loc.gov/METS/',
222
+ dc: 'http://purl.org/dc/elements/1.1/',
223
+ dnx: 'http://www.exlibrisgroup.com/dps/dnx',
224
+ xlin: 'http://www.w3.org/1999/xlink',
225
+ }
226
+
227
+ def initialize
228
+ @representations = {}
229
+ @files = {}
230
+ @divs = {}
231
+ @maps = {}
232
+ end
233
+
234
+ def self.parse(xml)
235
+ xml_doc = case xml
236
+ when String
237
+ Libis::Tools::XmlDocument.parse(xml).document
238
+ when Hash
239
+ Libis::Tools::XmlDocument.from_hash(xml).document
240
+ when Libis::Tools::XmlDocument
241
+ xml.document
242
+ when Nokogiri::XML::Document
243
+ xml
244
+ else
245
+ raise ArgumentError, "Libis::Tools::MetsFile#parse does not accept input of type #{xml.class}"
246
+ end
247
+
248
+ dmd_sec = xml_doc.root.xpath('mets:dmdSec', NS).inject({}) do |hash_dmd, dmd|
249
+ hash_dmd[dmd[:ID]] = dmd.xpath('.//dc:record', NS).first.children.inject({}) do |h, c|
250
+ h[c.name] = c.content
251
+ h
252
+ end
253
+ hash_dmd
254
+ end
255
+ amd_sec = xml_doc.root.xpath('mets:amdSec', NS).inject({}) do |hash_amd, amd|
256
+ hash_amd[amd[:ID]] = [:tech, :rights, :source, :digiprov].inject({}) do |hash_sec, sec|
257
+ md = amd.xpath("mets:#{sec}MD", NS).first
258
+ return hash_sec unless md
259
+ # hash_sec[sec] = md.xpath('mets:mdWrap/dnx:dnx/dnx:section', NS).inject({}) do |hash_md, dnx_sec|
260
+ hash_sec[sec] = md.xpath('.//dnx:section', NS).inject({}) do |hash_md, dnx_sec|
261
+ hash_md[dnx_sec[:id]] = dnx_sec.xpath('dnx:record', NS).inject([]) do |records, dnx_record|
262
+ records << dnx_record.xpath('dnx:key', NS).inject({}) do |record_hash, key|
263
+ record_hash[key[:id]] = key.content
264
+ record_hash
265
+ end
266
+ records
267
+ end
268
+ hash_md
269
+ end
270
+ hash_sec
271
+ end
272
+ hash_amd
273
+ end
274
+ rep_sec = xml_doc.root.xpath('.//mets:fileGrp', NS).inject({}) do |hash_rep, rep|
275
+ hash_rep[rep[:ID]] = {
276
+ amd: amd_sec[rep[:ADMID]],
277
+ dmd: amd_sec[rep[:DMDID]]
278
+ }.cleanup.merge(
279
+ rep.xpath('mets:file', NS).inject({}) do |hash_file, file|
280
+ hash_file[file[:ID]] = {
281
+ group: file[:GROUPID],
282
+ amd: amd_sec[file[:ADMID]],
283
+ dmd: dmd_sec[file[:DMDID]],
284
+ }.cleanup
285
+ hash_file
286
+ end
287
+ )
288
+ hash_rep
289
+ end
290
+ { amd: amd_sec['ie-amd'],
291
+ dmd: dmd_sec['ie-dmd'],
292
+ }.cleanup.merge(
293
+ xml_doc.root.xpath('.//mets:structMap[@TYPE="PHYSICAL"]', NS).inject({}) do |hash_map, map|
294
+ rep_id = map[:ID].gsub(/-\d+$/, '')
295
+ rep = rep_sec[rep_id]
296
+ div_parser = lambda do |div|
297
+ if div[:TYPE] == 'FILE'
298
+ file_id = div.xpath('mets:fptr').first[:FILEID]
299
+ {
300
+ id: file_id
301
+ }.merge rep[file_id]
302
+ else
303
+ div.children.inject({}) do |hash, child|
304
+ # noinspection RubyScope
305
+ hash[child[:LABEL]] = div_parser.call(child)
306
+ hash
307
+ end
308
+ end
309
+ end
310
+ hash_map[map.xpath('mets:div').first[:LABEL]] = {
311
+ id: rep_id,
312
+ amd: rep_sec[rep_id][:amd],
313
+ dmd: rep_sec[rep_id][:dmd],
314
+ }.cleanup.merge(
315
+ map.xpath('mets:div', NS).inject({}) do |hash, div|
316
+ hash[div[:LABEL]] = div_parser.call(div)
317
+ end
318
+ )
319
+ hash_map
320
+ end
321
+ )
322
+ end
323
+
324
+ def dc_record=(xml)
325
+ @dc_record = xml
326
+ end
327
+
328
+ def amd_info=(hash)
329
+ @dnx = {}
330
+ tech_data = []
331
+ data = {
332
+ IEEntityType: hash[:entity_type],
333
+ UserDefinedA: hash[:user_a],
334
+ UserDefinedB: hash[:user_b],
335
+ UserDefinedC: hash[:user_c],
336
+ status: hash[:status],
337
+ }.cleanup
338
+ tech_data << TechGeneralIE.new(data) unless data.empty?
339
+ data = {
340
+ policyId: hash[:retention_id],
341
+ }.cleanup
342
+ tech_data << RetentionPeriod.new(data) unless data.empty?
343
+ @dnx[:tech] = tech_data unless tech_data.empty?
344
+ data = {
345
+ policyId: hash[:access_right]
346
+ }.cleanup
347
+ rights_data = []
348
+ rights_data << Rights.new(data) unless data.empty?
349
+ @dnx[:rights] = rights_data unless rights_data.empty?
350
+ end
351
+
352
+ # @param [Hash] hash
353
+ # @return [Libis::Tools::MetsFile::Representation]
354
+ def representation(hash = {})
355
+ rep = Representation.new
356
+ rep.set_from_hash hash
357
+ @representations[rep.id] = rep
358
+ end
359
+
360
+ # @param [Hash] hash
361
+ # @return [Libis::Tools::MetsFile::Div]
362
+ def div(hash = {})
363
+ div = Libis::Tools::MetsFile::Div.new
364
+ div.set_from_hash hash
365
+ @divs[div.id] = div
366
+ end
367
+
368
+ # @param [Hash] hash
369
+ # @return [Libis::Tools::MetsFile::File]
370
+ def file(hash = {})
371
+ file = Libis::Tools::MetsFile::File.new
372
+ file.set_from_hash hash
373
+ @files[file.id] = file
374
+ end
375
+
376
+ # @param [Libis::Tools::MetsFile::Representation] rep
377
+ # @param [Libis::Tools::MetsFile::Div] div
378
+ # @return [Libis::Tools::MetsFile::Map]
379
+ def map(rep, div)
380
+ map = Libis::Tools::MetsFile::Map.new
381
+ map.representation = rep
382
+ map.div = div
383
+ @maps[map.id] = map
384
+ end
385
+
386
+ # @return [Libis::Tools::XmlDocument]
387
+ def xml_doc
388
+ ::Libis::Tools::XmlDocument.build do |xml|
389
+ xml[:mets].mets(
390
+ 'xmlns:mets' => NS[:mets],
391
+ ) {
392
+ add_dmd(xml)
393
+ add_amd(xml)
394
+ add_filesec(xml)
395
+ add_struct_map(xml)
396
+ }
397
+ end
398
+ end
399
+
400
+ protected
401
+
402
+ def dmd_id(id)
403
+ "#{id}-dmd"
404
+ end
405
+
406
+ def amd_id(id)
407
+ "#{id}-amd"
408
+ end
409
+
410
+ def add_dmd(xml, object = nil)
411
+ case object
412
+ when NilClass
413
+ add_dmd_section(xml, 'ie', @dc_record)
414
+ # @representations.values.each { |rep| add_dmd(xml, rep) }
415
+ @files.values.each { |file| add_dmd(xml, file) }
416
+ when Libis::Tools::MetsFile::File
417
+ add_dmd_section(xml, object.xml_id, object.dc_record)
418
+ # when Representation
419
+ # add_dmd_section(xml, object.xml_id, object.dc_record)
420
+ else
421
+ raise RuntimeError, "Unsupported object type: #{object.class}"
422
+ end
423
+ end
424
+
425
+ def add_amd(xml, object = nil)
426
+ case object
427
+ when NilClass
428
+ raise RuntimeError, 'No IE amd info present.' unless @dnx
429
+ add_amd_section(xml, 'ie', @dnx)
430
+ @representations.values.each { |rep| add_amd(xml, rep) }
431
+ @files.values.each { |file| add_amd(xml, file) }
432
+ when Libis::Tools::MetsFile::File
433
+ add_amd_section(xml, object.xml_id, object.amd)
434
+ object.manifestations.each { |manif| add_amd_section(xml, manif.xml_id, manif.amd) }
435
+ when Libis::Tools::MetsFile::Representation
436
+ add_amd_section(xml, object.xml_id, object.amd)
437
+ else
438
+ raise RuntimeError, "Unsupported object type: #{object.class}"
439
+ end
440
+ end
441
+
442
+ def add_filesec(xml, object = nil, representation = nil)
443
+ case object
444
+ when NilClass
445
+ xml[:mets].fileSec {
446
+ @representations.values.each { |rep| add_filesec(xml, rep) }
447
+ }
448
+ when Libis::Tools::MetsFile::Representation
449
+ h = {
450
+ ID: object.xml_id,
451
+ USE: object.usage_type,
452
+ ADMID: amd_id(object.xml_id),
453
+ # DDMID: dmd_id(object.xml_id),
454
+ }.cleanup
455
+ xml[:mets].fileGrp(h) {
456
+ @files.values.each { |obj| add_filesec(xml, obj, object) }
457
+ }
458
+ when Libis::Tools::MetsFile::File
459
+ if object.representation == representation
460
+ h = {
461
+ ID: object.xml_id,
462
+ MIMETYPE: object.mimetype,
463
+ ADMID: amd_id(object.xml_id),
464
+ GROUPID: object.group_id,
465
+ }.cleanup
466
+ h[:DMDID] = dmd_id(object.xml_id) if object.dc_record
467
+
468
+ xml[:mets].file(h) {
469
+ # noinspection RubyStringKeysInHashInspection
470
+ xml[:mets].FLocat(
471
+ LOCTYPE: 'URL',
472
+ 'xmlns:xlin' => NS[:xlin],
473
+ 'xlin:href' => object.target_location,
474
+ )
475
+ }
476
+ end
477
+ else
478
+ raise RuntimeError, "Unsupported object type: #{object.class}"
479
+ end
480
+ end
481
+
482
+ def add_struct_map(xml, object = nil)
483
+ case object
484
+ when NilClass
485
+ @maps.values.each do |map|
486
+ xml[:mets].structMap(
487
+ ID: "#{map.representation.xml_id}-1",
488
+ TYPE: 'PHYSICAL',
489
+ ) {
490
+ xml[:mets].div(LABEL: map.representation.label) {
491
+ add_struct_map(xml, map.div) if map.div
492
+ }
493
+ }
494
+ end
495
+ when Libis::Tools::MetsFile::Div
496
+ h = {
497
+ LABEL: object.label,
498
+ }.cleanup
499
+ xml[:mets].div(h) {
500
+ object.files.each { |file| add_struct_map(xml, file) }
501
+ object.divs.each { |div| add_struct_map(xml, div) }
502
+ }
503
+ when Libis::Tools::MetsFile::File
504
+ h = {
505
+ LABEL: object.label,
506
+ TYPE: 'FILE',
507
+ }.cleanup
508
+ xml[:mets].div(h) {
509
+ xml[:mets].fptr(FILEID: object.xml_id)
510
+ }
511
+ else
512
+ raise RuntimeError, "Unsupported object type: #{object.class}"
513
+ end
514
+
515
+ end
516
+
517
+ def add_dmd_section(xml, id, dc_record = nil)
518
+ return if dc_record.nil?
519
+ xml[:mets].dmdSec(ID: dmd_id(id)) {
520
+ xml[:mets].mdWrap(MDTYPE: 'DC') {
521
+ xml[:mets].xmlData {
522
+ xml << dc_record
523
+ }
524
+ }
525
+ }
526
+ end
527
+
528
+ def add_amd_section(xml, id, dnx_sections = {})
529
+ xml[:mets].amdSec(ID: amd_id(id)) {
530
+ [:tech, :rights, :source, :digiprov].each do |section_type|
531
+ xml.send("#{section_type}MD", ID: "#{amd_id(id)}-#{section_type.to_s}") {
532
+ xml[:mets].mdWrap(MDTYPE: 'OTHER', OTHERMDTYPE: 'dnx') {
533
+ xml[:mets].xmlData {
534
+ add_dnx_sections(xml, dnx_sections[section_type])
535
+ }
536
+ }
537
+ }
538
+ end
539
+ }
540
+ end
541
+
542
+ def add_dnx_sections(xml, section_data)
543
+ section_data ||= []
544
+ xml[:mets].dnx(xmlns: NS[:dnx]) {
545
+ (section_data).each do |section|
546
+ xml.section(id: section.tag) {
547
+ xml.record {
548
+ section.each_pair do |key, value|
549
+ next if value.nil?
550
+ xml.key(value, id: key)
551
+ end
552
+ }
553
+ }
554
+ end
555
+ }
556
+ end
557
+
558
+ def parse_div(div, rep)
559
+ if div[:TYPE] == 'FILE'
560
+ file_id = div.children.first[:FILEID]
561
+ {
562
+ id: file_id
563
+ }.merge rep[file_id]
564
+ else
565
+ div.children.inject({}) do |hash, child|
566
+ hash[child[:LABEL]] = parse_div child, rep
567
+ hash
568
+ end
569
+ end
570
+ end
571
+
572
+ end
573
+
574
+ end
575
+ end