Subversion Repositories Scribus

Rev

Rev 8289 | Rev 8390 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

/*
 *  digester.h
 *  
 *
 *  Created by Andreas Vox on 02.06.06.
 *  Copyright 2006 under GPL2. All rights reserved.
 *
 */




#ifndef DIGESTER_H
#define DIGESTER_H

#include <cassert>
#include <iostream>
#include <vector>
#include <map>
#include <string>
#include <typeinfo>

#include "desaxe_conf.h"
#include "saxhandler.h"


//#define DESAXE_DEBUG 1

namespace desaxe {
       
class Action;
class RuleState;

namespace {  // ANON
       
        class VarPtr
        {
        public:
                void* ptr;
                std::string type;
        };

       
        template<class ObjType>
        inline
        VarPtr mkcell(ObjType* obj)
        {
                VarPtr result;
                result.ptr = obj;//     result.ptr = const_cast<void*>(static_cast<const void*>(obj)); //??
                result.type = typeid(obj).name();
                return result;
        }
       
       
        template<class ObjType>
        inline
        void chkcell(const VarPtr& cell, std::vector<VarPtr>* stack = NULL)
        {
                ObjType* dummy;
                if( cell.type != typeid(dummy).name() )
                {
                        std::cerr << "requested type '" << typeid(dummy).name() << "' doesnt match cell type '" << cell.type << "'\n";
                        if (stack)
                        {
                                int i=0;
                                std::vector<VarPtr>::iterator it;
                                for (it = stack->begin(); it != stack->end(); ++it)
                                {
                                        std::cerr << i++ << "\t" << (*it).type << "\t" << (*it).ptr << "\n";
                                }
                        }
                        assert (false);
                }
        }
       


class Patch {
public:
        // internal linked list
        Patch* next;
        Patch(Patch* nxt) : next(nxt) {}
       
        virtual void run(VarPtr lnk) = 0;
        virtual ~Patch() {}
};


} // namespace ANON    



/**
   Digester helps you to create C++ objects from a SAX stream.
   All you have to do is register actions for patterns of element tags
   you expect in your XML stream. These actions can create new objects, set
   attributes, or call methods. Actions operate on intermediate objects which
   are hold on a stack maintained by Digester.  
 */

class Digester : public SaxHandler {   
public:
        Digester();
        Digester& operator=(const Digester& other);
        virtual ~Digester();
        void addRule(const Xml_string pattern, Action action);
       
        void parseFile(const Xml_string filename);
        void parseMemory(const char* data, unsigned int length);
       
        template<class ObjType>
                ObjType*  result();    
        int nrOfErrors() const;
        const Xml_string getError(int i) const;
       
// called by SAX parser:
        void beginDoc();
        void endDoc();
        void begin(Xml_string tag, Xml_attr attr);
        void end(Xml_string tag);
        void chars(Xml_string text);
       
// used by actions:
        void fail();
        void error(const Xml_string msg);
       
        template<class ObjType>
        ObjType*  top(unsigned int offset = 0);
       
        template<class ObjType>
        ObjType*  bottom(unsigned int offset = 0);
       
        template<class ObjType>
        void setResult( ObjType* res );
       
        void pop();
        void popn(unsigned int number);
       
        template<class ObjType>
        void push(ObjType* obj);
       
//  used to resolve idrefs and for general storage     
        template<class ObjType>
                ObjType*  lookup(const Xml_string idref);      
        template<class ObjType>
                void store(const Xml_string idref, ObjType* res );
       
//  used to resolve idrefs if use is before definition
        template<class LinkType>
                void patchCall(const Xml_string idref, void (*fun)(LinkType*) );
        template<class ObjType, class LinkType>
                void patchInvoke(const Xml_string idref, ObjType* obj, void (ObjType::*fun)(LinkType*) );
       
        // used to insert "/" where necessary
        static Xml_string concat(const Xml_string pattern1, const Xml_string pattern2);

private:
        RuleState*
                state;
       
        std::vector<VarPtr>
                objects;
       
        std::map<Xml_string, VarPtr>
                storage;

        std::map<Xml_string, Patch*>
                patches;
       
        VarPtr
                result_;
       
