metanorma/isodoc

View on GitHub
lib/isodoc/xref/xref_counter.rb

Summary

Maintainability
B
4 hrs
Test Coverage
require "roman-numerals"

module IsoDoc
  module XrefGen
    class ReqCounter
      # one counter for each requirements label
      def initialize
        @counters = {}
      end

      def increment(label, node)
        @counters[label] ||= Counter.new
        @counters[label].increment(node)
      end
    end

    class Counter
      attr_accessor :prefix_override

      def initialize(num = 0, opts = { numerals: :arabic })
        @unnumbered = false
        @num = num
        @letter = ""
        @subseq = ""
        reset_overrides
        @style = opts[:numerals]
        @skip_i = opts[:skip_i]
        @prefix = opts[:prefix]
        @base = ""
        if num.is_a? String
          if /^\d+$/.match?(num)
            @num = num.to_i
          else
            @num = nil
            @base = num[0..-2]
            @letter = num[-1]
          end
        end
      end

      def reset_overrides
        @letter_override = nil
        @number_override = nil
        @prefix_override = nil
      end

      def new_subseq_increment(node)
        @subseq = node["subsequence"]
        @num += 1 unless @num.nil?
        @letter = node["subsequence"] ? "a" : ""
        @base = ""
        new_subseq_increment1(node) if node["number"]
      end

      def new_subseq_increment1(node)
        /^(?<b>.*?)(?<n>\d*)(?<a>[a-zA-Z]*)$/ =~ node["number"]
        if !n.empty? || !a.empty?
          @letter_override = @letter = a unless a.empty?
          @number_override = @num = n.to_i unless n.empty?
          @base = b
        else
          @letter_override = node["number"]
          @letter = @letter_override if /^[a-zA-Z]$/.match?(@letter_override)
        end
      end

      def sequence_increment(node)
        if node["branch-number"]
          @prefix_override = node["branch-number"]
        elsif node["number"]
          @base = @letter_override = @number_override = ""
          /^(?<b>.*?)(?<n>\d+)$/ =~ node["number"]
          if blank?(n)
            @num = nil
            @base = node["number"][0..-2]
            @letter = @letter_override = node["number"][-1]
          else
            @number_override = @num = n.to_i
            @base = b
            @letter = ""
          end
        else @num += 1
        end
      end

      def subsequence_increment(node)
        return increment_letter unless node["number"]

        @base = ""
        @letter_override = node["number"]
        /^(?<b>.*?)(?<n>\d*)(?<a>[a-zA-Z])$/ =~ node["number"]
        if blank?(a) then subsequence_increment_no_letter(node)
        else
          @letter_override = @letter = a
          @base = b
          @number_override = @num = n.to_i unless n.empty?
        end
      end

      def subsequence_increment_no_letter(node)
        if /^\d+$/.match?(node["number"])
          @letter_override = @letter = ""
          @number_override = @num = node["number"].to_i
        else
          /^(?<b>.*)(?<a>[a-zA-Z])$/ =~ node["number"]
          unless blank?(a)
            @letter = @letter_override = a
            @base = b
          end
        end
      end

      def string_inc(str, start)
        return start if str.empty?

        str[0..-2] + (str[-1].ord + 1).chr.to_s
      end

      def increment_letter
        clock_letter
        @letter = (@letter.ord + 1).chr.to_s
        @skip_i && %w(i I).include?(@letter) and
          @letter = (@letter.ord + 1).chr.to_s
      end

      def clock_letter
        case @letter
        when "Z"
          @letter = "@"
          @base = string_inc(@base, "A")
        when "z"
          @letter = "`"
          @base = string_inc(@base, "a")
        end
      end

      def blank?(str)
        str.nil? || str.empty?
      end

      def increment(node)
        @unnumbered = node["unnumbered"] == "true" || node["hidden"] == "true" and return self
        reset_overrides
        if node["subsequence"] != @subseq &&
            !(blank?(node["subsequence"]) && blank?(@subseq))
          new_subseq_increment(node)
        elsif @letter.empty? then sequence_increment(node)
        else subsequence_increment(node)
        end
        self
      end

      def print
        @unnumbered and return nil
        @prefix_override and return @prefix_override
        num = @number_override || @num
        out = @style == :roman && !num.nil? ? RomanNumerals.to_roman(num) : num
        "#{@prefix}#{@base}#{out}#{@letter_override || @letter}"
      end

      def ol_type(list, depth)
        return list["type"].to_sym if list["type"]
        return :arabic if [2, 7].include? depth
        return :alphabet if [1, 6].include? depth
        return :alphabet_upper if [4, 9].include? depth
        return :roman if [3, 8].include? depth
        return :roman_upper if [5, 10].include? depth

        :arabic
      end

      def listlabel(list, depth)
        case ol_type(list, depth)
        when :arabic then @num.to_s
        when :alphabet then (96 + @num).chr.to_s
        when :alphabet_upper then (64 + @num).chr.to_s
        when :roman then RomanNumerals.to_roman(@num).downcase
        when :roman_upper then RomanNumerals.to_roman(@num).upcase
        end
      end
    end
  end
end