Module: Metanorma::Standoc::Validate

Included in:
Converter
Defined in:
lib/metanorma/standoc/validate.rb,
lib/metanorma/standoc/validate_term.rb,
lib/metanorma/standoc/validate_table.rb,
lib/metanorma/standoc/validate_schema.rb,
lib/metanorma/standoc/validate_section.rb

Constant Summary collapse

MATHML_NS =
"http://www.w3.org/1998/Math/MathML".freeze
TOO_BIG_IMG_ERR =
<<~ERR.freeze
  Image too large for Data URI encoding: disable Data URI encoding (`:data-uri-image: false`), or set `:data-uri-maxsize: 0`
ERR
SOURCELOCALITY =
"./origin//locality[@type = 'clause']/" \
"referenceFrom".freeze
SVG_NS =
"http://www.w3.org/2000/svg".freeze
WILDCARD_ATTRS =
"//stem | //metanorma-extension".freeze

Instance Method Summary collapse

Instance Method Details

#add_ns_to_fragment(xml_fragment) ⇒ Object



54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/metanorma/standoc/validate_schema.rb', line 54

def add_ns_to_fragment(xml_fragment)
  f = Nokogiri::XML(xml_fragment, &:strict)
  f.errors.any? || f.root.nil? and return nil
  root_tag = f.root.name
  f.root.namespace or
    f = Nokogiri::XML(xml_fragment
    .sub(/<#{root_tag}([^>]*)>/,
         "<#{root_tag}\\1 xmlns='#{xml_namespace}'>"))
  f
rescue StandardError
  nil
end

#asset_style(root) ⇒ Object



43
44
45
# File 'lib/metanorma/standoc/validate_section.rb', line 43

def asset_style(root)
  asset_title_style(root)
end

#asset_title_style(root) ⇒ Object



34
35
36
37
38
39
40
41
# File 'lib/metanorma/standoc/validate_section.rb', line 34

def asset_title_style(root)
  root.xpath("//figure[image][not(name)]").each do |node|
    style_warning(node, "Figure should have title", nil)
  end
  root.xpath("//table[not(name)]").each do |node|
    style_warning(node, "Table should have title", nil)
  end
end

#callouts_error(elem, callouts, annotations) ⇒ Object



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

def callouts_error(elem, callouts, annotations)
  if callouts.size != annotations.size && !annotations.empty?
    err = "mismatch of callouts (#{callouts.size}) and annotations " \
          "(#{annotations.size})"
    @log.add("Crossreferences", elem, err, severity: 0)
  end
end

#concept_validate(doc, tag, refterm) ⇒ Object



38
39
40
41
42
43
44
45
# File 'lib/metanorma/standoc/validate_term.rb', line 38

def concept_validate(doc, tag, refterm)
  concept_validate_ids(doc)
  doc.xpath("//#{tag}/xref").each do |x|
    @concept_ids[x["target"]] and next
    @log.add("Anchors", x, concept_validate_msg(doc, tag, refterm, x),
             severity: 0)
  end
end

#concept_validate_ids(doc) ⇒ Object



47
48
49
50
51
52
53
# File 'lib/metanorma/standoc/validate_term.rb', line 47

def concept_validate_ids(doc)
  @concept_ids ||= doc.xpath("//term | //definitions//dt")
    .each_with_object({}) { |x, m| m[x["id"]] = true }
  @concept_terms_tags ||= doc.xpath("//terms")
    .each_with_object({}) { |t, m| m[t["id"]] = true }
  nil
end

#concept_validate_msg(_doc, tag, refterm, xref) ⇒ Object



55
56
57
58
59
60
61
62
63
64
# File 'lib/metanorma/standoc/validate_term.rb', line 55

def concept_validate_msg(_doc, tag, refterm, xref)
  ret = <<~LOG
    #{tag.capitalize} #{xref.at("../#{refterm}")&.text} is pointing to #{xref['target']}, which is not a term or symbol
  LOG
  if @concept_terms_tags[xref["target"]]
    ret = ret.strip
    ret += ". Did you mean to point to a subterm?"
  end
  ret
end

#content_validate(doc) ⇒ Object



13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# File 'lib/metanorma/standoc/validate.rb', line 13

