25
#include "PreCompiled.h"
28
#include <xercesc/util/PlatformUtils.hpp>
29
#include <xercesc/util/XercesVersion.hpp>
30
#include <xercesc/dom/DOM.hpp>
31
#include <xercesc/dom/DOMImplementation.hpp>
32
#include <xercesc/dom/DOMImplementationLS.hpp>
33
#include <xercesc/framework/StdOutFormatTarget.hpp>
34
#include <xercesc/framework/LocalFileFormatTarget.hpp>
35
#include <xercesc/framework/LocalFileInputSource.hpp>
36
#include <xercesc/parsers/XercesDOMParser.hpp>
37
#include <xercesc/util/XMLUni.hpp>
38
#include <xercesc/util/XMLUniDefs.hpp>
39
#include <xercesc/util/XMLString.hpp>
40
#include <xercesc/sax/ErrorHandler.hpp>
41
#include <xercesc/sax/SAXParseException.hpp>
44
#include <zipios++/zipios-config.h>
45
#include <zipios++/zipfile.h>
46
#include <zipios++/zipinputstream.h>
47
#include <zipios++/zipoutputstream.h>
48
#include <zipios++/meta-iostreams.h>
50
#include "ProjectFile.h"
51
#include "DocumentObject.h"
52
#include <Base/FileInfo.h>
53
#include <Base/InputSource.h>
54
#include <Base/Reader.h>
55
#include <Base/Writer.h>
56
#include <Base/Stream.h>
57
#include <Base/XMLTools.h>
59
#ifndef XERCES_CPP_NAMESPACE_BEGIN
60
#define XERCES_CPP_NAMESPACE_QUALIFIER
61
using namespace XERCES_CPP_NAMESPACE;
63
XERCES_CPP_NAMESPACE_USE
71
explicit DocumentMetadata(XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument* xmlDocument)
72
: xmlDocument{xmlDocument}
75
ProjectFile::Metadata getMetadata() const
84
std::map<std::string, std::string> propMap = initMap();
86
DOMNodeList* nodes = xmlDocument->getElementsByTagName(XStr("Properties").unicodeForm());
87
for (XMLSize_t i = 0; i < nodes->getLength(); i++) {
88
DOMNode* node = nodes->item(i);
89
if (node->getNodeType() == DOMNode::ELEMENT_NODE) {
90
auto elem = static_cast<DOMElement*>(node);
91
DOMNodeList* propList = elem->getElementsByTagName(XStr("Property").unicodeForm());
92
for (XMLSize_t j = 0; j < propList->getLength(); j++) {
93
DOMNode* propNode = propList->item(j);
94
readProperty(propNode, propMap);
100
setMetadata(propMap);
104
void readProgramVersion()
106
if (DOMNodeList* nodes = xmlDocument->getElementsByTagName(XStr("Document").unicodeForm())) {
107
for (XMLSize_t i = 0; i < nodes->getLength(); i++) {
108
DOMNode* node = nodes->item(i);
109
if (node->getNodeType() == DOMNode::ELEMENT_NODE) {
110
DOMNode* nameAttr = node->getAttributes()->getNamedItem(XStr("ProgramVersion").unicodeForm());
112
std::string value = StrX(nameAttr->getNodeValue()).c_str();
113
metadata.programVersion = value;
120
static std::map<std::string, std::string> initMap()
123
std::map<std::string, std::string> propMap = {{"Comment", ""},
126
{"CreationDate", ""},
128
{"LastModifiedBy", ""},
129
{"LastModifiedDate", ""},
137
void setMetadata(const std::map<std::string, std::string>& propMap)
139
metadata.comment = propMap.at("Comment");
140
metadata.company = propMap.at("Company");
141
metadata.createdBy = propMap.at("CreatedBy");
142
metadata.creationDate = propMap.at("CreationDate");
143
metadata.label = propMap.at("Label");
144
metadata.lastModifiedBy = propMap.at("LastModifiedBy");
145
metadata.lastModifiedDate = propMap.at("LastModifiedDate");
146
metadata.license = propMap.at("License");
147
metadata.licenseURL = propMap.at("LicenseURL");
148
metadata.uuid = propMap.at("Uid");
151
static void readProperty(DOMNode* propNode, std::map<std::string, std::string>& propMap)
153
DOMNode* nameAttr = propNode->getAttributes()->getNamedItem(XStr("name").unicodeForm());
155
std::string name = StrX(nameAttr->getNodeValue()).c_str();
156
auto it = propMap.find(name);
157
if (it != propMap.end()) {
158
it->second = readValue(propNode);
163
static std::string readValue(DOMNode* node)
165
if (node->getNodeType() == DOMNode::ELEMENT_NODE) {
166
if (DOMElement* child = static_cast<DOMElement*>(node)->getFirstElementChild()) {
167
if (DOMNode* nameAttr = child->getAttributes()->getNamedItem(XStr("value").unicodeForm())) {
168
std::string value = StrX(nameAttr->getNodeValue()).c_str();
178
XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument* xmlDocument;
179
ProjectFile::Metadata metadata;
183
ProjectFile::ProjectFile()
184
: xmlDocument(nullptr)
187
ProjectFile::ProjectFile(std::string zipArchive)
188
: stdFile(std::move(zipArchive))
189
, xmlDocument(nullptr)
192
ProjectFile::~ProjectFile()
197
void ProjectFile::setProjectFile(const std::string& zipArchive)
199
stdFile = zipArchive;
201
xmlDocument = nullptr;
204
bool ProjectFile::loadDocument()
210
zipios::ZipFile project(stdFile);
211
if (!project.isValid()) {
214
std::unique_ptr<std::istream> str(project.getInputStream("Document.xml"));
216
std::unique_ptr<XercesDOMParser> parser(new XercesDOMParser);
217
parser->setValidationScheme(XercesDOMParser::Val_Auto);
218
parser->setDoNamespaces(false);
219
parser->setDoSchema(false);
220
parser->setValidationSchemaFullChecking(false);
221
parser->setCreateEntityReferenceNodes(false);
224
Base::StdInputSource inputSource(*str, stdFile.c_str());
225
parser->parse(inputSource);
226
xmlDocument = parser->adoptDocument();
229
catch (const XMLException&) {
232
catch (const DOMException&) {
240
ProjectFile::Metadata ProjectFile::getMetadata() const
246
DocumentMetadata reader(xmlDocument);
248
return reader.getMetadata();
251
std::list<ProjectFile::Object> ProjectFile::getObjects() const
253
std::list<Object> names;
258
DOMNodeList* nodes = xmlDocument->getElementsByTagName(XStr("Objects").unicodeForm());
259
for (XMLSize_t i = 0; i < nodes->getLength(); i++) {
260
DOMNode* node = nodes->item(i);
261
if (node->getNodeType() == DOMNode::ELEMENT_NODE) {
262
DOMNodeList* objectList =
263
static_cast<DOMElement*>(node)->getElementsByTagName(XStr("Object").unicodeForm());
264
for (XMLSize_t j = 0; j < objectList->getLength(); j++) {
265
DOMNode* objectNode = objectList->item(j);
267
objectNode->getAttributes()->getNamedItem(XStr("type").unicodeForm());
269
objectNode->getAttributes()->getNamedItem(XStr("name").unicodeForm());
270
if (typeAttr && nameAttr) {
272
obj.name = StrX(nameAttr->getNodeValue()).c_str();
273
obj.type = Base::Type::fromName(StrX(typeAttr->getNodeValue()).c_str());
274
names.push_back(obj);
283
std::list<std::string> ProjectFile::getObjectsOfType(const Base::Type& typeId) const
285
std::list<std::string> names;
290
DOMNodeList* nodes = xmlDocument->getElementsByTagName(XStr("Objects").unicodeForm());
291
for (XMLSize_t i = 0; i < nodes->getLength(); i++) {
292
DOMNode* node = nodes->item(i);
293
if (node->getNodeType() == DOMNode::ELEMENT_NODE) {
294
DOMNodeList* objectList =
295
static_cast<DOMElement*>(node)->getElementsByTagName(XStr("Object").unicodeForm());
296
for (XMLSize_t j = 0; j < objectList->getLength(); j++) {
297
DOMNode* objectNode = objectList->item(j);
299
objectNode->getAttributes()->getNamedItem(XStr("type").unicodeForm());
301
objectNode->getAttributes()->getNamedItem(XStr("name").unicodeForm());
302
if (typeAttr && nameAttr) {
303
if (Base::Type::fromName(StrX(typeAttr->getNodeValue()).c_str()) == typeId) {
304
names.emplace_back(StrX(nameAttr->getNodeValue()).c_str());
314
bool ProjectFile::restoreObject(const std::string& name,
315
App::PropertyContainer* obj,
318
Base::FileInfo fi(stdFile);
319
Base::ifstream file(fi, std::ios::in | std::ios::binary);
321
zipios::ZipInputStream zipstream(file);
322
Base::XMLReader reader(stdFile.c_str(), zipstream);
323
reader.setVerbose(verbose);
325
if (!reader.isValid()) {
330
reader.readElement("Properties");
331
reader.readEndElement("Properties");
334
reader.readElement("Objects");
335
reader.readEndElement("Objects");
337
reader.readElement("ObjectData");
338
long Cnt = reader.getAttributeAsInteger("Count");
339
for (long i = 0; i < Cnt; i++) {
340
reader.readElement("Object");
341
std::string nameAttr = reader.getAttribute("name");
343
if (nameAttr == name) {
345
obj->Restore(reader);
348
reader.readEndElement("Object");
350
reader.readEndElement("ObjectData");
352
reader.readFiles(zipstream);
357
Base::Type ProjectFile::getTypeId(const std::string& name) const
363
return Base::Type::badType();
366
DOMNodeList* nodes = xmlDocument->getElementsByTagName(XStr("Objects").unicodeForm());
367
for (XMLSize_t i = 0; i < nodes->getLength(); i++) {
368
DOMNode* node = nodes->item(i);
369
if (node->getNodeType() == DOMNode::ELEMENT_NODE) {
370
DOMNodeList* objectList =
371
static_cast<DOMElement*>(node)->getElementsByTagName(XStr("Object").unicodeForm());
372
for (XMLSize_t j = 0; j < objectList->getLength(); j++) {
373
DOMNode* objectNode = objectList->item(j);
375
objectNode->getAttributes()->getNamedItem(XStr("type").unicodeForm());
377
objectNode->getAttributes()->getNamedItem(XStr("name").unicodeForm());
378
if (typeAttr && nameAttr) {
379
if (strcmp(name.c_str(), StrX(nameAttr->getNodeValue()).c_str()) == 0) {
380
std::string typeId = StrX(typeAttr->getNodeValue()).c_str();
381
return Base::Type::fromName(typeId.c_str());
388
return Base::Type::badType();
391
std::list<ProjectFile::PropertyFile>
392
ProjectFile::getPropertyFiles(const std::string& name) const
407
std::list<PropertyFile> files;
408
DOMNodeList* nodes = xmlDocument->getElementsByTagName(XStr("ObjectData").unicodeForm());
409
for (XMLSize_t i = 0; i < nodes->getLength(); i++) {
410
DOMNode* node = nodes->item(i);
411
if (node->getNodeType() == DOMNode::ELEMENT_NODE) {
412
DOMNodeList* objectList =
413
static_cast<DOMElement*>(node)->getElementsByTagName(XStr("Object").unicodeForm());
414
for (XMLSize_t j = 0; j < objectList->getLength(); j++) {
415
DOMNode* objectNode = objectList->item(j);
417
objectNode->getAttributes()->getNamedItem(XStr("name").unicodeForm());
418
if (nameAttr && strcmp(name.c_str(), StrX(nameAttr->getNodeValue()).c_str()) == 0) {
421
findFiles(objectNode, files);
430
void ProjectFile::findFiles(XERCES_CPP_NAMESPACE_QUALIFIER DOMNode* node,
431
std::list<ProjectFile::PropertyFile>& files) const
433
if (node->hasAttributes()) {
434
ProjectFile::PropertyFile prop;
435
DOMNode* fileAttr = node->getAttributes()->getNamedItem(XStr("file").unicodeForm());
437
DOMNode* parentNode = node->getParentNode();
440
parentNode->getAttributes()->getNamedItem(XStr("name").unicodeForm());
442
prop.name = StrX(nameAttr->getNodeValue()).c_str();
446
parentNode->getAttributes()->getNamedItem(XStr("type").unicodeForm());
448
prop.type = Base::Type::fromName(StrX(typeAttr->getNodeValue()).c_str());
452
prop.file = StrX(fileAttr->getNodeValue()).c_str();
453
files.push_back(prop);
457
DOMNodeList* subNodes = node->getChildNodes();
458
for (XMLSize_t i = 0; i < subNodes->getLength(); i++) {
459
DOMNode* child = subNodes->item(i);
460
findFiles(child, files);
464
std::list<std::string> ProjectFile::getInputFiles(const std::string& name) const
479
std::list<std::string> files;
480
DOMNodeList* nodes = xmlDocument->getElementsByTagName(XStr("ObjectData").unicodeForm());
481
for (XMLSize_t i = 0; i < nodes->getLength(); i++) {
482
DOMNode* node = nodes->item(i);
483
if (node->getNodeType() == DOMNode::ELEMENT_NODE) {
484
DOMNodeList* objectList =
485
static_cast<DOMElement*>(node)->getElementsByTagName(XStr("Object").unicodeForm());
486
for (XMLSize_t j = 0; j < objectList->getLength(); j++) {
487
DOMNode* objectNode = objectList->item(j);
489
objectNode->getAttributes()->getNamedItem(XStr("name").unicodeForm());
490
if (nameAttr && strcmp(name.c_str(), StrX(nameAttr->getNodeValue()).c_str()) == 0) {
493
findFiles(objectNode, files);
502
void ProjectFile::findFiles(XERCES_CPP_NAMESPACE_QUALIFIER DOMNode* node,
503
std::list<std::string>& files) const
505
if (node->hasAttributes()) {
506
DOMNode* fileAttr = node->getAttributes()->getNamedItem(XStr("file").unicodeForm());
508
files.emplace_back(StrX(fileAttr->getNodeValue()).c_str());
512
DOMNodeList* subNodes = node->getChildNodes();
513
for (XMLSize_t i = 0; i < subNodes->getLength(); i++) {
514
DOMNode* child = subNodes->item(i);
515
findFiles(child, files);
519
std::string ProjectFile::extractInputFile(const std::string& name)
521
zipios::ZipFile project(stdFile);
522
std::unique_ptr<std::istream> str(project.getInputStream(name));
526
Base::FileInfo fi(Base::FileInfo::getTempFileName());
527
Base::ofstream file(fi, std::ios::out | std::ios::binary);
528
std::streambuf* buf = file.rdbuf();
532
return fi.filePath();
538
void ProjectFile::readInputFile(const std::string& name, std::ostream& str)
540
Base::FileInfo fi(extractInputFile(name));
542
Base::ifstream file(fi, std::ios::in | std::ios::binary);
551
void ProjectFile::readInputFileDirect(const std::string& name, std::ostream& str)
553
zipios::ZipFile project(stdFile);
554
std::unique_ptr<std::istream> istr(project.getInputStream(name));
556
*istr >> str.rdbuf();
560
std::string ProjectFile::replaceInputFile(const std::string& name, std::istream& inp)
563
std::string uuid = Base::Uuid::createUuid();
564
std::string fn = stdFile;
567
Base::FileInfo tmp(fn);
568
Base::ofstream newZip(tmp, std::ios::out | std::ios::binary);
571
const int compressionLevel = 6;
572
zipios::ZipOutputStream outZip(newZip);
573
outZip.setComment("FreeCAD Document");
574
outZip.setLevel(compressionLevel);
577
zipios::ZipFile project(stdFile);
578
zipios::ConstEntries files = project.entries();
579
for (const auto& it : files) {
580
std::string file = it->getFileName();
581
outZip.putNextEntry(file);
583
inp >> outZip.rdbuf();
586
std::unique_ptr<std::istream> str(project.getInputStream(file));
588
*str >> outZip.rdbuf();
600
std::string ProjectFile::replaceInputFiles(const std::map<std::string, std::istream*>& inp)
603
std::string uuid = Base::Uuid::createUuid();
604
std::string fn = stdFile;
607
Base::FileInfo tmp(fn);
608
Base::ofstream newZip(tmp, std::ios::out | std::ios::binary);
611
const int compressionLevel = 6;
612
zipios::ZipOutputStream outZip(newZip);
613
outZip.setComment("FreeCAD Document");
614
outZip.setLevel(compressionLevel);
617
zipios::ZipFile project(stdFile);
618
zipios::ConstEntries files = project.entries();
619
for (const auto& it : files) {
620
std::string file = it->getFileName();
621
outZip.putNextEntry(file);
623
auto jt = inp.find(file);
624
if (jt != inp.end()) {
625
*jt->second >> outZip.rdbuf();
628
std::unique_ptr<std::istream> str(project.getInputStream(file));
630
*str >> outZip.rdbuf();
643
ProjectFile::replacePropertyFiles(const std::map<std::string, App::Property*>& props)
646
std::string uuid = Base::Uuid::createUuid();
647
std::string fn = stdFile;
650
Base::FileInfo tmp(fn);
651
Base::ofstream newZip(tmp, std::ios::out | std::ios::binary);
656
const int compressionLevel = 6;
657
Base::ZipWriter writer(newZip);
658
writer.setComment("FreeCAD Document");
659
writer.setLevel(compressionLevel);
662
zipios::ZipFile project(stdFile);
663
zipios::ConstEntries files = project.entries();
664
for (const auto& it : files) {
665
std::string file = it->getFileName();
666
writer.putNextEntry(file.c_str());
668
auto jt = props.find(file);
669
if (jt != props.end()) {
670
jt->second->SaveDocFile(writer);
673
std::unique_ptr<std::istream> str(project.getInputStream(file));
675
*str >> writer.Stream().rdbuf();
685
bool ProjectFile::replaceProjectFile(const std::string& name, bool keepfile)
687
std::string uuid = Base::Uuid::createUuid();
688
std::string fn = stdFile;
693
Base::FileInfo orig(stdFile);
694
if (!orig.renameFile(fn.c_str())) {
700
Base::FileInfo other(name);
701
if (!other.renameFile(stdFile.c_str())) {
707
if (!orig.deleteFile()) {