Class: Metanorma::Iso::Validate

Inherits:
Standoc::Validate
  • Object
show all
Defined in:
lib/metanorma/iso/validate.rb,
lib/metanorma/iso/validate_list.rb,
lib/metanorma/iso/validate_xref.rb,
lib/metanorma/iso/validate_style.rb,
lib/metanorma/iso/validate_title.rb,
lib/metanorma/iso/validate_numeric.rb,
lib/metanorma/iso/validate_section.rb,
lib/metanorma/iso/validate_requirements.rb

Constant Summary collapse

COMMITTEE_XPATH =
<<~XPATH.freeze
  //contributor[role/description = 'committee']/organization/subdivision
XPATH
ASSETS_TO_STYLE =
"//term//source | //formula | //termnote | " \
"//p[not(ancestor::boilerplate)] | //li[not(p)] | //dt | " \
"//dd[not(p)] | //td[not(p)] | //th[not(p)]".freeze
SI_UNIT =

leaving out as problematic: N J K C S T H h d B o E

"(m|cm|mm|km|μm|nm|g|kg|mgmol|cd|rad|sr|Hz|Hz|MHz|Pa|hPa|kJ|" \
"V|kV|W|MW|kW|F|μF|Ω|Wb|°C|lm|lx|Bq|Gy|Sv|kat|l|t|eV|u|Np|Bd|" \
"bit|kB|MB|Hart|nat|Sh|var)".freeze
NONSTD_UNITS =
{
  sec: "s", mins: "min", hrs: "h", hr: "h", cc: "cm^3",
  lit: "l", amp: "A", amps: "A", rpm: "r/min"
}.freeze
SEQ =

spec of permissible section sequence we skip normative references, it goes to end of list

[
  { msg: "Initial section must be (content) Foreword",
    val: ["./self::foreword"] },
  { msg: "Prefatory material must be followed by (clause) Scope",
    val: ["./self::introduction", "./self::clause[@type = 'scope']"] },
  { msg: "Prefatory material must be followed by (clause) Scope",
    val: ["./self::clause[@type = 'scope']"] },
  { msg: "Normative References must be followed by " \
         "Terms and Definitions",
    val: ["./self::terms | .//terms"] },
].freeze
SECTIONS_XPATH =
"//foreword | //introduction | //sections/terms | .//annex | " \
"//sections/definitions | //sections/clause | " \
"//references[not(parent::clause)] | " \
"//clause[descendant::references][not(parent::clause)]".freeze
NORM_BIBITEMS =
"//references[@normative = 'true']/bibitem".freeze
ISO_PUBLISHER_XPATH =
<<~XPATH.freeze
  ./contributor[role/@type = 'publisher']/organization[abbreviation = 'ISO' or abbreviation = 'IEC' or name = 'International Organization for Standardization' or name = 'International Electrotechnical Commission']
XPATH
REQUIREMENT_RE_STR =
<<~REGEXP.freeze
  \\b
   ( shall | (is|are)_to |
     (is|are)_required_(not_)?to |
     (is|are)_required_that |
     has_to |
     only\\b[^.,]+\\b(is|are)_permitted |
     it_is_necessary |
     (is|are)_not_(allowed | permitted |
                   acceptable | permissible) |
     (is|are)_not_to_be |
     [.,:;]_do_not )
  \\b
REGEXP
RECOMMENDATION_RE_STR =
<<~REGEXP.freeze
  \\b
      should |
      ought_(not_)?to |
      it_is_(not_)?recommended_that
  \\b
REGEXP
PERMISSION_RE_STR =
<<~REGEXP.freeze
  \\b
       may |
      (is|are)_(permitted | allowed | permissible ) |
      it_is_not_required_that |
      no\\b[^.,]+\\b(is|are)_required
  \\b
REGEXP
POSSIBILITY_RE_STR =
<<~REGEXP.freeze
  \\b
     can | cannot | be_able_to |
     there_is_a_possibility_of |
     it_is_possible_to | be_unable_to |
     there_is_no_possibility_of |
     it_is_not_possible_to
  \\b
REGEXP
AMBIG_WORDS_RE_STR =
<<~REGEXP.freeze
  \\b(
      need_to | needs_to | might | could | family_of_standards | suite_of_standards
  )\\b
REGEXP
MISSPELLED_WORDS_RE_STR =
<<~REGEXP.freeze
  \\b(
      on-line | cyber_security | cyber-security
  )\\b
REGEXP

Instance Method Summary collapse

Instance Method Details

#ambig_words_check(text) ⇒ Object



99
100
101
102
103
104
105
106
# File 'lib/metanorma/iso/validate_requirements.rb', line 99

def ambig_words_check(text)
  @lang == "en" or return
  re = str_to_regexp(self.class::AMBIG_WORDS_RE_STR)
  text.gsub(/\s+/, " ").split(/\.\s+/).each do |t|
    return t if re.match t
  end
  nil
end

#asset_style(root) ⇒ Object



154
155
156
157
158
159
160
161
162
163
164
# File 'lib/metanorma/iso/validate_style.rb', line 154

def asset_style(root)
  root.xpath("//example | //termexample").each { |e| example_style(e) }
  root.xpath("//definition/verbal-definition").each do |e|
    definition_style(e)
  end
  root.xpath("//note").each { |e| note_style(e) }
  root.xpath("//fn").each { |e| footnote_style(e) }
  root.xpath(ASSETS_TO_STYLE).each { |e| style(e, extract_text(e)) }
  norm_bibitem_style(root)
  super
end

#bibdata_validate(doc) ⇒ Object



67
68
69
70
# File 'lib/metanorma/iso/validate.rb', line 67

def bibdata_validate(doc)
  doctype_validate(doc)
  iteration_validate(doc)
end

#bibitem_validate(xmldoc) ⇒ Object



106
107
108
109
110
111
112
# File 'lib/metanorma/iso/validate.rb', line 106

def bibitem_validate(xmldoc)
  xmldoc.xpath("//bibitem[date/on = '–']").each do |b|
    n = b.xpath("./note/@type").map { |n| n.text.split(",").map(&:strip) }
      .flatten
    n.include?("Unpublished-Status") or @log.add("ISO_8", b)
  end
end

#content_validate(doc) ⇒ Object



88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/metanorma/iso/validate.rb', line 88

def content_validate(doc)
  super
  root = doc.root
  title_validate(root)
  isosubgroup_validate(root)
  termdef_style(root)
  iso_xref_validate(root)
  bibdata_validate(root)
  bibitem_validate(root)
  figure_validate(root)
  list_validate(doc)
