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,305 @@
1
+ module Relaton
2
+ module Bib
3
+ module Converter
4
+ module BibXml
5
+ class ToRfcxml
6
+ def initialize(item, include_keywords: true)
7
+ @item = item
8
+ @include_keywords = include_keywords
9
+ end
10
+
11
+ def transform
12
+ model = ::Rfcxml::V3::Reference.new
13
+ model.anchor = @item.docnumber || derive_anchor
14
+ model.target = create_target
15
+ model.front = create_front
16
+ model.format = create_format
17
+ model
18
+ end
19
+
20
+ private
21
+
22
+ def derive_anchor
23
+ di = @item.docidentifier.detect(&:primary) || @item.docidentifier[0]
24
+ di&.content&.to_s&.gsub(" ", ".")
25
+ end
26
+
27
+ def create_target
28
+ target = @item.source.detect { |l| l.type.casecmp("src").zero? } ||
29
+ @item.source.detect { |l| l.type.casecmp("doi").zero? }
30
+ return unless target
31
+
32
+ target.content.to_s
33
+ end
34
+
35
+ def create_front # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
36
+ front = ::Rfcxml::V3::Front.new
37
+ front.title = Rfcxml::V3::Title.new(content: @item.title[0].content) if @item.title.any?
38
+ front.series_info = create_seriesinfo
39
+ front.author = create_authors
40
+ front.date = create_date
41
+ front.workgroup = create_workgroup
42
+ front.keyword = create_keyword if @include_keywords
43
+ front.abstract = create_abstract
44
+ front
45
+ end
46
+
47
+ def create_seriesinfo
48
+ docidentifier_to_seriesinfo + series_to_seriesinfo
49
+ end
50
+
51
+ def docidentifier_to_seriesinfo
52
+ @item.docidentifier.each_with_object([]) do |di, si|
53
+ if di.type == "DOI" && di.scope != "trademark"
54
+ si << Rfcxml::V3::SeriesInfo.new(name: di.type,
55
+ value: di.content)
56
+ end
57
+ end
58
+ end
59
+
60
+ def series_to_seriesinfo # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity
61
+ @item.series.select do |s|
62
+ s.title.reject { |t| t.content == "DOI" }.any?
63
+ end.uniq do |s|
64
+ s.title.find { |t| t.content != "DOI" }.content
65
+ end.each_with_object([]) do |s, si|
66
+ title = s.title.find { |t| t.content != "DOI" }
67
+ si << Rfcxml::V3::SeriesInfo.new(name: title.content,
68
+ value: s.number)
69
+ end
70
+ end
71
+
72
+ def create_authors # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
73
+ @item.contributor.reject { |c| committee_contributor?(c) }.map do |contrib|
74
+ role = "editor" if contrib.role.detect { |r| r.type == "editor" }
75
+ Rfcxml::V3::Author.new(
76
+ role: role,
77
+ initials: person_initials(contrib.person),
78
+ surname: person_surname(contrib.person),
79
+ fullname: person_fullname(contrib.person),
80
+ organization: create_organization(contrib),
81
+ address: create_address(contrib),
82
+ )
83
+ end
84
+ end
85
+
86
+ def person_fullname(person) # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
87
+ return unless person
88
+
89
+ if person.name.completename
90
+ person.name.completename.content
91
+ elsif person.name.forename.any?
92
+ parts = person.name.forename.map { |n| n.content || n.initial }
93
+ parts << person.name.surname.content if person.name.surname
94
+ parts.join(" ")
95
+ end
96
+ end
97
+
98
+ def person_initials(person) # rubocop:disable Metrics/AbcSize
99
+ return unless person
100
+
101
+ if person.name.formatted_initials
102
+ person.name.formatted_initials.content
103
+ elsif person.name.forename.any?
104
+ person.name.forename.map do |f|
105
+ "#{f.initial || f.content[0]}."
106
+ end.join " "
107
+ end
108
+ end
109
+
110
+ def person_surname(person)
111
+ return unless person
112
+
113
+ person.name.surname&.content
114
+ end
115
+
116
+ def create_organization(contrib) # rubocop:disable Metrics/PerceivedComplexity,Metrics/CyclomaticComplexity,Metrics/AbcSize
117
+ org = contrib_org(contrib)
118
+ return unless org
119
+
120
+ abbrev = org.abbreviation&.content
121
+ orgname = resolve_orgname(org, abbrev)
122
+ Rfcxml::V3::Organization.new(
123
+ content: orgname, abbrev: abbrev,
124
+ )
125
+ end
126
+
127
+ def contrib_org(contrib)
128
+ contrib.organization ||
129
+ (contrib.person && contrib.person.affiliation[0]&.organization)
130
+ end
131
+
132
+ def resolve_orgname(org, abbrev)
133
+ orgname = org.name&.first&.content
134
+ if ORGNAMES.key?(abbrev) then abbrev
135
+ else ORGNAMES.key(orgname) || orgname || abbrev
136
+ end
137
+ end
138
+
139
+ def create_address(contrib) # rubocop:disable Metrics/AbcSize
140
+ entity = contrib.person || contrib.organization
141
+ return unless entity.address.any? || entity.phone.any? ||
142
+ entity.email.any? || entity.uri.any?
143
+
144
+ Rfcxml::V3::Address.new(
145
+ postal: address_postal(entity),
146
+ phone: address_phone(entity),
147
+ email: address_email(entity),
148
+ uri: address_uri(entity),
149
+ )
150
+ end
151
+
152
+ def address_postal(entity)
153
+ args = address_postal_args(entity)
154
+ return unless args.values.any?(&:any?)
155
+
156
+ Rfcxml::V3::Postal.new(**args)
157
+ end
158
+
159
+ def address_postal_args(entity)
160
+ {
161
+ city: address_cities(entity),
162
+ code: address_postcodes(entity),
163
+ country: address_countries(entity),
164
+ region: address_states(entity),
165
+ street: address_streets(entity),
166
+ postal_line: address_postal_lines(entity),
167
+ }
168
+ end
169
+
170
+ def address_cities(entity)
171
+ entity.address.each_with_object([]) do |addr, cities|
172
+ next unless addr.city
173
+
174
+ cities << Rfcxml::V3::City.new(content: addr.city)
175
+ end
176
+ end
177
+
178
+ def address_postcodes(entity)
179
+ entity.address.each_with_object([]) do |addr, codes|
180
+ next unless addr.postcode
181
+
182
+ codes << Rfcxml::V3::Code.new(content: addr.postcode)
183
+ end
184
+ end
185
+
186
+ def address_countries(entity)
187
+ entity.address.each_with_object([]) do |addr, countries|
188
+ next unless addr.country
189
+
190
+ countries << Rfcxml::V3::Country.new(content: addr.country)
191
+ end
192
+ end
193
+
194
+ def address_states(entity)
195
+ entity.address.each_with_object([]) do |addr, states|
196
+ next unless addr.state
197
+
198
+ states << Rfcxml::V3::Region.new(content: addr.state)
199
+ end
200
+ end
201
+
202
+ def address_streets(entity)
203
+ entity.address.each_with_object([]) do |address, streets|
204
+ address.street.each do |street|
205
+ streets << Rfcxml::V3::Street.new(content: street)
206
+ end
207
+ end
208
+ end
209
+
210
+ def address_postal_lines(entity)
211
+ entity.address.each_with_object([]) do |addr, plines|
212
+ next unless addr.formatted_address
213
+
214
+ plines << Rfcxml::V3::PostalLine.new(
215
+ content: addr.formatted_address,
216
+ )
217
+ end
218
+ end
219
+
220
+ def address_phone(entity)
221
+ return unless entity.phone.any?
222
+
223
+ Rfcxml::V3::Phone.new(content: entity.phone[0].content)
224
+ end
225
+
226
+ def address_email(entity)
227
+ entity.email.each_with_object([]) do |email, emails|
228
+ emails << Rfcxml::V3::Email.new(content: email)
229
+ end
230
+ end
231
+
232
+ def address_uri(entity)
233
+ return unless entity.uri.any?
234
+
235
+ Rfcxml::V3::Uri.new(content: entity.uri[0].content)
236
+ end
237
+
238
+ def create_date # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity
239
+ pub_date = @item.date.detect { |d| d.type == "published" }
240
+ return unless pub_date
241
+
242
+ args = {}
243
+ [pub_date.at, pub_date.from, pub_date.to].compact.each do |d|
244
+ year, month, day = d.split("-")
245
+ args[:year] ||= year
246
+ args[:month] ||= ::Date::MONTHNAMES[month.to_i]
247
+ args[:day] ||= day
248
+ end
249
+ Rfcxml::V3::Date.new(**args)
250
+ end
251
+
252
+ def committee_contributor?(contrib)
253
+ contrib.role.any? do |r|
254
+ r.type == "author" &&
255
+ r.description.any? { |d| d.content == "committee" }
256
+ end
257
+ end
258
+
259
+ def create_workgroup
260
+ @item.contributor.each_with_object([]) do |contrib, wgs|
261
+ next unless committee_contributor?(contrib)
262
+
263
+ contrib.organization&.subdivision&.each do |sd|
264
+ sd.name.each do |n|
265
+ wgs << Rfcxml::V3::Workgroup.new(content: n.content)
266
+ end
267
+ end
268
+ end
269
+ end
270
+
271
+ def create_keyword
272
+ @item.keyword.inject([]) do |a, k|
273
+ if k.vocab
274
+ a + [Rfcxml::V3::Keyword.new(content: k.vocab.content)]
275
+ else
276
+ a + k.taxon.map { |t| Rfcxml::V3::Keyword.new(content: t.content) }
277
+ end
278
+ end
279
+ end
280
+
281
+ def create_abstract
282
+ return unless @item.abstract.any?
283
+
284
+ content = @item.abstract[0].content
285
+ Rfcxml::V3::Abstract.new(
286
+ t: [Rfcxml::V3::Text.new(content: content)],
287
+ )
288
+ end
289
+
290
+ FORMAT_TYPES = %w[TXT HTML PDF XML DOC].freeze
291
+
292
+ def create_format # rubocop:disable Metrics/AbcSize
293
+ @item.source.each_with_object([]) do |l, a|
294
+ next unless FORMAT_TYPES.any? { |ft| l.type.casecmp(ft).zero? }
295
+
296
+ a << Rfcxml::V3::Format.new(
297
+ type: l.type, target: l.content,
298
+ )
299
+ end
300
+ end
301
+ end
302
+ end
303
+ end
304
+ end
305
+ end
@@ -0,0 +1,52 @@
1
+ module Relaton
2
+ module Bib
3
+ module Converter
4
+ module BibXml
5
+ class ToRfcxmlReferencegroup
6
+ def initialize(item, include_keywords: true)
7
+ @item = item
8
+ @include_keywords = include_keywords
9
+ end
10
+
11
+ def transform
12
+ Rfcxml::V3::Referencegroup.new(
13
+ anchor: create_anchor,
14
+ target: create_target,
15
+ reference: build_references,
16
+ )
17
+ end
18
+
19
+ private
20
+
21
+ def build_references
22
+ @item.relation&.each_with_object([]) do |rel, refs|
23
+ next unless rel.type == "includes"
24
+
25
+ converter = ToRfcxml.new(
26
+ rel.bibitem, include_keywords: @include_keywords
27
+ )
28
+ refs << converter.transform
29
+ end
30
+ end
31
+
32
+ def create_anchor
33
+ docid = @item.docidentifier.detect(&:primary) ||
34
+ @item.docidentifier[0]
35
+ return unless docid
36
+
37
+ id = docid.content.to_s
38
+ id.sub(/^(RFC|BCP|FYI|STD) /, '\1').sub(/^\w+\./, "")
39
+ end
40
+
41
+ def create_target
42
+ target = @item.source.detect { |l| l.type.casecmp("src").zero? } ||
43
+ @item.source.detect { |l| l.type.casecmp("doi").zero? }
44
+ return unless target
45
+
46
+ target.content.to_s
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,51 @@
1
+ require_relative "bibxml/to_rfcxml"
2
+ require_relative "bibxml/to_rfcxml_referencegroup"
3
+ require_relative "bibxml/from_rfcxml"
4
+ require_relative "bibxml/from_rfcxml_referencegroup"
5
+
6
+ module Relaton
7
+ module Bib
8
+ module Converter
9
+ module BibXml
10
+ ORGNAMES = {
11
+ "IEEE" => "Institute of Electrical and Electronics Engineers",
12
+ "W3C" => "World Wide Web Consortium",
13
+ "3GPP" => "3rd Generation Partnership Project",
14
+ }.freeze
15
+
16
+ RFCPREFIXES = %w[RFC BCP FYI STD].freeze
17
+
18
+ # Forward: ItemData -> Rfcxml model
19
+ def self.from_item(item, include_keywords: true) # rubocop:disable Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
20
+ if bcp?(item)
21
+ ToRfcxmlReferencegroup.new(
22
+ item, include_keywords: include_keywords
23
+ ).transform
24
+ else
25
+ ToRfcxml.new(
26
+ item, include_keywords: include_keywords
27
+ ).transform
28
+ end
29
+ end
30
+
31
+ def self.bcp?(item) # rubocop:disable Metrics/CyclomaticComplexity
32
+ item.docnumber&.match(/^BCP/) ||
33
+ (item.docidentifier.detect(&:primary) ||
34
+ item.docidentifier[0])&.content&.to_s&.include?("BCP")
35
+ end
36
+ private_class_method :bcp?
37
+
38
+ # Reverse: XML string -> ItemData
39
+ def self.to_item(xml)
40
+ if xml.include?("<referencegroup") || xml.include?("<Referencegroup")
41
+ referencegroup = Rfcxml::V3::Referencegroup.from_xml(xml)
42
+ FromRfcxmlReferencegroup.new(referencegroup).transform
43
+ else
44
+ reference = Rfcxml::V3::Reference.from_xml(xml)
45
+ FromRfcxml.new(reference).transform
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end