'Convert a table-form CSV source into an element tree, consisting of
<table name="format">, <row> and <col field="fieldname"> elements.
@param source: the source (file-like object)
@param resourcename: the resource name
@param extra_data: dict of extra cols {key:value} to add to each row
@param hashtags: dict of hashtags for extra cols {key:hashtag}
@param delimiter: delimiter for values
@param quotechar: quotation character
@todo: add a character encoding parameter to skip the guessing'

@classmethod
def csv2tree(cls, source, resourcename=None, extra_data=None, hashtags=None, delimiter=',', quotechar='"'):

    import csv
    csv.field_size_limit(((2 ** 20) * 100))
    ATTRIBUTE = cls.ATTRIBUTE
    FIELD = ATTRIBUTE.field
    HASHTAG = ATTRIBUTE.hashtag
    TAG = cls.TAG
    COL = TAG.col
    SubElement = etree.SubElement
    root = etree.Element(TAG.table)
    if (resourcename is not None):
        root.set(ATTRIBUTE.name, resourcename)
    def add_col(row, key, value, hashtags=None):
        col = SubElement(row, COL)
        col.set(FIELD, s3_unicode(key))
        if hashtags:
            hashtag = hashtags.get(key)
            if (hashtag and hashtag[1:]):
                col.set(HASHTAG, hashtag)
        if value:
            text = s3_unicode(value).strip()
            if (text[:6].lower() not in ('null', '<null>')):
                try:
                    col.text = text
                except MemoryError:
                    current.log.error(('S3XML:    Unable    to    set    value    for    key    %s:    MemoryError' % key))
        else:
            col.text = ''
    def utf_8_encode(source):
        '\n                                                                UTF-8-recode    the    source    line    by    line,    guessing    the    character\n                                                                encoding    of    the    source.\n                                                '
        encodings = ('utf-8-sig', 'iso-8859-1')
        e = encodings[0]
        for line in source:
            if e:
                try:
                    (yield unicode(line, e, 'strict').encode('utf-8'))
                except:
                    pass
                else:
                    continue
            for encoding in encodings:
                try:
                    (yield unicode(line, encoding, 'strict').encode('utf-8'))
                except:
                    continue
                else:
                    e = encoding
                    break
    hashtags = (dict(hashtags) if hashtags else {})
    try:
        import StringIO
        if (not isinstance(source, StringIO.StringIO)):
            source = utf_8_encode(source)
        reader = csv.DictReader(source, delimiter=delimiter, quotechar=quotechar)
        ROW = TAG.row
        for (i, r) in enumerate(reader):
            if (not any(r.values())):
                continue
            if (i == 0):
                items = dict(((k, s3_unicode(v.strip())) for (k, v) in r.items() if (k and v and v.strip())))
                if all(((v[0] == '#') for v in items.values())):
                    hashtags.update(items)
                    continue
            row = SubElement(root, ROW)
            for k in r:
                if k:
                    add_col(row, k, r[k], hashtags=hashtags)
            if extra_data:
                for key in extra_data:
                    if (key not in r):
                        add_col(row, key, extra_data[key], hashtags=hashtags)
    except csv.Error:
        e = sys.exc_info()[1]
        raise HTTP(400, body=cls.json_message(False, 400, e))
    return etree.ElementTree(root)
