libis-tools 0.9.20 → 0.9.21

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 (47) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +36 -233
  3. data/Rakefile +5 -0
  4. data/lib/libis/tools.rb +1 -0
  5. data/lib/libis/tools/assert.rb +11 -0
  6. data/lib/libis/tools/checksum.rb +22 -5
  7. data/lib/libis/tools/command.rb +24 -3
  8. data/lib/libis/tools/config.rb +61 -33
  9. data/lib/libis/tools/config_file.rb +0 -1
  10. data/lib/libis/tools/deep_struct.rb +10 -2
  11. data/lib/libis/tools/extend/empty.rb +2 -2
  12. data/lib/libis/tools/extend/hash.rb +37 -18
  13. data/lib/libis/tools/extend/kernel.rb +9 -0
  14. data/lib/libis/tools/extend/string.rb +17 -8
  15. data/lib/libis/tools/logger.rb +95 -44
  16. data/lib/libis/tools/metadata.rb +5 -1
  17. data/lib/libis/tools/metadata/dublin_core_record.rb +22 -4
  18. data/lib/libis/tools/metadata/field_format.rb +49 -9
  19. data/lib/libis/tools/metadata/fix_field.rb +5 -0
  20. data/lib/libis/tools/metadata/mapper.rb +2 -1
  21. data/lib/libis/tools/metadata/mappers/flandrica.rb +8 -1
  22. data/lib/libis/tools/metadata/mappers/kuleuven.rb +6 -2
  23. data/lib/libis/tools/metadata/marc21_record.rb +1 -0
  24. data/lib/libis/tools/metadata/marc_record.rb +31 -12
  25. data/lib/libis/tools/metadata/parser/basic_parser.rb +2 -0
  26. data/lib/libis/tools/metadata/parser/dublin_core_parser.rb +2 -1
  27. data/lib/libis/tools/metadata/parser/marc21_parser.rb +2 -1
  28. data/lib/libis/tools/metadata/parser/marc_format_parser.rb +2 -1
  29. data/lib/libis/tools/metadata/parser/marc_rules.rb +2 -1
  30. data/lib/libis/tools/metadata/parser/marc_select_parser.rb +2 -1
  31. data/lib/libis/tools/metadata/parser/patch.rb +1 -0
  32. data/lib/libis/tools/metadata/parser/subfield_criteria_parser.rb +2 -1
  33. data/lib/libis/tools/metadata/sharepoint_mapping.rb +1 -0
  34. data/lib/libis/tools/metadata/sharepoint_record.rb +2 -0
  35. data/lib/libis/tools/metadata/var_field.rb +8 -0
  36. data/lib/libis/tools/mets_dnx.rb +61 -0
  37. data/lib/libis/tools/mets_file.rb +87 -604
  38. data/lib/libis/tools/mets_objects.rb +534 -0
  39. data/lib/libis/tools/parameter.rb +144 -21
  40. data/lib/libis/tools/thread_safe.rb +31 -0
  41. data/lib/libis/tools/version.rb +1 -1
  42. data/lib/libis/tools/xml_document.rb +18 -24
  43. data/libis-tools.gemspec +6 -2
  44. data/spec/config_spec.rb +3 -4
  45. data/spec/logger_spec.rb +13 -30
  46. data/spec/mets_file_spec.rb +17 -17
  47. metadata +53 -7
@@ -8,6 +8,7 @@ module Libis
8
8
  module Tools
9
9
  module Metadata
10
10
 
11
+ # Helper class implementing a variable field for MARC
11
12
  class VarField
12
13
 
13
14
  attr_reader :tag
@@ -15,6 +16,10 @@ module Libis
15
16
  attr_reader :ind2
16
17
  attr_reader :subfield_data
17
18
 
19
+ # Create new variable field with given tag and indicators
20
+ # @param [String] tag tag
21
+ # @param [String] ind1 first indicator. nil will be translated into empty string.
22
+ # @param [String] ind2 second indicator. nil will be translated into empty string.
18
23
  def initialize(tag, ind1, ind2)
19
24
  @tag = tag
20
25
  @ind1 = ind1 || ''
@@ -22,6 +27,9 @@ module Libis
22
27
  @subfield_data = Hash.new { |h, k| h[k] = Array.new }
23
28
  end
24
29
 
