metanorma-iso 1.7.2 → 1.8.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/rake.yml +11 -41
  3. data/.gitignore +2 -0
  4. data/.rubocop.yml +7 -1
  5. data/lib/asciidoctor/iso/base.rb +14 -11
  6. data/lib/asciidoctor/iso/biblio.rng +4 -6
  7. data/lib/asciidoctor/iso/cleanup.rb +40 -24
  8. data/lib/asciidoctor/iso/front.rb +29 -17
  9. data/lib/asciidoctor/iso/front_id.rb +81 -60
  10. data/lib/asciidoctor/iso/isodoc.rng +327 -2
  11. data/lib/asciidoctor/iso/isostandard.rng +12 -97
  12. data/lib/asciidoctor/iso/section.rb +2 -1
  13. data/lib/asciidoctor/iso/validate.rb +22 -109
  14. data/lib/asciidoctor/iso/validate_image.rb +97 -0
  15. data/lib/asciidoctor/iso/validate_requirements.rb +26 -20
  16. data/lib/asciidoctor/iso/validate_section.rb +39 -20
  17. data/lib/asciidoctor/iso/validate_style.rb +36 -24
  18. data/lib/asciidoctor/iso/validate_title.rb +23 -17
  19. data/lib/isodoc/iso/base_convert.rb +19 -2
  20. data/lib/isodoc/iso/html/style-human.css +7 -0
  21. data/lib/isodoc/iso/html/style-iso.css +7 -0
  22. data/lib/isodoc/iso/html_convert.rb +0 -1
  23. data/lib/isodoc/iso/i18n-en.yaml +4 -0
  24. data/lib/isodoc/iso/i18n-fr.yaml +4 -0
  25. data/lib/isodoc/iso/i18n-zh-Hans.yaml +4 -0
  26. data/lib/isodoc/iso/index.rb +140 -0
  27. data/lib/isodoc/iso/iso.amendment.xsl +1157 -208
  28. data/lib/isodoc/iso/iso.international-standard.xsl +1157 -208
  29. data/lib/isodoc/iso/metadata.rb +1 -0
  30. data/lib/isodoc/iso/presentation_xml_convert.rb +45 -37
  31. data/lib/isodoc/iso/sts_convert.rb +10 -13
  32. data/lib/isodoc/iso/word_convert.rb +0 -1
  33. data/lib/isodoc/iso/xref.rb +46 -25
  34. data/lib/metanorma/iso/processor.rb +1 -0
  35. data/lib/metanorma/iso/version.rb +1 -1
  36. data/metanorma-iso.gemspec +8 -8
  37. data/spec/{asciidoctor-iso → asciidoctor}/amd_spec.rb +5 -5
  38. data/spec/asciidoctor/base_spec.rb +825 -0
  39. data/spec/{asciidoctor-iso → asciidoctor}/blocks_spec.rb +0 -0
  40. data/spec/{asciidoctor-iso → asciidoctor}/cleanup_spec.rb +383 -25
  41. data/spec/{asciidoctor-iso → asciidoctor}/inline_spec.rb +0 -0
  42. data/spec/{asciidoctor-iso → asciidoctor}/lists_spec.rb +0 -0
  43. data/spec/{asciidoctor-iso → asciidoctor}/refs_spec.rb +0 -0
  44. data/spec/{asciidoctor-iso → asciidoctor}/section_spec.rb +0 -14
  45. data/spec/{asciidoctor-iso → asciidoctor}/table_spec.rb +0 -0
  46. data/spec/{asciidoctor-iso → asciidoctor}/validate_spec.rb +188 -83
  47. data/spec/isodoc/postproc_spec.rb +481 -438
  48. data/spec/isodoc/section_spec.rb +219 -0
  49. data/spec/spec_helper.rb +2 -0
  50. metadata +65 -64
  51. data/lib/isodoc/iso/html/scripts.html +0 -178
  52. data/spec/asciidoctor-iso/base_spec.rb +0 -704
@@ -10,7 +10,7 @@ module Asciidoctor
10
10
  end
11
11
 
12
12
  def scope_parse(attrs, xml, node)
13
- attrs = attrs.merge(type: "scope") unless @amd
13
+ attrs = attrs.merge(type: "scope") unless @amd
14
14
  clause_parse(attrs, xml, node)
