lib/metanorma/standoc/validate_table.rb
module Metanorma
module Standoc
module Validate
def table_validate(doc)
doc.xpath("//table[colgroup]").each do |t|
maxrowcols_validate(t, t.xpath("./colgroup/col").size)
end
doc.xpath("//table[.//*[@colspan] | .//*[@rowspan]]").each do |t|
maxrowcols_validate(t, max_td_count(t), mode: "row_cols")
end
doc.xpath("//table[.//*[@rowspan]]").each do |t|
maxrowcols_validate(t, max_td_count(t), mode: "thead_row")
end
end
def max_td_count(table)
max = 0
table.xpath("./tr").each do |tr|
n = tr.xpath("./td | ./th").size
max < n and max = n
end
max
end
def maxrowcols_validate(table, maxcols, mode: "row_cols")
case mode
when "row_cols"
maxrowcols_validate0(table, maxcols, "*", mode)
when "thead_row"
%w{thead tbody tfoot}.each do |w|
maxrowcols_validate0(table, maxcols, w, mode)
end
end
end
def maxrowcols_validate0(table, maxcols, tablechild, mode)
cells2d = table.xpath("./#{tablechild}/tr")
.each_with_object([]) { |_r, m| m << {} }
table.xpath("./#{tablechild}/tr").each_with_index do |tr, r|
curr = 0
tr.xpath("./td | ./th").each do |td|
curr = maxcols_validate1(td, r, curr, cells2d, maxcols, mode)
end
end
maxrows_validate(table, cells2d, tablechild, mode)
end
# code doesn't actually do anything, since Asciidoctor refuses to generate
# table with inconsistent column count
def maxcols_validate1(tcell, row, curr, cells2d, maxcols, mode)
rs = tcell&.attr("rowspan")&.to_i || 1
cs = tcell&.attr("colspan")&.to_i || 1
curr = table_tracker_update(cells2d, row, curr, rs, cs)
maxcols_check(curr + cs - 1, maxcols, tcell) if mode == "row_cols"
curr + cs
end
def table_tracker_update(cells2d, row, curr, rowspan, colspan)
cells2d[row] ||= {}
while cells2d[row][curr]
curr += 1
end
(row..(row + rowspan - 1)).each do |y2|
cells2d[y2] ||= {}
(curr..(curr + colspan - 1)).each { |x2| cells2d[y2][x2] = 1 }
end
curr
end
def maxrows_validate(table, cells2d, tablechild, mode)
err = "are inconsistent"
mode == "thead_row" and err = "cannot go outside #{tablechild}"
err = "Table rows in table #{err}: check rowspan"
if cells2d.any? { |x| x.size != cells2d.first.size }
@log.add("Table", table, err, severity: 0)
end
end
# if maxcols or maxrows negative, do not check them
def maxcols_check(col, maxcols, tcell)
if maxcols.positive? && col > maxcols
@log.add("Table", tcell, "Table exceeds maximum number of columns "\
"defined (#{maxcols})", severity: 0)
end
end
end
end
end