30
+ # Add subfield to variable field
31
+ # @param [String] name subfield indicator without '$'
32
+ # @param [String] value content of the subfield
25
33
  def add_subfield(name, value)
26
34
  @subfield_data[name] << value
27
35
  end
@@ -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
+
@@ -3,613 +3,43 @@ require 'ostruct'
3
3
 
4
4
  require 'libis/tools/extend/hash'
5
5
  require_relative 'xml_document'
6
+ require_relative 'thread_safe'
7
+ require_relative 'mets_dnx'
8
+ require_relative 'mets_objects'
6
9
 
7
10
  module Libis
8
11
  module Tools
9
12
 
10
13
  # 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 if respond_to?(k) }
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, :representation_code, :entity_type, :access_right_id,
45
- :user_a, :user_b, :user_c,
46
- :group_id, :priority, :order,
47
- :digital_original, :content, :context, :hardware, :carrier, :original_name,
48
- :preservation_levels, :env_dependencies, :hardware_ids, :software_ids,
49
- :hardware_infos, :software_infos, :relationship_infos, :environments,
50
- :dc_record, :source_metadata
51
-
52
- def xml_id
53
- "rep#{id}"
54
- end
55
-
56
- def amd
57
- dnx = {}
58
- tech_data = []
59
- # General characteristics
60
- data = {
61
- preservationType: preservation_type,
62
- usageType: usage_type,
63
- DigitalOriginal: digital_original,
64
- label: label,
65
- representationEntityType: entity_type,
66
- contentType: content,
67
- contextType: context,
68
- hardwareUsed: hardware,
69
- physicalCarrierMedia: carrier,
70
- deliveryPriority: priority,
71
- orderingSequence: order,
72
- RepresentationCode: representation_code,
73
- RepresentationOriginalName: original_name,
74
- UserDefinedA: user_a,
75
- UserDefinedB: user_b,
76
- UserDefinedC: user_c,
77
- }.cleanup
78
- tech_data << TechGeneralRep.new(data) unless data.empty?
79
- # Object characteristics
80
- data = {
81
- groupID: group_id
82
- }.cleanup
83
- tech_data << TechObjectChars.new(data) unless data.empty?
84
- # Preservation level
85
- if preservation_levels
86
- data_list = []
87
- preservation_levels.each do |preservation_level|
88
- data = {
89
- preservationLevelValue: preservation_level[:value],
90
- preservationLevelRole: preservation_level[:role],
91
- preservationLevelRationale: preservation_level[:rationale],
92
- preservationLevelDateAssigned: preservation_level[:date],
93
- }.cleanup
94
- data_list << OpenStruct.new(data) unless data.empty?
95
- end
96
- tech_data << PreservationLevel.new(array: data_list) unless data_list.empty?
97
- end
98
- # Dependencies
99
- if env_dependencies
100
- data_list = []
101
- env_dependencies.each do |dependency|
102
- data = {
103
- dependencyName: dependency[:name],
104
- dependencyIdentifierType1: dependency[:type1],
105
- dependencyIdentifierValue1: dependency[:value1],
106
- dependencyIdentifierType2: dependency[:type2],
107
- dependencyIdentifierValue2: dependency[:value2],
108
- dependencyIdentifierType3: dependency[:type3],
109
- dependencyIdentifierValue3: dependency[:value3],
110
- }.cleanup
111
- data_list << OpenStruct.new(data) unless data.empty?
112
- end
113
- tech_data << EnvDeps.new(array: data_list) unless data_list.empty?
114
- end
115
- # Hardware registry id
116
- if hardware_ids
117
- data_list = []
118
- hardware_ids.each do |id|
119
- data = {
120
- registryId: id
121
- }.cleanup
122
- data_list << OpenStruct.new(data) unless data.empty?
123
- end
124
- tech_data << HardwareId.new(array: data_list) unless data_list.empty?
125
- end
126
- # Software registry id
127
- if software_ids
128
- data_list = []
129
- software_ids.each do |id|
130
- data = {
131
- registryId: id
132
- }.cleanup
133
- data_list << OpenStruct.new(data) unless data.empty?
134
- end
135
- tech_data << SoftwareId.new(array: data_list) unless data_list.empty?
136
- end
137
- # Hardware
138
- if hardware_infos
139
- data_list = []
140
- hardware_infos.each do |hardware|
141
- data = {
142
- hardwareName: hardware[:name],
143
- hardwareType: hardware[:type],
144
- hardwareOtherInformation: hardware[:info],
145
- }.cleanup
146
- data_list << OpenStruct.new(data) unless data.empty?
147
- end
148
- tech_data << HardwareInfo.new(array: data_list) unless data_list.empty?
149
- end
150
- # Software
151
- if software_infos
152
- data_list = []
153
- software_infos.each do |software|
154
- data = {
155
- softwareName: software[:name],
156
- softwareVersion: software[:version],
157
- softwareType: software[:type],
158
- softwareOtherInformation: software[:info],
159
- softwareDependancy: software[:dependency],
160
- }.cleanup
161
- data_list << OpenStruct.new(data) unless data.empty?
162
- end
163
- tech_data << SoftwareInfo.new(array: data_list) unless data_list.empty?
164
- end
165
- # Relationship
166
- if relationship_infos
167
- data_list = []
168
- relationship_infos.each do |relationship|
169
- data = {
170
- relationshipType: relationship[:type],
171
- relationshipSubType: relationship[:subtype],
172
- relatedObjectIdentifierType1: relationship[:type1],
173
- relatedObjectIdentifierValue1: relationship[:id1],
174
- relatedObjectSequence1: relationship[:seq1],
175
- relatedObjectIdentifierType2: relationship[:type2],
176
- relatedObjectIdentifierValue2: relationship[:id2],
177
- relatedObjectSequence2: relationship[:seq2],
178
- relatedObjectIdentifierType3: relationship[:type3],
179
- relatedObjectIdentifierValue3: relationship[:id3],
180
- relatedObjectSequence3: relationship[:seq3],
181
- }.cleanup
182
- data_list << OpenStruct.new(data) unless data.empty?
183
- end
184
- tech_data << RelationShip.new(array: data_list) unless data_list.empty?
185
- end
186
- # Environment
187
- if environments
188
- data_list = []
189
- environments.each do |environment|
190
- data = {
191
- environmentCharacteristic: environment[:characteristic],
192
- environmentPurpose: environment[:purpose],
193
- environmentNote: environment[:note],
194
- }.cleanup
195
- data_list << OpenStruct.new(data) unless data.empty?
196
- end
197
- tech_data << Environment.new(array: data_list) unless data_list.empty?
198
- end
199
- # Finally assemble technical section
200
- dnx[:tech] = tech_data unless tech_data.empty?
201
- # Rights section
202
- rights_data = []
203
- data = {
204
- policyId: access_right_id
205
- }.cleanup
206
- rights_data << Rights.new(data) unless data.empty?
207
- dnx[:rights] = rights_data unless rights_data.empty?
208
- # Source metadata
209
- if source_metadata
210
- source_metadata.each_with_index do |metadata, i|
211
- dnx["source-#{metadata[:type].to_s.upcase}-#{i}"] = metadata[:data]
212
- end
213
- end
214
- dnx
215
- end
216
-
217
- end
218
14
 
