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
XERCES_CPP_NAMESPACE_USE
66
explicit DocumentMetadata(XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument* xmlDocument)
67
: xmlDocument{xmlDocument}
70
ProjectFile::Metadata getMetadata() const
79
std::map<std::string, std::string> propMap = initMap();
81
DOMNodeList* nodes = xmlDocument->getElementsByTagName(XStr("Properties").unicodeForm());
82
for (XMLSize_t i = 0; i < nodes->getLength(); i++) {
83
DOMNode* node = nodes->item(i);
84
if (node->getNodeType() == DOMNode::ELEMENT_NODE) {
85
auto elem = static_cast<DOMElement*>(node);
86
DOMNodeList* propList = elem->getElementsByTagName(XStr("Property").unicodeForm());
87
for (XMLSize_t j = 0; j < propList->getLength(); j++) {
88
DOMNode* propNode = propList->item(j);
89
readProperty(propNode, propMap);
99
void readProgramVersion()
101
if (DOMNodeList* nodes = xmlDocument->getElementsByTagName(XStr("Document").unicodeForm())) {
102
for (XMLSize_t i = 0; i < nodes->getLength(); i++) {
103
DOMNode* node = nodes->item(i);
104
if (node->getNodeType() == DOMNode::ELEMENT_NODE) {
105
DOMNode* nameAttr = node->getAttributes()->getNamedItem(XStr("ProgramVersion").unicodeForm());
107
std::string value = StrX(nameAttr->getNodeValue()).c_str();
108
metadata.programVersion = value;
115
static std::map<std::string, std::string> initMap()
118
std::map<std::string, std::string> propMap = {{"Comment", ""},
121
{"CreationDate", ""},
123
{"LastModifiedBy", ""},
124
{"LastModifiedDate", ""},
132
void setMetadata(const std::map<std::string, std::string>& propMap)
134
metadata.comment = propMap.at("Comment");
135
metadata.company = propMap.at("Company");
136
metadata.createdBy = propMap.at("CreatedBy");
137
metadata.creationDate = propMap.at("CreationDate");
138
metadata.label = propMap.at("Label");
139
metadata.lastModifiedBy = propMap.at("LastModifiedBy");
140
metadata.lastModifiedDate = propMap.at("LastModifiedDate");
141
metadata.license = propMap.at("License");
142
metadata.licenseURL = propMap.at("LicenseURL");
143
metadata.uuid = propMap.at("Uid");
146
static void readProperty(DOMNode* propNode, std::map<std::string, std::string>& propMap)
148
DOMNode* nameAttr = propNode->getAttributes()->getNamedItem(XStr("name").unicodeForm());
150
std::string name = StrX(nameAttr->getNodeValue()).c_str();
151
auto it = propMap.find(name);
152
if (it != propMap.end()) {
153
it->second = readValue(propNode);
158
static std::string readValue(DOMNode* node)
160
if (node->getNodeType() == DOMNode::ELEMENT_NODE) {
161
if (DOMElement* child = static_cast<DOMElement*>(node)->getFirstElementChild()) {
162
if (DOMNode* nameAttr = child->getAttributes()->getNamedItem(XStr("value").unicodeForm())) {
163
std::string value = StrX(nameAttr->getNodeValue()).c_str();
173
XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument* xmlDocument;
174
ProjectFile::Metadata metadata;
178
ProjectFile::ProjectFile()
179
: xmlDocument(nullptr)
182
ProjectFile::ProjectFile(std::string zipArchive)
183
: stdFile(std::move(zipArchive))
184
, xmlDocument(nullptr)
187
ProjectFile::~ProjectFile()
192
void ProjectFile::setProjectFile(const std::string& zipArchive)
194
stdFile = zipArchive;
196
xmlDocument = nullptr;
199
bool ProjectFile::loadDocument()
205
zipios::ZipFile project(stdFile);
206
if (!project.isValid()) {
209
std::unique_ptr<std::istream> str(project.getInputStream("Document.xml"));
211
std::unique_ptr<XercesDOMParser> parser(new XercesDOMParser);
212
parser->setValidationScheme(XercesDOMParser::Val_Auto);
213
parser->setDoNamespaces(false);
214
parser->setDoSchema(false);
215
parser->setValidationSchemaFullChecking(false);
216
parser->setCreateEntityReferenceNodes(false);
219
Base::StdInputSource inputSource(*str, stdFile.c_str());
220
parser->parse(inputSource);
221
xmlDocument = parser->adoptDocument();
224
catch (const XMLException&) {
227
catch (const DOMException&) {
235
ProjectFile::Metadata ProjectFile::getMetadata() const
241
DocumentMetadata reader(xmlDocument);
243
return reader.getMetadata();
246
std::list<ProjectFile::Object> ProjectFile::getObjects() const
248
std::list<Object> names;
253
DOMNodeList* nodes = xmlDocument->getElementsByTagName(XStr("Objects").unicodeForm());
254
for (XMLSize_t i = 0; i < nodes->getLength(); i++) {
255
DOMNode* node = nodes->item(i);
256
if (node->getNodeType() == DOMNode::ELEMENT_NODE) {
257
DOMNodeList* objectList =
258
static_cast<DOMElement*>(node)->getElementsByTagName(XStr("Object").unicodeForm());
259
for (XMLSize_t j = 0; j < objectList->getLength(); j++) {
260
DOMNode* objectNode = objectList->item(j);
262
objectNode->getAttributes()->getNamedItem(XStr("type").unicodeForm());
264
objectNode->getAttributes()->getNamedItem(XStr("name").unicodeForm());
265
if (typeAttr && nameAttr) {
267
obj.name = StrX(nameAttr->getNodeValue()).c_str();
268
obj.type = Base::Type::fromName(StrX(typeAttr->getNodeValue()).c_str());
269
names.push_back(obj);
278
std::list<std::string> ProjectFile::getObjectsOfType(const Base::Type& typeId) const
280
std::list<std::string> names;
285
DOMNodeList* nodes = xmlDocument->getElementsByTagName(XStr("Objects").unicodeForm());
286
for (XMLSize_t i = 0; i < nodes->getLength(); i++) {
287
DOMNode* node = nodes->item(i);
288
if (node->getNodeType() == DOMNode::ELEMENT_NODE) {
289
DOMNodeList* objectList =
290
static_cast<DOMElement*>(node)->getElementsByTagName(XStr("Object").unicodeForm());
291
for (XMLSize_t j = 0; j < objectList->getLength(); j++) {
292
DOMNode* objectNode = objectList->item(j);
294
objectNode->getAttributes()->getNamedItem(XStr("type").unicodeForm());
296
objectNode->getAttributes()->getNamedItem(XStr("name").unicodeForm());
297
if (typeAttr && nameAttr) {
298
if (Base::Type::fromName(StrX(typeAttr->getNodeValue()).c_str()) == typeId) {
299
names.emplace_back(StrX(nameAttr->getNodeValue()).c_str());
309
bool ProjectFile::restoreObject(const std::string& name,
310
App::PropertyContainer* obj,
313
Base::FileInfo fi(stdFile);
314
Base::ifstream file(fi, std::ios::in | std::ios::binary);
316
zipios::ZipInputStream zipstream(file);
317
Base::XMLReader reader(stdFile.c_str(), zipstream);
318
reader.setVerbose(verbose);
320
if (!reader.isValid()) {
325
reader.readElement("Properties");
326
reader.readEndElement("Properties");
329
reader.readElement("Objects");
330
reader.readEndElement("Objects");
332
reader.readElement("ObjectData");
333
long Cnt = reader.getAttributeAsInteger("Count");
334
for (long i = 0; i < Cnt; i++) {
335
reader.readElement("Object");
336
std::string nameAttr = reader.getAttribute("name");
338
if (nameAttr == name) {
340
obj->Restore(reader);
343
reader.readEndElement("Object");
345
reader.readEndElement("ObjectData");
347
reader.readFiles(zipstream);
352
Base::Type ProjectFile::getTypeId(const std::string& name) const
358
return Base::Type::badType();
361
DOMNodeList* nodes = xmlDocument->getElementsByTagName(XStr("Objects").unicodeForm());
362
for (XMLSize_t i = 0; i < nodes->getLength(); i++) {
363
DOMNode* node = nodes->item(i);
364
if (node->getNodeType() == DOMNode::ELEMENT_NODE) {
365
DOMNodeList* objectList =
366
static_cast<DOMElement*>(node)->getElementsByTagName(XStr("Object").unicodeForm());
367
for (XMLSize_t j = 0; j < objectList->getLength(); j++) {
368
DOMNode* objectNode = objectList->item(j);
370
objectNode->getAttributes()->getNamedItem(XStr("type").unicodeForm());
372
objectNode->getAttributes()->getNamedItem(XStr("name").unicodeForm());
373
if (typeAttr && nameAttr) {
374
if (strcmp(name.c_str(), StrX(nameAttr->getNodeValue()).c_str()) == 0) {
375
std::string typeId = StrX(typeAttr->getNodeValue()).c_str();
376
return Base::Type::fromName(typeId.c_str());
383
return Base::Type::badType();
386
std::list<ProjectFile::PropertyFile>
387
ProjectFile::getPropertyFiles(const std::string& name) const
402
std::list<PropertyFile> files;
403
DOMNodeList* nodes = xmlDocument->getElementsByTagName(XStr("ObjectData").unicodeForm());
404
for (XMLSize_t i = 0; i < nodes->getLength(); i++) {
405
DOMNode* node = nodes->item(i);
406
if (node->getNodeType() == DOMNode::ELEMENT_NODE) {
407
DOMNodeList* objectList =
408
static_cast<DOMElement*>(node)->getElementsByTagName(XStr("Object").unicodeForm());
409
for (XMLSize_t j = 0; j < objectList->getLength(); j++) {
410
DOMNode* objectNode = objectList->item(j);
412
objectNode->getAttributes()->getNamedItem(XStr("name").unicodeForm());
413
if (nameAttr && strcmp(name.c_str(), StrX(nameAttr->getNodeValue()).c_str()) == 0) {
416
findFiles(objectNode, files);
425
void ProjectFile::findFiles(XERCES_CPP_NAMESPACE_QUALIFIER DOMNode* node,
426
std::list<ProjectFile::PropertyFile>& files) const
428
if (node->hasAttributes()) {
429
ProjectFile::PropertyFile prop;
430
DOMNode* fileAttr = node->getAttributes()->getNamedItem(XStr("file").unicodeForm());
432
DOMNode* parentNode = node->getParentNode();
435
parentNode->getAttributes()->getNamedItem(XStr("name").unicodeForm());
437
prop.name = StrX(nameAttr->getNodeValue()).c_str();
441
parentNode->getAttributes()->getNamedItem(XStr("type").unicodeForm());
443
prop.type = Base::Type::fromName(StrX(typeAttr->getNodeValue()).c_str());
447
prop.file = StrX(fileAttr->getNodeValue()).c_str();
448
files.push_back(prop);
452
DOMNodeList* subNodes = node->getChildNodes();
453
for (XMLSize_t i = 0; i < subNodes->getLength(); i++) {
454
DOMNode* child = subNodes->item(i);
455
findFiles(child, files);
459
std::list<std::string> ProjectFile::getInputFiles(const std::string& name) const
474
std::list<std::string> files;
475
DOMNodeList* nodes = xmlDocument->getElementsByTagName(XStr("ObjectData").unicodeForm());
476
for (XMLSize_t i = 0; i < nodes->getLength(); i++) {
477
DOMNode* node = nodes->item(i);
478
if (node->getNodeType() == DOMNode::ELEMENT_NODE) {
479
DOMNodeList* objectList =
480
static_cast<DOMElement*>(node)->getElementsByTagName(XStr("Object").unicodeForm());
481
for (XMLSize_t j = 0; j < objectList->getLength(); j++) {
482
DOMNode* objectNode = objectList->item(j);
484
objectNode->getAttributes()->getNamedItem(XStr("name").unicodeForm());
485
if (nameAttr && strcmp(name.c_str(), StrX(nameAttr->getNodeValue()).c_str()) == 0) {
488
findFiles(objectNode, files);
497
void ProjectFile::findFiles(XERCES_CPP_NAMESPACE_QUALIFIER DOMNode* node,
498
std::list<std::string>& files) const
500
if (node->hasAttributes()) {
501
DOMNode* fileAttr = node->getAttributes()->getNamedItem(XStr("file").unicodeForm());
503
files.emplace_back(StrX(fileAttr->getNodeValue()).c_str());
507
DOMNodeList* subNodes = node->getChildNodes();
508
for (XMLSize_t i = 0; i < subNodes->getLength(); i++) {
509
DOMNode* child = subNodes->item(i);
510
findFiles(child, files);
514
std::string ProjectFile::extractInputFile(const std::string& name)
516
zipios::ZipFile project(stdFile);
517
std::unique_ptr<std::istream> str(project.getInputStream(name));
521
Base::FileInfo fi(Base::FileInfo::getTempFileName());
522
Base::ofstream file(fi, std::ios::out | std::ios::binary);
523
std::streambuf* buf = file.rdbuf();
527
return fi.filePath();
533
void ProjectFile::readInputFile(const std::string& name, std::ostream& str)
535
Base::FileInfo fi(extractInputFile(name));
537
Base::ifstream file(fi, std::ios::in | std::ios::binary);
546
void ProjectFile::readInputFileDirect(const std::string& name, std::ostream& str)
548
zipios::ZipFile project(stdFile);
549
std::unique_ptr<std::istream> istr(project.getInputStream(name));
551
*istr >> str.rdbuf();
555
std::string ProjectFile::replaceInputFile(const std::string& name, std::istream& inp)
558
std::string uuid = Base::Uuid::createUuid();
559
std::string fn = stdFile;
562
Base::FileInfo tmp(fn);
563
Base::ofstream newZip(tmp, std::ios::out | std::ios::binary);
566
const int compressionLevel = 6;
567
zipios::ZipOutputStream outZip(newZip);
568
outZip.setComment("FreeCAD Document");
569
outZip.setLevel(compressionLevel);
572
zipios::ZipFile project(stdFile);
573
zipios::ConstEntries files = project.entries();
574
for (const auto& it : files) {
575
std::string file = it->getFileName();
576
outZip.putNextEntry(file);
578
inp >> outZip.rdbuf();
581
std::unique_ptr<std::istream> str(project.getInputStream(file));
583
*str >> outZip.rdbuf();
595
std::string ProjectFile::replaceInputFiles(const std::map<std::string, std::istream*>& inp)
598
std::string uuid = Base::Uuid::createUuid();
599
std::string fn = stdFile;
602
Base::FileInfo tmp(fn);
603
Base::ofstream newZip(tmp, std::ios::out | std::ios::binary);
606
const int compressionLevel = 6;
607
zipios::ZipOutputStream outZip(newZip);
608
outZip.setComment("FreeCAD Document");
609
outZip.setLevel(compressionLevel);
612
zipios::ZipFile project(stdFile);
613
zipios::ConstEntries files = project.entries();
614
for (const auto& it : files) {
615
std::string file = it->getFileName();
616
outZip.putNextEntry(file);
618
auto jt = inp.find(file);
619
if (jt != inp.end()) {
620
*jt->second >> outZip.rdbuf();
623
std::unique_ptr<std::istream> str(project.getInputStream(file));
625
*str >> outZip.rdbuf();
638
ProjectFile::replacePropertyFiles(const std::map<std::string, App::Property*>& props)
641
std::string uuid = Base::Uuid::createUuid();
642
std::string fn = stdFile;
645
Base::FileInfo tmp(fn);
646
Base::ofstream newZip(tmp, std::ios::out | std::ios::binary);
651
const int compressionLevel = 6;
652
Base::ZipWriter writer(newZip);
653
writer.setComment("FreeCAD Document");
654
writer.setLevel(compressionLevel);
657
zipios::ZipFile project(stdFile);
658
zipios::ConstEntries files = project.entries();
659
for (const auto& it : files) {
660
std::string file = it->getFileName();
661
writer.putNextEntry(file.c_str());
663
auto jt = props.find(file);
664
if (jt != props.end()) {
665
jt->second->SaveDocFile(writer);
668
std::unique_ptr<std::istream> str(project.getInputStream(file));
670
*str >> writer.Stream().rdbuf();
680
bool ProjectFile::replaceProjectFile(const std::string& name, bool keepfile)
682
std::string uuid = Base::Uuid::createUuid();
683
std::string fn = stdFile;
688
Base::FileInfo orig(stdFile);
689
if (!orig.renameFile(fn.c_str())) {
695
Base::FileInfo other(name);
696
if (!other.renameFile(stdFile.c_str())) {
702
if (!orig.deleteFile()) {