end

#copied_instance_variablesObject



15
16
17
# File 'lib/metanorma/iso/validate.rb', line 15

def copied_instance_variables
  super + %i[amd vocab validate_years]
end

#definition_style(node) ⇒ Object

ISO/IEC DIR 2, 16.5.6



40
41
42
43
44
45
# File 'lib/metanorma/iso/validate_style.rb', line 40

def definition_style(node)
  @novalid and return
  r = requirement_check(extract_text(node)) and
    style_warning(node, "Definition may contain requirement", r,
                  display: false)
end

#doctype_validate(_xmldoc) ⇒ Object



52
53
54
55
56
57
58
59
# File 'lib/metanorma/iso/validate.rb', line 52

def doctype_validate(_xmldoc)
  %w(international-standard technical-specification technical-report
     publicly-available-specification international-workshop-agreement
     guide amendment technical-corrigendum committee-document addendum
     recommendation)
    .include? @doctype or
    @log.add("ISO_5", nil, params: [@doctype])
end

#eref_style_punct(node) ⇒ Object



134
135
136
137
138
139
140
# File 'lib/metanorma/iso/validate_style.rb', line 134

def eref_style_punct(node)
  node.xpath(".//eref[@type='footnote']").each do |e|
    /^\p{P}/.match?(e.next&.text) or next
    style_warning(node, "superscript cross-reference followed by punctuation",
                  node.to_xml)
  end
end

#example_style(node) ⇒ Object

ISO/IEC DIR 2, 16.5.7 ISO/IEC DIR 2, 25.5



49
50
51
52
53
# File 'lib/metanorma/iso/validate_style.rb', line 49

def example_style(node)
  @novalid and return
  style_no_guidance(node, extract_text(node), "Example")
  style(node, extract_text(node))
end

#external_constraint(text) ⇒ Object



86
87
88
89
90
91
# File 'lib/metanorma/iso/validate_requirements.rb', line 86

def external_constraint(text)
  text.split(/\.\s+/).each do |t|
    return t if /\b(must)\b/xi.match? t
  end
  nil
end

#extract_anchor_norm(root) ⇒ Object



18
19
20
21
22
23
24
25
26
27
28
# File 'lib/metanorma/iso/validate_xref.rb', line 18

def extract_anchor_norm(root)
  nodes = root.xpath("//annex[@obligation = 'normative'] | " \
    "//references[@obligation = 'normative']")
  ret = nodes.each_with_object({}) do |n, m|
    n["anchor"] and m[n["anchor"]] = true
  end
  nodes.each do |n|
    n.xpath(".//*[@anchor]").each { |n1| ret[n1["anchor"]] = true }
  end
  ret
end

#extract_bibitem_anchors(root) ⇒ Object



47
48
49
50
51
52
53
54
55
56
57
# File 'lib/metanorma/iso/validate_xref.rb', line 47

def extract_bibitem_anchors(root)
  ret = root.xpath("//references[@normative = 'true']//bibitem")
    .each_with_object({}) do |b, m|
    m[b["anchor"]] = { bib: b, norm: true }
  end
  root.xpath("//references[not(@normative = 'true')]//bibitem")
    .each do |b|
    ret[b["anchor"]] = { bib: b, norm: false }
  end
  ret
end

#extract_text(node) ⇒ Object



8
9
10
11
12
13
14
15
16
# File 'lib/metanorma/iso/validate_style.rb', line 8

def extract_text(node)
  node.nil? and return ""
  node1 = Nokogiri::XML.fragment(node.to_s)
  node1.xpath(".//link | .//locality | .//localityStack | " \
              ".//stem | .//sourcecode").each(&:remove)
  ret = ""
  node1.traverse { |x| ret += x.text if x.text? }
  HTMLEntities.new.decode(ret)
end

#figure_validate(xmldoc) ⇒ Object



84
85
86
# File 'lib/metanorma/iso/validate.rb', line 84

def figure_validate(xmldoc)
  subfigure_validate(xmldoc)
end

#footnote_style(node) ⇒ Object

ISO/IEC DIR 2, 26.5



63
64
65
66
67
# File 'lib/metanorma/iso/validate_style.rb', line 63

def footnote_style(node)
  @novalid and return
  style_no_guidance(node, extract_text(node), "Footnote")
  style(node, extract_text(node))
end

#foreword_style(_node) ⇒ Object

ISO/IEC DIR 2, 12.2 Annulled, ISO’s own foreword text fails this test



20
21
22
23
# File 'lib/metanorma/iso/validate_style.rb', line 20

def foreword_style(_node)
  @novalid and return
  # style_no_guidance(node, extract_text(node), "Foreword")
end

#foreword_validate(root) ⇒ Object

ISO/IEC DIR 2, 12.4



22
23
24
25
26
# File 'lib/metanorma/iso/validate_section.rb', line 22

def foreword_validate(root)
  f = root.at("//foreword") || return
  s = f.at("./clause")
  @log.add("ISO_23", f) unless s.nil?
end

#introduction_style(node) ⇒ Object

ISO/IEC DIR 2, 13.2



32
33
34
35
36
37
# File 'lib/metanorma/iso/validate_style.rb', line 32

def introduction_style(node)
  @novalid and return
  r = requirement_check(extract_text(node)) and
    style_warning(node, "Introduction may contain requirement", r,
                  display: false)
end

#iso_xref_validate(doc) ⇒ Object



111
112
113
114
115
116
117
# File 'lib/metanorma/iso/validate_xref.rb', line 111

def iso_xref_validate(doc)
  see_xrefs_validate(doc)
  term_xrefs_validate(doc)
  xrefs_mandate_validate(doc)
  see_erefs_validate(doc)
  locality_erefs_validate(doc)
end

#isosubgroup_validate(root) ⇒ Object



23
24
25
26
27
28
29
30
31
32
33
34
# File 'lib/metanorma/iso/validate.rb', line 23

def isosubgroup_validate(root)
  root.xpath("#{COMMITTEE_XPATH}[@type = 'Technical committee']/@subtype")
    .each do |t|
    %w{TC PC JTC JPC}.include?(t.text) or
      @log.add("ISO_2", nil, params: [t.text])
  end
  root.xpath("#{COMMITTEE_XPATH}[@type = 'Subcommittee']/@subtype")
    .each do |t|
    %w{SC JSC}.include?(t.text) or
      @log.add("ISO_3", nil, params: [t.text])
  end
end

#iteration_validate(xmldoc) ⇒ Object



