Class: IsoDoc::XrefGen::Counter

Inherits:
Object
  • Object
show all
Includes:
OlTypeProvider
Defined in:
lib/isodoc/xref/xref_counter.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from OlTypeProvider

#ol_type

Constructor Details

#initialize(num = 0, opts = { numerals: :arabic }) ⇒ Counter

Returns a new instance of Counter.



24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# File 'lib/isodoc/xref/xref_counter.rb', line 24

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]
  @separator = opts[:separator] || "."
  @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

Instance Attribute Details

#prefix_overrideObject

Returns the value of attribute prefix_override.



22
23
24
# File 'lib/isodoc/xref/xref_counter.rb', line 22

def prefix_override
  @prefix_override
end

Instance Method Details

#blank?(str) ⇒ Boolean

Returns:

  • (Boolean)


154
155
156
# File 'lib/isodoc/xref/xref_counter.rb', line 154

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

#clock_letterObject



143
144
145
146
147
148
149
150
151
152
# File 'lib/isodoc/xref/xref_counter.rb', line 143

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

#increment(node) ⇒ Object



158
159
160
161
162
163
164
165
166
167
168
169
# File 'lib/isodoc/xref/xref_counter.rb', line 158

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

#increment_letterObject



136
137
138
139
140
141
# File 'lib/isodoc/xref/xref_counter.rb', line 136

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

#listlabel(list, depth) ⇒ Object



210
211
212
213
214
215
216
217
218
219
# File 'lib/isodoc/xref/xref_counter.rb', line 210

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
  when :japanese then num.localize(:ja).spellout
  end
end

#new_subseq_increment(node) ⇒ Object



52
53
54
55
56
57
58
# File 'lib/isodoc/xref/xref_counter.rb', line 52

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

#new_subseq_increment1(node) ⇒ Object



60
61
62
63
64
65
66
67
68
69
70
# File 'lib/isodoc/xref/xref_counter.rb', line 60

def new_subseq_increment1(node)
  b, n, a = parse_number_suffix(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

#parse_number_suffix(str) ⇒ Object

Decompose a counter number string into [prefix, digits, letters] by scanning from the right. Examples:

"A1b"      => ["A",       "1",  "b"  ]
"prefix-3" => ["prefix-", "3",  ""   ]
"ABC"      => ["",        "",   "ABC" ]
"42"       => ["",        "42", ""   ]

Replaces polynomial regexes like /^(?<b>.*?)(?<n>d*)(?<a>*)$/ with O(n) rindex operations that cannot backtrack.



198
199
200
201
202
203
204
205
206
207
208
# File 'lib/isodoc/xref/xref_counter.rb', line 198

def parse_number_suffix(str)
  # Step 1: split off all trailing letters
  i = str.rindex(/[^a-zA-Z]/) # index of last non-letter char
  a = i.nil? ? str : str[(i + 1)..]
  rest = i.nil? ? "" : str[..i]
  # Step 2: split off all trailing digits from the remaining prefix
  j = rest.rindex(/[^\d]/) # index of last non-digit char
  n = j.nil? ? rest : rest[(j + 1)..]
  b = j.nil? ? "" : rest[..j]
  [b, n, a]
end


180
181
182
183
184
185
186
187
188
# File 'lib/isodoc/xref/xref_counter.rb', line 180

def print
  @unnumbered and return nil
  @prefix_override and return @prefix_override
  num = @number_override || @num
  out = style_number(num)
  prefix = @prefix
  prefix &&= "#{prefix}#{@separator}"
  "#{prefix}#{@base}#{out}#{@letter_override || @letter}"
end

#reset_overridesObject



46
47
48
49
50
# File 'lib/isodoc/xref/xref_counter.rb', line 46

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

#sequence_increment(node) ⇒ Object



72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/isodoc/xref/xref_counter.rb', line 72

def sequence_increment(node)
  if node["branch-number"]
    @prefix_override = node["branch-number"]
  elsif node["number"]
    @base = @letter_override = @number_override = ""
    b, n, a = parse_number_suffix(node["number"])
    # Original required digits at the absolute end (no trailing letters).
    # If there are trailing letters, treat as a no-digit-at-end match.
    n = "" unless a.empty?
    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

#string_inc(str, start) ⇒ Object



130
131
132
133
134
# File 'lib/isodoc/xref/xref_counter.rb', line 130

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

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

#style_number(num) ⇒ Object



171
172
173
174
175
176
177
178
# File 'lib/isodoc/xref/xref_counter.rb', line 171

def style_number(num)
  num.nil? and return num
  case @style
  when :roman then RomanNumerals.to_roman(num)
  when :japanese then num.localize(:ja).spellout
  else num
  end
end

#subsequence_increment(node) ⇒ Object



94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/isodoc/xref/xref_counter.rb', line 94

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

  @base = ""
  @letter_override = node["number"]
  # Replace polynomial /^(?<b>.*?)(?<n>\d*)(?<a>[a-zA-Z])$/ with
  # sequential parsing from the right: last char must be a single letter;
  # before it are optional digits; before that is the base prefix b.
  a = /[a-zA-Z]/.match?(node["number"][-1]) ? node["number"][-1] : nil
  if a
    rest = node["number"][..-2]
    j = rest.rindex(/[^\d]/)
    n = j.nil? ? rest : rest[(j + 1)..]
    b = j.nil? ? "" : rest[..j]
  end
  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

#subsequence_increment_no_letter(node) ⇒ Object



117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/isodoc/xref/xref_counter.rb', line 117

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