429 lines
15 KiB
C++
429 lines
15 KiB
C++
/***************************************************************************
|
|
* Copyright (c) 2011 Jürgen Riegel <juergen.riegel@web.de> *
|
|
* *
|
|
* 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 SRC_BASE_READER_H_
|
|
#define SRC_BASE_READER_H_
|
|
|
|
#include <bitset>
|
|
#include <map>
|
|
#include <memory>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include <xercesc/framework/XMLPScanToken.hpp>
|
|
#include <xercesc/sax2/DefaultHandler.hpp>
|
|
|
|
#include <boost/iostreams/categories.hpp>
|
|
|
|
#include "FileInfo.h"
|
|
|
|
|
|
namespace zipios
|
|
{
|
|
class ZipInputStream;
|
|
}
|
|
#ifndef XERCES_CPP_NAMESPACE_BEGIN
|
|
#define XERCES_CPP_NAMESPACE_QUALIFIER
|
|
namespace XERCES_CPP_NAMESPACE
|
|
{
|
|
class Attributes;
|
|
class DefaultHandler;
|
|
class SAX2XMLReader;
|
|
} // namespace XERCES_CPP_NAMESPACE
|
|
#else
|
|
XERCES_CPP_NAMESPACE_BEGIN
|
|
class Attributes;
|
|
class DefaultHandler;
|
|
class SAX2XMLReader;
|
|
XERCES_CPP_NAMESPACE_END
|
|
#endif
|
|
|
|
namespace Base
|
|
{
|
|
class Persistence;
|
|
|
|
/** The XML reader class
|
|
* This is an important helper class for the store and retrieval system
|
|
* of objects in FreeCAD. These classes mainly inherit the App::Persitance
|
|
* base class and implement the Restore() method.
|
|
* \par
|
|
* The reader gets mainly initialized by the App::Document on retrieving a
|
|
* document out of a file. From there subsequently the Restore() method will
|
|
* by called on all object stored.
|
|
* \par
|
|
* A simple example is the Restore of App::PropertyString:
|
|
* \code
|
|
void PropertyString::Save (short indent,std::ostream &str)
|
|
{
|
|
str << "<String value=\"" << _cValue.c_str() <<"\"/>" ;
|
|
}
|
|
|
|
void PropertyString::Restore(Base::Reader &reader)
|
|
{
|
|
// read my Element
|
|
reader.readElement("String");
|
|
// get the value of my Attribute
|
|
_cValue = reader.getAttribute<const char*>("value");
|
|
}
|
|
|
|
* \endcode
|
|
* \par
|
|
* An more complicated example is the retrieval of the App::PropertyContainer:
|
|
* \code
|
|
void PropertyContainer::Save (short indent,std::ostream &str)
|
|
{
|
|
std::map<std::string,Property*> Map;
|
|
getPropertyMap(Map);
|
|
|
|
str << ind(indent) << "<Properties Count=\"" << Map.size() << "\">" << endl;
|
|
std::map<std::string,Property*>::iterator it;
|
|
for(it = Map.begin(); it != Map.end(); ++it)
|
|
{
|
|
str << ind(indent+1) << "<Property name=\"" << it->first << "\" type=\"" <<
|
|
it->second->getTypeId().getName() << "\">" ; it->second->Save(indent+2,str); str << "</Property>" <<
|
|
endl;
|
|
}
|
|
str << ind(indent) << "</Properties>" << endl;
|
|
}
|
|
|
|
void PropertyContainer::Restore(Base::Reader &reader)
|
|
{
|
|
reader.readElement("Properties");
|
|
int Cnt = reader.getAttribute<long>("Count");
|
|
|
|
for(int i=0 ;i<Cnt ;i++)
|
|
{
|
|
reader.readElement("Property");
|
|
string PropName = reader.getAttribute<const char*>("name");
|
|
Property* prop = getPropertyByName(PropName.c_str());
|
|
if(prop)
|
|
prop->Restore(reader);
|
|
|
|
reader.readEndElement("Property");
|
|
}
|
|
reader.readEndElement("Properties");
|
|
}
|
|
* \endcode
|
|
* \see Base::Persistence
|
|
* \author Juergen Riegel
|
|
*/
|
|
class BaseExport XMLReader: public XERCES_CPP_NAMESPACE_QUALIFIER DefaultHandler
|
|
{
|
|
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
|
|
};
|
|
/// open the file and read the first element
|
|
XMLReader(const char* FileName, std::istream&);
|
|
~XMLReader() override;
|
|
|
|
/** @name boost iostream device interface */
|
|
//@{
|
|
using category = boost::iostreams::source_tag;
|
|
using char_type = char;
|
|
std::streamsize read(char_type* s, std::streamsize n);
|
|
//@}
|
|
|
|
bool isValid() const
|
|
{
|
|
return _valid;
|
|
}
|
|
bool isVerbose() const
|
|
{
|
|
return _verbose;
|
|
}
|
|
void setVerbose(bool on)
|
|
{
|
|
_verbose = on;
|
|
}
|
|
|
|
/** @name Parser handling */
|
|
//@{
|
|
/// get the local name of the current Element
|
|
const char* localName() const;
|
|
/// get the current element level
|
|
int level() const;
|
|
|
|
/// return true if the end of an element is reached, false otherwise
|
|
bool isEndOfElement() const;
|
|
|
|
/// return true if the on the start of the document, false otherwise
|
|
bool isStartOfDocument() const;
|
|
|
|
/// return true if the end of the document is reached, false otherwise
|
|
bool isEndOfDocument() const;
|
|
|
|
/// read until a start element is found (\<name\>) or start-end element (\<name/\>) (with
|
|
/// special name if given)
|
|
void readElement(const char* ElementName = nullptr);
|
|
|
|
/// Read in the next element. Return true if it succeeded and false otherwise
|
|
bool readNextElement();
|
|
|
|
/** read until an end element is found
|
|
*
|
|
* @param ElementName: optional end element name to look for. If given, then
|
|
* the parser will read until this name is found.
|
|
*
|
|
* @param level: optional level to look for. If given, then the parser will
|
|
* read until this level. Note that the parse only increase the level when
|
|
* finding a start element, not start-end element, and decrease the level
|
|
* after finding an end element. So, if you obtain the parser level after
|
|
* calling readElement(), you should specify a level minus one when calling
|
|
* this function. This \c level parameter is only useful if you know the
|
|
* child element may have the same name as its parent, otherwise, using \c
|
|
* ElementName is enough.
|
|
*/
|
|
void readEndElement(const char* ElementName = nullptr, int level = -1);
|
|
/// read until characters are found
|
|
void readCharacters(const char* filename, CharStreamFormat format = CharStreamFormat::Raw);
|
|
|
|
/** Obtain an input stream for reading characters
|
|
*
|
|
* @return Return a input stream for reading characters. The stream will be
|
|
* auto destroyed when you call with readElement() or readEndElement(), or
|
|
* you can end it explicitly with endCharStream().
|
|
*/
|
|
std::istream& beginCharStream(CharStreamFormat format = CharStreamFormat::Raw);
|
|
/// Manually end the current character stream
|
|
void endCharStream();
|
|
/// Obtain the current character stream
|
|
std::istream& charStream();
|
|
//@}
|
|
|
|
/// read binary file
|
|
void readBinFile(const char*);
|
|
//@}
|
|
|
|
/** @name Attribute handling */
|
|
//@{
|
|
/// get the number of attributes of the current element
|
|
unsigned int getAttributeCount() const;
|
|
/// check if the read element has a special attribute
|
|
bool hasAttribute(const char* AttrName) const;
|
|
|
|
private:
|
|
// all explicit template instantiations - this is for getting
|
|
// a compile error, rather than linker error.
|
|
template<typename T>
|
|
static constexpr bool instantiated =
|
|
std::is_same_v<T, bool> || std::is_same_v<T, const char*> || std::is_same_v<T, double>
|
|
|| std::is_same_v<T, int> || std::is_same_v<T, long> || std::is_same_v<T, unsigned long>;
|
|
|
|
public:
|
|
/// return the named attribute as T (does type checking); if missing return defaultValue.
|
|
/// If defaultValue is not set, it will default to the default initialization of the
|
|
/// corresponding type; bool: false, int: 0, ... as if one had used defaultValue=bool{}
|
|
/// or defaultValue=int{}
|
|
// General template, mark delete as it's not implemented, and should not be used!
|
|
template<typename T>
|
|
requires Base::XMLReader::instantiated<T>
|
|
T getAttribute(const char* AttrName, T defaultValue) const;
|
|
|
|
/// No default? Will throw exception if not found!
|
|
template<typename T>
|
|
requires Base::XMLReader::instantiated<T>
|
|
T getAttribute(const char* AttrName) const;
|
|
|
|
/// E.g. std::string, QString
|
|
template<typename T>
|
|
T getAttribute(const char* AttrName) const
|
|
{
|
|
return T(getAttribute<const char*>(AttrName));
|
|
}
|
|
/// E.g. std::string, QString
|
|
template<typename T>
|
|
T getAttribute(const char* AttrName, T defaultValue) const
|
|
{
|
|
return T(getAttribute<const char*>(AttrName, defaultValue));
|
|
}
|
|
|
|
/// Enum classes
|
|
template<typename T>
|
|
requires std::is_enum_v<T>
|
|
T getAttribute(const char* AttrName, T defaultValue) const
|
|
{
|
|
return static_cast<T>(
|
|
getAttribute<unsigned long>(AttrName, static_cast<unsigned long>(defaultValue)));
|
|
}
|
|
/// Enum classes
|
|
template<typename T>
|
|
requires std::is_enum_v<T>
|
|
T getAttribute(const char* AttrName) const
|
|
{
|
|
return static_cast<T>(getAttribute<unsigned long>(AttrName));
|
|
}
|
|
|
|
/** @name additional file reading */
|
|
//@{
|
|
/// add a read request of a persistent object
|
|
const char* addFile(const char* Name, Base::Persistence* Object);
|
|
/// process the requested file writes
|
|
void readFiles(zipios::ZipInputStream& zipstream) const;
|
|
/// Returns whether reader has any registered filenames
|
|
bool hasFilenames() const;
|
|
/// returns true if reading the file \a filename has failed
|
|
bool hasReadFailed(const std::string& filename) const;
|
|
bool isRegistered(Base::Persistence* Object) const;
|
|
virtual void addName(const char*, const char*);
|
|
virtual const char* getName(const char*) const;
|
|
virtual bool doNameMapping() const;
|
|
//@}
|
|
|
|
/// Schema Version of the document
|
|
int DocumentSchema {0};
|
|
/// Version of FreeCAD that wrote this document
|
|
std::string ProgramVersion;
|
|
/// Version of the file format
|
|
int FileVersion {0};
|
|
|
|
/// sets simultaneously the global and local PartialRestore bits
|
|
void setPartialRestore(bool on);
|
|
|
|
void clearPartialRestoreDocumentObject();
|
|
void clearPartialRestoreProperty();
|
|
void clearPartialRestoreObject();
|
|
|
|
/// return the status bits
|
|
bool testStatus(ReaderStatus pos) const;
|
|
/// set the status bits
|
|
void setStatus(ReaderStatus pos, bool on);
|
|
|
|
protected:
|
|
/// read the next element
|
|
bool read();
|
|
|
|
// -----------------------------------------------------------------------
|
|
// Handlers for the SAX ContentHandler interface
|
|
// -----------------------------------------------------------------------
|
|
/** @name Content handler */
|
|
//@{
|
|
void startDocument() override;
|
|
void endDocument() override;
|
|
void startElement(const XMLCh* const uri,
|
|
const XMLCh* const localname,
|
|
const XMLCh* const qname,
|
|
const XERCES_CPP_NAMESPACE_QUALIFIER Attributes& attrs) override;
|
|
void endElement(const XMLCh* const uri,
|
|
const XMLCh* const localname,
|
|
const XMLCh* const qname) override;
|
|
void characters(const XMLCh* const chars, const XMLSize_t length) override;
|
|
void ignorableWhitespace(const XMLCh* const chars, const XMLSize_t length) override;
|
|
//@}
|
|
|
|
/** @name Lexical handler */
|
|
//@{
|
|
void startCDATA() override;
|
|
void endCDATA() override;
|
|
//@}
|
|
|
|
/** @name Document handler */
|
|
//@{
|
|
void resetDocument() override;
|
|
//@}
|
|
|
|
|
|
// -----------------------------------------------------------------------
|
|
// Handlers for the SAX ErrorHandler interface
|
|
// -----------------------------------------------------------------------
|
|
/** @name Error handler */
|
|
//@{
|
|
void warning(const XERCES_CPP_NAMESPACE_QUALIFIER SAXParseException& exc) override;
|
|
void error(const XERCES_CPP_NAMESPACE_QUALIFIER SAXParseException& exc) override;
|
|
void fatalError(const XERCES_CPP_NAMESPACE_QUALIFIER SAXParseException& exc) override;
|
|
void resetErrors() override;
|
|
//@}
|
|
|
|
private:
|
|
int Level {0};
|
|
std::string LocalName;
|
|
std::string Characters;
|
|
unsigned int CharacterCount {0};
|
|
std::streamsize CharacterOffset {-1};
|
|
|
|
std::map<std::string, std::string> AttrMap;
|
|
using AttrMapType = std::map<std::string, std::string>;
|
|
|
|
enum
|
|
{
|
|
None = 0,
|
|
Chars,
|
|
StartDocument,
|
|
EndDocument,
|
|
StartElement,
|
|
StartEndElement,
|
|
EndElement,
|
|
StartCDATA,
|
|
EndCDATA
|
|
} ReadType {None};
|
|
|
|
|
|
FileInfo _File;
|
|
XERCES_CPP_NAMESPACE_QUALIFIER SAX2XMLReader* parser;
|
|
XERCES_CPP_NAMESPACE_QUALIFIER XMLPScanToken token;
|
|
bool _valid {false};
|
|
bool _verbose {true};
|
|
|
|
public:
|
|
struct FileEntry
|
|
{
|
|
std::string FileName;
|
|
Base::Persistence* Object;
|
|
};
|
|
std::vector<FileEntry> FileList;
|
|
|
|
private:
|
|
mutable std::vector<std::string> FailedFiles;
|
|
|
|
std::bitset<32> StatusBits;
|
|
|
|
std::unique_ptr<std::istream> CharStream;
|
|
};
|
|
|
|
class BaseExport Reader: public std::istream
|
|
{
|
|
public:
|
|
Reader(std::istream&, const std::string&, int version);
|
|
std::istream& getStream();
|
|
std::string getFileName() const;
|
|
int getFileVersion() const;
|
|
void initLocalReader(std::shared_ptr<Base::XMLReader>);
|
|
std::shared_ptr<Base::XMLReader> getLocalReader() const;
|
|
|
|
private:
|
|
std::istream& _str;
|
|
std::string _name;
|
|
int fileVersion;
|
|
std::shared_ptr<Base::XMLReader> localreader;
|
|
};
|
|
|
|
} // namespace Base
|
|
|
|
|
|
#endif // SRC_BASE_READER_H_
|