15
15
  end
16
16
 
@@ -32,6 +32,7 @@ module Asciidoctor
32
32
 
33
33
  def sectiontype(node, level = true)
34
34
  return nil if @amd
35
+
35
36
  super
36
37
  end
37
38
  end
@@ -1,8 +1,9 @@
1
1
  require "metanorma-standoc"
2
- require_relative "./validate_style.rb"
3
- require_relative "./validate_requirements.rb"
4
- require_relative "./validate_section.rb"
5
- require_relative "./validate_title.rb"
2
+ require_relative "./validate_style"
3
+ require_relative "./validate_requirements"
4
+ require_relative "./validate_section"
5
+ require_relative "./validate_title"
6
+ require_relative "./validate_image"
6
7
  require "nokogiri"
7
8
  require "jing"
8
9
  require "iev"
@@ -10,17 +11,6 @@ require "iev"
10
11
  module Asciidoctor
11
12
  module ISO
12
13
  class Converter < Standoc::Converter
13
- # ISO/IEC DIR 2, 22.3.2
14
- def onlychild_clause_validate(root)
15
- root.xpath(Standoc::Utils::SUBCLAUSE_XPATH).each do |c|
16
- next unless c.xpath("../clause").size == 1
17
- title = c.at("./title")
18
- location = c["id"] || c.text[0..60] + "..."
19
- location += ":#{title.text}" if c["id"] && !title.nil?
20
- @log.add("Style", nil, "#{location}: subclause is only child")
21
- end
22
- end
23
-
24
14
  def isosubgroup_validate(root)
25
15
  root.xpath("//technical-committee/@type").each do |t|
26
16
  unless %w{TC PC JTC JPC}.include? t.text
@@ -37,12 +27,13 @@ module Asciidoctor
37
27
  end
38
28
 
39
29
  # ISO/IEC DIR 2, 15.5.3
30
+ # does not deal with preceding text marked up
40
31
  def see_xrefs_validate(root)
41
32
  root.xpath("//xref").each do |t|
42
- # does not deal with preceding text marked up
43
33
  preceding = t.at("./preceding-sibling::text()[last()]")
44
34
  next unless !preceding.nil? &&
45
35
  /\b(see| refer to)\s*$/mi.match(preceding)
36
+
46
37
  (target = root.at("//*[@id = '#{t['target']}']")) || next
47
38
  if target&.at("./ancestor-or-self::*[@obligation = 'normative']")
