FreeCAD

Форк
0
/
Reader.cpp 
675 строк · 20.2 Кб
1
/***************************************************************************
2
 *   Copyright (c) 2011 Jürgen Riegel <juergen.riegel@web.de>              *
3
 *                                                                         *
4
 *   This file is part of the FreeCAD CAx development system.              *
5
 *                                                                         *
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.      *
10
 *                                                                         *
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.                  *
15
 *                                                                         *
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                                *
20
 *                                                                         *
21
 ***************************************************************************/
22

23

24
#include "PreCompiled.h"
25

26
#ifndef _PreComp_
27
#include <memory>
28
#include <xercesc/sax2/XMLReaderFactory.hpp>
29
#endif
30

31
#include <locale>
32

33
#include "Reader.h"
34
#include "Base64.h"
35
#include "Base64Filter.h"
36
#include "Console.h"
37
#include "InputSource.h"
38
#include "Persistence.h"
39
#include "Sequencer.h"
40
#include "Stream.h"
41
#include "XMLTools.h"
42

43
#ifdef _MSC_VER
44
#include <zipios++/zipios-config.h>
45
#endif
46
#include <zipios++/zipinputstream.h>
47
#include <boost/iostreams/filtering_stream.hpp>
48

49
#ifndef XERCES_CPP_NAMESPACE_BEGIN
50
#define XERCES_CPP_NAMESPACE_QUALIFIER
51
using namespace XERCES_CPP_NAMESPACE;
52
#else
53
XERCES_CPP_NAMESPACE_USE
54
#endif
55

56
using namespace std;
57

58

59
// ---------------------------------------------------------------------------
60
//  Base::XMLReader: Constructors and Destructor
61
// ---------------------------------------------------------------------------
62

63
Base::XMLReader::XMLReader(const char* FileName, std::istream& str)
64
    : _File(FileName)
65
{
66
#ifdef _MSC_VER
67
    str.imbue(std::locale::empty());
68
#else
69
    str.imbue(std::locale::classic());
70
#endif
71

72
    // create the parser
73
    parser = XMLReaderFactory::createXMLReader();  // NOLINT
74

75
    parser->setContentHandler(this);
76
    parser->setLexicalHandler(this);
77
    parser->setErrorHandler(this);
78

79
    try {
80
        StdInputSource file(str, _File.filePath().c_str());
81
        _valid = parser->parseFirst(file, token);
82
    }
83
    catch (const XMLException& toCatch) {
84
        char* message = XMLString::transcode(toCatch.getMessage());
85
        cerr << "Exception message is: \n" << message << "\n";
86
        XMLString::release(&message);
87
    }
88
    catch (const SAXParseException& toCatch) {
89
        char* message = XMLString::transcode(toCatch.getMessage());
90
        cerr << "Exception message is: \n" << message << "\n";
91
        XMLString::release(&message);
92
    }
93
#ifndef FC_DEBUG
94
    catch (...) {
95
        cerr << "Unexpected Exception \n";
96
    }
97
#endif
98
}
99

100
Base::XMLReader::~XMLReader()
101
{
102
    //  Delete the parser itself.  Must be done prior to calling Terminate, below.
103
    delete parser;
104
}
105

106
const char* Base::XMLReader::localName() const
107
{
108
    return LocalName.c_str();
109
}
110

111
unsigned int Base::XMLReader::getAttributeCount() const
112
{
113
    return static_cast<unsigned int>(AttrMap.size());
114
}
115

116
long Base::XMLReader::getAttributeAsInteger(const char* AttrName, const char* defaultValue) const
117
{
118
    return stol(getAttribute(AttrName, defaultValue));
119
}
120

121
unsigned long Base::XMLReader::getAttributeAsUnsigned(const char* AttrName,
122
                                                      const char* defaultValue) const
123
{
124
    return stoul(getAttribute(AttrName, defaultValue), nullptr);
125
}
126

127
double Base::XMLReader::getAttributeAsFloat(const char* AttrName, const char* defaultValue) const
128
{
129
    return stod(getAttribute(AttrName, defaultValue), nullptr);
130
}
131

132
const char* Base::XMLReader::getAttribute(const char* AttrName,            // NOLINT
133
                                          const char* defaultValue) const  // NOLINT
134
{
135
    auto pos = AttrMap.find(AttrName);
136

137
    if (pos != AttrMap.end()) {
138
        return pos->second.c_str();
139
    }
140
    if (defaultValue) {
141
        return defaultValue;
142
    }
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());
147
}
148

