root/src/Describer.h

////////////////////////////////////////////////////////////////////////
// Copyright (c) Nehmulos 2011-2014
// This file is part of N0 Strain Serialization Library.
//
// N0Strain-Serialization-Library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// N0Strain-Serialization-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 Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with N0Strain-Serialization-Library.  If not, see https://gnu.org/licenses/lgpl-3.0
////////////////////////////////////////////////////////////////////////




#ifndef DESCRIBER_H_
#define DESCRIBER_H_

#include <sstream>
#include <iostream>
#include <fstream>
#include <istream>
#include <ostream>
#include <map>
#include <stack>
#include <stdio.h>

#include "N0SlibConstants.h"
#include "Tag.h"
#include "DescribedObject.h"

namespace nw {

typedef Describer Descriptor; ///< Alias to itself, to provide a more common term
 class PointerCollector;

///////////////////////////////////////////////////////////////////////////////////////////////////
/// This Class allows to write data into a file or to read it from there
/// It is build up, so that it can be implemented
/// to provide markup files and binary files
/// To allow this, a slow down by taking Markup-Tag-Names
/// as Parameters, was accepted;
///////////////////////////////////////////////////////////////////////////////////////////////////
class Describer {
public:
        Describer();
        virtual ~Describer();

        void setPointers(const PointerCollector& pc);

        ///////////////////////////////////////////////////////////////////////////////////////////////
        /// Closes the files that was opened for the Serializer.
        /// It may writes a footer and should be called, Otherwise errors could appear.
        ///////////////////////////////////////////////////////////////////////////////////////////////
        virtual void close() = 0;

        ///////////////////////////////////////////////////////////////////////////////////////////////
        /// Returns true if the Serializer writes into a file, if it's a reader false is returned
        ///////////////////////////////////////////////////////////////////////////////////////////////
        virtual bool isInWriteMode() = 0;

        ///////////////////////////////////////////////////////////////////////////////////////////////
        /// returns the opposite of @isInWriteMode. This Function was created to tidy up your code.
        ///////////////////////////////////////////////////////////////////////////////////////////////
        bool isInReadMode();

        ///////////////////////////////////////////////////////////////////////////////////////////////
        /// returns true if the Serializer doesn't read or write anything to / from the harddrive.
        /// This was written to recognize PointerCollector.
        ///////////////////////////////////////////////////////////////////////////////////////////////
        virtual bool isSilentCrawler();


        ///////////////////////////////////////////////////////////////////////////////////////////////
        /// enters a childTag with the given Name, returns true if successful.
        /// In ReadMode it returns true if a tag with the given name Exists.
        /// Returns always true in WriteMode.
        ///////////////////////////////////////////////////////////////////////////////////////////////
        virtual bool push(const String tagName) = 0;

        ///////////////////////////////////////////////////////////////////////////////////////////////
        /// writes if the condition is true; set's the condition to true if it's in the file.
        /// Returns the condition if it 's used in a writer.
        /// Set's the condition to true / false if the push could / couldn't be read.
        /// The return value is equivalent to the condition, still the return value should be used
        /// for conditions, because it will be surely true for the PointerCollector class
        /// Example:
        ///     if(file->conditionalPush("awesomeTag", awesomeness) {
        ///     // This will only be written if awesomeness is true
        ///             // and only be read if it exists in the file
        ///     file->serialize("thisIsAwesome", awesomenessLevel);
        ///             file->serialize("AwesomeStuff", otherAwesomeStuff);
        /// }
        ///////////////////////////////////////////////////////////////////////////////////////////////
        virtual bool conditionalPush(const String tagname, bool condition);
        virtual void pop() = 0; ///< leaves the current tag; jumps into it's parentTag. Like an end() for push()

        ///////////////////////////////////////////////////////////////////////////////////////////////
        /// Open a simple array that contains comma separated values, or a base64 encrypted string.
        /// Data can be added by using pushValueToSimpleArray
        /// If arrayName is empty only the value will be written.
        ///////////////////////////////////////////////////////////////////////////////////////////////
//      virtual void describeSimpleArray(String arrayName, unsigned int size) = 0;
//
//      /// push a value the open simpleArray
//      template<class T> virtual bool pushValueToSimpleArray(T& value);

        ///////////////////////////////////////////////////////////////////////////////////////////////
        /// Opens an array with the number of (size) elements.
        /// The Array is wrapped by a tag with the arrayName
        /// Each element in the array has the ElementsName as tagName if names in arrays are supported
        /// It fills the ToIterate list of enterNextElement;
        /// Use it before you loop through an array with enterNextElement
        /// If arrayName is empty, the elements will be created in the currentTag.
        ///////////////////////////////////////////////////////////////////////////////////////////////
        virtual void describeArray(const String arrayName, const String ElementsName, unsigned int size) = 0;