48
39
  @log.add("Style", t,
@@ -54,36 +45,34 @@ module Asciidoctor
54
45
  # ISO/IEC DIR 2, 15.5.3
55
46
  def see_erefs_validate(root)
56
47
  root.xpath("//eref").each do |t|
57
- preceding = t.at("./preceding-sibling::text()[last()]")
58
- next unless !preceding.nil? &&
59
- /\b(see|refer to)\s*$/mi.match(preceding)
48
+ prec = t.at("./preceding-sibling::text()[last()]")
49
+ next unless !prec.nil? && /\b(see|refer to)\s*$/mi.match(prec)
50
+
60
51
  unless target = root.at("//*[@id = '#{t['bibitemid']}']")
61
52
  @log.add("Bibliography", t,
62
53
  "'#{t} is not pointing to a real reference")
63
54
  next
64
55
  end
65
- if target.at("./ancestor::references[@normative = 'true']")
56
+ target.at("./ancestor::references[@normative = 'true']") and
66
57
  @log.add("Style", t,
67
58
  "'see #{t}' is pointing to a normative reference")
68
- end
69
59
  end
70
60
  end
71
61
 
72
62
  # ISO/IEC DIR 2, 10.4
73
63
  def locality_erefs_validate(root)
74
64
  root.xpath("//eref[descendant::locality]").each do |t|
75
- if /^(ISO|IEC)/.match t["citeas"]
76
- unless /:[ ]?(\d+{4}|–)$/.match t["citeas"]
77
- @log.add("Style", t,
78
- "undated reference #{t['citeas']} should not contain "\
79
- "specific elements")
80
- end
65
+ if /^(ISO|IEC)/.match?(t["citeas"]) &&
66
+ !(/: ?(\d+{4}|–)$/.match?(t["citeas"]))
67
+ @log.add("Style", t,
68
+ "undated reference #{t['citeas']} should not contain "\
69
+ "specific elements")
81
70
  end
82
71
  end
83
72
  end
84
73
 
85
- def termdef_warn(text, re, t, term, msg)
86
- re.match(text) && @log.add("Style", t, "#{term}: #{msg}")
74
+ def termdef_warn(text, regex, elem, term, msg)
75
+ regex.match(text) && @log.add("Style", elem, "#{term}: #{msg}")
87
76
  end
88
77
 
89
78
  # ISO/IEC DIR 2, 16.5.6
@@ -103,6 +92,7 @@ module Asciidoctor
103
92
  def cited_term_style(xmldoc)
104
93
  xmldoc.xpath("//term//xref").each do |x|
105
94
  next unless xmldoc.at("//term[@id = '#{x['target']}']")
95
+
106
96
  x&.previous&.text == " (" and x&.previous&.previous&.name == "em" or
107
97
  style_warning(x, "term citation not preceded with italicised term",
108
98
  x.parent.text)
@@ -111,8 +101,8 @@ module Asciidoctor
111
101
 
112
102
  def doctype_validate(xmldoc)
113
103
  doctype = xmldoc&.at("//bibdata/ext/doctype")&.text
114
- %w(international-standard technical-specification technical-report
115
- publicly-available-specification international-workshop-agreement
104
+ %w(international-standard technical-specification technical-report
105
+ publicly-available-specification international-workshop-agreement
116
106
  guide amendment technical-corrigendum).include? doctype or
117
107
  @log.add("Document Attributes", nil,
118
108
  "#{doctype} is not a recognised document type")
@@ -146,83 +136,6 @@ module Asciidoctor
146
136
  "#{iteration} is not a recognised iteration")
147
137
  end
148
138
 
149
- # DRG directives 3.7; but anticipated by standoc
150
- def subfigure_validate(xmldoc)
151
- xmldoc.xpath("//figure//figure").each do |f|
152
- { footnote: "fn", note: "note", key: "dl" }.each do |k, v|
153
- f.xpath(".//#{v}").each do |n|
154
- @log.add("Style", n, "#{k} is not permitted in a subfigure")
155
- end
156
- end
157
- end
158
- end
159
-
160
- def image_name_prefix(xmldoc)
161
- std = xmldoc&.at("//bibdata/ext/structuredidentifier/project-number") or return
162
- num = xmldoc&.at("//bibdata/docnumber")&.text or return
163
- ed = xmldoc&.at("//bibdata/edition")&.text || "1"
164
- prefix = num
165
- part = std["part"] and prefix += "-#{std['part']}"
166
- prefix += "_ed#{ed}"
167
- amd = std["amendment"] and prefix += "amd#{amd}"
168
- prefix
169
- end
170
-
171
- def image_name_suffix(xmldoc)
172
- case xmldoc&.at("//bibdata/language")&.text
173
- when "fr" then "_f"
174
- when "de" then "_d"
175
- when "ru" then "_r"
176
- when "es" then "_s"
177
- when "ar" then "_a"
178
- when "en" then "_e"
179
- else
180
- "_e"
181
- end
182
- end
183
-
184
- def disjunct_error(i, cond1, cond2, msg1, msg2)
185
- cond1 && !cond2 and @log.add("Style", i, "image name #{i['src']} #{msg1}")
186
- !cond1 && cond2 and @log.add("Style", i, "image name #{i['src']} #{msg2}")
187
- end
188
-
189
- def image_name_validate1(i, prefix)
190
- m = %r[(SL)?#{prefix}fig(?<tab>Tab)?(?<annex>[A-Z])?(Text)?(?<num>\d+)
191
- (?<subfig>[a-z])?(?<key>_key\d+)?(?<lang>_[a-z])?$]x.match(File.basename(i["src"], ".*"))
192
- if m.nil?
193
- @log.add("Style", i, "image name #{i['src']} does not match DRG requirements")
194
- return
195
- end
196
- warn i['src']
197
- disjunct_error(i, i.at("./ancestor::table"), !m[:tab].nil?,
198
- "is under a table but is not so labelled", "is labelled as under a table but is not")
199
- disjunct_error(i, i.at("./ancestor::annex"), !m[:annex].nil?,
200
- "is under an annex but is not so labelled", "is labelled as under an annex but is not")
201
- disjunct_error(i, i.xpath("./ancestor::figure").size > 1, !m[:subfig].nil?,
202
- "does not have a subfigure letter but is a subfigure",
203
- "has a subfigure letter but is not a subfigure")
204
- lang = image_name_suffix(i.document.root)
205
- (m[:lang] || "_e") == lang or @log.add("Style", i, "image name #{i['src']} expected to have suffix #{lang}")
206
- end
207
-
208
- # DRG directives 3.2
209
- def image_name_validate(xmldoc)
210
- prefix = image_name_prefix(xmldoc) or return
211
- xmldoc.xpath("//image").each do |i|
212
- if /^ISO_\d+_/.match(File.basename(i["src"]))
213
- elsif /^(SL)?#{prefix}fig/.match(File.basename(i["src"]))
214
- image_name_validate1(i, prefix)
215
- else
216
- @log.add("Style", i, "image name #{i['src']} does not match DRG requirements: expect #{prefix}fig")
217
- end
218
- end
219
- end
220
-
221
- def figure_validate(xmldoc)
222
- image_name_validate(xmldoc)
223
- subfigure_validate(xmldoc)
224
- end
225
-
226
139
  def bibdata_validate(doc)
227
140
  doctype_validate(doc)
228
141
  script_validate(doc)
@@ -250,7 +163,7 @@ module Asciidoctor
250
163
  xmldoc.xpath("//bibitem[date/on = '–']").each do |b|
251
164
  b.at("./note[@type = 'Unpublished-Status']") or
252
165
  @log.add("Style", b,
253
- "Reference #{b&.at("./@id")&.text} does not have an "\
166
+ "Reference #{b&.at('./@id')&.text} does not have an "\
254
167
  "associated footnote indicating unpublished status")
255
168
  end
256
169
  end
@@ -0,0 +1,97 @@
1
+ module Asciidoctor
2
+ module ISO
3
+ class Converter < Standoc::Converter
4
+ # DRG directives 3.7; but anticipated by standoc
5
+ def subfigure_validate(xmldoc)
6
+ xmldoc.xpath("//figure//figure").each do |f|
7
+ { footnote: "fn", note: "note", key: "dl" }.each do |k, v|
8
+ f.xpath(".//#{v}").each do |n|
9
+ @log.add("Style", n, "#{k} is not permitted in a subfigure")
10
+ end
11
+ end
12
+ end
13
+ end
14
+
15
+ def image_name_prefix(xmldoc)
16
+ std = xmldoc&.at("//bibdata/ext/structuredidentifier/project-number") or
17
+ return
18
+ num = xmldoc&.at("//bibdata/docnumber")&.text or return
19
+ ed = xmldoc&.at("//bibdata/edition")&.text || "1"
20
+ prefix = num
21
+ std["part"] and prefix += "-#{std['part']}"
22
+ prefix += "_ed#{ed}"
23
+ amd = std["amendment"] and prefix += "amd#{amd}"
24
+ prefix
25
+ end
26
+
27
+ def image_name_suffix(xmldoc)
28
+ case xmldoc&.at("//bibdata/language")&.text
29
+ when "fr" then "_f"
30
+ when "de" then "_d"
31
+ when "ru" then "_r"
32
+ when "es" then "_s"
33
+ when "ar" then "_a"
34
+ when "en" then "_e"
35
+ else
36
+ "_e"
37
+ end
38
+ end
39
+
40
+ def disjunct_error(img, cond1, cond2, msg1, msg2)
41
+ cond1 && !cond2 and
42
+ @log.add("Style", img, "image name #{img['src']} #{msg1}")
43
+ !cond1 && cond2 and
44
+ @log.add("Style", img, "image name #{img['src']} #{msg2}")
45
+ end
46
+
47
+ def image_name_parse(img, prefix)
48
+ m = %r[(SL)?#{prefix}fig(?<tab>Tab)?(?<annex>[A-Z])?(Text)?(?<num>\d+)
49
+ (?<subfig>[a-z])?(?<key>_key\d+)?(?<lang>_[a-z])?$]x
50
+ .match(File.basename(img["src"], ".*"))
51
+ m.nil? and
52
+ @log.add("Style", img,
53
+ "image name #{img['src']} does not match DRG requirements")
54
+ m
55
+ end
56
+
57
+ def image_name_validate1(i, prefix)
58
+ m = image_name_parse(i, prefix) or return
59
+ warn i["src"]
60
+ disjunct_error(i, i.at("./ancestor::table"), !m[:tab].nil?,
61
+ "is under a table but is not so labelled",
62
+ "is labelled as under a table but is not")
63
+ disjunct_error(i, i.at("./ancestor::annex"), !m[:annex].nil?,
64
+ "is under an annex but is not so labelled",
65
+ "is labelled as under an annex but is not")
66
+ disjunct_error(i, i.xpath("./ancestor::figure").size > 1, !m[:subfig].nil?,
67
+ "does not have a subfigure letter but is a subfigure",
68
+ "has a subfigure letter but is not a subfigure")
69
+ lang = image_name_suffix(i.document.root)
70
+ (m[:lang] || "_e") == lang or
71
+ @log.add("Style", i,
72
+ "image name #{i['src']} expected to have suffix #{lang}")
73
+ end
74
+
75
+ # DRG directives 3.2
76
+ def image_name_validate(xmldoc)
77
+ prefix = image_name_prefix(xmldoc) or return
78
+ xmldoc.xpath("//image").each do |i|
79
+ next if i["src"].start_with?("data:")
80
+
81
+ if /^ISO_\d+_/.match?(File.basename(i["src"]))
82
+ elsif /^(SL)?#{prefix}fig/.match?(File.basename(i["src"]))
83
+ image_name_validate1(i, prefix)
84
+ else
85
+ @log.add("Style", i,
86
+ "image name #{i['src']} does not match DRG requirements: expect #{prefix}fig")
87
+ end
88
+ end
89
+ end
90
+
91
+ def figure_validate(xmldoc)
92
+ image_name_validate(xmldoc)
93
+ subfigure_validate(xmldoc)
94
+ end
95
+ end
96
+ end
97
+ end
@@ -1,6 +1,4 @@
1
1
  require "metanorma-standoc"
2
- require "nokogiri"
3
- require "pp"
4
2
 
5
3
  module Asciidoctor
6
4
  module ISO
@@ -19,13 +17,15 @@ module Asciidoctor
19
17
  [.,:;]_do_not )
