relaton-bib 1.20.8 → 2.0.0

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 (128) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -1
  3. data/.rubocop.yml +1 -1
  4. data/CLAUDE.md +96 -0
  5. data/Gemfile +1 -4
  6. data/README.adoc +195 -154
  7. data/bin/console +1 -1
  8. data/grammars/basicdoc.rng +1572 -671
  9. data/grammars/biblio-standoc.rng +107 -46
  10. data/grammars/biblio.rng +1012 -377
  11. data/grammars/versions.json +26 -26
  12. data/lib/relaton/bib/converter/asciibib/to_asciibib.rb +663 -0
  13. data/lib/relaton/bib/converter/asciibib.rb +13 -0
  14. data/lib/relaton/bib/converter/bibtex/from_bibtex.rb +245 -0
  15. data/lib/relaton/bib/converter/bibtex/to_bibtex.rb +341 -0
  16. data/lib/relaton/bib/converter/bibtex.rb +23 -0
  17. data/lib/relaton/bib/converter/bibxml/from_rfcxml.rb +383 -0
  18. data/lib/relaton/bib/converter/bibxml/from_rfcxml_referencegroup.rb +71 -0
  19. data/lib/relaton/bib/converter/bibxml/to_rfcxml.rb +305 -0
  20. data/lib/relaton/bib/converter/bibxml/to_rfcxml_referencegroup.rb +52 -0
  21. data/lib/relaton/bib/converter/bibxml.rb +51 -0
  22. data/lib/relaton/bib/hash_parser_v1.rb +754 -0
  23. data/lib/relaton/bib/item_data.rb +229 -0
  24. data/lib/relaton/bib/model/abstract.rb +16 -0
  25. data/lib/relaton/bib/model/address.rb +22 -0
  26. data/lib/relaton/bib/model/affiliation.rb +16 -0
  27. data/lib/relaton/bib/model/bibdata.rb +24 -0
  28. data/lib/relaton/bib/model/bibitem.rb +23 -0
  29. data/lib/relaton/bib/model/contact.rb +18 -0
  30. data/lib/relaton/bib/model/contribution_info.rb +16 -0
  31. data/lib/relaton/bib/model/contributor.rb +71 -0
  32. data/lib/relaton/bib/model/copyright.rb +27 -0
  33. data/lib/relaton/bib/model/date.rb +31 -0
  34. data/lib/relaton/bib/model/depiction.rb +16 -0
  35. data/lib/relaton/bib/model/docidentifier.rb +33 -0
  36. data/lib/relaton/bib/model/doctype.rb +14 -0
  37. data/lib/relaton/bib/model/edition.rb +14 -0
  38. data/lib/relaton/bib/model/ext.rb +26 -0
  39. data/lib/relaton/bib/model/extent.rb +16 -0
  40. data/lib/relaton/bib/model/formattedref.rb +43 -0
  41. data/lib/relaton/bib/model/full_name_type.rb +64 -0
  42. data/lib/relaton/bib/model/fullname.rb +11 -0
  43. data/lib/relaton/bib/model/ics.rb +23 -0
  44. data/lib/relaton/bib/model/image.rb +28 -0
  45. data/lib/relaton/bib/model/item.rb +137 -0
  46. data/lib/relaton/bib/model/item_base.rb +27 -0
  47. data/lib/relaton/bib/model/keyword.rb +30 -0
  48. data/lib/relaton/bib/model/locality.rb +18 -0
  49. data/lib/relaton/bib/model/locality_stack.rb +14 -0
  50. data/lib/relaton/bib/model/localized_string.rb +57 -0
  51. data/lib/relaton/bib/model/localized_string_attrs.rb +24 -0
  52. data/lib/relaton/bib/model/logo.rb +14 -0
  53. data/lib/relaton/bib/model/medium.rb +22 -0
  54. data/lib/relaton/bib/model/note.rb +16 -0
  55. data/lib/relaton/bib/model/organization.rb +13 -0
  56. data/lib/relaton/bib/model/organization_type.rb +42 -0
  57. data/lib/relaton/bib/model/person.rb +36 -0
  58. data/lib/relaton/bib/model/phone.rb +14 -0
  59. data/lib/relaton/bib/model/place.rb +33 -0
  60. data/lib/relaton/bib/model/price.rb +14 -0
  61. data/lib/relaton/bib/model/relation.rb +43 -0
  62. data/lib/relaton/bib/model/series.rb +34 -0
  63. data/lib/relaton/bib/model/size.rb +23 -0
  64. data/lib/relaton/bib/model/source_locality_stack.rb +14 -0
  65. data/lib/relaton/bib/model/status.rb +27 -0
  66. data/lib/relaton/bib/model/structured_identifier.rb +48 -0
  67. data/lib/relaton/bib/model/subdivision.rb +16 -0
  68. data/lib/relaton/bib/model/title.rb +56 -0
  69. data/lib/relaton/bib/model/type/string_date.rb +48 -0
  70. data/lib/relaton/bib/model/uri.rb +18 -0
  71. data/lib/relaton/bib/model/validity.rb +16 -0
  72. data/lib/relaton/bib/model/version.rb +14 -0
  73. data/lib/relaton/bib/namespace_helper.rb +21 -0
  74. data/lib/relaton/bib/util.rb +18 -0
  75. data/lib/relaton/bib/version.rb +5 -0
  76. data/lib/relaton/bib.rb +45 -0
  77. data/relaton-bib.gemspec +11 -8
  78. metadata +125 -67
  79. data/lib/relaton_bib/bib_item_locality.rb +0 -175
  80. data/lib/relaton_bib/biblio_note.rb +0 -72
  81. data/lib/relaton_bib/biblio_version.rb +0 -46
  82. data/lib/relaton_bib/bibliographic_date.rb +0 -119
  83. data/lib/relaton_bib/bibliographic_item.rb +0 -668
  84. data/lib/relaton_bib/bibliographic_size.rb +0 -103
  85. data/lib/relaton_bib/bibtex_parser.rb +0 -246
  86. data/lib/relaton_bib/bibxml_parser.rb +0 -422
  87. data/lib/relaton_bib/classification.rb +0 -40
  88. data/lib/relaton_bib/config.rb +0 -16
  89. data/lib/relaton_bib/contribution_info.rb +0 -117
  90. data/lib/relaton_bib/contributor.rb +0 -277
  91. data/lib/relaton_bib/copyright_association.rb +0 -79
  92. data/lib/relaton_bib/deep_dup.rb +0 -30
  93. data/lib/relaton_bib/document_identifier.rb +0 -118
  94. data/lib/relaton_bib/document_relation.rb +0 -95
  95. data/lib/relaton_bib/document_relation_collection.rb +0 -57
  96. data/lib/relaton_bib/document_status.rb +0 -92
  97. data/lib/relaton_bib/document_type.rb +0 -52
  98. data/lib/relaton_bib/edition.rb +0 -55
  99. data/lib/relaton_bib/editorial_group.rb +0 -41
  100. data/lib/relaton_bib/extent.rb +0 -39
  101. data/lib/relaton_bib/forename.rb +0 -65
  102. data/lib/relaton_bib/formatted_ref.rb +0 -17
  103. data/lib/relaton_bib/formatted_string.rb +0 -133
  104. data/lib/relaton_bib/full_name.rb +0 -108
  105. data/lib/relaton_bib/hash_converter.rb +0 -568
  106. data/lib/relaton_bib/hit.rb +0 -52
  107. data/lib/relaton_bib/hit_collection.rb +0 -105
  108. data/lib/relaton_bib/ics.rb +0 -42
  109. data/lib/relaton_bib/image.rb +0 -95
  110. data/lib/relaton_bib/localized_string.rb +0 -149
  111. data/lib/relaton_bib/medium.rb +0 -76
  112. data/lib/relaton_bib/organization.rb +0 -165
  113. data/lib/relaton_bib/person.rb +0 -129
  114. data/lib/relaton_bib/place.rb +0 -203
  115. data/lib/relaton_bib/renderer/bibtex_builder.rb +0 -312
  116. data/lib/relaton_bib/renderer/bibxml.rb +0 -296
  117. data/lib/relaton_bib/series.rb +0 -119
  118. data/lib/relaton_bib/structured_identifier.rb +0 -173
  119. data/lib/relaton_bib/technical_committee.rb +0 -34
  120. data/lib/relaton_bib/typed_title_string.rb +0 -191
  121. data/lib/relaton_bib/typed_uri.rb +0 -57
  122. data/lib/relaton_bib/util.rb +0 -16
  123. data/lib/relaton_bib/validity.rb +0 -52
  124. data/lib/relaton_bib/version.rb +0 -3
  125. data/lib/relaton_bib/workers_pool.rb +0 -43
  126. data/lib/relaton_bib/workgroup.rb +0 -58
  127. data/lib/relaton_bib/xml_parser.rb +0 -718
  128. data/lib/relaton_bib.rb +0 -111