        std::vector<Xml_string>
                errors;
};



template<class ObjType>
inline
ObjType*  Digester::top(unsigned int offset)
{
#ifdef DESAXE_DEBUG
        std::cerr << "top(" << offset << ") of " << objects.size() << "\n";
#endif
        unsigned int count = objects.size();
        assert (offset < count);
        chkcell<ObjType>(objects[count - offset - 1], &objects);
#ifdef DESAXE_DEBUG
        std::cerr << "stack-> " << static_cast<ObjType*>(objects[count - offset - 1].ptr) << "\n";
#endif
       
        return static_cast<ObjType*>(objects[count - offset - 1].ptr);
}


template<class ObjType>
inline
ObjType*  Digester::bottom(unsigned int offset)
{
#ifdef DESAXE_DEBUG
        std::cerr << "bottom(" << offset << ") of " << objects.size() << "\n";
#endif
        unsigned int count = objects.size();
        assert (offset < count);
        chkcell<ObjType> (objects[offset]);
        return static_cast<ObjType*>(objects[offset].ptr);
}


template<class ObjType>
inline
ObjType*  Digester::result()
{
        ObjType* dummy;
        if (result_.type != typeid(dummy).name())
                return NULL;
#ifdef DESAXE_DEBUG
        std::cerr << "result-> " << static_cast<ObjType*>(result_.ptr) << "\n";
#endif
        return static_cast<ObjType*>(result_.ptr);
}


template<class ObjType>
inline
void Digester::setResult(ObjType* res)
{
#ifdef DESAXE_DEBUG
        std::cerr << res << " ->result\n";
#endif
        result_ = mkcell(res);
}


inline
void Digester::pop()
{
        unsigned int count = objects.size();
        assert (1 <= count);
        objects.pop_back();
}

inline
void Digester::popn(unsigned int number)
{
        unsigned int count = objects.size();
        assert (number <= count);
        objects.resize(count - number);
}


template<class ObjType>
inline
void Digester::push(ObjType* obj)
{
#ifdef DESAXE_DEBUG
        std::cerr << "stack<- " << obj << "\n";
#endif
        objects.push_back(mkcell(obj));
}


// now lookup / store / patch business

namespace {
       
        template <class LinkType>
        struct Patch1 : public Patch
        {
                typedef void (*FunType1)(LinkType*);
                FunType1 fun;
               
                Patch1(FunType1 fn, Patch* nxt = NULL) : Patch(nxt), fun(fn) {}
               
                void run(VarPtr link)
                {
                        fun( static_cast<LinkType*>(link.ptr) );
                }
        };
       
       
        template <class ObjType, class LinkType>
                struct Patch2 : public Patch
        {
                typedef void (ObjType::*FunType2)(LinkType*);
                ObjType* obj;
                FunType2 fun;
               
                Patch2(ObjType* ob, FunType2 fn, Patch* nxt = NULL) : Patch(nxt), obj(ob), fun(fn) {}

                void run(VarPtr link)
                {
                        (obj->*fun)( static_cast<LinkType*>(link.ptr) );
                }
        };
       
       
        void runPatches(Patch*& list, VarPtr link)
        {
                while (list)
                {
                        Patch* nxt = list->next;
                        list->run(link);
                        delete list;
                        list = nxt;
                }
        }
       
       
        void deletePatches(std::map<Xml_string, Patch*>& patches)
        {
                std::map<Xml_string, Patch*>::iterator it;
                for (it = patches.begin(); it != patches.end(); ++it)
                {
                        Patch* list = it->second;
                        while (list)
                        {
                                Patch* nxt = list->next;
                                delete list;
                                list = nxt;
                        }
                }
                patches.clear();
        }

       
//      template<> class Patch1<void>;
//      template<> class Patch2<VarPtr,void>;
       
} //namespace ANON



template<class ObjType>
inline
ObjType*  Digester::lookup(const Xml_string idref)
{
        std::map<Xml_string, VarPtr>::iterator cell = storage.find(idref);
        if (cell == storage.end())
        {
#ifdef DESAXE_DEBUG
                std::cerr << "lookup[" << idref << "]-> NULL\n";
#endif
                return NULL;
        }
        else
        {
                chkcell<ObjType> (cell->second);
#ifdef DESAXE_DEBUG
                std::cerr << "lookup[" << idref << "]-> " << static_cast<ObjType*>(cell->second.ptr) << "\n";
#endif
                return static_cast<ObjType*>(cell->second.ptr);
        }
}



template<class ObjType>
inline
void Digester::store(const Xml_string idref, ObjType* obj)
{
#ifdef DESAXE_DEBUG
        std::cerr << "store[" << idref << "] <- " << obj << "\n";
#endif
        storage[idref] = mkcell(obj);
        runPatches(patches[idref], storage[idref]);
}



template<class LinkType>
void Digester::patchCall(const Xml_string idref, void (*fun)(LinkType*) )
{
        std::map<Xml_string, VarPtr>::iterator cell = storage.find(idref);
        if (cell == storage.end())
        {
                patches[idref] = new Patch1<LinkType>(fun, patches[idref] );
        }
        else
        {
                Patch1<LinkType>(fun).run(cell->second);
        }
}


template<class ObjType, class LinkType>
void Digester::patchInvoke(const Xml_string idref, ObjType* obj, void (ObjType::*fun)(LinkType*) )
{
        std::map<Xml_string, VarPtr>::iterator cell = storage.find(idref);
        if (cell == storage.end())
        {
                patches[idref] = new Patch2<ObjType,LinkType>(obj, fun, patches[idref] );
        }
        else
        {
                Patch2<ObjType,LinkType>(obj, fun).run(cell->second);
        }
}

} // namespace desaxe

#endif