metanorma-standoc 1.11.1 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (146) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/rake.yml +3 -31
  3. data/.gitignore +23 -0
  4. data/Gemfile +2 -0
  5. data/lib/asciidoctor/standoc/base.rb +2 -241
  6. data/lib/asciidoctor/standoc/blocks.rb +2 -238
  7. data/lib/asciidoctor/standoc/blocks_notes.rb +2 -100
  8. data/lib/asciidoctor/standoc/cleanup.rb +2 -207
  9. data/lib/asciidoctor/standoc/cleanup_amend.rb +2 -53
  10. data/lib/asciidoctor/standoc/cleanup_block.rb +2 -173
  11. data/lib/asciidoctor/standoc/cleanup_boilerplate.rb +2 -204
  12. data/lib/asciidoctor/standoc/cleanup_footnotes.rb +2 -108
  13. data/lib/asciidoctor/standoc/cleanup_image.rb +2 -69
  14. data/lib/asciidoctor/standoc/cleanup_inline.rb +2 -179
  15. data/lib/asciidoctor/standoc/cleanup_maths.rb +2 -221
  16. data/lib/asciidoctor/standoc/cleanup_ref.rb +2 -169
  17. data/lib/asciidoctor/standoc/cleanup_ref_dl.rb +2 -93
  18. data/lib/asciidoctor/standoc/cleanup_reqt.rb +2 -110
  19. data/lib/asciidoctor/standoc/cleanup_section.rb +2 -184
  20. data/lib/asciidoctor/standoc/cleanup_section_names.rb +2 -91
  21. data/lib/asciidoctor/standoc/cleanup_symbols.rb +2 -47
  22. data/lib/asciidoctor/standoc/cleanup_table.rb +2 -67
  23. data/lib/asciidoctor/standoc/cleanup_terms.rb +2 -113
  24. data/lib/asciidoctor/standoc/cleanup_terms_designations.rb +2 -161
  25. data/lib/asciidoctor/standoc/cleanup_text.rb +2 -95
  26. data/lib/asciidoctor/standoc/cleanup_toc.rb +3 -0
  27. data/lib/asciidoctor/standoc/cleanup_xref.rb +2 -106
  28. data/lib/asciidoctor/standoc/converter.rb +2 -123
  29. data/lib/asciidoctor/standoc/datamodel/attributes_table_preprocessor.rb +2 -56
  30. data/lib/asciidoctor/standoc/datamodel/diagram_preprocessor.rb +2 -102
  31. data/lib/asciidoctor/standoc/datamodel/plantuml_renderer.rb +3 -404
  32. data/lib/asciidoctor/standoc/deprecated.rb +5 -0
  33. data/lib/asciidoctor/standoc/front.rb +2 -219
  34. data/lib/asciidoctor/standoc/front_contributor.rb +2 -191
  35. data/lib/asciidoctor/standoc/inline.rb +2 -229
  36. data/lib/asciidoctor/standoc/lists.rb +2 -119
  37. data/lib/asciidoctor/standoc/macros.rb +2 -203
  38. data/lib/asciidoctor/standoc/macros_form.rb +2 -62
  39. data/lib/asciidoctor/standoc/macros_note.rb +2 -44
  40. data/lib/asciidoctor/standoc/macros_plantuml.rb +2 -112
  41. data/lib/asciidoctor/standoc/macros_terms.rb +2 -180
  42. data/lib/asciidoctor/standoc/ref.rb +2 -225
  43. data/lib/asciidoctor/standoc/ref_sect.rb +2 -143
  44. data/lib/asciidoctor/standoc/ref_utility.rb +2 -0
  45. data/lib/asciidoctor/standoc/render.rb +3 -0
  46. data/lib/asciidoctor/standoc/reqt.rb +2 -89
  47. data/lib/asciidoctor/standoc/section.rb +2 -190
  48. data/lib/asciidoctor/standoc/table.rb +2 -84
  49. data/lib/asciidoctor/standoc/term_lookup_cleanup.rb +2 -178
  50. data/lib/asciidoctor/standoc/terms.rb +2 -153
  51. data/lib/asciidoctor/standoc/utils.rb +2 -116
  52. data/lib/asciidoctor/standoc/validate.rb +2 -157
  53. data/lib/asciidoctor/standoc/validate_section.rb +2 -54
  54. data/lib/isodoc/html/htmlstyle.css +20 -11
  55. data/lib/isodoc/html/htmlstyle.scss +11 -11
  56. data/lib/metanorma/standoc/base.rb +149 -0
  57. data/lib/{asciidoctor → metanorma}/standoc/basicdoc.rng +0 -0
  58. data/lib/{asciidoctor → metanorma}/standoc/biblio.rng +0 -0
  59. data/lib/metanorma/standoc/blocks.rb +239 -0
  60. data/lib/metanorma/standoc/blocks_notes.rb +101 -0
  61. data/lib/metanorma/standoc/cleanup.rb +146 -0
  62. data/lib/metanorma/standoc/cleanup_amend.rb +54 -0
  63. data/lib/metanorma/standoc/cleanup_block.rb +173 -0
  64. data/lib/metanorma/standoc/cleanup_boilerplate.rb +213 -0
  65. data/lib/metanorma/standoc/cleanup_footnotes.rb +109 -0
  66. data/lib/metanorma/standoc/cleanup_image.rb +70 -0
  67. data/lib/metanorma/standoc/cleanup_inline.rb +190 -0
  68. data/lib/metanorma/standoc/cleanup_maths.rb +222 -0
  69. data/lib/metanorma/standoc/cleanup_ref.rb +170 -0
  70. data/lib/metanorma/standoc/cleanup_ref_dl.rb +104 -0
  71. data/lib/metanorma/standoc/cleanup_reqt.rb +111 -0
  72. data/lib/metanorma/standoc/cleanup_section.rb +212 -0
  73. data/lib/metanorma/standoc/cleanup_section_names.rb +92 -0
  74. data/lib/metanorma/standoc/cleanup_symbols.rb +48 -0
  75. data/lib/metanorma/standoc/cleanup_table.rb +68 -0
  76. data/lib/metanorma/standoc/cleanup_terms.rb +140 -0
  77. data/lib/metanorma/standoc/cleanup_terms_designations.rb +199 -0
  78. data/lib/metanorma/standoc/cleanup_text.rb +96 -0
  79. data/lib/metanorma/standoc/cleanup_toc.rb +98 -0
  80. data/lib/metanorma/standoc/cleanup_xref.rb +107 -0
  81. data/lib/metanorma/standoc/converter.rb +124 -0
  82. data/lib/metanorma/standoc/datamodel/attributes_table_preprocessor.rb +57 -0
  83. data/lib/metanorma/standoc/datamodel/diagram_preprocessor.rb +103 -0
  84. data/lib/metanorma/standoc/datamodel/plantuml_renderer.rb +409 -0
  85. data/lib/metanorma/standoc/front.rb +224 -0
  86. data/lib/metanorma/standoc/front_contributor.rb +192 -0
  87. data/lib/metanorma/standoc/inline.rb +232 -0
  88. data/lib/{asciidoctor → metanorma}/standoc/isodoc.rng +90 -18
  89. data/lib/metanorma/standoc/lists.rb +120 -0
  90. data/lib/metanorma/standoc/macros.rb +204 -0
  91. data/lib/metanorma/standoc/macros_form.rb +63 -0
  92. data/lib/metanorma/standoc/macros_note.rb +45 -0
  93. data/lib/metanorma/standoc/macros_plantuml.rb +113 -0
  94. data/lib/metanorma/standoc/macros_terms.rb +181 -0
  95. data/lib/metanorma/standoc/ref.rb +243 -0
  96. data/lib/metanorma/standoc/ref_sect.rb +153 -0
  97. data/lib/metanorma/standoc/ref_utility.rb +129 -0
  98. data/lib/metanorma/standoc/render.rb +115 -0
  99. data/lib/metanorma/standoc/reqt.rb +90 -0
  100. data/lib/{asciidoctor → metanorma}/standoc/reqt.rng +0 -0
  101. data/lib/metanorma/standoc/section.rb +209 -0
  102. data/lib/metanorma/standoc/table.rb +85 -0
  103. data/lib/metanorma/standoc/term_lookup_cleanup.rb +179 -0
  104. data/lib/metanorma/standoc/terms.rb +160 -0
  105. data/lib/metanorma/standoc/utils.rb +101 -0
  106. data/lib/metanorma/standoc/validate.rb +158 -0
  107. data/lib/metanorma/standoc/validate_section.rb +55 -0
  108. data/lib/metanorma/standoc/version.rb +1 -1
  109. data/lib/{asciidoctor → metanorma}/standoc/views/datamodel/model_representation.adoc.erb +0 -0
  110. data/lib/{asciidoctor → metanorma}/standoc/views/datamodel/plantuml_representation.adoc.erb +0 -0
  111. data/lib/metanorma-standoc.rb +1 -1
  112. data/metanorma-standoc.gemspec +4 -4
  113. data/spec/{asciidoctor → metanorma}/base_spec.rb +73 -8
  114. data/spec/{asciidoctor → metanorma}/blank_spec.rb +1 -1
  115. data/spec/{asciidoctor → metanorma}/blocks_spec.rb +49 -20
  116. data/spec/{asciidoctor → metanorma}/cleanup_blocks_spec.rb +25 -1
  117. data/spec/{asciidoctor → metanorma}/cleanup_sections_spec.rb +2 -2
  118. data/spec/{asciidoctor → metanorma}/cleanup_spec.rb +9 -9
  119. data/spec/{asciidoctor → metanorma}/cleanup_terms_spec.rb +528 -91
  120. data/spec/{asciidoctor → metanorma}/datamodel/attributes_table_preprocessor_spec.rb +22 -22
  121. data/spec/{asciidoctor → metanorma}/datamodel/diagram_preprocessor_spec.rb +17 -17
  122. data/spec/{asciidoctor → metanorma}/inline_spec.rb +175 -6
  123. data/spec/{asciidoctor → metanorma}/isobib_cache_spec.rb +5 -9
  124. data/spec/{asciidoctor → metanorma}/lists_spec.rb +1 -1
  125. data/spec/{asciidoctor → metanorma}/macros_json2text_spec.rb +0 -0
  126. data/spec/{asciidoctor → metanorma}/macros_plantuml_spec.rb +3 -3
  127. data/spec/{asciidoctor → metanorma}/macros_spec.rb +8 -8
  128. data/spec/{asciidoctor → metanorma}/macros_yaml2text_spec.rb +0 -0
  129. data/spec/metanorma/refs_dl_spec.rb +863 -0
  130. data/spec/{asciidoctor → metanorma}/refs_spec.rb +1277 -687
  131. data/spec/{asciidoctor → metanorma}/section_spec.rb +90 -3
  132. data/spec/{asciidoctor → metanorma}/table_spec.rb +1 -1
  133. data/spec/{asciidoctor → metanorma}/validate_spec.rb +2 -2
  134. data/spec/spec_helper.rb +0 -1
  135. data/spec/vcr_cassettes/dated_iso_ref_joint_iso_iec.yml +179 -179
  136. data/spec/vcr_cassettes/dated_iso_ref_joint_iso_iec1.yml +12 -12
  137. data/spec/vcr_cassettes/isobib_get_123.yml +13 -13
  138. data/spec/vcr_cassettes/isobib_get_123_1.yml +98 -98
  139. data/spec/vcr_cassettes/isobib_get_123_1_fr.yml +111 -111
  140. data/spec/vcr_cassettes/isobib_get_123_2001.yml +13 -13
  141. data/spec/vcr_cassettes/isobib_get_124.yml +14 -14
  142. data/spec/vcr_cassettes/rfcbib_get_rfc8341.yml +14 -14
  143. data/spec/vcr_cassettes/separates_iev_citations_by_top_level_clause.yml +46 -46
  144. metadata +89 -38
  145. data/lib/asciidoctor/standoc/ref_date_id.rb +0 -62
  146. data/spec/asciidoctor/refs_dl_spec.rb +0 -864