61
62
63
64
65
# File 'lib/metanorma/iso/validate.rb', line 61

def iteration_validate(xmldoc)
  iteration = xmldoc&.at("//bibdata/status/iteration")&.text or return
  /^\d+/.match(iteration) or
    @log.add("ISO_6", nil, params: [iteration])
end

#li_depth_validate(doc) ⇒ Object



24
25
26
27
28
29
# File 'lib/metanorma/iso/validate_list.rb', line 24

def li_depth_validate(doc)
  doc.xpath("//li//li//li//li").each do |l|
    l.at(".//li") and
      style_warning(l, "List more than four levels deep", nil)
  end
end

#list_after_colon_punctuation(list, entries) ⇒ Object

if first list entry starts lowercase, treat as sentence broken up



69
70
71
72
73
74
75
76
77
78
# File 'lib/metanorma/iso/validate_list.rb', line 69

def list_after_colon_punctuation(list, entries)
  lower = starts_lowercase?(list.at(".//li").text)
  entries.each_with_index do |li, i|
    if lower
      list_semicolon_phrase(li, i == entries.size - 1)
    else
      list_full_sentence(li)
    end
  end
end

#list_full_sentence(elem) ⇒ Object



102
103
104
105
106
107
108
109
110
111
112
# File 'lib/metanorma/iso/validate_list.rb', line 102

def list_full_sentence(elem)
  %w(Cyrl Latn Grek).include?(@script) or return
  text = elem.text.strip
  starts_uppercase?(text) || starts_numeric?(elem) or
    style_warning(elem, "List entry of separate sentences must start " \
                        "with uppercase letter", text, display: false)
  punct = text.strip.sub(/^.*?(\S)\z/m, "\\1")
  punct == "." or
    style_warning(elem, "List entry of separate sentences must " \
                        "end with full stop", text, display: false)
end

#list_punctuation(doc) ⇒ Object



32
33
34
35
36
37
38
39
40
41
42
# File 'lib/metanorma/iso/validate_list.rb', line 32

def list_punctuation(doc)
  return if @novalid

  ((doc.xpath("//ol") - doc.xpath("//ul//ol | //ol//ol")) +
   (doc.xpath("//ul") - doc.xpath("//ul//ul | //ol//ul"))).each do |list|
    next if skip_list_punctuation(list)

    prec = list.at("./preceding::text()[normalize-space(.) != ''][1]")
    list_punctuation1(list, prec&.text)
  end
end

#list_punctuation1(list, prectext) ⇒ Object



56
57
58
59
60
61
62
63
64
65
66
# File 'lib/metanorma/iso/validate_list.rb', line 56

def list_punctuation1(list, prectext)
  prectext ||= ""
  entries = list.xpath(".//li")
  %w(Cyrl Latn Grek).include?(@script) or return
  case prectext.strip[-1]
  when ":", "" then list_after_colon_punctuation(list, entries)
  when "." then entries.each { |li| list_full_sentence(li) }
  else style_warning(list, "All lists must be preceded by " \
                           "colon or full stop", prectext, display: false)
  end
end

#list_semicolon_phrase(elem, last) ⇒ Object



80
81
82
83
84
85
86
# File 'lib/metanorma/iso/validate_list.rb', line 80

def list_semicolon_phrase(elem, last)
  text = elem.text.strip
  starts_lowercase?(text) or
    style_warning(elem, "List entry of broken up sentence must start " \
                        "with lowercase letter", text, display: false)
  list_semicolon_phrase_punct(elem, text, last)
end

#list_semicolon_phrase_punct(elem, text, last) ⇒ Object



88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/metanorma/iso/validate_list.rb', line 88

def list_semicolon_phrase_punct(elem, text, last)
  punct = text.strip.sub(/^.*?(\S)\z/m, "\\1")
  if last
    punct == "." or
      style_warning(elem, "Final list entry of broken up " \
                          "sentence must end with full stop", text,
                    display: false)
  else
    punct == ";" or
      style_warning(elem, "List entry of broken up sentence must " \
                          "end with semicolon", text, display: false)
  end
end

#list_validate(doc) ⇒ Object



101
102
103
104
# File 'lib/metanorma/iso/validate.rb', line 101

def list_validate(doc)
  listcount_validate(doc)
  list_punctuation(doc)
end

#listcount_validate(doc) ⇒ Object



5
6
7
8
9
10
# File 'lib/metanorma/iso/validate_list.rb', line 5

def listcount_validate(doc)
  return if @novalid

  ol_count_validate(doc)
  li_depth_validate(doc)
end

#locality_erefs_validate(root) ⇒ Object

ISO/IEC DIR 2, 10.4



60
61
62
63
64
65
66
67
# File 'lib/metanorma/iso/validate_xref.rb', line 60

def locality_erefs_validate(root)
  root.xpath("//eref[descendant::locality]").each do |t|
    if /^(ISO|IEC)/.match?(t["citeas"]) &&
        !/: ?(\d+{4}|–)$/.match?(t["citeas"])
      @log.add("ISO_49", t, params: [t["citeas"]])
    end
  end
end

#misspelled_words_check(text) ⇒ Object



114
115
116
117
118
119
120
121
# File 'lib/metanorma/iso/validate_requirements.rb', line 114

def misspelled_words_check(text)
  @lang == "en" or return
  re = str_to_regexp(self.class::MISSPELLED_WORDS_RE_STR)
  text.gsub(/\s+/, " ").split(/\.\s+/).each do |t|
    return t if re.match t
  end
  nil
end

#norm_bibitem_style(root) ⇒ Object

ISO/IEC DIR 2, 10.2



185
186
187
188
189
190
191
# File 'lib/metanorma/iso/validate_section.rb', line 185

def norm_bibitem_style(root)
  root.xpath(NORM_BIBITEMS).each do |b|
    if b.at(ISO_PUBLISHER_XPATH).nil?
      @log.add("ISO_42", b, params: [b.text])
    end
  end
end

#normref_validate(root) ⇒ Object

ISO/IEC DIR 2, 15.4



29
30
31
32
33
# File 'lib/metanorma/iso/validate_section.rb', line 29

def normref_validate(root)
  f = root.at("//references[@normative = 'true']") || return
  f.at("./references | ./clause") &&
    @log.add("ISO_24", f)
end

#note_style(node) ⇒ Object

ISO/IEC DIR 2, 24.5



56
57
58
59
60
# File 'lib/metanorma/iso/validate_style.rb', line 56

def note_style(node)
  @novalid and return
  style_no_guidance(node, extract_text(node), "Note")
  style(node, extract_text(node))
