# Copyright (C) 2003, 2005 by Intevation GmbH
# Authors:
# Jonathan Coles <jonathan@intevation.de>
#
# This program is free software under the GPL (>=v2)
# Read the file COPYING coming with GRASS for details.

"""
Parser for thuban session files.
"""

__version__ = "$Revision: 2642 $"

import os
import xml.sax
import xml.sax.handler
from xml.sax import make_parser, ErrorHandler, SAXNotRecognizedException
from Thuban import internal_from_unicode

class XMLReader(xml.sax.handler.ContentHandler):

    # Dictionary mapping element names (or (URI, element name) pairs for
    # documents using namespaces) to method names. The methods should
    # accept the same parameters as the startElement (or startElementNS)
    # methods. The start_dispatcher is used by the default startElement
    # and startElementNS methods to call a method for the open tag of an
    # element.
    start_dispatcher = {}

    # end_dispatcher works just like start_dispatcher but it's used by
    # endElement and endElementNS. The method whose names it maps to
    # should accept the same parameters as endElement and endElementNS.
    end_dispatcher = {}


    def __init__(self):
        self.chars = ''
        self.__directory = ""
        self.__dispatchers = {}

    def read(self, file_or_filename):

        if hasattr(file_or_filename, "read"):
            # it's a file object
            self.__directory = ""
            self.__file = file_or_filename
        else:
            filename = file_or_filename
            self.__directory = os.path.dirname(filename)
            self.__file = open(filename)

        parser = make_parser()
        parser.setContentHandler(self)
        parser.setErrorHandler(ErrorHandler())
        parser.setFeature(xml.sax.handler.feature_namespaces, 1)

        #
        # Well, this isn't pretty, but it appears that if you
        # use Python 2.2 without the site-package _xmlplus then
        # the following will fail, and without them it will work.
        # However, if you do have the site-package and you don't
        # call these functions, the reader raises an exception
        #
        # The reason we set these to 0 in the first place is 
        # because there is an unresolved issue with external
        # entities causing an exception in the reader
        #
        try:
            parser.setFeature(xml.sax.handler.feature_validation,0)
            parser.setFeature(xml.sax.handler.feature_external_ges,0)
            parser.setFeature(xml.sax.handler.feature_external_pes,0)
        except SAXNotRecognizedException:
            pass

        parser.parse(self.__file)

        self.close()

    def close(self):
        self.__file.close()
        
    def GetFilename(self):
        if hasattr(self.__file, "name"):
            return self.__file.name

        return ""

    def GetDirectory(self):
        return self.__directory


    def AddDispatchers(self, dict):
        """Add the function names that should be used to process XML tags.

        dict -- a dictionary whose keys are XML tag strings and whose values
                are pairs of strings such that the first string is
                the name of the function that should be called when the
                XML tag opens and the second string is the name of the
                function that should be called when the XML tag closes.
                If a pair element is None, no function is called.
        """

        self.__dispatchers.update(dict)

    def startElementNS(self, name, qname, attrs):
        """Call the method given for name in self.start_dispatcher
        """
        if name[0] is None:
            method_name = self.__dispatchers.get(name[1])
        else:
            # Dispatch with namespace
            method_name = self.__dispatchers.get(name)
        if method_name is not None and method_name[0] is not None:
            getattr(self, method_name[0])(name, qname, attrs)

    def endElementNS(self, name, qname):
        """Call the method given for name in self.end_dispatcher
        """
        if name[0] is None:
            method_name = self.__dispatchers.get(name[1])
        else:
            # Dispatch with namespace
            method_name = self.__dispatchers.get(name)
        if method_name is not None and method_name[1] is not None:
            getattr(self, method_name[1])(name, qname)

    def encode(self, str):
        """Return the unicode object str in Thuban's internal representation

        If str is None, return None
        """
        if str is None:
            return None
        return internal_from_unicode(str)