219
- class File
220
- include IdContainer
221
-
222
- attr_accessor :label, :note, :location, :target_location, :mimetype, :puid, :size, :entity_type,
223
- :creation_date, :modification_date, :composition_level, :group_id,
224
- :checksum_MD5, :checksum_SHA1, :checksum_SHA256,:checksum_SHA384,:checksum_SHA512,
225
- :fixity_type, :fixity_value,
226
- :preservation_levels, :inhibitors, :env_dependencies, :hardware_ids, :software_ids,
227
- :signatures, :hardware_infos, :software_infos, :relationship_infos, :environments, :applications,
228
- :dc_record, :source_metadata, :representation
229
-
230
- def xml_id
231
- "fid#{id}"
232
- end
233
-
234
- def make_group_id
235
- "grp#{group_id || master.id rescue id}"
236
- end
237
-
238
- def master
239
- @master ||= nil
240
- end
241
-
242
- def master=(file)
243
- @master = file
244
- end
245
-
246
- def manifestations
247
- @manifestations ||= Array.new
248
- end
249
-
250
- def add_manifestation(file)
251
- manifestations << file
252
- file.master = self
253
- end
254
-
255
- def orig_name
256
- ::File.basename(location)
257
- end
258
-
259
- def orig_path
260
- ::File.dirname(location)
261
- end
262
-
263
- def target
264
- if target_location.nil?
265
- return "#{xml_id}#{::File.extname(location)}"
266
- end
267
- target_location
268
- end
269
-
270
- def amd
271
- dnx = {}
272
- tech_data = []
273
- # General File charateristics
274
- data = {
275
- label: label,
276
- note: note,
277
- fileCreationDate: creation_date,
278
- fileModificationDate: modification_date,
279
- FileEntityType: entity_type,
280
- compositionLevel: composition_level,
281
- # fileLocationType: 'FILE',
282
- # fileLocation: '',
283
- fileOriginalName: orig_name,
284
- fileOriginalPath: orig_path,
285
- fileOriginalID: location,
286
- fileExtension: ::File.extname(orig_name),
287
- fileMIMEType: mimetype,
288
- fileSizeBytes: size,
289
- formatLibraryId: puid
290
- }.cleanup
291
- tech_data << TechGeneralFile.new(data) unless data.empty?
292
- # Fixity
293
- %w'MD5 SHA1 SHA256 SHA384 SHA512'.each do |checksum_type|
294
- if (checksum = self.send("checksum_#{checksum_type}"))
295
- data = {
296
- fixityType: checksum_type,
297
- fixityValue: checksum,
298
- }.cleanup
299
- tech_data << TechFixity.new(data) unless data.empty?
300
- end
301
- end
302
- # Object characteristics
303
- data = {
304
- groupID: make_group_id
305
- }.cleanup
306
- tech_data << TechObjectChars.new(data) unless data.empty?
307
- # Preservation level
308
- if preservation_levels
309
- data_list = []
310
- preservation_levels.each do |preservation_level|
311
- data = {
312
- preservationLevelValue: preservation_level[:value],
313
- preservationLevelRole: preservation_level[:role],
314
- preservationLevelRationale: preservation_level[:rationale],
315
- preservationLevelDateAssigned: preservation_level[:date],
316
- }.cleanup
317
- data_list << OpenStruct.new(data) unless data.empty?
318
- end
319
- tech_data << PreservationLevel.new(array: data_list) unless data_list.empty?
320
- end
321
- # Inhibitor
322
- if inhibitors
323
- data_list = []
324
- inhibitors.each do |inhibitor|
325
- data = {
326
- inhibitorType: inhibitor[:type],
327
- inhibitorTarget: inhibitor[:target],
328
- inhibitorKey: inhibitor[:key],
329
- }.cleanup
330
- data_list << OpenStruct.new(data) unless data.empty?
331
- end
332
- tech_data << Inhibitor.new(array: data_list) unless data_list.empty?
333
- end
334
- # Dependencies
335
- if env_dependencies
336
- data_list = []
337
- env_dependencies.each do |dependency|
338
- data = {
339
- dependencyName: dependency[:name],
340
- dependencyIdentifierType1: dependency[:type1],
341
- dependencyIdentifierValue1: dependency[:value1],
342
- dependencyIdentifierType2: dependency[:type2],
343
- dependencyIdentifierValue2: dependency[:value2],
344
- dependencyIdentifierType3: dependency[:type3],
345
- dependencyIdentifierValue3: dependency[:value3],
346
- }.cleanup
347
- data_list << OpenStruct.new(data) unless data.empty?
348
- end
349
- tech_data << EnvDeps.new(array: data_list) unless data_list.empty?
350
- end
351
- # Hardware registry id
352
- if hardware_ids
353
- data_list = []
354
- hardware_ids.each do |id|
355
- data = {
356
- registryId: id
357
- }.cleanup
358
- data_list << OpenStruct.new(data) unless data.empty?
359
- end
360
- tech_data << HardwareId.new(array: data_list) unless data_list.empty?
361
- end
362
- # Software registry id
363
- if software_ids
364
- data_list = []
365
- software_ids.each do |id|
366
- data = {
367
- registryId: id
368
- }.cleanup
369
- data_list << OpenStruct.new(data) unless data.empty?
370
- end
371
- tech_data << SoftwareId.new(array: data_list) unless data_list.empty?
372
- end
373
- # Singatures
374
- if signatures
375
- data_list = []
376
- signatures.each do |signature|
377
- data = {
378
- signatureInformationEncoding: signature[:encoding],
379
- signer: signature[:signer],
380
- signatureMethod: signature[:method],
381
- signatureValue: signature[:value],
382
- signatureValidationRules: signature[:rules],
383
- signatureProperties: signature[:properties],
384
- }.cleanup
385
- data_list << OpenStruct.new(data) unless data.empty?
386
- end
387
- tech_data << Signature.new(array: data_list) unless data_list.empty?
388
- end
389
- # Hardware
390
- if hardware_infos
391
- data_list = []
392
- hardware_infos.each do |hardware|
393
- data = {
394
- hardwareName: hardware[:name],
395
- hardwareType: hardware[:type],
396
- hardwareOtherInformation: hardware[:info],
397
- }.cleanup
398
- data_list << OpenStruct.new(data) unless data.empty?
399
- end
400
- tech_data << HardwareInfo.new(array: data_list) unless data_list.empty?
401
- end
402
- # Software
403
- if software_infos
404
- data_list = []
405
- software_infos.each do |software|
406
- data = {
407
- softwareName: software[:name],
408
- softwareVersion: software[:version],
409
- softwareType: software[:type],
410
- softwareOtherInformation: software[:info],
411
- softwareDependancy: software[:dependency],
412
- }.cleanup
413
- data_list << OpenStruct.new(data) unless data.empty?
414
- end
415
- tech_data << SoftwareInfo.new(array: data_list) unless data_list.empty?
416
- end
417
- # Relationship
418
- if relationship_infos
419
- data_list = []
420
- relationship_infos.each do |relationship|
421
- data = {
422
- relationshipType: relationship[:type],
423
- relationshipSubType: relationship[:subtype],
424
- relatedObjectIdentifierType1: relationship[:type1],
425
- relatedObjectIdentifierValue1: relationship[:id1],
426
- relatedObjectSequence1: relationship[:seq1],
427
- relatedObjectIdentifierType2: relationship[:type2],
428
- relatedObjectIdentifierValue2: relationship[:id2],
429
- relatedObjectSequence2: relationship[:seq2],
430
- relatedObjectIdentifierType3: relationship[:type3],
431
- relatedObjectIdentifierValue3: relationship[:id3],
432
- relatedObjectSequence3: relationship[:seq3],
433
- }.cleanup
434
- data_list << OpenStruct.new(data) unless data.empty?
435
- end
436
- tech_data << RelationShip.new(array: data_list) unless data_list.empty?
437
- end
438
- # Environment
439
- if environments
440
- data_list = []
441
- environments.each do |environment|
442
- data = {
443
- environmentCharacteristic: environment[:characteristic],
444
- environmentPurpose: environment[:purpose],
445
- environmentNote: environment[:note],
446
- }.cleanup
447
- data_list << OpenStruct.new(data) unless data.empty?
448
- end
449
- tech_data << Environment.new(array: data_list) unless data_list.empty?
450
- end
451
- # Application
452
- if applications
453
- data_list = []
454
- applications.each do |application|
455
- data = {
456
- creatingApplicationName: application[:name],
457
- creatingApplicationVersion: application[:version],
458
- dateCreatedByApplication: application[:date],
459
- }.cleanup
460
- data_list << OpenStruct.new(data) unless data.empty?
461
- end
462
- tech_data << Application.new(array: data_list) unless data_list.empty?
463
- end
464
- # Finally assemble technical section
465
- dnx[:tech] = tech_data unless tech_data.empty?
466
- dnx
467
- end
468
-
469
- end
470
-
471
- class Div
472
- include IdContainer
473
-
474
- attr_accessor :label
475
-
476
- def xml_id
477
- "div-#{id}"
478
- end
479
-
480
- def children
481
- files + divs
482
- end
483
-
484
- def files
485
- @files ||= Array.new
486
- end
487
-
488
- def divs
489
- @divs ||= Array.new
490
- end
491
-
492
- def <<(obj)
493
- case obj
494
- when File
495
- files << obj
496
- when Div
497
- divs << obj
498
- else
499
- raise RuntimeError, "Child object type not supported: #{obj.class}"
500
- end
501
- end
502
- end
503
-
504
- class Map
505
- include IdContainer
506
-
507
- attr_accessor :representation, :div
508
-
509
- def xml_id
510
- "smap-#{id}"
511
- end
512
-
513
- end
514
-
515
- class DnxSection < OpenStruct
516
- def self.tag(value = nil)
517
- var_name = '@tag'
518
- if value.nil?
519
- instance_variable_get(var_name)
520
- else
521
- instance_variable_set(var_name, value)
522
- end
523
- end
524
-
525
- def tag
526
- self.class.tag
527
- end
528
- end
529
-
530
- class TechObjectChars < DnxSection
531
- tag 'objectCharacteristics'
532
- end
533
-
534
- class TechGeneralIE < DnxSection
535
- tag 'generalIECharacteristics'
536
- end
537
-
538
- class TechGeneralRep < DnxSection
539
- tag 'generalRepCharacteristics'
540
- end
541
-
542
- class TechGeneralFile < DnxSection
543
- tag 'generalFileCharacteristics'
544
- end
545
-
546
- class RetentionPeriod < DnxSection
547
- tag 'retentionPolicy'
548
- end
549
-
550
- class TechFixity < DnxSection
551
- tag 'fileFixity'
552
- end
553
-
554
- class Rights < DnxSection
555
- tag 'accessRightsPolicy'
556
- end
557
-
558
- class WebHarvesting < DnxSection
559
- tag 'webHarvesting'
560
- end
561
-
562
- class CollectionInfo < DnxSection
563
- tag 'Collection'
564
- end
565
-
566
- class PreservationLevel < DnxSection
567
- tag 'preservationLevel'
568
- end
569
-
570
- class EnvDeps < DnxSection
571
- tag 'environmentDependencies'
572
- end
573
-
574
- class HardwareId < DnxSection
575
- tag 'envHardwareRegistry'
576
- end
577
-
578
- class SoftwareId < DnxSection
579
- tag 'envSoftwareRegistry'
580
- end
581
-
582
- class HardwareInfo < DnxSection
583
- tag 'environmentHardware'
584
- end
585
-
586
- class SoftwareInfo < DnxSection
587
- tag 'environmentSoftware'
588
- end
589
-
590
- class Relationship < DnxSection
591
- tag 'relationship'
592
- end
593
-
594
- class Environment < DnxSection
595
- tag 'environment'
596
- end
597
-
598
- class Inhibitor < DnxSection
599
- tag 'inhibitors'
600
- end
601
-
602
- class Signature < DnxSection
603
- tag 'signatureInformation'
604
- end
605
-
606
- class Application < DnxSection
607
- tag 'creatingApplication'
608
- end
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 {Representation}s, {File}s, {Div}isions and {Map}s.
18
+ # These are simple container classes that take care of assigning proper ids and accept most known attributes.
19
+ # Each of the container classes have a corresponding method on the METS class that takes a Hash with attributes
20
+ # and returns the created object.
21
+ #
22
+ # {Div} and {File} instances can be added to a {Div} instance and a Div can be associated with a {Representation},
23
+ # thus creating a structmap.
24
+ #
25
+ # The {#amd_info=} method on the {MetsFile} class sets the main amd parameters and
26
+ #
27
+ # With the help of the {DnxSection} class and it's derived classes, the container classes can generate the amd
28
+ # sections for the METS file.
29
+ class MetsFile
609
30
 
