#!/usr/bin/env python
"""a validating CSSParser
"""
__all__ = ['CSSParser']
__docformat__ = 'restructuredtext'
__version__ = '$Id: parse.py 1264 2008-05-28 16:24:41Z cthedot $'

import os
import codecs
import urllib
import cssutils

class CSSParser(object):
    """
    parses a CSS StyleSheet string or file and
    returns a DOM Level 2 CSS StyleSheet object

    Usage::

        parser = CSSParser()
        
        # optionally
        parser.setFetcher(fetcher)
        
        sheet = parser.parseFile('test1.css', 'ascii')

        print sheet.cssText
    """
    def __init__(self, log=None, loglevel=None, raiseExceptions=False,
                 fetcher=None):
        """
        log
            logging object
        loglevel
            logging loglevel
        raiseExceptions
            if log should simple log (default) or raise errors
        fetcher
            see ``setFetchUrl(fetcher)``
        """
        if log is not None:
            cssutils.log.setlog(log)
        if loglevel is not None:
            cssutils.log.setloglevel(loglevel)

        cssutils.log.raiseExceptions = raiseExceptions
        self.__tokenizer = cssutils.tokenize2.Tokenizer()
        self.setFetcher(fetcher)

    def parseString(self, cssText, encoding=None, href=None, media=None, 
                    title=None):
        """Return parsed CSSStyleSheet from given string cssText.

        cssText
            CSS string to parse
        encoding
            If ``None`` the encoding will be read from BOM or an @charset
            rule or defaults to UTF-8. 
            If given overrides any found encoding including the ones for 
            imported sheets.
            It also will be used to decode ``cssText`` if given as a (byte)
            string.
        href
            The href attribute to assign to the parsed style sheet.
            Used to resolve other urls in the parsed sheet like @import hrefs
        media
            The media attribute to assign to the parsed style sheet
            (may be a MediaList, list or a string)
        title
            The title attribute to assign to the parsed style sheet
        """
        if isinstance(cssText, str): 
            cssText = codecs.getdecoder('css')(cssText, encoding=encoding)[0]

        sheet = cssutils.css.CSSStyleSheet(href=href,
                                           media=cssutils.stylesheets.MediaList(media),
                                           title=title)
        sheet._setFetcher(self.__fetcher)
        # tokenizing this ways closes open constructs and adds EOF
        sheet._setCssTextWithEncodingOverride(self.__tokenizer.tokenize(cssText, 
                                                                        fullsheet=True), 
                                              encoding)
        return sheet

    def parseFile(self, filename, encoding=None, 
                  href=None, media=None, title=None):
        """Retrieve and return a CSSStyleSheet from given filename.

        filename
            of the CSS file to parse, if no ``href`` is given filename is
            converted to a (file:) URL and set as ``href`` of resulting 
            stylesheet.
            If href is given it is set as ``sheet.href``. Either way
            ``sheet.href`` is used to resolve e.g. stylesheet imports via
            @import rules. 
        encoding
            Value ``None`` defaults to encoding detection via BOM or an 
            @charset rule.
            Other values override detected encoding for the sheet at 
            ``filename`` including any imported sheets.

        for other parameters see ``parseString``
        """
        if not href:
            # prepend // for file URL, urllib does not do this?
            href = u'file:' + urllib.pathname2url(os.path.abspath(filename))
            
        return self.parseString(open(filename, 'rb').read(), 
                                encoding=encoding, # read returns a str
                                href=href, media=media, title=title)

    def parseUrl(self, href, encoding=None, media=None, title=None):
        """Retrieve and return a CSSStyleSheet from given href (an URL).

        href
            URL of the CSS file to parse, will also be set as ``href`` of 
            resulting stylesheet
        encoding
            Value ``None`` defaults to encoding detection via HTTP, BOM or an 
            @charset rule.
            A value overrides detected encoding for the sheet at ``href`` 
            including any imported sheets.

        for other parameters see ``parseString``
        """
        encoding, text = cssutils.util._readUrl(href, 
                                                overrideEncoding=encoding)
        if text is not None:
            return self.parseString(text,
                                    encoding=encoding,  
                                    href=href, media=media, title=title)                                

    def setFetcher(self, fetcher=None):
        """Replace the default URL fetch function with a custom one.
        The fetcher function gets a single parameter    
        
        ``url``
            the URL to read
    
        and returns ``(encoding, content)`` where ``encoding`` is the HTTP
        charset normally given via the Content-Type header (which may simply
        omit the charset) and ``content`` being the (byte) string content.
        The Mimetype should be 'text/css' but this has to be checked by the
        fetcher itself (the default fetcher emits a warning if encountering
        a different mimetype).
    
        Calling ``setFetcher`` with ``fetcher=None`` resets cssutils
        to use its default function.
        """
        self.__fetcher = fetcher

    @cssutils.util.Deprecated('Use cssutils.CSSParser().parseFile() instead.')
    def parse(self, filename, encoding=None, 
              href=None, media=None, title=None):
        self.parseFile(filename, encoding, href, media, title)
