1
/***************************************************************************
2
* Copyright (c) 2011 Jürgen Riegel <juergen.riegel@web.de> *
4
* This file is part of the FreeCAD CAx development system. *
6
* This library is free software; you can redistribute it and/or *
7
* modify it under the terms of the GNU Library General Public *
8
* License as published by the Free Software Foundation; either *
9
* version 2 of the License, or (at your option) any later version. *
11
* This library is distributed in the hope that it will be useful, *
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14
* GNU Library General Public License for more details. *
16
* You should have received a copy of the GNU Library General Public *
17
* License along with this library; see the file COPYING.LIB. If not, *
18
* write to the Free Software Foundation, Inc., 59 Temple Place, *
19
* Suite 330, Boston, MA 02111-1307, USA *
21
***************************************************************************/
24
#include "PreCompiled.h"
28
#include <xercesc/sax2/XMLReaderFactory.hpp>
35
#include "Base64Filter.h"
37
#include "InputSource.h"
38
#include "Persistence.h"
44
#include <zipios++/zipios-config.h>
46
#include <zipios++/zipinputstream.h>
47
#include <boost/iostreams/filtering_stream.hpp>
49
#ifndef XERCES_CPP_NAMESPACE_BEGIN
50
#define XERCES_CPP_NAMESPACE_QUALIFIER
51
using namespace XERCES_CPP_NAMESPACE;
53
XERCES_CPP_NAMESPACE_USE
59
// ---------------------------------------------------------------------------
60
// Base::XMLReader: Constructors and Destructor
61
// ---------------------------------------------------------------------------
63
Base::XMLReader::XMLReader(const char* FileName, std::istream& str)
67
str.imbue(std::locale::empty());
69
str.imbue(std::locale::classic());
73
parser = XMLReaderFactory::createXMLReader(); // NOLINT
75
parser->setContentHandler(this);
76
parser->setLexicalHandler(this);
77
parser->setErrorHandler(this);
80
StdInputSource file(str, _File.filePath().c_str());
81
_valid = parser->parseFirst(file, token);
83
catch (const XMLException& toCatch) {
84
char* message = XMLString::transcode(toCatch.getMessage());
85
cerr << "Exception message is: \n" << message << "\n";
86
XMLString::release(&message);
88
catch (const SAXParseException& toCatch) {
89
char* message = XMLString::transcode(toCatch.getMessage());
90
cerr << "Exception message is: \n" << message << "\n";
91
XMLString::release(&message);
95
cerr << "Unexpected Exception \n";
100
Base::XMLReader::~XMLReader()
102
// Delete the parser itself. Must be done prior to calling Terminate, below.
106
const char* Base::XMLReader::localName() const
108
return LocalName.c_str();
111
unsigned int Base::XMLReader::getAttributeCount() const
113
return static_cast<unsigned int>(AttrMap.size());
116
long Base::XMLReader::getAttributeAsInteger(const char* AttrName, const char* defaultValue) const
118
return stol(getAttribute(AttrName, defaultValue));
121
unsigned long Base::XMLReader::getAttributeAsUnsigned(const char* AttrName,
122
const char* defaultValue) const
124
return stoul(getAttribute(AttrName, defaultValue), nullptr);
127
double Base::XMLReader::getAttributeAsFloat(const char* AttrName, const char* defaultValue) const
129
return stod(getAttribute(AttrName, defaultValue), nullptr);
132
const char* Base::XMLReader::getAttribute(const char* AttrName, // NOLINT
133
const char* defaultValue) const // NOLINT
135
auto pos = AttrMap.find(AttrName);
137
if (pos != AttrMap.end()) {
138
return pos->second.c_str();
143
// wrong name, use hasAttribute if not sure!
144
std::ostringstream msg;
145
msg << "XML Attribute: \"" << AttrName << "\" not found";
146
throw Base::XMLAttributeError(msg.str());
149
bool Base::XMLReader::hasAttribute(const char* AttrName) const
151
return AttrMap.find(AttrName) != AttrMap.end();
154
bool Base::XMLReader::read()
159
parser->parseNext(token);
161
catch (const XMLException& toCatch) {
163
char* message = XMLString::transcode(toCatch.getMessage());
164
std::string what = message;
165
XMLString::release(&message);
166
throw Base::XMLBaseException(what);
168
catch (const SAXParseException& toCatch) {
170
char* message = XMLString::transcode(toCatch.getMessage());
171
std::string what = message;
172
XMLString::release(&message);
173
throw Base::XMLParseException(what);
176
throw Base::XMLBaseException("Unexpected XML exception");
181
void Base::XMLReader::readElement(const char* ElementName)
184
int currentLevel = Level;
185
std::string currentName = LocalName;
191
if (ReadType == EndElement && currentName == LocalName && currentLevel >= Level) {
192
// we have reached the end of the element when calling this method
193
// thus we must stop reading on.
196
if (ReadType == EndDocument) {
197
// the end of the document has been reached but we still try to continue on reading
198
throw Base::XMLParseException("End of document reached");
200
} while ((ReadType != StartElement && ReadType != StartEndElement)
201
|| (ElementName && LocalName != ElementName));
204
bool Base::XMLReader::readNextElement()
212
if (ReadType == StartElement) {
215
if (ReadType == StartEndElement) {
218
if (ReadType == EndElement) {
221
if (ReadType == EndDocument) {
226
return (ReadType == StartElement || ReadType == StartEndElement);
229
int Base::XMLReader::level() const
234
bool Base::XMLReader::isEndOfElement() const
236
return (ReadType == EndElement);
239
bool Base::XMLReader::isStartOfDocument() const
241
return (ReadType == StartDocument);
244
bool Base::XMLReader::isEndOfDocument() const
246
return (ReadType == EndDocument);
249
void Base::XMLReader::readEndElement(const char* ElementName, int level)
251
// if we are already at the end of the current element
252
if ((ReadType == EndElement || ReadType == StartEndElement) && ElementName
253
&& LocalName == ElementName && (level < 0 || level == Level)) {
256
if (ReadType == EndDocument) {
257
// the end of the document has been reached but we still try to continue on reading
258
throw Base::XMLParseException("End of document reached");
267
if (ReadType == EndDocument) {
270
} while (ReadType != EndElement
271
|| (ElementName && (LocalName != ElementName || (level >= 0 && level != Level))));
274
void Base::XMLReader::readCharacters(const char* filename, CharStreamFormat format)
276
Base::FileInfo fi(filename);
277
Base::ofstream to(fi, std::ios::out | std::ios::binary | std::ios::trunc);
279
throw Base::FileException("XMLReader::readCharacters() Could not open file!");
282
beginCharStream(format) >> to.rdbuf();
287
std::streamsize Base::XMLReader::read(char_type* s, std::streamsize n)
291
if (CharacterOffset < 0) {
296
std::streamsize copy_size =
297
static_cast<std::streamsize>(Characters.size()) - CharacterOffset;
301
std::memcpy(s, Characters.c_str() + CharacterOffset, copy_size);
304
CharacterOffset += copy_size;
310
if (ReadType == Chars) {
314
CharacterOffset = -1;
322
void Base::XMLReader::endCharStream()
324
CharacterOffset = -1;
328
std::istream& Base::XMLReader::charStream()
331
throw Base::XMLParseException("no current character stream");
336
std::istream& Base::XMLReader::beginCharStream(CharStreamFormat format)
339
throw Base::XMLParseException("recursive character stream");
342
// TODO: An XML element can actually contain a mix of child elements and
343
// characters. So we should not actually demand 'StartElement' here. But
344
// with the current implementation of character stream, we cannot track
345
// child elements and character content at the same time.
346
if (ReadType == StartElement) {
350
else if (ReadType == StartEndElement) {
351
// If we are currently at a self-closing element, just leave the offset
352
// as negative and do not read any characters. This will result in an
353
// empty input stream for the caller.
354
CharacterOffset = -1;
357
throw Base::XMLParseException("invalid state while reading character stream");
360
CharStream = std::make_unique<boost::iostreams::filtering_istream>();
361
auto* filteringStream = dynamic_cast<boost::iostreams::filtering_istream*>(CharStream.get());
362
if (format == CharStreamFormat::Base64Encoded) {
363
filteringStream->push(
364
base64_decoder(Base::base64DefaultBufferSize, Base64ErrorHandling::silent));
366
filteringStream->push(boost::ref(*this));
370
void Base::XMLReader::readBinFile(const char* filename)
372
Base::FileInfo fi(filename);
373
Base::ofstream to(fi, std::ios::out | std::ios::binary);
375
throw Base::FileException("XMLReader::readBinFile() Could not open file!");
384
} while (ReadType != EndCDATA);
386
to << Base::base64_decode(Characters);
390
void Base::XMLReader::readFiles(zipios::ZipInputStream& zipstream) const
392
// It's possible that not all objects inside the document could be created, e.g. if a module
393
// is missing that would know these object types. So, there may be data files inside the zip
394
// file that cannot be read. We simply ignore these files.
395
// On the other hand, however, it could happen that a file should be read that is not part of
396
// the zip file. This happens e.g. if a document is written without GUI up but is read with GUI
397
// up. In this case the associated GUI document asks for its file which is not part of the ZIP
399
// In either case it's guaranteed that the order of the files is kept.
400
zipios::ConstEntryPointer entry;
402
entry = zipstream.getNextEntry();
404
catch (const std::exception&) {
405
// There is no further file at all. This can happen if the
406
// project file was created without GUI
409
std::vector<FileEntry>::const_iterator it = FileList.begin();
410
Base::SequencerLauncher seq("Importing project files...", FileList.size());
411
while (entry->isValid() && it != FileList.end()) {
412
std::vector<FileEntry>::const_iterator jt = it;
413
// Check if the current entry is registered, otherwise check the next registered files as
414
// soon as both file names match
415
while (jt != FileList.end() && entry->getName() != jt->FileName) {
418
// If this condition is true both file names match and we can read-in the data, otherwise
419
// no file name for the current entry in the zip was registered.
420
if (jt != FileList.end()) {
422
Base::Reader reader(zipstream, jt->FileName, FileVersion);
423
jt->Object->RestoreDocFile(reader);
424
if (reader.getLocalReader()) {
425
reader.getLocalReader()->readFiles(zipstream);
429
// For any exception we just continue with the next file.
430
// It doesn't matter if the last reader has read more or
431
// less data than the file size would allow.
432
// All what we need to do is to notify the user about the
434
Base::Console().Error("Reading failed from embedded file: %s\n",
435
entry->toString().c_str());
437
// Go to the next registered file name
443
// In either case we must go to the next entry
445
entry = zipstream.getNextEntry();
447
catch (const std::exception&) {
448
// there is no further entry
454
const char* Base::XMLReader::addFile(const char* Name, Base::Persistence* Object)
457
temp.FileName = Name;
458
temp.Object = Object;
460
FileList.push_back(temp);
461
FileNames.push_back(temp.FileName);
466
const std::vector<std::string>& Base::XMLReader::getFilenames() const
471
bool Base::XMLReader::isRegistered(Base::Persistence* Object) const
474
for (const auto& it : FileList) {
475
if (it.Object == Object) {
484
void Base::XMLReader::addName(const char* /*unused*/, const char* /*unused*/)
487
const char* Base::XMLReader::getName(const char* name) const
492
bool Base::XMLReader::doNameMapping() const
497
// ---------------------------------------------------------------------------
498
// Base::XMLReader: Implementation of the SAX DocumentHandler interface
499
// ---------------------------------------------------------------------------
500
void Base::XMLReader::startDocument()
502
ReadType = StartDocument;
505
void Base::XMLReader::endDocument()
507
ReadType = EndDocument;
510
void Base::XMLReader::startElement(const XMLCh* const /*uri*/,
511
const XMLCh* const localname,
512
const XMLCh* const /*qname*/,
513
const XERCES_CPP_NAMESPACE_QUALIFIER Attributes& attrs)
515
Level++; // new scope
516
LocalName = StrX(localname).c_str();
518
// saving attributes of the current scope, delete all previously stored ones
520
for (unsigned int i = 0; i < attrs.getLength(); i++) {
521
AttrMap[StrX(attrs.getQName(i)).c_str()] = StrXUTF8(attrs.getValue(i)).c_str();
524
ReadType = StartElement;
527
void Base::XMLReader::endElement(const XMLCh* const /*uri*/,
528
const XMLCh* const localname,
529
const XMLCh* const /*qname*/)
531
Level--; // end of scope
532
LocalName = StrX(localname).c_str();
534
if (ReadType == StartElement) {
535
ReadType = StartEndElement;
538
ReadType = EndElement;
542
void Base::XMLReader::startCDATA()
544
ReadType = StartCDATA;
547
void Base::XMLReader::endCDATA()
552
void Base::XMLReader::characters(const XMLCh* const chars, const XMLSize_t length)
554
Characters = StrX(chars).c_str();
556
CharacterCount += length;
559
void Base::XMLReader::ignorableWhitespace(const XMLCh* const /*chars*/, const XMLSize_t /*length*/)
561
// fSpaceCount += length;
564
void Base::XMLReader::resetDocument()
567
// fCharacterCount = 0;
568
// fElementCount = 0;
573
// ---------------------------------------------------------------------------
574
// Base::XMLReader: Overrides of the SAX ErrorHandler interface
575
// ---------------------------------------------------------------------------
576
void Base::XMLReader::error(const XERCES_CPP_NAMESPACE_QUALIFIER SAXParseException& e)
578
// print some details to error output and throw an
579
// exception to abort the parsing
580
cerr << "Error at file " << StrX(e.getSystemId()) << ", line " << e.getLineNumber() << ", char "
581
<< e.getColumnNumber() << endl;
585
void Base::XMLReader::fatalError(const XERCES_CPP_NAMESPACE_QUALIFIER SAXParseException& e)
587
// print some details to error output and throw an
588
// exception to abort the parsing
589
cerr << "Fatal Error at file " << StrX(e.getSystemId()) << ", line " << e.getLineNumber()
590
<< ", char " << e.getColumnNumber() << endl;
594
void Base::XMLReader::warning(const XERCES_CPP_NAMESPACE_QUALIFIER SAXParseException& e)
596
// print some details to error output and throw an
597
// exception to abort the parsing
598
cerr << "Warning at file " << StrX(e.getSystemId()) << ", line " << e.getLineNumber()
599
<< ", char " << e.getColumnNumber() << endl;
603
void Base::XMLReader::resetErrors()
606
bool Base::XMLReader::testStatus(ReaderStatus pos) const
608
return StatusBits.test(static_cast<size_t>(pos));
611
void Base::XMLReader::setStatus(ReaderStatus pos, bool on)
613
StatusBits.set(static_cast<size_t>(pos), on);
616
void Base::XMLReader::setPartialRestore(bool on)
618
setStatus(PartialRestore, on);
619
setStatus(PartialRestoreInDocumentObject, on);
620
setStatus(PartialRestoreInProperty, on);
621
setStatus(PartialRestoreInObject, on);
624
void Base::XMLReader::clearPartialRestoreDocumentObject()
626
setStatus(PartialRestoreInDocumentObject, false);
627
setStatus(PartialRestoreInProperty, false);
628
setStatus(PartialRestoreInObject, false);
631
void Base::XMLReader::clearPartialRestoreProperty()
633
setStatus(PartialRestoreInProperty, false);
634
setStatus(PartialRestoreInObject, false);
637
void Base::XMLReader::clearPartialRestoreObject()
639
setStatus(PartialRestoreInObject, false);
642
// ----------------------------------------------------------
645
Base::Reader::Reader(std::istream& str, const std::string& name, int version)
646
: std::istream(str.rdbuf())
649
, fileVersion(version)
652
std::string Base::Reader::getFileName() const
657
int Base::Reader::getFileVersion() const
662
std::istream& Base::Reader::getStream()
667
void Base::Reader::initLocalReader(std::shared_ptr<Base::XMLReader> reader)
669
this->localreader = reader;
672
std::shared_ptr<Base::XMLReader> Base::Reader::getLocalReader() const
674
return (this->localreader);