149
bool Base::XMLReader::hasAttribute(const char* AttrName) const
150
{
151
    return AttrMap.find(AttrName) != AttrMap.end();
152
}
153

154
bool Base::XMLReader::read()
155
{
156
    ReadType = None;
157

158
    try {
159
        parser->parseNext(token);
160
    }
161
    catch (const XMLException& toCatch) {
162

163
        char* message = XMLString::transcode(toCatch.getMessage());
164
        std::string what = message;
165
        XMLString::release(&message);
166
        throw Base::XMLBaseException(what);
167
    }
168
    catch (const SAXParseException& toCatch) {
169

170
        char* message = XMLString::transcode(toCatch.getMessage());
171
        std::string what = message;
172
        XMLString::release(&message);
173
        throw Base::XMLParseException(what);
174
    }
175
    catch (...) {
176
        throw Base::XMLBaseException("Unexpected XML exception");
177
    }
178
    return true;
179
}
180

181
void Base::XMLReader::readElement(const char* ElementName)
182
{
183
    bool ok {};
184
    int currentLevel = Level;
185
    std::string currentName = LocalName;
186
    do {
187
        ok = read();
188
        if (!ok) {
189
            break;
190
        }
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.
194
            break;
195
        }
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");
199
        }
200
    } while ((ReadType != StartElement && ReadType != StartEndElement)
201
             || (ElementName && LocalName != ElementName));
202
}
203

204
bool Base::XMLReader::readNextElement()
205
{
206
    bool ok {};
207
    while (true) {
208
        ok = read();
209
        if (!ok) {
210
            break;
211
        }
212
        if (ReadType == StartElement) {
213
            break;
214
        }
215
        if (ReadType == StartEndElement) {
216
            break;
217
        }
218
        if (ReadType == EndElement) {
219
            break;
220
        }
221
        if (ReadType == EndDocument) {
222
            break;
223
        }
224
    };
225

226
    return (ReadType == StartElement || ReadType == StartEndElement);
227
}
228

229
int Base::XMLReader::level() const
230
{
231
    return Level;
232
}
233

234
bool Base::XMLReader::isEndOfElement() const
235
{
236
    return (ReadType == EndElement);
237
}
238

239
bool Base::XMLReader::isStartOfDocument() const
240
{
241
    return (ReadType == StartDocument);
242
}
243

244
bool Base::XMLReader::isEndOfDocument() const
245
{
246
    return (ReadType == EndDocument);
247
}
248

249
void Base::XMLReader::readEndElement(const char* ElementName, int level)
250
{
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)) {
254
        return;
255
    }
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");
259
    }
260

261
    bool ok {};
262
    do {
263
        ok = read();
264
        if (!ok) {
265
            break;
266
        }
267
        if (ReadType == EndDocument) {
268
            break;
269
        }
270
    } while (ReadType != EndElement
271
             || (ElementName && (LocalName != ElementName || (level >= 0 && level != Level))));
272
}
273

274
void Base::XMLReader::readCharacters(const char* filename, CharStreamFormat format)
275
{
276
    Base::FileInfo fi(filename);
277
    Base::ofstream to(fi, std::ios::out | std::ios::binary | std::ios::trunc);
278
    if (!to) {
279
        throw Base::FileException("XMLReader::readCharacters() Could not open file!");
280
    }
281

282
    beginCharStream(format) >> to.rdbuf();
283
    to.close();
284
    endCharStream();
285
}
286

287
std::streamsize Base::XMLReader::read(char_type* s, std::streamsize n)
288
{
289

290
    char_type* buf = s;
291
    if (CharacterOffset < 0) {
292
        return -1;
293
    }
294

295
    for (;;) {
296
        std::streamsize copy_size =
297
            static_cast<std::streamsize>(Characters.size()) - CharacterOffset;
298
        if (n < copy_size) {
299
            copy_size = n;
300
        }
301
        std::memcpy(s, Characters.c_str() + CharacterOffset, copy_size);
302
        n -= copy_size;
303
        s += copy_size;
304
        CharacterOffset += copy_size;
305

306
        if (!n) {
307
            break;
308
        }
309

310
        if (ReadType == Chars) {
311
            read();
312
        }
313
        else {
314
            CharacterOffset = -1;
315
            break;
316
        }
317
    }
318

319
    return s - buf;
320
}
321

