Source: fun/Deserializer.h
|
|
|
|
// Deserializer - base class for retrieving Serializables
// 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.
#ifndef _FUN_DESERIALIZER_H
#define _FUN_DESERIALIZER_H
#include
#include
#include
#include
#include
class QColor;
class QPoint;
class QSize;
namespace fun {
class ClassLoader;
// Remove when you can...
#define DESERIALIZER_API_COMPAT 1
// A half-finished, probably broken idea.
#define DESERIALIZER_USE_CONTEXT 1
/**
* Deserializer is an abstract base class for objects which can retrieve
* Serializable objects from files or streams.
*
* See Serializer for additional notes.
*
* As Deserializer is abstract, subclasses must provide implementations for:
* - get(const QString &, QString &, const QString &) const
* - get(const QString &, QStringList &list) const
* - get(const QString &, Serializable *&, const QString &, Serializable *) const
* - get(const QString &, std::vector &, const QString &) const
* - getSerializableClass() const
* Also, getComment() does nothing in the base class.
*
* Note that Deserializer error handling currently sucks vacuously: there
* isn't any. Some of the methods which could be complex operations
* should throw exceptions or something. I know! I'll wait until there's
* a lot of code using this, and then change the API!
*/
class Deserializer
{
public:
Deserializer() { }
virtual ~Deserializer() { }
bool isSet(const QString &key) const;
/**
* Searches 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.
*
* This is one of the methods which subclasses must implement.
*/
virtual void get(const QString &key, QString &rv, const QString &defaultVal = QString()) const = 0;
// When you remove the old API compat, keep this one around for internal
// use? but get rid of defaultVal NO, keep it around as a public method,
// but toss defaultval.
virtual QString getString(const QString &key, QString defaultVal = QString()) const { QString rv; get(key, rv, defaultVal); return rv; }
/**
* This is just like the string version of get(), except that it
* converts the child node's contents to an int. If no child with the
* given key is found, or the child can't be converted to an int, the
* given default value is returned.
*/
virtual void get(const QString &key, int &rv, int defaultVal = 0) const;
/**
* This is just like the string version of get(), except that it
* converts the child node's contents to an unsigned int. If no child
* with the given key is found, or the child can't be converted to an
* unsigned int, the given default value is returned.
*/
virtual void get(const QString &key, unsigned &rv, unsigned defaultVal = 0) const;
/**
* This is just like the string version of get(), except that it
* converts the child node's contents to a bool. If no child with the
* given key is found, or the child can't be converted to a bool, the
* given default value is returned. The static stringIsBool and
* stringToBool methods will tell you whether a string can be converted
* to a bool.
*/
virtual void get(const QString &key, bool &rv, bool defaultVal = false) const;
/**
* This is just like the string version of get(), except that it
* converts the child node's contents to a float. If no child with the
* given key is found, or the child can't be converted to a float, the
* given default value is returned.
*/
virtual void get(const QString &key, float &rv, float defaultVal = 0.0) const;
/**
* This is just like the string version of get(), except that it
* converts the child node's contents to a double. If no child with the
* given key is found, or the child can't be converted to a double, the
* given default value is returned.
*/
virtual void get(const QString &key, double &rv, double defaultVal = 0.0) const;
/**
* This is just like the string version of get(), except that it
* converts the child node's contents to a QColor. If no child with the
* given key is found, or the child can't be converted to a QColor, the
* given default value is returned.
*/
virtual void get(const QString &key, QColor &rv, const QColor &defaultVal = QColor()) const;
/**
* This is just like the string version of get(), except that it
* converts the child node's contents to a QPoint. If no child with the
* given key is found, or the child can't be converted to a QPoint, the
* given default value is returned.
* UNTESTED!
*/
virtual void get(const QString &key, QPoint &rv, const QPoint &dflt ) const;
/**
* This is just like the string version of get(), except that it
* converts the child node's contents to a QSize. If no child with the
* given key is found, or the child can't be converted to a QSize, the
* given default value is returned.
* UNTESTED!
*/
virtual void get(const QString &key, QSize &rv, const QSize &dflt ) const;
/**
* Searches the given node for a child element with the given key,
* and attempts to instantiate a Serializable object (loading the class
* from a ClassLoader if necessary), and pass the child
* node to the Serializable's deserialize() method. If no such child is
* found, the given default value is returned. If a child is found but
* can't be instantiated or loaded, NULL is returned. The caller is
* responsible for deleting the Serializable if a new one is created.
* (You will be able to tell if rv != defaultVal.)
*
* For example, suppose you had a simple XML file:
*
*
* 2
* 17
* Mean/hungry
*
*
* Calling get("bunny", bunny) would cause a new instance of DevilBunny
* to be created (and the class to be loaded from a shared object, if
* necessary), and the new instance would be loaded from the bunny XML
* node. See Serializable for more information, but presumably
* DevilBunny::deserialize() would look something like this:
*
* void
* DevilBunnyBox::deserialize(const Deserializer &node)
* {
* Serializable::deserialize(node);
* node.get("xpos", xpos);
* node.get("ypos", ypos);
* node.get("disposition", disp);
* // maybe some error checking, or loading other stuff
* }
*/
virtual void get(const QString &key, Serializable *&rv, Serializable *defaultVal = NULL) const;
/**
* This is just like the other get(..., Serializable *&, ...) 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.
*
* This is one of the methods which subclasses must implement.
*/
virtual void get(const QString &key, Serializable *&rv, const QString &dfltClassName, Serializable *dflt = NULL) const = 0;
/**
* This is a template version which takes a pointer to a pointer to a
* Serializable subclass. The given pointer will be changed to point
* to the new object which was deserialized, or NULL if no object of
* the given class was deserialized. (This does the dynamic_cast for
* you, and cleans things up if the new object isn't the expected type.)
*/
template
void get(const QString &key, SC *&rv, const QString &dfltClassName=QString()) const;
// template
// Serializable *getSerializable(const QString &key, SC **scp, const QString &dfltClassName=QString()) const;
/**
* For each of the given node's child elements with the given key, this
* attempts to instantiate a Serializable object of the appropriate class,
* load the object from the child node, and add the object to the given
* QPtrList. The caller is responsible for freeing any Serializable objects
* returned. The given default class name will be used for instantiating
* Serializable objects for child nodes who do not specify a class of their
* own.
*
* For example, suppose you were loading an object which contained
* multiple chefs:
*
*
* Pierre
* 2
* 5
*
*
* Ludwig
* 2
* 4
*
*
* Julia
* 3
* 7
*
*
* Calling get("chef", list, "SousChef") would cause three
* new instances of SousChef to be created (and the class to be loaded
* from a shared object, if necessary), and the new instances would be
* loaded from their own child nodes, and would be added to the given
* list.
*
* In the base class, this is a wrapper around the std::vector version.
*/
virtual void get(const QString &key, QPtrList &list, const QString &dfltClassName = QString()) const;
/**
* Clearly this is wrong & should be removed, but I don't know how to do
* it properly with STL. It should be a template method which takes a
* back_insert_iterator, but this abstract base class doesn't know how
* the nonabstract subclass is going to implement its list of children.
* So then this should have a virtual protected "foreach" member which
* takes some function pointer & calls that once for each matching child
* (and then the back_insert_iterator would get passed to that), but
* that again would have to be a template member (I think), which means
* it couldn't be virtual, which means it couldn't be abstract.
*
* This is one of the methods which subclasses must implement.
*/
virtual void get(const QString &key, std::vector &list, const QString &dfltClassName = QString()) const = 0;
/**
* Same as the version which takes a QPtrList of Serializables.
* The only reason this exists is that even if your class Foo is a
* subclass of Serializable, QPtrList isn't a subclass of
* QPtrList, and can't be passed to the get() which
* takes a QPtrList. So this calls that version,
* and then dynamic_cast's its elements to the template class, and
* sticks them in the given list if that succeeds.
*
* One API lameness here is that you might want to use some
* collection other than a QPtrList, but we can't take a
* QPtrCollection because it doesn't have any way of getting at the
* elements, and we can't take an iterator because the various
* QPtrCollection subclasses' iterators don't share a base class.
* Something to fix if/when we switch to STL...
*/
template
void get(const QString &key, QPtrList &list, const QString &dfltClassName = QString()) const;
/**
* Same as the version which takes a std::vector of Serializables.
* The only reason this exists is that even if your class Foo is a
* subclass of Serializable, std::vector isn't a subclass of
* std::vector, and can't be passed to the get() which
* takes a std::vector. So this calls that version,
* and then dynamic_cast's its elements to the template class, and
* sticks them in the given list if that succeeds.
*
* Like the method this calls, this is wrong & should be removed.
*/
template
void get(const QString &key, std::vector &list, const QString &dfltClassName = QString()) const;
/**
* template version which takes an array, list & len will be changed,
* & left alone if no objects can be retrieved. if any can, the contents
* of list will be deleted, if any.
*/
template
void get(const QString &key, SC **&list, unsigned &len, const QString &dfltClassName = QString()) const;
/**
* Gets a list of QStrings with the given key.
*
* This is one of the methods which subclasses must implement.
*/
virtual void get(const QString &key, QStringList &list) const = 0;
/**
* replaces an array of qstrings, deletes the array if not null
*/
virtual void get(const QString &key, QString *&list, unsigned &len) const;
/**
* This returns the name of the class which stored itself to the given
* node, if any.
*
* This is one of the methods which subclasses must implement.
*/
virtual const char *getSerializableClass() const = 0;
/**
* When getSerializable() is called, a ClassLoader is used to
* instantiate an object of the correct class. setDefaultClassLoader()
* sets the default ClassLoader which will be used for this. You can
* pass NULL if you want Deserializer to return to using a default
* ClassLoader.
*/
static void setDefaultClassLoader(ClassLoader *);
/**
*/
static ClassLoader &getDefaultClassLoader();
/**
* This is just a utility for converting QStrings to bools.
*/
static bool stringToBool(const QString &str, bool *ok = NULL);
/**
* This is just a utility for figuring out whether a string can be
* converted to a bool value. It returns true for "true", "false,"
* "y", "yes", etc., and integers.
*/
static bool stringIsBool(const QString &str);
/**
* Gets the comment associated with an element, if any. The default
* base class behavior is to do nothing.
*/
virtual QString getComment(const QString &key) const { return QString(); }
#if DESERIALIZER_USE_CONTEXT
/**
* Optional class for sharing context during the deserialization of
* complex trees. For example, suppose you have some child object which
* keeps a pointer to some other child object. At serialization time,
* you only want to save some string or int ID for the referenced object;
* at deserialization time, you need the "owner" of the referenced object
* in order to translate the ID into the actual referenced object. You
* could create your own Deserializer::Context subclass which contains a
* pointer or reference to the "owner" object, then pass that to
* pushContext() before deserializing the child objects. Inside the
* child object deserialization, getContext(), dynamic_cast it to your
* Context subclass, and (if successful) get your parent object from the
* Context. Back in the parent object, after deserializing all children,
* call popContext() to delete the Context object.
*
* This base class is useless; it only exists so that getContext() can
* return a polymorphic object which you can dynamic_cast to your useful
* subclass.
*
* (This whole approach could be stupid; I'm not sure yet.)
*/
class Context
{
public:
Context(ClassLoader *cp = NULL) : cl(cp) { }
virtual ~Context() { } // we just need it to be polymorphic (I think
// that's a requirement for dynamic_cast...)
virtual ClassLoader *getDefaultLoader() { return cl; }
/**
* You still own the ClassLoader.
*/
virtual void setDefaultLoader(ClassLoader *c) { cl = c; }
private:
ClassLoader *cl;
};
/**
* Pushes the given Context object on the stack. (NULL is OK.) It will
* be deleted when you call popContext().
*
* The default implementation is not reentrant; if you expect to perform
* deserializations simultaneously on multiple threads, you will need to
* reimplement this in your Deserializer subclass. (When you do that,
* you will notice that it's a const method... hey, goof-ups like this
* are the reason for "mutable", right? The alternatives are to make
* the context stack global & not thread-safe--the base class does
* that--or change Serializable::deserialize() to not take a const
* Deserializer, but I don't want to do that. Or some fourth approach
* I'm not thinking of...)
also, if this thing's getDefaultLoader() returns a ClassLoader, that one
will be used for all instantiations.
*/
virtual void pushContext(Context *value) const;
/**
* Gets the current Context, or NULL.
*/
virtual Context *getContext() const;
/**
* Pops the current Context off the stack and deletes it.
*/
virtual void popContext() const;
#endif // DESERIALIZER_USE_CONTEXT
#if DESERIALIZER_API_COMPAT
virtual int getInt(const QString &key, int defaultVal = 0) const { int rv; get(key, rv, defaultVal); return rv; }
virtual unsigned getUInt(const QString &key, unsigned defaultVal = 0) const { unsigned rv; get(key, rv, defaultVal); return rv; }
virtual bool getBool(const QString &key, bool defaultVal = false) const { bool rv; get(key, rv, defaultVal); return rv; }
virtual float getFloat(const QString &key, float defaultVal = 0.0) const { float rv; get(key, rv, defaultVal); return rv; }
virtual double getDouble(const QString &key, double defaultVal = 0.0) const { double rv; get(key, rv, defaultVal); return rv; }
virtual QColor getColor(const QString &key, QColor defaultVal = QColor()) const { QColor rv; get(key, rv, defaultVal); return rv; }
virtual QPoint getPoint( const QString &key, const QPoint &dflt ) const { QPoint rv; get(key, rv, dflt); return rv; }
virtual QSize getSize( const QString &key, const QSize &dflt ) const { QSize rv; get(key, rv, dflt); return rv; }
virtual Serializable *getSerializable(const QString &key, Serializable *defaultVal = NULL) const { Serializable *rv; get(key, rv, defaultVal); return rv; }
virtual Serializable *getSerializable(const QString &key, const QString &dfltClassName, Serializable *dflt = NULL) const { Serializable *rv; get(key, rv, dfltClassName, dflt); return rv; }
virtual int getSerializables(const QString &key, QPtrList &list, const QString &dfltClassName = 0) const { int before = list.count(); get(key, list, dfltClassName); return list.count() - before; }
template
int getSerializables(const QString &key, QPtrList &list, const QString &dfltClassName = QString()) const { int before = list.count(); get(key, list, dfltClassName); return list.count() - before; }
virtual int getStrings(const QString &key, QStringList &list) const;// { int before = list.count(); get(key, list); return list.count() - before; }
#endif
};
template
void
Deserializer::get(const QString &key, SC *&rv, const QString &dfltClassName) const
{
rv = NULL;
Serializable *ts = getSerializable(key, dfltClassName);
if (!ts) return;
SC *tscp = dynamic_cast(ts);
if (!tscp)
{
NOT_DONE("need to warn that dynamic cast failed");
delete ts;
return;
}
rv = tscp;
}
template
void
Deserializer::get(const QString &key, QPtrList &list, const QString &dfltClassName) const
{
QPtrList tl;
get(key, tl, dfltClassName);
QPtrListIterator it(tl);
Serializable *ser;
while ((ser = it.current()) != NULL)
{
++it;
SC *ts = dynamic_cast(ser);
if (ts) list.append(ts);
else
{
NOT_DONE("need to warn that dynamic cast failed");
delete ser;
}
}
}
template
void
Deserializer::get(const QString &key, std::vector &list, const QString &dfltClassName) const
{
//XXX this is stupid!!
std::vector tl;
get(key, tl, dfltClassName);
for (int i = 0; i < tl.size(); ++i)
{
SC *ts = dynamic_cast(tl[i]);
if (ts) list.push_back(ts);
else if (tl[i])
{
NOT_DONE("need to warn that dynamic cast failed");
delete tl[i];
}
}
}
template
void
Deserializer::get(const QString &key, SC **&list, unsigned &len, const QString &dfltClassName) const
{
QPtrList tl;
get(key, tl, dfltClassName);
if (!tl.count()) return;
unsigned tlen = len;
if (list)
{
for (unsigned i = 0; i < tlen; ++i)
{
if (list[i]) delete list[i];
}
delete [] list;
}
list = new (SC*)[tl.count()];
if (!list)
{
NOT_DONE("urkk, out of memory");
len = 0;
return;
}
QPtrListIterator it(tl);
Serializable *ser;
tlen = 0;
while ((ser = it.current()) != NULL)
{
++it;
SC *ts = dynamic_cast(ser);
if (ts) list[tlen++] = ts;
else
{
NOT_DONE("need to warn that dynamic cast failed...");
//also, we're wasting an array element here.
delete ser;
}
}
len = tlen;
}
} // namespace fun
#endif // _FUN_DESERIALIZER_H
Generated by: stephan on cheyenne on Mon Aug 11 14:06:52 2003, using kdoc 2.0a54. |