def content_validate(doc)
  @doctype = doc.at("//bibdata/ext/doctype")&.text
  repeat_id_validate(doc.root) # feeds xref_validate
  xref_validate(doc) # feeds nested_asset_validate
  nested_asset_validate(doc)
  section_validate(doc)
  norm_ref_validate(doc)
  iev_validate(doc.root)
  concept_validate(doc, "concept", "refterm")
  concept_validate(doc, "related", "preferred//name")
  preferred_validate(doc)
  termsect_validate(doc)
  table_validate(doc)
  requirement_validate(doc)
  image_validate(doc)
  math_validate(doc)
  fatalerrors = @log.abort_messages
  fatalerrors.empty? or
    clean_abort("\n\nFATAL ERRROS:\n\n#{fatalerrors.join("\n\n")}", doc)
end

#empty_table_validate(doc) ⇒ Object



17
18
19
20
21
# File 'lib/metanorma/standoc/validate_table.rb', line 17

def empty_table_validate(doc)
  doc.xpath("//table[not(.//tr)]").each do |t|
    @log.add("Table", t, "Empty table", severity: 0)
  end
end

#expand_path(loc) ⇒ Object



117
118
119
120
121
122
# File 'lib/metanorma/standoc/validate.rb', line 117

def expand_path(loc)
  relative_path = File.join(@localdir, loc)
  [loc, relative_path].detect do |p|
    File.exist?(p) ? p : nil
  end
end

#find_illegal_designations(xmldoc) ⇒ Object



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

def find_illegal_designations(xmldoc)
  xmldoc.xpath("//preferred | //admitted | //deprecates")
    .each_with_object({}) do |d, m|
    d.ancestors.detect { |x| x.name == "terms" } and next
    c = d.ancestors.detect do |x|
      section_containers.include?(x.name)
    end
    c["id"] ||= "_#{UUIDTools::UUID.random_create}"
    m[c["id"]] ||= { clause: c, designations: [] }
    m[c["id"]][:designations] << d
  end
end

#formattedstr_strip(doc) ⇒ Object

RelaxNG cannot cope well with wildcard attributes. So we strip any attributes from FormattedString instances (which can contain xs:any markup, and are signalled with @format) before validation.



91
92
93
94
95
96
97
98
99
100
101
# File 'lib/metanorma/standoc/validate_schema.rb', line 91

def formattedstr_strip(doc)
  doc.xpath(WILDCARD_ATTRS, "m" => SVG_NS).each do |n|
    n.elements.each do |e|
      e.traverse do |e1|
        e1.element? and e1.each { |k, _v| e1.delete(k) } # rubocop:disable Style/HashEachMethods
      end
    end
  end
  doc.xpath("//m:svg", "m" => SVG_NS).each { |n| n.replace("<svg/>") }
  doc
end

#fragment_schema(root_element) ⇒ Object



67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/metanorma/standoc/validate_schema.rb', line 67

def fragment_schema(root_element)
  temp_schema = Tempfile.new(["dynamic_schema", ".rng"])
  temp_schema.write(<<~SCHEMA)
            <grammar xmlns="http://relaxng.org/ns/structure/1.0">
      <include href="#{schema_location}">
        <start combine="choice">
            <ref name="#{root_element}"/>
        </start>
    </include>
            </grammar>
  SCHEMA
  temp_schema.close
  [temp_schema, Nokogiri::XML::RelaxNG(File.open(temp_schema.path))]
rescue StandardError # error because root_element is not in schema
  [temp_schema, nil]
end

#hanging_para_style(root) ⇒ Object



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

def hanging_para_style(root)
  root.xpath("//clause | //annex | //foreword | //introduction | " \
             "//acknowledgements").each do |c|
    next unless c.at("./clause")
    next if c.elements.reject do |n|
              %w(clause title).include? n.name
            end.empty?

    style_warning(c, "Hanging paragraph in clause")
  end
end

#iev_validate(xmldoc) ⇒ Object



16
17
18
19
20
21
22
23
24
25
# File 'lib/metanorma/standoc/validate_term.rb', line 16

def iev_validate(xmldoc)
  @iev = init_iev or return
  xmldoc.xpath("//term").each do |t|
    t.xpath(".//termsource").each do |src|
      (/^IEC[  ]60050-/.match(src.at("./origin/@citeas")&.text) &&
    loc = src.xpath(SOURCELOCALITY)&.text) or next
      iev_validate1(t, loc, xmldoc)
    end
  end
end

#iev_validate1(term, loc, xmldoc) ⇒ Object