end

#ol_count_validate(doc) ⇒ Object



12
13
14
15
16
17
18
19
20
21
22
# File 'lib/metanorma/iso/validate_list.rb', line 12

def ol_count_validate(doc)
  doc.xpath("//clause | //annex").each do |c|
    next if c.xpath(".//ol").empty?

    ols = c.xpath(".//ol") -
      c.xpath(".//ul//ol | .//ol//ol | .//clause//ol")
    ols.size > 1 and
      style_warning(c, "More than 1 ordered list in a numbered clause",
                    nil)
  end
end

#onlychild_clause_validate(root) ⇒ Object

ISO/IEC DIR 2, 22.3.2



201
202
203
204
205
206
# File 'lib/metanorma/iso/validate_section.rb', line 201

def onlychild_clause_validate(root)
  root.xpath(Standoc::Utils::SUBCLAUSE_XPATH).each do |c|
    c.xpath("../clause").size == 1 or next
    @log.add("ISO_43", c)
  end
end

#permission_check(text) ⇒ Object



60
61
62
63
64
65
66
67
# File 'lib/metanorma/iso/validate_requirements.rb', line 60

def permission_check(text)
  @lang == "en" or return
  re = str_to_regexp(self.class::PERMISSION_RE_STR)
  text.gsub(/\s+/, " ").split(/\.\s+/).each do |t|
    return t if re.match t
  end
  nil
end

#possibility_check(text) ⇒ Object



79
80
81
82
83
84
# File 'lib/metanorma/iso/validate_requirements.rb', line 79

def possibility_check(text)
  @lang == "en" or return
  re = str_to_regexp(self.class::POSSIBILITY_RE_STR)
  text.gsub(/\s+/, " ").split(/\.\s+/).each { |t| return t if re.match t }
  nil
end

#recommendation_check(text) ⇒ Object



42
43
44
45
46
47
48
49
# File 'lib/metanorma/iso/validate_requirements.rb', line 42

def recommendation_check(text)
  @lang == "en" or return
  re = str_to_regexp(self.class::RECOMMENDATION_RE_STR)
  text.gsub(/\s+/, " ").split(/\.\s+/).each do |t|
    return t if re.match t
  end
  nil
end

#requirement_check(text) ⇒ Object



25
26
27
28
29
30
31
32
# File 'lib/metanorma/iso/validate_requirements.rb', line 25

def requirement_check(text)
  @lang == "en" or return
  re = str_to_regexp(self.class::REQUIREMENT_RE_STR)
  text.gsub(/\s+/, " ").split(/\.\s+/).each do |t|
    return t if re.match t
  end
  nil
end

#schema_fileObject



114
115
116
117
118
119
120
# File 'lib/metanorma/iso/validate.rb', line 114

def schema_file
  case @doctype
  when "amendment", "technical-corrigendum" # @amd
    "isostandard-amd.rng"
  else "isostandard-compile.rng"
  end
end

#scope_style(node) ⇒ Object

ISO/IEC DIR 2, 14.2



26
27
28
29
# File 'lib/metanorma/iso/validate_style.rb', line 26

def scope_style(node)
  @novalid and return
  style_no_guidance(node, extract_text(node), "Scope")
end

#section_style(root) ⇒ Object



157
158
159
160
161
162
163
164
165
# File 'lib/metanorma/iso/validate_section.rb', line 157

def section_style(root)
  foreword_style(root.at("//foreword"))
  introduction_style(root.at("//introduction"))
  scope_style(root.at("//clause[@type = 'scope']"))
  scope = root.at("//clause[@type = 'scope']/clause")
  # ISO/IEC DIR 2, 14.4
  scope.nil? || @log.add("ISO_39", scope)
  tech_report_style(root)
end

#section_validate(doc) ⇒ Object



6
7
8
9
10
11
12
13
14
15
16
17
18
19
# File 'lib/metanorma/iso/validate_section.rb', line 6

def section_validate(doc)
  unless %w(amendment technical-corrigendum).include? @doctype
    foreword_validate(doc.root)
    normref_validate(doc.root)
    symbols_validate(doc.root)
    sections_presence_validate(doc.root)
    sections_sequence_validate(doc.root)
  end
  section_style(doc.root)
  subclause_validate(doc.root)
  onlychild_clause_validate(doc.root)
  @vocab and vocab_terms_titles_validate(doc.root)
  super
end

#sections_presence_validate(root) ⇒ Object



59
60
61
62
63
64
65
66
# File 'lib/metanorma/iso/validate_section.rb', line 59

def sections_presence_validate(root)
  root.at("//sections/clause[@type = 'scope']") or
    @log.add("ISO_29", nil)
  root.at("//references[@normative = 'true']") or
    @log.add("ISO_30", nil)
  root.at("//terms") or
    @log.add("ISO_31", nil)
end

#sections_sequence_validate(root) ⇒ Object



88
89
90
91
92
93
94
95
96
# File 'lib/metanorma/iso/validate_section.rb', line 88

def sections_sequence_validate(root)
  names, n = sections_sequence_validate_start(root)
  if @vocab
    names, n = sections_sequence_validate_body_vocab(names, n)
  else
    names, n = sections_sequence_validate_body(names, n)
  end
  sections_sequence_validate_end(names, n)
end

#sections_sequence_validate_body(names, elem) ⇒ Object



111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/metanorma/iso/validate_section.rb', line 111

def sections_sequence_validate_body(names, elem)
  if elem.nil? || elem.name != "clause"
    @log.add("ISO_32", elem)
  end
  elem&.at("./self::clause") or
    @log.add("ISO_33", elem)
  elem&.at("./self::clause[@type = 'scope']") and
    @log.add("ISO_34", elem)
  elem = names.shift
  while elem&.name == "clause"
    elem&.at("./self::clause[@type = 'scope']") and
      @log.add("ISO_34", elem)
    elem = names.shift
  end
  %w(annex references).include? elem&.name or
    @log.add("ISO_36", elem)
  [names, elem]
end

#sections_sequence_validate_body_vocab(names, elem) ⇒ Object



130
131
132
133
134
135
136
137
# File 'lib/metanorma/iso/validate_section.rb', line 130

def sections_sequence_validate_body_vocab(names, elem)
  while elem && %w(clause terms).include?(elem.name)
    elem = names.shift
  end
  %w(annex references).include? elem&.name or
    @log.add("ISO_37", elem)
  [names, elem]
end

#sections_sequence_validate_end(names, elem) ⇒ Object