610
- attr_reader :representations, :files, :divs, :maps
31
+ # Keeps track of {Representation}s created
32
+ attr_reader :representations
33
+ # Keeps track of {File}s created
34
+ attr_reader :files
35
+ # Keeps track of {Div}s created
36
+ attr_reader :divs
37
+ # Keeps track of {Map}s created
38
+ attr_reader :maps
611
39
 
612
40
  # noinspection RubyConstantNamingConvention
41
+
42
+ # Namespace constants for METS XML
613
43
  NS = {
614
44
  mets: 'http://www.loc.gov/METS/',
615
45
  dc: 'http://purl.org/dc/elements/1.1/',
@@ -617,6 +47,7 @@ module Libis
617
47
  xlin: 'http://www.w3.org/1999/xlink',
618
48
  }
619
49
 
50
+ # Creates an initializes a new {MetsFile} instance
620
51
  def initialize
621
52
  @representations = {}
622
53
  @files = {}
@@ -626,6 +57,20 @@ module Libis
626
57
  @dc_record = nil
627
58
  end
628
59
 
60
+ # Reads an existing METS XML file and parses into a large Hash structure for inspection.
61
+ # It will not immediately allow you to create a {MetsFile} instance from it, but with some inspection and
62
+ # knowledge of METS file structure it should be possible to recreate a similar file using the class.
63
+ #
64
+ # The returned Hash has the following structure:
65
+ #
66
+ # * :amd - the general AMD section with subsections
67
+ # * :dmd - the general DMD section with the DC record(s)
68
+ # Each amd section has one or more subsections with keys :tech, :rights, :source or :digiprov. Each
69
+ # subsection is a Hash with section id as key and an array as value. For each <record> element a Hash is
70
+ # added to the array with <key@id> as key and <key> content as value.
71
+ #
72
+ # @param [String,Hash,::Libis::Tools::XmlDocument, Nokogiri::XML::Document] xml XML file in any of the listed formats.
73
+ # @return [Hash] The parsed information.
629
74
  def self.parse(xml)