        ///////////////////////////////////////////////////////////////////////////////////////////////
        /// Read / Write an array containing elements of a simple data.
        /// The elements must be simple values wich will be stored anonymously by using describeValue.
        /// The Datatype of the elements must be supported by Describer::describeValue.
        /// Describer::describeValue must be called only ONCE after enterNextElement has been called.
        /// If anonymous elements are not supported by the Derived Describer they will use a <value> tag
        /// If you want to store complex Objects use describeArray(string, string, uint).
        /// @parm arrayName Name of the Tag containing the Array.
        /// @parm size Only used when writing. Determins how long enterNextElement returns true.
        ///////////////////////////////////////////////////////////////////////////////////////////////
        virtual void describeValueArray(const String arrayName, unsigned int size) = 0;

        // //helps to reserve enough memory for vectors, instead of pushing all the time
        // //Maybe just really use references in serializeArray, it would take less code
        //virtual unsigned int getArraySize();


        ///////////////////////////////////////////////////////////////////////////////////////////////
        /// Jumps into the next element in an array.
        ///
        /// Creates an arrayElement and returns true if the iterator is smaller than the current array's size
        /// use it as condition in a for loop with an unsigned int as Iterator.
        /// example: for(int i=0; describer->enterNextElement(i); ++i) { /* ... */ }
        /// Don't forget to call serializeArray before your "for" loop
        ///////////////////////////////////////////////////////////////////////////////////////////////
        virtual bool enterNextElement(unsigned int iterator) = 0;

        ///////////////////////////////////////////////////////////////////////////////////////////////
        /// Extending classes will write/read the references; used to serialize your raw data.
        /// This functions cover all wide-spread data-types
        /// If the Serializer is a Writer it writes the reference to the file
        /// If the Serializer is a Reader it set's the reference to
        ///    the value of the childTag for the matching Name
        ///////////////////////////////////////////////////////////////////////////////////////////////
        /* Serialize functions */
        virtual void describeValue(bool&)            = 0; ///< Describe a  boolean          reference
        virtual void describeValue(char&)            = 0; ///< Describe a  character        reference
        virtual void describeValue(unsigned char&)   = 0; ///< Describe an unsigned char    reference
        virtual void describeValue(signed char&)     = 0; ///< Describe a  signed char      reference
        //2 byte
        virtual void describeValue(short&)           = 0; ///< Describe a  short            reference
        virtual void describeValue(unsigned short&)  = 0; ///< Describe an unsigned short   reference
        //4 byte
        virtual void describeValue(int&)             = 0; ///< Describe an integer          reference
        virtual void describeValue(unsigned int&)    = 0; ///< Describe an unsigned integer reference
        virtual void describeValue(long&)            = 0; ///< Describe a  long             reference
        virtual void describeValue(unsigned long&)   = 0; ///< Describe an unsigned long    reference
        virtual void describeValue(float&)           = 0; ///< Describe a  float            reference
        virtual void describeValue(Pointer)          = 0; ///< Describe a  PointerId        reference
        //8 byte
        virtual void describeValue(double&)          = 0; ///< Describe a double            reference
        virtual void describeValue(long long&)       = 0; ///< Describe a long long         reference
        //12 byte
        virtual void describeValue(long double&)     = 0; ///< Describe a long double       reference
        //variable size
        virtual void describeValue(String&)          = 0; ///< Describe a String reference
        virtual void describeBlob(const String blobName, nw::Pointer binaryBlob, unsigned int blobSize) = 0; ///< Describe Binary blobs
        virtual void comment(const String text) = 0;       ///< Write a comment if it's supported

        virtual void describeName(const String childName) = 0;

