metanorma 2.0.0 → 2.5.5

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 (68) hide show
  1. checksums.yaml +4 -4
  2. data/.envrc +1 -0
  3. data/.gitignore +3 -0
  4. data/.rubocop.yml +23 -2
  5. data/Gemfile +17 -0
  6. data/Gemfile.devel +10 -0
  7. data/README.adoc +1 -1
  8. data/lib/metanorma/collection/artifact_store.rb +142 -0
  9. data/lib/metanorma/collection/collection.rb +160 -122
  10. data/lib/metanorma/collection/config/bibdata.rb +3 -3
  11. data/lib/metanorma/collection/config/compile_options.rb +4 -5
  12. data/lib/metanorma/collection/config/config.rb +81 -83
  13. data/lib/metanorma/collection/config/converters.rb +92 -8
  14. data/lib/metanorma/collection/config/directive.rb +9 -3
  15. data/lib/metanorma/collection/config/doc_container.rb +28 -0
  16. data/lib/metanorma/collection/config/manifest.rb +59 -29
  17. data/lib/metanorma/collection/config/namespaces.rb +12 -0
  18. data/lib/metanorma/collection/document/document.rb +56 -15
  19. data/lib/metanorma/collection/filelookup/filelookup.rb +185 -77
  20. data/lib/metanorma/collection/filelookup/filelookup_sectionsplit.rb +99 -27
  21. data/lib/metanorma/collection/filelookup/utils.rb +52 -0
  22. data/lib/metanorma/collection/helpers.rb +82 -0
  23. data/lib/metanorma/collection/log.rb +24 -0
  24. data/lib/metanorma/collection/manifest/manifest.rb +45 -14
  25. data/lib/metanorma/collection/multilingual/multilingual.rb +660 -0
  26. data/lib/metanorma/collection/renderer/filelocation.rb +166 -0
  27. data/lib/metanorma/collection/renderer/fileparse.rb +135 -115
  28. data/lib/metanorma/collection/renderer/fileprocess.rb +185 -37
  29. data/lib/metanorma/collection/renderer/navigation.rb +21 -7
  30. data/lib/metanorma/collection/renderer/render_word.rb +15 -10
  31. data/lib/metanorma/collection/renderer/renderer.rb +236 -32
  32. data/lib/metanorma/collection/renderer/svg.rb +107 -0
  33. data/lib/metanorma/collection/renderer/utils.rb +110 -39
  34. data/lib/metanorma/collection/sectionsplit/collection.rb +110 -0
  35. data/lib/metanorma/collection/sectionsplit/sectionsplit.rb +198 -115
  36. data/lib/metanorma/collection/util/disambig_files.rb +4 -5
  37. data/lib/metanorma/collection/util/util.rb +155 -18
  38. data/lib/metanorma/collection/xrefprocess/xrefprocess.rb +47 -33
  39. data/lib/metanorma/compile/assets/icc-boilerplate.adoc +41 -0
  40. data/lib/metanorma/compile/compile.rb +181 -156
  41. data/lib/metanorma/compile/compile_options.rb +186 -79
  42. data/lib/metanorma/compile/extract.rb +75 -58
  43. data/lib/metanorma/compile/flavor.rb +28 -0
  44. data/lib/metanorma/compile/output_filename.rb +75 -0
  45. data/lib/metanorma/compile/output_filename_config.rb +27 -0
  46. data/lib/metanorma/compile/relaton_drop.rb +56 -0
  47. data/lib/metanorma/compile/render.rb +226 -0
  48. data/lib/metanorma/compile/validator.rb +26 -0
  49. data/lib/metanorma/compile/writeable.rb +12 -0
  50. data/lib/metanorma/util/fontist_helper.rb +35 -19
  51. data/lib/metanorma/version.rb +1 -1
  52. data/lib/metanorma.rb +1 -7
  53. data/metanorma.gemspec +15 -21
  54. data/plans/collection-rendering-architecture-review.md +516 -0
  55. metadata +65 -113
  56. data/.hound.yml +0 -5
  57. data/lib/metanorma/asciidoctor_extensions/glob_include_processor.rb +0 -22
  58. data/lib/metanorma/asciidoctor_extensions.rb +0 -1
  59. data/lib/metanorma/compile/compile_validate.rb +0 -68
  60. data/lib/metanorma/config/config.rb +0 -23
  61. data/lib/metanorma/input/asciidoc.rb +0 -107
  62. data/lib/metanorma/input/base.rb +0 -9
  63. data/lib/metanorma/input.rb +0 -7
  64. data/lib/metanorma/processor/processor.rb +0 -54
  65. data/lib/metanorma/processor.rb +0 -6
  66. data/lib/metanorma/registry/registry.rb +0 -63
  67. data/lib/metanorma/shale_monkeypatch.rb +0 -15
  68. data/lib/metanorma/util/util.rb +0 -45
