metanorma/metanorma

View on GitHub
lib/metanorma/util.rb

Summary

Maintainability
A
1 hr
Test Coverage
module Metanorma
  module Util
    def self.log(message, type = :info)
      log_types = Metanorma.configuration.logs.map(&:to_s) || []

      if log_types.include?(type.to_s)
        puts(message)
      end

      if type == :fatal
        exit(1)
      end
    end

    # dependency ordering
    def self.sort_extensions_execution_ord(ext)
      case ext
      when :xml then 0
      when :rxl then 1
      when :presentation then 2
      else
        99
      end
    end

    def self.sort_extensions_execution(ext)
      ext.sort do |a, b|
        sort_extensions_execution_ord(a) <=> sort_extensions_execution_ord(b)
      end
    end

    def self.recursive_string_keys(hash)
      case hash
      when Hash then hash.map { |k, v| [k.to_s, recursive_string_keys(v)] }.to_h
      when Enumerable then hash.map { |v| recursive_string_keys(v) }
      else
        hash
      end
    end

    def self.gather_bibitems(xml)
      xml.xpath("//xmlns:bibitem[@id]").each_with_object({}) do |b, m|
        if m[b["id"]]
          b.remove
          next # we can't update duplicate bibitem, processing updates wrong one
        else
          m[b["id"]] = b
        end
      end
    end

    def self.gather_bibitemids(xml)
      xml.xpath("//*[@bibitemid]").each_with_object({}) do |e, m|
        /^semantic__/.match?(e.name) and next
        m[e["bibitemid"]] ||= []
        m[e["bibitemid"]] << e
      end
    end

    def self.gather_citeases(xml)
      xml.xpath("//*[@citeas]").each_with_object({}) do |e, m|
        /^semantic__/.match?(e.name) and next
        m[e["citeas"]] ||= []
        m[e["citeas"]] << e
      end
    end

    def self.add_suffix_to_attributes(doc, suffix, tag_name, attr_name, isodoc)
      (suffix.nil? || suffix.empty?) and return
      doc.xpath(isodoc.ns("//#{tag_name}[@#{attr_name}]")).each do |elem|
        a = elem.attributes[attr_name].value
        /_#{suffix}$/.match?(a) or
          elem.attributes[attr_name].value = "#{a}_#{suffix}"
      end
    end

    def self.hash_key_detect(directives, key, variable)
      c = directives.detect { |x| x.is_a?(Hash) && x.has_key?(key) } or
        return variable
      c[key]
    end

    def self.rel_path_resolve(dir, path)
      path.nil? and return path
      path.empty? and return path
      p = Pathname.new(path)
      p.absolute? ? path : File.join(dir, path)
    end

    def self.key(ident)
      @c ||= HTMLEntities.new
      @c.decode(ident).gsub(/(\p{Zs})+/, " ")
    end

    class DisambigFiles
      def initialize
        @seen_filenames = []
      end

      def strip_root(name)
        name.sub(%r{^(\./)?(\.\./)+}, "")
      end

      def source2dest_filename(name, disambig = true)
        n = strip_root(name)
        dir = File.dirname(n)
        base = File.basename(n)
        if disambig && @seen_filenames.include?(base)
          base = disambiguate_filename(base)
        end
        @seen_filenames << base
        dir == "." ? base : File.join(dir, base)
      end

      def disambiguate_filename(base)
        m = /^(?<start>.+\.)(?!0)(?<num>\d+)\.(?<suff>[^.]*)$/.match(base) ||
          /^(?<start>.+\.)(?<suff>[^.]*)/.match(base) ||
          /^(?<start>.+)$/.match(base)
        i = m.names.include?("num") ? m["num"].to_i + 1 : 1
        while @seen_filenames.include? base = "#{m['start']}#{i}.#{m['suff']}"
          i += 1
        end
        base
      end
    end
  end
end