/src/libe-book/src/lib/FictionBook2Parser.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 <cassert> |
11 | | |
12 | | #include "libebook_utils.h" |
13 | | #include "libebook_xml.h" |
14 | | |
15 | | #include "FictionBook2BinaryContext.h" |
16 | | #include "FictionBook2BlockContext.h" |
17 | | #include "FictionBook2Collector.h" |
18 | | #include "FictionBook2ContentCollector.h" |
19 | | #include "FictionBook2ExtrasCollector.h" |
20 | | #include "FictionBook2Parser.h" |
21 | | #include "FictionBook2MetadataCollector.h" |
22 | | #include "FictionBook2MetadataContext.h" |
23 | | #include "FictionBook2TableContext.h" |
24 | | #include "FictionBook2TextContext.h" |
25 | | #include "FictionBook2Token.h" |
26 | | |
27 | | namespace libebook |
28 | | { |
29 | | |
30 | | namespace |
31 | | { |
32 | | |
33 | | class DocumentContext : public FictionBook2ParserContext |
34 | | { |
35 | | // no copying |
36 | | DocumentContext(const DocumentContext &other); |
37 | | DocumentContext &operator=(const DocumentContext &other); |
38 | | |
39 | | public: |
40 | | DocumentContext(FictionBook2Collector::NoteMap_t ¬es, FictionBook2Collector::BinaryMap_t &bitmaps, librevenge::RVNGTextInterface *document = nullptr); |
41 | | |
42 | | private: |
43 | | std::shared_ptr<FictionBook2XMLParserContext> leaveContext() const override; |
44 | | |
45 | | std::shared_ptr<FictionBook2XMLParserContext> element(const FictionBook2TokenData &name, const FictionBook2TokenData &ns) override; |
46 | | void startOfElement() override; |
47 | | void endOfElement() override; |
48 | | void attribute(const FictionBook2TokenData &name, const FictionBook2TokenData *ns, const char *value) override; |
49 | | void endOfAttributes() override; |
50 | | void text(const char *text) override; |
51 | | |
52 | | private: |
53 | | librevenge::RVNGTextInterface *const m_document; |
54 | | FictionBook2Collector::NoteMap_t &m_notes; |
55 | | FictionBook2Collector::BinaryMap_t &m_bitmaps; |
56 | | bool m_generating; |
57 | | }; |
58 | | |
59 | | class FictionBookGeneratorContext : public FictionBook2NodeContextBase |
60 | | { |
61 | | // no copying |
62 | | FictionBookGeneratorContext(const FictionBookGeneratorContext &other); |
63 | | FictionBookGeneratorContext &operator=(const FictionBookGeneratorContext &other); |
64 | | |
65 | | public: |
66 | | FictionBookGeneratorContext(std::shared_ptr<FictionBook2ParserContext> parentContext, const FictionBook2Collector::NoteMap_t ¬es, const FictionBook2Collector::BinaryMap_t &bitmaps, librevenge::RVNGTextInterface *document); |
67 | | |
68 | | private: |
69 | | std::shared_ptr<FictionBook2XMLParserContext> element(const FictionBook2TokenData &name, const FictionBook2TokenData &ns) override; |
70 | | void startOfElement() override; |
71 | | void endOfElement() override; |
72 | | void attribute(const FictionBook2TokenData &name, const FictionBook2TokenData *ns, const char *value) override; |
73 | | |
74 | | private: |
75 | | librevenge::RVNGTextInterface *const m_document; |
76 | | librevenge::RVNGPropertyList m_metadata; |
77 | | FictionBook2MetadataCollector m_metadataCollector; |
78 | | FictionBook2ContentCollector m_contentCollector; |
79 | | bool m_bodyRead; |
80 | | }; |
81 | | |
82 | | class FictionBookGathererContext : public FictionBook2NodeContextBase |
83 | | { |
84 | | // no copying |
85 | | FictionBookGathererContext(const FictionBookGathererContext &other); |
86 | | FictionBookGathererContext &operator=(const FictionBookGathererContext &other); |
87 | | |
88 | | public: |
89 | | FictionBookGathererContext(std::shared_ptr<FictionBook2ParserContext> parentContext, FictionBook2Collector::NoteMap_t ¬es, FictionBook2Collector::BinaryMap_t &bitmaps); |
90 | | |
91 | | private: |
92 | | std::shared_ptr<FictionBook2XMLParserContext> element(const FictionBook2TokenData &name, const FictionBook2TokenData &ns) override; |
93 | | void endOfElement() override; |
94 | | void attribute(const FictionBook2TokenData &name, const FictionBook2TokenData *ns, const char *value) override; |
95 | | |
96 | | private: |
97 | | FictionBook2Collector::NoteMap_t &m_notes; |
98 | | FictionBook2Collector::BinaryMap_t &m_bitmaps; |
99 | | FictionBook2ExtrasCollector m_collector; |
100 | | bool m_firstBody; |
101 | | }; |
102 | | |
103 | | class StylesheetContext : public FictionBook2NodeContextBase |
104 | | { |
105 | | }; |
106 | | |
107 | | } |
108 | | |
109 | | namespace |
110 | | { |
111 | | |
112 | | FictionBookGeneratorContext::FictionBookGeneratorContext( |
113 | | std::shared_ptr<FictionBook2ParserContext> parentContext, |
114 | | const FictionBook2Collector::NoteMap_t ¬es, const FictionBook2Collector::BinaryMap_t &bitmaps, |
115 | | librevenge::RVNGTextInterface *const document) |
116 | 3.28k | : FictionBook2NodeContextBase(parentContext) |
117 | 3.28k | , m_document(document) |
118 | 3.28k | , m_metadata() |
119 | 3.28k | , m_metadataCollector(m_metadata) |
120 | 3.28k | , m_contentCollector(m_document, m_metadata, notes, bitmaps) |
121 | 3.28k | , m_bodyRead(false) |
122 | 3.28k | { |
123 | 3.28k | } |
124 | | |
125 | | std::shared_ptr<FictionBook2XMLParserContext> FictionBookGeneratorContext::element(const FictionBook2TokenData &name, const FictionBook2TokenData &ns) |
126 | 11.2k | { |
127 | 11.2k | if (FictionBook2Token::NS_FICTIONBOOK == getFictionBook2TokenID(ns)) |
128 | 10.9k | { |
129 | 10.9k | switch (getFictionBook2TokenID(name)) |
130 | 10.9k | { |
131 | 0 | case FictionBook2Token::stylesheet : |
132 | | // ignore |
133 | 0 | break; |
134 | 6.00k | case FictionBook2Token::description : |
135 | 6.00k | return std::make_shared<FictionBook2DescriptionContext>(shared_from_this(), &m_metadataCollector); |
136 | 2.35k | case FictionBook2Token::body : |
137 | 2.35k | { |
138 | 2.35k | if (!m_bodyRead) |
139 | 1.28k | { |
140 | 1.28k | m_document->startDocument(librevenge::RVNGPropertyList()); |
141 | 1.28k | m_document->setDocumentMetaData(m_metadata); |
142 | 1.28k | m_bodyRead = true; |
143 | 1.28k | return std::make_shared<FictionBook2BodyContext>(shared_from_this(), &m_contentCollector); |
144 | 1.28k | } |
145 | 2.35k | } |
146 | 1.06k | break; |
147 | 2.63k | default : |
148 | 2.63k | break; |
149 | 10.9k | } |
150 | 10.9k | } |
151 | | |
152 | 4.00k | return std::make_shared<FictionBook2SkipElementContext>(shared_from_this()); |
153 | 11.2k | } |
154 | | |
155 | | void FictionBookGeneratorContext::startOfElement() |
156 | 3.28k | { |
157 | 3.28k | } |
158 | | |
159 | | void FictionBookGeneratorContext::endOfElement() |
160 | 2.96k | { |
161 | 2.96k | m_document->endDocument(); |
162 | 2.96k | } |
163 | | |
164 | | void FictionBookGeneratorContext::attribute(const FictionBook2TokenData &, const FictionBook2TokenData *, const char *) |
165 | 70 | { |
166 | 70 | } |
167 | | |
168 | | FictionBookGathererContext::FictionBookGathererContext(std::shared_ptr<FictionBook2ParserContext> parentContext, FictionBook2Collector::NoteMap_t ¬es, FictionBook2Collector::BinaryMap_t &bitmaps) |
169 | 5.70k | : FictionBook2NodeContextBase(parentContext) |
170 | 5.70k | , m_notes(notes) |
171 | 5.70k | , m_bitmaps(bitmaps) |
172 | 5.70k | , m_collector(m_notes, m_bitmaps) |
173 | 5.70k | , m_firstBody(true) |
174 | 5.70k | { |
175 | 5.70k | } |
176 | | |
177 | | std::shared_ptr<FictionBook2XMLParserContext> FictionBookGathererContext::element(const FictionBook2TokenData &name, const FictionBook2TokenData &ns) |
178 | 22.1k | { |
179 | 22.1k | if (FictionBook2Token::NS_FICTIONBOOK == getFictionBook2TokenID(ns)) |
180 | 21.8k | { |
181 | 21.8k | switch (getFictionBook2TokenID(name)) |
182 | 21.8k | { |
183 | 8.27k | case FictionBook2Token::body : |
184 | 8.27k | { |
185 | 8.27k | if (m_firstBody) |
186 | 3.19k | m_firstBody = false; |
187 | 5.07k | else |
188 | 5.07k | return std::make_shared<FictionBook2BodyContext>(shared_from_this(), &m_collector); |
189 | 8.27k | } |
190 | 3.19k | break; |
191 | 4.30k | case FictionBook2Token::binary : |
192 | 4.30k | return std::make_shared<FictionBook2BinaryContext>(shared_from_this(), &m_collector); |
193 | 9.30k | default: |
194 | 9.30k | break; |
195 | 21.8k | } |
196 | 21.8k | } |
197 | | |
198 | 12.8k | return std::make_shared<FictionBook2SkipElementContext>(shared_from_this()); |
199 | 22.1k | } |
200 | | |
201 | | void FictionBookGathererContext::endOfElement() |
202 | 3.05k | { |
203 | 3.05k | } |
204 | | |
205 | | void FictionBookGathererContext::attribute(const FictionBook2TokenData &, const FictionBook2TokenData *, const char *) |
206 | 78 | { |
207 | 78 | } |
208 | | |
209 | | DocumentContext::DocumentContext(FictionBook2Collector::NoteMap_t ¬es, FictionBook2Collector::BinaryMap_t &bitmaps, librevenge::RVNGTextInterface *const document) |
210 | 44.7k | : FictionBook2ParserContext(nullptr) |
211 | 44.7k | , m_document(document) |
212 | 44.7k | , m_notes(notes) |
213 | 44.7k | , m_bitmaps(bitmaps) |
214 | 44.7k | , m_generating(document != nullptr) |
215 | 44.7k | { |
216 | 44.7k | } |
217 | | |
218 | | std::shared_ptr<FictionBook2XMLParserContext> DocumentContext::element(const FictionBook2TokenData &name, const FictionBook2TokenData &ns) |
219 | 9.00k | { |
220 | 9.00k | if ((FictionBook2Token::NS_FICTIONBOOK == getFictionBook2TokenID(ns)) && (FictionBook2Token::FictionBook == getFictionBook2TokenID(name))) |
221 | 8.98k | { |
222 | 8.98k | if (m_generating) |
223 | 3.28k | return std::make_shared<FictionBookGeneratorContext>(shared_from_this(), m_notes, m_bitmaps, m_document); |
224 | 5.70k | else |
225 | 5.70k | return std::make_shared<FictionBookGathererContext>(shared_from_this(), m_notes, m_bitmaps); |
226 | 8.98k | } |
227 | | |
228 | 18 | return nullptr; |
229 | 9.00k | } |
230 | | |
231 | | std::shared_ptr<FictionBook2XMLParserContext> DocumentContext::leaveContext() const |
232 | 0 | { |
233 | 0 | return nullptr; |
234 | 0 | } |
235 | | |
236 | | void DocumentContext::startOfElement() |
237 | 0 | { |
238 | 0 | } |
239 | | |
240 | | void DocumentContext::endOfElement() |
241 | 0 | { |
242 | 0 | } |
243 | | |
244 | | void DocumentContext::attribute(const FictionBook2TokenData &, const FictionBook2TokenData *, const char *) |
245 | 0 | { |
246 | 0 | } |
247 | | |
248 | | void DocumentContext::endOfAttributes() |
249 | 0 | { |
250 | 0 | } |
251 | | |
252 | | void DocumentContext::text(const char *) |
253 | 0 | { |
254 | 0 | } |
255 | | |
256 | | } |
257 | | |
258 | | namespace |
259 | | { |
260 | | |
261 | | void processAttribute(std::shared_ptr<FictionBook2XMLParserContext> const context, const xmlTextReaderPtr reader) |
262 | 159k | { |
263 | 159k | const FictionBook2TokenData *const name = getFictionBook2Token(xmlTextReaderConstLocalName(reader)); |
264 | 159k | const xmlChar *const nsUri = xmlTextReaderConstNamespaceUri(reader); |
265 | 159k | const FictionBook2TokenData *const ns = nsUri ? getFictionBook2Token(nsUri) : nullptr; |
266 | 159k | if (name && (FictionBook2Token::NS_XMLNS != getFictionBook2TokenID(ns))) // ignore unknown attributes and namespace decls |
267 | 111k | context->attribute(*name, ns, reinterpret_cast<const char *>(xmlTextReaderConstValue(reader))); |
268 | 159k | } |
269 | | |
270 | | std::shared_ptr<FictionBook2XMLParserContext> processNode(std::shared_ptr<FictionBook2XMLParserContext> context, const xmlTextReaderPtr reader) |
271 | 5.57M | { |
272 | 5.57M | std::shared_ptr<FictionBook2XMLParserContext> newContext = context; |
273 | 5.57M | switch (xmlTextReaderNodeType(reader)) |
274 | 5.57M | { |
275 | 2.85M | case XML_READER_TYPE_ELEMENT : |
276 | 2.85M | { |
277 | 2.85M | const xmlChar *name_str = xmlTextReaderConstLocalName(reader); |
278 | 2.85M | const xmlChar *ns_str = xmlTextReaderConstNamespaceUri(reader); |
279 | | |
280 | 2.85M | const FictionBook2TokenData *name = name_str ? getFictionBook2Token(name_str) : nullptr; |
281 | 2.85M | const FictionBook2TokenData *ns = ns_str ? getFictionBook2Token(ns_str) : nullptr; |
282 | | |
283 | 2.85M | if (!name || !ns) |
284 | | // TODO: unknown elements should not be skipped entirely, but |
285 | | // their content should be processed as if they did not exist. |
286 | | // Unfortunately this would be quite hard (if not impossible) to |
287 | | // do in the current parser framework |
288 | 1.04M | newContext = std::make_shared<FictionBook2SkipElementContext>(std::dynamic_pointer_cast<FictionBook2ParserContext>(context)); |
289 | 1.81M | else |
290 | 1.81M | newContext = context->element(*name, *ns); |
291 | | |
292 | 2.85M | if (newContext) |
293 | 2.85M | { |
294 | 2.85M | newContext->startOfElement(); |
295 | 2.85M | const bool isEmpty = xmlTextReaderIsEmptyElement(reader); |
296 | | |
297 | 2.85M | if (xmlTextReaderHasAttributes(reader)) |
298 | 97.3k | { |
299 | 97.3k | int ret = xmlTextReaderMoveToFirstAttribute(reader); |
300 | 256k | while (1 == ret) |
301 | 159k | { |
302 | 159k | processAttribute(newContext, reader); |
303 | 159k | ret = xmlTextReaderMoveToNextAttribute(reader); |
304 | 159k | } |
305 | 97.3k | if (0 > ret) // some error while reading |
306 | 0 | newContext = nullptr; |
307 | 97.3k | } |
308 | 2.85M | if (newContext) |
309 | 2.85M | newContext->endOfAttributes(); |
310 | | |
311 | 2.85M | if (newContext && isEmpty) |
312 | 2.18M | { |
313 | 2.18M | newContext->endOfElement(); |
314 | 2.18M | newContext = newContext->leaveContext(); |
315 | 2.18M | } |
316 | 2.85M | } |
317 | | |
318 | 2.85M | break; |
319 | 0 | } |
320 | 0 | case XML_READER_TYPE_ATTRIBUTE : |
321 | 0 | assert(false && "How did i ever got there?"); |
322 | 0 | processAttribute(context, reader); |
323 | 0 | break; |
324 | 640k | case XML_READER_TYPE_END_ELEMENT : |
325 | 640k | { |
326 | 640k | context->endOfElement(); |
327 | 640k | newContext = context->leaveContext(); |
328 | 640k | break; |
329 | 0 | } |
330 | 1.57M | case XML_READER_TYPE_TEXT : |
331 | 1.57M | { |
332 | 1.57M | std::unique_ptr<xmlChar, decltype(xmlFree)> text(xmlTextReaderReadString(reader), xmlFree); |
333 | 1.57M | context->text(reinterpret_cast<char *>(text.get())); |
334 | 1.57M | break; |
335 | 0 | } |
336 | 501k | default : |
337 | | // ignore other types of XML content |
338 | 501k | break; |
339 | 5.57M | } |
340 | | |
341 | 5.57M | return newContext; |
342 | 5.57M | } |
343 | | |
344 | | } |
345 | | |
346 | | FictionBook2Parser::FictionBook2Parser(librevenge::RVNGInputStream *input) |
347 | 25.9k | : m_input(input) |
348 | 25.9k | { |
349 | 25.9k | assert(m_input); |
350 | 25.9k | } |
351 | | |
352 | | bool FictionBook2Parser::parse(std::shared_ptr<FictionBook2XMLParserContext> const context) const |
353 | 44.7k | { |
354 | 44.7k | m_input->seek(0, librevenge::RVNG_SEEK_SET); |
355 | | |
356 | 44.7k | const auto reader = makeXmlReader(m_input); |
357 | 44.7k | if (!reader) |
358 | 0 | return false; |
359 | | |
360 | 44.7k | int ret = xmlTextReaderRead(reader.get()); |
361 | 44.7k | std::shared_ptr<FictionBook2XMLParserContext> currentContext = context; |
362 | 5.61M | while ((1 == ret) && currentContext) |
363 | 5.57M | { |
364 | 5.57M | currentContext = processNode(currentContext, reader.get()); |
365 | 5.57M | if (currentContext) |
366 | 5.57M | ret = xmlTextReaderRead(reader.get()); |
367 | 5.57M | } |
368 | | |
369 | 44.7k | xmlTextReaderClose(reader.get()); |
370 | | |
371 | | // we processed all input and it was valid |
372 | 44.7k | return (!currentContext || (currentContext == context)) && m_input->isEnd(); |
373 | 44.7k | } |
374 | | |
375 | | bool FictionBook2Parser::parse(librevenge::RVNGTextInterface *const document) const |
376 | 25.9k | { |
377 | 25.9k | FictionBook2Collector::NoteMap_t notes; |
378 | 25.9k | FictionBook2Collector::BinaryMap_t bitmaps; |
379 | | |
380 | 25.9k | { |
381 | | // in the 1st pass we gather notes and bitmaps |
382 | 25.9k | DocumentContext context(notes, bitmaps); |
383 | 25.9k | if (!parse(std::shared_ptr<DocumentContext>(&context, EBOOKDummyDeleter()))) |
384 | 7.08k | return false; |
385 | 25.9k | } |
386 | | |
387 | 18.8k | DocumentContext context(notes, bitmaps, document); |
388 | 18.8k | return parse(std::shared_ptr<DocumentContext>(&context, EBOOKDummyDeleter())); |
389 | 25.9k | } |
390 | | |
391 | | } |
392 | | |
393 | | /* vim:set shiftwidth=2 softtabstop=2 expandtab: */ |