module Metanorma::Standoc::Validate

Constants

SOURCELOCALITY

Public Instance Methods

asset_style(root) click to toggle source
# File lib/metanorma/standoc/validate_section.rb, line 38
def asset_style(root)
  asset_title_style(root)
end
asset_title_style(root) click to toggle source
# File lib/metanorma/standoc/validate_section.rb, line 29
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
concept_validate(doc, tag, refterm) click to toggle source
# File lib/metanorma/standoc/validate.rb, line 68
def concept_validate(doc, tag, refterm)
  found = false
  doc.xpath("//#{tag}/xref").each do |x|
    next if doc.at("//term[@id = '#{x['target']}']")
    next if doc.at("//definitions//dt[@id = '#{x['target']}']")

    ref = x&.at("../#{refterm}")&.text
    @log.add("Anchors", x,
             "#{tag.capitalize} #{ref} is pointing to "\
             "#{x['target']}, which is not a term or symbol")
    found = true
  end
  found and
    @fatalerror << "#{tag.capitalize} not cross-referencing term or symbol"
end
content_validate(doc) click to toggle source
# File lib/metanorma/standoc/validate.rb, line 43
def content_validate(doc)
  @fatalerror = []
  xref_validate(doc)
  section_validate(doc)
  norm_ref_validate(doc)
  repeat_id_validate(doc.root)
  iev_validate(doc.root)
  concept_validate(doc, "concept", "refterm")
  concept_validate(doc, "related", "preferred//name")
  @fatalerror.empty? or clean_abort(@fatalerror.join("\n"), doc.to_xml)
end
formattedstr_strip(doc) click to toggle source

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.

# File lib/metanorma/standoc/validate.rb, line 126
def formattedstr_strip(doc)
  doc.xpath("//*[@format] | //stem | //bibdata//description | "\
            "//formattedref | //bibdata//note | //bibdata/abstract | "\
            "//bibitem/abstract | //bibitem/note | //misc-container")
    .each do |n|
    n.elements.each do |e|
      e.traverse do |e1|
        e1.element? and e1.each { |k, _v| e1.delete(k) }
      end
    end
  end
  doc
end
hanging_para_style(root) click to toggle source
# File lib/metanorma/standoc/validate_section.rb, line 42
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) click to toggle source
# File lib/metanorma/standoc/validate.rb, line 21
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) click to toggle source
# File lib/metanorma/standoc/validate.rb, line 32
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}"))
end
init_iev() click to toggle source
# File lib/metanorma/standoc/validate.rb, line 13
def init_iev
  return nil if @no_isobib
  return @iev if @iev

  @iev = Iev::Db.new(@iev_globalname, @iev_localname) unless @no_isobib
  @iev
end
norm_ref_validate(doc) click to toggle source
# File lib/metanorma/standoc/validate.rb, line 55
def norm_ref_validate(doc)
  found = false
  doc.xpath("//references[@normative = 'true']/bibitem").each do |b|
    next unless docid = b.at("./docidentifier[@type = 'metanorma']")
    next unless /^\[\d+\]$/.match?(docid.text)

    @log.add("Bibliography", b,
             "Numeric reference in normative references")
    found = true
  end
  found and @fatalerror << "Numeric reference in normative references"
end
repeat_id_validate(doc) click to toggle source
# File lib/metanorma/standoc/validate.rb, line 95
def repeat_id_validate(doc)
  ids = {}
  doc.xpath("//*[@id]").each do |x|
    ids = repeat_id_validate1(ids, x)
  end
end
repeat_id_validate1(ids, elem) click to toggle source
# File lib/metanorma/standoc/validate.rb, line 84
def repeat_id_validate1(ids, elem)
  if ids[elem["id"]]
    @log.add("Anchors", elem, "Anchor #{elem['id']} has already been "\
                              "used at line #{ids[elem['id']]}")
    @fatalerror << "Multiple instances of same ID: #{elem['id']}"
  else
    ids[elem["id"]] = elem.line
  end
  ids
end
schema_validate(doc, schema) click to toggle source
# File lib/metanorma/standoc/validate.rb, line 102
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.to_xml)
  ensure
    f.close!
  end
end
schema_validate1(file, doc, schema) click to toggle source
# File lib/metanorma/standoc/validate.rb, line 112
def schema_validate1(file, doc, schema)
  file.write(doc.to_xml)
  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) click to toggle source
# 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) click to toggle source
# File lib/metanorma/standoc/validate_section.rb, line 12
def sourcecode_style(root)
  root.xpath("//sourcecode").each do |x|
    callouts = x.elements.select { |e| e.name == "callout" }
    annotations = x.elements.select { |e| e.name == "annotation" }
    if callouts.size != annotations.size
      @log.add("AsciiDoc Input", x,
               "mismatch of callouts and annotations")
    end
  end
end
style_warning(node, msg, text = nil) click to toggle source
# File lib/metanorma/standoc/validate_section.rb, line 23
def style_warning(node, msg, text = nil)
  w = msg
  w += ": #{text}" if text
  @log.add("Metanorma XML Style Warning", node, w)
end
validate(doc) click to toggle source
# File lib/metanorma/standoc/validate.rb, line 151
def validate(doc)
  content_validate(doc)
  schema_validate(formattedstr_strip(doc.dup),
                  File.join(File.dirname(__FILE__), "isodoc.rng"))
end
xref_validate(doc) click to toggle source

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

# File lib/metanorma/standoc/validate.rb, line 141
def xref_validate(doc)
  ids = doc.xpath("//*/@id").each_with_object({}) { |x, m| m[x.text] = 1 }
  doc.xpath("//xref/@target | //xref/@to").each do |x|
    next if ids[x.text]

    @log.add("Anchors", x.parent,
             "Crossreference target #{x.text} is undefined")
  end
end