metanorma-iso 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (103) hide show
  1. checksums.yaml +7 -0
  2. data/.gitattributes +4 -0
  3. data/.gitignore +11 -0
  4. data/.hound.yml +3 -0
  5. data/.oss-guides.rubocop.yml +1077 -0
  6. data/.rubocop.ribose.yml +66 -0
  7. data/.rubocop.tb.yml +650 -0
  8. data/.rubocop.yml +15 -0
  9. data/.travis.yml +21 -0
  10. data/CODE_OF_CONDUCT.md +46 -0
  11. data/Gemfile +7 -0
  12. data/LICENSE +25 -0
  13. data/Makefile +39 -0
  14. data/README.adoc +882 -0
  15. data/Rakefile +6 -0
  16. data/asciidoctor-iso.gemspec.old +50 -0
  17. data/bin/rspec +18 -0
  18. data/docs/customisation.adoc +186 -0
  19. data/docs/guidance.adoc +436 -0
  20. data/docs/htmloutput.adoc +115 -0
  21. data/docs/quickstart.adoc +375 -0
  22. data/lib/asciidoctor-iso.rb +11 -0
  23. data/lib/asciidoctor/iso/base.rb +48 -0
  24. data/lib/asciidoctor/iso/biblio.rng +836 -0
  25. data/lib/asciidoctor/iso/cleanup.rb +39 -0
  26. data/lib/asciidoctor/iso/converter.rb +19 -0
  27. data/lib/asciidoctor/iso/front.rb +131 -0
  28. data/lib/asciidoctor/iso/isodoc.rng +1059 -0
  29. data/lib/asciidoctor/iso/isostandard.rnc +176 -0
  30. data/lib/asciidoctor/iso/isostandard.rng +1001 -0
  31. data/lib/asciidoctor/iso/section.rb +72 -0
  32. data/lib/asciidoctor/iso/validate.rb +190 -0
  33. data/lib/asciidoctor/iso/validate_requirements.rb +105 -0
  34. data/lib/asciidoctor/iso/validate_section.rb +214 -0
  35. data/lib/asciidoctor/iso/validate_style.rb +134 -0
  36. data/lib/asciidoctor/iso/version.rb +5 -0
  37. data/lib/isodoc/iso/html/header.html +206 -0
  38. data/lib/isodoc/iso/html/html_iso_intro.html +34 -0
  39. data/lib/isodoc/iso/html/html_iso_titlepage.html +34 -0
  40. data/lib/isodoc/iso/html/htmlstyle.scss +46 -0
  41. data/lib/isodoc/iso/html/isodoc.scss +696 -0
  42. data/lib/isodoc/iso/html/scripts.html +174 -0
  43. data/lib/isodoc/iso/html/style-human.scss +1277 -0
  44. data/lib/isodoc/iso/html/style-iso.scss +1257 -0
  45. data/lib/isodoc/iso/html/word_iso_intro.html +72 -0
  46. data/lib/isodoc/iso/html/word_iso_titlepage.html +62 -0
  47. data/lib/isodoc/iso/html/wordstyle.scss +1175 -0
  48. data/lib/isodoc/iso/html_convert.rb +118 -0
  49. data/lib/isodoc/iso/metadata.rb +107 -0
  50. data/lib/isodoc/iso/word_convert.rb +141 -0
  51. data/lib/metanorma/iso.rb +7 -0
  52. data/lib/metanorma/iso/processor.rb +44 -0
  53. data/spec/asciidoctor-iso/base_spec.rb +350 -0
  54. data/spec/asciidoctor-iso/blocks_spec.rb +469 -0
  55. data/spec/asciidoctor-iso/cleanup_spec.rb +765 -0
  56. data/spec/asciidoctor-iso/inline_spec.rb +162 -0
  57. data/spec/asciidoctor-iso/isobib_cache_spec.rb +332 -0
  58. data/spec/asciidoctor-iso/lists_spec.rb +190 -0
  59. data/spec/asciidoctor-iso/macros_spec.rb +111 -0
  60. data/spec/asciidoctor-iso/refs_spec.rb +643 -0
  61. data/spec/asciidoctor-iso/section_spec.rb +334 -0
  62. data/spec/asciidoctor-iso/table_spec.rb +307 -0
  63. data/spec/asciidoctor-iso/validate_spec.rb +907 -0
  64. data/spec/assets/header.html +7 -0
  65. data/spec/assets/html.css +2 -0
  66. data/spec/assets/htmlcover.html +4 -0
  67. data/spec/assets/htmlintro.html +5 -0
  68. data/spec/assets/i18n.yaml +2 -0
  69. data/spec/assets/iso.doc +1093 -0
  70. data/spec/assets/iso.headless.html +33 -0
  71. data/spec/assets/iso.html +278 -0
  72. data/spec/assets/iso.xml +8 -0
  73. data/spec/assets/rice_image1.png +0 -0
  74. data/spec/assets/scripts.html +3 -0
  75. data/spec/assets/std.css +2 -0
  76. data/spec/assets/word.css +2 -0
  77. data/spec/assets/wordcover.html +3 -0
  78. data/spec/assets/wordintro.html +4 -0
  79. data/spec/examples/103_01_02.html +247 -0
  80. data/spec/examples/english.yaml +69 -0
  81. data/spec/examples/iso_123_.xml +45 -0
  82. data/spec/examples/iso_123_all_parts.xml +45 -0
  83. data/spec/examples/iso_123_no_year_note.xml +46 -0
  84. data/spec/examples/iso_124_.xml +41 -0
  85. data/spec/examples/iso_216_.xml +47 -0
  86. data/spec/examples/iso_iec_12382_.xml +48 -0
  87. data/spec/examples/rice.adoc +715 -0
  88. data/spec/examples/rice.preview.html +1877 -0
  89. data/spec/examples/rice.sh +4 -0
  90. data/spec/examples/rice_images/rice_image1.png +0 -0
  91. data/spec/examples/rice_images/rice_image2.png +0 -0
  92. data/spec/examples/rice_images/rice_image3_1.png +0 -0
  93. data/spec/examples/rice_images/rice_image3_2.png +0 -0
  94. data/spec/examples/rice_images/rice_image3_3.png +0 -0
  95. data/spec/isodoc/i18n_spec.rb +642 -0
  96. data/spec/isodoc/iso_spec.rb +168 -0
  97. data/spec/isodoc/metadata_spec.rb +152 -0
  98. data/spec/isodoc/postproc_spec.rb +405 -0
  99. data/spec/isodoc/section_spec.rb +522 -0
  100. data/spec/isodoc/xref_spec.rb +1337 -0
  101. data/spec/metanorma/processor_spec.rb +70 -0
  102. data/spec/spec_helper.rb +227 -0
  103. metadata +402 -0
