/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: */ |