20
18
  \\b
21
19
  REGEXP
22
- REQUIREMENT_RE =
23
- Regexp.new(REQUIREMENT_RE_STR.gsub(/\s/, "").gsub(/_/, "\\s"),
24
- Regexp::IGNORECASE)
20
+
21
+ def requirement_re
22
+ Regexp.new(self.class::REQUIREMENT_RE_STR.gsub(/\s/, "")
23
+ .gsub(/_/, "\\s"), Regexp::IGNORECASE)
24
+ end
25
25
 
26
26
  def requirement_check(text)
27
27
  text.split(/\.\s+/).each do |t|
28
- return t if REQUIREMENT_RE.match t
28
+ return t if requirement_re.match t
29
29
  end
30
30
  nil
31
31
  end
@@ -37,13 +37,15 @@ module Asciidoctor
37
37
  it_is_(not_)?recommended_that
38
38
  \\b
39
39
  REGEXP
40
- RECOMMENDATION_RE =
41
- Regexp.new(RECOMMENDATION_RE_STR.gsub(/\s/, "").gsub(/_/, "\\s"),
42
- Regexp::IGNORECASE)
40
+
41
+ def recommendation_re
42
+ Regexp.new(self.class::RECOMMENDATION_RE_STR.gsub(/\s/, "")
43
+ .gsub(/_/, "\\s"), Regexp::IGNORECASE)
44
+ end
43
45
 
