/*********************************************************************
 * This file is part of the cpplibs suite.
 *
 * Copyright (C) 2001 Topi Mäenpää
 * All rights reserved.
 *
 * This program is free software. You can redistribute and/or modify
 * it under the terms of the free software licence found in the
 * accompanying file "COPYING". The licence terms must always be
 * redistributed with this source file. The above copyright notice
 * must be reproduced in all modified and unmodified copies of this
 * source file.
 *
 * $Revision: 1.7 $
 *********************************************************************/

#ifndef _SERIALIZATION_H
#define _SERIALIZATION_H

#include "io/IO.h"
#include "Hashtable.h"

namespace util
{
	/**
	 * An interface for dynamic type object serialization. In dynamic
	 * type serialization, an object can be stored and restored via a
	 * pointer to a base class. That is, if one has class B that is
	 * inherited from class A, dynamic type serialization allows one to
	 * write the contents of B via a pointer to A. In order to be
	 * dynamically serializable, a class or a parent class of a class
	 * must implement this interface.
	 **/
	class Serializable
	{
	protected:
		/**
		 * A virtual destructor is needed to make sure all subclasses have
		 * a vtable. Otherwise the rtti system does not work correctly.
		 **/
		virtual ~Serializable() {}
	};

	/**
	 * Serializer is an interface for classes that are able to write and
	 * read Serializable objects. A Serializable class must be
	 * accompanied with a Serializer in order to be dynamically
	 * serializable. A convenient way of creating the serializer is an
	 * inner class:<br>
	 *
	 * <pre>
	 * class MyClass : public Serializable
	 * {
	 * public:
	 *   class Serializer;
	 *
	 *   ...
	 * };
	 *
	 * class MyClass::Serializer : public util::Serializer
	 * {
	 * public:
	 *   void writeToStream(ostream& out, const Serializable& obj) throw (IOException&);
	 *   Serializable* readFromStream(istream& in) throw (IOException&);
	 * };
	 * </pre>
	 *
	 * Now you'll be able to use MyClass::Serializer as a serializer for
	 * MyClass.
	 **/
	class Serializer
	{
	public:
		/**
		 * Write an object to a stream. This method shoud write the
		 * contents of an object to the given stream by using a stream
		 * operator, for example. Note that the data output to stream must
		 * always be started with a fully-qualified class name
		 * (namespace::class). One may want to use Util::getClassName to
		 * make the class name correctly formatted.
		 *
		 * @param out the output stream to write the object to
		 * @param obj the object to be written
		 * @see Util::parseName(const char*)
		 **/
		virtual void writeToStream(std::ostream& out, const Serializable& obj) throw (io::IOException&) = 0;

		/**
		 * Read an object from a stream. This method should return a
		 * pointer to a newly allocated object whose contents were read
		 * from the given stream by using a stream operator, for example.
		 *
		 * @param in the stream to read an object from
		   @return a pointer to a newly allocated object
		 **/
		virtual Serializable* readFromStream(std::istream& in) throw (io::IOException&) = 0;
	};


	/**
	 * A class for collecting all dynamic type serializers together.
	 * Whenever a dynamic type (i.e. a data pointed to by a base class
	 * pointer) is to be deserialized, this class does the task. All
	 * classes that can be read are added to the associative array prior
	 * to calling the readObject method.<p>
	 *
	 * To make a class "dynamic type" serializable, you must do the
	 * following:
	 * <ul>
	 * <li>Inherit the class or one of its parents from Serializable.
	 * <li>Write a Serializer for the class. Inner class is an elegant
	 * solution.
	 * <li>Associate the class name with its Serializer, i.e. use
	 * Serialization::put(className,serializer).
	 * </ul>
	 *
	 * Here's how it goes in real life:<br>
	 * <pre>
	 * namespace mynamespace
	 * {
	 *   class MyBase : public Serializable { ... };
	 *   class MyDerived : public MyBase { ... };
	 *
	 *   //These could also be inner classes
	 *   class MyBaseSerializer : public Serializer { ... };
	 *   class MyDerivedSerializer : public Serializer { ... };
	 *
	 *   ...
	 *
	 *   Serialization s;
	 *   MyBaseSerializer baseSerializer;
	 *   MyDerivedSerializer derivedSerializer;
	 *   s.put("mynamespace::MyBase",&baseSerializer);
	 *   s.put("mynamespace::MyDerived",&derivedSerializer);
	 *
	 *   //Now, cin must start with either mynamespace::MyBase or
	 *   //mynamespace::MyDerived. The actual type can be unknown
	 *   //to us.
	 *
	 *   MyBase* m = (MyBase*)s.readObject(cin);
	 *
	 *   MyDerived d;
	 *   MyBase* m2 = &d;
	 *
	 *   //Now, the contents of d are written to cout.
	 *   //The Serializer must write the fully-qualified class name
	 *   //of the object (mynamespace::MyDerived) first.
	 *   s.writeObject(cout,*m2);
	 *
	 *   ...
	 *
	 * }
	 * </pre>
	 **/
	class Serialization : public Hashtable<std::string,Serializer*>
	{
	public:
		/**
		 * The default constructor. Turns template parameter stripping on.
		 **/
		Serialization() : _bStripTemplateParams(true) {}
		
		/**
		 * Read an object from a stream. Serializer first reads a class
		 * name, then consults the internal associative array for a
		 * Serializer that is able to read the class and gives it the
		 * turn. The Serializer reads the object from a stream, and a
		 * newly allocated object is returned to the caller.
		 *
		 * @exception IOException& if an error occurs while reading
		 **/
		Serializable* readObject(std::istream& in) throw (io::IOException&);

		/**
		 * Write an object to a stream. The referenced object may be any
		 * type inherited from Serializable. Its class name is fetched
		 * on-line, and a corresponding Serializer is searched. If a
		 * Serializer for the class is found, it will be responsible for
		 * creating a new object and reading it from a stream.
		 *
		 * @exception IOException& if an error occurs while writing
		 **/
		void writeObject(std::ostream& out, const Serializable& obj) throw (io::IOException&);

		/**
		 * The Serialization can be set to ignore template parameters.
		 * That is, all instances of a template class are treated with the
		 * same Serializer. If you have MyClass<int> and MyClass<double>,
		 * the class name written to and read from a stream must be
		 * "MyClass".
		 * @param strip if true, template parameters are stripped.
		 *        The default value is true.
		 **/
		void setStripTemplateParams(bool strip) { _bStripTemplateParams = strip; }

		/**
		 * Check if this Serialization is currently stripping template
		 * parameters.
		 **/
		bool getStripTemplateParams(void) const { return _bStripTemplateParams; }

	private:
		bool _bStripTemplateParams;
	};
}

#endif