@@ -0,0 +1,166 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Metanorma
4
+ class Collection
5
+ class Renderer
6
+ def file_compile_formats(filename, identifier, opts)
7
+ f = @files.get(identifier, :outputs)
8
+ format = opts[:extension_keys]
9
+ concatenate_presentation?({ format: }) and format << :presentation
10
+ format.each do |e|
11
+ e == :pdf and output_filename = opts[:pdffile]
12
+ file_compile_format(filename, identifier, e, f, output_filename)
13
+ end
14
+ @files.set(identifier, :outputs, f)
15
+ end
16
+
17
+ # Generate output filename with correct extension
18
+ def output_filename_with_extension(fname, format)
19
+ ext = @compile.processor.output_formats[format]
20
+ fname_base = File.basename(fname)
21
+ # ONLY replace .xml or .html extensions, NEVER remove section numbers
22
+ if /\.(xml|html)$/.match?(fname_base)
23
+ fname_base.sub(/\.(xml|html)$/, ".#{ext}")
24
+ else
25
+ "#{fname_base}.#{ext}"
26
+ end
27
+ end
28
+
29
+ # Move file to new output filename if specified. A member that
30
+ # failed to render has nothing to move: keep the original name
31
+ # registered so output verification reports it as missing, rather
32
+ # than crashing the whole collection on the mv (#586).
33
+ def handle_new_output_filename(output_fname, new_output_fname)
34
+ return output_fname unless new_output_fname
35
+
36
+ src = File.join(@outdir, output_fname)
37
+ File.exist?(src) or return output_fname
38
+ FileUtils.mv(src, File.join(@outdir, new_output_fname))
39
+ new_output_fname
40
+ end
41
+
42
+ # Apply custom filename pattern with substitutions
43
+ def apply_custom_filename_pattern(ident, custom_fname, output_fname,
44
+ format)
45
+ idx = @files.get(ident, :idx)
46
+ original_file = @files.get(ident, :ref)
47
+ basename = File.basename(original_file, ".*")
48
+ basename_legacy = File.basename(original_file)
49
+ custom_fname = @files.substitute_filename_pattern(
50
+ custom_fname, document_num: idx,
51
+ basename: basename, basename_legacy: basename_legacy
52
+ )
53
+ if File.dirname(custom_fname) == "."
54
+ output_fname
55
+ else
56
+ preserve_output_dir_structure(custom_fname, output_fname,
57
+ format, explicit_custom: true)
58
+ end
59
+ end
60
+
61
+ # Apply directory preservation for files with directory structure
62
+ def apply_directory_preservation(fname, output_fname, format)
63
+ fname_dir = File.dirname(fname)
64
+ output_fname = File.join(fname_dir, output_fname)
65
+ preserve_output_dir_structure(fname, output_fname, format)
66
+ end
67
+
68
+ # Compile single format for a file and update outputs hash
69
+ def file_compile_format(fname, ident, format, outputs, new_output_fname)
70
+ ext = @compile.processor.output_formats[format]
71
+ output_fname = output_filename_with_extension(fname, format)
72
+ output_fname = handle_new_output_filename(output_fname,
73
+ new_output_fname)
74
+ if !new_output_fname && (custom = @files.preserve_directory_structure?(ident))
75
+ output_fname = apply_custom_filename_pattern(ident, custom,
76
+ output_fname, format)
77
+ elsif !new_output_fname && File.dirname(fname) != "."
78
+ output_fname = apply_directory_preservation(fname, output_fname,
79
+ format)
80
+ end
81
+ should_skip = /html$/.match?(ext) && @files.get(ident, :sectionsplit)
82
+ should_skip or outputs[format] = File.join(@outdir, output_fname)
83
+ end
84
+
85
+ # Determine if file should be moved to subdirectory
86
+ def should_move_to_subdirectory?(ext, output_basename, explicit_custom)
87
+ if explicit_custom
88
+ # For explicit output-filename: move html, doc, pdf, presentation.xml
89
+ ext.end_with?("html") || ext == "doc" || ext == "pdf" ||
90
+ output_basename.end_with?(".presentation.xml")
91
+ else
92
+ # Default: only HTML and presentation files
93
+ ext.end_with?("html") ||
94
+ output_basename.end_with?(".html", ".presentation.xml")
95
+ end
96
+ end
97
+
98
+ # Move file from root to subdirectory
99
+ def move_file_to_subdirectory(fname_dir, output_basename)
100
+ output_with_dir = File.join(fname_dir, output_basename)
101
+ output_dest = File.join(@outdir, output_with_dir)
102
+ output_src = File.join(@outdir, output_basename)
103
+ return nil unless File.exist?(output_src) && output_src != output_dest
104
+
105
+ FileUtils.mkdir_p(File.dirname(output_dest))
106
+ FileUtils.mv(output_src, output_dest)
107
+ output_with_dir
108
+ end
109
+
110
+ # Handle case where destination already exists
111
+ def handle_existing_destination(output_with_dir, output_src)
112
+ output_dest = File.join(@outdir, output_with_dir)
113
+ return nil unless File.exist?(output_dest)
114
+ return output_with_dir unless File.exist?(output_src)
115
+
116
+ FileUtils.rm_f(output_src)
117
+ err_src = output_src.sub(/\.html$/, ".err.html")
118
+ FileUtils.rm_f(err_src) if File.exist?(err_src)
119
+ output_with_dir
120
+ end
121
+
122
+ # Generate custom basename with extension
123
+ def custom_basename_with_extension(fname, output_basename)
124
+ custom_basename = File.basename(fname, ".*")
125
+ ext = File.extname(output_basename)
126
+ "#{custom_basename}#{ext}"
127
+ end
128
+
129
+ # Preserve directory structure from input filename in output
130
+ def preserve_output_dir_structure(fname, output_fname, format = nil,
131
+ explicit_custom: false)
132
+ fname_dir = File.dirname(fname)
133
+ return output_fname if fname_dir == "."
134
+
135
+ output_basename = File.basename(output_fname)
136
+ ext = format ? @compile.processor.output_formats[format].to_s : ""
137
+ should_move = should_move_to_subdirectory?(ext, output_basename,
138
+ explicit_custom)
139
+ if should_move
140
+ result = move_file_to_subdirectory(fname_dir, output_basename)
141
+ return result if result
142
+
143
+ handle_existing_destination(File.join(fname_dir, output_basename),
144
+ File.join(@outdir,
145
+ output_basename)) || output_fname
146
+ elsif explicit_custom
147
+ custom_basename_with_extension(fname, output_basename)
148
+ else
149
+ output_basename
150
+ end
151
+ end
152
+
153
+ def copy_file_to_dest(identifier)
154
+ out = Pathname.new(@files.get(identifier, :out_path)).cleanpath
155
+ out.absolute? and
156
+ out = out.relative_path_from(File.expand_path(FileUtils.pwd))
157
+ dest = File.join(@outdir,
158
+ @disambig.source2dest_filename(out.to_s,
159
+ preserve_dirs: true))
160
+ FileUtils.mkdir_p(File.dirname(dest))
161
+ source = @files.get(identifier, :ref)
162
+ source != dest and FileUtils.cp_r source, dest, remove_destination: true
163
+ end
164
+ end
165
+ end
166
+ end
@@ -4,7 +4,7 @@ module Metanorma
4
4
  # Resolves references to other files in the collection. Three routines:
5
5
  # 1. Eref to a document that has been split into multiple documents
6
6
  # (sectionsplit) are resolved to direct eref to the split document
7
- # 2. Indirect erefs to a file anchor in an unknwon file in the collection
7
+ # 2. Indirect erefs to a file anchor in an unknown file in the collection
8
8
  # (bibitem[@type = 'internal'] ) are resolved to direct eref to the
9
9
  # containing document
10
10
  # 3. Direct erefs to other files in collection
@@ -14,21 +14,46 @@ module Metanorma
14
14
  # @param internal_refs [Hash{String=>Hash{String=>String}] schema name to
15
15
  # anchor to filename
16
16
  # @return [String] XML content
17
+ # Each pass below mutates the SAME Nokogiri tree in place; the inline
18
+ # comments state the post-condition each pass guarantees on exit. The
19
+ # contract between passes is "the tree is now in state X" -- there is no
20
+ # intermediate typed representation, so the ordering is load-bearing.
21
+ # `sso` truthy (sectionsplit_output) => `xml` is Presentation XML and the
22
+ # semantic-only passes are skipped; otherwise `xml` is Semantic XML.
23
+ # See the pipeline map in the collection architecture review plan.
17
24
  def update_xrefs(file, docid, internal_refs)
18
25
  xml, sso = update_xrefs_prep(file, docid)