44
46
  def recommendation_check(text)
45
47
  text.split(/\.\s+/).each do |t|
46
- return t if RECOMMENDATION_RE.match t
48
+ return t if recommendation_re.match t
47
49
  end
48
50
  nil
49
51
  end
@@ -56,13 +58,15 @@ module Asciidoctor
56
58
  no\\b[^.,]+\\b(is|are)_required
57
59
  \\b
58
60
  REGEXP
59
- PERMISSION_RE =
60
- Regexp.new(PERMISSION_RE_STR.gsub(/\s/, "").gsub(/_/, "\\s"),
61
- Regexp::IGNORECASE)
61
+
62
+ def permission_re
63
+ Regexp.new(self.class::PERMISSION_RE_STR.gsub(/\s/, "")
64
+ .gsub(/_/, "\\s"), Regexp::IGNORECASE)
65
+ end
62
66
 
63
67
  def permission_check(text)
64
68
  text.split(/\.\s+/).each do |t|
65
- return t if PERMISSION_RE.match t
69
+ return t if permission_re.match t
66
70
  end
67
71
  nil
68
72
  end
@@ -76,18 +80,20 @@ module Asciidoctor
76
80
  it_is_not_possible_to
77
81
  \\b
78
82
  REGEXP
79
- POSSIBILITY_RE =
80
- Regexp.new(POSSIBILITY_RE_STR.gsub(/\s/, "").gsub(/_/, "\\s"),
81
- Regexp::IGNORECASE)
82
83
 