139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
# File 'lib/metanorma/iso/validate_section.rb', line 139

def sections_sequence_validate_end(names, elem)
  while elem&.name == "annex"
    elem = names.shift
    if elem.nil?
      @log.add("ISO_38", nil)
    end
  end
  elem.nil? and return
  elem&.at("./self::references[@normative = 'true']") ||
    @log.add("ISO_38", nil)
  elem = names&.shift
  elem.nil? and return
  elem&.at("./self::references[@normative = 'false']") ||
    @log.add("ISO_40", elem)
  names.empty? ||
    @log.add("ISO_41", elem)
end

#sections_sequence_validate_start(root) ⇒ Object



98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/metanorma/iso/validate_section.rb', line 98

def sections_sequence_validate_start(root)
  names = root.xpath(SECTIONS_XPATH)
  names = seqcheck(names, SEQ[0][:msg], SEQ[0][:val])
  n = names[0]
  names = seqcheck(names, SEQ[1][:msg], SEQ[1][:val])
  n&.at("./self::introduction") and
    names = seqcheck(names, SEQ[2][:msg], SEQ[2][:val])
  names = seqcheck(names, SEQ[3][:msg], SEQ[3][:val])
  n = names.shift
  n = names.shift if n&.at("./self::definitions")
  [names, n]
end

#see_erefs_validate(root) ⇒ Object

ISO/IEC DIR 2, 15.5.3



31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# File 'lib/metanorma/iso/validate_xref.rb', line 31

def see_erefs_validate(root)
  @lang == "en" or return
  bibitemids = extract_bibitem_anchors(root)
  root.xpath("//eref").each do |t|
    prec = t.at("./preceding-sibling::text()[last()]")
    !prec.nil? && /\b(see|refer to)\p{Zs}*\Z/mi.match(prec) or next
    unless target = bibitemids[t["bibitemid"]]
      # unless target = root.at("//bibitem[@anchor = '#{t['bibitemid']}']")
      @log.add("ISO_47", t, params: [t])
      next
    end
    target[:norm] and
      @log.add("ISO_48", t, params: [t])
  end
end

#see_xrefs_validate(root) ⇒ Object

ISO/IEC DIR 2, 15.5.3, 20.2 does not deal with preceding text marked up



6
7
8
9
10
11
12
13
14
15
16
# File 'lib/metanorma/iso/validate_xref.rb', line 6

def see_xrefs_validate(root)
  @lang == "en" or return
  anchors = extract_anchor_norm(root)
  root.xpath("//xref").each do |t|
    preceding = t.at("./preceding-sibling::text()[last()]")
    !preceding.nil? &&
      /\b(see| refer to)\p{Zs}*\Z/mi.match(preceding) or next
    anchors[t["target"]] and
      @log.add("ISO_46", t, params: [t["target"]])
  end
end

#seqcheck(names, msg, accepted) ⇒ Object



48
49
50
51
52
53
54
55
56
57
# File 'lib/metanorma/iso/validate_section.rb', line 48

def seqcheck(names, msg, accepted)
  n = names.shift
  return [] if n.nil?

  test = accepted.map { |a| n.at(a) }
  if test.all?(&:nil?)
    @log.add("ISO_28", nil, params: [msg])
  end
  names
end

#skip_list_punctuation(list) ⇒ Object



44
45
46
47
48
49
50
51
52
53
54
# File 'lib/metanorma/iso/validate_list.rb', line 44

def skip_list_punctuation(list)
  return true if list.at("./ancestor::table")
  return true if list.at("./following-sibling::term") # terms boilerplate

  list.xpath(".//li").each do |entry|
    l = entry.dup
    l.xpath(".//ol | .//ul").each(&:remove)
    l.text.split.size > 2 and return false
  end
  true
end

#starts_lowercase?(text) ⇒ Boolean

allow that all-caps word (acronym) is agnostic as to lowercase

Returns:

  • (Boolean)


115
116
117
118
# File 'lib/metanorma/iso/validate_list.rb', line 115

def starts_lowercase?(text)
  text.match?(/^[^[[:upper:]][[:lower:]]]*[[:lower:]]/) ||
    text.match?(/^[^[[:upper:]][[:lower:]]]*[[:upper:]][[:upper:]]+[^[[:alpha:]]]/)
end

#starts_numeric?(elem) ⇒ Boolean

Returns:

  • (Boolean)


124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/metanorma/iso/validate_list.rb', line 124

def starts_numeric?(elem)
  traverse_breadth_first(elem) do |n|
    n.name == "stem" and return true
    %w(xref eref).include?(n.name) &&
      n.text.strip.empty? and return true
    n.text? or next
    t = n.text.strip
    t.empty? and next
    return /^\d/.match?(t)
  end
  false
end

#starts_uppercase?(text) ⇒ Boolean

Returns:

  • (Boolean)


120
121
122
# File 'lib/metanorma/iso/validate_list.rb', line 120

def starts_uppercase?(text)
  text.match?(/^[^[[:upper:]][[:lower:]]]*[[:upper:]]/)
end

#str_to_regexp(str) ⇒ Object



21
22
23
# File 'lib/metanorma/iso/validate_requirements.rb', line 21

def str_to_regexp(str)
  Regexp.new(str.gsub(/\s/, "").gsub("_", "\\s"), Regexp::IGNORECASE)
end

#style(node, text) ⇒ Object



87
88
89
90
91
92
93
94
95
96
# File 'lib/metanorma/iso/validate_style.rb', line 87

def style(node, text)
  @novalid and return
  @novalid_number or style_number(node, text)
  style_percent(node, text)
  style_abbrev(node, text)
  style_units(node, text)
  style_punct(node, text)
  style_subscript(node)
  style_problem_words(node, text)
end

#style_abbrev(node, text) ⇒ Object

ISO/IEC DIR 2, 8.4 ISO/IEC DIR 2, 9.3



115
116
117
118
119
120
121
# File 'lib/metanorma/iso/validate_style.rb', line 115

def style_abbrev(node, text)
  style_regex(/(?:\A|\p{Zs})(?!e\.g\.|i\.e\.)
              (?<num>[a-z]{1,2}\.(?:[a-z]{1,2}|\.))\b/ix,
              "no dots in abbreviations", node, text)
  style_regex(/\d\s*(?<num>ppm)\b/i,
              "language-specific abbreviation of unit (ppm): ISO/IEC DIR 2, 9.3", node, text)
end

#style_no_guidance(node, text, docpart) ⇒ Object



123
124
125
126
127
128
129
130
131
132
133
134
# File 'lib/metanorma/iso/validate_requirements.rb', line 123

