Source: fun/Deserializer.h


Annotated List
Files
Globals
Hierarchy
Index
//  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.