module Metanorma module IEEE class Converter < Standoc::Converter def section_validate(doc) doctype = doc&.at("//bibdata/ext/doctype")&.text unless %w(amendment technical-corrigendum).include? doctype sections_presence_validate(doc.root) sections_sequence_validate(doc.root) end subclause_validate(doc.root) onlychild_clause_validate(doc.root) super end def sections_presence_validate(root) root.at("//sections/clause[@type = 'overview']") or @log.add("Style", nil, "Overview clause missing") root.at("//sections/clause[@type = 'overview']/clause[@type = 'scope']") or @log.add("Style", nil, "Scope subclause missing") root.at("//sections/clause[@type = 'overview']/clause[@type = 'word-usage']") or @log.add("Style", nil, "Word Usage subclause missing") root.at("//references[@normative = 'true']") or @log.add("Style", nil, "Normative references missing") root.at("//terms") or @log.add("Style", nil, "Definitions missing") end 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("Style", nil, msg) end names end # spec of permissible section sequence # we skip normative references, it goes to end of list SEQ = [ { msg: "Initial section must be (content) Abstract", val: ["./self::abstract"] }, { msg: "Prefatory material must be followed by (clause) Overview", val: ["./self::clause[@type = 'overview']"] }, { msg: "Normative References must be followed by "\ "Definitions", val: ["./self::terms | .//terms"] }, ].freeze SECTIONS_XPATH = "//preface/abstract | //sections/terms | .//annex | "\ "//sections/definitions | //sections/clause | "\ "//references[not(parent::clause)] | "\ "//clause[descendant::references][not(parent::clause)]".freeze def sections_sequence_validate(root) names, n = sections_sequence_validate_start(root) names, n = sections_sequence_validate_body(names, n) sections_sequence_validate_end(names, n) end 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]) names = seqcheck(names, SEQ[2][:msg], SEQ[2][:val]) n = names.shift n = names.shift if n&.at("./self::definitions") [names, n] end def sections_sequence_validate_body(names, elem) [names, elem] end def sections_sequence_validate_end(names, elem) while elem&.name == "annex" elem = names.shift if elem.nil? @log.add("Style", nil, "Document must include (references) "\ "Normative References") end end elem&.at("./self::references[@normative = 'true']") || @log.add("Style", nil, "Document must include (references) "\ "Normative References") elem = names&.shift elem&.at("./self::references[@normative = 'false']") || @log.add("Style", elem, "Final section must be (references) Bibliography") names.empty? || @log.add("Style", elem, "There are sections after the final Bibliography") end # Style manual 13.1 def subclause_validate(root) root.xpath("//clause/clause/clause/clause/clause/clause") .each do |c| style_warning(c, "Exceeds the maximum clause depth of 5", nil) end end # Style manual 13.1 def onlychild_clause_validate(root) root.xpath(Standoc::Utils::SUBCLAUSE_XPATH).each do |c| next unless c.xpath("../clause").size == 1 title = c.at("./title") location = c["id"] || "#{c.text[0..60]}..." location += ":#{title.text}" if c["id"] && !title.nil? @log.add("Style", nil, "#{location}: subclause is only child") end end end end end