def style_no_guidance(node, text, docpart)
  @lang == "en" or return
  r = requirement_check(text) and
    style_warning(node, "#{docpart} may contain requirement", r,
                  display: false)
  r = permission_check(text) and
    style_warning(node, "#{docpart} may contain permission", r,
                  display: false)
  r = recommendation_check(text) and
    style_warning(node, "#{docpart} may contain recommendation", r,
                  display: false)
end

#style_non_std_units(node, text) ⇒ Object

ISO/IEC DIR 2, 9.3



98
99
100
101
102
103
# File 'lib/metanorma/iso/validate_numeric.rb', line 98

def style_non_std_units(node, text)
  NONSTD_UNITS.each do |k, v|
    style_regex(/\b(?<num>[0-9][0-9,]*\p{Zs}+#{k})\b/,
                "non-standard unit (should be #{v})", node, text)
  end
end

#style_number(node, text) ⇒ Object

ISO/IEC DIR 2, 9.1 ISO/IEC DIR 2, Table B.1 www.iso.org/ISO-house-style.html#iso-hs-s-text-r-n-numbers



42
43
44
45
46
47
48
49
50
51
# File 'lib/metanorma/iso/validate_numeric.rb', line 42

def style_number(node, text)
  style_number_grouping(node, text)
  style_regex(/(?:^|\p{Zs})(?<num>[0-9]+\.[0-9]+)(?!\.[0-9])/i,
              "possible decimal point: mark up numbers with stem:[]", node, text)
  @lang == "en" and style_regex(/\b(?<num>billions?)\b/i,
                                "ambiguous number", node, text)
  style_regex(/(?:^|\p{Zs})(?<num>-[0-9][0-9,.]*)/i,
              "hyphen instead of minus sign U+2212", node, text)
  @novalid_number = true
end

#style_number_grouping(node, text) ⇒ Object



53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/metanorma/iso/validate_numeric.rb', line 53

def style_number_grouping(node, text)
  if @validate_years
    style_two_regex_not_prev(
      node, text, /^(?<num>-?[0-9]{4,}[,0-9]*)\Z/,
      %r{\b(ISO|IEC|IEEE|(in|January|February|March|April|May|June|August|September|October|November|December)\b)\Z},
      "number not broken up in threes: mark up numbers with stem:[]"
    )
  else
    style_two_regex_not_prev(
      node, text, /^(?<num>-?(?:[0-9]{5,}[,0-9]*|[03-9]\d\d\d|1[0-8]\d\d|2[1-9]\d\d|20[5-9]\d))\Z/,
      %r{\b(ISO|IEC|IEEE|\b)\Z},
      "number not broken up in threes: mark up numbers with stem:[]"
    )
  end
end

#style_percent(node, text) ⇒ Object

ISO/IEC DIR 2, 9.2.1



70
71
72
73
74
75
# File 'lib/metanorma/iso/validate_numeric.rb', line 70

def style_percent(node, text)
  style_regex(/\b(?<num>[0-9.,]+%)/,
              "no space before percent sign", node, text)
  style_regex(/\b(?<num>[0-9.,]+ \u00b1 [0-9,.]+ %)/,
              "unbracketed tolerance before percent sign", node, text)
end

#style_problem_words(node, text) ⇒ Object



103
104
105
106
107
108
109
110
111
# File 'lib/metanorma/iso/validate_style.rb', line 103

def style_problem_words(node, text)
  r = ambig_words_check(text) and
    style_warning(node, "may contain ambiguous provision", r,
                  display: false)
  r = misspelled_words_check(text) and
    style_warning(node, "dispreferred spelling", r,
                  display: false)
  style_regex(/\b(?<num>billions?)\b/i, "ambiguous number", node, text)
end

#style_punct(node, text) ⇒ Object



125
126
127
128
129
130
131
# File 'lib/metanorma/iso/validate_style.rb', line 125

def style_punct(node, text)
  @lang == "en" and style_regex(/\b(?<num>and\/?or)\b/i,
                                "Use 'either x or y, or both'", node, text)
  style_regex(/\p{Zs}(?<num>&)\p{Zs}/i,
              "Avoid ampersand in ordinary text'", node, text)
  eref_style_punct(node)
end

#style_regex(regex, warning, node, text) ⇒ Object



69
70
71
# File 'lib/metanorma/iso/validate_style.rb', line 69

def style_regex(regex, warning, node, text)
  (m = regex.match(text)) && style_warning(node, warning, m[:num])
end

#style_subscript(node) ⇒ Object



5
6
7
8
# File 'lib/metanorma/iso/validate_numeric.rb', line 5

def style_subscript(node)
  style_subscript_proper(node)
  style_subscript_mathml(node)
end

#style_subscript_mathml(node) ⇒ Object

Check MathML subscripts - only topmost level msubs (no msub ancestors)



25
26
27
28
29
30
31
32
33
34
35
36
37
# File 'lib/metanorma/iso/validate_numeric.rb', line 25

def style_subscript_mathml(node)
  node.xpath(".//m:msub[not(ancestor::m:msub)]",
             "m" => MATHML_NS).each do |x|
    depth = calculate_mathml_subscript_depth(x)
    depth < 2 and next # No warning for single level subscripts
    if [2, 3].include?(depth)
      style_warning(node, "may contain nested subscripts", x.to_xml)
    else # depth > 3
      style_warning(node, "no more than 3 levels of subscript nesting allowed",
                    x.to_xml)
    end
  end
end

#style_subscript_proper(node) ⇒ Object

Check HTML subscripts - only topmost level subs (no sub ancestors)



11
12
13
14
15
16
17
18
19
20
21
22
# File 'lib/metanorma/iso/validate_numeric.rb', line 11

def style_subscript_proper(node)
  node.xpath(".//sub[not(ancestor::sub)]").each do |x|
    depth = calculate_subscript_depth(x)
    depth < 2 and next # No warning for single level subscripts
    if [2, 3].include?(depth)
      style_warning(node, "may contain nested subscripts", x.to_xml)
    else # depth >= 3
      style_warning(node, "no more than 3 levels of subscript nesting allowed",
                    x.to_xml)
    end
  end
end

#style_two_regex_not_prev(n, text, regex, re_prev, warning) ⇒ Object

style check with a regex on a token and a negative match on its preceding token



75
76
77
78
79
80
81
82
83
84
85
# File 'lib/metanorma/iso/validate_style.rb', line 75

def style_two_regex_not_prev(n, text, regex, re_prev, warning)
  text.nil? and return
  arr = Tokenizer::WhitespaceTokenizer.new.tokenize(text)
  arr.each_index do |i|
    m = regex.match arr[i]
    m_prev = i.zero? ? nil : re_prev.match(arr[i - 1])
    if !m.nil? && m_prev.nil?
      style_warning(n, warning, m[:num])
    end
  end
end

#style_units(node, text) ⇒ Object

ISO/IEC DIR 2, 9.3



83
84
85
86
87
88
89
90
# File 'lib/metanorma/iso/validate_numeric.rb', line 83

def style_units(node, text)
  style_regex(/\b(?<num>[0-9][0-9,]*\p{Zs}+[\u00b0\u2032\u2033])/,
              "space between number and degrees/minutes/seconds",
              node, text)
  style_regex(/(?<![A-Za-z0-9])(?<num>[1-9][0-9,]*#{SI_UNIT})\b/o,
              "no space between number and SI unit", node, text)
  style_non_std_units(node, text)
end

#style_warning(node, msg, text = nil, display: true) ⇒ Object



142
143
144
145
146
147
# File 'lib/metanorma/iso/validate_style.rb', line 142

def style_warning(node, msg, text = nil, display: true)
  @novalid and return
  w = msg
  w += ": #{text}" if text
  @log.add("STANDOC_48", node, params: [w], display:)
end

#subclause_validate(root) ⇒ Object



193
194
195
196
197
198
# File 'lib/metanorma/iso/validate_section.rb', line 193

def subclause_validate(root)
  root.xpath("//clause/clause/clause/clause/clause/clause/clause/clause")
    .each do |c|
    style_warning(c, "Exceeds the maximum clause depth of 7", nil)
  end
end

#subfigure_validate(xmldoc) ⇒ Object

DRG directives 3.7; but anticipated by standoc



73
74
75
76
77
78
79
80
81
82
# File 'lib/metanorma/iso/validate.rb', line 73

def subfigure_validate(xmldoc)
  elems = { footnote: "fn", note: "note", key: "key" }
  xmldoc.xpath("//figure//figure").each do |f|
    elems.each do |k, v|
      f.xpath(".//#{v}").each do |n|
        @log.add("ISO_7", n, params: [k])
      end
    end
  end
end

#symbols_validate(root) ⇒ Object



35
36
37
38
39
40
41
42
43
44
45
46
# File 'lib/metanorma/iso/validate_section.rb', line 35

def symbols_validate(root)
  f = root.xpath("//definitions")
  f.empty? && return
  (f.size == 1 || @vocab) or
    @log.add("ISO_25", f.first)
  f.first.elements.reject { |e| %w(title dl).include? e.name }.empty? or
    @log.add("ISO_26", f.first)
  @vocab and f.each do |f1|
    f1.at("./ancestor::annex") or
      @log.add("ISO_27", f1)
  end
end

#tech_report_style(root) ⇒ Object



167
168
169
170
171
172
173
174
175
# File 'lib/metanorma/iso/validate_section.rb', line 167

def tech_report_style(root)
  @doctype == "technical-report" or return
  root.xpath("//sections/clause[not(@type = 'scope')] | //annex")
    .each do |s|
    r = requirement_check(extract_text(s)) and
      style_warning(s,
                    "Technical Report clause may contain requirement", r)
  end
end

#term_xrefs_validate(xmldoc) ⇒ Object



70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/metanorma/iso/validate_xref.rb', line 70

def term_xrefs_validate(xmldoc)
  termids = xmldoc
    .xpath("//sections/terms | //sections/clause[.//terms] | " \
           "//annex[.//terms]").each_with_object({}) do |t, m|
    t.xpath(".//*/@anchor").each { |a| m[a.text] = true }
    t.xpath(".//*/@id").each { |a| m[a.text] = true }
    t.name == "terms" and m[t["anchor"] || t["id"]] = true
  end
  xmldoc.xpath(".//xref").each do |x|
    term_xrefs_validate1(x, termids)
  end
end

#term_xrefs_validate1(xref, termids) ⇒ Object



83
84
85
86
87
88
89
# File 'lib/metanorma/iso/validate_xref.rb', line 83

def term_xrefs_validate1(xref, termids)
  closest_id = xref.xpath("./ancestor::*[@id]")&.last or return
  termids[xref["target"]] && !termids[closest_id["id"]] and
    @log.add("ISO_50", xref, params: [xref["target"]])
  !termids[xref["target"]] && termids[closest_id["id"]] and
    @log.add("ISO_51", xref, params: [xref["target"]])
end

#termdef_style(xmldoc) ⇒ Object

ISO/IEC DIR 2, 16.5.6



41
42
43
44
45
46
47
48
49
50
# File 'lib/metanorma/iso/validate.rb', line 41

def termdef_style(xmldoc)
  xmldoc.xpath("//term").each do |t|
    para = t.at("./definition/verbal-definition") || return
    term = t.at("./preferred//name").text
    @lang == "en" and
      termdef_warn(para.text, /\A(the|a)\b/i, t, term, "ISO_4")
    %(Cyrl Latn).include?(@script) and
      termdef_warn(para.text, /\.\Z/i, t, term, "ISO_35")
  end
end

#termdef_warn(text, regex, elem, term, msg) ⇒ Object



36
37
38
# File 'lib/metanorma/iso/validate.rb', line 36

def termdef_warn(text, regex, elem, term, msg)
  regex.match(text) && @log.add(msg, elem, params: [term])
end

#title_all_siblings(xpath, label) ⇒ Object

ISO/IEC DIR 2, 22.2



78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/metanorma/iso/validate_title.rb', line 78

def title_all_siblings(xpath, label)
  notitle = false
  withtitle = false
  xpath.each do |s|
    title_all_siblings(s.xpath("./clause | ./terms | ./references"),
                       s&.at("./title")&.text || s["anchor"])
    subtitle = s.at("./title")
    notitle = notitle || (!subtitle || subtitle.text.empty?)
    withtitle = withtitle || (subtitle && !subtitle.text.empty?)
  end
  notitle && withtitle &&
    @log.add("ISO_20", nil, params: [label])
end

#title_first_level_validate(root) ⇒ Object

ISO/IEC DIR 2, 22.2



66
67
68
69
70
71
72
73
74
75
# File 'lib/metanorma/iso/validate_title.rb', line 66

def title_first_level_validate(root)
  root.xpath(SECTIONS_XPATH).each do |s|
    title = s&.at("./title")&.text || s.name
    s.xpath("./clause | ./terms | ./references").each do |ss|
      subtitle = ss.at("./title")
      (!subtitle.nil? && !subtitle&.text&.empty?) or
        @log.add("ISO_19", ss, params: [title])
    end
  end
end

#title_intro_validate(root) ⇒ Object



10
11
12
13
14
15
16
17
18
19
# File 'lib/metanorma/iso/validate_title.rb', line 10

def title_intro_validate(root)
  title_intro_en = title_lang_part(root, "intro", "en")
  title_intro_fr = title_lang_part(root, "intro", "fr")
  if title_intro_en.nil? && !title_intro_fr.nil?
    @log.add("ISO_10", title_intro_fr)
  end
  if !title_intro_en.nil? && title_intro_fr.nil?
    @log.add("ISO_11", title_intro_en)
  end
end

#title_lang_part(doc, part, lang) ⇒ Object



6
7
8
# File 'lib/metanorma/iso/validate_title.rb', line 6

def title_lang_part(doc, part, lang)
  doc.at("//bibdata/title[@type='title-#{part}' and @language='#{lang}']")
end

#title_main_validate(root) ⇒ Object



21
22
23
24
25
26
27
28
29
30
# File 'lib/metanorma/iso/validate_title.rb', line 21

def title_main_validate(root)
  title_main_en = title_lang_part(root, "main", "en")
  title_main_fr = title_lang_part(root, "main", "fr")
  if title_main_en.nil? && !title_main_fr.nil?
    @log.add("ISO_12", title_main_fr)
  end
  if !title_main_en.nil? && title_main_fr.nil?
    @log.add("ISO_13", title_main_en)
  end
end

#title_names_type_validate(root) ⇒ Object

ISO/IEC DIR 2, 11.5.2



53
54
55
56
57
58
59
60
61
62
63
# File 'lib/metanorma/iso/validate_title.rb', line 53

def title_names_type_validate(root)
  @lang == "en" or return
  doctypes = /International\sStandard | Technical\sSpecification |
  Publicly\sAvailable\sSpecification | Technical\sReport | Guide /xi
  title_main_en = title_lang_part(root, "main", "en")
  !title_main_en.nil? && doctypes.match(title_main_en.text) and
    @log.add("ISO_17", title_main_en)
  title_intro_en = title_lang_part(root, "intro", "en")
  !title_intro_en.nil? && doctypes.match(title_intro_en.text) and
    @log.add("ISO_18", title_intro_en)
end

#title_no_full_stop_validate(root) ⇒ Object



93
94
95
96
97
98
99
100
101
# File 'lib/metanorma/iso/validate_title.rb', line 93

def title_no_full_stop_validate(root)
  root.xpath("//preface//title | //sections//title | //annex//title | " \
             "//references/title | //preface//name | //sections//name | " \
             "//annex//name").each do |t|
    style_regex(/\A(?<num>.+\.\Z)/i,
                "No full stop at end of title or caption",
                t, t.text.strip)
  end
end

#title_part_validate(root) ⇒ Object



32
33
34
35
36
37
38
39
# File 'lib/metanorma/iso/validate_title.rb', line 32

def title_part_validate(root)
  title_part_en = title_lang_part(root, "part", "en")
  title_part_fr = title_lang_part(root, "part", "fr")
  title_part_en.nil? && !title_part_fr.nil? &&
    @log.add("ISO_14", title_part_fr)
  !title_part_en.nil? && title_part_fr.nil? &&
    @log.add("ISO_15", title_part_en)
end

#title_subpart_validate(root) ⇒ Object

ISO/IEC DIR 2, 11.4



42
43
44
45
46
47
48
49
50
# File 'lib/metanorma/iso/validate_title.rb', line 42

def title_subpart_validate(root)
  docid = root.at("//bibdata/docidentifier[@type = 'ISO']")
  subpart = /-\d+-\d+/.match docid
  iec = root.at("//bibdata/contributor[role/@type = 'publisher']/" \
                "organization[abbreviation = 'IEC' or " \
                "name = 'International Electrotechnical Commission']")
  subpart && !iec and
    @log.add("ISO_16", docid)
end

#title_validate(root) ⇒ Object



103
104
105
106
107
108
109
110
111
112
# File 'lib/metanorma/iso/validate_title.rb', line 103

def title_validate(root)
  title_intro_validate(root)
  title_main_validate(root)
  title_part_validate(root)
  title_subpart_validate(root)
  title_names_type_validate(root)
  title_first_level_validate(root)
  title_all_siblings(root.xpath(SECTIONS_XPATH), "(top level)")
  title_no_full_stop_validate(root)
end

#vocab_terms_titles_validate(root) ⇒ Object



209
210
211
212
213
214
215
216
217
218
219
220
# File 'lib/metanorma/iso/validate_section.rb', line 209

def vocab_terms_titles_validate(root)
  terms = root.xpath("//sections/terms | //sections/clause[.//terms]")
  if terms.size == 1
    ((t = terms.first.at("./title")) && (t&.text == @i18n.termsdef)) or
      @log.add("ISO_44", terms.first)
  elsif terms.size > 1
    terms.each do |x|
      ((t = x.at("./title")) && /^#{@i18n.termsrelated}/.match?(t&.text)) or
        @log.add("ISO_45", x)
    end
  end
end

#xrefs_mandate_validate(xmldoc) ⇒ Object

require that all assets of a particular type be cross-referenced within the document



93
94
95
96
97
98
# File 'lib/metanorma/iso/validate_xref.rb', line 93

def xrefs_mandate_validate(xmldoc)
  xrefs_mandate_validate1(xmldoc, "//annex", "Annex")
  xrefs_mandate_validate1(xmldoc, "//table", "Table")
  xrefs_mandate_validate1(xmldoc, "//figure", "Figure")
  xrefs_mandate_validate1(xmldoc, "//formula", "Formula")
end

#xrefs_mandate_validate1(xmldoc, xpath, name) ⇒ Object



100
101
102
103
104
105
106
107
108
109
# File 'lib/metanorma/iso/validate_xref.rb', line 100

def xrefs_mandate_validate1(xmldoc, xpath, name)
  exc = %w(table note example figure).map { |x| "//#{x}#{xpath}" }
    .join(" | ")
  (xmldoc.xpath(xpath) - xmldoc.xpath(exc)).each do |x|
    x["unnumbered"] == "true" and next
    @doc_xrefs[x["anchor"]] or
      @log.add(xpath == "//formula" ? "ISO_22" : "ISO_21",
               x, params: [name, x["anchor"]])
  end
end