libis-tools 0.9.1

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