@@ -0,0 +1,754 @@
1
+ require_relative "../bib"
2
+
3
+ module Relaton
4
+ module Bib
5
+ module HashParserV1
6
+ include Core::ArrayWrapper
7
+ include Core::DateParser
8
+ extend self
9
+
10
+ # @param args [Hash]
11
+ # @return [Hash]
12
+ def hash_to_bib(args) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
13
+ return nil unless args.is_a?(Hash)
14
+
15
+ ret = Marshal.load(Marshal.dump(symbolize(args))) # deep copy
16
+ ret[:fetched] &&= ::Date.parse(ret[:fetched])
17
+ ret.delete(:"schema-version")
18
+ ret.delete(:schema_version)
19
+ id_hash_to_bib ret
20
+ title_hash_to_bib(ret)
21
+ link_hash_to_bib(ret)
22
+ language_hash_to_bib(ret)
23
+ script_hash_to_bib(ret)
24
+ dates_hash_to_bib(ret)
25
+ docid_hash_to_bib(ret)
26
+ version_hash_to_bib(ret)
27
+ biblionote_hash_to_bib(ret)
28
+ abstract_hash_to_bib(ret)
29
+ formattedref_hash_to_bib(ret)
30
+ docstatus_hash_to_bib(ret)
31
+ contributors_hash_to_bib(ret)
32
+ editorialgroup_hash_to_bib(ret)
33
+ edition_hash_to_bib(ret)
34
+ copyright_hash_to_bib(ret)
35
+ relations_hash_to_bib(ret)
36
+ series_hash_to_bib(ret)
37
+ medium_hash_to_bib(ret)
38
+ place_hash_to_bib(ret)
39
+ extent_hash_to_bib(ret)
40
+ size_hash_to_bib(ret)
41
+ accesslocation_hash_to_bib(ret)
42
+ classification_hash_to_bib(ret)
43
+ validity_hash_to_bib(ret)
44
+ keyword_hash_to_bib(ret)
45
+ ret[:license] = array(ret[:license])
46
+ ext_hash_to_bib ret
47
+ ret
48
+ end
49
+
50
+ def id_hash_to_bib(ret)
51
+ return unless ret[:id]
52
+
53
+ ret[:id] = ret[:id].gsub(/\W+/, "")
54
+ end
55
+
56
+ def ext_hash_to_bib(ret) # rubocop:disable Metrics/AbcSize
57
+ ret[:ext] ||= {}
58
+ doctype_hash_to_bib ret
59
+ ret[:ext][:subdoctype] = ret.delete(:subdoctype) if ret[:subdoctype]
60
+ ret[:ext][:flavor] ||= flavor(ret)
61
+ ics_hash_to_bib ret
62
+ structuredidentifier_hash_to_bib ret
63
+ ret[:ext] = Bib::Ext.new(**ret[:ext])
64
+ end
65
+
66
+ def flavor(ret)
67
+ return unless ret[:docid]
68
+
69
+ docid = ret[:docidentifier].find(&:primary)
70
+ return unless docid
71
+
72
+ docid.type.downcase
73
+ end
74
+
75
+ def keyword_hash_to_bib(ret)
76
+ ret[:keyword] = array(ret[:keyword]).map do |keyword|
77
+ Bib::Keyword.new vocab: localizedstring(keyword)
78
+ end
79
+ end
80
+
81
+ def extent_hash_to_bib(ret)
82
+ return unless ret[:extent]
83
+
84
+ ret[:extent] = array(ret[:extent]).map do |e|
85
+ Relaton::Bib::Extent.new locality: locality(e[:locality]),
86
+ locality_stack: locality_stack(e[:locality_stack])
87
+ end
88
+ end
89
+
90
+ def locality(locality)
91
+ array(locality).map { |l| Bib::Locality.new(**l) }
92
+ end
93
+
94
+ def locality_stack(locality_stack)
95
+ array(locality_stack).map { |l| Bib::LocalityStack.new locality: locality(l[:locality]) }
96
+ end
97
+
98
+ def size_hash_to_bib(ret)
99
+ return unless ret[:size]
100
+
101
+ ret[:size] = array(ret[:size])
102
+ value = ret[:size]&.map do |val|
103
+ val[:content] = val.delete(:value)
104
+ Bib::Size::Value.new(**val)
105
+ end
106
+ ret[:size] = Bib::Size.new(value: value)
107
+ end
108
+
109
+ def title_hash_to_bib(ret)
110
+ # return unless ret[:title]
111
+
112
+ # ret[:title] = array(ret[:title]).reduce(Bib::TitleCollection.new) do |m, t|
113
+ # m << (t.is_a?(Hash) ? Bib::Title.new(**t) : Bib::Title.new(content: t))
114
+ # end
115
+ ret[:title] &&= title_collection(ret[:title])
116
+ end
117
+
118
+ def language_hash_to_bib(ret)
119
+ return unless ret[:language]
120
+
121
+ ret[:language] = array(ret[:language])
122
+ end
123
+
124
+ def script_hash_to_bib(ret)
125
+ return unless ret[:script]
126
+
127
+ ret[:script] = array(ret[:script])
128
+ end
129
+
130
+ def abstract_hash_to_bib(ret)
131
+ ret[:abstract] &&= array(ret[:abstract]).map do |a|
132
+ a = { content: a } if a.is_a?(String)
133
+ lang_scrip_array_to_string(a)
134
+ Bib::Abstract.new(**a)
135
+ end
136
+ end
137
+
138
+ def link_hash_to_bib(ret)
139
+ return unless ret[:link]
140
+
141
+ ret[:source] = array(ret[:link]).map do |l|
142
+ Bib::Uri.new(**l)
143
+ end
144
+ end
145
+
146
+ def place_hash_to_bib(ret)
147
+ ret[:place] &&= array(ret[:place]).map { |pl| create_place(pl) }
148
+ end
149
+
150
+ def create_place(place)
151
+ if place.is_a?(String)
152
+ Bib::Place.new(formatted_place: place)
153
+ else
154
+ place[:region] &&= create_region(place[:region])
155
+ place[:country] &&= create_region(place[:country])
156
+ Bib::Place.new(**place)
157
+ end
158
+ end
159
+
160
+ def create_region(region)
161
+ array(region).map do |r|
162
+ r[:content] ||= r.delete(:name)
163
+ r[:iso] ||= r.delete(:code)
164
+ Bib::Place::RegionType.new(**r)
165
+ end
166
+ end
167
+
168
+ def accesslocation_hash_to_bib(ret)
169
+ return unless ret[:accesslocation]
170
+
171
+ ret[:accesslocation] = array(ret[:accesslocation])
172
+ end
173
+
174
+ def dates_hash_to_bib(ret)
175
+ ret[:date] &&= array(ret[:date]).map do |d|
176
+ # `at` is synonym of `on`, which is reserved word in YAML
177
+ d[:at] = d.delete(:value) if d[:value]
178
+ Bib::Date.new(**d)
179
+ end
180
+ end
181
+
182
+ def docid_hash_to_bib(ret) # rubocop:disable Metrics/AbcSize
183
+ return unless ret[:docid]
184
+
185
+ ret[:docidentifier] = array(ret[:docid]).map do |id|
186
+ id[:type] ||= id[:id].match(/^\w+(?=\s)/)&.to_s
187
+ id[:content] = id[:id]
188
+ create_docid(**id)
189
+ end
190
+ end
191
+
192
+ def create_docid(**args)
193
+ Bib::Docidentifier.new(**args)
194
+ end
195
+
196
+ def version_hash_to_bib(ret)
197
+ return unless ret[:version]
198
+
199
+ ret[:version] = array(ret[:version]).map do |v|
200
+ v[:revision_date] &&= parse_date(v[:revision_date], str: false)
201
+ Bib::Version.new(**v)
202
+ end
203
+ end
204
+
205
+ def biblionote_hash_to_bib(ret)
206
+ ret[:note] = array(ret.delete(:biblionote)).map do |n|
207
+ n.is_a?(String) ? Bib::Note.new(content: n) : Bib::Note.new(**n)
208
+ end
209
+ end
210
+
211
+ def formattedref_hash_to_bib(ret)
212
+ ret[:formattedref] &&
213
+ ret[:formattedref] = formattedref(ret[:formattedref])
214
+ end
215
+
216
+ def docstatus_hash_to_bib(ret)
217
+ ret[:docstatus] && ret[:status]= Bib::Status.new(
218
+ stage: stage(ret[:docstatus][:stage]),
219
+ substage: stage(ret[:docstatus][:substage]),
220
+ iteration: ret[:docstatus][:iteration],
221
+ )
222
+ end
223
+
224
+ # @param stg [Hash]
225
+ # @return [Relaton::Bib::DocumentStatus::Stage]
226
+ def stage(stg)
227
+ return unless stg
228
+
229
+ args = stg.is_a?(String) ? { content: stg } : stg
230
+ args[:content] ||= args.delete(:value)
231
+ Bib::Status::Stage.new(**args)
232
+ end
233
+
234
+ def contributors_hash_to_bib(ret) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength,Metrics/PerceivedComplexity
235
+ ret[:contributor] &&= array(ret[:contributor]).map do |c|
236
+ roles = array(c[:role]).map do |r|
237
+ if r.is_a? Hash
238
+ desc = array(r[:description]).map { |d| localized_marked_up_string d }
239
+ Bib::Contributor::Role.new(type: r[:type], description: desc)
240
+ else
241
+ Bib::Contributor::Role.new(type: r)
242
+ end
243
+ end
244
+ c[:role] = roles
245
+ (c[:person] &&= create_person(c[:person])) ||
246
+ (c[:organization] &&= create_organization(c[:organization]))
247
+ Bib::Contributor.new(**c)
248
+ end
249
+ end
250
+
251
+ def edition_hash_to_bib(ret)
252
+ ret[:edition] &&= if ret[:edition].is_a?(Hash)
253
+ Bib::Edition.new(**ret[:edition])
254
+ else
255
+ Bib::Edition.new(content: ret[:edition])
256
+ end
257
+ end
258
+
259
+ def create_organization(org)
260
+ return nil if org.nil?
261
+
262
+ Bib::Organization.new(**org_hash_to_bib(org))
263
+ end
264
+
265
+ def org_hash_to_bib(org) # rubocop:disable Metrics/AbcSize
266
+ org[:identifier] = create_org_identifier(org[:identifier])
267
+ org[:subdivision] = create_org_subdivision(org[:subdivision])
268
+ org[:address] = address_hash_to_bib(org[:contact])
269
+ org[:phone] = phone_hash_to_bib(org[:contact])
270
+ org[:email] = email_hash_to_bib(org[:contact])
271
+ org[:uri] = uri_hash_to_bib(org[:contact] || org)
272
+ org[:logo] = array(org[:logo]).map do |l|
273
+ Bib::Logo.new(image: Bib::Image.new(**l[:image]))
274
+ end
275
+ org[:name] = typed_localized_string(org[:name])
276
+ org[:abbreviation] &&= localizedstring(org[:abbreviation])
277
+ org
278
+ end
279
+
280
+ def create_org_identifier(identifier)
281
+ array(identifier).map do |id|
282
+ Bib::Organization::Identifier.new(type: id[:type], content: id[:id])
283
+ end
284
+ end
285
+
286
+ def create_org_subdivision(subdivision)
287
+ array(subdivision).map do |sub|
288
+ if sub.is_a? String
289
+ orgname = Bib::TypedLocalizedString.new(content: sub)
290
+ Bib::Subdivision.new(name: [orgname])
291
+ elsif sub.is_a?(Hash) && sub[:content]
292
+ sub[:name] = sub.delete(:content)
293
+ Bib::Subdivision.new(**org_hash_to_bib(sub))
294
+ end
295
+ end.compact
296
+ end
297
+
298
+ def typed_localized_string(typed_strs)
299
+ array(typed_strs).map do |args|
300
+ if args.is_a? String
301
+ Bib::TypedLocalizedString.new(content: args)
302
+ elsif args.is_a? Hash
303
+ Bib::TypedLocalizedString.new(**args)
304
+ end
305
+ end
306
+ end
307
+
308
+ # def create_org_name(name)
309
+ # array(name).map do |nm|
310
+ # if nm.is_a?(Hash)
311
+ # Bib::Organization::Name.new(**nm)
312
+ # else
313
+ # Bib::Organization::Name.new(content: nm)
314
+ # end
315
+ # end
316
+ # end
317
+
318
+ def create_person(person)
319
+ Bib::Person.new(
320
+ name: fullname_hash_to_bib(person),
321
+ credential: array(person[:credential]),
322
+ affiliation: affiliation_hash_to_bib(person),
323
+ address: address_hash_to_bib(person[:contact]),
324
+ phone: phone_hash_to_bib(person[:contact]),
325
+ email: email_hash_to_bib(person[:contact]),
326
+ uri: uri_hash_to_bib(person[:contact]),
327
+ identifier: person_identifiers_hash_to_bib(person),
328
+ )
329
+ end
330
+
331
+ def fullname_hash_to_bib(person) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
332
+ n = person[:name]
333
+ fname = given_hash_to_bib n[:given] || n # `n` is for backward compatibility
334
+ Bib::FullName.new(
335
+ abbreviation: localizedstring(n[:abbreviation]),
336
+ forename: fname,
337
+ formatted_initials: localizedstring(n.dig(:given, :formatted_initials)),
338
+ addition: array(n[:addition])&.map { |f| localizedstring(f) },
339
+ prefix: array(n[:prefix])&.map { |f| localizedstring(f) },
340
+ surname: localizedstring(n[:surname]),
341
+ completename: localizedstring(n[:completename]),
342
+ )
343
+ end
344
+
345
+ def given_hash_to_bib(given)
346
+ return unless given
347
+
348
+ array(given[:forename])&.map { |f| forename_hash_to_bib(f) }
349
+ end
350
+
351
+ def forename_hash_to_bib(fname)
352
+ case fname
353
+ when Hash
354
+ lang_scrip_array_to_string fname
355
+ FullNameType::Forename.new(**fname)
356
+ when String then FullNameType::Forename.new(content: fname)
357
+ end
358
+ end
359
+
360
+ def person_identifiers_hash_to_bib(person)
361
+ array(person[:identifier])&.map do |a|
362
+ Bib::Person::Identifier.new(type: a[:type], content: a[:id])
363
+ end
364
+ end
365
+
366
+ def affiliation_hash_to_bib(person) # rubocop:disable Metrics/AbcSize
367
+ return [] unless person[:affiliation]
368
+
369
+ array(person[:affiliation]).map do |a|
370
+ a[:description] = array(a[:description]).map do |d|
371
+ localized_marked_up_string d
372
+ end
373
+ Bib::Affiliation.new(
374
+ organization: create_organization(a[:organization]),
375
+ description: a[:description], name: localizedstring(a[:name])
376
+ )
377
+ end
378
+ end
379
+
380
+ def address_hash_to_bib(contact)
381
+ array(contact).reduce([]) do |a, c|
382
+ next a unless c[:address]
383
+
384
+ a << create_address(c[:address])
385
+ end
386
+ end
387
+
388
+ def phone_hash_to_bib(contact)
389
+ array(contact).reduce([]) do |a, c|
390
+ next a unless c[:phone]
391
+
392
+ c[:type] ||= "work"
393
+ a << Bib::Phone.new(type: c[:type], content: c[:phone])
394
+ end
395
+ end
396
+
397
+ def email_hash_to_bib(contact)
398
+ array(contact).reduce([]) do |a, c|
399
+ next a unless c[:email]
400
+
401
+ a << c[:email]
402
+ end
403
+ end
404
+
405
+ def uri_hash_to_bib(contact)
406
+ array(contact).reduce([]) do |a, c|
407
+ next a unless c[:uri] || c[:url]
408
+
409
+ a << Bib::Uri.new(content: c[:uri] || c[:url])
410
+ end
411
+ end
412
+
413
+ # def contacts_hash_to_bib(entity) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength,Metrics/CyclomaticComplexity
414
+ # return [] unless entity[:contact]
415
+
416
+ # Relaton.array(entity[:contact]).map do |a|
417
+ # type, value = a.reject { |k, _| k == :type }.flatten
418
+ # case type
419
+ # when :street, :city, :state, :country, :postcode # it's for old version compatibility, should be removed in the future
420
+ # a[:street] = Relaton.array(a[:street])
421
+ # Bib::Address.new(**a)
422
+ # when :address then create_address(a[:address])
423
+ # when :phone, :email, :uri
424
+ # Bib::Contact.new(type: type.to_s, value: value, subtype: a[:type])
425
+ # else # it's for old version compatibility, should be removed in the future
426
+ # Bib::Contact.new(**a)
427
+ # end
428
+ # end
429
+ # end
430
+
431
+ def create_address(adr)
432
+ if adr.is_a?(Hash)
433
+ adr[:street] = array(adr[:street])
434
+ Bib::Address.new(**adr)
435
+ else
436
+ Bib::Address.new(formatted_address: adr)
437
+ end
438
+ end
439
+
440
+ # @param ret [Hash]
441
+ def copyright_hash_to_bib(ret)
442
+ ret[:copyright] &&= array(ret[:copyright]).map do |c|
443
+ c[:owner] = array(c[:owner]).map do |o|
444
+ create_contribution_info(o)
445
+ end
446
+ Bib::Copyright.new(**c)
447
+ end
448
+ end
449
+
450
+ def create_contribution_info(contrib)
451
+ if contrib[:name].is_a?(Hash) && (contrib[:name].keys & %i[prefix forename addition surname completename]).any?
452
+ Bib::ContributionInfo.new person: create_person(contrib)
453
+ else
454
+ Bib::ContributionInfo.new organization: create_organization(contrib)
455
+ end
456
+ end
457
+
458
+ # @param ret [Hash]
459
+ def relations_hash_to_bib(ret) # rubocop:disable Metrics/MethodLength,Metrics/AbcSize
460
+ return unless ret[:relation]
461
+
462
+ ret[:relation] &&= array(ret[:relation]).map do |rel|
463
+ rel[:description] = LocalizedMarkedUpString.new(**rel[:description]) if rel[:description]
464
+ relation_bibitem_hash_to_bib(rel)
465
+ relation_locality_hash_to_bib(rel)
466
+ relation_locality_stack_hash_to_bib(rel)
467
+ relaton_source_locality_stack_hash_to_bib(rel)
468
+ relation_source_locality_hash_to_bib(rel)
469
+ create_relation rel
470
+ end
471
+ end
472
+
473
+ def create_relation(rel)
474
+ Bib::Relation.new(**rel)
475
+ end
476
+
477
+ # @param rel [Hash] relation
478
+ def relation_bibitem_hash_to_bib(rel)
479
+ if rel[:bibitem]
480
+ rel[:bibitem] = bib_item hash_to_bib(rel[:bibitem])
481
+ else
482
+ Util.warn "Bibitem missing: `#(rel)`"
483
+ rel[:bibitem] = nil
484
+ end
485
+ end
486
+
487
+ # @param item_hash [Hash]
488
+ # @return [Relaton::Bib::Item]
489
+ def bib_item(item_hash)
490
+ ItemData.new(**item_hash)
491
+ end
492
+
493
+ # @param rel [Hash] relation
494
+ # @return [Relaton::Bib::LocalityStack]
495
+ def relation_locality_hash_to_bib(rel)
496
+ return unless rel[:locality]&.any?
497
+
498
+ rel[:locality] = array(rel[:locality]).map do |bl|
499
+ Bib::Locality.new(**bl)
500
+ end
501
+ end
502
+
503
+ def relation_locality_stack_hash_to_bib(rel)
504
+ return unless rel[:locality_stack]&.any?
505
+
506
+ rel[:locality_stack] = array(rel[:locality_stack]).map do |ls|
507
+ Bib::LocalityStack.new locality: relation_locality_hash_to_bib(ls)
508
+ end
509
+ end
510
+
511
+ # def locality_locality_stack(lls)
512
+ # if lls[:locality_stack]
513
+ # RelatonBib.array(lls[:locality_stack]).map do |lc|
514
+ # l = lc[:locality] || lc
515
+ # Locality.new(l[:type], l[:reference_from], l[:reference_to])
516
+ # end
517
+ # else
518
+ # [Locality.new(lls[:type], lls[:reference_from], lls[:reference_to])]
519
+ # end
520
+ # end
521
+
522
+ # @param rel [Hash] relation
523
+ def relation_source_locality_hash_to_bib(rel)
524
+ rel[:source_locality] = array(rel[:source_locality]).map do |loc|
525
+ Bib::Locality.new(**loc)
526
+ end
527
+ end
528
+
529
+ def relaton_source_locality_stack_hash_to_bib(rel) # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength
530
+ sls = array(rel[:source_locality_stack])
531
+ array(rel[:source_locality]).each do |loc|
532
+ sls << array(loc[:source_locality_stack]) if loc[:source_locality_stack]
533
+ end
534
+
535
+ case rel[:source_locality]
536
+ when Hash then rel[:source_locality].delete(:source_locality_stack)
537
+ when Array then rel[:source_locality].delete_if { |loc| loc[:source_locality_stack] }
538
+ end
539
+
540
+ rel[:source_locality_stack] = sls.map do |loc|
541
+ SourceLocalityStack.new source_locality: relation_source_locality_hash_to_bib(source_locality: loc)
542
+ end
543
+ end
544
+
545
+ # @param ret [Hash]
546
+ def series_hash_to_bib(ret) # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
547
+ ret[:series] &&= array(ret[:series]).map do |s|
548
+ s[:formattedref] && s[:formattedref] = formattedref(s[:formattedref])
549
+ s[:title] &&= title_collection(s[:title])
550
+ s[:place] &&= create_place(s[:place])
551
+ s[:abbreviation] &&= localizedstring(s[:abbreviation])
552
+ s[:from] &&= s[:from].to_s
553
+ s[:to] &&= s[:to].to_s
554
+ Bib::Series.new(**s)
555
+ end
556
+ end
557
+
558
+ #
559
+ # @param title [Hash, Strinbg, Array<Hash, String>]
560
+ #
561
+ # @return [Relaton::Bib::TitleCollection]
562
+ #
563
+ def title_collection(title)
564
+ array(title).map do |t|
565
+ if t.is_a?(Hash)
566
+ lang_scrip_array_to_string t
567
+ Bib::Title.new(**t)
568
+ elsif t.is_a?(String)
569
+ Bib::Title.new(content: t)
570
+ end
571
+ end
572
+ end
573
+
574
+ # @param ret [Hash]
575
+ def medium_hash_to_bib(ret)
576
+ ret[:medium] = Bib::Medium.new(**ret[:medium]) if ret[:medium]
577
+ end
578
+
579
+ # @param ret [Hash]
580
+ def classification_hash_to_bib(ret)
581
+ if ret[:classification]
582
+ ret[:classification] = array(ret[:classification]).map do |cls|
583
+ cls[:content] ||= cls.delete(:value)
584
+ Docidentifier.new(**cls)
585
+ end
586
+ end
587
+ end
588
+
589
+ # @param ret [Hash]
590
+ def validity_hash_to_bib(ret)
591
+ return unless ret[:validity]
592
+
593
+ b = parse_validity_time(ret[:validity], :begins)
594
+ e = parse_validity_time(ret[:validity], :ends)
595
+ r = parse_validity_time(ret[:validity], :revision)
596
+ ret[:validity] = Bib::Validity.new(begins: b, ends: e, revision: r)
597
+ end
598
+
599
+ def parse_validity_time(val, period)
600
+ t = val[period]&.to_s
601
+ return unless t
602
+
603
+ p = period == :ends ? -1 : 1
604
+ case t
605
+ when /^\d{4}$/
606
+ ::Date.new(t.to_i, p, p).to_datetime.to_time.utc
607
+ when /^(?<year>\d{4})-(?<month>\d{1,2})$/
608
+ ::Date.new($~[:year].to_i, $~[:month].to_i, p).to_datetime.to_time.utc
609
+ else
610
+ # Default to UTC when no timezone is specified
611
+ t += " UTC" unless t.match?(/[+-]\d{2}:?\d{2}\s*$|Z\s*$/i)
612
+ ::Time.parse t
613
+ end
614
+ end
615
+
616
+ # @param ret [Hash]
617
+ def editorialgroup_hash_to_bib(ret) # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity
618
+ eg = ret.dig(:ext, :editorialgroup) || ret[:editorialgroup]
619
+ return unless eg
620
+
621
+ ret[:ext]&.delete(:editorialgroup)
622
+ ret.delete(:editorialgroup)
623
+ ret[:contributor] ||= []
624
+ array(eg).each do |wg|
625
+ wg[:content] ||= wg.delete(:name)
626
+ ret[:contributor] << Bib::Contributor.new(
627
+ role: [Bib::Contributor::Role.new(
628
+ type: "author",
629
+ description: [Bib::LocalizedMarkedUpString.new(content: "committee")],
630
+ )],
631
+ organization: Bib::Organization.new(
632
+ name: wg[:prefix] ? [Bib::TypedLocalizedString.new(content: wg[:prefix])] : [],
633
+ subdivision: [Bib::Subdivision.new(
634
+ type: "technical-committee",
635
+ subtype: wg[:type],
636
+ name: [Bib::TypedLocalizedString.new(content: wg[:content])],
637
+ identifier: eg_identifiers(wg),
638
+ )],
639
+ abbreviation: wg[:prefix] ? Bib::LocalizedString.new(content: wg[:prefix]) : nil,
640
+ ),
641
+ )
642
+ end
643
+ end
644
+
645
+ def eg_identifiers(workgroup) # rubocop:disable Metrics/MethodLength
646
+ ids = []
647
+ if workgroup[:number]
648
+ ids << Bib::OrganizationType::Identifier.new(
649
+ content: workgroup[:number].to_s,
650
+ )
651
+ end
652
+ if workgroup[:identifier]
653
+ ids << Bib::OrganizationType::Identifier.new(
654
+ content: workgroup[:identifier],
655
+ )
656
+ end
657
+ ids
658
+ end
659
+
660
+ # @param ret [Hash]
661
+ def ics_hash_to_bib(ret)
662
+ ics = ret.dig(:ext, :ics) || ret[:ics] # @todo remove ret[:ics] in the future
663
+ return unless ics
664
+
665
+ ret[:ext][:ics] = array(ics).map { |item| Bib::ICS.new(**item) }
666
+ end
667
+
668
+ # @param ret [Hash]
669
+ def structuredidentifier_hash_to_bib(ret)
670
+ struct_id = ret.dig(:ext, :structuredidentifier) || ret[:structuredidentifier] # @todo remove ret[:structuredidentifier] in the future
671
+ return unless struct_id
672
+
673
+ sids = array(struct_id).map do |si|
674
+ si[:agency] = array si[:agency]
675
+ si[:klass] = si.delete(:class)
676
+ Bib::StructuredIdentifier.new(**si)
677
+ end
678
+ ret[:ext][:structuredidentifier] = sids
679
+ end
680
+
681
+ # @param ogj [Hash, Array, String]
682
+ # @return [Hash, Array, String]
683
+ def symbolize(obj)
684
+ case obj
685
+ when Hash
686
+ obj.reduce({}) do |memo, (k, v)|
687
+ memo[k.to_sym] = symbolize(v)
688
+ memo
689
+ end
690
+ when Array then obj.reduce([]) { |memo, v| memo << symbolize(v) }
691
+ else obj
692
+ end
693
+ end
694
+
695
+ # @param lst [Hash, Array<Relaton::Bib::LocalizedString>, String]
696
+ # @return [Relaton::Bib::LocalizedString]
697
+ def localizedstring(lst)
698
+ return unless lst
699
+
700
+ if lst.is_a?(Hash)
701
+ lang_scrip_array_to_string lst
702
+ Bib::LocalizedString.new(**lst)
703
+ else Bib::LocalizedString.new(content: lst)
704
+ end
705
+ end
706
+
707
+ def lang_scrip_array_to_string(lst)
708
+ lst[:language] = lst[:language][0] if lst[:language].is_a?(Array)
709
+ lst[:script] = lst[:script][0] if lst[:script].is_a?(Array)
710
+ end
711
+
712
+ def localized_marked_up_string(lst)
713
+ return unless lst
714
+
715
+ if lst.is_a?(Hash)
716
+ lang_scrip_array_to_string lst
717
+ LocalizedMarkedUpString.new(**lst)
718
+ else LocalizedMarkedUpString.new(content: lst)
719
+ end
720
+ end
721
+
722
+ # @param frf [Hash, String]
723
+ # @return [Relaton::Bib::Formattedref]
724
+ def formattedref(frf)
725
+ if frf.is_a?(Hash)
726
+ lang_scrip_array_to_string(frf)
727
+ Relaton::Bib::Formattedref.new(**frf)
728
+ else
729
+ Relaton::Bib::Formattedref.new(content: frf)
730
+ end
731
+ end
732
+
733
+ def doctype_hash_to_bib(ret)
734
+ doctype = ret.dig(:ext, :doctype) || ret[:doctype] # @todo remove ret[:doctype] in the future
735
+ return unless doctype
736
+
737
+ ret[:ext][:doctype] = create_doctype(doctype_args(doctype))
738
+ end
739
+
740
+ def doctype_args(args)
741
+ if args.is_a?(String)
742
+ { content: args }
743
+ else
744
+ args[:content] = args.delete(:type)
745
+ args
746
+ end
747
+ end
748
+
749
+ def create_doctype(args)
750
+ Bib::Doctype.new(**args)
751
+ end
752
+ end
753
+ end
754
+ end