// SerialTree - a simple serializer & deserializer
// Copyright (C) 2000-2003 bozo & sgbeal @users.sourceforge.net
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
// Note that any references to "I" in this file may be reference to either
// rusty or stephan.
#ifndef _FUN_SERIALTREE_H
#define _FUN_SERIALTREE_H
#include
#include
#include
#include
#include
#include
#include
//#include // for QDomNode
class QDomDocument;
class QDomNode;
class QDomElement;
class QTextStream;
namespace fun {
class SerialTree;
/**
* The templated deserialize() is NOT a member function because I couldn't get the compiler to allow
* a static template, declared as:
* static template Deser *deserialize( SerialTree *tree, Deser *ptrToType, const char *classNameInSerialTree );
* (non-static works fine). Won't compile (gcc 2.95.2).
* -- Stephan
* Call it like:
* #include
* GPiece *piece = ::deserialize( mySerialTree, (GPiece *)NULL, "GPiece" );
* If the top-level node of the tree is an object of that type, it will return a pointer
* to the deserialized object. It returns NULL if it can't cast the object, or some other
* error happens.
* The class to be loaded MUST inherit from Serializable for this to work.
*/
template
SerType *deserialize( SerialTree *tree, const SerType *ptrToType, const char *classNameInSerialTree )
{ // deprecated
if ( ! tree ) return NULL;
SerType *gp;
Serializable *sp = tree->getSerializable(classNameInSerialTree);
if ( sp == NULL ) return NULL;
gp = dynamic_cast(sp);
return gp;
}
/**
* Another form of deserialize() which takes an XML string instead of a SerialTree.
* use like this: Foo *foo = ::deserialize( xml_string, (Foo *)NULL, "Foo" );
* The last argument, a string, must be the base class of the node. For libGCom,
* this is always "GCom".
* todo: clsPtr should be const.
*/
template
Type * deserialize( const QString &xml, Type *clsPtr, const QString &className )
{ // deprecated
SerialTree *tree = SerialTree::loadFromXMLString( xml );
if( ! tree ) return NULL;
Type *obj = 0;
//obj = dynamic_cast(tree->getSerializable( className ));
obj = ::deserialize( tree, clsPtr, className );
delete( tree );
return obj;
}
// quasi-experimental (stephan). The global-namespace versions are deprecated.
/**
SerType class to be loaded MUST inherit from Serializable for these templates
to work.
*/
template
SerType *deserialize( SerialTree *tree, const char *classNameInSerialTree );
template
SerType * deserialize( const QString &xml, const QString &className );
// Turn this on to have keys & serializable class names be stored
// internally as ints with a lookup table instead of as QStrings. I did
// this because gprof was showing a lot of time spent in the QString
// destructor, but this didn't make things faster overall. There might be
// some point where this is useful, like for saving to binary or over a
// network connection. (Loading from binary version 2, which uses this,
// *is* faster.)
#define BOZO_CONTEXT 1
// Turn this on to have fundamental types stored natively instead of as
// QStrings. This sort of thing should only make a difference during
// clone(), because when we're deserializing from a text or XML file,
// everything is a string. (Well, the other place it should make a
// difference--and the reason I'm trying it--is in serializing to binary.
// I want to see if that's faster during deserialization.)
//
// Other effects of turning this on:
// - larger child nodes, but that might not result in more memory use,
// depending on what you put into it (because an int or float takes less
// than a QString representation of an int or float)
// - the virtual get() & put() methods which take fundamental types are
// reimplemented (since the base class versions just convert the values
// to QStrings)
//
// DANGER! If you write SerialTrees to binary files while this is on,
// and then turn it off, you won't be able to read those files anymore!
// (Or, put another way, people who have this turned on won't be able to
// share binary files with people who have it turned off.) My fault; I
// should have not checked this in, or handled that properly in the binary
// file support.
#define BOZO_NATIVE 1
#if BOZO_CONTEXT
class STContext; // internal class
#endif
/**
* SerialTree is a simple Serializer and Deserializer which currently can
* be saved to & loaded from XML if your Qt has XML support. It should also
* have saveToTextFile and loadFromTextFile methods, but at the moment,
* it doesn't.
*
* I would probably be using STL if I had a larger brain.
*/
class SerialTree : public Serializer, public Deserializer
{
public:
friend std::ostream & operator << (std::ostream&, SerialTree &);
/**
* Creates an empty SerialTree node.
*/
SerialTree();
virtual ~SerialTree();
/**
* Searches the given node for a child element with the given key,
* and places its contents in rv. If no child with the given
* key is found, the given default value is used.
*/
void get(const QString &key, QString &rv, const QString &defaultVal = QString()) const;
/**
* This is just like the other getSerializable() method, except that it also
* takes a default class name to be used if the child element doesn't
* specify a class. This is just so that files which have dozens of
* objects all of the same class don't need to specify the class in
* every element.
*/
void get(const QString &key, Serializable *&rv, const QString &dfltClassName, Serializable *dflt = NULL) const;
/**
* See Deserializer::get(const QString &, std::vector &,
* const QString &) for copious notes on this topic.
*/
void get(const QString &key, std::vector &list, const QString &dfltClassName = QString()) const;
void get(const QString &key, QStringList &list) const;
/**
* Creates and appends a new child node containing the given string value.
*/
void put(const QString &key, const QString &val);
/**
* This causes the given Serializable to store itself into a new node, which
* is appended as a child to the given node.
*/
void put(const QString &key, const Serializable *val);
#if BOZO_NATIVE
void put(const QString &key, int val);
void put(const QString &key, unsigned val);
void put(const QString &key, bool val);
void put(const QString &key, float val);
void put(const QString &key, double val);
void get(const QString &key, int &rv, int defaultVal = 0) const;
void get(const QString &key, unsigned &rv, unsigned defaultVal = 0) const;
void get(const QString &key, bool &rv, bool defaultVal = false) const;
void get(const QString &key, float &rv, float defaultVal = 0.0) const;
void get(const QString &key, double &rv, double defaultVal = 0.0) const;
#endif // BOZO_NATIVE
#if REIMPLEMENT_INHERITED_TEMPLATES
// Ha ha ha. If you try to make these "inline", gcc 3.2 says
// "declaration does not declare anything". Good! I don't think I
// should have to declare these anyway!
/**
* Just a wrapper around the version inherited from Deserializer. See
* test/why.cpp for a lengthier complaint.
*/
template
void get(const QString &key, SC *&rv, const QString &dfltClassName=QString()) const
{ Deserializer::get(key, rv, dfltClassName); }
/**
* Just a wrapper around the version inherited from Deserializer. See
* test/why.cpp for a lengthier complaint.
*/
template
void get(const QString &key, QPtrList &list, const QString &dfltClassName = QString()) const
{ Deserializer::get(key, list, dfltClassName); }
/**
* Just a wrapper around the version inherited from Deserializer. See
* test/why.cpp for a lengthier complaint.
*/
template
void get(const QString &key, SC **&list, unsigned &len, const QString &dfltClassName = QString()) const
{ Deserializer::get(key, list, len, dfltClassName); }
#endif // REIMPLEMENT_INHERITED_TEMPLATES
/**
* This sets the name of the Serializable subclass name, so that the same
* class can be instantiated from this node later.
*/
void setSerializableClass(const char *name);
/**
* This returns the name of the class which stored itself to the given
* node, if any.
*/
const char *getSerializableClass() const;
/**
* Print debugging information about the structure & contents of a node
* and all of its children to cout.
*/
void dumpTree(QString indent = "") const;
/**
* Opens the given filename and attempts to load its contents into a
* SerialTree. If successful, it returns the new tree, which the caller
* is responsible for deleting. Otherwise, it returns NULL, and you
* must consult steaming goat entrails to ascertain the reason.
*
* (Basically this attempts to determine whether the file contains XML
* or plain text, and then calls loadFromXMLFile() or loadFromTextFile().)
*/
static SerialTree *loadFromFile(const QString &filename);
/**
* Opens the given filename and attempts to load its contents into a
* SerialTree. If successful, it tries to retrieve a Serializable of
* the given class from the given node. If successful, it sets rv to
* the new Serializable, which the caller is responsible for deleting.
* Otherwise, it returns NULL, and you must consult steaming goat
* entrails to ascertain the reason.
*/
template
static void loadFromFile(const QString &filename, SC *&rv, const QString &node = "wrapper");
/**
* Opens the given filename and attempts to load its contents into a
* SerialTree. If successful, it returns the new tree, which the caller
* is responsible for deleting. Otherwise, it returns NULL, and you
* must consult steaming goat entrails to ascertain the reason.
*/
static SerialTree *loadFromXMLFile(const QString &filename);
/**
* Just like loadFromXLMFile() except that it takes a QDomDocument.
*/
static SerialTree *loadFromDomDoc( QDomDocument &xdoc );
/**
* Just like loadFromXLMFile() except that it takes a QString which
* contains XML code.
*/
static SerialTree *loadFromXMLString( const QString &xmlstuff );
/**
* Opens the given filename and attempts to save the tree as XML.
* If you want to append to the file instead of stomping its contents,
* the optional mode argument should be IO_Append (defined in qiodevice.h).
* NOTE that's being ignored right now.
*/
bool saveToXMLFile(const QString &filename, int mode = 0, int gzipLevel = 0);
/**
* Writes XML data to the given ostream. Returns false if the XML data is 0 bytes, else true.
*/
bool saveXMLToStream(std::ostream &os);
/**
* Attempts to read a SerialTree as text from the given input stream.
* If successful, it returns the new tree, which the caller
* is responsible for deleting. Otherwise, it returns NULL, and you
* must consult steaming goat entrails to ascertain the reason.
*/
static SerialTree *loadFromTextStream(QTextStream &is);
/**
* Save the tree as text to the given output stream.
*/
bool saveToTextStream(std::ostream &os);
/**
* Returns an XML string.
*/
QString toString();
bool saveToTextStream( QTextStream &os, int indent=2);
static SerialTree *decode( QDropEvent *event );
/**
* Opens the given filename and attempts to load its contents into a
* SerialTree. If successful, it returns the new tree, which the caller
* is responsible for deleting. Otherwise, it returns NULL, and you
* must consult steaming goat entrails to ascertain the reason.
*/
static SerialTree *loadFromTextFile(const QString &filename);
/**
* Opens the given filename and attempts to save the tree as text.
* If you want to append to the file instead of stomping its contents,
* the optional mode argument should be IO_Append (defined in qiodevice.h).
* NOTE that's being ignored right now.
*/
bool saveToTextFile(const QString &filename, int mode = 0);
/**
* Attempts to read a SerialTree in binary from the given input stream.
* If successful, it returns the new tree, which the caller
* is responsible for deleting. Otherwise, it returns NULL, and you
* must consult steaming goat entrails to ascertain the reason.
*/
static SerialTree *loadFromBinaryStream(QDataStream &is);
/**
* Save the tree as binary to the given output stream.
*
* The version number is 1 for version 1, 2 for version 2, or 0 for the
* default (which depends on whether BOZO_CONTEXT is set in SerialTree.h).
*/
bool saveToBinaryStream(QDataStream &os, int version = 0);
/**
* Opens the given filename and attempts to load its contents into a
* SerialTree. If successful, it returns the new tree, which the caller
* is responsible for deleting. Otherwise, it returns NULL, and you
* must consult steaming goat entrails to ascertain the reason.
*/
static SerialTree *loadFromBinaryFile(const QString &filename);
/**
* Opens the given filename and attempts to save the tree as binary.
* If you want to append to the file instead of stomping its contents,
* the optional mode argument should be IO_Append (defined in qiodevice.h).
* NOTE that's being ignored right now.
*
* The version number is 1 for version 1, 2 for version 2, or 0 for the
* default (which depends on whether BOZO_CONTEXT is set in SerialTree.h).
*/
bool saveToBinaryFile(const QString &filename, int verson = 0, int mode = 0);
/**
* Copies the object by serializing & deserializing it. (Maybe
* not the most efficient way, but it's easy...)
*
* If a Deserialization::Context is given, it will be passed to
* pushContext() before deserialization, and deleted afterwards.
*/
template
static SC *clone(const SC &rv, Deserializer::Context *context = NULL);
private:
Serializable *toSerializable(const QString & dfltClassName) const;
// protected:
// /**
// * Subclasses might want to override this to create children of their own
// * class.
// */
// virtual SerialTree *addChild(const QString &key);
private:
struct Child
{
~Child();
#if BOZO_CONTEXT
// The reason for the hideously ugly code (instead of using the
// same variable name, which would have meant less #ifdefs in the
// source files) is so that the compiler forces me to check every
// place where they're used.
unsigned ikey;
#else
QString skey;
#endif // BOZO_CONTEXT
enum ChildType
{
CT_TREE,
CT_QSTRING
#if BOZO_NATIVE
, CT_INT,
CT_UNSIGNED,
CT_BOOL,
CT_FLOAT,
CT_DOUBLE
#endif // BOZO_NATIVE
} ct;
union VT
{
inline VT(QString *sv) : s(sv) { }
inline VT(SerialTree *tv) : t(tv) { }
#if BOZO_NATIVE
inline VT(int iv) : i(iv) { }
inline VT(unsigned uv) : u(uv) { }
inline VT(bool bv) : b(bv) { }
inline VT(float fv) : f(fv) { }
inline VT(double dv) : d(dv) { }
#endif // BOZO_NATIVE
QString *s;
SerialTree *t;
#if BOZO_NATIVE
int i;
unsigned u;
bool b;
float f;
double d; // nuts, that bloats this up to 8 bytes. You could
// make that pointer-to-double if you don't expect
// to use them much...
#endif // BOZO_NATIVE
} val;
#if BOZO_NATIVE
QString stringVal() const;
#endif // BOZO_NATIVE
#if BOZO_CONTEXT
inline Child(unsigned k, QString *s) : ikey(k), ct(CT_QSTRING), val(s) { }
inline Child(unsigned k, SerialTree *t) : ikey(k), ct(CT_TREE), val(t) { }
#if BOZO_NATIVE
inline Child(unsigned k, int i) : ikey(k), ct(CT_INT), val(i) { }
inline Child(unsigned k, unsigned u) : ikey(k), ct(CT_UNSIGNED), val(u) { }
inline Child(unsigned k, bool b) : ikey(k), ct(CT_BOOL), val(b) { }
inline Child(unsigned k, float f) : ikey(k), ct(CT_FLOAT), val(f) { }
inline Child(unsigned k, double d) : ikey(k), ct(CT_DOUBLE), val(d) { }
#endif // BOZO_NATIVE
#else
inline Child(const QString &k, QString *s) : skey(k), ct(CT_QSTRING), val(s) { }
inline Child(const QString &k, SerialTree *t) : skey(k), ct(CT_TREE), val(t) { }
#if BOZO_NATIVE
inline Child(const QString &k, int i) : skey(k), ct(CT_INT), val(i) { }
inline Child(const QString &k, unsigned u) : skey(k), ct(CT_UNSIGNED), val(u) { }
inline Child(const QString &k, bool b) : skey(k), ct(CT_BOOL), val(b) { }
inline Child(const QString &k, float f) : skey(k), ct(CT_FLOAT), val(f) { }
inline Child(const QString &k, double d) : skey(k), ct(CT_DOUBLE), val(d) { }
#endif // BOZO_NATIVE
#endif // BOZO_CONTEXT
};
/**
NOT TRUE
* Returns the first child with the given key. If no such child exists,
* then getChild(key).isNull() will be false. If you simply want to
* determine whether such a child exists, use hasChild(key).
*/
// const SerialTree &getChild(const QString &key) const;
const Child *getChild(const QString &key) const;
std::vector children;
#if BOZO_CONTEXT
int serialClassID;
#else
char *serialClass;
#endif // BOZO_CONTEXT
#if BOZO_CONTEXT
SerialTree(STContext *);
STContext *context;
#endif // BOZO_CONTEXT
// internal XML support
void toXMLNode(QDomNode &xmlnode) const;
static void childToXMLNode(const Child &child, QDomNode &xmlnode);
#if BOZO_CONTEXT
static Child *childFromXMLNode(STContext *, const QString &key, const QDomElement &xmlnode);
#else
static Child *childFromXMLNode(const QString &key, const QDomElement &xmlnode);
#endif // BOZO_CONTEXT
// internal text support
bool childrenToText(std::ostream &os);
void childrenFromText(QString &buf, int &bufpos, QTextStream &is);
// internal binary support
bool childrenToBinary1(QDataStream &os);
void childrenFromBinary1(QDataStream &is);
#if BOZO_CONTEXT
bool childrenToBinary2(QDataStream &os);
void childrenFromBinary2(QDataStream &is);
void contextToBinary2(QDataStream &os);
void contextFromBinary2(QDataStream &is);
#endif // BOZO_CONTEXT
// // experiment by stephan
// public:
// const QString &name() const { return this->m_name; };
// void setName( const QString &n ) { this->m_name = n; };
// /**
// operator >> added by stephan, and is completely untested.
// */
// friend SerialTree & operator <<( SerialTree &lhs, const Serializable &rhs )
// {
// // QString n = (n = lhs.name()).isEmpty() ? QString("SerialTree") : n;
// // cerr << "SerialTree << " << n.data() << endl;
// // lhs.putSerializable( n.isEmpty() ? QString("SerialTree") : n, rhs );
// return lhs;
// };
// /**
// operator >> added by stephan, and is completely untested.
// */
// const SerialTree & operator >>( Serializable &rhs )
// {
// QString n = (n = this->name()).isEmpty() ? QString("SerialTree") : n;
// cerr << "SerialTree >> " << n.data() << endl;
// // Serializable *sp = this->getSerializable( );
// // if ( sp == NULL ) return NULL;
// // gp = dynamic_cast(sp);
// // this->deserialize( rhs );
// return *this;
// };
// private:
// QString m_name;
};
template
void
SerialTree::loadFromFile(const QString &filename, SC *&rv, const QString &node)
{
rv = NULL;
Deserializer *ds = loadFromFile(filename);
if (!ds) return;
ds->get(node, rv);
delete ds;
}
template
SC *
SerialTree::clone(const SC &src, Deserializer::Context *cp)
{
SC *rv = NULL;
SerialTree st;
Deserializer *dp = &st; // arghh, see tests/why.cpp
st.put("clone", &src);
if (cp) dp->pushContext(cp);
dp->get("clone", rv);
if (cp) dp->popContext();
return rv;
}
} // namespace fun
#if BOZO_CONTEXT
#include
#include
namespace fun {
class STContext
{
public:
STContext();
~STContext();
unsigned getKey(const QString &key);
unsigned getClass(const char *key);
const QString &getKeyString(unsigned id) { return *(keylut[id]); }
const char *getClassString(unsigned id) { return classlut[id]; }
int refcount;
QDict keymap;
QAsciiDict classmap;
std::vector keylut;
std::vector classlut;
};
} // namespace fun
#endif // BOZO_CONTEXT
#endif // _FUN_SERIALTREE_H
| Generated by: stephan on cheyenne on Mon Aug 11 14:06:52 2003, using kdoc 2.0a54. |