# Source code for sqlobject.converters

    from array import array
    import datetime
    from decimal import Decimal
    import time
    import sys
    from .compat import PY2, buffer_type

    if PY2:
        from types import ClassType, InstanceType, NoneType
    else:
        # Use suitable aliases for now
        ClassType = type
        NoneType = type(None)
        # This is may not be what we want in all cases, but will do for now
        InstanceType = object

    try:
        from mx.DateTime import DateTimeType as mxDateTimeType, \
            DateTimeDeltaType as mxDateTimeDeltaType
    except ImportError:
        mxDateTimeType = None
        mxDateTimeDeltaType = None

    try:
        from DateTime import DateTime as zopeDateTime
    except ImportError:
        zopeDateTimeType = None
    else:
        zopeDateTimeType = type(zopeDateTime())

    try:
        import Sybase
        NumericType = Sybase.NumericType
    except ImportError:
        NumericType = None

    ########################################
    # Quoting
    ########################################

    sqlStringReplace = [
        ("'", "''"),
        ('\\', '\\\\'),
        ('\000', '\\0'),
        ('\b', '\\b'),
        ('\n', '\\n'),
        ('\r', '\\r'),
        ('\t', '\\t'),
    ]

    class ConverterRegistry:

        def __init__(self):
            self.basic = {}
            self.klass = {}

        def registerConverter(self, typ, func):
            if type(typ) is ClassType:
                self.klass[typ] = func
            else:
                self.basic[typ] = func

        if PY2:
            def lookupConverter(self, value, default=None):
                if type(value) is InstanceType:
                    # lookup on klasses dict
                    return self.klass.get(value.__class__, default)
                return self.basic.get(type(value), default)
        else:
            def lookupConverter(self, value, default=None):
                # python 3 doesn't have classic classes, so everything's
                # in self.klass due to comparison order in registerConvertor
                return self.klass.get(value.__class__, default)

    converters = ConverterRegistry()
    registerConverter = converters.registerConverter
    lookupConverter = converters.lookupConverter

    def StringLikeConverter(value, db):
        if isinstance(value, array):
            try:
                value = value.tounicode()
            except ValueError:
                value = value.tostring()
        elif isinstance(value, buffer_type):
            value = str(value)

        if db in ('mysql', 'postgres'):
            for orig, repl in sqlStringReplace:
                value = value.replace(orig, repl)
        elif db in ('sqlite', 'firebird', 'sybase', 'maxdb', 'mssql'):
            value = value.replace("'", "''")
        else:
            assert 0, "Database %s unknown" % db
        if (db == 'postgres') and ('\\' in value):
            return "E'%s'" % value
        return "'%s'" % value

    registerConverter(str, StringLikeConverter)
    if PY2:
        # noqa for flake8 & python3
        registerConverter(unicode, StringLikeConverter)  # noqa
    registerConverter(array, StringLikeConverter)
    if PY2:
        registerConverter(bytearray, StringLikeConverter)
        registerConverter(buffer_type, StringLikeConverter)
    else:
        registerConverter(memoryview, StringLikeConverter)

    def IntConverter(value, db):
        return repr(int(value))

    registerConverter(int, IntConverter)

    def LongConverter(value, db):
        return str(value)

    if sys.version_info[0] < 3:
        # noqa for flake8 & python3
        registerConverter(long, LongConverter)  # noqa

    if NumericType:
        registerConverter(NumericType, IntConverter)

    def BoolConverter(value, db):
        if db == 'postgres':
            if value:
                return "'t'"
            else:
                return "'f'"
        else:
            if value:
                return '1'
            else:
                return '0'

    registerConverter(bool, BoolConverter)

    def FloatConverter(value, db):
        return repr(value)

    registerConverter(float, FloatConverter)

    def NoneConverter(value, db):
        return "NULL"

    registerConverter(NoneType, NoneConverter)

    def SequenceConverter(value, db):
        return "(%s)" % ", ".join([sqlrepr(v, db) for v in value])

    registerConverter(tuple, SequenceConverter)
    registerConverter(list, SequenceConverter)
    registerConverter(dict, SequenceConverter)
    registerConverter(set, SequenceConverter)
    registerConverter(frozenset, SequenceConverter)

    if hasattr(time, 'struct_time'):
        def StructTimeConverter(value, db):
            return time.strftime("'%Y-%m-%d %H:%M:%S'", value)

        registerConverter(time.struct_time, StructTimeConverter)

    def DateTimeConverter(value, db):
        return "'%04d-%02d-%02d %02d:%02d:%02d'" % (
            value.year, value.month, value.day,
            value.hour, value.minute, value.second)

    def DateTimeConverterMS(value, db):
        return "'%04d-%02d-%02d %02d:%02d:%02d.%06d'" % (
            value.year, value.month, value.day,
            value.hour, value.minute, value.second, value.microsecond)

    registerConverter(datetime.datetime, DateTimeConverterMS)

    def DateConverter(value, db):
        return "'%04d-%02d-%02d'" % (value.year, value.month, value.day)

    registerConverter(datetime.date, DateConverter)

    def TimeConverter(value, db):
        return "'%02d:%02d:%02d'" % (value.hour, value.minute, value.second)

    def TimeConverterMS(value, db):
        return "'%02d:%02d:%02d.%06d'" % (value.hour, value.minute,
                                          value.second, value.microsecond)

    registerConverter(datetime.time, TimeConverterMS)

    if mxDateTimeType:
        def mxDateTimeConverter(value, db):
            return "'%s'" % value.strftime("%Y-%m-%d %H:%M:%S")

        registerConverter(mxDateTimeType, mxDateTimeConverter)

        def mxTimeConverter(value, db):
            return "'%s'" % value.strftime("%H:%M:%S")

        registerConverter(mxDateTimeDeltaType, mxTimeConverter)

    if zopeDateTimeType:
        def zopeDateTimeConverter(value, db):
            return "'%s'" % value.strftime("%Y-%m-%d %H:%M:%S")

        registerConverter(zopeDateTimeType, zopeDateTimeConverter)

    def DecimalConverter(value, db):
        return value.to_eng_string()

    registerConverter(Decimal, DecimalConverter)

    def TimedeltaConverter(value, db):

        return """INTERVAL '%d days %d seconds'""" % \
            (value.days, value.seconds)

    registerConverter(datetime.timedelta, TimedeltaConverter)

    def sqlrepr(obj, db=None):
        try:
            reprFunc = obj.__sqlrepr__
        except AttributeError:
            converter = lookupConverter(obj)
            if converter is None:
                raise ValueError("Unknown SQL builtin type: %s for %s" %
                                 (type(obj), repr(obj)))
            return converter(obj, db)
        else:
            return reprFunc(db)

    def quote_str(s, db):
        if (db == 'postgres') and ('\\' in s):
            return "E'%s'" % s
        return "'%s'" % s

    def unquote_str(s):
        if s[:2].upper().startswith("E'") and s.endswith("'"):
            return s[2:-1]
        elif s.startswith("'") and s.endswith("'"):
            return s[1:-1]
        else:
            return s