322
void Base::XMLReader::endCharStream()
323
{
324
    CharacterOffset = -1;
325
    CharStream.reset();
326
}
327

328
std::istream& Base::XMLReader::charStream()
329
{
330
    if (!CharStream) {
331
        throw Base::XMLParseException("no current character stream");
332
    }
333
    return *CharStream;
334
}
335

336
std::istream& Base::XMLReader::beginCharStream(CharStreamFormat format)
337
{
338
    if (CharStream) {
339
        throw Base::XMLParseException("recursive character stream");
340
    }
341

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) {
347
        CharacterOffset = 0;
348
        read();
349
    }
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;
355
    }
356
    else {
357
        throw Base::XMLParseException("invalid state while reading character stream");
358
    }
359

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));
365
    }
366
    filteringStream->push(boost::ref(*this));
367
    return *CharStream;
368
}
369

370
void Base::XMLReader::readBinFile(const char* filename)
371
{
372
    Base::FileInfo fi(filename);
373
    Base::ofstream to(fi, std::ios::out | std::ios::binary);
374
    if (!to) {
375
        throw Base::FileException("XMLReader::readBinFile() Could not open file!");
376
    }
377

378
    bool ok {};
379
    do {
380
        ok = read();
381
        if (!ok) {
382
            break;
383
        }
384
    } while (ReadType != EndCDATA);
385

386
    to << Base::base64_decode(Characters);
387
    to.close();
388
}
389

390
void Base::XMLReader::readFiles(zipios::ZipInputStream& zipstream) const
391
{
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
398
    // file, then.
399
    // In either case it's guaranteed that the order of the files is kept.
400
    zipios::ConstEntryPointer entry;
401
    try {
402
        entry = zipstream.getNextEntry();
403
    }
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
407
        return;
408
    }
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) {
416
            ++jt;
417
        }
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()) {
421
            try {
422
                Base::Reader reader(zipstream, jt->FileName, FileVersion);
423
                jt->Object->RestoreDocFile(reader);
424
                if (reader.getLocalReader()) {
425
                    reader.getLocalReader()->readFiles(zipstream);
426
                }
427
            }
428
            catch (...) {
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
433
                // failure.
434
                Base::Console().Error("Reading failed from embedded file: %s\n",
435
                                      entry->toString().c_str());
436
            }
437
            // Go to the next registered file name
438
            it = jt + 1;
439
        }
440

441
        seq.next();
442

443
        // In either case we must go to the next entry
444
        try {
445
            entry = zipstream.getNextEntry();
446
        }
447
        catch (const std::exception&) {
448
            // there is no further entry
449
            break;
450
        }
451
    }
452
}
453

454
const char* Base::XMLReader::addFile(const char* Name, Base::Persistence* Object)
455
{
456
    FileEntry temp;
457
    temp.FileName = Name;
458
    temp.Object = Object;
459

460
    FileList.push_back(temp);
461
    FileNames.push_back(temp.FileName);
462

463
    return Name;
464
}
465

466
const std::vector<std::string>& Base::XMLReader::getFilenames() const
467
{
468
    return FileNames;
469
}
470

471
bool Base::XMLReader::isRegistered(Base::Persistence* Object) const
472
{
473
    if (Object) {
474
        for (const auto& it : FileList) {
475
            if (it.Object == Object) {
476
                return true;
477
            }
478
        }
479
    }
480

481
    return false;
482
}
483

484
void Base::XMLReader::addName(const char* /*unused*/, const char* /*unused*/)
485
{}
486

487
const char* Base::XMLReader::getName(const char* name) const
488
{
489
    return name;
490
}
491

492
bool Base::XMLReader::doNameMapping() const
493
{
494
    return false;
495
}
496

497
// ---------------------------------------------------------------------------
498
//  Base::XMLReader: Implementation of the SAX DocumentHandler interface
499
// ---------------------------------------------------------------------------
500
void Base::XMLReader::startDocument()
501
{
502
    ReadType = StartDocument;
503
}
504

505
void Base::XMLReader::endDocument()
506
{
507
    ReadType = EndDocument;
508
}
509

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)
514
{
515
    Level++;  // new scope
516
    LocalName = StrX(localname).c_str();
517

518
    // saving attributes of the current scope, delete all previously stored ones
519
    AttrMap.clear();
520
    for (unsigned int i = 0; i < attrs.getLength(); i++) {
521
        AttrMap[StrX(attrs.getQName(i)).c_str()] = StrXUTF8(attrs.getValue(i)).c_str();
522
    }
523

524
    ReadType = StartElement;
525
}
526

