# This file is part of Pantry, command-line nutrient analysis.
# 
# Version 22, released Saturday, October 13, 2007.
# 
# Copyright 2007 Omari Norman.
# 
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated
# documentation files (the "Software"), to deal in the
# Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute,
# sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
# 
# The above copyright notice and this permission notice shall
# be included in all copies or substantial portions of the
# Software.
# 
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
# PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
# OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# 
# Pantry's "built-in" XML validator is a modified version of
# xmlproc.  xmlproc is Copyright 1998-2000 by Lars Marius
# Garshol, Oslo, Norway.
# 
# All Rights Reserved
# 
# Permission to use, copy, modify, and distribute this
# software and its documentation for any purpose and without
# fee is hereby granted, provided that the above copyright
# notice appear in all copies and that modified copies are
# clearly marked as such.
# 
# LARS MARIUS GARSHOL DISCLAIMS ALL WARRANTIES WITH REGARD TO
# THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL LARS MARIUS
# GARSHOL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
# DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
# USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

""" Help converting to and from Unicode. """

from __future__ import division

def convertUni(toConvert):
    """ Convert unicode to a number. 

    If toConvert is searchable by re.search, but cannot be converted
    (because for instance it is 'aaa') or is a zero-length string,
    then UnconvertableError is raised. """

    if ' ' in toConvert:
        toConvertSplit = toConvert.split(' ')
        if len(toConvertSplit) != 2:
            raise UnconvertableError
        baseUni, fractionUni = toConvertSplit
        baseNum = _convertBase(baseUni)
        fractionNum = _convertFraction(fractionUni)
        result = baseNum + fractionNum
    elif '/' in toConvert:
        result = _convertFraction(toConvert)
    else:
        result = _convertBase(toConvert)
    return result

def _convertBase(toConvert):
    """ Converts a unicode to a number.

    The unicode must be something that can be converted with int() or float(),
    otherwise UnconvertableError is raised. """

    if '.' in toConvert:
        converter = float
    else:
        converter = int

    try:
        result = converter(toConvert)
    except ValueError:
        raise UnconvertableError
    return result

def _convertFraction(toConvert):
    """ Converts a unicode to a number.

    The unicode must contain a '/' and have both a denominator and a 
    numerator that can be converted with _convertBase. Otherwise,
    UnconvertableError is raised. """

    toConvertSplit = toConvert.split('/')
    if len(toConvertSplit) != 2:
        raise UnconvertableError
    numeratorUni, denominatorUni = toConvertSplit
    numeratorNum = _convertBase(numeratorUni)
    denominatorNum = _convertBase(denominatorUni)
    if denominatorNum == 0:
        raise UnconvertableError
    return numeratorNum / denominatorNum

    
def convertOptions(options, encoding):
    """ Convert string options in optparse to unicode. """
    
    class Holder:
        pass
    result = Holder()

    oldVars = vars(options)
    newVars = {}
    for optName, optValue in oldVars.items():
        try:
            newVars[optName] = optValue.decode(encoding)
        except AttributeError:
            # can't be decoded; maybe it is an iterable that
            # can be decoded
            try:
                newVars[optName] = convertList(optValue)
            except TypeError:
                # not an iterable either; just add it to the result
                newVars[optName] = optValue

    for optName, optValue in newVars.items():
        setattr(result, optName, optValue)

    return result

def convertList(toConvert, encoding):
    """ Returns list or tuple with items converted to unicode.

    Only attempts to convert items that are strings or unicode. """

    result = []
    for item in toConvert:
        try:
            newItem = item.decode(encoding)
        except (AttributeError, TypeError):
            result.append(item)
        else:
            result.append(newItem)
    return result

class Error(Exception):
    pass

class UnconvertableError(Error):
    """ convertUni failed because arg can't be converted. """
    pass