83
- def possibility(text)
84
- text.split(/\.\s+/).each { |t| return t if POSSIBILITY_RE.match t }
84
+ def possibility_re
85
+ Regexp.new(self.class::POSSIBILITY_RE_STR.gsub(/\s/, "")
86
+ .gsub(/_/, "\\s"), Regexp::IGNORECASE)
87
+ end
88
+
89
+ def possibility_check(text)
90
+ text.split(/\.\s+/).each { |t| return t if possibility_re.match t }
85
91
  nil
86
92
  end
87
93
 
88
94
  def external_constraint(text)
89
95
  text.split(/\.\s+/).each do |t|
90
- return t if /\b(must)\b/xi.match t
96
+ return t if /\b(must)\b/xi.match? t
91
97
  end
92
98
  nil
93
99
  end
@@ -50,10 +50,11 @@ module Asciidoctor
50
50
  end
51
51
 
52
52
  def seqcheck(names, msg, accepted)
53
- n = names.shift
53
+ n = names.shift
54
54
  return [] if n.nil?
55
+
55
56
  test = accepted.map { |a| n.at(a) }
56
- if test.all? { |a| a.nil? }
57
+ if test.all?(&:nil?)
57
58
  @log.add("Style", nil, msg)
58
59
  end
59
60
  names
@@ -74,37 +75,38 @@ module Asciidoctor
74
75
  [
75
76
  {
76
77
  msg: "Initial section must be (content) Foreword",
77
- val: ["./self::foreword"]
78
+ val: ["./self::foreword"],
78
79
  },
79
80
  {
80
81
  msg: "Prefatory material must be followed by (clause) Scope",
81
- val: ["./self::introduction", "./self::clause[@type = 'scope']" ]
82
+ val: ["./self::introduction", "./self::clause[@type = 'scope']"],
82
83
  },
83
84
  {
84
85
  msg: "Prefatory material must be followed by (clause) Scope",
85
- val: ["./self::clause[@type = 'scope']" ]
86
+ val: ["./self::clause[@type = 'scope']"],
86
87
  },
87
88
  {
88
89
  msg: "Normative References must be followed by "\
89
90
  "Terms and Definitions",
90
- val: ["./self::terms | .//terms"]
91
+ val: ["./self::terms | .//terms"],
91
92
  },
92
- ].freeze
93
+ ].freeze
93
94
 
94
95
  SECTIONS_XPATH =
95
96
  "//foreword | //introduction | //sections/terms | .//annex | "\
96
- "//sections/definitions | //sections/clause | //references[not(parent::clause)] | "\
97
+ "//sections/definitions | //sections/clause | "\
98
+ "//references[not(parent::clause)] | "\
97
99
  "//clause[descendant::references][not(parent::clause)]".freeze
98
100
 
99
101
  def sections_sequence_validate(root)
100
102
  names = root.xpath(SECTIONS_XPATH)
101
- names = seqcheck(names, SEQ[0][:msg], SEQ[0][:val])
103
+ names = seqcheck(names, SEQ[0][:msg], SEQ[0][:val])
102
104
  n = names[0]