630
75
  xml_doc = case xml
631
76
  when String
@@ -716,17 +161,40 @@ module Libis
716
161
  )
717
162
  end
718
163
 
164
+ # Sets the DC record for the global amd section.
165
+ #
166
+ # @param [String] xml Serialized Dublin Core XML file
719
167
  def dc_record=(xml)
720
168
  @dc_record = xml
721
169
  end
722
170
 
171
+ # Sets the attributes for the global amd section.
172
+ #
173
+ # @param [Hash] hash name, value pairs for the DNX sections. Each will go into it's appropriate AMD and DNX
174
+ # sections automatically.
175
+ # The following names are currently supported:
176
+ # * status
177
+ # * entity_type
178
+ # * user_a
179
+ # * user_b
180
+ # * user_c
181
+ # * submission_reason
182
+ # * retention_id - RentionPolicy ID
183
+ # * harvest_url
184
+ # * harvest_id
185
+ # * harvest_target
186
+ # * harvest_group
187
+ # * harvest_date
188
+ # * harvest_time
189
+ # * collection_id - Collection ID where the IE should be added to
190
+ # * access_right - AccessRight ID
191
+ # * source_metadata - Array with hashes like {type: 'MyXML', data: '<xml ....>'}
723
192
  def amd_info=(hash)