19
- @nested || sso or
26
+ # post: repository docidentifiers supplied on bibitems naming a
27
+ # collection file; `sso` resolved (Presentation vs Semantic).
28
+ @nested || sso || @reinflate or
20
29
  Metanorma::Collection::XrefProcess::xref_process(xml, xml, nil, docid,
21
- @isodoc)
22
- @nested or update_indirect_refs_to_docs(xml, docid, internal_refs)
23
- @files.add_document_suffix(docid, xml)
24
- @nested or update_sectionsplit_refs_to_docs(xml, internal_refs)
25
- update_direct_refs_to_docs(xml, docid)
26
- hide_refs(xml)
27
- sso and eref2link(xml)
28
- svgmap_resolve(xml, docid)
30
+ @isodoc, sso)
31
+ # post (semantic only): intra-doc xref/eref turned into internal erefs;
32
+ # repo bibitems copied; indirect bibliography inserted.
33
+ @ncnames = {}
34
+ @nested or update_indirect_refs_to_docs(xml, docid, internal_refs, sso)
35
+ # post: bibitem[@type='internal'] anchors resolved to the containing
36
+ # collection document.
37
+ @reinflate or @files.add_document_suffix(docid, xml)
38
+ # post: every @id/@anchor namespaced with this doc's NCName suffix, so
39
+ # concatenated documents do not collide.
40
+ @nested or update_sectionsplit_refs_to_docs(xml, docid, internal_refs,
41
+ sso)
42
+ # post: erefs targeting a sectionsplit document point at the specific
43
+ # split section file that holds the anchor.
44
+ update_direct_refs_to_docs(xml, docid, sso)
45
+ # post: repo(current-metanorma-collection/x) bibitems replaced in situ
46
+ # with resolved bibdata + citation URI; their erefs become hyperlinks.
47
+ ::Metanorma::Collection::Util::hide_refs(xml)
48
+ # post: references containers with only hidden bibitems flagged hidden.
49
+ sso and eref2link(xml, sso)
50
+ @nested || @reinflate or svgmap_resolve(xml, docid, sso)
29
51
  xml.to_xml
30
52
  end
31
53
 
54
+ ## sso (sectionsplit_output) truthy => Presentation XML, else Semantic.
55
+ ## This is the single discriminator that decides which grammar `xml`
56
+ ## carries and therefore which passes in update_xrefs run.
32
57
  def update_xrefs_prep(file, docid)
33
58
  docxml = file.is_a?(String) ? Nokogiri::XML(file, &:huge) : file
34
59
  supply_repo_ids(docxml)
@@ -36,8 +61,9 @@ module Metanorma
36
61
  [docxml, sso]