        void describe(const String childName,bool& ref)          {describeName(childName); describeValue(ref);}
        void describe(const String childName,char& ref)          {describeName(childName); describeValue(ref);}
        void describe(const String childName,unsigned char& ref) {describeName(childName); describeValue(ref);}
        void describe(const String childName,signed char& ref)   {describeName(childName); describeValue(ref);}
        void describe(const String childName,short& ref)         {describeName(childName); describeValue(ref);}
        void describe(const String childName,unsigned short& ref){describeName(childName); describeValue(ref);}
        void describe(const String childName,int& ref)           {describeName(childName); describeValue(ref);}
        void describe(const String childName,unsigned int& ref)  {describeName(childName); describeValue(ref);}
        void describe(const String childName,long& ref)          {describeName(childName); describeValue(ref);}
        void describe(const String childName,unsigned long& ref) {describeName(childName); describeValue(ref);}
        void describe(const String childName,float& ref)         {describeName(childName); describeValue(ref);}
        void describe(const String childName,Pointer ref)        {describeName(childName); describeValue(ref);}
        void describe(const String childName,double& ref)        {describeName(childName); describeValue(ref);}
        void describe(const String childName,long long& ref)     {describeName(childName); describeValue(ref);}
        void describe(const String childName,long double& ref)   {describeName(childName); describeValue(ref);}
        void describe(const String childName,String& ref)        {describeName(childName); describeValue(ref);}


        /* (De)Serialization functions with default values */
        void describe(const String childName,bool& ref, bool defaultValue);
        //Containers
//      template<class T> void describe(std::vector<T>&);
//      template<class T> void describe(std::stack<T>&);
//      template<class T> void describe(std::list<T>&);
//      template<class T> void describe(std::deque<T>&);
//      template<class T> void describe(std::queue<T>&);
//      template<class T> void describe(std::priority_queue<T>&);
        template<typename KeyT, typename ValueT> void describe(String mapName, std::map<KeyT, ValueT>& m)
        {
                if(isInWriteMode())
                {
                        push(mapName);
                        typename std::map<KeyT,ValueT>::iterator it;
                        for(it=m.begin(); it != m.end(); ++it)
                        {
                                std::stringstream ss(it->first);
                                String elementTagName;
                                ss >> elementTagName;
                                ss << it->second;
                                this->describeValue(elementTagName, it->second);
                        }
                        pop();
                }
                else if(isInReadMode())
                {
                        describeArray(mapName, "", 0);
                        for (int i = 0; enterNextElement(i); ++i)
                        {
                                Tag t;
//                              t = this->getNextTag();
                        }
                }
        }
//      template<class T> void describe(std::multimap<T>&);
//      template<class T> void describe(std::set<T>&);
//      template<class T> void describe(std::multiset<T>&);

        /// return error message or empty string if there was no error
        std::string getErrorMessage();


        ///////////////////////////////////////////////////////////////////////////////////////////////
        //
        // States
        // these functions are used to store custom states in a describer.
        // They can be used to check if a static value is already described.
        //
        ///////////////////////////////////////////////////////////////////////////////////////////////

        bool hasState(const String stateName);                                                  ///< Return true if a state was set.
        bool getBooleanState(String stateName);                                         ///< Return a boolean Value for the State
        void getState(const String stateName, void* pointerToPointer);  ///< Return a stored Pointer for the State
        void setState(const String stateName, bool value);                              ///< Add a new State and set it's value
        void setState(const String stateName, void* value);                             ///< Add a new State and set it's value

        ///////////////////////////////////////////////////////////////////////////////////////////////
        /// Set this pointer to a function that returns a new Serialized Object for the classId
        /// You only have to set this if you want to serialize Classes, that derive from SerializedObject
        ///////////////////////////////////////////////////////////////////////////////////////////////
        static DescribedObject* (*createObjectForClassId)(ClassId);
        static String encodingUsed;

protected:
        void describePointers();                                        ///< Serialize the N0Slib:Header tag and all unhandled Pointers;
        bool pointerIsKnown(DescribedObject* pointer);                  ///< return true if the pointer was already collected
        std::vector<DescribedObject *> unhandledPointers;             ///< store Pointers temporary until they were written to the header
        std::vector<DescribedObject *> handledPointers;                      ///< Store pointers that were written to / read from the file
        DescribedObject* getSerializedObjectForId(unsigned int id);         ///< Get a handled Pointer for it's id
        unsigned int getIDForSerializedObject(Pointer pointerToPointer);///< Get an id for a handled Pointer

        std::stringstream errorMessage; ///< write error message into this stream
private:
        ///////////////////////////////////////////////////////////////////////////////////////////////
        /// Save states to remember if static values were already serialized.
        /// Also save pointers to static values.
        /// The states can be accessed via getState and addState
        ///////////////////////////////////////////////////////////////////////////////////////////////
        std::vector<Tag*> states;

        ///////////////////////////////////////////////////////////////////////////////////////////////
        /// Saves the content of the collected Pointers and marks them as handled.
        ///////////////////////////////////////////////////////////////////////////////////////////////
        inline void serializePointerData();
};

}  // namespace nw
#endif /* DESCRIBER_H_ */