@@ -0,0 +1,179 @@
1
+ # frozen_string_literal: true.
2
+ require "metanorma/standoc/utils"
3
+
4
+
5
+ module Metanorma
6
+ module Standoc
7
+ # Intelligent term lookup xml modifier
8
+ class TermLookupCleanup
9
+ AUTOMATIC_GENERATED_ID_REGEXP = /\A_/.freeze
10
+ EXISTING_TERM_REGEXP = /\Aterm-/.freeze
11
+ EXISTING_SYMBOL_REGEXP = /\Asymbol-/.freeze
12
+
13
+ attr_reader :xmldoc, :termlookup, :log
14
+
15
+ def initialize(xmldoc, log)
16
+ @xmldoc = xmldoc
17
+ @log = log
18
+ @termlookup = { term: {}, symbol: {}, secondary2primary: {} }
19
+ @idhash = {}
20
+ end
21
+
22
+ def call
23
+ @idhash = populate_idhash
24
+ @termlookup = replace_automatic_generated_ids_terms
25
+ set_termxref_tags_target
26
+ concept_cleanup
27
+ related_cleanup
28
+ end
29
+
30
+ private
31
+
32
+ def concept_cleanup
33
+ xmldoc.xpath("//concept").each do |n|
34
+ n.delete("type")
35
+ refterm = n.at("./refterm") or next
36
+ p = @termlookup[:secondary2primary][refterm.text] and
37
+ refterm.children = p
38
+ end
39
+ end
40
+
41
+ def related_cleanup
42
+ xmldoc.xpath("//related").each do |n|
43
+ refterm = n.at("./refterm") or next
44
+ p = @termlookup[:secondary2primary][refterm.text] and
45
+ refterm.children = p
46
+ refterm.replace("<preferred><expression><name>#{refterm.children.to_xml}"\
47
+ "</name></expression></preferred>")
48
+ end
49
+ end
50
+
51
+ def populate_idhash
52
+ xmldoc.xpath("//*[@id]").each_with_object({}) do |n, mem|
53
+ next unless /^(term|symbol)-/.match?(n["id"])
54
+
55
+ mem[n["id"]] = true
56
+ end
57
+ end
58
+
59
+ def set_termxref_tags_target
60
+ xmldoc.xpath("//termxref").each do |node|
61
+ target = normalize_ref_id(node.text)
62
+ if termlookup[:term][target].nil? && termlookup[:symbol][target].nil?
63
+ remove_missing_ref(node, target)
64
+ next
65
+ end
66
+ x = node.at("../xrefrender") and modify_ref_node(x, target)
67
+ node.name = "refterm"
68
+ end
69
+ end
70
+
71
+ def remove_missing_ref(node, target)
72
+ if node.at("../concept[@type = 'symbol']")
73
+ remove_missing_ref_symbol(node, target)
74
+ else
75
+ remove_missing_ref_term(node, target)
76
+ end
77
+ end
78
+
79
+ def remove_missing_ref_term(node, target)
80
+ log.add("AsciiDoc Input", node,
81
+ %(Error: Term reference in `term[#{target}]` missing: \
82
+ "#{target}" is not defined in document))
83
+ node.name = "strong"
84
+ node&.at("../xrefrender")&.remove
85
+ display = node&.at("../renderterm")&.remove&.children
86
+ display = [] if display.nil? || display&.to_xml == node.text
87
+ d = display.empty? ? "" : ", display <tt>#{display.to_xml}</tt>"
88
+ node.children = "term <tt>#{node.text}</tt>#{d} "\
89
+ "not resolved via ID <tt>#{target}</tt>"
90
+ end
91
+
92
+ def remove_missing_ref_symbol(node, target)
93
+ log.add("AsciiDoc Input", node,
94
+ %(Error: Symbol reference in `symbol[#{target}]` missing: \
95
+ "#{target}" is not defined in document))
96
+ node.name = "strong"
97
+ node&.at("../xrefrender")&.remove
98
+ display = node&.at("../renderterm")&.remove&.children
99
+ display = [] if display.nil? || display&.to_xml == node.text
100
+ d = display.empty? ? "" : ", display <tt>#{display.to_xml}</tt>"
101
+ node.children = "symbol <tt>#{node.text}</tt>#{d} "\
102
+ "not resolved via ID <tt>#{target}</tt>"
103
+ end
104
+
105
+ def modify_ref_node(node, target)
106
+ node.name = "xref"
107
+ s = termlookup[:symbol][target]
108
+ t = termlookup[:term][target]
109
+ type = node.parent["type"]
110
+ if type == "term" || ((!type || node.parent.name == "related") && t)
111
+ node["target"] = t
112
+ elsif type == "symbol" ||
113
+ ((!type || node.parent.name == "related") && s)
114
+ node["target"] = s
115
+ end
116
+ end
117
+
118
+ def replace_automatic_generated_ids_terms
119
+ r = xmldoc.xpath("//term").each.with_object({}) do |n, res|
120
+ normalize_id_and_memorize(n, res, "./preferred//name",
121
+ "term")
122
+ end
123
+ s = xmldoc.xpath("//definitions//dt").each.with_object({}) do |n, res|
124
+ normalize_id_and_memorize(n, res, ".", "symbol")
125
+ end
126
+ { term: r, symbol: s, secondary2primary: pref_secondary2primary }
127
+ end
128
+
129
+ def pref_secondary2primary
130
+ xmldoc.xpath("//term").each.with_object({}) do |n, res|
131
+ n.xpath("./preferred//name").each_with_index do |p, i|
132
+ i.zero? and term = p.text
133
+ i.positive? and res[p.text] = term
134
+ end
135
+ end
136
+ end
137
+
138
+ def normalize_id_and_memorize(node, res_table, text_selector, prefix)
139
+ normalize_id_and_memorize_init(node, res_table, text_selector, prefix)
140
+ memorize_other_pref_terms(node, res_table, text_selector)
141
+ end
142
+
143
+ def normalize_id_and_memorize_init(node, res_table, text_selector, prefix)
144
+ term_text = normalize_ref_id(node.at(text_selector).text)
145
+ unless AUTOMATIC_GENERATED_ID_REGEXP.match(node["id"]).nil? &&
146
+ !node["id"].nil?
147
+ id = unique_text_id(term_text, prefix)
148
+ node["id"] = id
149
+ @idhash[id] = true
150
+ end
151
+ res_table[term_text] = node["id"]
152
+ end
153
+
154
+ def memorize_other_pref_terms(node, res_table, text_selector)
155
+ node.xpath(text_selector).each_with_index do |p, i|
156
+ next unless i.positive?
157
+
158
+ res_table[normalize_ref_id(p.text)] = node["id"]
159
+ end
160
+ end
161
+
162
+ def normalize_ref_id(text)
163
+ Metanorma::Utils::to_ncname(text.downcase.gsub(/[[:space:]]/, "-"))
164
+ end
165
+
166
+ def unique_text_id(text, prefix)
167
+ unless @idhash["#{prefix}-#{text}"]
168
+ return "#{prefix}-#{text}"
169
+ end
170
+
171
+ (1..Float::INFINITY).lazy.each do |index|
172
+ unless @idhash["#{prefix}-#{text}-#{index}"]
173
+ break("#{prefix}-#{text}-#{index}")
174
+ end
175
+ end
176
+ end
177
+ end
178
+ end
179
+ end
@@ -0,0 +1,160 @@
1
+ module Metanorma
2
+ module Standoc
3
+ module Section
4
+ def in_terms?
5
+ @term_def
6
+ end
7
+
8
+ def nonterm_symbols_parse(attrs, xml, node)
9
+ defs = @definitions
10
+ @definitions = false
11
+ clause_parse(attrs, xml, node)
12
+ @definitions = defs
13
+ end
14
+
15
+ def symbols_attrs(node, attr)
16
+ case sectiontype1(node)
17
+ when "symbols" then attr.merge(type: "symbols")
18
+ when "abbreviated terms", "abbreviations"
19
+ attr.merge(type: "abbreviated_terms")
20
+ else
21
+ attr
22
+ end
23
+ end
24
+
25
+ def symbols_parse(attr, xml, node)
26
+ node.role == "nonterm" and return nonterm_symbols_parse(attr, xml, node)
27
+ xml.definitions **attr_code(attr) do |xml_section|
28
+ xml_section.title { |t| t << node.title }
29
+ defs = @definitions
30
+ termdefs = @term_def
31
+ @definitions = true
32
+ @term_def = false
33
+ xml_section << node.content
34
+ @definitions = defs
35
+ @term_def = termdefs
36
+ end
37
+ end
38
+
39
+ def nonterm_term_def_subclause_parse(attrs, xml, node)
40
+ defs = @term_def
41
+ @term_def = false
42
+ clause_parse(attrs, xml, node)
43
+ @term_def = defs
44
+ end
45
+
46
+ def terms_boilerplate_parse(attrs, xml, node)
47
+ defs = @term_def
48
+ @term_def = false
49
+ clause_parse(attrs.merge(type: "boilerplate"), xml, node)
50
+ @term_def = defs
51
+ end
52
+
53
+ # subclause contains subclauses
54
+ def term_def_subclause_parse(attrs, xml, node)
55
+ node.role == "nonterm" and
56
+ return nonterm_term_def_subclause_parse(attrs, xml, node)
57
+ node.role == "boilerplate" and
58
+ return terms_boilerplate_parse(attrs, xml, node)
59
+ st = sectiontype(node, false)
60
+ return symbols_parse(attrs, xml, node) if @definitions
61
+
62
+ sub = node.find_by(context: :section) { |s| s.level == node.level + 1 }
63
+ sub.empty? || (return term_def_parse(attrs, xml, node, false))
64
+ st == "symbols and abbreviated terms" and
65
+ return symbols_parse(attrs, xml, node)
66
+ st == "terms and definitions" and return clause_parse(attrs, xml, node)
67
+ term_def_subclause_parse1(attrs, xml, node)
68
+ end
69
+
70
+ def term_def_subclause_parse1(attrs, xml, node)
71
+ xml.term **attr_code(attrs) do |xml_section|
72
+ term_designation(xml_section, node, "preferred", node.title)
73
+ xml_section << node.content
74
+ end
75
+ end
76
+
77
+ def term_def_parse(attrs, xml, node, _toplevel)
78
+ xml.terms **attr_code(attrs) do |section|
79
+ section.title { |t| t << node.title }
80
+ (s = node.attr("source")) && s.split(",").each do |s1|
81
+ section.termdocsource(nil, **attr_code(bibitemid: s1))
82
+ end
83
+ section << node.content
84
+ end
85
+ end
86
+
87
+ def term_designation(xml, _node, tag, text)
88
+ xml.send tag do |p|
89
+ p.expression do |e|
90
+ e.name { |name| name << text }
91
+ end
92
+ end
93
+ end
94
+
95
+ def term_source_attrs(_node, seen_xref)
96
+ { case: seen_xref.children[0]["case"],
97
+ droploc: seen_xref.children[0]["droploc"],
98
+ bibitemid: seen_xref.children[0]["target"],
99
+ format: seen_xref.children[0]["format"], type: "inline" }
100
+ end
101
+
102
+ def add_term_source(node, xml_t, seen_xref, match)
103
+ if seen_xref.children[0].name == "concept"
104
+ xml_t.origin { |o| o << seen_xref.children[0].to_xml }
105
+ else
106
+ attrs = term_source_attrs(node, seen_xref)
107
+ attrs.delete(:text)
108
+ xml_t.origin **attr_code(attrs) do |o|
109
+ o << seen_xref.children[0].children.to_xml
110
+ end
111
+ end
112
+ add_term_source_mod(xml_t, match)
113
+ end
114
+
115
+ def add_term_source_mod(xml_t, match)
116
+ match[:text] && xml_t.modification do |mod|
117
+ mod.p { |p| p << match[:text].sub(/^\s+/, "") }
118
+ end
119
+ end
120
+
121
+ TERM_REFERENCE_RE_STR = <<~REGEXP.freeze
122
+ ^(?<xref><(xref|concept)[^>]+>(.*?</(xref|concept)>)?)
123
+ (,\s(?<text>.*))?
124
+ $
125
+ REGEXP
126
+ TERM_REFERENCE_RE =
127
+ Regexp.new(TERM_REFERENCE_RE_STR.gsub(/\s/, "").gsub(/_/, "\\s"),
128
+ Regexp::IGNORECASE | Regexp::MULTILINE)
129
+
130
+ def extract_termsource_refs(text, node)
131
+ matched = TERM_REFERENCE_RE.match text
132
+ matched.nil? and @log.add("AsciiDoc Input", node,
133
+ "term reference not in expected format:"\
134
+ "#{text}")
135
+ matched
136
+ end
137
+
138
+ def termsource(node)
139
+ matched = extract_termsource_refs(node.content, node) || return
140
+ noko do |xml|
141
+ status = node.attr("status") ||
142
+ (matched[:text] ? "modified" : "identical")
143
+ attrs = { status: status, type: node.attr("type") || "authoritative" }
144
+ xml.termsource **attrs do |xml_t|
145
+ seen_xref = Nokogiri::XML.fragment(matched[:xref])
146
+ add_term_source(node, xml_t, seen_xref, matched)
147
+ end
148
+ end.join("\n")
149
+ end
150
+
151
+ def termdefinition(node)
152
+ noko do |xml|
153
+ xml.definition do |d|
154
+ d << node.content
155
+ end
156
+ end.join("\n")
157
+ end
158
+ end
159
+ end
160
+ end
@@ -0,0 +1,101 @@
1
+ require "date"
2
+ require "nokogiri"
3
+ require "htmlentities"
4
+ require "json"
5
+ require "pathname"
6
+ require "uuidtools"
7
+
8
+ module Metanorma
9
+ module Standoc
10
+ module Utils
11
+ def convert(node, transform = nil, opts = {})
12
+ transform ||= node.node_name
13
+ opts.empty? ? (send transform, node) : (send transform, node, opts)
14
+ end
15
+
16
+ def document_ns_attributes(_doc)
17
+ nil
18
+ end
19
+
20
+ NOKOHEAD = <<~HERE.freeze
21
+ <!DOCTYPE html SYSTEM
22
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
23
+ <html xmlns="http://www.w3.org/1999/xhtml">
24
+ <head> <title></title> <meta charset="UTF-8" /> </head>
25
+ <body> </body> </html>
26
+ HERE
27
+
28
+ # block for processing XML document fragments as XHTML,
29
+ # to allow for HTMLentities
30
+ # Unescape special chars used in Asciidoctor substitution processing
31
+ def noko(&block)
32
+ doc = ::Nokogiri::XML.parse(NOKOHEAD)
33
+ fragment = doc.fragment("")
34
+ ::Nokogiri::XML::Builder.with fragment, &block
35
+ fragment.to_xml(encoding: "US-ASCII", indent: 0).lines.map do |l|
36
+ l.gsub(/>\n$/, ">").gsub(/\s*\n$/m, " ").gsub("&#150;", "\u0096")
37
+ .gsub("&#151;", "\u0097").gsub("&#x96;", "\u0096")
38
+ .gsub("&#x97;", "\u0097")
39
+ end
40
+ end
41
+
42
+ def attr_code(attributes)
43
+ attributes.compact.transform_values do |v|
44
+ v.is_a?(String) ? HTMLEntities.new.decode(v) : v
45
+ end
46
+ end
47
+
48
+ # if the contents of node are blocks, output them to out;
49
+ # else, wrap them in <p>
50
+ def wrap_in_para(node, out)
51
+ if node.blocks? then out << node.content
52
+ else
53
+ out.p { |p| p << node.content }
54
+ end
55
+ end
56
+
57
+ SUBCLAUSE_XPATH = "//clause[not(parent::sections)]"\
58
+ "[not(ancestor::boilerplate)]".freeze
59
+
60
+ def isodoc(lang, script, i18nyaml = nil)
61
+ conv = html_converter(EmptyAttr.new)
62
+ i18n = conv.i18n_init(lang, script, i18nyaml)
63
+ conv.metadata_init(lang, script, i18n)
64
+ conv
65
+ end
66
+
67
+ def dl_to_attrs(elem, dlist, name)
68
+ e = dlist.at("./dt[text()='#{name}']") or return
69
+ val = e.at("./following::dd/p") || e.at("./following::dd") or return
70
+ elem[name] = val.text
71
+ end
72
+
73
+ def dl_to_elems(ins, elem, dlist, name)
74
+ a = elem.at("./#{name}[last()]")
75
+ ins = a if a
76
+ dlist.xpath("./dt[text()='#{name}']").each do |e|
77
+ v = e.at("./following::dd")
78
+ e = v.elements and e.size == 1 && e.first.name == "p" and v = e.first
79
+ v.name = name
80
+ ins.next = v
81
+ ins = ins.next
82
+ end
83
+ ins
84
+ end
85
+
86
+ def term_expr(elem)
87
+ "<expression><name>#{elem}</name></expression>"
88
+ end
89
+
90
+ class EmptyAttr
91
+ def attr(_any_attribute)
92
+ nil
93
+ end
94
+
95
+ def attributes
96
+ {}
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,158 @@
1
+ require "metanorma/standoc/utils"
2
+ require_relative "./validate_section"
3
+ require "nokogiri"
4
+ require "jing"
5
+ require "iev"
6
+
7
+ module Metanorma
8
+ module Standoc
9
+ module Validate
10
+ SOURCELOCALITY = "./origin//locality[@type = 'clause']/"\
11
+ "referenceFrom".freeze
12
+
13
+ def init_iev
14
+ return nil if @no_isobib
15
+ return @iev if @iev
16
+
17
+ @iev = Iev::Db.new(@iev_globalname, @iev_localname) unless @no_isobib
18
+ @iev
19
+ end
20
+
21
+ def iev_validate(xmldoc)
22
+ @iev = init_iev or return
23
+ xmldoc.xpath("//term").each do |t|
24
+ t.xpath(".//termsource").each do |src|
25
+ (/^IEC 60050-/.match(src&.at("./origin/@citeas")&.text) &&
26
+ loc = src.xpath(SOURCELOCALITY)&.text) or next
27
+ iev_validate1(t, loc, xmldoc)
28
+ end
29
+ end
30
+ end
31
+
32
+ def iev_validate1(term, loc, xmldoc)
33
+ iev = @iev.fetch(loc,
34
+ xmldoc&.at("//language")&.text || "en") or return
35
+ pref = term.xpath("./preferred//name").inject([]) do |m, x|
36
+ m << x&.text&.downcase
37
+ end
38
+ pref.include?(iev.downcase) or
39
+ @log.add("Bibliography", term, %(Term "#{pref[0]}" does not match ) +
40
+ %(IEV #{loc} "#{iev}"))
41
+ end
42
+
43
+ def content_validate(doc)
44
+ @fatalerror = []
45
+ xref_validate(doc)
46
+ section_validate(doc)
47
+ norm_ref_validate(doc)
48
+ repeat_id_validate(doc.root)
49
+ iev_validate(doc.root)
50
+ concept_validate(doc, "concept", "refterm")
51
+ concept_validate(doc, "related", "preferred//name")
52
+ @fatalerror.empty? or clean_abort(@fatalerror.join("\n"), doc.to_xml)
53
+ end
54
+
55
+ def norm_ref_validate(doc)
56
+ found = false
57
+ doc.xpath("//references[@normative = 'true']/bibitem").each do |b|
58
+ next unless docid = b.at("./docidentifier[@type = 'metanorma']")
59
+ next unless /^\[\d+\]$/.match?(docid.text)
60
+
61
+ @log.add("Bibliography", b,
62
+ "Numeric reference in normative references")
63
+ found = true
64
+ end
65
+ found and @fatalerror << "Numeric reference in normative references"
66
+ end
67
+
68
+ def concept_validate(doc, tag, refterm)
69
+ found = false
70
+ doc.xpath("//#{tag}/xref").each do |x|
71
+ next if doc.at("//term[@id = '#{x['target']}']")
72
+ next if doc.at("//definitions//dt[@id = '#{x['target']}']")
73
+
74
+ ref = x&.at("../#{refterm}")&.text
75
+ @log.add("Anchors", x,
76
+ "#{tag.capitalize} #{ref} is pointing to "\
77
+ "#{x['target']}, which is not a term or symbol")
78
+ found = true
79
+ end
80
+ found and
81
+ @fatalerror << "#{tag.capitalize} not cross-referencing term or symbol"
82
+ end
83
+
84
+ def repeat_id_validate1(ids, elem)
85
+ if ids[elem["id"]]
86
+ @log.add("Anchors", elem, "Anchor #{elem['id']} has already been "\
87
+ "used at line #{ids[elem['id']]}")
88
+ @fatalerror << "Multiple instances of same ID: #{elem['id']}"
89
+ else
90
+ ids[elem["id"]] = elem.line
91
+ end
92
+ ids
93
+ end
94
+
95
+ def repeat_id_validate(doc)
96
+ ids = {}
97
+ doc.xpath("//*[@id]").each do |x|
98
+ ids = repeat_id_validate1(ids, x)
99
+ end
100
+ end
101
+
102
+ def schema_validate(doc, schema)
103
+ Tempfile.open(["tmp", ".xml"], encoding: "UTF-8") do |f|
104
+ schema_validate1(f, doc, schema)
105
+ rescue Jing::Error => e
106
+ clean_abort("Jing failed with error: #{e}", doc.to_xml)
107
+ ensure
108
+ f.close!
109
+ end
110
+ end
111
+
112
+ def schema_validate1(file, doc, schema)
113
+ file.write(doc.to_xml)
114
+ file.close
115
+ errors = Jing.new(schema, encoding: "UTF-8").validate(file.path)
116
+ warn "Syntax Valid!" if errors.none?
117
+ errors.each do |e|
118
+ @log.add("Metanorma XML Syntax",
119
+ "XML Line #{'%06d' % e[:line]}:#{e[:column]}", e[:message])
120
+ end
121
+ end
122
+
123
+ # RelaxNG cannot cope well with wildcard attributes. So we strip
124
+ # any attributes from FormattedString instances (which can contain
125
+ # xs:any markup, and are signalled with @format) before validation.
126
+ def formattedstr_strip(doc)
127
+ doc.xpath("//*[@format] | //stem | //bibdata//description | "\
128
+ "//formattedref | //bibdata//note | //bibdata/abstract | "\
129
+ "//bibitem/abstract | //bibitem/note | //misc-container")
130
+ .each do |n|
131
+ n.elements.each do |e|
132
+ e.traverse do |e1|
133
+ e1.element? and e1.each { |k, _v| e1.delete(k) }
134
+ end
135
+ end
136
+ end
137
+ doc
138
+ end
139
+
140
+ # manually check for xref/@target, xref/@to integrity
141
+ def xref_validate(doc)
142
+ ids = doc.xpath("//*/@id").each_with_object({}) { |x, m| m[x.text] = 1 }
143
+ doc.xpath("//xref/@target | //xref/@to").each do |x|
144
+ next if ids[x.text]
145
+
146
+ @log.add("Anchors", x.parent,
147
+ "Crossreference target #{x.text} is undefined")
148
+ end
149
+ end
150
+
151
+ def validate(doc)
152
+ content_validate(doc)
153
+ schema_validate(formattedstr_strip(doc.dup),
154
+ File.join(File.dirname(__FILE__), "isodoc.rng"))
155
+ end
156
+ end
157
+ end
158
+ end
@@ -0,0 +1,55 @@
1
+ require "nokogiri"
2
+
3
+ module Metanorma
4
+ module Standoc
5
+ module Validate
6
+ def section_validate(doc)
7
+ sourcecode_style(doc.root)
8
+ hanging_para_style(doc.root)
9
+ asset_style(doc.root)
10
+ end
11
+
12
+ def sourcecode_style(root)
13
+ root.xpath("//sourcecode").each do |x|
14
+ callouts = x.elements.select { |e| e.name == "callout" }
15
+ annotations = x.elements.select { |e| e.name == "annotation" }
16
+ if callouts.size != annotations.size
17
+ @log.add("AsciiDoc Input", x,
18
+ "mismatch of callouts and annotations")
19
+ end
20
+ end
21
+ end
22
+
23
+ def style_warning(node, msg, text = nil)
24
+ w = msg
25
+ w += ": #{text}" if text
26
+ @log.add("Metanorma XML Style Warning", node, w)
27
+ end
28
+
29
+ def asset_title_style(root)
30
+ root.xpath("//figure[image][not(name)]").each do |node|
31
+ style_warning(node, "Figure should have title", nil)
32
+ end
33
+ root.xpath("//table[not(name)]").each do |node|
34
+ style_warning(node, "Table should have title", nil)
35
+ end
36
+ end
37
+
38
+ def asset_style(root)
39
+ asset_title_style(root)
40
+ end
41
+
42
+ def hanging_para_style(root)
43
+ root.xpath("//clause | //annex | //foreword | //introduction | "\
44
+ "//acknowledgements").each do |c|
45
+ next unless c.at("./clause")
46
+ next if c.elements.reject do |n|
47
+ %w(clause title).include? n.name
48
+ end.empty?
49
+
50
+ style_warning(c, "Hanging paragraph in clause")
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -19,6 +19,6 @@ module Metanorma
19
19
  end
20
20
 
21
21
  module Standoc
22
- VERSION = "1.11.1".freeze
22
+ VERSION = "2.0.0".freeze
23
23
  end
24
24
  end
@@ -1,6 +1,6 @@
1
1
  require "asciidoctor" unless defined? Asciidoctor::Converter
2
2
  require_relative "isodoc/pdf_convert"
3
- require_relative "asciidoctor/standoc/converter"
3
+ require_relative "metanorma/standoc/converter"
4
4
  require_relative "metanorma/standoc/version"
5
5
  require "asciidoctor/extensions"
6
6