metanorma-standoc 3.0.6 → 3.0.8
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.
- checksums.yaml +4 -4
- data/lib/metanorma/standoc/anchor.rb +2 -2
- data/lib/metanorma/standoc/base.rb +1 -1
- data/lib/metanorma/standoc/basicdoc.rng +9 -5
- data/lib/metanorma/standoc/blocks.rb +7 -4
- data/lib/metanorma/standoc/blocks_image.rb +2 -6
- data/lib/metanorma/standoc/blocks_notes.rb +2 -6
- data/lib/metanorma/standoc/cleanup_amend.rb +6 -8
- data/lib/metanorma/standoc/cleanup_asciibib.rb +17 -13
- data/lib/metanorma/standoc/cleanup_bibdata.rb +19 -13
- data/lib/metanorma/standoc/cleanup_bibitem.rb +9 -6
- data/lib/metanorma/standoc/cleanup_block.rb +6 -6
- data/lib/metanorma/standoc/cleanup_boilerplate.rb +18 -7
- data/lib/metanorma/standoc/cleanup_footnotes.rb +2 -4
- data/lib/metanorma/standoc/cleanup_image.rb +3 -3
- data/lib/metanorma/standoc/cleanup_inline.rb +12 -38
- data/lib/metanorma/standoc/cleanup_review.rb +7 -5
- data/lib/metanorma/standoc/cleanup_section.rb +9 -4
- data/lib/metanorma/standoc/cleanup_section_names.rb +1 -0
- data/lib/metanorma/standoc/cleanup_table.rb +1 -2
- data/lib/metanorma/standoc/cleanup_terms.rb +1 -1
- data/lib/metanorma/standoc/cleanup_terms_designations.rb +1 -1
- data/lib/metanorma/standoc/cleanup_text.rb +9 -8
- data/lib/metanorma/standoc/cleanup_toc.rb +1 -1
- data/lib/metanorma/standoc/cleanup_xref.rb +1 -1
- data/lib/metanorma/standoc/converter.rb +1 -0
- data/lib/metanorma/standoc/front_organisation.rb +13 -4
- data/lib/metanorma/standoc/init.rb +15 -7
- data/lib/metanorma/standoc/inline.rb +10 -8
- data/lib/metanorma/standoc/isodoc.rng +147 -5
- data/lib/metanorma/standoc/localbib.rb +1 -2
- data/lib/metanorma/standoc/macros_form.rb +21 -3
- data/lib/metanorma/standoc/macros_inline.rb +1 -1
- data/lib/metanorma/standoc/macros_link.rb +4 -5
- data/lib/metanorma/standoc/ref.rb +2 -2
- data/lib/metanorma/standoc/ref_sect.rb +1 -1
- data/lib/metanorma/standoc/ref_utility.rb +4 -3
- data/lib/metanorma/standoc/section.rb +43 -85
- data/lib/metanorma/standoc/sectiontype.rb +76 -0
- data/lib/metanorma/standoc/table.rb +9 -13
- data/lib/metanorma/standoc/term_lookup_cleanup.rb +26 -9
- data/lib/metanorma/standoc/terms.rb +1 -1
- data/lib/metanorma/standoc/utils.rb +5 -1
- data/lib/metanorma/standoc/validate.rb +79 -13
- data/lib/metanorma/standoc/validate_schema.rb +2 -0
- data/lib/metanorma/standoc/validate_section.rb +5 -6
- data/lib/metanorma/standoc/validate_term.rb +8 -7
- data/lib/metanorma/standoc/version.rb +1 -1
- data/metanorma-standoc.gemspec +1 -1
- metadata +5 -4
@@ -32,7 +32,8 @@ module Metanorma
|
|
32
32
|
end
|
33
33
|
|
34
34
|
def use_my_anchor(ref, id, opt)
|
35
|
-
ref.parent.elements.last["
|
35
|
+
ref.parent.elements.last["anchor"] = id
|
36
|
+
add_id(ref.parent.elements.last)
|
36
37
|
a = opt[:hidden] and ref.parent.elements.last["hidden"] = a
|
37
38
|
a = opt[:dropid] and
|
38
39
|
ref.parent.elements.last["suppress_identifier"] = a
|
@@ -195,8 +196,8 @@ module Metanorma
|
|
195
196
|
|
196
197
|
def ref_attributes(match)
|
197
198
|
code = analyse_ref_code(match[:code])
|
198
|
-
|
199
|
-
|
199
|
+
{ anchor: match[:anchor], id: "_#{UUIDTools::UUID.random_create}",
|
200
|
+
type: "standard",
|
200
201
|
suppress_identifier: code[:dropid] || nil }
|
201
202
|
end
|
202
203
|
|
@@ -1,6 +1,7 @@
|
|
1
1
|
require "uri" if /^2\./.match?(RUBY_VERSION)
|
2
2
|
require_relative "ref_sect"
|
3
3
|
require_relative "terms"
|
4
|
+
require_relative "sectiontype"
|
4
5
|
|
5
6
|
module Metanorma
|
6
7
|
module Standoc
|
@@ -9,85 +10,13 @@ module Metanorma
|
|
9
10
|
@term_def = false
|
10
11
|
@norm_ref = false
|
11
12
|
|
12
|
-
def sectiontype1(node)
|
13
|
-
node.attr("style") == "abstract" and return "abstract"
|
14
|
-
node.attr("heading")&.downcase ||
|
15
|
-
node.title
|
16
|
-
.gsub(%r{<index>.*?</index>}m, "")
|
17
|
-
.gsub(%r{<fn[^<>]*>.*?</fn>}m, "")
|
18
|
-
.gsub(/<[^<>]+>/, "")
|
19
|
-
.strip.downcase.sub(/\.$/, "")
|
20
|
-
end
|
21
|
-
|
22
|
-
def sectiontype(node, level = true)
|
23
|
-
ret = sectiontype1(node)
|
24
|
-
ret1 = preface_main_filter(sectiontype_streamline(ret), node)
|
25
|
-
ret1 == "symbols and abbreviated terms" and return ret1
|
26
|
-
!level || node.level == 1 || node.attr("heading") or return nil
|
27
|
-
!node.attr("heading") && @seen_headers.include?(ret) and return nil
|
28
|
-
@seen_headers << ret unless ret1.nil?
|
29
|
-
@seen_headers_canonical << ret1 unless ret1.nil?
|
30
|
-
ret1
|
31
|
-
end
|
32
|
-
|
33
|
-
def sectiontype_streamline(ret)
|
34
|
-
case ret
|
35
|
-
when "terms and definitions",
|
36
|
-
"terms, definitions, symbols and abbreviated terms",
|
37
|
-
"terms, definitions, symbols and abbreviations",
|
38
|
-
"terms, definitions and symbols",
|
39
|
-
"terms, definitions and abbreviations",
|
40
|
-
"terms, definitions and abbreviated terms"
|
41
|
-
"terms and definitions"
|
42
|
-
when "symbols and abbreviated terms",
|
43
|
-
"symbols", "abbreviated terms", "abbreviations",
|
44
|
-
"symbols and abbreviations"
|
45
|
-
"symbols and abbreviated terms"
|
46
|
-
when "acknowledgements", "acknowledgments"
|
47
|
-
"acknowledgements"
|
48
|
-
else
|
49
|
-
ret
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
PREFACE_CLAUSE_NAMES =
|
54
|
-
%w(abstract foreword introduction metanorma-extension termdocsource
|
55
|
-
misc-container metanorma-extension acknowledgements).freeze
|
56
|
-
|
57
|
-
MAIN_CLAUSE_NAMES =
|
58
|
-
["normative references", "terms and definitions", "scope",
|
59
|
-
"symbols and abbreviated terms", "clause", "bibliography"].freeze
|
60
|
-
|
61
|
-
def role_style(node, value)
|
62
|
-
node.role == value || node.attr("style") == value
|
63
|
-
end
|
64
|
-
|
65
|
-
def start_main_section(ret, node)
|
66
|
-
role_style(node, "preface") and return
|
67
|
-
@preface = false if self.class::MAIN_CLAUSE_NAMES.include?(ret)
|
68
|
-
@preface = false if self.class::PREFACE_CLAUSE_NAMES
|
69
|
-
.intersection(@seen_headers_canonical + [ret]).empty?
|
70
|
-
end
|
71
|
-
|
72
|
-
def preface_main_filter(ret, node)
|
73
|
-
start_main_section(ret, node)
|
74
|
-
if @preface
|
75
|
-
self.class::MAIN_CLAUSE_NAMES.include?(ret) and return nil
|
76
|
-
else
|
77
|
-
self.class::PREFACE_CLAUSE_NAMES.include?(ret) and return nil
|
78
|
-
end
|
79
|
-
|
80
|
-
ret
|
81
|
-
end
|
82
|
-
|
83
13
|
def section_attributes(node)
|
84
|
-
ret =
|
85
|
-
{
|
86
|
-
unnumbered: node.option?("unnumbered") ? "true" : nil,
|
87
|
-
annex: role_style(node, "appendix") && node.level == 1 ? true : nil,
|
14
|
+
ret = id_unnum_attrs(node).merge(
|
15
|
+
{ annex: role_style(node, "appendix") && node.level == 1 ? true : nil,
|
88
16
|
colophon: role_style(node, "colophon") ? true : nil,
|
89
|
-
preface: role_style(node, "preface") ? true : nil }
|
90
|
-
|
17
|
+
preface: role_style(node, "preface") ? true : nil },
|
18
|
+
)
|
19
|
+
%w(language script branch-number type tag keeptitle
|
91
20
|
multilingual-rendering).each do |k|
|
92
21
|
a = node.attr(k) and ret[k.to_sym] = a
|
93
22
|
end
|
@@ -120,6 +49,8 @@ module Metanorma
|
|
120
49
|
symbols_parse(symbols_attrs(node, a), xml, node)
|
121
50
|
when "acknowledgements"
|
122
51
|
acknowledgements_parse(a, xml, node)
|
52
|
+
when "executivesummary"
|
53
|
+
executivesummary_parse(a, xml, node)
|
123
54
|
when "bibliography"
|
124
55
|
bibliography_parse(a, xml, node)
|
125
56
|
else
|
@@ -188,47 +119,74 @@ module Metanorma
|
|
188
119
|
clause_parse(attrs.merge(type: "scope"), xml, node)
|
189
120
|
end
|
190
121
|
|
191
|
-
def
|
122
|
+
def clause_attrs_preprocess(attrs, node)
|
192
123
|
attrs[:"inline-header"] = node.option? "inline-header"
|
193
124
|
attrs[:bibitem] = true if node.option? "bibitem"
|
194
125
|
attrs[:level] = node.attr("level")
|
195
126
|
set_obligation(attrs, node)
|
127
|
+
end
|
128
|
+
|
129
|
+
def clause_parse(attrs, xml, node)
|
130
|
+
clause_attrs_preprocess(attrs, node)
|
131
|
+
node.option?("appendix") && support_appendix?(node) and
|
132
|
+
return appendix_parse(attrs, xml, node)
|
196
133
|
xml.send :clause, **attr_code(attrs) do |xml_section|
|
197
134
|
xml_section.title { |n| n << node.title } unless node.title.nil?
|
198
135
|
xml_section << node.content
|
199
136
|
end
|
200
137
|
end
|
201
138
|
|
202
|
-
def
|
139
|
+
def annex_attrs_preprocess(attrs, node)
|
203
140
|
attrs[:"inline-header"] = node.option? "inline-header"
|
204
141
|
set_obligation(attrs, node)
|
142
|
+
end
|
143
|
+
|
144
|
+
def annex_parse(attrs, xml, node)
|
145
|
+
annex_attrs_preprocess(attrs, node)
|
205
146
|
xml.annex **attr_code(attrs) do |xml_section|
|
206
147
|
xml_section.title { |name| name << node.title }
|
207
148
|
xml_section << node.content
|
208
149
|
end
|
209
150
|
end
|
210
151
|
|
152
|
+
def support_appendix?(_node)
|
153
|
+
false
|
154
|
+
end
|
155
|
+
|
156
|
+
def appendix_parse(attrs, xml, node)
|
157
|
+
attrs[:"inline-header"] = node.option? "inline-header"
|
158
|
+
set_obligation(attrs, node)
|
159
|
+
xml.appendix **attr_code(attrs) do |xml_section|
|
160
|
+
xml_section.title { |name| name << node.title }
|
161
|
+
xml_section << node.content
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
211
165
|
def introduction_parse(attrs, xml, node)
|
212
166
|
xml.introduction **attr_code(attrs) do |xml_section|
|
213
167
|
xml_section.title { |t| t << @i18n.introduction }
|
214
|
-
|
215
|
-
xml_section << content
|
168
|
+
xml_section << node.content
|
216
169
|
end
|
217
170
|
end
|
218
171
|
|
219
172
|
def foreword_parse(attrs, xml, node)
|
220
173
|
xml.foreword **attr_code(attrs) do |xml_section|
|
221
174
|
xml_section.title { |t| t << node.title }
|
222
|
-
|
223
|
-
xml_section << content
|
175
|
+
xml_section << node.content
|
224
176
|
end
|
225
177
|
end
|
226
178
|
|
227
179
|
def acknowledgements_parse(attrs, xml, node)
|
228
180
|
xml.acknowledgements **attr_code(attrs) do |xml_section|
|
229
181
|
xml_section.title { |t| (t << node.title) || @i18n.acknowledgements }
|
230
|
-
|
231
|
-
|
182
|
+
xml_section << node.content
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
def executivesummary_parse(attrs, xml, node)
|
187
|
+
xml.executivesummary **attr_code(attrs) do |xml_section|
|
188
|
+
xml_section.title { |t| (t << node.title) || @i18n.executivesummary }
|
189
|
+
xml_section << node.content
|
232
190
|
end
|
233
191
|
end
|
234
192
|
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module Metanorma
|
2
|
+
module Standoc
|
3
|
+
module Section
|
4
|
+
def sectiontype1(node)
|
5
|
+
node.attr("style") == "abstract" and return "abstract"
|
6
|
+
node.attr("heading")&.downcase ||
|
7
|
+
node.title
|
8
|
+
.gsub(%r{<index>.*?</index>}m, "")
|
9
|
+
.gsub(%r{<fn[^<>]*>.*?</fn>}m, "")
|
10
|
+
.gsub(/<[^<>]+>/, "")
|
11
|
+
.strip.downcase.sub(/\.$/, "")
|
12
|
+
end
|
13
|
+
|
14
|
+
def sectiontype(node, level = true)
|
15
|
+
ret = sectiontype1(node)
|
16
|
+
ret1 = preface_main_filter(sectiontype_streamline(ret), node)
|
17
|
+
ret1 == "symbols and abbreviated terms" and return ret1
|
18
|
+
!level || node.level == 1 || node.attr("heading") or return nil
|
19
|
+
!node.attr("heading") && @seen_headers.include?(ret) and return nil
|
20
|
+
@seen_headers << ret unless ret1.nil?
|
21
|
+
@seen_headers_canonical << ret1 unless ret1.nil?
|
22
|
+
ret1
|
23
|
+
end
|
24
|
+
|
25
|
+
def sectiontype_streamline(ret)
|
26
|
+
case ret
|
27
|
+
when "terms and definitions",
|
28
|
+
"terms, definitions, symbols and abbreviated terms",
|
29
|
+
"terms, definitions, symbols and abbreviations",
|
30
|
+
"terms, definitions and symbols",
|
31
|
+
"terms, definitions and abbreviations",
|
32
|
+
"terms, definitions and abbreviated terms"
|
33
|
+
"terms and definitions"
|
34
|
+
when "symbols and abbreviated terms",
|
35
|
+
"symbols", "abbreviated terms", "abbreviations",
|
36
|
+
"symbols and abbreviations"
|
37
|
+
"symbols and abbreviated terms"
|
38
|
+
when "acknowledgements", "acknowledgments"
|
39
|
+
"acknowledgements"
|
40
|
+
when "executive summary", "executive-summary", "executive_summary"
|
41
|
+
"executivesummary"
|
42
|
+
else
|
43
|
+
ret
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
PREFACE_CLAUSE_NAMES =
|
48
|
+
%w(abstract foreword introduction metanorma-extension termdocsource
|
49
|
+
misc-container metanorma-extension acknowledgements executivesummary)
|
50
|
+
.freeze
|
51
|
+
|
52
|
+
MAIN_CLAUSE_NAMES =
|
53
|
+
["normative references", "terms and definitions", "scope",
|
54
|
+
"symbols and abbreviated terms", "clause", "bibliography"].freeze
|
55
|
+
|
56
|
+
def role_style(node, value)
|
57
|
+
node.role == value || node.attr("style") == value
|
58
|
+
end
|
59
|
+
|
60
|
+
def start_main_section(ret, node)
|
61
|
+
role_style(node, "preface") and return
|
62
|
+
@preface = false if self.class::MAIN_CLAUSE_NAMES.include?(ret)
|
63
|
+
@preface = false if self.class::PREFACE_CLAUSE_NAMES
|
64
|
+
.intersection(@seen_headers_canonical + [ret]).empty?
|
65
|
+
end
|
66
|
+
|
67
|
+
def preface_main_filter(ret, node)
|
68
|
+
start_main_section(ret, node)
|
69
|
+
@preface && self.class::MAIN_CLAUSE_NAMES.include?(ret) and return nil
|
70
|
+
!@preface && self.class::PREFACE_CLAUSE_NAMES.include?(ret) and
|
71
|
+
return nil
|
72
|
+
ret
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -3,11 +3,8 @@ module Metanorma
|
|
3
3
|
module Table
|
4
4
|
def table_attrs(node)
|
5
5
|
keep_attrs(node)
|
6
|
-
.merge(
|
7
|
-
|
8
|
-
unnumbered: node.option?("unnumbered") ? "true" : nil,
|
9
|
-
number: node.attr("number"),
|
10
|
-
subsequence: node.attr("subsequence"),
|
6
|
+
.merge(id_unnum_attrs(node))
|
7
|
+
.merge(headerrows: node.attr("headerrows"),
|
11
8
|
alt: node.attr("alt"),
|
12
9
|
summary: node.attr("summary"),
|
13
10
|
width: node.attr("width"))
|
@@ -28,11 +25,9 @@ module Metanorma
|
|
28
25
|
private
|
29
26
|
|
30
27
|
def colgroup(node, xml_table)
|
31
|
-
|
32
|
-
|
33
|
-
cols
|
34
|
-
return unless (cols.size > 1) && cols.all? { |c| /\d/.match(c) }
|
35
|
-
|
28
|
+
node.option? "autowidth" and return
|
29
|
+
cols = node.attr("cols")&.split(",") or return
|
30
|
+
(cols.size > 1) && cols.all? { |c| /\d/.match(c) } or return
|
36
31
|
xml_table.colgroup do |cg|
|
37
32
|
node.columns.each do |col|
|
38
33
|
cg.col width: "#{col.attr 'colpcwidth'}%"
|
@@ -57,9 +52,10 @@ module Metanorma
|
|
57
52
|
end
|
58
53
|
|
59
54
|
def table_cell(node, xml_tr, tblsec)
|
60
|
-
cell_attributes =
|
61
|
-
{
|
62
|
-
rowspan: node.rowspan, align: node.attr("halign") }
|
55
|
+
cell_attributes = id_attr(node).merge(
|
56
|
+
{ colspan: node.colspan, valign: node.attr("valign"),
|
57
|
+
rowspan: node.rowspan, align: node.attr("halign") },
|
58
|
+
)
|
63
59
|
cell_tag = "td"
|
64
60
|
cell_tag = "th" if tblsec == :head || node.style == :header
|
65
61
|
xml_tr.send cell_tag, **attr_code(cell_attributes) do |thd|
|
@@ -16,11 +16,13 @@ module Metanorma
|
|
16
16
|
@unique_designs = {}
|
17
17
|
@c = HTMLEntities.new
|
18
18
|
@terms_tags = xmldoc.xpath("//terms").each_with_object({}) do |t, m|
|
19
|
-
m[t["id"]] = true
|
19
|
+
#m[t["id"]] = true
|
20
|
+
m[t["anchor"]] = true
|
20
21
|
end
|
21
22
|
end
|
22
23
|
|
23
24
|
def call
|
25
|
+
#require "debug"; binding.b
|
24
26
|
@idhash = populate_idhash
|
25
27
|
@unique_designs = unique_designators
|
26
28
|
@lookup = replace_automatic_generated_ids_terms
|
@@ -29,6 +31,7 @@ module Metanorma
|
|
29
31
|
related_cleanup
|
30
32
|
remove_missing_refs
|
31
33
|
concept_cleanup2
|
34
|
+
anchor_to_id
|
32
35
|
end
|
33
36
|
|
34
37
|
private
|
@@ -36,7 +39,7 @@ module Metanorma
|
|
36
39
|
def unique_designators
|
37
40
|
ret = xmldoc
|
38
41
|
.xpath("//preferred/expression/name | //admitted/expression/name | " \
|
39
|
-
"//
|
42
|
+
"//deprecates/expression/name").each_with_object({}) do |n, m|
|
40
43
|
m[n.text] ||= 0
|
41
44
|
m[n.text] += 1
|
42
45
|
end
|
@@ -69,9 +72,12 @@ module Metanorma
|
|
69
72
|
end
|
70
73
|
|
71
74
|
def populate_idhash
|
72
|
-
xmldoc.xpath("//*[@id]").each_with_object({}) do |n, mem|
|
73
|
-
|
74
|
-
|
75
|
+
#xmldoc.xpath("//*[@id]").each_with_object({}) do |n, mem|
|
76
|
+
xmldoc.xpath("//*[@anchor]").each_with_object({}) do |n, mem|
|
77
|
+
#/^(term|symbol)-/.match?(n["id"]) or next
|
78
|
+
/^(term|symbol)-/.match?(n["anchor"]) or next
|
79
|
+
#mem[n["id"]] = true
|
80
|
+
mem[n["anchor"]] = true
|
75
81
|
end
|
76
82
|
end
|
77
83
|
|
@@ -207,18 +213,22 @@ module Metanorma
|
|
207
213
|
|
208
214
|
def norm_id_memorize_init(node, res_table, selector, prefix, use_domain)
|
209
215
|
term_text = norm_ref_id(node, selector, use_domain) or return
|
210
|
-
unless AUTO_GEN_ID_REGEXP.match(node["id"]).nil? && !node["id"].nil?
|
216
|
+
#unless AUTO_GEN_ID_REGEXP.match(node["id"]).nil? && !node["id"].nil?
|
217
|
+
unless AUTO_GEN_ID_REGEXP.match(node["anchor"]).nil? && !node["anchor"].nil?
|
211
218
|
id = unique_text_id(term_text, prefix)
|
212
|
-
node["id"] = id
|
219
|
+
#node["id"] = id
|
220
|
+
node["anchor"] = id
|
213
221
|
@idhash[id] = true
|
214
222
|
end
|
215
|
-
res_table[term_text] = node["id"]
|
223
|
+
#res_table[term_text] = node["id"]
|
224
|
+
res_table[term_text] = node["anchor"]
|
216
225
|
end
|
217
226
|
|
218
227
|
def memorize_other_pref_terms(node, res_table, text_selector, use_domain)
|
219
228
|
node.xpath(text_selector).each_with_index do |p, i|
|
220
229
|
i.positive? or next
|
221
|
-
res_table[norm_ref_id1(p, use_domain ? node : nil)] = node["id"]
|
230
|
+
#res_table[norm_ref_id1(p, use_domain ? node : nil)] = node["id"]
|
231
|
+
res_table[norm_ref_id1(p, use_domain ? node : nil)] = node["anchor"]
|
222
232
|
end
|
223
233
|
end
|
224
234
|
|
@@ -255,6 +265,13 @@ module Metanorma
|
|
255
265
|
end
|
256
266
|
end
|
257
267
|
|
268
|
+
def anchor_to_id
|
269
|
+
xmldoc.xpath("//*[@anchor]").each do |n|
|
270
|
+
/^(term|symbol)-/.match?(n["anchor"]) or next
|
271
|
+
n["id"] or add_id(n)
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
258
275
|
include ::Metanorma::Standoc::Utils
|
259
276
|
end
|
260
277
|
end
|
@@ -157,7 +157,7 @@ module Metanorma
|
|
157
157
|
def termsource(node)
|
158
158
|
matched = extract_termsource_refs(node.content, node) or return
|
159
159
|
noko do |xml|
|
160
|
-
xml.
|
160
|
+
xml.source **termsource_attrs(node, matched) do |xml_t|
|
161
161
|
seen_xref = Nokogiri::XML.fragment(matched[:xref])
|
162
162
|
add_term_source(node, xml_t, seen_xref, matched)
|
163
163
|
end
|
@@ -35,6 +35,10 @@ module Metanorma
|
|
35
35
|
Metanorma::Utils::attr_code(attributes)
|
36
36
|
end
|
37
37
|
|
38
|
+
def add_id(node)
|
39
|
+
node["id"] = "_#{UUIDTools::UUID.random_create}"
|
40
|
+
end
|
41
|
+
|
38
42
|
def csv_split(text, delim = ";")
|
39
43
|
Metanorma::Utils::csv_split(@c.decode(text), delim)
|
40
44
|
.map { |x| @c.encode(x, :basic, :hexadecimal) }
|
@@ -119,7 +123,7 @@ module Metanorma
|
|
119
123
|
end
|
120
124
|
|
121
125
|
SECTION_CONTAINERS =
|
122
|
-
%w(foreword introduction acknowledgements abstract
|
126
|
+
%w(foreword introduction acknowledgements executivesummary abstract
|
123
127
|
clause references terms definitions annex appendix indexsect
|
124
128
|
executivesummary).freeze
|
125
129
|
|
@@ -12,7 +12,7 @@ module Metanorma
|
|
12
12
|
module Validate
|
13
13
|
def content_validate(doc)
|
14
14
|
@doctype = doc.at("//bibdata/ext/doctype")&.text
|
15
|
-
repeat_id_validate(doc.root) # feeds xref_validate
|
15
|
+
repeat_id_validate(doc.root) # feeds xref_validate, termsect_validate
|
16
16
|
xref_validate(doc) # feeds nested_asset_validate
|
17
17
|
nested_asset_validate(doc)
|
18
18
|
section_validate(doc)
|
@@ -92,7 +92,7 @@ module Metanorma
|
|
92
92
|
end
|
93
93
|
|
94
94
|
def nested_asset_xref_report(outer, inner, _doc)
|
95
|
-
i = @doc_xrefs[inner["
|
95
|
+
i = @doc_xrefs[inner["anchor"]] or return
|
96
96
|
err2 = "There is a crossreference to an instance of #{inner.name} " \
|
97
97
|
"nested within #{outer.name}: #{i.to_xml}"
|
98
98
|
@log.add("Style", i, err2)
|
@@ -158,32 +158,98 @@ module Metanorma
|
|
158
158
|
schema_validate(formattedstr_strip(doc.dup), schema_location)
|
159
159
|
end
|
160
160
|
|
161
|
+
# Check should never happen with content ids, but will check it anyway
|
162
|
+
# since consequences are so catastrophic
|
161
163
|
def repeat_id_validate1(elem)
|
162
164
|
if @doc_ids[elem["id"]]
|
163
165
|
@log.add("Anchors", elem,
|
164
|
-
"
|
165
|
-
"used at line #{@doc_ids[elem['id']]}", severity: 0)
|
166
|
+
"ID #{elem['id']} has already been " \
|
167
|
+
"used at line #{@doc_ids[elem['id']][:line]}", severity: 0)
|
168
|
+
else
|
169
|
+
@doc_ids[elem["id"]] =
|
170
|
+
{ line: elem.line, anchor: elem["anchor"] }.compact
|
166
171
|
end
|
167
|
-
@doc_ids[elem["id"]] = elem.line
|
168
172
|
end
|
169
173
|
|
174
|
+
def repeat_anchor_validate1(elem)
|
175
|
+
if @doc_anchors[elem["anchor"]]
|
176
|
+
@log.add("Anchors", elem,
|
177
|
+
"Anchor #{elem['anchor']} has already been used at line " \
|
178
|
+
"#{@doc_anchors[elem['anchor']][:line]}", severity: 0)
|
179
|
+
else
|
180
|
+
@doc_anchors[elem["anchor"]] = { line: elem.line, id: elem["id"] }
|
181
|
+
@doc_anchor_seq << elem["anchor"]
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
# Check should never happen with content ids, but will check it anyway
|
170
186
|
def repeat_id_validate(doc)
|
171
|
-
|
187
|
+
repeat_id_validate_prep
|
172
188
|
doc.xpath("//*[@id]").each do |x|
|
189
|
+
@doc_id_seq << x["id"]
|
173
190
|
repeat_id_validate1(x)
|
191
|
+
x["anchor"] and repeat_anchor_validate1(x)
|
174
192
|
end
|
193
|
+
@doc_id_seq_hash = @doc_id_seq.each_with_index
|
194
|
+
.with_object({}) do |(x, i), m|
|
195
|
+
m[x] = i
|
196
|
+
end
|
197
|
+
@doc_anchor_seq_hash = @doc_anchor_seq.each_with_index
|
198
|
+
.with_object({}) do |(x, i), m|
|
199
|
+
m[x] = i
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
def repeat_id_validate_prep
|
204
|
+
@doc_ids = {} # hash of all ids in document to line number, anchor
|
205
|
+
@doc_anchors = {} # hash of all anchors in document to line number, id
|
206
|
+
@doc_id_seq = [] # ordered list of all ids in document
|
207
|
+
@doc_anchor_seq = [] # ordered list of all anchors in document
|
175
208
|
end
|
176
209
|
|
177
|
-
#
|
210
|
+
# Retrieve anchors between two nominated values
|
211
|
+
# (exclusive of start_id AND exclusive of end_id)
|
212
|
+
def get_anchors_between(start_id, end_id)
|
213
|
+
start_index = @doc_anchor_seq_hash[start_id]
|
214
|
+
end_index = @doc_anchor_seq_hash[end_id]
|
215
|
+
start_index.nil? || end_index.nil? and return []
|
216
|
+
start_index >= end_index and return []
|
217
|
+
@doc_anchor_seq[start_index...end_index]
|
218
|
+
end
|
219
|
+
|
220
|
+
# manually check for xref/@target et sim. integrity
|
178
221
|
def xref_validate(doc)
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
222
|
+
xref_validate_exists(doc)
|
223
|
+
xref_range_record(doc)
|
224
|
+
end
|
225
|
+
|
226
|
+
def xref_validate_exists(doc)
|
227
|
+
@doc_xrefs = {}
|
228
|
+
Metanorma::Utils::anchor_attributes.each do |a|
|
229
|
+
doc.xpath("//#{a[0]}/@#{a[1]}").each do |x|
|
230
|
+
@doc_xrefs[x.text] = x.parent
|
231
|
+
@doc_anchors[x.text] and next
|
232
|
+
@log.add("Anchors", x.parent,
|
233
|
+
"Crossreference target #{x} is undefined", severity: 1)
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
# If there is an xref range, record the IDs between the two targets
|
239
|
+
def xref_range_record(doc)
|
240
|
+
doc.xpath("//xref//location[@connective = 'to']").each do |to|
|
241
|
+
process_range_location(to)
|
185
242
|
end
|
186
243
|
end
|
244
|
+
|
245
|
+
def process_range_location(to_location)
|
246
|
+
# Get the preceding location element if it exists
|
247
|
+
from = to_location.previous_element
|
248
|
+
from && from.name == "location" or return
|
249
|
+
from["target"] && to_location["target"] or return
|
250
|
+
get_anchors_between(from["target"], to_location["target"])
|
251
|
+
.each { |id| @doc_xrefs[id] = from }
|
252
|
+
end
|
187
253
|
end
|
188
254
|
end
|
189
255
|
end
|
@@ -46,12 +46,11 @@ module Metanorma
|
|
46
46
|
|
47
47
|
def hanging_para_style(root)
|
48
48
|
root.xpath("//clause | //annex | //foreword | //introduction | " \
|
49
|
-
"//acknowledgements").each do |c|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
49
|
+
"//acknowledgements | //executivesummary").each do |c|
|
50
|
+
c.at("./clause") or next
|
51
|
+
c.elements.reject do |n|
|
52
|
+
%w(clause title).include? n.name
|
53
|
+
end.empty? and next
|
55
54
|
style_warning(c, "Hanging paragraph in clause")
|
56
55
|
end
|
57
56
|
end
|
@@ -16,7 +16,7 @@ module Metanorma
|
|
16
16
|
def iev_validate(xmldoc)
|
17
17
|
@iev = init_iev or return
|
18
18
|
xmldoc.xpath("//term").each do |t|
|
19
|
-
t.xpath("
|
19
|
+
t.xpath("./source | ./preferred/source | ./admitted/source | ./deprecates/source | ./related/source").each do |src|
|
20
20
|
(/^IEC[ ]60050-/.match(src.at("./origin/@citeas")&.text) &&
|
21
21
|
loc = src.xpath(SOURCELOCALITY)&.text) or next
|
22
22
|
iev_validate1(t, loc, xmldoc)
|
@@ -46,15 +46,16 @@ module Metanorma
|
|
46
46
|
|
47
47
|
def concept_validate_ids(doc)
|
48
48
|
@concept_ids ||= doc.xpath("//term | //definitions//dt")
|
49
|
-
.each_with_object({}) { |x, m| m[x["
|
49
|
+
.each_with_object({}) { |x, m| m[x["anchor"]] = true }
|
50
50
|
@concept_terms_tags ||= doc.xpath("//terms")
|
51
|
-
.each_with_object({}) { |t, m| m[t["
|
51
|
+
.each_with_object({}) { |t, m| m[t["anchor"]] = true }
|
52
52
|
nil
|
53
53
|
end
|
54
54
|
|
55
55
|
def concept_validate_msg(_doc, tag, refterm, xref)
|
56
|
+
t = @doc_ids.dig(xref["target"], :anchor) || xref["target"]
|
56
57
|
ret = <<~LOG
|
57
|
-
#{tag.capitalize} #{xref.at("../#{refterm}")&.text} is pointing to #{
|
58
|
+
#{tag.capitalize} #{xref.at("../#{refterm}")&.text} is pointing to #{t}, which is not a term or symbol
|
58
59
|
LOG
|
59
60
|
if @concept_terms_tags[xref["target"]]
|
60
61
|
ret = ret.strip
|
@@ -79,7 +80,7 @@ module Metanorma
|
|
79
80
|
def preferred_validate_report(terms)
|
80
81
|
terms.each do |k, v|
|
81
82
|
v.size > 1 or next
|
82
|
-
loc = v.map { |x| x["
|
83
|
+
loc = v.map { |x| x["anchor"] }.join(", ")
|
83
84
|
err = "Term #{k} occurs twice as preferred designation: #{loc}"
|
84
85
|
@log.add("Terms", v.first, err, severity: 1)
|
85
86
|
end
|
@@ -92,7 +93,7 @@ module Metanorma
|
|
92
93
|
c = d.ancestors.detect do |x|
|
93
94
|
section_containers.include?(x.name)
|
94
95
|
end
|
95
|
-
c["id"]
|
96
|
+
c["id"] or add_id(c["id"])
|
96
97
|
m[c["id"]] ||= { clause: c, designations: [] }
|
97
98
|
m[c["id"]][:designations] << d
|
98
99
|
end
|
@@ -106,7 +107,7 @@ module Metanorma
|
|
106
107
|
end.join(", ")
|
107
108
|
err = <<~ERROR
|
108
109
|
Clause not recognised as a term clause, but contains designation markup
|
109
|
-
(preferred:[], admitted:[], alt:[], deprecated:[]):<br/>
|
110
|
+
(<code>preferred:[], admitted:[], alt:[], deprecated:[]</code>):<br/>
|
110
111
|
#{desgns}</br>
|
111
112
|
Ensure the parent clause is recognised as a terms clause by inserting <code>[heading=terms and definitions]</code> above the title,
|
112
113
|
in case the heading is not automatically recognised. See also <a href="https://www.metanorma.org/author/topics/sections/concepts/#clause-title">Metanorma documentation</a>.
|