# Copyright (c) 2000-2001 LOGILAB S.A. (Paris, FRANCE).
# http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# 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.
__revision__ = '$Id: sax.py,v 1.10 2002/01/10 17:01:30 syt Exp $'

from xml.sax import ContentHandler

## standard handler for configuration files ###################################
class DictSaxHandlerMixin(ContentHandler):
    """
    easily construct xml config file parsing by inheriting from this class
    
    The subclass shoud define 3 dictionaries, 2 tuples and a string variable
    which explain the document structure.
    This handler is initialized with an object which can be handled as a
    dictionary, and will contain configuration information at the end of
    parsing.

    required dictionary:
    * _ROOT , the root element name
    * _MASTER, dictionary designing the first level key
    for instance, with _MASTER == {'part1':'name', 'part2':None}
    the handler, when it receives a startElement with name == 'part1', will
    append to the handler stack pref_o.prefs[attrs.get(_MASTER[name])], where
    pref_o is the preference object given to __init__ and attrs the attributes
    dictionary given to startElement. On the other hand, when it receives a
    startElement with name=='part2', it will append pref_o.prefs[name] to the
    handler stack.
    * _SUB, dictionary designing the deeper level key.
    for instance, with _SUB == {'entry':'name', 'state':None}
    the handler, when it receives a startElement with name == 'entry', will
    append to the handler stack stack[-1].setdefault(attrs.get(_SUB[name]),{})
    where stack[-1] is the last elmeent on the stack. As _MASTER, when it
    receives a startElement with name=='state', it will append
    stack[-1].setdefault(name,{}) to the handler stack.
    * _KEY, tuple designing last level key
    * _LIST, tuple designing multiple last level key, same as above, but
    value will be append to a list.
    * _CB, dictionary where key are element of _KEY or _LIST and value function
    to call when retreiving the element value.

    All first level key should be predefined in the preference object dictionary
    ("prefs")
    For example see narval.NarvalRC or narval.gui.PrefModel
    """
    
    def __init__(self, pref_o=None):
        if pref_o is not None:
            self.initState(pref_o)
                
    def initState(self, pref_o):
        self._stack = [pref_o]
        self._ALL = {}
        self.last_key = None
        for l in (self._MASTER.keys(), self._S_LIST, self._LIST):
            for e in l:
                self._ALL[e] = 1

        
    def startElement(self, name, attrs):
        self.last_key = None
        ##
        if self._MASTER.has_key(name):
            key = self._MASTER[name]
            if key:
                self._stack.append(self._stack[-1].setdefault(attrs.get(key),{}))
            else:
                self._stack.append(self._stack[-1].setdefault(name, {}))
        elif name in self._S_LIST:
            l = self._stack[-1].setdefault(name, [])
            l.append({})
            self._stack.append(l[-1])
        elif name in self._LIST:
            self.last_key = name
            self._stack.append(self._stack[-1].setdefault(name, []))
        ##    
        if name in self._KEY:
            self.last_key = name
            
    def endElement(self, name):
        if self._ALL.has_key(name):
            self._stack.pop()

    def characters(self, ch):
        key = self.last_key
        if key:
            ch = ' '.join(ch.split())
            if len(ch) > 0:
                if self._CB.has_key(key):
                    ch = self._CB[key](ch)
                if key in self._LIST:
                    self._stack[-1].append(ch)
                else:
                    self._stack[-1][key] = ch

## Readers ####################################################################        
class PrefReader:
    def __init__(self, handler_class, pref_o):
        from xml.sax import make_parser
        self._p = make_parser()
        self._h = handler_class(pref_o)
        self._p.setContentHandler(self._h)
        #from os.path import normpath, join
        #cat_path = normcase(normpath(join(config.get_home(),'dtd/CATALOG')))

    def fromFile(self, file):
        f = open(file)
        self._p.parse(f)
        f.close()

    def fromString(self, str):
        from cStringIO import StringIO
        buf = StringIO(str)
        self._p.parse(buf)
        buf.close()
