def self.next_table(table_reader, parent, attributes)
table = Table.new(parent, attributes)
if attributes.key? 'title'
table.title = attributes.delete 'title'
table.assign_caption(attributes.delete 'caption')
end
if (attributes.key? 'cols') && !(colspecs = parse_colspecs attributes['cols']).empty?
table.create_columns colspecs
explicit_colspecs = true
end
skipped = table_reader.skip_blank_lines || 0
parser_ctx = Table::ParserContext.new table_reader, table, attributes
format, loop_idx, implicit_header_boundary = parser_ctx.format, -1, nil
implicit_header = true unless skipped > 0 || (attributes.key? 'header-option') || (attributes.key? 'noheader-option')
while (line = table_reader.read_line)
if (loop_idx += 1) > 0 && line.empty?
line = nil
implicit_header_boundary += 1 if implicit_header_boundary
elsif format == 'psv'
if parser_ctx.starts_with_delimiter? line
line = line.slice 1, line.length
parser_ctx.close_open_cell
implicit_header_boundary = nil if implicit_header_boundary
else
next_cellspec, line = parse_cellspec line, :start, parser_ctx.delimiter
if next_cellspec
parser_ctx.close_open_cell next_cellspec
implicit_header_boundary = nil if implicit_header_boundary
elsif implicit_header_boundary && implicit_header_boundary == loop_idx
implicit_header, implicit_header_boundary = false, nil
end
end
end
if loop_idx == 0 && implicit_header
if table_reader.has_more_lines? && table_reader.peek_line.empty?
implicit_header_boundary = 1
else
implicit_header = false
end
end
while true
if line && (m = parser_ctx.match_delimiter line)
case format
when 'csv'
if parser_ctx.buffer_has_unclosed_quotes? m.pre_match
break if (line = parser_ctx.skip_past_delimiter m).empty?
redo
end
parser_ctx.buffer = %(#{parser_ctx.buffer}#{m.pre_match})
when 'dsv'
if m.pre_match.end_with? '\\'
if (line = parser_ctx.skip_past_escaped_delimiter m).empty?
parser_ctx.buffer = %(#{parser_ctx.buffer}#{LF})
parser_ctx.keep_cell_open
break
end
redo
end
parser_ctx.buffer = %(#{parser_ctx.buffer}#{m.pre_match})
else
if m.pre_match.end_with? '\\'
if (line = parser_ctx.skip_past_escaped_delimiter m).empty?
parser_ctx.buffer = %(#{parser_ctx.buffer}#{LF})
parser_ctx.keep_cell_open
break
end
redo
end
next_cellspec, cell_text = parse_cellspec m.pre_match
parser_ctx.push_cellspec next_cellspec
parser_ctx.buffer = %(#{parser_ctx.buffer}#{cell_text})
end
line = nil if (line = m.post_match).empty?
parser_ctx.close_cell
else
parser_ctx.buffer = %(#{parser_ctx.buffer}#{line}#{LF})
case format
when 'csv'
parser_ctx.buffer = %(#{parser_ctx.buffer.rstrip} )
if parser_ctx.buffer_has_unclosed_quotes?
implicit_header, implicit_header_boundary = false, nil if implicit_header_boundary && loop_idx == 0
parser_ctx.keep_cell_open
else
parser_ctx.close_cell true
end
when 'dsv'
parser_ctx.close_cell true
else
parser_ctx.keep_cell_open
end
break
end
end
if parser_ctx.cell_open?
parser_ctx.close_cell true unless table_reader.has_more_lines?
else
table_reader.skip_blank_lines || break
end
end
unless (table.attributes['colcount'] ||= table.columns.size) == 0 || explicit_colspecs
table.assign_column_widths
end
if implicit_header
table.has_header_option = true
attributes['header-option'] = ''
attributes['options'] = (attributes.key? 'options') ? %(#{attributes['options']},header) : 'header'
end
table.partition_header_footer attributes
table
end