metanorma 1.7.6 → 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 (59) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +5 -1
  3. data/lib/metanorma/array_monkeypatch.rb +9 -0
  4. data/lib/metanorma/asciidoctor_extensions/glob_include_processor.rb +13 -11
  5. data/lib/metanorma/collection/collection.rb +225 -0
  6. data/lib/metanorma/collection/config/bibdata.rb +12 -0
  7. data/lib/metanorma/collection/config/compile_options.rb +13 -0
  8. data/lib/metanorma/collection/config/config.rb +163 -0
  9. data/lib/metanorma/collection/config/converters.rb +30 -0
  10. data/lib/metanorma/collection/config/directive.rb +10 -0
  11. data/lib/metanorma/collection/config/manifest.rb +88 -0
  12. data/lib/metanorma/collection/document/document.rb +133 -0
  13. data/lib/metanorma/collection/filelookup/filelookup.rb +250 -0
  14. data/lib/metanorma/collection/filelookup/filelookup_sectionsplit.rb +87 -0
  15. data/lib/metanorma/collection/manifest/manifest.rb +237 -0
  16. data/lib/metanorma/collection/renderer/fileparse.rb +247 -0
  17. data/lib/metanorma/collection/renderer/fileprocess.rb +173 -0
  18. data/lib/metanorma/collection/renderer/navigation.rb +133 -0
  19. data/lib/metanorma/collection/renderer/render_word.rb +133 -0
  20. data/lib/metanorma/collection/renderer/renderer.rb +157 -0
  21. data/lib/metanorma/collection/renderer/utils.rb +183 -0
  22. data/lib/metanorma/collection/sectionsplit/sectionsplit.rb +218 -0
  23. data/lib/metanorma/collection/util/disambig_files.rb +37 -0
  24. data/lib/metanorma/collection/util/util.rb +72 -0
  25. data/lib/metanorma/collection/xrefprocess/xrefprocess.rb +222 -0
  26. data/lib/metanorma/{compile.rb → compile/compile.rb} +17 -11
  27. data/lib/metanorma/{compile_options.rb → compile/compile_options.rb} +9 -5
  28. data/lib/metanorma/{compile_validate.rb → compile/compile_validate.rb} +1 -1
  29. data/lib/metanorma/{extract.rb → compile/extract.rb} +2 -2
  30. data/lib/metanorma/{config.rb → config/config.rb} +1 -1
  31. data/lib/metanorma/input/asciidoc.rb +3 -3
  32. data/lib/metanorma/input/base.rb +1 -5
  33. data/lib/metanorma/processor/processor.rb +54 -0
  34. data/lib/metanorma/processor.rb +1 -49
  35. data/lib/metanorma/{registry.rb → registry/registry.rb} +0 -1
  36. data/lib/metanorma/shale_monkeypatch.rb +15 -0
  37. data/lib/metanorma/util/fontist_helper.rb +130 -0
  38. data/lib/metanorma/util/util.rb +45 -0
  39. data/lib/metanorma/util/worker_pool.rb +39 -0
  40. data/lib/metanorma/version.rb +1 -1
  41. data/lib/metanorma.rb +13 -8
  42. data/metanorma.gemspec +2 -1
  43. metadata +51 -26
  44. data/Gemfile.devel +0 -2
  45. data/lib/metanorma/collection.rb +0 -243
  46. data/lib/metanorma/collection_fileparse.rb +0 -254
  47. data/lib/metanorma/collection_fileprocess.rb +0 -157
  48. data/lib/metanorma/collection_manifest.rb +0 -139
  49. data/lib/metanorma/collection_render_utils.rb +0 -169
  50. data/lib/metanorma/collection_render_word.rb +0 -131
  51. data/lib/metanorma/collection_renderer.rb +0 -230
  52. data/lib/metanorma/document.rb +0 -133
  53. data/lib/metanorma/files_lookup.rb +0 -208
  54. data/lib/metanorma/files_lookup_sectionsplit.rb +0 -84
  55. data/lib/metanorma/fontist_utils.rb +0 -122
  56. data/lib/metanorma/sectionsplit.rb +0 -216
  57. data/lib/metanorma/sectionsplit_links.rb +0 -189
  58. data/lib/metanorma/util.rb +0 -127
  59. data/lib/metanorma/worker_pool.rb +0 -29
