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.
- checksums.yaml +4 -4
- data/.github/workflows/rake.yml +3 -31
- data/.gitignore +23 -0
- data/Gemfile +2 -0
- data/lib/asciidoctor/standoc/base.rb +2 -241
- data/lib/asciidoctor/standoc/blocks.rb +2 -238
- data/lib/asciidoctor/standoc/blocks_notes.rb +2 -100
- data/lib/asciidoctor/standoc/cleanup.rb +2 -207
- data/lib/asciidoctor/standoc/cleanup_amend.rb +2 -53
- data/lib/asciidoctor/standoc/cleanup_block.rb +2 -173
- data/lib/asciidoctor/standoc/cleanup_boilerplate.rb +2 -204
- data/lib/asciidoctor/standoc/cleanup_footnotes.rb +2 -108
- data/lib/asciidoctor/standoc/cleanup_image.rb +2 -69
- data/lib/asciidoctor/standoc/cleanup_inline.rb +2 -179
- data/lib/asciidoctor/standoc/cleanup_maths.rb +2 -221
- data/lib/asciidoctor/standoc/cleanup_ref.rb +2 -169
- data/lib/asciidoctor/standoc/cleanup_ref_dl.rb +2 -93
- data/lib/asciidoctor/standoc/cleanup_reqt.rb +2 -110
- data/lib/asciidoctor/standoc/cleanup_section.rb +2 -184
- data/lib/asciidoctor/standoc/cleanup_section_names.rb +2 -91
- data/lib/asciidoctor/standoc/cleanup_symbols.rb +2 -47
- data/lib/asciidoctor/standoc/cleanup_table.rb +2 -67
- data/lib/asciidoctor/standoc/cleanup_terms.rb +2 -113
- data/lib/asciidoctor/standoc/cleanup_terms_designations.rb +2 -161
- data/lib/asciidoctor/standoc/cleanup_text.rb +2 -95
- data/lib/asciidoctor/standoc/cleanup_toc.rb +3 -0
- data/lib/asciidoctor/standoc/cleanup_xref.rb +2 -106
- data/lib/asciidoctor/standoc/converter.rb +2 -123
- data/lib/asciidoctor/standoc/datamodel/attributes_table_preprocessor.rb +2 -56
- data/lib/asciidoctor/standoc/datamodel/diagram_preprocessor.rb +2 -102
- data/lib/asciidoctor/standoc/datamodel/plantuml_renderer.rb +3 -404
- data/lib/asciidoctor/standoc/deprecated.rb +5 -0
- data/lib/asciidoctor/standoc/front.rb +2 -219
- data/lib/asciidoctor/standoc/front_contributor.rb +2 -191
- data/lib/asciidoctor/standoc/inline.rb +2 -229
- data/lib/asciidoctor/standoc/lists.rb +2 -119
- data/lib/asciidoctor/standoc/macros.rb +2 -203
- data/lib/asciidoctor/standoc/macros_form.rb +2 -62
- data/lib/asciidoctor/standoc/macros_note.rb +2 -44
- data/lib/asciidoctor/standoc/macros_plantuml.rb +2 -112
- data/lib/asciidoctor/standoc/macros_terms.rb +2 -180
- data/lib/asciidoctor/standoc/ref.rb +2 -225
- data/lib/asciidoctor/standoc/ref_sect.rb +2 -143
- data/lib/asciidoctor/standoc/ref_utility.rb +2 -0
- data/lib/asciidoctor/standoc/render.rb +3 -0
- data/lib/asciidoctor/standoc/reqt.rb +2 -89
- data/lib/asciidoctor/standoc/section.rb +2 -190
- data/lib/asciidoctor/standoc/table.rb +2 -84
- data/lib/asciidoctor/standoc/term_lookup_cleanup.rb +2 -178
- data/lib/asciidoctor/standoc/terms.rb +2 -153
- data/lib/asciidoctor/standoc/utils.rb +2 -116
- data/lib/asciidoctor/standoc/validate.rb +2 -157
- data/lib/asciidoctor/standoc/validate_section.rb +2 -54
- data/lib/isodoc/html/htmlstyle.css +20 -11
- data/lib/isodoc/html/htmlstyle.scss +11 -11
- data/lib/metanorma/standoc/base.rb +149 -0
- data/lib/{asciidoctor → metanorma}/standoc/basicdoc.rng +0 -0
- data/lib/{asciidoctor → metanorma}/standoc/biblio.rng +0 -0
- data/lib/metanorma/standoc/blocks.rb +239 -0
- data/lib/metanorma/standoc/blocks_notes.rb +101 -0
- data/lib/metanorma/standoc/cleanup.rb +146 -0
- data/lib/metanorma/standoc/cleanup_amend.rb +54 -0
- data/lib/metanorma/standoc/cleanup_block.rb +173 -0
- data/lib/metanorma/standoc/cleanup_boilerplate.rb +213 -0
- data/lib/metanorma/standoc/cleanup_footnotes.rb +109 -0
- data/lib/metanorma/standoc/cleanup_image.rb +70 -0
- data/lib/metanorma/standoc/cleanup_inline.rb +190 -0
- data/lib/metanorma/standoc/cleanup_maths.rb +222 -0
- data/lib/metanorma/standoc/cleanup_ref.rb +170 -0
- data/lib/metanorma/standoc/cleanup_ref_dl.rb +104 -0
- data/lib/metanorma/standoc/cleanup_reqt.rb +111 -0
- data/lib/metanorma/standoc/cleanup_section.rb +212 -0
- data/lib/metanorma/standoc/cleanup_section_names.rb +92 -0
- data/lib/metanorma/standoc/cleanup_symbols.rb +48 -0
- data/lib/metanorma/standoc/cleanup_table.rb +68 -0
- data/lib/metanorma/standoc/cleanup_terms.rb +140 -0
- data/lib/metanorma/standoc/cleanup_terms_designations.rb +199 -0
- data/lib/metanorma/standoc/cleanup_text.rb +96 -0
- data/lib/metanorma/standoc/cleanup_toc.rb +98 -0
- data/lib/metanorma/standoc/cleanup_xref.rb +107 -0
- data/lib/metanorma/standoc/converter.rb +124 -0
- data/lib/metanorma/standoc/datamodel/attributes_table_preprocessor.rb +57 -0
- data/lib/metanorma/standoc/datamodel/diagram_preprocessor.rb +103 -0
- data/lib/metanorma/standoc/datamodel/plantuml_renderer.rb +409 -0
- data/lib/metanorma/standoc/front.rb +224 -0
- data/lib/metanorma/standoc/front_contributor.rb +192 -0
- data/lib/metanorma/standoc/inline.rb +232 -0
- data/lib/{asciidoctor → metanorma}/standoc/isodoc.rng +90 -18
- data/lib/metanorma/standoc/lists.rb +120 -0
- data/lib/metanorma/standoc/macros.rb +204 -0
- data/lib/metanorma/standoc/macros_form.rb +63 -0
- data/lib/metanorma/standoc/macros_note.rb +45 -0
- data/lib/metanorma/standoc/macros_plantuml.rb +113 -0
- data/lib/metanorma/standoc/macros_terms.rb +181 -0
- data/lib/metanorma/standoc/ref.rb +243 -0
- data/lib/metanorma/standoc/ref_sect.rb +153 -0
- data/lib/metanorma/standoc/ref_utility.rb +129 -0
- data/lib/metanorma/standoc/render.rb +115 -0
- data/lib/metanorma/standoc/reqt.rb +90 -0
- data/lib/{asciidoctor → metanorma}/standoc/reqt.rng +0 -0
- data/lib/metanorma/standoc/section.rb +209 -0
- data/lib/metanorma/standoc/table.rb +85 -0
- data/lib/metanorma/standoc/term_lookup_cleanup.rb +179 -0
- data/lib/metanorma/standoc/terms.rb +160 -0
- data/lib/metanorma/standoc/utils.rb +101 -0
- data/lib/metanorma/standoc/validate.rb +158 -0
- data/lib/metanorma/standoc/validate_section.rb +55 -0
- data/lib/metanorma/standoc/version.rb +1 -1
- data/lib/{asciidoctor → metanorma}/standoc/views/datamodel/model_representation.adoc.erb +0 -0
- data/lib/{asciidoctor → metanorma}/standoc/views/datamodel/plantuml_representation.adoc.erb +0 -0
- data/lib/metanorma-standoc.rb +1 -1
- data/metanorma-standoc.gemspec +4 -4
- data/spec/{asciidoctor → metanorma}/base_spec.rb +73 -8
- data/spec/{asciidoctor → metanorma}/blank_spec.rb +1 -1
- data/spec/{asciidoctor → metanorma}/blocks_spec.rb +49 -20
- data/spec/{asciidoctor → metanorma}/cleanup_blocks_spec.rb +25 -1
- data/spec/{asciidoctor → metanorma}/cleanup_sections_spec.rb +2 -2
- data/spec/{asciidoctor → metanorma}/cleanup_spec.rb +9 -9
- data/spec/{asciidoctor → metanorma}/cleanup_terms_spec.rb +528 -91
- data/spec/{asciidoctor → metanorma}/datamodel/attributes_table_preprocessor_spec.rb +22 -22
- data/spec/{asciidoctor → metanorma}/datamodel/diagram_preprocessor_spec.rb +17 -17
- data/spec/{asciidoctor → metanorma}/inline_spec.rb +175 -6
- data/spec/{asciidoctor → metanorma}/isobib_cache_spec.rb +5 -9
- data/spec/{asciidoctor → metanorma}/lists_spec.rb +1 -1
- data/spec/{asciidoctor → metanorma}/macros_json2text_spec.rb +0 -0
- data/spec/{asciidoctor → metanorma}/macros_plantuml_spec.rb +3 -3
- data/spec/{asciidoctor → metanorma}/macros_spec.rb +8 -8
- data/spec/{asciidoctor → metanorma}/macros_yaml2text_spec.rb +0 -0
- data/spec/metanorma/refs_dl_spec.rb +863 -0
- data/spec/{asciidoctor → metanorma}/refs_spec.rb +1277 -687
- data/spec/{asciidoctor → metanorma}/section_spec.rb +90 -3
- data/spec/{asciidoctor → metanorma}/table_spec.rb +1 -1
- data/spec/{asciidoctor → metanorma}/validate_spec.rb +2 -2
- data/spec/spec_helper.rb +0 -1
- data/spec/vcr_cassettes/dated_iso_ref_joint_iso_iec.yml +179 -179
- data/spec/vcr_cassettes/dated_iso_ref_joint_iso_iec1.yml +12 -12
- data/spec/vcr_cassettes/isobib_get_123.yml +13 -13
- data/spec/vcr_cassettes/isobib_get_123_1.yml +98 -98
- data/spec/vcr_cassettes/isobib_get_123_1_fr.yml +111 -111
- data/spec/vcr_cassettes/isobib_get_123_2001.yml +13 -13
- data/spec/vcr_cassettes/isobib_get_124.yml +14 -14
- data/spec/vcr_cassettes/rfcbib_get_rfc8341.yml +14 -14
- data/spec/vcr_cassettes/separates_iev_citations_by_top_level_clause.yml +46 -46
- metadata +89 -38
- data/lib/asciidoctor/standoc/ref_date_id.rb +0 -62
- 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("–", "\u0096")
|
37
|
+
.gsub("—", "\u0097").gsub("–", "\u0096")
|
38
|
+
.gsub("—", "\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
|
File without changes
|
File without changes
|
data/lib/metanorma-standoc.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
require "asciidoctor" unless defined? Asciidoctor::Converter
|
2
2
|
require_relative "isodoc/pdf_convert"
|
3
|
-
require_relative "
|
3
|
+
require_relative "metanorma/standoc/converter"
|
4
4
|
require_relative "metanorma/standoc/version"
|
5
5
|
require "asciidoctor/extensions"
|
6
6
|
|