# Source code for sqlobject.styles

    import re

    __all__ = ["Style", "MixedCaseUnderscoreStyle", "DefaultStyle",
               "MixedCaseStyle"]

    class Style(object):

        """
        The base Style class, and also the simplest implementation.  No
        translation occurs -- column names and attribute names match,
        as do class names and table names (when using auto class or
        schema generation).
        """

        def __init__(self, pythonAttrToDBColumn=None,
                     dbColumnToPythonAttr=None,
                     pythonClassToDBTable=None,
                     dbTableToPythonClass=None,
                     idForTable=None,
                     longID=False):
            if pythonAttrToDBColumn:
                self.pythonAttrToDBColumn = \
                    lambda a, s=self: pythonAttrToDBColumn(s, a)
            if dbColumnToPythonAttr:
                self.dbColumnToPythonAttr = \
                    lambda a, s=self: dbColumnToPythonAttr(s, a)
            if pythonClassToDBTable:
                self.pythonClassToDBTable = \
                    lambda a, s=self: pythonClassToDBTable(s, a)
            if dbTableToPythonClass:
                self.dbTableToPythonClass = \
                    lambda a, s=self: dbTableToPythonClass(s, a)
            if idForTable:
                self.idForTable = lambda a, s=self: idForTable(s, a)
            self.longID = longID

        def pythonAttrToDBColumn(self, attr):
            return attr

        def dbColumnToPythonAttr(self, col):
            return col

        def pythonClassToDBTable(self, className):
            return className

        def dbTableToPythonClass(self, table):
            return table

        def idForTable(self, table):
            if self.longID:
                return self.tableReference(table)
            else:
                return 'id'

        def pythonClassToAttr(self, className):
            return lowerword(className)

        def instanceAttrToIDAttr(self, attr):
            return attr + "ID"

        def instanceIDAttrToAttr(self, attr):
            return attr[:-2]

        def tableReference(self, table):
            return table + "_id"

    class MixedCaseUnderscoreStyle(Style):

        """
        This is the default style.  Python attributes use mixedCase,
        while database columns use underscore_separated.
        """

        def pythonAttrToDBColumn(self, attr):
            return mixedToUnder(attr)

        def dbColumnToPythonAttr(self, col):
            return underToMixed(col)

        def pythonClassToDBTable(self, className):
            return className[0].lower() \
                + mixedToUnder(className[1:])

        def dbTableToPythonClass(self, table):
            return table[0].upper() \
                + underToMixed(table[1:])

        def pythonClassToDBTableReference(self, className):
            return self.tableReference(self.pythonClassToDBTable(className))

        def tableReference(self, table):
            return table + "_id"

    DefaultStyle = MixedCaseUnderscoreStyle

    class MixedCaseStyle(Style):

        """
        This style leaves columns as mixed-case, and uses long
        ID names (like ProductID instead of simply id).
        """

        def pythonAttrToDBColumn(self, attr):
            return capword(attr)

        def dbColumnToPythonAttr(self, col):
            return lowerword(col)

        def dbTableToPythonClass(self, table):
            return capword(table)

        def tableReference(self, table):
            return table + "ID"

    defaultStyle = DefaultStyle()

    def getStyle(soClass, dbConnection=None):
        if dbConnection is None:
            if hasattr(soClass, '_connection'):
                dbConnection = soClass._connection
        if hasattr(soClass.sqlmeta, 'style') and soClass.sqlmeta.style:
            return soClass.sqlmeta.style
        elif dbConnection and dbConnection.style:
            return dbConnection.style
        else:
            return defaultStyle

    ############################################################
    # Text utilities
    ############################################################

    _mixedToUnderRE = re.compile(r'[A-Z]+')

    def mixedToUnder(s):
        if s.endswith('ID'):
            return mixedToUnder(s[:-2] + "_id")
        trans = _mixedToUnderRE.sub(mixedToUnderSub, s)
        if trans.startswith('_'):
            trans = trans[1:]
        return trans

    def mixedToUnderSub(match):
        m = match.group(0).lower()
        if len(m) > 1:
            return '_%s_%s' % (m[:-1], m[-1])
        else:
            return '_%s' % m

    def capword(s):
        return s[0].upper() + s[1:]

    def lowerword(s):
        return s[0].lower() + s[1:]

    _underToMixedRE = re.compile('_.')

    def underToMixed(name):
        if name.endswith('_id'):
            return underToMixed(name[:-3] + "ID")
        return _underToMixedRE.sub(lambda m: m.group(0)[1].upper(),
                                   name)