From 0d8561664420400c27b91fa2bbae31e51dad76ec Mon Sep 17 00:00:00 2001 From: wmayer Date: Fri, 8 Mar 2024 15:13:15 +0100 Subject: [PATCH] App: Add class ProjectFile to access metadata and data files of a project --- src/App/CMakeLists.txt | 2 + src/App/ProjectFile.cpp | 708 ++++++++++++++++++++++++++++++++++++++++ src/App/ProjectFile.h | 204 ++++++++++++ 3 files changed, 914 insertions(+) create mode 100644 src/App/ProjectFile.cpp create mode 100644 src/App/ProjectFile.h diff --git a/src/App/CMakeLists.txt b/src/App/CMakeLists.txt index 100addc08b..c809a0aeec 100644 --- a/src/App/CMakeLists.txt +++ b/src/App/CMakeLists.txt @@ -165,6 +165,7 @@ SET(Document_CPP_SRCS InventorObject.cpp MeasureDistance.cpp Placement.cpp + ProjectFile.cpp OriginFeature.cpp Range.cpp Transactions.cpp @@ -211,6 +212,7 @@ SET(Document_HPP_SRCS InventorObject.h MeasureDistance.h Placement.h + ProjectFile.h OriginFeature.h Range.h Transactions.h diff --git a/src/App/ProjectFile.cpp b/src/App/ProjectFile.cpp new file mode 100644 index 0000000000..440dbd494a --- /dev/null +++ b/src/App/ProjectFile.cpp @@ -0,0 +1,708 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later + +/*************************************************************************** + * Copyright (c) 2024 Werner Mayer * + * * + * This file is part of FreeCAD. * + * * + * FreeCAD is free software: you can redistribute it and/or modify it * + * under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 2.1 of the * + * License, or (at your option) any later version. * + * * + * FreeCAD is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with FreeCAD. If not, see * + * . * + * * + **************************************************************************/ + + +#include "PreCompiled.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "ProjectFile.h" +#include "DocumentObject.h" +#include +#include +#include +#include +#include +#include + +XERCES_CPP_NAMESPACE_USE +using namespace App; + +namespace { +class DocumentMetadata +{ +public: + explicit DocumentMetadata(XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument* xmlDocument) + : xmlDocument{xmlDocument} + {} + + ProjectFile::Metadata getMetadata() const + { + return metadata; + } + + void readXML() + { + readProgramVersion(); + + std::map propMap = initMap(); + + DOMNodeList* nodes = xmlDocument->getElementsByTagName(XStr("Properties").unicodeForm()); + for (XMLSize_t i = 0; i < nodes->getLength(); i++) { + DOMNode* node = nodes->item(i); + if (node->getNodeType() == DOMNode::ELEMENT_NODE) { + auto elem = static_cast(node); + DOMNodeList* propList = elem->getElementsByTagName(XStr("Property").unicodeForm()); + for (XMLSize_t j = 0; j < propList->getLength(); j++) { + DOMNode* propNode = propList->item(j); + readProperty(propNode, propMap); + } + } + break; + } + + setMetadata(propMap); + } + +private: + void readProgramVersion() + { + if (DOMNodeList* nodes = xmlDocument->getElementsByTagName(XStr("Document").unicodeForm())) { + for (XMLSize_t i = 0; i < nodes->getLength(); i++) { + DOMNode* node = nodes->item(i); + if (node->getNodeType() == DOMNode::ELEMENT_NODE) { + DOMNode* nameAttr = node->getAttributes()->getNamedItem(XStr("ProgramVersion").unicodeForm()); + if (nameAttr) { + std::string value = StrX(nameAttr->getNodeValue()).c_str(); + metadata.programVersion = value; + break; + } + } + } + } + } + std::map initMap() + { + // clang-format off + std::map propMap = {{"Comment", ""}, + {"Company", ""}, + {"CreatedBy", ""}, + {"CreationDate", ""}, + {"Label", ""}, + {"LastModifiedBy", ""}, + {"LastModifiedDate", ""}, + {"License", ""}, + {"LicenseURL", ""}, + {"Uid", ""}}; + return propMap; + // clang-format on + } + + void setMetadata(const std::map& propMap) + { + metadata.comment = propMap.at("Comment"); + metadata.company = propMap.at("Company"); + metadata.createdBy = propMap.at("CreatedBy"); + metadata.creationDate = propMap.at("CreationDate"); + metadata.label = propMap.at("Label"); + metadata.lastModifiedBy = propMap.at("LastModifiedBy"); + metadata.lastModifiedDate = propMap.at("LastModifiedDate"); + metadata.license = propMap.at("License"); + metadata.licenseURL = propMap.at("LicenseURL"); + metadata.uuid = propMap.at("Uid"); + } + + void readProperty(DOMNode* propNode, std::map& propMap) + { + DOMNode* nameAttr = propNode->getAttributes()->getNamedItem(XStr("name").unicodeForm()); + if (nameAttr) { + std::string name = StrX(nameAttr->getNodeValue()).c_str(); + auto it = propMap.find(name); + if (it != propMap.end()) { + it->second = readValue(propNode); + } + } + } + + std::string readValue(DOMNode* node) + { + if (node->getNodeType() == DOMNode::ELEMENT_NODE) { + if (DOMElement* child = static_cast(node)->getFirstElementChild()) { + if (DOMNode* nameAttr = child->getAttributes()->getNamedItem(XStr("value").unicodeForm())) { + std::string value = StrX(nameAttr->getNodeValue()).c_str(); + return value; + } + } + } + + return {}; + } + +private: + XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument* xmlDocument; + ProjectFile::Metadata metadata; +}; +} + +ProjectFile::ProjectFile() + : xmlDocument(nullptr) +{} + +ProjectFile::ProjectFile(std::string zipArchive) + : stdFile(std::move(zipArchive)) + , xmlDocument(nullptr) +{} + +ProjectFile::~ProjectFile() +{ + delete xmlDocument; +} + +void ProjectFile::setProjectFile(const std::string& zipArchive) +{ + stdFile = zipArchive; + delete xmlDocument; + xmlDocument = nullptr; +} + +bool ProjectFile::loadDocument() +{ + if (xmlDocument) { + return true; // already loaded + } + + zipios::ZipFile project(stdFile); + if (!project.isValid()) { + return false; + } + std::unique_ptr str(project.getInputStream("Document.xml")); + if (str) { + std::unique_ptr parser(new XercesDOMParser); + parser->setValidationScheme(XercesDOMParser::Val_Auto); + parser->setDoNamespaces(false); + parser->setDoSchema(false); + parser->setValidationSchemaFullChecking(false); + parser->setCreateEntityReferenceNodes(false); + + try { + Base::StdInputSource inputSource(*str, stdFile.c_str()); + parser->parse(inputSource); + xmlDocument = parser->adoptDocument(); + return true; + } + catch (const XMLException&) { + return false; + } + catch (const DOMException&) { + return false; + } + } + + return false; +} + +ProjectFile::Metadata ProjectFile::getMetadata() const +{ + if (!xmlDocument) { + return {}; + } + + DocumentMetadata reader(xmlDocument); + reader.readXML(); + return reader.getMetadata(); +} + +std::list ProjectFile::getObjects() const +{ + std::list names; + if (!xmlDocument) { + return names; + } + + DOMNodeList* nodes = xmlDocument->getElementsByTagName(XStr("Objects").unicodeForm()); + for (XMLSize_t i = 0; i < nodes->getLength(); i++) { + DOMNode* node = nodes->item(i); + if (node->getNodeType() == DOMNode::ELEMENT_NODE) { + DOMNodeList* objectList = + static_cast(node)->getElementsByTagName(XStr("Object").unicodeForm()); + for (XMLSize_t j = 0; j < objectList->getLength(); j++) { + DOMNode* objectNode = objectList->item(j); + DOMNode* typeAttr = + objectNode->getAttributes()->getNamedItem(XStr("type").unicodeForm()); + DOMNode* nameAttr = + objectNode->getAttributes()->getNamedItem(XStr("name").unicodeForm()); + if (typeAttr && nameAttr) { + Object obj; + obj.name = StrX(nameAttr->getNodeValue()).c_str(); + obj.type = Base::Type::fromName(StrX(typeAttr->getNodeValue()).c_str()); + names.push_back(obj); + } + } + } + } + + return names; +} + +std::list ProjectFile::getObjectsOfType(const Base::Type& typeId) const +{ + std::list names; + if (!xmlDocument) { + return names; + } + + DOMNodeList* nodes = xmlDocument->getElementsByTagName(XStr("Objects").unicodeForm()); + for (XMLSize_t i = 0; i < nodes->getLength(); i++) { + DOMNode* node = nodes->item(i); + if (node->getNodeType() == DOMNode::ELEMENT_NODE) { + DOMNodeList* objectList = + static_cast(node)->getElementsByTagName(XStr("Object").unicodeForm()); + for (XMLSize_t j = 0; j < objectList->getLength(); j++) { + DOMNode* objectNode = objectList->item(j); + DOMNode* typeAttr = + objectNode->getAttributes()->getNamedItem(XStr("type").unicodeForm()); + DOMNode* nameAttr = + objectNode->getAttributes()->getNamedItem(XStr("name").unicodeForm()); + if (typeAttr && nameAttr) { + if (Base::Type::fromName(StrX(typeAttr->getNodeValue()).c_str()) == typeId) { + names.emplace_back(StrX(nameAttr->getNodeValue()).c_str()); + } + } + } + } + } + + return names; +} + +bool ProjectFile::restoreObject(const std::string& name, + App::PropertyContainer* obj, + bool verbose) +{ + Base::FileInfo fi(stdFile); + Base::ifstream file(fi, std::ios::in | std::ios::binary); + + zipios::ZipInputStream zipstream(file); + Base::XMLReader reader(stdFile.c_str(), zipstream); + reader.setVerbose(verbose); + + if (!reader.isValid()) { + return false; + } + + // skip document properties + reader.readElement("Properties"); + reader.readEndElement("Properties"); + + // skip objects + reader.readElement("Objects"); + reader.readEndElement("Objects"); + + reader.readElement("ObjectData"); + long Cnt = reader.getAttributeAsInteger("Count"); + for (long i = 0; i < Cnt; i++) { + reader.readElement("Object"); + std::string nameAttr = reader.getAttribute("name"); + + if (nameAttr == name) { + // obj->StatusBits.set(4); + obj->Restore(reader); + // obj->StatusBits.reset(4); + } + reader.readEndElement("Object"); + } + reader.readEndElement("ObjectData"); + + reader.readFiles(zipstream); + + return true; +} + +Base::Type ProjectFile::getTypeId(const std::string& name) const +{ + // + // + // + if (!xmlDocument) { + return Base::Type::badType(); + } + + DOMNodeList* nodes = xmlDocument->getElementsByTagName(XStr("Objects").unicodeForm()); + for (XMLSize_t i = 0; i < nodes->getLength(); i++) { + DOMNode* node = nodes->item(i); + if (node->getNodeType() == DOMNode::ELEMENT_NODE) { + DOMNodeList* objectList = + static_cast(node)->getElementsByTagName(XStr("Object").unicodeForm()); + for (XMLSize_t j = 0; j < objectList->getLength(); j++) { + DOMNode* objectNode = objectList->item(j); + DOMNode* typeAttr = + objectNode->getAttributes()->getNamedItem(XStr("type").unicodeForm()); + DOMNode* nameAttr = + objectNode->getAttributes()->getNamedItem(XStr("name").unicodeForm()); + if (typeAttr && nameAttr) { + if (strcmp(name.c_str(), StrX(nameAttr->getNodeValue()).c_str()) == 0) { + std::string typeId = StrX(typeAttr->getNodeValue()).c_str(); + return Base::Type::fromName(typeId.c_str()); + } + } + } + } + } + + return Base::Type::badType(); +} + +std::list +ProjectFile::getPropertyFiles(const std::string& name) const +{ + // + // + // + // + // + // + // + // + // + if (!xmlDocument) { + return {}; + } + + std::list files; + DOMNodeList* nodes = xmlDocument->getElementsByTagName(XStr("ObjectData").unicodeForm()); + for (XMLSize_t i = 0; i < nodes->getLength(); i++) { + DOMNode* node = nodes->item(i); + if (node->getNodeType() == DOMNode::ELEMENT_NODE) { + DOMNodeList* objectList = + static_cast(node)->getElementsByTagName(XStr("Object").unicodeForm()); + for (XMLSize_t j = 0; j < objectList->getLength(); j++) { + DOMNode* objectNode = objectList->item(j); + DOMNode* nameAttr = + objectNode->getAttributes()->getNamedItem(XStr("name").unicodeForm()); + if (nameAttr && strcmp(name.c_str(), StrX(nameAttr->getNodeValue()).c_str()) == 0) { + // now go recursively through the sub-tree (i.e. the properties) and collect + // every file attribute + findFiles(objectNode, files); + break; + } + } + } + } + return files; +} + +void ProjectFile::findFiles(XERCES_CPP_NAMESPACE_QUALIFIER DOMNode* node, + std::list& files) const +{ + if (node->hasAttributes()) { + ProjectFile::PropertyFile prop; + DOMNode* fileAttr = node->getAttributes()->getNamedItem(XStr("file").unicodeForm()); + if (fileAttr) { + DOMNode* parentNode = node->getParentNode(); + if (parentNode) { + DOMNode* nameAttr = + parentNode->getAttributes()->getNamedItem(XStr("name").unicodeForm()); + if (nameAttr) { + prop.name = StrX(nameAttr->getNodeValue()).c_str(); + } + + DOMNode* typeAttr = + parentNode->getAttributes()->getNamedItem(XStr("type").unicodeForm()); + if (typeAttr) { + prop.type = Base::Type::fromName(StrX(typeAttr->getNodeValue()).c_str()); + } + } + + prop.file = StrX(fileAttr->getNodeValue()).c_str(); + files.push_back(prop); + } + } + + DOMNodeList* subNodes = node->getChildNodes(); + for (XMLSize_t i = 0; i < subNodes->getLength(); i++) { + DOMNode* child = subNodes->item(i); + findFiles(child, files); + } +} + +std::list ProjectFile::getInputFiles(const std::string& name) const +{ + // + // + // + // + // + // + // + // + // + if (!xmlDocument) { + return {}; + } + + std::list files; + DOMNodeList* nodes = xmlDocument->getElementsByTagName(XStr("ObjectData").unicodeForm()); + for (XMLSize_t i = 0; i < nodes->getLength(); i++) { + DOMNode* node = nodes->item(i); + if (node->getNodeType() == DOMNode::ELEMENT_NODE) { + DOMNodeList* objectList = + static_cast(node)->getElementsByTagName(XStr("Object").unicodeForm()); + for (XMLSize_t j = 0; j < objectList->getLength(); j++) { + DOMNode* objectNode = objectList->item(j); + DOMNode* nameAttr = + objectNode->getAttributes()->getNamedItem(XStr("name").unicodeForm()); + if (nameAttr && strcmp(name.c_str(), StrX(nameAttr->getNodeValue()).c_str()) == 0) { + // now go recursively through the sub-tree (i.e. the properties) and collect + // every file attribute + findFiles(objectNode, files); + break; + } + } + } + } + return files; +} + +void ProjectFile::findFiles(XERCES_CPP_NAMESPACE_QUALIFIER DOMNode* node, + std::list& files) const +{ + if (node->hasAttributes()) { + DOMNode* fileAttr = node->getAttributes()->getNamedItem(XStr("file").unicodeForm()); + if (fileAttr) { + files.emplace_back(StrX(fileAttr->getNodeValue()).c_str()); + } + } + + DOMNodeList* subNodes = node->getChildNodes(); + for (XMLSize_t i = 0; i < subNodes->getLength(); i++) { + DOMNode* child = subNodes->item(i); + findFiles(child, files); + } +} + +std::string ProjectFile::extractInputFile(const std::string& name) +{ + zipios::ZipFile project(stdFile); + std::unique_ptr str(project.getInputStream(name)); + if (str) { + // write it to a tmp. file as writing to the string stream + // might take too long + Base::FileInfo fi(Base::FileInfo::getTempFileName()); + Base::ofstream file(fi, std::ios::out | std::ios::binary); + std::streambuf* buf = file.rdbuf(); + (*str) >> buf; + file.flush(); + file.close(); + return fi.filePath(); + } + + return {}; +} + +void ProjectFile::readInputFile(const std::string& name, std::stringstream& str) +{ + Base::FileInfo fi(extractInputFile(name)); + if (fi.exists()) { + Base::ifstream file(fi, std::ios::in | std::ios::binary); + file >> str.rdbuf(); + file.close(); + fi.deleteFile(); + } +} + +// Read the given input file from the zip directly into the given stream (not using a temporary +// file) +void ProjectFile::readInputFileDirect(const std::string& name, std::stringstream& str) +{ + zipios::ZipFile project(stdFile); + std::unique_ptr istr(project.getInputStream(name)); + if (istr) { + *istr >> str.rdbuf(); + } +} + +std::string ProjectFile::replaceInputFile(const std::string& name, std::istream& inp) +{ + // create a new zip file with the name '.' + std::string uuid = Base::Uuid::createUuid(); + std::string fn = stdFile; + fn += "."; + fn += uuid; + Base::FileInfo tmp(fn); + Base::ofstream newZip(tmp, std::ios::out | std::ios::binary); + + // standard compression + const int compressionLevel = 6; + zipios::ZipOutputStream outZip(newZip); + outZip.setComment("FreeCAD Document"); + outZip.setLevel(compressionLevel); + + // open the original zip file + zipios::ZipFile project(stdFile); + zipios::ConstEntries files = project.entries(); + for (const auto& it : files) { + std::string file = it->getFileName(); + outZip.putNextEntry(file); + if (file == name) { + inp >> outZip.rdbuf(); + } + else { + std::unique_ptr str(project.getInputStream(file)); + if (str) { + *str >> outZip.rdbuf(); + } + } + } + + project.close(); + outZip.close(); + newZip.close(); + + return fn; +} + +std::string ProjectFile::replaceInputFiles(const std::map& inp) +{ + // create a new zip file with the name '.' + std::string uuid = Base::Uuid::createUuid(); + std::string fn = stdFile; + fn += "."; + fn += uuid; + Base::FileInfo tmp(fn); + Base::ofstream newZip(tmp, std::ios::out | std::ios::binary); + + // standard compression + const int compressionLevel = 6; + zipios::ZipOutputStream outZip(newZip); + outZip.setComment("FreeCAD Document"); + outZip.setLevel(compressionLevel); + + // open the original zip file + zipios::ZipFile project(stdFile); + zipios::ConstEntries files = project.entries(); + for (const auto& it : files) { + std::string file = it->getFileName(); + outZip.putNextEntry(file); + + auto jt = inp.find(file); + if (jt != inp.end()) { + *jt->second >> outZip.rdbuf(); + } + else { + std::unique_ptr str(project.getInputStream(file)); + if (str) { + *str >> outZip.rdbuf(); + } + } + } + + project.close(); + outZip.close(); + newZip.close(); + + return fn; +} + +std::string +ProjectFile::replacePropertyFiles(const std::map& props) +{ + // create a new zip file with the name '.' + std::string uuid = Base::Uuid::createUuid(); + std::string fn = stdFile; + fn += "."; + fn += uuid; + Base::FileInfo tmp(fn); + Base::ofstream newZip(tmp, std::ios::out | std::ios::binary); + + // open extra scope + { + // standard compression + const int compressionLevel = 6; + Base::ZipWriter writer(newZip); + writer.setComment("FreeCAD Document"); + writer.setLevel(compressionLevel); + + // open the original zip file + zipios::ZipFile project(stdFile); + zipios::ConstEntries files = project.entries(); + for (const auto& it : files) { + std::string file = it->getFileName(); + writer.putNextEntry(file.c_str()); + + auto jt = props.find(file); + if (jt != props.end()) { + jt->second->SaveDocFile(writer); + } + else { + std::unique_ptr str(project.getInputStream(file)); + if (str) { + *str >> writer.Stream().rdbuf(); + } + } + } + project.close(); + } + + return fn; +} + +bool ProjectFile::replaceProjectFile(const std::string& name, bool keepfile) +{ + std::string uuid = Base::Uuid::createUuid(); + std::string fn = stdFile; + fn += "."; + fn += uuid; + + // Now rename the orginal file to something unique + Base::FileInfo orig(stdFile); + if (!orig.renameFile(fn.c_str())) { + return false; + } + orig.setFile(fn); + + // rename the tmp.file to the original file name + Base::FileInfo other(name); + if (!other.renameFile(stdFile.c_str())) { + return false; + } + + // and delete the renamed original file. + if (!keepfile) { + if (!orig.deleteFile()) { + return false; + } + } + + return true; +} diff --git a/src/App/ProjectFile.h b/src/App/ProjectFile.h new file mode 100644 index 0000000000..b4e2a6c409 --- /dev/null +++ b/src/App/ProjectFile.h @@ -0,0 +1,204 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later + +/*************************************************************************** + * Copyright (c) 2024 Werner Mayer * + * * + * This file is part of FreeCAD. * + * * + * FreeCAD is free software: you can redistribute it and/or modify it * + * under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 2.1 of the * + * License, or (at your option) any later version. * + * * + * FreeCAD is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with FreeCAD. If not, see * + * . * + * * + **************************************************************************/ + +#ifndef APP_PROJECTFILE_H +#define APP_PROJECTFILE_H + +#include +#include +#include +#include +#include +#include +#include + +XERCES_CPP_NAMESPACE_BEGIN +class DOMDocument; +class DOMElement; +class DOMNode; +XERCES_CPP_NAMESPACE_END + +namespace App +{ + +class PropertyContainer; +class Property; + +/** + * @code + * ProjectFile proj(project.fcstd); + * if (proj.loadDocument()) { + * std::list names = proj.getObjects(); + * std::list files = proj.getInputFiles(names.front().name); + * std::string meshFile = proj.extractInputFile(files.front()); + * std::ifstream str(meshFile.c_str(), std::ios::in | std::ios::binary); + * Mesh::MeshObject mesh; + * mesh.load(str); + * } + * @endcode + */ +class AppExport ProjectFile +{ +public: + struct Object + { + std::string name; + Base::Type type; + }; + struct PropertyFile + { + std::string file; + std::string name; + Base::Type type; + }; + struct Metadata + { + std::string comment; + std::string company; + std::string createdBy; + std::string creationDate; + std::string label; + std::string lastModifiedBy; + std::string lastModifiedDate; + std::string license; + std::string licenseURL; + std::string programVersion; + std::string uuid; + }; + /** + * Default constructor + */ + ProjectFile(); + /** + * Creates an instance of ProjectFile pointing to a project file. + * You have to call @ref loadDocument() afterwards. + */ + explicit ProjectFile(std::string zipArchive); + /** + * Destructor + */ + ~ProjectFile(); + /** + * Copy/move constructor & assignment + */ + ProjectFile(const ProjectFile&) = delete; + ProjectFile(ProjectFile&&) = delete; + ProjectFile& operator=(const ProjectFile&) = delete; + ProjectFile& operator=(ProjectFile&&) = delete; + /** + * Assigns the file name of another project file. This clears any internal data, thus + * @ref loadDocument() needs to be called afterwards. + */ + void setProjectFile(const std::string& zipArchive); + /** + * Loads the embedded document file of the project file. This is needed to query any information + * about objects, their type ids or any referenced input files. + */ + bool loadDocument(); + /** + * Return the meta data of the loaded document. + */ + Metadata getMetadata() const; + /** + * Returns a list of all object names with their type ids which are part of the project file. + */ + std::list getObjects() const; + /** + * Returns a list of object names of a given type id which are part of the project file. + */ + std::list getObjectsOfType(const Base::Type&) const; + /** + * Restores the object identified by its @a name and loads all properties of @a prop + * that match with the saved properties. + */ + bool restoreObject(const std::string& name, App::PropertyContainer*, bool verbose = true); + /** + * Retrieves the type id of a given object name. + */ + Base::Type getTypeId(const std::string& name) const; + /** + * Retrieves a list of input file names and their associated property names and type ids + * to the given object name. + * @see getInputFiles(). + */ + std::list getPropertyFiles(const std::string& name) const; + /** + * Retrieves a list of input file names referenced to the given object name. + * This method does the same as @ref getPropertyFiles() unless that it only + * returns the file names. + */ + std::list getInputFiles(const std::string& name) const; + /** + * Extracts an input file of @a name and saves it as file to the disk. + * It's up to the client code to delete the file if needed. + */ + std::string extractInputFile(const std::string& name); + /** + * Extracts, via a temporary file the content of an input file of @a name. + */ + void readInputFile(const std::string& name, std::stringstream& str); + /** + * Directly extracts the content of an input file of @a name. + */ + void readInputFileDirect(const std::string& name, std::stringstream& str); + /** + * Replaces the input file @a name with the content of the given @a stream. + * The method returns the file name of the newly created project file. + */ + std::string replaceInputFile(const std::string& name, std::istream& inp); + /** + * Replaces the input files with the streams of @a inp. + * The method returns the file name of the newly created project file. + * If you want to replace the original project file you must call the + * method @ref replaceProjectFile() with the returned file name as argument. + */ + std::string replaceInputFiles(const std::map& inp); + /** + * Replaces the property files with the content of the properties in @a props. + * The method returns the file name of the newly created project file. + * If you want to replace the original project file you must call the + * method @ref replaceProjectFile() with the returned file name as argument. + */ + std::string replacePropertyFiles(const std::map& props); + /** + * Replaces the project file with the given @a name. If @a keepfile is true + * then the renamed original file won't be deleted. + * Returns true if the replacement succeeded and false otherwise. + * If you want to replace the original project file you must call the + * method @ref replaceProjectFile() with the returned file name as argument. + */ + bool replaceProjectFile(const std::string& name, bool keepfile = false); + +private: + void findFiles(XERCES_CPP_NAMESPACE_QUALIFIER DOMNode*, std::list&) const; + void findFiles(XERCES_CPP_NAMESPACE_QUALIFIER DOMNode*, std::list&) const; + +private: + std::string stdFile; + XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument* xmlDocument; +}; + + +} // namespace APP + +#endif // APP_PROJECTFILE_H