527
void Base::XMLReader::endElement(const XMLCh* const /*uri*/,
528
                                 const XMLCh* const localname,
529
                                 const XMLCh* const /*qname*/)
530
{
531
    Level--;  // end of scope
532
    LocalName = StrX(localname).c_str();
533

534
    if (ReadType == StartElement) {
535
        ReadType = StartEndElement;
536
    }
537
    else {
538
        ReadType = EndElement;
539
    }
540
}
541

542
void Base::XMLReader::startCDATA()
543
{
544
    ReadType = StartCDATA;
545
}
546

547
void Base::XMLReader::endCDATA()
548
{
549
    ReadType = EndCDATA;
550
}
551

552
void Base::XMLReader::characters(const XMLCh* const chars, const XMLSize_t length)
553
{
554
    Characters = StrX(chars).c_str();
555
    ReadType = Chars;
556
    CharacterCount += length;
557
}
558

559
void Base::XMLReader::ignorableWhitespace(const XMLCh* const /*chars*/, const XMLSize_t /*length*/)
560
{
561
    // fSpaceCount += length;
562
}
563

564
void Base::XMLReader::resetDocument()
565
{
566
    // fAttrCount = 0;
567
    // fCharacterCount = 0;
568
    // fElementCount = 0;
569
    // fSpaceCount = 0;
570
}
571

572

573
// ---------------------------------------------------------------------------
574
//  Base::XMLReader: Overrides of the SAX ErrorHandler interface
575
// ---------------------------------------------------------------------------
576
void Base::XMLReader::error(const XERCES_CPP_NAMESPACE_QUALIFIER SAXParseException& e)
577
{
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;
582
    throw e;
583
}
584

585
void Base::XMLReader::fatalError(const XERCES_CPP_NAMESPACE_QUALIFIER SAXParseException& e)
586
{
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;
591
    throw e;
592
}
593

594
void Base::XMLReader::warning(const XERCES_CPP_NAMESPACE_QUALIFIER SAXParseException& e)
595
{
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;
600
    throw e;
601
}
602

603
void Base::XMLReader::resetErrors()
604
{}
605

606
bool Base::XMLReader::testStatus(ReaderStatus pos) const
607
{
608
    return StatusBits.test(static_cast<size_t>(pos));
609
}
610

611
void Base::XMLReader::setStatus(ReaderStatus pos, bool on)
612
{
613
    StatusBits.set(static_cast<size_t>(pos), on);
614
}
615

616
void Base::XMLReader::setPartialRestore(bool on)
617
{
618
    setStatus(PartialRestore, on);
619
    setStatus(PartialRestoreInDocumentObject, on);
620
    setStatus(PartialRestoreInProperty, on);
621
    setStatus(PartialRestoreInObject, on);
622
}
623

624
void Base::XMLReader::clearPartialRestoreDocumentObject()
625
{
626
    setStatus(PartialRestoreInDocumentObject, false);
627
    setStatus(PartialRestoreInProperty, false);
628
    setStatus(PartialRestoreInObject, false);
629
}
630

631
void Base::XMLReader::clearPartialRestoreProperty()
632
{
633
    setStatus(PartialRestoreInProperty, false);
634
    setStatus(PartialRestoreInObject, false);
635
}
636

637
void Base::XMLReader::clearPartialRestoreObject()
638
{
639
    setStatus(PartialRestoreInObject, false);
640
}
641

642
// ----------------------------------------------------------
643

644
// NOLINTNEXTLINE
645
Base::Reader::Reader(std::istream& str, const std::string& name, int version)
646
    : std::istream(str.rdbuf())
647
    , _str(str)
648
    , _name(name)
649
    , fileVersion(version)
650
{}
651

652
std::string Base::Reader::getFileName() const
653
{
654
    return this->_name;
655
}
656

657
int Base::Reader::getFileVersion() const
658
{
659
    return fileVersion;
660
}
661

662
std::istream& Base::Reader::getStream()
663
{
664
    return this->_str;
665
}
666

667
void Base::Reader::initLocalReader(std::shared_ptr<Base::XMLReader> reader)
668
{
669
    this->localreader = reader;
670
}
671

672
std::shared_ptr<Base::XMLReader> Base::Reader::getLocalReader() const
673
{
674
    return (this->localreader);
675
}
676

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.