103
- names = seqcheck(names, SEQ[1][:msg], SEQ[1][:val])
105
+ names = seqcheck(names, SEQ[1][:msg], SEQ[1][:val])
104
106
  if n&.at("./self::introduction")
105
- names = seqcheck(names, SEQ[2][:msg], SEQ[2][:val])
107
+ names = seqcheck(names, SEQ[2][:msg], SEQ[2][:val])
106
108
  end
107
- names = seqcheck(names, SEQ[3][:msg], SEQ[3][:val])
109
+ names = seqcheck(names, SEQ[3][:msg], SEQ[3][:val])
108
110
  n = names.shift
109
111
  if n&.at("./self::definitions")
110
112
  n = names.shift
@@ -117,11 +119,11 @@ module Asciidoctor
117
119
  "Terms and Definitions")
118
120
  n&.at("./self::clause[@type = 'scope']") &&
119
121
  @log.add("Style", nil, "Scope must occur before Terms and Definitions")
120
- n = names.shift
122
+ n = names.shift
121
123
  while n&.name == "clause"
122
124
  n&.at("./self::clause[@type = 'scope']")
123
125
  @log.add("Style", nil, "Scope must occur before Terms and Definitions")
124
- n = names.shift
126
+ n = names.shift
125
127
  end
126
128
  unless %w(annex references).include? n&.name
127
129
  @log.add("Style", nil, "Only annexes and references can follow clauses")
@@ -145,6 +147,7 @@ module Asciidoctor
145
147
 
146
148
  def style_warning(node, msg, text = nil)
147
149
  return if @novalid
150
+
148
151
  w = msg
149
152
  w += ": #{text}" if text
150
153
  @log.add("Style", node, w)
@@ -165,15 +168,18 @@ module Asciidoctor
165
168
 
166
169
  def tech_report_style(root)
167
170
  root.at("//bibdata/ext/doctype")&.text == "technical-report" or return
168
- root.xpath("//sections/clause[not(@type = 'scope')] | //annex").each do |s|
169
- r = requirement_check(extract_text(s))
170
- style_warning(s, "Technical Report clause may contain requirement", r) if r
171
+ root.xpath("//sections/clause[not(@type = 'scope')] | //annex")
172
+ .each do |s|
173
+ r = requirement_check(extract_text(s)) and
174
+ style_warning(s,
175
+ "Technical Report clause may contain requirement", r)
171
176
  end
172
177
  end
173
178
 
174
179
  ASSETS_TO_STYLE =
175
- "//termsource | //formula | //termnote | //p[not(ancestor::boilerplate)] | "\
176
- "//li[not(p)] | //dt | //dd[not(p)] | //td[not(p)] | //th[not(p)]".freeze
180
+ "//termsource | //formula | //termnote | "\
181
+ "//p[not(ancestor::boilerplate)] | //li[not(p)] | //dt | "\
182
+ "//dd[not(p)] | //td[not(p)] | //th[not(p)]".freeze
177
183
 
178
184
  NORM_BIBITEMS =
179
185
  "//references[@normative = 'true']/bibitem".freeze
@@ -198,10 +204,23 @@ module Asciidoctor
198
204
  end
199
205
 
200
206
  def subclause_validate(root)
201
- root.xpath("//clause/clause/clause/clause/clause/clause/clause/clause").each do |c|
207
+ root.xpath("//clause/clause/clause/clause/clause/clause/clause/clause")
208
+ .each do |c|
202
209
  style_warning(c, "Exceeds the maximum clause depth of 7", nil)
203
210
  end
204
211
  end
212
+
213
+ # ISO/IEC DIR 2, 22.3.2
214
+ def onlychild_clause_validate(root)
215
+ root.xpath(Standoc::Utils::SUBCLAUSE_XPATH).each do |c|
216
+ next unless c.xpath("../clause").size == 1
217
+
218
+ title = c.at("./title")
219
+ location = c["id"] || "#{c.text[0..60]}..."
220
+ location += ":#{title.text}" if c["id"] && !title.nil?
221
+ @log.add("Style", nil, "#{location}: subclause is only child")
222
+ end
223
+ end
205
224
  end
206
225
  end
207
226
  end