@@ -0,0 +1,72 @@
1
+ require "htmlentities"
2
+ require "uri"
3
+
4
+ module Asciidoctor
5
+ module ISO
6
+ class Converter < Standoc::Converter
7
+ def section(node)
8
+ a = { id: Standoc::Utils::anchor_or_uuid(node) }
9
+ noko do |xml|
10
+ case sectiontype(node)
11
+ when "introduction" then
12
+ if node.level == 1 then introduction_parse(a, xml, node)
13
+ else
14
+ clause_parse(a, xml, node)
15
+ end
16
+ when "patent notice" then patent_notice_parse(xml, node)
17
+ when "scope" then scope_parse(a, xml, node)
18
+ when "normative references" then norm_ref_parse(a, xml, node)
19
+ when "terms and definitions",
20
+ "terms, definitions, symbols and abbreviated terms",
21
+ "terms, definitions, symbols and abbreviations",
22
+ "terms, definitions and symbols",
23
+ "terms, definitions and abbreviations",
24
+ "terms, definitions and abbreviated terms"
25
+ @term_def = true
26
+ term_def_parse(a, xml, node, true)
27
+ @term_def = false
28
+ when "symbols and abbreviated terms"
29
+ symbols_parse(a, xml, node)
30
+ when "bibliography" then bibliography_parse(a, xml, node)
31
+ else
32
+ if @term_def then term_def_subclause_parse(a, xml, node)
33
+ elsif @biblio then bibliography_parse(a, xml, node)
34
+ elsif node.attr("style") == "bibliography" && node.level == 1
35
+ bibliography_parse(a, xml, node)
36
+ elsif node.attr("style") == "appendix" && node.level == 1
37
+ annex_parse(a, xml, node)
38
+ elsif node.option? "appendix"
39
+ appendix_parse(a, xml, node)
40
+ else
41
+ clause_parse(a, xml, node)
42
+ end
43
+ end
44
+ end.join("\n")
45
+ end
46
+
47
+ def appendix_parse(attrs, xml, node)
48
+ attrs["inline-header".to_sym] = node.option? "inline-header"
49
+ set_obligation(attrs, node)
50
+ xml.appendix **attr_code(attrs) do |xml_section|
51
+ xml_section.title { |name| name << node.title }
52
+ xml_section << node.content
53
+ end
54
+ end
55
+
56
+ def patent_notice_parse(xml, node)
57
+ # xml.patent_notice do |xml_section|
58
+ # xml_section << node.content
59
+ # end
60
+ xml << node.content
61
+ end
62
+
63
+ def scope_parse(attrs, xml, node)
64
+ xml.clause **attr_code(attrs) do |xml_section|
65
+ xml_section.title { |t| t << "Scope" }
66
+ content = node.content
67
+ xml_section << content
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,190 @@
1
+ require "metanorma-standoc"
2
+ require_relative "./validate_style.rb"
3
+ require_relative "./validate_requirements.rb"
4
+ require_relative "./validate_section.rb"
5
+ require "nokogiri"
6
+ require "jing"
7
+ require "pp"
8
+ require "iev"
9
+
10
+ module Asciidoctor
11
+ module ISO
12
+ class Converter < Standoc::Converter
13
+ def title_intro_validate(root)
14
+ title_intro_en = root.at("//title-intro[@language='en']")
15
+ title_intro_fr = root.at("//title-intro[@language='fr']")
16
+ if title_intro_en.nil? && !title_intro_fr.nil?
17
+ warn "No English Title Intro!"
18
+ end
19
+ if !title_intro_en.nil? && title_intro_fr.nil?
20
+ warn "No French Title Intro!"
21
+ end
22
+ end
23
+
24
+ def title_main_validate(root)
25
+ title_main_en = root.at("//title-main[@language='en']")
26
+ title_main_fr = root.at("//title-main[@language='fr']")
27
+ if title_main_en.nil? && !title_main_fr.nil?
28
+ warn "No English Title!"
29
+ end
30
+ if !title_main_en.nil? && title_main_fr.nil?
31
+ warn "No French Title!"
32
+ end
33
+ end
34
+
35
+ def title_part_validate(root)
36
+ title_part_en = root.at("//title-part[@language='en']")
37
+ title_part_fr = root.at("//title-part[@language='fr']")
38
+ (title_part_en.nil? && !title_part_fr.nil?) &&
39
+ warn("No English Title Part!")
40
+ (!title_part_en.nil? && title_part_fr.nil?) &&
41
+ warn("No French Title Part!")
42
+ end
43
+
44
+ def title_subpart_validate(root)
45
+ subpart = root.at("//bibdata/docidentifier/project-number[@subpart]")
46
+ iec = root.at("//bibdata/contributor[role/@type = 'publisher']/"\
47
+ "organization[abbreviation = 'IEC' or "\
48
+ "name = 'International Electrotechnical Commission']")
49
+ warn("Subpart defined on non-IEC document!") if subpart && !iec
50
+ end
51
+
52
+ def title_names_type_validate(root)
53
+ doctypes = /International\sStandard | Technical\sSpecification |
54
+ Publicly\sAvailable\sSpecification | Technical\sReport | Guide /xi
55
+ title_main_en = root.at("//title-main[@language='en']")
56
+ if !title_main_en.nil? && doctypes.match(title_main_en.text)
57
+ warn "Main Title may name document type"
58
+ end
59
+ title_intro_en = root.at("//title-intro[@language='en']")
60
+ if !title_intro_en.nil? && doctypes.match(title_intro_en.text)
61
+ warn "Title Intro may name document type"
62
+ end
63
+ end
64
+
65
+ def title_first_level_validate(root)
66
+ root.xpath(SECTIONS_XPATH).each do |s|
67
+ title = s&.at("./title")&.text || s.name
68
+ s.xpath("./clause | ./terms | ./references").each do |ss|
69
+ subtitle = ss.at("./title")
70
+ !subtitle.nil? && !subtitle&.text&.empty? ||
71
+ warn("#{title}: each first-level subclause must have a title")
72
+ end
73
+ end
74
+ end
75
+
76
+ def title_all_siblings(xpath, label)
77
+ notitle = false
78
+ withtitle = false
79
+ xpath.each do |s|
80
+ title_all_siblings(s.xpath("./clause | ./terms | ./references"),
81
+ s&.at("./title")&.text || s["id"])
82
+ subtitle = s.at("./title")
83
+ notitle = notitle || (!subtitle || subtitle.text.empty?)
84
+ withtitle = withtitle || (subtitle && !subtitle.text.empty?)
85
+ end
86
+ notitle && withtitle &&
87
+ warn("#{label}: all subclauses must have a title, or none")
88
+ end
89
+
90
+ def title_validate(root)
91
+ title_intro_validate(root)
92
+ title_main_validate(root)
93
+ title_part_validate(root)
94
+ title_subpart_validate(root)
95
+ title_names_type_validate(root)
96
+ title_first_level_validate(root)
97
+ title_all_siblings(root.xpath(SECTIONS_XPATH), "(top level)")
98
+ end
99
+
100
+ def onlychild_clause_validate(root)
101
+ root.xpath(Standoc::Utils::SUBCLAUSE_XPATH).each do |c|
102
+ next unless c.xpath("../clause").size == 1
103
+ title = c.at("./title")
104
+ location = c["id"] || c.text[0..60] + "..."
105
+ location += ":#{title.text}" if c["id"] && !title.nil?
106
+ warn "ISO style: #{location}: subclause is only child"
107
+ end
108
+ end
109
+
110
+ def isosubgroup_validate(root)
111
+ root.xpath("//technical-committee/@type").each do |t|
112
+ unless %w{TC PC JTC JPC}.include? t.text
113
+ warn "ISO: invalid technical committee type #{t}"
114
+ end
115
+ end
116
+ root.xpath("//subcommittee/@type").each do |t|
117
+ unless %w{SC JSC}.include? t.text
118
+ warn "ISO: invalid subcommittee type #{t}"
119
+ end
120
+ end
121
+ end
122
+
123
+ def see_xrefs_validate(root)
124
+ root.xpath("//xref").each do |t|
125
+ # does not deal with preceding text marked up
126
+ preceding = t.at("./preceding-sibling::text()[last()]")
127
+ next unless !preceding.nil? && /\bsee\s*$/mi.match(preceding)
128
+ (target = root.at("//*[@id = '#{t['target']}']")) || next
129
+ if target&.at("./ancestor-or-self::*[@obligation = 'normative']")
130
+ warn "ISO: 'see #{t['target']}' is pointing to a normative section"
131
+ end
132
+ end
133
+ end
134
+
135
+ def see_erefs_validate(root)
136
+ root.xpath("//eref").each do |t|
137
+ preceding = t.at("./preceding-sibling::text()[last()]")
138
+ next unless !preceding.nil? && /\bsee\s*$/mi.match(preceding)
139
+ target = root.at("//*[@id = '#{t['bibitemid']}']")
140
+ if target.at("./ancestor::references"\
141
+ "[title = 'Normative References']")
142
+ warn "ISO: 'see #{t}' is pointing to a normative reference"
143
+ end
144
+ end
145
+ end
146
+
147
+ def locality_erefs_validate(root)
148
+ root.xpath("//eref[locality]").each do |t|
149
+ unless /:[ ]?\d+{4}$/.match t["citeas"]
150
+ warn "ISO: undated reference #{t['citeas']} should not contain "\
151
+ "specific elements"
152
+ end
153
+ end
154
+ end
155
+
156
+ def termdef_warn(text, re, term, msg)
157
+ re.match(text) && warn("ISO style: #{term}: #{msg}")
158
+ end
159
+
160
+ def termdef_style(xmldoc)
161
+ xmldoc.xpath("//term").each do |t|
162
+ para = t.at("./definition") || return
163
+ term = t.at("./preferred").text
164
+ termdef_warn(para.text, /^(the|a)\b/i, term,
165
+ "term definition starts with article")
166
+ termdef_warn(para.text, /\.$/i, term,
167
+ "term definition ends with period")
168
+ end
169
+ end
170
+
171
+ def content_validate(doc)
172
+ super
173
+ title_validate(doc.root)
174
+ isosubgroup_validate(doc.root)
175
+ onlychild_clause_validate(doc.root)
176
+ termdef_style(doc.root)
177
+ iev_validate(doc.root)
178
+ see_xrefs_validate(doc.root)
179
+ see_erefs_validate(doc.root)
180
+ locality_erefs_validate(doc.root)
181
+ end
182
+
183
+ def validate(doc)
184
+ content_validate(doc)
185
+ schema_validate(formattedstr_strip(doc.dup),
186
+ File.join(File.dirname(__FILE__), "isostandard.rng"))
187
+ end
188
+ end
189
+ end
190
+ end
@@ -0,0 +1,105 @@
1
+ require "metanorma-standoc"
2
+ require "nokogiri"
3
+ require "pp"
4
+
5
+ module Asciidoctor
6
+ module ISO
7
+ class Converter < Standoc::Converter
8
+ REQUIREMENT_RE_STR = <<~REGEXP.freeze
9
+ \\b
10
+ ( shall | (is|are)_to |
11
+ (is|are)_required_(not_)?to |
12
+ (is|are)_required_that |
13
+ has_to |
14
+ only\\b[^.,]+\\b(is|are)_permitted |
15
+ it_is_necessary |
16
+ (is|are)_not_(allowed | permitted |
17
+ acceptable | permissible) |
18
+ (is|are)_not_to_be |
19
+ do_not )
20
+ \\b
21
+ REGEXP
22
+ REQUIREMENT_RE =
23
+ Regexp.new(REQUIREMENT_RE_STR.gsub(/\s/, "").gsub(/_/, "\\s"),
24
+ Regexp::IGNORECASE)
25
+
26
+ def requirement(text)
27
+ text.split(/\.\s+/).each do |t|
28
+ return t if REQUIREMENT_RE.match t
29
+ end
30
+ nil
31
+ end
32
+
33
+ RECOMMENDATION_RE_STR = <<~REGEXP.freeze
34
+ \\b
35
+ should |
36
+ ought_(not_)?to |
37
+ it_is_(not_)?recommended_that
38
+ \\b
39
+ REGEXP
40
+ RECOMMENDATION_RE =
41
+ Regexp.new(RECOMMENDATION_RE_STR.gsub(/\s/, "").gsub(/_/, "\\s"),
42
+ Regexp::IGNORECASE)
43
+
44
+ def recommendation(text)
45
+ text.split(/\.\s+/).each do |t|
46
+ return t if RECOMMENDATION_RE.match t
47
+ end
48
+ nil
49
+ end
50
+
51
+ PERMISSION_RE_STR = <<~REGEXP.freeze
52
+ \\b
53
+ may |
54
+ (is|are)_(permitted | allowed | permissible ) |
55
+ it_is_not_required_that |
56
+ no\\b[^.,]+\\b(is|are)_required
57
+ \\b
58
+ REGEXP
59
+ PERMISSION_RE =
60
+ Regexp.new(PERMISSION_RE_STR.gsub(/\s/, "").gsub(/_/, "\\s"),
61
+ Regexp::IGNORECASE)
62
+
63
+ def permission(text)
64
+ text.split(/\.\s+/).each do |t|
65
+ return t if PERMISSION_RE.match t
66
+ end
67
+ nil
68
+ end
69
+
70
+ POSSIBILITY_RE_STR = <<~REGEXP.freeze
71
+ \\b
72
+ can | cannot | be_able_to |
73
+ there_is_a_possibility_of |
74
+ it_is_possible_to | be_unable_to |
75
+ there_is_no_possibility_of |
76
+ it_is_not_possible_to
77
+ \\b
78
+ REGEXP
79
+ POSSIBILITY_RE =
80
+ Regexp.new(POSSIBILITY_RE_STR.gsub(/\s/, "").gsub(/_/, "\\s"),
81
+ Regexp::IGNORECASE)
82
+
83
+ def possibility(text)
84
+ text.split(/\.\s+/).each { |t| return t if POSSIBILITY_RE.match t }
85
+ nil
86
+ end
87
+
88
+ def external_constraint(text)
89
+ text.split(/\.\s+/).each do |t|
90
+ return t if /\b(must)\b/xi.match t
91
+ end
92
+ nil
93
+ end
94
+
95
+ def style_no_guidance(node, text, docpart)
96
+ r = requirement(text)
97
+ style_warning(node, "#{docpart} may contain requirement", r) if r
98
+ r = permission(text)
99
+ style_warning(node, "#{docpart} may contain permission", r) if r
100
+ r = recommendation(text)
101
+ style_warning(node, "#{docpart} may contain recommendation", r) if r
102
+ end
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,214 @@
1
+ require "nokogiri"
2
+
3
+ module Asciidoctor
4
+ module ISO
5
+ class Converter < Standoc::Converter
6
+ def section_validate(doc)
7
+ foreword_validate(doc.root)
8
+ normref_validate(doc.root)
9
+ symbols_validate(doc.root)
10
+ sections_sequence_validate(doc.root)
11
+ section_style(doc.root)
12
+ subclause_validate(doc.root)
13
+ super
14
+ end
15
+
16
+ def foreword_validate(root)
17
+ f = root.at("//foreword") || return
18
+ s = f.at("./clause")
19
+ warn "ISO style: foreword contains subclauses" unless s.nil?
20
+ end
21
+
22
+ def normref_validate(root)
23
+ f = root.at("//references[title = 'Normative References']") || return
24
+ f.at("./references | ./clause") &&
25
+ warn("ISO style: normative references contains subclauses")
26
+ end
27
+
28
+ ONE_SYMBOLS_WARNING = "ISO style: only one Symbols and Abbreviated "\
29
+ "Terms section in the standard".freeze
30
+
31
+ NON_DL_SYMBOLS_WARNING = "ISO style: Symbols and Abbreviated Terms can "\
32
+ "only contain a definition list".freeze
33
+
34
+ def symbols_validate(root)
35
+ f = root.xpath("//definitions")
36
+ f.empty? && return
37
+ (f.size == 1) || warn(ONE_SYMBOLS_WARNING)
38
+ f.first.elements.each do |e|
39
+ unless e.name == "dl"
40
+ warn(NON_DL_SYMBOLS_WARNING)
41
+ return
42
+ end
43
+ end
44
+ end
45
+
46
+ def seqcheck(names, msg, accepted)
47
+ n = names.shift
48
+ unless accepted.include? n
49
+ warn "ISO style: #{msg}"
50
+ names = []
51
+ end
52
+ names
53
+ end
54
+
55
+ # spec of permissible section sequence
56
+ # we skip normative references, it goes to end of list
57
+ SEQ =
58
+ [
59
+ {
60
+ msg: "Initial section must be (content) Foreword",
61
+ val: [{ tag: "foreword", title: "Foreword" }],
62
+ },
63
+ {
64
+ msg: "Prefatory material must be followed by (clause) Scope",
65
+ val: [{ tag: "introduction", title: "Introduction" },
66
+ { tag: "clause", title: "Scope" }],
67
+ },
68
+ {
69
+ msg: "Prefatory material must be followed by (clause) Scope",
70
+ val: [{ tag: "clause", title: "Scope" }],
71
+ },
72
+ {
73
+ msg: "Normative References must be followed by "\
74
+ "Terms and Definitions",
75
+ val: [
76
+ { tag: "terms", title: "Terms and definitions" },
77
+ { tag: "clause", title: "Terms and definitions" },
78
+ {
79
+ tag: "terms",
80
+ title: "Terms, definitions, symbols and abbreviated terms",
81
+ },
82
+ {
83
+ tag: "clause",
84
+ title: "Terms, definitions, symbols and abbreviated terms",
85
+ },
86
+ ],
87
+ },
88
+ ].freeze
89
+
90
+ SECTIONS_XPATH =
91
+ "//foreword | //introduction | //sections/terms | .//annex | "\
92
+ "//sections/definitions | //sections/clause | //references[not(parent::clause)] | "\
93
+ "//clause[descendant::references][not(parent::clause)]".freeze
94
+
95
+ def sections_sequence_validate(root)
96
+ f = root.xpath(SECTIONS_XPATH)
97
+ names = f.map { |s| { tag: s.name, title: s&.at("./title")&.text } }
98
+ names = seqcheck(names, SEQ[0][:msg], SEQ[0][:val]) || return
99
+ n = names[0]
100
+ names = seqcheck(names, SEQ[1][:msg], SEQ[1][:val]) || return
101
+ if n == { tag: "introduction", title: "Introduction" }
102
+ names = seqcheck(names, SEQ[2][:msg], SEQ[2][:val]) || return
103
+ end
104
+ names = seqcheck(names, SEQ[3][:msg], SEQ[3][:val]) || return
105
+ n = names.shift
106
+ if n == { tag: "definitions", title: nil }
107
+ n = names.shift || return
108
+ end
109
+ unless n
110
+ warn "ISO style: Document must contain at least one clause"
111
+ return
112
+ end
113
+ n[:tag] == "clause" ||
114
+ warn("ISO style: Document must contain clause after "\
115
+ "Terms and Definitions")
116
+ n == { tag: "clause", title: "Scope" } &&
117
+ warn("ISO style: Scope must occur before Terms and Definitions")
118
+ n = names.shift || return
119
+ while n[:tag] == "clause"
120
+ n[:title] == "Scope" &&
121
+ warn("ISO style: Scope must occur before Terms and Definitions")
122
+ n = names.shift || return
123
+ end
124
+ unless n[:tag] == "annex" || n[:tag] == "references"
125
+ warn "ISO style: Only annexes and references can follow clauses"
126
+ end
127
+ while n[:tag] == "annex"
128
+ n = names.shift
129
+ if n.nil?
130
+ warn("ISO style: Document must include (references) "\
131
+ "Normative References")
132
+ return
133
+ end
134
+ end
135
+ n == { tag: "references", title: "Normative References" } ||
136
+ warn("ISO style: Document must include (references) "\
137
+ "Normative References")
138
+ n = names.shift
139
+ n == { tag: "references", title: "Bibliography" } ||
140
+ warn("ISO style: Final section must be (references) Bibliography")
141
+ names.empty? ||
142
+ warn("ISO style: There are sections after the final Bibliography")
143
+ end
144
+
145
+ def style_warning(node, msg, text)
146
+ return if @novalid
147
+ w = "ISO style: WARNING (#{Standoc::Utils::current_location(node)}): #{msg}"
148
+ w += ": #{text}" if text
149
+ warn w
150
+ end
151
+
152
+ NORM_ISO_WARN = "non-ISO/IEC reference not expected as normative".freeze
153
+ SCOPE_WARN = "Scope contains subclauses: should be succint".freeze
154
+
155
+ def section_style(root)
156
+ foreword_style(root.at("//foreword"))
157
+ introduction_style(root.at("//introduction"))
158
+ scope_style(root.at("//clause[title = 'Scope']"))
159
+ scope = root.at("//clause[title = 'Scope']/clause")
160
+ scope.nil? || style_warning(scope, SCOPE_WARN, nil)
161
+ end
162
+
163
+ def sourcecode_style(root)
164
+ root.xpath("//sourcecode").each do |x|
165
+ callouts = x.elements.select { |e| e.name == "callout" }
166
+ annotations = x.elements.select { |e| e.name == "annotation" }
167
+ if callouts.size != annotations.size
168
+ warn "#{x['id']}: mismatch of callouts and annotations"
169
+ end
170
+ end
171
+ end
172
+
173
+ ASSETS_TO_STYLE =
174
+ "//termsource | //formula | //termnote | //p | //li[not(p)] | "\
175
+ "//dt | //dd[not(p)] | //td[not(p)] | //th[not(p)]".freeze
176
+
177
+ NORM_BIBITEMS =
178
+ "//references[title = 'Normative References']/bibitem".freeze
179
+
180
+ def asset_title_style(root)
181
+ root.xpath("//figure[image][not(title)]").each do |node|
182
+ style_warning(node, "Figure should have title", nil)
183
+ end
184
+ root.xpath("//table[not(title)]").each do |node|
185
+ style_warning(node, "Table should have title", nil)
186
+ end
187
+ end
188
+
189
+ def norm_bibitem_style(root)
190
+ root.xpath(NORM_BIBITEMS).each do |b|
191
+ if b.at(Standoc::Converter::ISO_PUBLISHER_XPATH).nil?
192
+ Standoc::Utils::warning(b, NORM_ISO_WARN, b.text)
193
+ end
194
+ end
195
+ end
196
+
197
+ def asset_style(root)
198
+ root.xpath("//example | //termexample").each { |e| example_style(e) }
199
+ root.xpath("//definition").each { |e| definition_style(e) }
200
+ root.xpath("//note").each { |e| note_style(e) }
201
+ root.xpath("//fn").each { |e| footnote_style(e) }
202
+ root.xpath(ASSETS_TO_STYLE).each { |e| style(e, extract_text(e)) }
203
+ norm_bibitem_style(root)
204
+ super
205
+ end
206
+
207
+ def subclause_validate(root)
208
+ root.xpath("//clause/clause/clause/clause/clause/clause/clause/clause").each do |c|
209
+ style_warning(c, "Exceeds the maximum clause depth of 7", nil)
210
+ end
211
+ end
212
+ end
213
+ end
214
+ end