37
62
  end
38
63
 
39
- def update_sectionsplit_refs_to_docs(docxml, internal_refs)
40
- Util::gather_citeases(docxml).each do |k, v|
64
+ def update_sectionsplit_refs_to_docs(docxml, docid, internal_refs,
65
+ presxml)
66
+ Util::gather_citeases(docxml, presxml).each do |k, v|
41
67
  (@files.get(k) && @files.get(k, :sectionsplit)) or next
42
68
  opts = { key: @files.get(k, :indirect_key),
43
69
  source_suffix: docxml.root["document_suffix"],
@@ -45,13 +71,15 @@ module Metanorma
45
71
  refs = v.each_with_object({}) do |eref, m|
46
72
  update_sectionsplit_eref_to_doc(eref, internal_refs, m, opts)
47
73
  end
48
- add_hidden_bibliography(docxml, refs)
74
+ add_hidden_bibliography(docxml, refs, docid)
49
75
  end
50
76
  end
51
77
 
78
+ ANCHOR_XPATH = "xmlns:locality[@type = 'anchor']/xmlns:referenceFrom"
79
+ .freeze
80
+
52
81
  def update_sectionsplit_eref_to_doc(eref, internal_refs, doclist, opts)
53
- a = eref.at(ns("./localityStack/locality[@type = 'anchor']/" \
54
- "referenceFrom")) or return
82
+ a = eref.at("./xmlns:localityStack/#{ANCHOR_XPATH}") or return
55
83
  doc = internal_refs[opts[:key]]["#{a.text}_#{opts[:target_suffix]}"]
56
84
  bibitemid = Metanorma::Utils::to_ncname("#{doc}_#{opts[:source_suffix]}")
57
85
  eref["bibitemid"] = bibitemid
@@ -59,21 +87,9 @@ module Metanorma
59
87
  doclist
60
88
  end
61
89
 
62
- def new_hidden_ref(xmldoc)
63
- ins = xmldoc.at(ns("bibliography")) or
64
- xmldoc.root << "<bibliography/>" and ins = xmldoc.at(ns("bibliography"))
65
- ins.at(ns("./referenced[@hidden = 'true']")) or
66
- ins.add_child("<references hidden='true' normative='false'/>").first
67
- end
68
-
69
- def eref2link(docxml)
70
- isodoc = IsoDoc::PresentationXMLConvert.new({})
71
- isodoc.bibitem_lookup(docxml)
72
- isodoc.eref2link(docxml)
73
- end
74
-
75
- BIBITEM_NOT_REPO_XPATH = "//bibitem[not(ancestor::bibitem)]" \
76
- "[not(./docidentifier[@type = 'repository'])]".freeze
90
+ BIBITEM_NOT_REPO_XPATH = <<~XPATH.strip
91
+ //bibitem[not(ancestor::bibitem)][not(ancestor::bibdata)][not(./docidentifier[@type = 'repository'])]
92
+ XPATH
77
93
 
78
94
  def supply_repo_ids(doc)
79
95
  doc.xpath(ns(BIBITEM_NOT_REPO_XPATH)).each do |b|
@@ -86,47 +102,39 @@ module Metanorma
86
102
  end
87
103
  end
88
104
 
89
- def svgmap_resolve(docxml, docid)
90
- ids = @files.get(docid, :ids)
91
- dir = File.join(@dirname, File.dirname(@files.get(docid, :rel_path)))
92
- docxml = datauri_encode(docxml, dir)
93
- isodoc = IsoDoc::PresentationXMLConvert.new({})
94
- isodoc.bibitem_lookup(docxml)
95
- docxml.xpath(ns("//svgmap//eref")).each do |e|
96
- svgmap_resolve1(e, isodoc, docxml, ids)
97
- end
98
- Vectory::SvgMapping.new(docxml, "").call
99
- docxml.xpath(ns("//svgmap")).each { |s| isodoc.svgmap_extract(s) }
100
- end
101
-
102
- def svgmap_resolve1(eref, isodoc, _docxml, ids)
103
- href = isodoc.eref_target(eref) or return
104
- href == "##{eref['bibitemid']}" ||
105
- (href =~ /^#/ && !ids[href.sub(/^#/, "")]) and return
106
- eref["target"] = href.strip
107
- eref.name = "link"
108
- eref.elements&.remove
109
- end
110
-
111
105
  # repo(current-metanorma-collection/ISO 17301-1:2016)
112
106
  # replaced by bibdata of "ISO 17301-1:2016" in situ as bibitem.
113
107
  # Any erefs to that bibitem id are replaced with relative URL
114
108
  # Preferably with anchor, and is a job to realise dynamic lookup
115
109
  # of localities.
116
- def update_direct_refs_to_docs(docxml, identifier)
117
- erefs, erefs1 = update_direct_refs_to_docs_prep(docxml)
118
- docxml.xpath(ns("//bibitem")).each do |b|
110
+ def update_direct_refs_to_docs(xml, identifier, presxml)
111
+ erefs, erefs_no_anchor, anchors, erefs1 =
112
+ update_direct_refs_to_docs_prep(xml, presxml)
113
+ x = xml.xpath(ns("//bibitem")) - xml.xpath(ns("//bibdata//bibitem"))
114
+ x.each do |b|
119
115
  docid = b.at(ns("./docidentifier[@type = 'repository']")) or next
120
116
  strip_unresolved_repo_erefs(identifier, docid, erefs1, b) or next
121
117
  update_bibitem(b, identifier)
122
118
  docid = docid_to_citeas(b) or next
123
- erefs[docid] and update_anchors(b, docid, erefs[docid])
119
+ erefs[docid] and
120
+ update_anchors(b, docid, erefs[docid], erefs_no_anchor[docid],
121
+ anchors[docid])
124
122
  end
125
123
  end
126
124
 
127
- def update_direct_refs_to_docs_prep(docxml)
128
- @ncnames = {}
129
- [Util::gather_citeases(docxml), Util::gather_bibitemids(docxml)]
125
+ # Hash(docid) of arrays
126
+ def update_direct_refs_to_docs_prep(docxml, presxml)
127
+ erefs = Util::gather_citeases(docxml, presxml)
128
+ no_anchor = erefs.keys.each_with_object({}) { |k, m| m[k] = [] }
129
+ anchors = erefs.keys.each_with_object({}) { |k, m| m[k] = [] }
130
+ erefs.each do |k, v|
131
+ v.each do |e|
132
+ if loc = e.at(".//#{ANCHOR_XPATH}") then anchors[k] << loc
133
+ else no_anchor[k] << e
134
+ end
135
+ end
136
+ end
137
+ [erefs, no_anchor, anchors, Util::gather_bibitemids(docxml, presxml)]
130
138
  end
131
139
 
132
140
  # strip erefs if they are repository erefs, but do not point to a document
@@ -135,8 +143,13 @@ module Metanorma
135
143
  # Do not do this if this is a sectionsplit collection or a nested manifest.
136
144
  # Return false if bibitem is not to be further processed
137
145
  def strip_unresolved_repo_erefs(_document_id, bib_docid, erefs, bibitem)
138
- %r{^current-metanorma-collection/(?!Missing:)}.match?(bib_docid.text) and
146
+ if %r{^current-metanorma-collection/(?!Missing:)}.match?(bib_docid.text)
147
+ # Isolated build: keep the cross-document ref as a stub (its bibitemid
148
+ # and repository docidentifier intact) for a later reinflation pass to
149
+ # relink, rather than resolving it now against an absent target.
150
+ @preserve_unresolved and return false
139
151
  return true
152
+ end
140
153
  @nested and return false
141
154
  erefs[bibitem["id"]]&.each { |x| x.parent and strip_eref(x) }
142
155
  false
@@ -144,90 +157,98 @@ module Metanorma
144
157
 
145
158
  # Resolve erefs to a container of ids in another doc,
146
159
  # to an anchor eref (direct link)
147
- def update_indirect_refs_to_docs(docxml, _docidentifier, internal_refs)
148
- bibitems, erefs = update_indirect_refs_to_docs_prep(docxml)
160
+ def update_indirect_refs_to_docs(docxml, _docid, internal_refs, presxml)
161
+ bib, erefs, doc_suffix, doc_type, f = update_indirect_refs_prep(docxml,
162
+ presxml)
149
163
  internal_refs.each do |schema, ids|
164
+ add_suffix = doc_suffix && doc_type && doc_type != schema
150
165
  ids.each do |id, file|
151
- k = indirect_ref_key(schema, id, docxml)
152
- update_indirect_refs_to_docs1(docxml, k, file, bibitems, erefs)
166
+ f[file] ||= { url: @files.url?(file),
167
+ parentid: @files.get(file) && @files.get(file,
168
+ :parentid) }
169
+ k = indirect_ref_key(schema, id, doc_suffix, add_suffix)
170
+ update_indirect_refs_to_docs1(f[file], k, file, bib, erefs)
153
171
  end
154
172
  end
155
173
  end
156
174
 
157
- def update_indirect_refs_to_docs_prep(docxml)
175
+ def update_indirect_refs_prep(docxml, presxml)
158
176
  @updated_anchors = {}
159
- [Util::gather_bibitems(docxml), Util::gather_bibitemids(docxml)]
177
+ @indirect_keys = {}
178
+ [Util::gather_bibitems(docxml), Util::gather_bibitemids(docxml, presxml),
179
+ docxml.root["document_suffix"], docxml.root["type"], {}]
160
180
  end
161
181
 
162
- def indirect_ref_key(schema, id, docxml)
182
+ def indirect_ref_key(schema, id, doc_suffix, add_suffix)
163
183
  /^#{schema}_/.match?(id) and return id
164
- ret = "#{schema}_#{id}"
165
- suffix = docxml.root["document_suffix"]
166
- (k = docxml.root["type"]) && k != schema && suffix and
167
- ret = "#{ret}_#{suffix}"
168
- ret
184
+ x = @indirect_keys.dig(schema, id) and return x
185
+ @indirect_keys[schema] ||= {}
186
+ @indirect_keys[schema][id] = if add_suffix
187
+ "#{schema}_#{id}_#{doc_suffix}"
188
+ else
189
+ "#{schema}_#{id}"
190
+ end
169
191
  end
170
192
 
171
- def update_indirect_refs_to_docs1(_docxml, key, file, bibitems, erefs)
193
+ def update_indirect_refs_to_docs1(filec, key, file, bibitems, erefs)
172
194
  erefs[key]&.each do |e|
173
195
  e["citeas"] = file
174
- update_indirect_refs_to_docs_anchor(e, file)
196
+ update_indirect_refs_to_docs_anchor(e, file, filec[:url],
197
+ filec[:parentid])
175
198
  end
176
199
  update_indirect_refs_to_docs_docid(bibitems[key], file)
177
200
  end
178
201
 
179
- def update_indirect_refs_to_docs_anchor(eref, file)
180
- a = eref.at(ns(".//locality[@type = 'anchor']/referenceFrom")) or return
181
- suffix = file
182
- @files.get(file) && p = @files.get(file, :parentid) and
183
- suffix = "#{p}_#{suffix}"
202
+ def update_indirect_refs_to_docs_anchor(eref, file, url, parentid)
203
+ a = eref.at(".//#{ANCHOR_XPATH}") or return
204
+ parentid and file = "#{parentid}_#{file}"
184
205
  existing = a.text
185
- anchor = existing
186
- @files.url?(file) or
187
- anchor = Metanorma::Utils::to_ncname("#{anchor}_#{suffix}")
206
+ anchor = if url then existing
207
+ else
208
+ @indirect_keys[existing] ||= {}
209
+ @indirect_keys[existing][file] ||= Metanorma::Utils::to_ncname("#{existing}_#{file}")
210
+ end
188
211
  @updated_anchors[existing] or a.children = anchor
189
212
  @updated_anchors[anchor] = true
190
213
  end
191
214
 
192
- def update_indirect_refs_to_docs_docid(bibitem, file)
193
- docid = bibitem&.at(ns("./docidentifier[@type = 'repository']")) or
194
- return
215
+ def update_indirect_refs_to_docs_docid(bib, file)
216
+ docid = bib&.at(ns("./docidentifier[@type = 'repository']")) or return
195
217
  docid.children = "current-metanorma-collection/#{file}"
196
218
  docid.previous =
197
219
  "<docidentifier type='metanorma-collection'>#{file}</docidentifier>"
198
220
  end
199
221
 
200
- # update crossrefences to other documents, to include
201
- # disambiguating document suffix on id
202
- def update_anchors(bib, docid, erefs)
203
- erefs.each do |e|
204
- if @files.get(docid) then update_anchor_loc(bib, e, docid)
205
- else
206
- msg = "<strong>** Unresolved reference to document #{docid} " \
207
- "from eref</strong>"
208
- e << msg
209
- strip_eref(e)
210
- @log&.add("Cross-References", e, msg)
211
- end
222
+ # bottleneck
223
+ def update_anchors(bib, docid, erefs, erefs_no_anchor, erefs_anchors)
224
+ @files.get(docid) or error_anchor(erefs, docid)
225
+ has_anchors, url, ncn_docid = update_anchors_prep(docid)
226
+ erefs_no_anchor.each do |e|
227
+ update_anchor_create_loc(bib, e, docid)
228
+ end
229
+ !url && has_anchors or return
230
+ erefs_anchors.each do |e|
231
+ update_anchors1(docid, ncn_docid, e)
232
+ end
233
+ end
234
+
235
+ def update_anchors1(docid, ncn_docid, anchor)
236
+ @concat_anchors[anchor.text] ||= "#{ncn_docid}_#{anchor.text}"
237
+ if @files.get(docid).dig(:anchors_lookup, @concat_anchors[anchor.text])
238
+ anchor.content = @concat_anchors[anchor.text]
212
239
  end
213
240
  end
214
241
 
215
- def update_anchor_loc(bib, eref, docid)
216
- loc = eref.at(".//xmlns:locality[@type = 'anchor']") or
217
- return update_anchor_create_loc(bib, eref, docid)
218
- ref = loc.at("./xmlns:referenceFrom") or return
219
- anchor = suffix_anchor(ref, docid)
220
- a = @files.get(docid, :anchors) or return
221
- a.inject([]) { |m, (_, x)| m + x.values }
222
- .include?(anchor) or return
223
- ref.content = anchor
242
+ def update_anchors_prep(docid)
243
+ @concat_anchors = {}
244
+ [@files.get(docid)&.key?(:anchors_lookup), @files.url?(docid),
245
+ Metanorma::Utils::to_ncname(docid)]
224
246
  end
225
247
 
226
- def suffix_anchor(ref, docid)
227
- @ncnames[docid] ||= Metanorma::Utils::to_ncname(docid)
228
- anchor = ref.text
229
- @files.url?(docid) or anchor = "#{@ncnames[docid]}_#{anchor}"
230
- anchor
248
+ # encode both prefix and suffix to NCName
249
+ def suffix_anchor_indirect(prefix, suffix)
250
+ k = "#{prefix}_#{suffix}"
251
+ @ncnames[k] ||= Metanorma::Utils::to_ncname(k)
231
252
  end
232
253
 
233
254
  # if there is a crossref to another document, with no anchor, retrieve the
@@ -237,10 +258,9 @@ module Metanorma
237
258
  type = ins.at(ns("./locality/@type"))&.text
238
259
  type = "clause" if type == "annex"
239
260
  ref = ins.at(ns("./locality/referenceFrom"))&.text
240
- anchor = @files.get(docid, :anchors).dig(type, ref) or return
241
- ins << "<locality type='anchor'><referenceFrom>#{anchor.sub(/^_/,
242
- '')}" \
243
- "</referenceFrom></locality>"
261
+ a = @files.get(docid, :anchors).dig(type, ref) or return
262
+ ins << "<locality type='anchor'><referenceFrom>#{a}" \
263
+ "</referenceFrom></locality>"
244
264
  end
245
265
  end
246
266
  end