27
28
29
30
31
32
33
34
35
36
# File 'lib/metanorma/standoc/validate_term.rb', line 27

def iev_validate1(term, loc, xmldoc)
  iev = @iev.fetch(loc,
                   xmldoc.at("//language")&.text || "en") or return
  pref = term.xpath("./preferred//name").inject([]) do |m, x|
    m << x.text&.downcase
  end
  pref.include?(iev.downcase) or
    @log.add("Bibliography", term, %(Term "#{pref[0]}" does not match ) +
             %(IEV #{loc} "#{iev}"), severity: 1)
end

#image_exists(doc) ⇒ Object



107
108
109
110
111
112
113
114
115
# File 'lib/metanorma/standoc/validate.rb', line 107

def image_exists(doc)
  doc.xpath("//image").each do |i|
    Vectory::Utils::url?(i["src"]) and next
    Vectory::Utils::datauri?(i["src"]) and next
    expand_path(i["src"]) and next
    @log.add("Images", i.parent,
             "Image not found: #{i['src']}", severity: 0)
  end
end

#image_toobig(doc) ⇒ Object



148
149
150
151
152
153
154
# File 'lib/metanorma/standoc/validate.rb', line 148

def image_toobig(doc)
  @dataurimaxsize.zero? and return
  doc.xpath("//image").each do |i|
    i["src"].size > @dataurimaxsize and
      @log.add("Images", i.parent, TOO_BIG_IMG_ERR, severity: 0)
  end
end

#image_validate(doc) ⇒ Object



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

def image_validate(doc)
  image_exists(doc)
  image_toobig(doc)
  png_validate(doc)
end

#init_ievObject



9
10
11
12
13
14
# File 'lib/metanorma/standoc/validate_term.rb', line 9

def init_iev
  @no_isobib and return nil
  @iev and return @iev
  @iev = ::Iev::Db.new(@iev_globalname, @iev_localname) unless @no_isobib
  @iev
end

#math_validate(doc) ⇒ Object



36
37
38
39
40
41
42
43
44
45
46
47
# File 'lib/metanorma/standoc/validate.rb', line 36

def math_validate(doc)
  doc.xpath("//m:math", "m" => MATHML_NS).each do |m|
    if m.parent["validate"] == "false"
      m.parent.delete("validate")
    else
      math = mathml_sanitise(m.dup)
      Plurimath::Math.parse(math, "mathml").to_mathml
    end
  rescue StandardError => e
    math_validate_error(math, m, e)
  end
end

#math_validate_error(math, elem, error) ⇒ Object



54
55
56
57
58
59
60
61
62
# File 'lib/metanorma/standoc/validate.rb', line 54

def math_validate_error(math, elem, error)
  a = elem.parent.at("./asciimath")
  l = elem.parent.at("./latexmath")
  orig = ""
  a and orig += "\n\tAsciimath original: #{@c.decode(a.children.to_xml)}"
  l and orig += "\n\tLatexmath original: #{@c.decode(l.children.to_xml)}"
  @log.add("Maths", elem,
           "Invalid MathML: #{math}\n #{error}#{orig}", severity: 0)
end

#mathml_sanitise(math) ⇒ Object



49
50
51
52
# File 'lib/metanorma/standoc/validate.rb', line 49

def mathml_sanitise(math)
  math.to_xml(encoding: "US-ASCII").gsub(/ xmlns=["'][^"']+["']/, "")
    .gsub(%r{<[^:/>]+:}, "<").gsub(%r{</[^:/>]+:}, "</")
end

#max_td_count(table) ⇒ Object



23
24
25
26
27
28
29
30
# File 'lib/metanorma/standoc/validate_table.rb', line 23

def max_td_count(table)
  max = 0
  table.xpath("./tr").each do |tr|
    n = tr.xpath("./td | ./th").size
    max < n and max = n
  end
  max
end

#maxcols_check(col, maxcols, tcell) ⇒ Object

if maxcols or maxrows negative, do not check them



87
88
89
90
91
92
# File 'lib/metanorma/standoc/validate_table.rb', line 87

def maxcols_check(col, maxcols, tcell)
  if maxcols.positive? && col > maxcols
    @log.add("Table", tcell, "Table exceeds maximum number of columns "\
                             "defined (#{maxcols})", severity: 0)
  end
end

#maxcols_validate1(tcell, row, curr, cells2d, maxcols, mode) ⇒ Object

code doesn’t actually do anything, since Asciidoctor refuses to generate table with inconsistent column count



57
58
59
60
61
62
63
# File 'lib/metanorma/standoc/validate_table.rb', line 57

def maxcols_validate1(tcell, row, curr, cells2d, maxcols, mode)
  rs = tcell&.attr("rowspan")&.to_i || 1
  cs = tcell&.attr("colspan")&.to_i || 1
  curr = table_tracker_update(cells2d, row, curr, rs, cs)
  maxcols_check(curr + cs - 1, maxcols, tcell) if mode == "row_cols"
  curr + cs
end

#maxrowcols_validate(table, maxcols, mode: "row_cols") ⇒ Object



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

def maxrowcols_validate(table, maxcols, mode: "row_cols")
  case mode
  when "row_cols"
    maxrowcols_validate0(table, maxcols, "*", mode)
  when "thead_row"
    %w{thead tbody tfoot}.each do |w|
      maxrowcols_validate0(table, maxcols, w, mode)
    end
  end
end

#maxrowcols_validate0(table, maxcols, tablechild, mode) ⇒ Object



43
44
45
46
47
48
49
50
51
52
53
# File 'lib/metanorma/standoc/validate_table.rb', line 43

def maxrowcols_validate0(table, maxcols, tablechild, mode)
  cells2d = table.xpath("./#{tablechild}/tr")
    .each_with_object([]) { |_r, m| m << {} }
  table.xpath("./#{tablechild}/tr").each_with_index do |tr, r|
    curr = 0
    tr.xpath("./td | ./th").each do |td|
      curr = maxcols_validate1(td, r, curr, cells2d, maxcols, mode)
    end
  end
  maxrows_validate(table, cells2d, tablechild, mode)
end

#maxrows_validate(table, cells2d, tablechild, mode) ⇒ Object



77
78
79
80
81
82
83
84
# File 'lib/metanorma/standoc/validate_table.rb', line 77

def maxrows_validate(table, cells2d, tablechild, mode)
  err = "are inconsistent"
  mode == "thead_row" and err = "cannot go outside #{tablechild}"
  err = "Table rows in table #{err}: check rowspan"
  if cells2d.any? { |x| x.size != cells2d.first.size }
    @log.add("Table", table, err, severity: 0)
  end
end

#nested_asset_report(outer, inner, doc) ⇒ Object



86
87
88
89
90
91
92
# File 'lib/metanorma/standoc/validate.rb', line 86

def nested_asset_report(outer, inner, doc)
  outer.name == "figure" && inner.name == "figure" and return
  err =
    "There is an instance of #{inner.name} nested within #{outer.name}"
  @log.add("Style", inner, err)
  nested_asset_xref_report(outer, inner, doc)
end

#nested_asset_validate(doc) ⇒ Object



64
65
66
67
# File 'lib/metanorma/standoc/validate.rb', line 64

def nested_asset_validate(doc)
  nested_asset_validate_basic(doc)
  nested_note_validate(doc)
end

#nested_asset_validate_basic(doc) ⇒ Object



69
70
71
72
73
74
75
76
# File 'lib/metanorma/standoc/validate.rb', line 69

def nested_asset_validate_basic(doc)
  a = "//example | //figure | //termnote | //termexample | //table"
  doc.xpath("#{a} | //note").each do |m|
    m.xpath(a.gsub(%r{//}, ".//")).each do |n|
      nested_asset_report(m, n, doc)
    end
  end
end

#nested_asset_xref_report(outer, inner, _doc) ⇒ Object



94
95
96
97
98
99
# File 'lib/metanorma/standoc/validate.rb', line 94

def nested_asset_xref_report(outer, inner, _doc)
  i = @doc_xrefs[inner["id"]] or return
  err2 = "There is a crossreference to an instance of #{inner.name} " \
         "nested within #{outer.name}: #{i.to_xml}"
  @log.add("Style", i, err2)
end

#nested_note_validate(doc) ⇒ Object



78
79
80
81
82
83
84
# File 'lib/metanorma/standoc/validate.rb', line 78

def nested_note_validate(doc)
  doc.xpath("//termnote | //note").each do |m|
    m.xpath(".//note").each do |n|
      nested_asset_report(m, n, doc)
    end
  end
end

#norm_ref_validate(doc) ⇒ Object



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

def norm_ref_validate(doc)
  doc.xpath("//references[@normative = 'true']/bibitem").each do |b|
    docid = b.at("./docidentifier[@type = 'metanorma']") or next
    /^\[\d+\]$/.match?(docid.text) or next
    @log.add("Bibliography", b,
             "Numeric reference in normative references", severity: 1)
  end
end

#png_validate(doc) ⇒ Object



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

def png_validate(doc)
  doc.xpath("//image[@mimetype = 'image/png']").each do |i|
    Vectory::Utils::url?(i["src"]) and next
    decoded = if Vectory::Utils::datauri?(i["src"])
                Vectory::Utils::decode_datauri(i["src"])[:data]
              else
                path = expand_path(i["src"]) or next
                File.binread(path)
              end
    png_validate1(i, decoded)
  end
end

#png_validate1(img, buffer) ⇒ Object



137
138
139
140
141
142
# File 'lib/metanorma/standoc/validate.rb', line 137

def png_validate1(img, buffer)
  PngCheck.check_buffer(buffer)
rescue PngCheck::CorruptPngError => e
  @log.add("Images", img.parent,
           "Corrupt PNG image detected: #{e.message}")
end

#preferred_validate(doc) ⇒ Object



66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/metanorma/standoc/validate_term.rb', line 66

def preferred_validate(doc)
  ret = doc.xpath("//term").each_with_object({}) do |t, m|
    prefix = t.at("./domain")&.text
    t.xpath("./preferred//name").each do |n|
      ret = n.text
      prefix and ret = "<#{prefix}> #{ret}"
      m[ret] ||= []
      m[ret] << t
    end
  end
  preferred_validate_report(ret)
end

#preferred_validate_report(terms) ⇒ Object



79
80
81
82
83
84
85
86
# File 'lib/metanorma/standoc/validate_term.rb', line 79

def preferred_validate_report(terms)
  terms.each do |k, v|
    v.size > 1 or next
    loc = v.map { |x| x["id"] }.join(", ")
    err = "Term #{k} occurs twice as preferred designation: #{loc}"
    @log.add("Terms", v.first, err, severity: 1)
  end
end

#repeat_id_validate(doc) ⇒ Object



170
171
172
173
174
175
# File 'lib/metanorma/standoc/validate.rb', line 170

def repeat_id_validate(doc)
  @doc_ids = {}
  doc.xpath("//*[@id]").each do |x|
    repeat_id_validate1(x)
  end
end

#repeat_id_validate1(elem) ⇒ Object



161
162
163
164
165
166
167
168
# File 'lib/metanorma/standoc/validate.rb', line 161

def repeat_id_validate1(elem)
  if @doc_ids[elem["id"]]
    @log.add("Anchors", elem,
             "Anchor #{elem['id']} has already been " \
             "used at line #{@doc_ids[elem['id']]}", severity: 0)
  end
  @doc_ids[elem["id"]] = elem.line
end

#schema_fileObject



13
14
15
# File 'lib/metanorma/standoc/validate_schema.rb', line 13

def schema_file
  "isodoc-compile.rng"
end

#schema_locationObject



6
7
8
9
10
11
# File 'lib/metanorma/standoc/validate_schema.rb', line 6

def schema_location
  self.class.respond_to?(:_file) and ret = self.class::_file
  ret ||= caller_locations(1..1).first.absolute_path
  ret ||= __FILE__
  File.join(File.dirname(ret), schema_file)
end

#schema_validate(doc, schema) ⇒ Object



17
18
19
20
21
22
23
24
25
# File 'lib/metanorma/standoc/validate_schema.rb', line 17

def schema_validate(doc, schema)
  Tempfile.open(["tmp", ".xml"], encoding: "UTF-8") do |f|
    schema_validate1(f, doc, schema)
  rescue Jing::Error => e
    clean_abort("Jing failed with error: #{e}", doc)
  ensure
    f.close!
  end
end

#schema_validate1(file, doc, schema) ⇒ Object



27
28
29
30
31
32
33
34
35
36
# File 'lib/metanorma/standoc/validate_schema.rb', line 27

def schema_validate1(file, doc, schema)
  file.write(to_xml(doc))
  file.close
  errors = Jing.new(schema, encoding: "UTF-8").validate(file.path)
  warn "Syntax Valid!" if errors.none?
  errors.each do |e|
    @log.add("Metanorma XML Syntax",
             "XML Line #{'%06d' % e[:line]}:#{e[:column]}", e[:message])
  end
end

#section_validate(doc) ⇒ Object



6
7
8
9
10
# File 'lib/metanorma/standoc/validate_section.rb', line 6

def section_validate(doc)
  sourcecode_style(doc.root)
  hanging_para_style(doc.root)
  asset_style(doc.root)
end

#sourcecode_style(root) ⇒ Object



12
13
14
15
16
17
18
# File 'lib/metanorma/standoc/validate_section.rb', line 12

def sourcecode_style(root)
  root.xpath("//sourcecode").each do |x|
    callouts = x.xpath("./body/callout")
    annotations = x.xpath("./annotation")
    callouts_error(x, callouts, annotations)
  end
end

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



28
29
30
31
32
# File 'lib/metanorma/standoc/validate_section.rb', line 28

def style_warning(node, msg, text = nil)
  w = msg
  w += ": #{text}" if text
  @log.add("Style", node, w)
end

#table_tracker_update(cells2d, row, curr, rowspan, colspan) ⇒ Object



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

def table_tracker_update(cells2d, row, curr, rowspan, colspan)
  cells2d[row] ||= {}
  while cells2d[row][curr]
    curr += 1
  end
  (row..(row + rowspan - 1)).each do |y2|
    cells2d[y2] ||= {}
    (curr..(curr + colspan - 1)).each { |x2| cells2d[y2][x2] = 1 }
  end
  curr
end

#table_validate(doc) ⇒ Object



4
5
6
7
8
9
10
11
12
13
14
15
# File 'lib/metanorma/standoc/validate_table.rb', line 4

def table_validate(doc)
  empty_table_validate(doc)
  doc.xpath("//table[colgroup]").each do |t|
    maxrowcols_validate(t, t.xpath("./colgroup/col").size)
  end
  doc.xpath("//table[.//*[@colspan] | .//*[@rowspan]]").each do |t|
    maxrowcols_validate(t, max_td_count(t), mode: "row_cols")
  end
  doc.xpath("//table[.//*[@rowspan]]").each do |t|
    maxrowcols_validate(t, max_td_count(t), mode: "thead_row")
  end
end

#termsect_validate(xmldoc) ⇒ Object



101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/metanorma/standoc/validate_term.rb', line 101

def termsect_validate(xmldoc)
  errors = find_illegal_designations(xmldoc)
  errors.each_value do |v|
    desgns = v[:designations].map do |x|
      @c.encode(x.text.strip,  :basic, :hexadecimal)
    end.join(", ")
    err = <<~ERROR
      Clause not recognised as a term clause, but contains designation markup
       (preferred:[], admitted:[], alt:[], deprecated:[]):<br/>
      #{desgns}</br>
      Ensure the parent clause is recognised as a terms clause by inserting <code>[heading=terms and definitions]</code> above the title,
      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>.
    ERROR
    @log.add("Terms", v[:clause], err, severity: 0)
  end
end

#validate(doc) ⇒ Object



156
157
158
159
# File 'lib/metanorma/standoc/validate.rb', line 156

def validate(doc)
  content_validate(doc)
  schema_validate(formattedstr_strip(doc.dup), schema_location)
end

#validate_document_fragment(xml_fragment) ⇒ Object



38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/metanorma/standoc/validate_schema.rb', line 38

def validate_document_fragment(xml_fragment)
  f = add_ns_to_fragment(xml_fragment) or
    return [true,
            "Fragment is not well-formed XML, not validating"]
  begin
    temp_schema, schema = fragment_schema(f.root.name)
    schema or return [false, "Did not expect element #{f.root.name}"]
    validation_errors = schema.validate(f)
    [validation_errors.none? do |x|
      x.to_s.include?("Did not expect element")
    end, validation_errors]
  ensure
    temp_schema.unlink
  end
end

#xref_validate(doc) ⇒ Object

manually check for xref/@target, xref/@to integrity



178
179
180
181
182
183
184
185
186
# File 'lib/metanorma/standoc/validate.rb', line 178

def xref_validate(doc)
  @doc_xrefs = doc.xpath("//xref/@target | //xref/@to | //index/@to")
    .each_with_object({}) do |x, m|
    m[x.text] = x
    @doc_ids[x.text] and next
    @log.add("Anchors", x.parent,
             "Crossreference target #{x} is undefined", severity: 1)
  end
end