724
- @dnx = {}
725
193
  tech_data = []
726
194
  data = {
727
195
  groupID: hash[:group_id]
728
196
  }.cleanup
729
- tech_data << TechObjectChars.new(data) unless data.empty?
197
+ tech_data << ObjectCharacteristics.new(data) unless data.empty?
730
198
  data = {
731
199
  status: hash[:status],
732
200
  IEEntityType: hash[:entity_type],
@@ -735,11 +203,11 @@ module Libis
735
203
  UserDefinedC: hash[:user_c],
736
204
  submissionReason: hash[:submission_reason],
737
205
  }.cleanup
738
- tech_data << TechGeneralIE.new(data) unless data.empty?
206
+ tech_data << GeneralIECharacteristics.new(data) unless data.empty?
739
207
  data = {
740
208
  policyId: hash[:retention_id],
741
209
  }.cleanup
742
- tech_data << RetentionPeriod.new(data) unless data.empty?
210
+ tech_data << RetentionPolicy.new(data) unless data.empty?
743
211
  data = {
744
212
  primarySeedURL: hash[:harvest_url],
745
213
  WCTIdentifier: hash[:harvest_id],
@@ -752,19 +220,20 @@ module Libis
752
220
  data = {
753
221
  collectionId: hash[:collection_id]
754
222
  }.cleanup
755
- tech_data << CollectionInfo.new(data) unless data.empty?
223
+ tech_data << Collection.new(data) unless data.empty?
756
224
  @dnx[:tech] = tech_data unless tech_data.empty?
757
225
  data = {
758
226
  policyId: hash[:access_right]
759
227
  }.cleanup
760
228
  rights_data = []
761
- rights_data << Rights.new(data) unless data.empty?
229
+ rights_data << AccessRightsPolicy.new(data) unless data.empty?
762
230
  @dnx[:rights] = rights_data unless rights_data.empty?
763
231
  (hash[:source_metadata] || []).each_with_index do |metadata, i|
764
232
  @dnx["source-#{metadata[:type].to_s.upcase}-#{i+1}"] = metadata[:data]
765
233
  end
766
234
  end
767
235
 
236
+ # Create a new representation. See {Representation} for supported Hash keys.
768
237
  # @param [Hash] hash
769
238
  # @return [Libis::Tools::MetsFile::Representation]
770
239
  def representation(hash = {})
@@ -773,6 +242,7 @@ module Libis
773
242
  @representations[rep.id] = rep
774
243
  end
775
244
 
245
+ # Create a new division. See {Div} for supported Hash keys.
776
246
  # @param [Hash] hash
777
247
  # @return [Libis::Tools::MetsFile::Div]
778
248
  def div(hash = {})
@@ -781,6 +251,7 @@ module Libis
781
251
  @divs[div.id] = div
782
252
  end
783
253
 
254
+ # Create a new file. See {File} for supported Hash keys.
784
255
  # @param [Hash] hash
785
256
  # @return [Libis::Tools::MetsFile::File]
786
257
  def file(hash = {})
@@ -789,6 +260,7 @@ module Libis
789
260
  @files[file.id] = file
790
261
  end
791
262
 
263
+ # Create a new structmap.
792
264
  # @param [Libis::Tools::MetsFile::Representation] rep
793
265
  # @param [Libis::Tools::MetsFile::Div] div
794
266
  # @return [Libis::Tools::MetsFile::Map]
@@ -799,6 +271,7 @@ module Libis
799
271
  @maps[map.id] = map
800
272
  end
801
273
 
274
+ # Create the METS XML document.
802
275
  # @return [Libis::Tools::XmlDocument]
803
276
  def xml_doc
804
277
  ::Libis::Tools::XmlDocument.build do |xml|
@@ -815,14 +288,17 @@ module Libis
815
288
 
816
289
  protected
817
290
 
291
+ # ID for the DMD section of a representation, division or file
818
292
  def dmd_id(id)
819
293
  "#{id}-dmd"
820
294
  end
821
295
 
296
+ # ID for the AMD section of a representation, division or file
822
297
  def amd_id(id)
823
298
  "#{id}-amd"
824
299
  end
825
300
 
301
+ # Helper method to create the XML DMD sections
826
302
  def add_dmd(xml, object = nil)
827
303
  case object
828
304
  when NilClass
@@ -838,6 +314,7 @@ module Libis
838
314
  end
839
315
  end
840
316
 
317
+ # Helper method to create the XML AMD sections
841
318
  def add_amd(xml, object = nil)
842
319
  case object
843
320
  when NilClass
@@ -855,6 +332,7 @@ module Libis
855
332
  end
856
333
  end
857
334
 
335
+ # Helper method to create the XML file section
858
336
  def add_filesec(xml, object = nil, representation = nil)
859
337
  case object
860
338
  when NilClass
@@ -895,6 +373,7 @@ module Libis
895
373
  end
896
374
  end
897
375
 
376
+ # Helper method to create the Structmap
898
377
  def add_struct_map(xml, object = nil)
899
378
  case object
900
379
  when NilClass
@@ -930,6 +409,7 @@ module Libis
930
409
 
931
410
  end
932
411
 
412
+ # Helper method to create a single XML DMD section
933
413
  def add_dmd_section(xml, id, dc_record = nil)
934
414
  return if dc_record.nil?
935
415
  xml[:mets].dmdSec(ID: dmd_id(id)) {
@@ -941,6 +421,7 @@ module Libis
941
421
  }
942
422
  end
943
423
 
424
+ # Helper method to create a single AMD section
944
425
  def add_amd_section(xml, id, dnx_sections = {})
945
426
  xml[:mets].amdSec(ID: amd_id(id)) {
946
427
  dnx_sections.each do |section_type, data|
@@ -965,6 +446,7 @@ module Libis
965
446
  }
966
447
  end
967
448
 
449
+ # Helper method to create the XML DNX sections
968
450
  def add_dnx_sections(xml, section_data)
969
451
  section_data ||= []
970
452
  xml.dnx(xmlns: NS[:dnx]) {
@@ -984,6 +466,7 @@ module Libis
984
466
  }
985
467
  end
986
468
 
469
+ # Helper method to parse a XML div
987
470
  def parse_div(div, rep)
988
471
  if div[:TYPE] == 'FILE'
989
472
  file_id = div.children.first[:FILEID]