diff --git a/src/App/Extension.h b/src/App/Extension.h index 79171b2071..84bb442a10 100644 --- a/src/App/Extension.h +++ b/src/App/Extension.h @@ -273,6 +273,7 @@ public: //@{ virtual void extensionSave(Base::Writer&) const {} virtual void extensionRestore(Base::XMLReader&) {} + virtual void extensionRestore(Base::DocumentReader&) {} //@} /** @name TypeHandling */ diff --git a/src/App/ExtensionContainer.cpp b/src/App/ExtensionContainer.cpp index 30022e809b..372967dbae 100644 --- a/src/App/ExtensionContainer.cpp +++ b/src/App/ExtensionContainer.cpp @@ -30,6 +30,7 @@ #include "Extension.h" #include "ExtensionContainer.h" +#include using namespace App; @@ -307,6 +308,18 @@ void ExtensionContainer::Restore(Base::XMLReader& reader) { App::PropertyContainer::Restore(reader); } +void ExtensionContainer::Restore(Base::DocumentReader& reader,XERCES_CPP_NAMESPACE_QUALIFIER DOMElement *viewProviderEl) { + //restore dynamic extensions. + //Note 1: The extension element must be read first, before all other object elements. That is + // needed as the element works together with an object element attribute, which would be + // cleared if another attribute is read first + //Note 2: This must happen before the py object of this container is used, as only in the + // pyobject constructor the extension methods are added to the container. + restoreExtensions(reader,viewProviderEl); + //TODO NOW: + //App::PropertyContainer::Restore(reader); +} + void ExtensionContainer::saveExtensions(Base::Writer& writer) const { //we don't save anything if there are no dynamic extensions @@ -352,6 +365,70 @@ void ExtensionContainer::saveExtensions(Base::Writer& writer) const { writer.decInd(); } +void ExtensionContainer::restoreExtensions(Base::DocumentReader& reader,XERCES_CPP_NAMESPACE_QUALIFIER DOMElement *viewProviderEl) { + //Dynamic extensions are optional (also because they are introduced late into the document format) + //and hence it is possible that the element does not exist. As we cannot check for the existence of + //an element a object attribute is set if extensions are available. Here we check that + //attribute, and only if it exists the extensions element will be available. + const char* expanded_cstr = reader.GetAttribute(viewProviderEl,"expanded"); + if(!expanded_cstr) + return; + auto ExtensionsDOM = reader.FindElement(viewProviderEl,"Extensions"); + if(ExtensionsDOM){ + const char* cnt_cstr = reader.GetAttribute(ExtensionsDOM,"Count"); + if(cnt_cstr){ + long Cnt = reader.ContentToInt( cnt_cstr ); + for (int i=0 ;i(extension.createInstance()); + //check if this really is a python extension! + if (!ext->isPythonExtension()) { + delete ext; + std::stringstream str; + str << "Extension is not a python addable version: '" << type_cstr << "'" << std::ends; + throw Base::TypeError(str.str()); + } + ext->initExtension(this); + if( strcmp(ext->getExtensionTypeId().getName(), type_cstr) == 0 ) + ext->extensionRestore(reader); + } + } + catch (const Base::XMLParseException&) { + throw; // re-throw + } + catch (const Base::Exception &e) { + Base::Console().Error("%s\n", e.what()); + } + catch (const std::exception &e) { + Base::Console().Error("%s\n", e.what()); + } + catch (const char* e) { + Base::Console().Error("%s\n", e); + } + #ifndef FC_DEBUG + catch (...) { + Base::Console().Error("ExtensionContainer::Restore: Unknown C++ exception thrown\n"); + } + #endif + } + } + } +} + void ExtensionContainer::restoreExtensions(Base::XMLReader& reader) { //Dynamic extensions are optional (also because they are introduced late into the document format) diff --git a/src/App/ExtensionContainer.h b/src/App/ExtensionContainer.h index d3661c1dcf..9c90b95fbf 100644 --- a/src/App/ExtensionContainer.h +++ b/src/App/ExtensionContainer.h @@ -25,6 +25,14 @@ #define APP_EXTENSIONCONTAINER_H #include "PropertyContainer.h" +#include + +XERCES_CPP_NAMESPACE_BEGIN + class DOMNode; + class DOMElement; +// class DefaultHandler; +// class SAX2XMLReader; +XERCES_CPP_NAMESPACE_END namespace App { @@ -177,11 +185,13 @@ public: void Save(Base::Writer& writer) const override; void Restore(Base::XMLReader& reader) override; + void Restore(Base::DocumentReader& reader,XERCES_CPP_NAMESPACE_QUALIFIER DOMElement *viewProviderEl); //those methods save/restore the dynamic extensions without handling properties, which is something //done by the default Save/Restore methods. void saveExtensions(Base::Writer& writer) const; void restoreExtensions(Base::XMLReader& reader); + void restoreExtensions(Base::DocumentReader& reader,XERCES_CPP_NAMESPACE_QUALIFIER DOMElement *viewProviderEl); /** Extends the rules for handling property name changed, so that extensions are given an opportunity to handle it. * If an extension handles a change, neither the rest of the extensions, nor the container itself get to handle it. diff --git a/src/App/PropertyContainer.cpp b/src/App/PropertyContainer.cpp index 09e9120c69..837162fc4b 100644 --- a/src/App/PropertyContainer.cpp +++ b/src/App/PropertyContainer.cpp @@ -30,6 +30,7 @@ #include "Property.h" #include "PropertyContainer.h" +#include FC_LOG_LEVEL_INIT("App",true,true) @@ -402,6 +403,103 @@ void PropertyContainer::Restore(Base::XMLReader &reader) reader.readEndElement("Properties"); } +void PropertyContainer::Restore(Base::DocumentReader &reader) +{ + //TODO NOW + Base::Console().Error("PropertyContainer::Restore: DocumentReader\n"); + reader.clearPartialRestoreProperty(); + /* + reader.clearPartialRestoreProperty(); + reader.readElement("Properties"); + int Cnt = reader.getAttributeAsInteger("Count"); + + int transientCount = 0; + if(reader.hasAttribute("TransientCount")) + transientCount = reader.getAttributeAsUnsigned("TransientCount"); + + for (int i=0;igetName() << "'"); + if(prop && reader.hasAttribute("status")) + prop->setStatusValue(reader.getAttributeAsUnsigned("status")); + } + + for (int i=0 ;igetContainer() != this) + prop = dynamicProps.restore(*this,PropName.c_str(),TypeName.c_str(),reader); + + decltype(Property::StatusBits) status; + if(reader.hasAttribute("status")) { + status = decltype(status)(reader.getAttributeAsUnsigned("status")); + if(prop) + prop->setStatusValue(status.to_ulong()); + } + // name and type match + if (prop && strcmp(prop->getTypeId().getName(), TypeName.c_str()) == 0) { + if (!prop->testStatus(Property::Transient) + && !status.test(Property::Transient) + && !status.test(Property::PropTransient) + && !prop->testStatus(Property::PropTransient)) + { + FC_TRACE("restore property '" << prop->getName() << "'"); + prop->Restore(reader); + }else + FC_TRACE("skip transient '" << prop->getName() << "'"); + } + // name matches but not the type + else if (prop) { + handleChangedPropertyType(reader, TypeName.c_str(), prop); + } + // name doesn't match, the sub-class then has to know + // if the property has been renamed or removed + else { + handleChangedPropertyName(reader, TypeName.c_str(), PropName.c_str()); + } + + if (reader.testStatus(Base::XMLReader::ReaderStatus::PartialRestoreInProperty)) { + Base::Console().Error("Property %s of type %s was subject to a partial restore.\n",PropName.c_str(),TypeName.c_str()); + reader.clearPartialRestoreProperty(); + } + } + catch (const Base::XMLParseException&) { + throw; // re-throw + } + catch (const Base::RestoreError &) { + reader.setPartialRestore(true); + reader.clearPartialRestoreProperty(); + Base::Console().Error("Property %s of type %s was subject to a partial restore.\n",PropName.c_str(),TypeName.c_str()); + } + catch (const Base::Exception &e) { + Base::Console().Error("%s\n", e.what()); + } + catch (const std::exception &e) { + Base::Console().Error("%s\n", e.what()); + } + catch (const char* e) { + Base::Console().Error("%s\n", e); + } +#ifndef FC_DEBUG + catch (...) { + Base::Console().Error("PropertyContainer::Restore: Unknown C++ exception thrown\n"); + } +#endif + reader.readEndElement("Property"); + } + reader.readEndElement("Properties"); + */ +} + void PropertyContainer::onPropertyStatusChanged(const Property &prop, unsigned long oldStatus) { (void)prop; diff --git a/src/App/PropertyContainer.h b/src/App/PropertyContainer.h index c86b03e9a4..4a165d51ee 100644 --- a/src/App/PropertyContainer.h +++ b/src/App/PropertyContainer.h @@ -220,6 +220,7 @@ public: void Save (Base::Writer &writer) const override; void Restore(Base::XMLReader &reader) override; + void Restore(Base::DocumentReader &reader) override; virtual void editProperty(const char * /*propName*/) {} diff --git a/src/Base/CMakeLists.txt b/src/Base/CMakeLists.txt index 3b5c9e159a..1c8ad149b6 100644 --- a/src/Base/CMakeLists.txt +++ b/src/Base/CMakeLists.txt @@ -267,6 +267,7 @@ SET(FreeCADBase_CPP_SRCS Writer.cpp XMLTools.cpp ZipHeader.cpp + DocumentReader.cpp ) SET(SWIG_HEADERS diff --git a/src/Base/DocumentReader.cpp b/src/Base/DocumentReader.cpp new file mode 100644 index 0000000000..01b97285f0 --- /dev/null +++ b/src/Base/DocumentReader.cpp @@ -0,0 +1,314 @@ +/*************************************************************************** + * Copyright (c) 2011 Jürgen Riegel * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library 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 Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ +//#include "PreCompiled.h" + +//#include + +#include "DocumentReader.h" + +//#include "Reader.h" +//#include "Base64.h" +//#include "Console.h" +#include "InputSource.h" +//#include "Persistence.h" +//#include "Sequencer.h" +//#include "Stream.h" +#include "XMLTools.h" +//#ifdef _MSC_VER +//#include +//#endif +//#include + +#include +#include +#ifndef _PreComp_ +//# include +//# include +# include +//# include +//# include +//# include +//# include +# include +//# include +//# include +//# include +//# include +//# include +#endif + +XERCES_CPP_NAMESPACE_USE + +//using namespace std; +using namespace Base; + +// --------------------------------------------------------------------------- +// DocumentReader: Constructors and Destructor +// --------------------------------------------------------------------------- +static XercesDOMParser::ValSchemes gValScheme = XercesDOMParser::Val_Auto; +DocumentReader::DocumentReader() +{ + + gDoNamespaces = false; + gDoSchema = false; + gSchemaFullChecking = false; + gDoCreate = true; + + /* + gOutputEncoding = nullptr; + gMyEOLSequence = nullptr; + + gSplitCdataSections = true; + gDiscardDefaultContent = true; + gUseFilter = true; + gFormatPrettyPrint = true; + */ + +} + +//DocumentReader::~DocumentReader() +//{ + //delete parser; +//} +//int DocumentReader::LoadDocument(const XERCES_CPP_NAMESPACE_QUALIFIER InputSource& inputSource) +//int DocumentReader::LoadDocument(std::istream& Stream,std::string filename) +int DocumentReader::LoadDocument(Base::Reader& reader) +{ + FileInfo _File( reader.getFileName() ); + StdInputSource inputSource(reader, _File.filePath().c_str()); + + // + // Create our parser, then attach an error handler to the parser. + // The parser will call back to methods of the ErrorHandler if it + // discovers errors during the course of parsing the XML document. + // + XercesDOMParser *parser = new XercesDOMParser; + parser->setValidationScheme(gValScheme); + parser->setDoNamespaces(gDoNamespaces); + parser->setDoSchema(gDoSchema); + parser->setValidationSchemaFullChecking(gSchemaFullChecking); + parser->setCreateEntityReferenceNodes(gDoCreate); + + DOMTreeErrorReporter *errReporter = new DOMTreeErrorReporter(); + parser->setErrorHandler(errReporter); + // + // Parse the XML file, catching any XML exceptions that might propagate + // out of it. + // + bool errorsOccured = false; + try { + parser->parse(inputSource); + } + catch (const XMLException& e) { + std::cerr << "An error occurred during parsing\n Message: " + << StrX(e.getMessage()) << std::endl; + errorsOccured = true; + } + catch (const DOMException& e) { + std::cerr << "A DOM error occurred during parsing\n DOMException code: " + << e.code << std::endl; + errorsOccured = true; + } + catch (...) { + std::cerr << "An error occurred during parsing\n " << std::endl; + errorsOccured = true; + } + + if (errorsOccured) { + delete parser; + delete errReporter; + return 0; + } + + XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument* _pDocument = parser->adoptDocument(); + delete parser; + delete errReporter; + + if (!_pDocument) + throw XMLBaseException("Malformed Parameter document: Invalid document"); + + DOMElement* rootElem = _pDocument->getDocumentElement(); + if (!rootElem) + throw XMLBaseException("Malformed Parameter document: Root group not found"); + + _pGroupNode = rootElem; + + if (!_pGroupNode){ + throw XMLBaseException("Malformed document."); + } + + return 1; +} + +XERCES_CPP_NAMESPACE_QUALIFIER DOMElement *DocumentReader::GetRootElement() const +{ + //if (!_pGroupNode) + //return nullptr; + return _pGroupNode; +} + +XERCES_CPP_NAMESPACE_QUALIFIER DOMElement *DocumentReader::FindElement(const char* Type) const +{ + if(!Type) + return nullptr; + + for (DOMNode *clChild = _pGroupNode->getFirstChild(); clChild != nullptr; clChild = clChild->getNextSibling()) { + if (clChild->getNodeType() == DOMNode::ELEMENT_NODE) { + if (!strcmp(Type,StrX(clChild->getNodeName()).c_str())) { + if (clChild->getAttributes()->getLength() > 0) { + return static_cast(clChild); + } + } + } + } + return nullptr; +} + +XERCES_CPP_NAMESPACE_QUALIFIER DOMElement *DocumentReader::FindElement(XERCES_CPP_NAMESPACE_QUALIFIER DOMElement* Start, const char* Type) const +{ + if(!Start || !Type) + return nullptr; + for (DOMNode *clChild = Start->getFirstChild(); clChild != nullptr; clChild = clChild->getNextSibling()) { + if (clChild->getNodeType() == DOMNode::ELEMENT_NODE) { + if (!strcmp(Type,StrX(clChild->getNodeName()).c_str())) { + return static_cast(clChild); + //if (clChild->getAttributes()->getLength() > 0) { + //return static_cast(clChild); + //} + } + } + } + return nullptr; +} + +XERCES_CPP_NAMESPACE_QUALIFIER DOMElement *DocumentReader::FindNextElement(XERCES_CPP_NAMESPACE_QUALIFIER DOMNode *Prev, const char* Type) const +{ + if (!Prev || !Type) + return nullptr; + DOMNode *clChild = Prev; + while ((clChild = clChild->getNextSibling()) != nullptr) { + if (clChild->getNodeType() == DOMNode::ELEMENT_NODE) { + // the right node Type + if (!strcmp(Type,StrX(clChild->getNodeName()).c_str())) { + return static_cast(clChild); + } + } + } + return nullptr; +} + +/* +//CONTENT: +const char * DocumentReader::GetContent(XERCES_CPP_NAMESPACE_QUALIFIER DOMElement *DOMEl) const +{ + if (!DOMEl) + return nullptr; + return StrX(DOMEl->getAttribute(XStr("Value").unicodeForm())).c_str(); +} + +std::string DocumentReader::GetContentASCII(XERCES_CPP_NAMESPACE_QUALIFIER DOMElement *DOMEl) const +{ + //return std::string(StrXUTF8(pcElem->getNodeValue()).c_str() ); + //maybe its better to use getNodeValue() + if (!DOMEl) + return nullptr; + return std::string( StrXUTF8(DOMEl->getAttribute(XStr("Value").unicodeForm())).c_str() ); +} +*/ + +long DocumentReader::ContentToInt( const char* content ) const +{ + return atol( content ); +} + +unsigned long DocumentReader::ContentToUnsigned(const char* content) const +{ + return strtoul(content,nullptr,10); +} + +double DocumentReader::ContentToFloat(const char* content) const +{ + return atof(content); +} + +bool DocumentReader::ContentToBool(const char* content) const +{ + if (strcmp(content,"1")) + return false; + else + return true; +} + +//ATTRIBUTE: +const char * DocumentReader::GetAttribute(XERCES_CPP_NAMESPACE_QUALIFIER DOMElement *DOMEl, const char* Attr) const +{ + if(!Attr) + return nullptr; + XStr xstr( Attr ); + bool hasAttr = DOMEl->hasAttribute(xstr.unicodeForm()); + if (!hasAttr){ + return nullptr; + } + const XMLCh * attr = DOMEl->getAttribute( xstr.unicodeForm() ); + return strdup( StrX( attr ).c_str() ); +} + +const char * DocumentReader::GetAttribute(const char* Attr) const +{ + if(!Attr) + return nullptr; + XStr xstr( Attr ); + bool hasAttr = _pGroupNode->hasAttribute(xstr.unicodeForm()); + if (!hasAttr){ + return nullptr; + } + const XMLCh * attr = _pGroupNode->getAttribute( xstr.unicodeForm() ); + //stringLen + return strdup( StrX( attr ).c_str() );//strdup is needed since pointer from strx only exists in context where StrX() is created. +} + +/* +unsigned int Base::XMLReader::getAttributeCount() const +{ + return static_cast(AttrMap.size()); +} +*/ + +//Status + +void Base::DocumentReader::clearPartialRestoreProperty() +{ + setStatus(PartialRestoreInProperty, false); + setStatus(PartialRestoreInObject, false); +} + +bool Base::DocumentReader::testStatus(ReaderStatus pos) const +{ + return StatusBits.test(static_cast(pos)); +} + +void Base::DocumentReader::setStatus(ReaderStatus pos, bool on) +{ + StatusBits.set(static_cast(pos), on); +} + + diff --git a/src/Base/DocumentReader.h b/src/Base/DocumentReader.h new file mode 100644 index 0000000000..e414046f37 --- /dev/null +++ b/src/Base/DocumentReader.h @@ -0,0 +1,98 @@ +/*************************************************************************** + * Copyright (c) 2011 Jürgen Riegel * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library 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 Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + +#ifndef BASE_DOCUMENTREADER_H +#define BASE_DOCUMENTREADER_H + +#include +#include +//#include +//#include +#include +#include + +//#include +//#include +//#include +#include +//#include "FileInfo.h"//reemplazado por: +#include +//#include + +XERCES_CPP_NAMESPACE_BEGIN + class DOMNode; + class DOMElement; +// class DefaultHandler; +// class SAX2XMLReader; +XERCES_CPP_NAMESPACE_END +namespace Base +{ +class Reader; +class Persistence; + +class BaseExport DocumentReader //: public ParameterManager +{ +public: + enum ReaderStatus { + PartialRestore = 0, // This bit indicates that a partial restore took place somewhere in this Document + PartialRestoreInDocumentObject = 1, // This bit is local to the DocumentObject being read indicating a partial restore therein + PartialRestoreInProperty = 2, // Local to the Property + PartialRestoreInObject = 3 // Local to the object partially restored itself + }; + DocumentReader(); + int LoadDocument(Base::Reader& reader); + + XERCES_CPP_NAMESPACE_QUALIFIER DOMElement *GetRootElement() const; + XERCES_CPP_NAMESPACE_QUALIFIER DOMElement *FindElement(XERCES_CPP_NAMESPACE_QUALIFIER DOMElement* Start, const char* Type) const; + XERCES_CPP_NAMESPACE_QUALIFIER DOMElement *FindElement(const char* Type) const; + XERCES_CPP_NAMESPACE_QUALIFIER DOMElement *FindNextElement(XERCES_CPP_NAMESPACE_QUALIFIER DOMNode *Prev, const char* Type) const; + + long ContentToASCII(const char* Content) const; + + long ContentToInt(const char* Content) const; + unsigned long ContentToUnsigned(const char* Content) const; + double ContentToFloat(const char* Content) const; + bool ContentToBool(const char* Content) const; + + const char* GetAttribute(XERCES_CPP_NAMESPACE_QUALIFIER DOMElement* DOMEl, const char* Attr) const; + const char* GetAttribute(const char* Attr) const; + + /// return the status bits + bool testStatus(ReaderStatus pos) const; + /// set the status bits + void setStatus(ReaderStatus pos, bool on); + + void clearPartialRestoreProperty(); + +protected: + XERCES_CPP_NAMESPACE_QUALIFIER DOMElement *_pGroupNode; + bool gDoNamespaces ; + bool gDoSchema ; + bool gSchemaFullChecking ; + bool gDoCreate ; + std::bitset<32> StatusBits; + +}; + +} + +#endif diff --git a/src/Base/Parameter.cpp b/src/Base/Parameter.cpp index 93d45a9ca9..6753142d14 100644 --- a/src/Base/Parameter.cpp +++ b/src/Base/Parameter.cpp @@ -75,43 +75,44 @@ using namespace Base; //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -class DOMTreeErrorReporter : public ErrorHandler +DOMTreeErrorReporter::DOMTreeErrorReporter(): + fSawErrors(false) { +} + +void DOMTreeErrorReporter::warning(const SAXParseException&) { -public: - // ----------------------------------------------------------------------- - // Constructors and Destructor - // ----------------------------------------------------------------------- - DOMTreeErrorReporter() : - fSawErrors(false) { - } + // + // Ignore all warnings. + // +} - ~DOMTreeErrorReporter() override = default; +void DOMTreeErrorReporter::error(const SAXParseException& toCatch) +{ + fSawErrors = true; + std::cerr << "Error at file \"" << StrX(toCatch.getSystemId()) + << "\", line " << toCatch.getLineNumber() + << ", column " << toCatch.getColumnNumber() + << "\n Message: " << StrX(toCatch.getMessage()) << std::endl; +} +void DOMTreeErrorReporter::fatalError(const SAXParseException& toCatch) +{ + fSawErrors = true; + std::cerr << "Fatal Error at file \"" << StrX(toCatch.getSystemId()) + << "\", line " << toCatch.getLineNumber() + << ", column " << toCatch.getColumnNumber() + << "\n Message: " << StrX(toCatch.getMessage()) << std::endl; +} - // ----------------------------------------------------------------------- - // Implementation of the error handler interface - // ----------------------------------------------------------------------- - void warning(const SAXParseException& toCatch) override; - void error(const SAXParseException& toCatch) override; - void fatalError(const SAXParseException& toCatch) override; - void resetErrors() override; - - // ----------------------------------------------------------------------- - // Getter methods - // ----------------------------------------------------------------------- - bool getSawErrors() const; - - // ----------------------------------------------------------------------- - // Private data members - // - // fSawErrors - // This is set if we get any errors, and is queryable via a getter - // method. Its used by the main code to suppress output if there are - // errors. - // ----------------------------------------------------------------------- - bool fSawErrors; -}; +void DOMTreeErrorReporter::resetErrors() +{ + // No-op in this case +} +inline bool DOMTreeErrorReporter::getSawErrors() const +{ + return fSawErrors; +} class DOMPrintFilter : public DOMLSSerializerFilter { @@ -157,14 +158,6 @@ private : void operator=(const DOMErrorHandler&); }; - - -inline bool DOMTreeErrorReporter::getSawErrors() const -{ - return fSawErrors; -} - - //************************************************************************** //************************************************************************** // ParameterManager @@ -1865,42 +1858,6 @@ void ParameterManager::CheckDocument() const } -//************************************************************************** -//************************************************************************** -// DOMTreeErrorReporter -//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - -void DOMTreeErrorReporter::warning(const SAXParseException&) -{ - // - // Ignore all warnings. - // -} - -void DOMTreeErrorReporter::error(const SAXParseException& toCatch) -{ - fSawErrors = true; - std::cerr << "Error at file \"" << StrX(toCatch.getSystemId()) - << "\", line " << toCatch.getLineNumber() - << ", column " << toCatch.getColumnNumber() - << "\n Message: " << StrX(toCatch.getMessage()) << std::endl; -} - -void DOMTreeErrorReporter::fatalError(const SAXParseException& toCatch) -{ - fSawErrors = true; - std::cerr << "Fatal Error at file \"" << StrX(toCatch.getSystemId()) - << "\", line " << toCatch.getLineNumber() - << ", column " << toCatch.getColumnNumber() - << "\n Message: " << StrX(toCatch.getMessage()) << std::endl; -} - -void DOMTreeErrorReporter::resetErrors() -{ - // No-op in this case -} - - //************************************************************************** //************************************************************************** // DOMPrintFilter diff --git a/src/Base/Parameter.h b/src/Base/Parameter.h index 72a2391d4f..eece3510b6 100644 --- a/src/Base/Parameter.h +++ b/src/Base/Parameter.h @@ -65,6 +65,10 @@ using PyObject = struct _object; # pragma warning( disable : 4275 ) #endif +#ifndef _PreComp_ +# include +#endif + XERCES_CPP_NAMESPACE_BEGIN class DOMNode; @@ -279,6 +283,7 @@ protected: ~ParameterGrp() override; /// helper function for GetGroup Base::Reference _GetGroup(const char* Name); + bool ShouldRemove() const; void _Reset(); @@ -411,16 +416,17 @@ public: /// Saves an XML document by calling the serializer's save method. void SaveDocument() const; //@} - -private: - - XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument *_pDocument; - ParameterSerializer * paramSerializer; - - bool gDoNamespaces ; + +protected: + bool gDoNamespaces ; bool gDoSchema ; bool gSchemaFullChecking ; bool gDoCreate ; + XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument *_pDocument; +private: + ParameterManager(); + ~ParameterManager() override; + ParameterSerializer * paramSerializer; const XMLCh* gOutputEncoding ; @@ -431,11 +437,42 @@ private: bool gUseFilter ; bool gFormatPrettyPrint ; -private: - ParameterManager(); - ~ParameterManager() override; }; +XERCES_CPP_NAMESPACE_USE + +class DOMTreeErrorReporter : public ErrorHandler +{ +public: + // ----------------------------------------------------------------------- + // Constructors and Destructor + // ----------------------------------------------------------------------- + DOMTreeErrorReporter(); + ~DOMTreeErrorReporter() override = default; + // ----------------------------------------------------------------------- + // Implementation of the error handler interface + // ----------------------------------------------------------------------- + void warning(const SAXParseException& toCatch) override; + void error(const SAXParseException& toCatch) override; + void fatalError(const SAXParseException& toCatch) override; + void resetErrors() override; + // ----------------------------------------------------------------------- + // Getter methods + // ----------------------------------------------------------------------- + bool getSawErrors() const; +private: + // ----------------------------------------------------------------------- + // Private data members + // + // fSawErrors + // This is set if we get any errors, and is queryable via a getter + // method. Its used by the main code to suppress output if there are + // errors. + // ----------------------------------------------------------------------- + bool fSawErrors; +}; + + /** python wrapper function */ BaseExport PyObject* GetPyObject( const Base::Reference &hcParamGrp); diff --git a/src/Base/Persistence.cpp b/src/Base/Persistence.cpp index 892fc4df36..742a06cf35 100644 --- a/src/Base/Persistence.cpp +++ b/src/Base/Persistence.cpp @@ -61,6 +61,12 @@ void Persistence::Save (Writer &/*writer*/) const assert(0); } +void Persistence::Restore(DocumentReader &/*reader*/) +{ + // you have to implement this method in all descending classes! + assert(0); +} + void Persistence::Restore(XMLReader &/*reader*/) { // you have to implement this method in all descending classes! diff --git a/src/Base/Persistence.h b/src/Base/Persistence.h index 75deac6212..7941f7c748 100644 --- a/src/Base/Persistence.h +++ b/src/Base/Persistence.h @@ -31,6 +31,7 @@ namespace Base class Reader; class Writer; class XMLReader; +class DocumentReader; /// Persistence class and root of the type system class BaseExport Persistence : public BaseClass @@ -77,6 +78,7 @@ public: * \endcode */ virtual void Restore(XMLReader &/*reader*/) = 0; + virtual void Restore(DocumentReader &/*reader*/); /** This method is used to save large amounts of data to a binary file. * Sometimes it makes no sense to write property data as XML. In case the * amount of data is too big or the data type has a more effective way to diff --git a/src/Base/Reader.cpp b/src/Base/Reader.cpp index 5580d0897a..92e207953b 100644 --- a/src/Base/Reader.cpp +++ b/src/Base/Reader.cpp @@ -25,6 +25,7 @@ #ifndef _PreComp_ # include +# include #endif #include @@ -42,7 +43,7 @@ #include #endif #include - +#include XERCES_CPP_NAMESPACE_USE @@ -330,24 +331,11 @@ void Base::XMLReader::readFiles(zipios::ZipInputStream &zipstream) const // no file name for the current entry in the zip was registered. if (jt != FileList.end()) { try { - Base::Reader reader(zipstream, jt->FileName, FileVersion); - - try{ - jt->Object->RestoreDocFile(reader); - }catch (const Base::XMLParseException& e) { - //For some reason catching this error in RestoreDocFile(reader) its working but - //still need to catch it here again. Im not sure how its that possible since its from a constructor. - - //It comes from trying to read "ProjectUnitSystem" from GuiDocument.xml and reaching EndDocument - //because was not found. - //I dont think EndDocument should throw error anyway. - if(e.getMessage() != "End of document reached"){ - throw; - } - } - - if (reader.getLocalReader()) - reader.getLocalReader()->readFiles(zipstream); + Base::Reader reader(zipstream, jt->FileName, FileVersion); + jt->Object->RestoreDocFile(reader); + + if (reader.getLocalReader()) + reader.getLocalReader()->readFiles(zipstream); }catch(...) { // For any exception we just continue with the next file. // It doesn't matter if the last reader has read more or diff --git a/src/Gui/Document.cpp b/src/Gui/Document.cpp index 703856a99c..f4c31f7b8a 100644 --- a/src/Gui/Document.cpp +++ b/src/Gui/Document.cpp @@ -33,6 +33,7 @@ # include # include # include +# include #endif #include @@ -45,6 +46,7 @@ #include #include #include +#include #include #include @@ -66,7 +68,6 @@ #include "ViewProviderDocumentObjectGroup.h" #include "WaitCursor.h" - FC_LOG_LEVEL_INIT("Gui", true, true) using namespace Gui; @@ -680,7 +681,6 @@ void Document::slotNewObject(const App::DocumentObject& Obj) { auto pcProvider = static_cast(getViewProvider(&Obj)); if (!pcProvider) { - //Base::Console().Log("Document::slotNewObject() called\n"); std::string cName = Obj.getViewProviderNameStored(); for(;;) { if (cName.empty()) { @@ -762,7 +762,6 @@ void Document::slotDeletedObject(const App::DocumentObject& Obj) { std::list::iterator vIt; setModified(true); - //Base::Console().Log("Document::slotDeleteObject() called\n"); // cycling to all views of the document ViewProvider* viewProvider = getViewProvider(&Obj); @@ -1415,88 +1414,134 @@ void Document::Restore(Base::XMLReader &reader) */ void Document::RestoreDocFile(Base::Reader &reader) { - // We must create an XML parser to read from the input stream - std::shared_ptr localreader = std::make_shared("GuiDocument.xml", reader); - localreader->FileVersion = reader.getFileVersion(); - - localreader->readElement("Document"); - long scheme = localreader->getAttributeAsInteger("SchemaVersion"); - localreader->DocumentSchema = scheme; - - bool hasExpansion = localreader->hasAttribute("HasExpansion"); - if(hasExpansion) { - auto tree = TreeWidget::instance(); + Base::DocumentReader docReader; + docReader.LoadDocument(reader); + //docReader.GetRootElement()//can be used to get Document XMLElement, but not needed. + const char* SchemaVersion_cstr = docReader.GetAttribute("SchemaVersion"); + long SchemaVersion = docReader.ContentToInt( SchemaVersion_cstr ); + + const char* HasExpansion_cstr = docReader.GetAttribute("HasExpansion"); + if(HasExpansion_cstr){ + auto tree = TreeWidget::instance(); if(tree) { auto docItem = tree->getDocumentItem(this); if(docItem) - docItem->Restore(*localreader); + docItem->Restore(docReader); } } - + // At this stage all the document objects and their associated view providers exist. // Now we must restore the properties of the view providers only. - // - // SchemeVersion "1" - if (scheme == 1) { - // read the viewproviders itself - localreader->readElement("ViewProviderData"); - int Cnt = localreader->getAttributeAsInteger("Count"); - for (int i=0; ireadElement("ViewProvider"); - std::string name = localreader->getAttribute("name"); - bool expanded = false; - if (!hasExpansion && localreader->hasAttribute("expanded")) { - const char* attr = localreader->getAttribute("expanded"); - if (strcmp(attr,"1") == 0) { - expanded = true; - } - } - ViewProvider* pObj = getViewProviderByName(name.c_str()); - if (pObj) // check if this feature has been registered - pObj->Restore(*localreader); - if (pObj && expanded) { - auto vp = static_cast(pObj); - this->signalExpandObject(*vp, TreeItemMode::ExpandItem,0,0); - } - localreader->readEndElement("ViewProvider"); - } - localreader->readEndElement("ViewProviderData"); + if (SchemaVersion == 1) { + auto VProviderDataDOM = docReader.FindElement("ViewProviderData"); + if(VProviderDataDOM){ + const char* vpd_count_cstr = docReader.GetAttribute(VProviderDataDOM,"Count"); + if(vpd_count_cstr){ + long Cnt = docReader.ContentToInt( vpd_count_cstr ); + auto prev_ViewProviderDOM = docReader.FindElement(VProviderDataDOM,"ViewProvider"); + if(prev_ViewProviderDOM){ + const char* name_cstr = docReader.GetAttribute(prev_ViewProviderDOM,"name"); + const char* expanded_cstr = docReader.GetAttribute(prev_ViewProviderDOM,"expanded"); + bool expanded = false; + if (!HasExpansion_cstr && expanded_cstr) { + if (strcmp(expanded_cstr,"1") == 0) { + expanded = true; + } + } + + ViewProvider* pObj = getViewProviderByName(name_cstr); + if (pObj) + pObj->Restore(docReader,prev_ViewProviderDOM); + + if (pObj && expanded) { + auto vp = static_cast(pObj); + this->signalExpandObject(*vp, TreeItemMode::ExpandItem,0,0); + } + } + for (int i=1; ireadElement("Camera"); - const char* ppReturn = localreader->getAttribute("settings"); - cameraSettings.clear(); - if(ppReturn && ppReturn[0]) { - saveCameraSettings(ppReturn); - try { - const char** pReturnIgnore=nullptr; - std::list mdi = getMDIViews(); - for (const auto & it : mdi) { - if (it->onHasMsg("SetCamera")) - it->onMsg(cameraSettings.c_str(), pReturnIgnore); - } - } - catch (const Base::Exception& e) { - Base::Console().Error("%s\n", e.what()); - } - } + bool expanded = false; + if (!HasExpansion_cstr && expanded_cstr_i) { + if (strcmp(expanded_cstr_i,"1") == 0) { + expanded = true; + } + } + ViewProvider* pObj = getViewProviderByName(name_cstr_i); + if (pObj) + pObj->Restore(docReader,ViewProviderDOM_i);//Im still implementing this, which calls ExtensionContainer::Restore. + + if (pObj && expanded) { + auto vp = static_cast(pObj); + this->signalExpandObject(*vp, TreeItemMode::ExpandItem,0,0); + } + prev_ViewProviderDOM = ViewProviderDOM_i; + } + } + } + } + // read camera settings + auto CameraDOM = docReader.FindElement("Camera"); + if(CameraDOM){ + const char* ppReturn = docReader.GetAttribute(CameraDOM,"settings"); + cameraSettings.clear(); + if(ppReturn && ppReturn[0]) { + saveCameraSettings(ppReturn); + try { + const char** pReturnIgnore=nullptr; + std::list mdi = getMDIViews(); + for (const auto & it : mdi) { + if (it->onHasMsg("SetCamera")) + it->onMsg(cameraSettings.c_str(), pReturnIgnore); + } + } + catch (const Base::Exception& e) { + Base::Console().Error("%s\n", e.what()); + } + } + } - try{ - localreader->readElement("ProjectUnitSystem"); - int US = localreader->getAttributeAsInteger("US"); - int ignore = localreader->getAttributeAsInteger("ignore"); + /* + try{//if this fails then all other reading attemps will fail, since XERCES doesnt allow to get back to previous point, reading the file should restarted each time an element its not present, since it looks for it untill the end of the document. + //Since ProjectUnitSystem its the only optional field, and its being read at the end of this function, it does not cause any issue, but if other optional its added the way of reading XML used in Reader.cpp will be obsolete + + localreader->readElement("ProjectUnitSystem"); + Base::Console().Error("readElement(ProjectUnitSystem)\n"); + int US = localreader->getAttributeAsInteger("US"); + int ignore = localreader->getAttributeAsInteger("ignore"); d->projectUnitSystem = US; d->projectUnitSystemIgnore = ignore; + }catch (const Base::XMLParseException& e) { Base::Console().Warning("ProjectUnitSystem not found: %s\n", e.getMessage().c_str()); - } + }catch(const Base::XMLBaseException& e){ + Base::Console().Warning("ProjectUnitSystem XMLBaseException: %s\n", e.getMessage().c_str()); + }catch(...) { + Base::Console().Error("ProjectUnitSystem catch(...)\n"); + } + */ + } - - localreader->readEndElement("Document"); - - // reset modified flag - reader.initLocalReader(localreader); - setModified(false); + /* + + try{ + localreader->readEndElement("Document"); + }catch(const Base::XMLParseException& e){ + Base::Console().Warning("readEndElement(Document) XMLParseException: %s\n", e.getMessage().c_str()); + }catch(const Base::XMLBaseException& e){ + Base::Console().Warning("readEndElement(Document) XMLBaseException: %s\n", e.getMessage().c_str()); + }catch(...) { + Base::Console().Error("readEndElement(Document) unkown error.\n"); + } + */ + + // reset modified flag + //dont see what this does: + //reader.initLocalReader(localreader); + setModified(false); } void Document::slotStartRestoreDocument(const App::Document& doc) diff --git a/src/Gui/Tree.cpp b/src/Gui/Tree.cpp index 41bdcdebc0..54b7d5b2f6 100644 --- a/src/Gui/Tree.cpp +++ b/src/Gui/Tree.cpp @@ -66,6 +66,10 @@ #include "Widgets.h" #include "Workbench.h" +#include +#include +#include + FC_LOG_LEVEL_INIT("Tree", false, true, true) @@ -348,6 +352,21 @@ public: } reader.readEndElement("Expand", level - 1); } + + void restore(Base::DocumentReader& reader, XERCES_CPP_NAMESPACE_QUALIFIER DOMElement *expandEl) { + Base::Console().Error("ExpandInfo restore\n"); + const char* count_cstr = reader.GetAttribute(expandEl,"count"); + if(count_cstr){ + long count = reader.ContentToInt( count_cstr ); + Base::Console().Error("ExpandInfo count: %d\n",count); + auto _expandEl = reader.FindElement(expandEl,"Expand"); + const char* name_cstr = reader.GetAttribute(_expandEl,"name"); + Base::Console().Error("ExpandInfo name_cstr: %s\n",name_cstr); + auto& entry = (*this)[name_cstr]; + entry.reset(new ExpandInfo); + entry->restore(reader,_expandEl); + } + } }; // --------------------------------------------------------------------------- @@ -3927,6 +3946,24 @@ void DocumentItem::Restore(Base::XMLReader& reader) { } } +void DocumentItem::Restore(Base::DocumentReader& reader) { + Base::Console().Error("DocumentItem::Restore() DocumentReader\n"); + auto expandEl = reader.FindElement("Expand"); + if( !reader.GetAttribute(expandEl,"count") ) + return; + _ExpandInfo.reset(new ExpandInfo); + _ExpandInfo->restore(reader,expandEl); + //TODO NOW + for (auto inst : TreeWidget::Instances) { + if (inst != getTree()) { + auto docItem = inst->getDocumentItem(document()); + if (docItem) + docItem->_ExpandInfo = _ExpandInfo; + + } + } +} + void DocumentItem::restoreItemExpansion(const ExpandInfoPtr& info, DocumentObjectItem* item) { item->setExpanded(true); if (!info) diff --git a/src/Gui/Tree.h b/src/Gui/Tree.h index b41fc0c27a..377b564376 100644 --- a/src/Gui/Tree.h +++ b/src/Gui/Tree.h @@ -297,6 +297,7 @@ public: unsigned int getMemSize () const override; void Save (Base::Writer &) const override; void Restore(Base::XMLReader &) override; + void Restore(Base::DocumentReader& reader) override; class ExpandInfo; using ExpandInfoPtr = std::shared_ptr; diff --git a/src/Gui/ViewProvider.cpp b/src/Gui/ViewProvider.cpp index eb8d64f456..f985cca235 100644 --- a/src/Gui/ViewProvider.cpp +++ b/src/Gui/ViewProvider.cpp @@ -839,6 +839,18 @@ void ViewProvider::Restore(Base::XMLReader& reader) { // setStatus(Gui::isRestoring, false); } +void ViewProvider::Restore(Base::DocumentReader& reader, XERCES_CPP_NAMESPACE_QUALIFIER DOMElement *viewProviderEl) { + // Because some PropertyLists type properties are stored in a separate file, + // and is thus restored outside this function. So we rely on Gui::Document + // to set the isRestoring flags for us. + // + // setStatus(Gui::isRestoring, true); + + TransactionalObject::Restore(reader,viewProviderEl); + + // setStatus(Gui::isRestoring, false); +} + void ViewProvider::updateData(const App::Property* prop) { auto vector = getExtensionsDerivedFromType(); diff --git a/src/Gui/ViewProvider.h b/src/Gui/ViewProvider.h index 1ec43105e8..101375776b 100644 --- a/src/Gui/ViewProvider.h +++ b/src/Gui/ViewProvider.h @@ -498,6 +498,7 @@ public: //restoring the object from document: //this may be of interest to extensions, hence call them void Restore(Base::XMLReader& reader) override; + void Restore(Base::DocumentReader& reader,XERCES_CPP_NAMESPACE_QUALIFIER DOMElement *viewProviderEl); bool isRestoring() {return testStatus(Gui::isRestoring);}