metanorma/metanorma-standoc

View on GitHub
lib/metanorma/standoc/cleanup_section.rb

Summary

Maintainability
A
45 mins
Test Coverage
require_relative "cleanup_section_names"

module Metanorma
  module Standoc
    module Cleanup
      def make_preface(xml, sect)
        if xml.at("//foreword | //introduction | //acknowledgements | " \
                  "//*[@preface]")
          preface = sect.add_previous_sibling("<preface/>").first
          f = xml.at("//foreword") and to_preface(preface, f)
          f = xml.at("//introduction") and to_preface(preface, f)
          move_clauses_into_preface(xml, preface)
          f = xml.at("//acknowledgements") and to_preface(preface, f)
        end
        make_abstract(xml, sect)
      end

      def move_clauses_into_preface(xml, preface)
        xml.xpath("//*[@preface]").each do |c|
          to_preface(preface, c)
        end
      end

      def to_preface(preface, clause)
        clause.delete("preface")
        preface.add_child clause.remove
      end

      def make_colophon(xml)
        xml.at("//clause[@colophon]") or return
        colophon = xml.root.add_child("<colophon/>").first
        xml.xpath("//*[@colophon]").each do |c|
          c.delete("colophon")
          colophon.add_child c.remove
        end
      end

      def make_abstract(xml, sect)
        if xml.at("//abstract[not(ancestor::bibitem)]")
          preface = sect.at("//preface") ||
            sect.add_previous_sibling("<preface/>").first
          abstract = xml.at("//abstract[not(ancestor::bibitem)]").remove
          preface.prepend_child abstract.remove
          bibabstract = bibabstract_location(xml)
          bibabstract.next = clean_abstract(abstract.dup)
        end
      end

      def clean_abstract(dupabstract)
        dupabstract.traverse { |n| n.remove_attribute("id") }
        %w(language script unnumbered).each do |w|
          dupabstract.remove_attribute(w)
        end
        dupabstract.at("./title")&.remove
        dupabstract
      end

      def bibabstract_location(xml)
        xml.at("//bibdata/script") || xml.at("//bibdata/language") ||
          xml.at("//bibdata/contributor[not(following-sibling::contributor)]") ||
          xml.at("//bibdata/date[not(following-sibling::date)]") ||
          xml.at("//docnumber") ||
          xml.at("//bibdata/docidentifier" \
                 "[not(following-sibling::docidentifier)]") ||
          xml.at("//bibdata/uri[not(following-sibling::uri)]") ||
          xml.at("//bibdata/title[not(following-sibling::title)]")
      end

      def make_bibliography(xml, sect)
        if xml.at("//sections/references | //xref[@hidden]")
          biblio = sect.add_next_sibling("<bibliography/>").first
          xml.xpath("//sections/references").each do |r|
            biblio.add_child r.remove
          end
        end
      end

      def make_indexsect(xml, sect)
        xml.xpath("//sections/indexsect").reverse_each do |r|
          sect.next = r.remove
        end
      end

      def sections_order_cleanup(xml)
        s = xml.at("//sections")
        pop_floating_title(xml)
        make_preface(xml, s)
        make_annexes(xml)
        make_indexsect(xml, s)
        make_bibliography(xml, s)
        make_colophon(xml)
        xml.xpath("//sections/annex").reverse_each { |r| s.next = r.remove }
      end

      def make_annexes(xml)
        xml.xpath("//*[@annex]").each do |y|
          y.delete("annex")
          y.name == "annex" || !y.ancestors("annex").empty? and next
          y.wrap("<annex/>")
          y.parent["id"] = "_#{UUIDTools::UUID.random_create}"
          %w(obligation language script).each do |w|
            y.parent[w] = y[w]
          end
        end
      end

      def maxlevel(xml)
        max = 5
        xml.xpath("//clause[@level]").each do |c|
          max = c["level"].to_i if max < c["level"].to_i
        end
        max
      end

      def sections_level_cleanup(xml)
        m = maxlevel(xml)
        m < 6 and return
        m.downto(6).each do |l|
          xml.xpath("//clause[@level = '#{l}']").each do |c|
            c.delete("level")
            c.previous_element << c.remove
          end
        end
      end

      def sections_cleanup(xml)
        misccontainer_cleanup(xml)
        sections_order_cleanup(xml)
        sections_level_cleanup(xml)
        sections_names_cleanup(xml)
        sections_variant_title_cleanup(xml)
        change_clauses(xml)
      end

      def misccontainer_cleanup(xml)
        m = xml.at("//misc-container-clause") or return
        ins = add_misc_container(xml)
        ins << m.remove.children
      end

      def single_clause_annex(xml)
        xml.xpath("//annex").each do |a|
          single_clause_annex1(a)
        end
      end

      def obligations_cleanup(xml)
        obligations_cleanup_info(xml)
        obligations_cleanup_norm(xml)
        obligations_cleanup_inherit(xml)
      end

      def obligations_cleanup_info(xml)
        xml.xpath("//foreword | //introduction | //acknowledgements | " \
                  "//references | //preface//clause").each do |r|
          r["obligation"] = "informative"
        end
      end

      def obligations_cleanup_norm(xml)
        s = xml.at("//clause[@type = 'scope']") and
          s["obligation"] = "normative"
        xml.xpath("//terms").each { |r| r["obligation"] = "normative" }
        xml.xpath("//definitions").each { |r| r["obligation"] = "normative" }
      end

      def obligations_cleanup_inherit(xml)
        xml.xpath("//annex | //clause[not(ancestor::boilerplate)]").each do |r|
          r["obligation"] = "normative" unless r["obligation"]
        end
        xml.xpath(Utils::SUBCLAUSE_XPATH).each do |r|
          o = r.at("./ancestor::*/@obligation")&.text and r["obligation"] = o
        end
      end

      def clausebefore_cleanup(xmldoc)
        sections_clausebefore_cleanup(xmldoc)
        preface_clausebefore_cleanup(xmldoc)
      end

      def preface_clausebefore_cleanup(xmldoc)
        xmldoc.at("//preface") or return
        ins = insert_before(xmldoc, "//preface")
        xmldoc.xpath("//preface//*[@beforeclauses = 'true']").each do |x|
          x.delete("beforeclauses")
          ins.previous = x.remove
        end
        xmldoc.xpath("//*[@coverpage = 'true']").each do |x|
          ins.previous = x.remove
        end
      end

      def sections_clausebefore_cleanup(xmldoc)
        xmldoc.at("//sections") or return
        ins = insert_before(xmldoc, "//sections")
        xmldoc.xpath("//sections//*[@beforeclauses = 'true']")
          .reverse.each do |x|
          x.delete("beforeclauses")
          ins.previous = x.remove
        end
        endofpreface_clausebefore(xmldoc, ins)
      end

      # only move clausebefore notes at the very end of preface
      def endofpreface_clausebefore(xmldoc, ins)
        xmldoc.xpath("//preface//*[@beforeclauses = 'true']").reverse
          .each do |x|
          textafter = xmldoc.xpath("//preface//*") & x.xpath("./following::*")
          textafter.text.strip.empty? or break
          x.delete("beforeclauses")
          ins.previous = x.remove
        end
      end

      def insert_before(xmldoc, xpath)
        unless ins = xmldoc.at(xpath).children.first
          xmldoc.at(xpath) << " "
          ins = xmldoc.at(xpath).children.first
        end
        ins
      end

      def floatingtitle_cleanup(xmldoc)
        pop_floating_title(xmldoc) # done again, after endofpreface_clausebefore
        floating_title_preface2sections(xmldoc)
      end

      def pop_floating_title(xmldoc)
        loop do
          found = false
          xmldoc.xpath("//floating-title").each do |t|
            t.next_element.nil? or next
            %w(sections annex preface).include? t.parent.name and next
            t.parent.next = t
            found = true
          end
          break unless found
        end
      end

      def floating_title_preface2sections(xmldoc)
        t = xmldoc.at("//preface/floating-title") or return
        s = xmldoc.at("//sections")
        unless t.next_element
          s.children.first.previous = t.remove
        end
      end
    end
  end
end