Coverage Report

Created: 2026-04-29 07:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libe-book/src/lib/FictionBook2TableContext.cpp
Line
Count
Source
1
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/*
3
 * This file is part of the libe-book project.
4
 *
5
 * This Source Code Form is subject to the terms of the Mozilla Public
6
 * License, v. 2.0. If a copy of the MPL was not distributed with this
7
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8
 */
9
10
#include <algorithm>
11
#include <cstdlib>
12
#include <deque>
13
14
#include "FictionBook2Collector.h"
15
#include "FictionBook2TableContext.h"
16
#include "FictionBook2Token.h"
17
18
namespace libebook
19
{
20
21
class FictionBook2TableModel
22
{
23
  typedef std::deque<bool> Row_t;
24
  typedef std::deque<Row_t> Table_t;
25
26
public:
27
  FictionBook2TableModel();
28
29
  /** Add a row.
30
    *
31
    * @return then number of cells covered at the beginning of the row.
32
    */
33
  size_t addRow();
34
35
  /** Add a cell spanning @c rowSpan rows and @c colSpan columns.
36
    *
37
    * @return the total number of following covered cells.
38
    */
39
  size_t addCell(size_t rowSpan, size_t columnSpan);
40
41
private:
42
  void ensureColumns(Row_t &row);
43
44
private:
45
  Table_t m_table;
46
  size_t m_rows;
47
  size_t m_columns;
48
  size_t m_current_row;
49
  size_t m_current_column;
50
};
51
52
FictionBook2TableModel::FictionBook2TableModel()
53
2.22k
  : m_table()
54
2.22k
  , m_rows(0)
55
2.22k
  , m_columns(0)
56
2.22k
  , m_current_row(0)
57
2.22k
  , m_current_column(0)
58
2.22k
{
59
2.22k
}
60
61
size_t FictionBook2TableModel::addRow()
62
0
{
63
0
  if (m_rows > 0)
64
0
    ++m_current_row;
65
0
  m_current_column = 0;
66
67
0
  if (m_current_row == m_rows)
68
0
  {
69
0
    m_table.push_back(Row_t(m_columns, false));
70
0
    ++m_rows;
71
0
    return 0;
72
0
  }
73
74
0
  size_t covered = 0;
75
0
  const Row_t &row = m_table[m_current_row];
76
0
  for (; (covered != row.size()) && row[covered]; ++covered)
77
0
    ;
78
0
  return covered;
79
0
}
80
81
size_t FictionBook2TableModel::addCell(const size_t rowSpan, const size_t columnSpan)
82
0
{
83
  // make sure the cell and all the covered cells fit into the current row
84
0
  const size_t addedColumns = (columnSpan > 0) ? columnSpan : 1;
85
0
  if ((m_current_column + addedColumns) > m_columns)
86
0
    m_columns = m_current_column + addedColumns;
87
88
0
  if (rowSpan != 0)
89
0
  {
90
0
    for (; m_rows < (m_current_row + rowSpan); ++m_rows)
91
0
      m_table.push_back(Row_t(m_columns, false));
92
0
    for (size_t row = m_current_row + 1; row < m_current_row + rowSpan; ++row)
93
0
    {
94
0
      ensureColumns(m_table[row]);
95
0
      m_table[row][m_current_column] = true;
96
0
    }
97
0
  }
98
99
0
  ++m_current_column;
100
101
0
  size_t column = m_current_column;
102
103
0
  {
104
0
    Row_t &row = m_table[m_current_row];
105
106
0
    ensureColumns(row);
107
108
    // cover the cells
109
0
    std::fill_n(row.begin() + long(m_current_column), addedColumns - 1, true);
110
0
    m_current_column += addedColumns;
111
112
    // find the next uncovered cell position in the current row
113
0
    while ((m_current_column < m_columns) && row[m_current_column])
114
0
      ++m_current_column;
115
0
  }
116
117
0
  return m_current_column - column - 1;
118
0
}
119
120
void FictionBook2TableModel::ensureColumns(Row_t &row)
121
0
{
122
0
  if (row.size() < m_columns)
123
0
    row.insert(row.end(), m_columns - row.size(), false);
124
0
}
125
126
FictionBook2TableContext::FictionBook2TableContext(std::shared_ptr<FictionBook2ParserContext> parentContext, const FictionBook2BlockFormat &format)
127
2.22k
  : FictionBook2BlockFormatContextBase(parentContext, format)
128
2.22k
  , m_model(new FictionBook2TableModel())
129
2.22k
{
130
2.22k
}
131
132
FictionBook2TableContext::~FictionBook2TableContext()
133
2.22k
{
134
2.22k
}
135
136
std::shared_ptr<FictionBook2XMLParserContext> FictionBook2TableContext::element(const FictionBook2TokenData &name, const FictionBook2TokenData &ns)
137
494
{
138
494
  if (FictionBook2Token::NS_FICTIONBOOK == getFictionBook2TokenID(ns))
139
491
  {
140
491
    switch (getFictionBook2TokenID(name))
141
491
    {
142
0
    case FictionBook2Token::tr :
143
0
      return std::make_shared<FictionBook2TrContext>(shared_from_this(), m_model, getBlockFormat());
144
491
    default :
145
491
      break;
146
491
    }
147
491
  }
148
149
494
  return std::make_shared<FictionBook2SkipElementContext>(shared_from_this());
150
494
}
151
152
void FictionBook2TableContext::startOfElement()
153
2.22k
{
154
2.22k
  getCollector()->openTable(getBlockFormat());
155
2.22k
}
156
157
void FictionBook2TableContext::endOfElement()
158
2.20k
{
159
2.20k
  getCollector()->closeTable();
160
2.20k
}
161
162
void FictionBook2TableContext::attribute(const FictionBook2TokenData &name, const FictionBook2TokenData *ns, const char *value)
163
5.59k
{
164
5.59k
  if (FictionBook2_NO_NAMESPACE(ns))
165
5.25k
  {
166
5.25k
    switch (getFictionBook2TokenID(name))
167
5.25k
    {
168
4
    case FictionBook2Token::id :
169
4
      getCollector()->defineID(value);
170
4
      break;
171
0
    case FictionBook2Token::style :
172
      // ignore
173
0
      break;
174
5.25k
    default :
175
5.25k
      break;
176
5.25k
    }
177
5.25k
  }
178
5.59k
}
179
180
FictionBook2CellContext::FictionBook2CellContext(std::shared_ptr<FictionBook2ParserContext> parentContext, const std::shared_ptr<FictionBook2TableModel> &model, const FictionBook2BlockFormat &format, const bool header)
181
0
  : FictionBook2StyleContextBase(parentContext, FictionBook2Style(format))
182
0
  , m_model(model)
183
0
  , m_header(header)
184
0
  , m_opened(false)
185
0
  , m_columnSpan(0)
186
0
  , m_rowSpan(0)
187
0
  , m_coveredColumns(0)
188
0
{
189
0
}
190
191
void FictionBook2CellContext::startOfElement()
192
0
{
193
0
}
194
195
void FictionBook2CellContext::endOfElement()
196
0
{
197
0
  if (!m_opened)
198
0
    openCell();
199
200
0
  getCollector()->closeTableCell();
201
0
  size_t covered = m_coveredColumns;
202
0
  for (; covered > 0; --covered)
203
0
    getCollector()->insertCoveredTableCell();
204
0
}
205
206
void FictionBook2CellContext::attribute(const FictionBook2TokenData &name, const FictionBook2TokenData *ns, const char *value)
207
0
{
208
0
  if (FictionBook2_NO_NAMESPACE(ns))
209
0
  {
210
0
    switch (getFictionBook2TokenID(name))
211
0
    {
212
0
    case FictionBook2Token::colspan :
213
0
      m_columnSpan = std::atoi(value);
214
0
      break;
215
0
    case FictionBook2Token::rowspan :
216
0
      m_rowSpan = std::atoi(value);
217
0
      break;
218
0
    case FictionBook2Token::align :
219
      // TODO: handle this
220
0
      break;
221
0
    case FictionBook2Token::id :
222
0
      getCollector()->defineID(value);
223
0
      break;
224
0
    case FictionBook2Token::style :
225
      // ignore
226
0
      break;
227
0
    default :
228
0
      break;
229
0
    }
230
0
  }
231
0
}
232
233
void FictionBook2CellContext::endOfAttributes()
234
0
{
235
0
  openCell();
236
0
}
237
238
void FictionBook2CellContext::openCell()
239
0
{
240
0
  m_coveredColumns = m_model->addCell(size_t(m_rowSpan), size_t(m_columnSpan));
241
242
0
  getCollector()->openTableCell(m_rowSpan, m_columnSpan);
243
0
  m_opened = true;
244
0
}
245
246
FictionBook2TrContext::FictionBook2TrContext(std::shared_ptr<FictionBook2ParserContext> parentContext, const std::shared_ptr<FictionBook2TableModel> &model, const FictionBook2BlockFormat &format)
247
0
  : FictionBook2BlockFormatContextBase(parentContext, format)
248
0
  , m_model(model)
249
0
  , m_opened(false)
250
0
{
251
0
}
252
253
std::shared_ptr<FictionBook2XMLParserContext> FictionBook2TrContext::element(const FictionBook2TokenData &name, const FictionBook2TokenData &ns)
254
0
{
255
0
  if (FictionBook2Token::NS_FICTIONBOOK == getFictionBook2TokenID(ns))
256
0
  {
257
0
    switch (getFictionBook2TokenID(name))
258
0
    {
259
0
    case FictionBook2Token::th :
260
0
      if (!m_opened)
261
0
        openRow(true);
262
0
      return std::make_shared<FictionBook2CellContext>(shared_from_this(), m_model, getBlockFormat(), true);
263
0
    case FictionBook2Token::td :
264
0
      if (!m_opened)
265
0
        openRow(false);
266
0
      return std::make_shared<FictionBook2CellContext>(shared_from_this(), m_model, getBlockFormat(), false);
267
0
    default :
268
0
      break;
269
0
    }
270
0
  }
271
272
0
  return std::make_shared<FictionBook2SkipElementContext>(shared_from_this());
273
0
}
274
275
void FictionBook2TrContext::endOfElement()
276
0
{
277
0
  if (!m_opened)
278
0
    openRow(false);
279
280
0
  getCollector()->closeTableRow();
281
0
}
282
283
void FictionBook2TrContext::attribute(const FictionBook2TokenData &name, const FictionBook2TokenData *ns, const char *)
284
0
{
285
0
  if ((FictionBook2_NO_NAMESPACE(ns)) && (FictionBook2Token::align == getFictionBook2TokenID(name)))
286
0
  {
287
    // TODO: use this
288
0
  }
289
0
}
290
291
void FictionBook2TrContext::openRow(const bool header)
292
0
{
293
0
  getBlockFormat().headerRow = header;
294
295
0
  size_t coveredCells = m_model->addRow();
296
297
0
  getCollector()->openTableRow(getBlockFormat());
298
0
  m_opened = true;
299
0
  for (; coveredCells > 0; --coveredCells)
300
0
    getCollector()->insertCoveredTableCell();
301
0
}
302
303
}
304
305
/* vim:set shiftwidth=2 softtabstop=2 expandtab: */