@@ -0,0 +1,133 @@
1
+ module Metanorma
2
+ class Collection
3
+ class Document
4
+ # @return [Strin]
5
+ attr_reader :file, :attachment, :bibitem, :index
6
+
7
+ # @param bibitem [RelatonBib::BibliographicItem]
8
+ def initialize(bibitem, file, options = {})
9
+ @bibitem = bibitem
10
+ @file = file
11
+ @attachment = options[:attachment]
12
+ @index = options[:index]
13
+ @index = true if @index.nil?
14
+ @raw = options[:raw]
15
+ end
16
+
17
+ class << self
18
+ # @param file [String] file path
19
+ # @param attachment [Bool] is an attachment
20
+ # @param identifier [String] is the identifier assigned the file
21
+ # in the collection file
22
+ # @param index [Bool] is indication on whether to index this file in coverpage
23
+ # @return [Metanorma::Document]
24
+ def parse_file(file, attachment, identifier = nil, index = true)
25
+ new(bibitem(file, attachment, identifier), file,
26
+ { attachment: attachment, index: index })
27
+ end
28
+
29
+ # #param xml [Nokogiri::XML::Document, Nokogiri::XML::Element]
30
+ # @return [Metanorma::Document]
31
+ def parse_xml(xml)
32
+ new from_xml(xml)
33
+ end
34
+
35
+ # raw XML file, can be used to put in entire file instead of just bibitem
36
+ def raw_file(filename)
37
+ doc = Nokogiri::XML(File.read(filename, encoding: "UTF-8"), &:huge)
38
+ new(doc, filename, raw: true)
39
+ end
40
+
41
+ def attachment_bibitem(identifier)
42
+ Nokogiri::XML <<~DOCUMENT
43
+ <bibdata><docidentifier>#{identifier}</docidentifier></bibdata>
44
+ DOCUMENT
45
+ end
46
+
47
+ private
48
+
49
+ def mn2relaton_parser(tag)
50
+ case tag.sub(/-standard/, "")
51
+ when "bipm" then RelatonBipm::XMLParser
52
+ when "bsi" then RelatonBsi::XMLParser
53
+ when "ietf" then RelatonIetf::XMLParser
54
+ when "iho" then RelatonIho::XMLParser
55
+ when "itu" then RelatonItu::XMLParser
56
+ when "iec" then RelatonIec::XMLParser
57
+ when "iso" then RelatonIsoBib::XMLParser
58
+ when "nist" then RelatonNist::XMLParser
59
+ when "ogc" then RelatonOgc::XMLParser
60
+ else RelatonBib::XMLParser
61
+ end
62
+ end
63
+
64
+ # #param xml [Nokogiri::XML::Document, Nokogiri::XML::Element]
65
+ # @return [RelatonBib::BibliographicItem,RelatonIso::IsoBibliographicItem]
66
+ def from_xml(xml)
67
+ b = xml.at("//xmlns:bibitem|//xmlns:bibdata")
68
+ r = mn2relaton_parser(xml.root.name)
69
+ r.from_xml(b.to_xml)
70
+ end
71
+
72
+ # @param file [String]
73
+ # @return [Symbol] file type
74
+ def format(file)
75
+ case file
76
+ when /\.xml$/ then :xml
77
+ when /.ya?ml$/ then :yaml
78
+ end
79
+ end
80
+
81
+ # @param file [String]
82
+ # @return [RelatonBib::BibliographicItem,
83
+ # RelatonIso::IsoBibliographicItem]
84
+ def bibitem(file, attachment, identifier)
85
+ if attachment then attachment_bibitem(identifier)
86
+ else
87
+ case format(file)
88
+ when :xml
89
+ from_xml (Nokogiri::XML(File.read(file, encoding: "UTF-8"),
90
+ &:huge))
91
+ when :yaml
92
+ yaml = File.read(file, encoding: "UTF-8")
93
+ Relaton::Cli::YAMLConvertor.convert_single_file(yaml)
94
+ end
95
+ end
96
+ end
97
+ end
98
+
99
+ # @param builder [Nokogiri::XML::Builder, nil]
100
+ # @return [Nokogiri::XML::Builder, String]
101
+ def to_xml(builder = nil)
102
+ if builder
103
+ render_xml builder
104
+ else
105
+ Nokogiri::XML::Builder.new do |b|
106
+ root = render_xml b
107
+ root["xmlns"] = "http://metanorma.org"
108
+ end.to_xml
109
+ end
110
+ end
111
+
112
+ # @return [String]
113
+ def type
114
+ first = @bibitem.docidentifier.first
115
+ @type ||= (first&.type&.downcase ||
116
+ first&.id&.match(/^[^\s]+/)&.to_s)&.downcase ||
117
+ "standoc"
118
+ end
119
+
120
+ private
121
+
122
+ def render_xml(builder)
123
+ if @raw
124
+ builder.parent.add_child(@bibitem.root)
125
+ else
126
+ builder.send("#{type}-standard") do |b|
127
+ b << @bibitem.to_xml(bibdata: true)
128
+ end
129
+ end
130
+ end
131
+ end
132
+ end
133
+ end
@@ -0,0 +1,250 @@
1
+ require "isodoc"
2
+ require "htmlentities"
3
+ require "metanorma-utils"
4
+ require_relative "filelookup_sectionsplit"
5
+
6
+ module Metanorma
7
+ class Collection
8
+ class FileLookup
9
+ attr_accessor :files_to_delete, :parent
10
+
11
+ # hash for each document in collection of document identifier to:
12
+ # document reference (fileref or id), type of document reference,
13
+ # and bibdata entry for that file
14
+ # @param path [String] path to collection
15
+ def initialize(path, parent)
16
+ @c = HTMLEntities.new
17
+ @files = {}
18
+ @parent = parent
19
+ @xml = parent.xml
20
+ @isodoc = parent.isodoc
21
+ @path = path
22
+ @compile = parent.compile
23
+ @documents = parent.documents
24
+ @files_to_delete = []
25
+ @disambig = Util::DisambigFiles.new
26
+ @manifest = parent.manifest
27
+ read_files(@manifest.entry)
28
+ end
29
+
30
+ def read_files(entries)
31
+ Array(entries).each do |e|
32
+ e.file and read_file(e)
33
+ read_files(e.entry)
34
+ end
35
+ end
36
+
37
+ def read_file(manifest)
38
+ i, k = read_file_idents(manifest)
39
+ entry = file_entry(manifest, k) or return
40
+ bibdata_process(entry, i)
41
+ bibitem_process(entry)
42
+ @files[key(i)] = entry
43
+ end
44
+
45
+ def read_file_idents(manifest)
46
+ id = manifest.identifier
47
+ sanitised_id = key(@isodoc.docid_prefix("", manifest.identifier.dup))
48
+ # if manifest.bibdata and # NO, DO NOT FISH FOR THE GENUINE IDENTIFIER IN BIBDATA
49
+ # d = manifest.bibdata.docidentifier.detect { |x| x.primary } ||
50
+ # manifest.bibdata.docidentifier.first
51
+ # k = d.id
52
+ # i = key(@isodoc.docid_prefix(d.type, d.id.dup))
53
+ # end
54
+ [id, sanitised_id]
55
+ end
56
+
57
+ def bibdata_process(entry, ident)
58
+ if entry[:attachment]
59
+ entry[:bibdata] =
60
+ Metanorma::Collection::Document.attachment_bibitem(ident).root
61
+ else
62
+ file, _filename = targetfile(entry, read: true)
63
+ xml = Nokogiri::XML(file, &:huge)
64
+ add_document_suffix(ident, xml)
65
+ entry.merge!(anchors: read_anchors(xml), ids: read_ids(xml),
66
+ bibdata: xml.at(ns("//bibdata")),
67
+ document_suffix: xml.root["document_suffix"])
68
+ end
69
+ end
70
+
71
+ def bibitem_process(entry)
72
+ entry[:bibitem] = entry[:bibdata].dup
73
+ entry[:bibitem].name = "bibitem"
74
+ entry[:bibitem]["hidden"] = "true"
75
+ entry[:bibitem].at("./*[local-name() = 'ext']")&.remove
76
+ end
77
+
78
+ # ref is the absolute source file address
79
+ # rel_path is the relative source file address, relative to the YAML locaton
80
+ # out_path is the destination file address, with any references outside
81
+ # the working directory (../../...) truncated, and based on relative path
82
+ # identifier is the id with only spaces, no nbsp
83
+ def file_entry(ref, identifier)
84
+ ref.file or return
85
+ abs = @documents[Util::key identifier].file
86
+ ret = if ref.file
87
+ { type: "fileref", ref: abs, rel_path: ref.file, url: ref.url,
88
+ out_path: output_file_path(ref) }
89
+ else { type: "id", ref: ref.id }
90
+ end
91
+ file_entry_copy(ref, ret)
92
+ ret.compact
93
+ end
94
+
95
+ # TODO make the output file location reflect source location universally,
96
+ # not just for attachments: no File.basename
97
+ def output_file_path(ref)
98
+ f = File.basename(ref.file)
99
+ ref.attachment and f = ref.file
100
+ @disambig.source2dest_filename(f)
101
+ end
102
+
103
+ def file_entry_copy(ref, ret)
104
+ %w(attachment sectionsplit index presentation-xml url
105
+ bare-after-first).each do |s|
106
+ ref.respond_to?(s.to_sym) and
107
+ ret[s.gsub("-", "").to_sym] = ref.send(s)
108
+ end
109
+ end
110
+
111
+ def add_document_suffix(identifier, doc)
112
+ document_suffix = Metanorma::Utils::to_ncname(identifier)
113
+ Metanorma::Utils::anchor_attributes.each do |(tag_name, attribute_name)|
114
+ Util::add_suffix_to_attributes(doc, document_suffix, tag_name,
115
+ attribute_name, @isodoc)
116
+ end
117
+ url_in_css_styles(doc, document_suffix)
118
+ doc.root["document_suffix"] ||= ""
119
+ doc.root["document_suffix"] += document_suffix
120
+ end
121
+
122
+ # update relative URLs, url(#...), in CSS in @style attrs (including SVG)
123
+ def url_in_css_styles(doc, document_suffix)
124
+ doc.xpath("//*[@style]").each do |s|
125
+ s["style"] = s["style"]
126
+ .gsub(%r{url\(#([^)]+)\)}, "url(#\\1_#{document_suffix})")
127
+ end
128
+ end
129
+
130
+ # return citation url for file
131
+ # @param doc [Boolean] I am a Metanorma document,
132
+ # so my URL should end with html or pdf or whatever
133
+ def url(ident, options)
134
+ data = get(ident)
135
+ data[:url] || targetfile(data, options)[1]
136
+ end
137
+
138
+ # are references to the file to be linked to a file in the collection,
139
+ # or externally? Determines whether file suffix anchors are to be used
140
+ def url?(ident)
141
+ data = get(ident) or return false
142
+ data[:url]
143
+ end
144
+
145
+ # return file contents + output filename for each file in the collection,
146
+ # given a docref entry
147
+ # @param data [Hash] docref entry
148
+ # @param read [Boolean] read the file in and return it
149
+ # @param doc [Boolean] I am a Metanorma document,
150
+ # so my URL should end with html or pdf or whatever
151
+ # @param relative [Boolean] Return output path,
152
+ # formed relative to YAML file, not input path, relative to calling function
153
+ # @return [Array<String, nil>]
154
+ def targetfile(data, options)
155
+ options = { read: false, doc: true, relative: false }.merge(options)
156
+ path = options[:relative] ? data[:rel_path] : data[:ref]
157
+ if data[:type] == "fileref"
158
+ ref_file path, data[:out_path], options[:read], options[:doc]
159
+ else
160
+ xml_file data[:id], options[:read]
161
+ end
162
+ end
163
+
164
+ def targetfile_id(ident, options)
165
+ targetfile(get(ident), options)
166
+ end
167
+
168
+ def ref_file(ref, out, read, doc)
169
+ file = File.read(ref, encoding: "utf-8") if read
170
+ filename = out.dup
171
+ filename.sub!(/\.xml$/, ".html") if doc
172
+ [file, filename]
173
+ end
174
+
175
+ def xml_file(id, read)
176
+ file = @xml.at(ns("//doc-container[@id = '#{id}']")).to_xml if read
177
+ filename = "#{id}.html"
178
+ [file, filename]
179
+ end
180
+
181
+ # map locality type and label (e.g. "clause" "1") to id = anchor for
182
+ # a document
183
+ # Note: will only key clauses, which have unambiguous reference label in
184
+ # locality. Notes, examples etc with containers are just plunked against
185
+ # UUIDs, so that their IDs can at least be registered to be tracked
186
+ # as existing.
187
+ def read_anchors(xml)
188
+ xrefs = @isodoc.xref_init(@lang, @script, @isodoc, @isodoc.i18n,
189
+ { locale: @locale })
190
+ xrefs.parse xml
191
+ xrefs.get.each_with_object({}) do |(k, v), ret|
192
+ read_anchors1(k, v, ret)
193
+ end
194
+ end
195
+
196
+ def read_anchors1(key, val, ret)
197
+ val[:type] ||= "clause"
198
+ ret[val[:type]] ||= {}
199
+ index = if val[:container] || val[:label].nil? || val[:label].empty?
200
+ UUIDTools::UUID.random_create.to_s
201
+ else val[:label]
202
+ end
203
+ ret[val[:type]][index] = key
204
+ ret[val[:type]][val[:value]] = key if val[:value]
205
+ end
206
+
207
+ # Also parse all ids in doc (including ones which won't be xref targets)
208
+ def read_ids(xml)
209
+ ret = {}
210
+ xml.traverse do |x|
211
+ x.text? and next
212
+ /^semantic__/.match?(x.name) and next
213
+ x["id"] and ret[x["id"]] = true
214
+ end
215
+ ret
216
+ end
217
+
218
+ def key(ident)
219
+ @c.decode(ident).gsub(/(\p{Zs})+/, " ").sub(/^metanorma-collection /,
220
+ "")
221
+ end
222
+
223
+ def keys
224
+ @files.keys
225
+ end
226
+
227
+ def get(ident, attr = nil)
228
+ if attr then @files[key(ident)][attr]
229
+ else @files[key(ident)]
230
+ end
231
+ end
232
+
233
+ def set(ident, attr, value)
234
+ @files[key(ident)][attr] = value
235
+ end
236
+
237
+ def each
238
+ @files.each
239
+ end
240
+
241
+ def each_with_index
242
+ @files.each_with_index
243
+ end
244
+
245
+ def ns(xpath)
246
+ @isodoc.ns(xpath)
247
+ end
248
+ end
249
+ end
250
+ end
@@ -0,0 +1,87 @@
1
+ require_relative "../sectionsplit/sectionsplit"
2
+
3
+ module Metanorma
4
+ class Collection
5
+ class FileLookup
6
+ def add_section_split
7
+ ret = @files.keys.each_with_object({}) do |k, m|
8
+ if @files[k][:sectionsplit] && !@files[k][:attachment]
9
+ process_section_split_instance(k, m)
10
+ cleanup_section_split_instance(k, m)
11
+ end
12
+ m[k] = @files[k]
13
+ end
14
+ @files = ret
15
+ end
16
+
17
+ def process_section_split_instance(key, manifest)
18
+ s, sectionsplit_manifest = sectionsplit(key)
19
+ s.each_with_index do |f1, i|
20
+ add_section_split_instance(f1, manifest, key, i)
21
+ end
22
+ manifest["#{key}:index.html"] =
23
+ add_section_split_cover(sectionsplit_manifest, key)
24
+ end
25
+
26
+ def cleanup_section_split_instance(key, manifest)
27
+ @files_to_delete << manifest["#{key}:index.html"][:ref]
28
+ # @files[key].delete(:ids).delete(:anchors)
29
+ @files[key][:indirect_key] = @sectionsplit.key
30
+ end
31
+
32
+ def add_section_split_cover(manifest, ident)
33
+ cover = @sectionsplit
34
+ .section_split_cover(manifest, @parent.dir_name_cleanse(ident),
35
+ one_doc_collection?)
36
+ @files[ident][:out_path] = cover
37
+ { attachment: true, index: false, out_path: cover,
38
+ ref: File.join(File.dirname(manifest.file), cover) }
39
+ end
40
+
41
+ def one_doc_collection?
42
+ return false
43
+ docs = 0
44
+ @files.each_value do |v|
45
+ v[:attachment] and next
46
+ v[:presentationxml] and next
47
+ docs += 1
48
+ end
49
+ docs > 1
50
+ end
51
+
52
+ def add_section_split_instance(file, manifest, key, idx)
53
+ presfile, newkey, xml =
54
+ add_section_split_instance_prep(file, key)
55
+ manifest[newkey] =
56
+ { parentid: key, presentationxml: true, type: "fileref",
57
+ rel_path: file[:url], out_path: File.basename(file[:url]),
58
+ anchors: read_anchors(xml), ids: read_ids(xml),
59
+ sectionsplit_output: true,
60
+ bibdata: @files[key][:bibdata], ref: presfile }
61
+ @files_to_delete << file[:url]
62
+ manifest[newkey][:bare] = true unless idx.zero?
63
+ end
64
+
65
+ def add_section_split_instance_prep(file, key)
66
+ presfile = File.join(File.dirname(@files[key][:ref]),
67
+ File.basename(file[:url]))
68
+ newkey = key("#{key.strip} #{file[:title]}")
69
+ xml = Nokogiri::XML(File.read(presfile), &:huge)
70
+ [presfile, newkey, xml]
71
+ end
72
+
73
+ def sectionsplit(ident)
74
+ file = @files[ident][:ref]
75
+ @sectionsplit = ::Metanorma::Collection::Sectionsplit
76
+ .new(input: file, base: @files[ident][:out_path], dir: File.dirname(file),
77
+ output: @files[ident][:out_path], compile_opts: @parent.compile_options,
78
+ fileslookup: self, ident: ident, isodoc: @isodoc)
79
+ coll = @sectionsplit.sectionsplit.sort_by { |f| f[:order] }
80
+ xml = Nokogiri::XML(File.read(file, encoding: "UTF-8"), &:huge)
81
+ [coll, @sectionsplit
82
+ .collection_manifest(File.basename(file), coll, xml, nil,
83
+ File.dirname(file))]
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,237 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../../util/util"
4
+
5
+ module Metanorma
6
+ class Collection
7
+ class Manifest
8
+ # @return [Metanorma::Collection]
9
+ attr_reader :collection, :config
10
+
11
+ # @param level [String]
12
+ # @param dir [String]
13
+ # @param title [String, nil]
14
+ # @param docref [Array<Hash{String=>String}>]
15
+ # @param manifest [Array<Metanorma::Collection::Manifest>]
16
+ def initialize(config, collection, dir)
17
+ @collection = collection
18
+ @dir = dir
19
+ @disambig = ::Metanorma::Collection::Util::DisambigFiles.new
20
+ @config = manifest_postprocess(config)
21
+ end
22
+
23
+ def manifest_postprocess(config)
24
+ manifest_bibdata(config)
25
+ manifest_expand_yaml(config, @dir)
26
+ manifest_compile_adoc(config)
27
+ manifest_filexist(config)
28
+ manifest_sectionsplit(config)
29
+ manifest_identifier(config)
30
+ config
31
+ end
32
+
33
+ def manifest_bibdata(config)
34
+ b = config.bibdata
35
+ @lang = b&.language&.first || "en"
36
+ @script = b&.script&.first || "Latn"
37
+ end
38
+
39
+ GUID = "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"
40
+
41
+ def manifest_identifier(config)
42
+ no_id = populate_id_from_doc(config)
43
+ config.identifier =
44
+ if no_id && config.file # make file name be id
45
+ @collection.class.resolve_identifier(File.basename(config.file))
46
+ else
47
+ @collection.class.resolve_identifier(config.identifier)
48
+ end
49
+ Array(config.entry).each do |f|
50
+ manifest_identifier(f)
51
+ end
52
+ end
53
+
54
+ def populate_id_from_doc(config)
55
+ no_id = /^#{GUID}$/o.match?(config.identifier)
56
+ # GUID assumed to be no identifier supplied
57
+ if no_id && /\.xml$/.match?(config.file) &&
58
+ (i = retrieve_id_from_doc(config.file))
59
+ config.identifier = i
60
+ no_id = false
61
+ end
62
+ no_id
63
+ end
64
+
65
+ def retrieve_id_from_doc(file)
66
+ x = Nokogiri::XML(File.read(File.join(@dir, file)), &:huge)
67
+ i = x.at("//xmlns:bibdata/xmlns:docidentifier[@primary = 'true']") ||
68
+ x.at("//xmlns:bibdata/xmlns:docidentifier")
69
+ i or return nil
70
+ @doctype ||= i["type"]&.downcase || "standoc"
71
+ load_isodoc
72
+ Util::key(@isodoc.docid_prefix(i["type"], i.text))
73
+ end
74
+
75
+ def load_isodoc
76
+ @isodoc and return
77
+ @collection.compile.load_flavor(@doctype)
78
+ @isodoc = Util::load_isodoc(@doctype)
79
+ @isodoc.i18n_init(@lang, @script, nil) # for @i18n.all_parts in docid
80
+ end
81
+
82
+ def manifest_sectionsplit(config)
83
+ if config.sectionsplit && !config.file
84
+ config.sectionsplit = nil
85
+ Array(config.entry).each do |e|
86
+ e.attachment and next
87
+ e.sectionsplit = true
88
+ end
89
+ end
90
+ Array(config.entry).each do |f|
91
+ manifest_sectionsplit(f)
92
+ end
93
+ end
94
+
95
+ def manifest_filexist(config)
96
+ if config.file
97
+ file = @collection.class.resolve_fileref(@dir, config.file)
98
+ @collection.class.check_file_existence(file)
99
+ config.file = Pathname.new(file).relative_path_from(Pathname.new(@dir))
100
+ end
101
+ Array(config.entry).each do |f|
102
+ manifest_filexist(f)
103
+ end
104
+ end
105
+
106
+ def manifest_expand_yaml(config, dir)
107
+ Array(config.entry).each do |e|
108
+ currdir = dir
109
+ /\.ya?ml$/.match?(e.file) and
110
+ currdir = manifest_expand_yaml_entry(e, dir)
111
+ manifest_expand_yaml(e, currdir)
112
+ end
113
+ config
114
+ end
115
+
116
+ def manifest_expand_yaml_entry(entry, dir)
117
+ f = @collection.class.resolve_fileref(dir, entry.file)
118
+ currdir = File.dirname(f)
119
+ @collection.class.check_file_existence(f)
120
+ entry.file = nil
121
+ entry.entry = ::Metanorma::Collection::Config::Config.from_yaml(File.read(f)).manifest
122
+ if currdir != dir
123
+ prefix = Pathname.new(currdir).relative_path_from(Pathname.new(dir))
124
+ update_filepaths(entry.entry, prefix.to_s)
125
+ end
126
+ currdir
127
+ end
128
+
129
+ def update_filepaths(entry, prefix)
130
+ entry.file && !(Pathname.new entry.file).absolute? and
131
+ entry.file = File.join(prefix, entry.file)
132
+ entry.entry.each do |f|
133
+ update_filepaths(f, prefix)
134
+ end
135
+ end
136
+
137
+ def manifest_compile_adoc(config)
138
+ if /\.adoc$/.match?(config.file)
139
+ file = @collection.class.resolve_fileref(@dir, config.file)
140
+ config.file = compile_adoc(file, config.file)
141
+ end
142
+ Array(config.entry).each do |f|
143
+ manifest_compile_adoc(f)
144
+ end
145
+ end
146
+
147
+ def compile_adoc(resolved_filename, rel_filename)
148
+ compile_adoc_file(resolved_filename)
149
+ set_adoc2xml(rel_filename)
150
+ end
151
+
152
+ # @param fileref [String]
153
+ def set_adoc2xml(fileref)
154
+ File.join(
155
+ File.dirname(fileref),
156
+ File.basename(fileref).gsub(/.adoc$/, ".xml"),
157
+ )
158
+ end
159
+
160
+ # param filepath [String]
161
+ # @raise [AdocFileNotFoundException]
162
+ def compile_adoc_file(file)
163
+ f = (Pathname.new file).absolute? ? file : File.join(@dir, file)
164
+ unless File.exist? f
165
+ raise AdocFileNotFoundException.new "#{f} not found!"
166
+ end
167
+
168
+ ::Metanorma::Util.log("[metanorma] Info: Compiling #{f}...", :info)
169
+ ::Metanorma::Compile.new
170
+ .compile(f, agree_to_terms: true, no_install_fonts: true,
171
+ extension_keys: [:xml])
172
+ ::Metanorma::Util.log("[metanorma] Info: Compiling #{f}...done!", :info)
173
+ end
174
+
175
+ def documents(mnf = @config)
176
+ Array(mnf.entry).each_with_object({}) do |dr, m|
177
+ if dr.file
178
+ m[Util::key dr.identifier] = documents_add(dr)
179
+ elsif dr.entry
180
+ m.merge! documents(dr)
181
+ end
182
+ m
183
+ end
184
+ end
185
+
186
+ def documents_add(docref)
187
+ ::Metanorma::Collection::Document.parse_file(
188
+ Util::rel_path_resolve(@dir, docref.file),
189
+ docref.attachment, docref.identifier, docref.index
190
+ )
191
+ end
192
+
193
+ def to_xml(builder = nil)
194
+ clean_manifest(@config)
195
+ if builder
196
+ builder.parent.add_child(@config.to_xml)
197
+ else
198
+ @config.to_xml
199
+ end
200
+ end
201
+
202
+ def clean_manifest_bibdata(mnf)
203
+ if mnf.file && !mnf.attachment && !mnf.sectionsplit && @collection &&
204
+ d = @collection.bibdatas[Util::key mnf.identifier]
205
+ mnf.bibdata = d.bibitem.dup
206
+ end
207
+ end
208
+
209
+ def clean_manifest_id(mnf)
210
+ @collection.directives.detect do |d|
211
+ d.key == "documents-inline"
212
+ end or return
213
+ id = @collection.documents.find_index do |k, _|
214
+ k == mnf.identifier
215
+ end
216
+ id and mnf.id = format("doc%<index>09d", index: id)
217
+ end
218
+
219
+ def clean_manifest(mnf)
220
+ clean_manifest_bibdata(mnf)
221
+ # mnf.file &&= @disambig.strip_root(mnf.file)
222
+ clean_manifest_id(mnf)
223
+ Array(mnf.entry).each { |e| clean_manifest(e) }
224
+ end
225
+
226
+ # @return [Array<Hash{String=>String}>]
227
+ def docrefs
228
+ @config.entry
229
+ end
230
+
231
+ def docref_by_id(docid)
232
+ @config.entry.detect { |k| k.identifier == docid } ||
233
+ @config.entry.detect { |k| /^#{k.identifier}/ =~ docid }
234
+ end
235
+ end
236
+ end
237
+ end