1 /***************************************************************************
2     Copyright (C) 2020 Robby Stephenson <robby@periapsis.org>
3  ***************************************************************************/
4 
5 /***************************************************************************
6  *                                                                         *
7  *   This program is free software; you can redistribute it and/or         *
8  *   modify it under the terms of the GNU General Public License as        *
9  *   published by the Free Software Foundation; either version 2 of        *
10  *   the License or (at your option) version 3 or any later version        *
11  *   accepted by the membership of KDE e.V. (or its successor approved     *
12  *   by the membership of KDE e.V.), which shall act as a proxy            *
13  *   defined in Section 14 of version 3 of the license.                    *
14  *                                                                         *
15  *   This program is distributed in the hope that it will be useful,       *
16  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
17  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
18  *   GNU General Public License for more details.                          *
19  *                                                                         *
20  *   You should have received a copy of the GNU General Public License     *
21  *   along with this program.  If not, see <http://www.gnu.org/licenses/>. *
22  *                                                                         *
23  ***************************************************************************/
24 
25 #include "tellicoxmlreader.h"
26 #include "tellico_xml.h"
27 #include "xmlstatehandler.h"
28 #include "../collection.h"
29 #include "../tellico_debug.h"
30 
31 #include <KLocalizedString>
32 
33 using Tellico::Import::TellicoXmlReader;
34 
TellicoXmlReader()35 TellicoXmlReader::TellicoXmlReader() : m_data(new SAX::StateData) {
36   m_handlers.push(new SAX::RootHandler(m_data));
37 }
38 
~TellicoXmlReader()39 TellicoXmlReader::~TellicoXmlReader() {
40   delete m_data;
41   m_data = nullptr;
42   qDeleteAll(m_handlers);
43   m_handlers.clear();
44 }
45 
readNext(const QByteArray & data_)46 bool TellicoXmlReader::readNext(const QByteArray& data_) {
47   Q_ASSERT(!m_handlers.isEmpty());
48   if(m_handlers.isEmpty()) {
49     return false;
50   }
51 
52   m_xml.addData(data_);
53   while(!m_xml.atEnd()) {
54     const auto tokenType = m_xml.readNext();
55     switch(tokenType) {
56       case QXmlStreamReader::StartElement:
57         handleStart();
58         break;
59       case QXmlStreamReader::EndElement:
60         handleEnd();
61         break;
62       case QXmlStreamReader::Characters:
63         handleCharacters();
64         break;
65       case QXmlStreamReader::Invalid:
66         if(m_xml.error() != QXmlStreamReader::PrematureEndOfDocumentError) {
67           myDebug() << "Error!" << m_xml.errorString();
68         }
69         continue; // break out of while loop
70       default:
71         // not handling anything else
72         break;
73     }
74   }
75   // success is no error or error being simply premature end of document
76   return !m_xml.hasError() || m_xml.error() == QXmlStreamReader::PrematureEndOfDocumentError;
77 }
78 
errorString() const79 QString TellicoXmlReader::errorString() const {
80   // for custom errors, include the line and column number
81   return m_xml.error() == QXmlStreamReader::CustomError ?
82     QString::fromLatin1("Fatal parsing error in line %1, column %2. %3")
83                    .arg(m_xml.lineNumber())
84                    .arg(m_xml.columnNumber())
85                    .arg(m_xml.errorString()) :
86     m_xml.errorString();
87 }
88 
isNotWellFormed() const89 bool TellicoXmlReader::isNotWellFormed() const {
90   return m_xml.error() == QXmlStreamReader::NotWellFormedError;
91 }
92 
collection() const93 Tellico::Data::CollPtr TellicoXmlReader::collection() const {
94   return m_data->coll;
95 }
96 
hasImages() const97 bool TellicoXmlReader::hasImages() const {
98   return m_data->hasImages;
99 }
100 
setLoadImages(bool loadImages_)101 void TellicoXmlReader::setLoadImages(bool loadImages_) {
102   m_data->loadImages = loadImages_;
103 }
104 
setShowImageLoadErrors(bool showImageErrors_)105 void TellicoXmlReader::setShowImageLoadErrors(bool showImageErrors_) {
106   m_data->showImageLoadErrors = showImageErrors_;
107 }
108 
handleStart()109 void TellicoXmlReader::handleStart() {
110   SAX::StateHandler* handler = m_handlers.top()->nextHandler(m_xml.namespaceUri(), m_xml.name());
111   Q_ASSERT(handler);
112   m_handlers.push(handler);
113   if(!handler->start(m_xml.namespaceUri(), m_xml.name(), m_xml.attributes())) {
114     m_xml.raiseError(m_data->error);
115   }
116 }
117 
handleEnd()118 void TellicoXmlReader::handleEnd() {
119   m_data->text = m_data->text.trimmed();
120   SAX::StateHandler* handler = m_handlers.pop();
121   if(!handler->end(m_xml.namespaceUri(), m_xml.name())) {
122     m_xml.raiseError(m_data->error);
123   }
124   // need to reset character data, too
125   m_data->text.clear();
126   delete handler;
127 }
128 
handleCharacters()129 void TellicoXmlReader::handleCharacters() {
130   m_data->text.append(m_xml.text());
131 }
132