'Creates a record (Storage) from a <resource> element and validates
it
@param table: the database table
@param element: the element
@param original: the original record
@param files: dict of attached upload files
@param postprocess: post-process hook (xml_post_parse)
@param skip: fields to skip'

@classmethod
def record(cls, table, element, original=None, files=None, skip=[], postprocess=None):

    valid = True
    record = Storage()
    db = current.db
    auth = current.auth
    utable = auth.settings.table_user
    gtable = auth.settings.table_group
    UID = cls.UID
    uid = None
    if ((UID in table.fields) and (UID not in skip)):
        uid = current.xml.import_uid(element.get(UID, None))
        if uid:
            record[UID] = uid
    ATTRIBUTE = cls.ATTRIBUTE
    FIELD = ATTRIBUTE['field']
    VALUE = ATTRIBUTE['value']
    ERROR = ATTRIBUTE['error']
    DELETED = cls.DELETED
    APPROVED = cls.APPROVED
    IGNORE_FIELDS = cls.IGNORE_FIELDS
    OGROUP = cls.OGROUP
    USER_FIELDS = (cls.CUSER, cls.MUSER, cls.OUSER)
    deleted = False
    for f in cls.ATTRIBUTES_TO_FIELDS:
        if (f == DELETED):
            if ((f in table) and (element.get(f, 'false').lower() == 'true')):
                record[f] = deleted = True
                replaced_by = element.get(ATTRIBUTE['replaced_by'], None)
                if replaced_by:
                    record[cls.REPLACEDBY] = replaced_by
                break
            else:
                continue
        if (f == APPROVED):
            if ('approved_by' in table):
                approved = element.get(f)
                if approved:
                    if (approved.lower() == 'false'):
                        record['approved_by'] = None
                    elif (table['approved_by'].default is None):
                        auth.permission.set_default_approver(table, force=True)
                elif (table['approved_by'].default is None):
                    auth.permission.set_default_approver(table)
            continue
        if ((f in IGNORE_FIELDS) or (f in skip)):
            continue
        elif (f in USER_FIELDS):
            v = element.get(f, None)
            if (v and utable and ('email' in utable)):
                query = (utable.email == v)
                user = db(query).select(utable.id, limitby=(0, 1)).first()
                if user:
                    record[f] = user.id
            continue
        elif (f == OGROUP):
            v = element.get(f, None)
            if (v and gtable and ('role' in gtable)):
                query = (gtable.role == v)
                role = db(query).select(gtable.id, limitby=(0, 1)).first()
                if role:
                    record[f] = role.id
            continue
        if hasattr(table, f):
            v = value = element.get(f, None)
            if (value is not None):
                field_type = str(table[f].type)
                if (field_type in ('datetime', 'date', 'time')):
                    (value, error) = cls._dtparse(v, field_type=field_type)
                else:
                    try:
                        (value, error) = s3_validate(table, f, v, original)
                    except AttributeError:
                        continue
                if error:
                    element.set(ERROR, ('%s:    %s' % (f, error)))
                    valid = False
                    continue
                record[f] = value
    if (deleted and uid):
        return record
    xml_decode = cls.xml_decode
    for child in element.findall('data'):
        error = None
        f = child.get(FIELD, None)
        if ((not f) or (not hasattr(table, f))):
            continue
        if ((f in IGNORE_FIELDS) or (f in skip)):
            continue
        field_type = str(table[f].type)
        if (field_type in ('id', 'blob')):
            continue
        elif (field_type == 'upload'):
            download_url = child.get(ATTRIBUTE['url'])
            filename = child.get(ATTRIBUTE['filename'])
            upload = None
            if (filename and files and (filename in files)):
                upload = files[filename]
            elif (download_url == 'local'):
                value = filename
            elif download_url:
                if (not isinstance(download_url, str)):
                    try:
                        download_url = download_url.encode('utf-8')
                    except UnicodeEncodeError:
                        continue
                if (not filename):
                    filename = (download_url.split('?')[0] or 'upload.bin')
                try:
                    upload = urllib2.urlopen(download_url)
                except IOError:
                    continue
            if upload:
                if (not isinstance(filename, str)):
                    try:
                        filename = filename.encode('utf-8')
                    except UnicodeEncodeError:
                        continue
                field = table[f]
                value = field.store(upload, filename)
            elif (download_url != 'local'):
                continue
        else:
            value = child.get(VALUE, None)
        skip_validation = False
        is_text = (field_type in ('string', 'text'))
        if (value is None):
            if (field_type == 'password'):
                value = child.text
                skip_validation = True
                decode_value = False
            else:
                decode_value = (not is_text)
                value = xml_decode(child.text)
        else:
            decode_value = True
        if ((value is None) and is_text):
            value = ''
        elif ((value == '') and (not is_text)):
            value = None
        if (value is not None):
            if (field_type in ('datetime', 'date', 'time')):
                (value, error) = cls._dtparse(value, field_type=field_type)
                skip_validation = True
                v = value
            elif (field_type == 'upload'):
                pass
            elif (isinstance(value, basestring) and len(value) and decode_value):
                try:
                    _value = json.loads(value)
                    if (_value != float('inf')):
                        value = _value
                except:
                    error = sys.exc_info()[1]
            if (not skip_validation):
                if (not isinstance(value, (basestring, list, tuple, bool))):
                    v = str(value)
                elif isinstance(value, basestring):
                    v = value.encode('utf-8')
                else:
                    v = value
                filename = None
                try:
                    if ((field_type == 'upload') and (download_url != 'local') and table[f].requires):
                        (filename, stream) = field.retrieve(value)
                        v = filename
                        if isinstance(stream, basestring):
                            stream = open(stream, 'rb')
                        if (not error):
                            dummy = Storage({'filename': filename, 'file': stream})
                            (dummy, error) = s3_validate(table, f, dummy, original)
                    elif (field_type == 'password'):
                        v = str(value)
                        (value, error) = s3_validate(table, f, v)
                    else:
                        (value, error) = s3_validate(table, f, v, original)
                except AttributeError:
                    continue
                except IOError:
                    if filename:
                        error = ('Cannot    read    uploaded    file:    %s' % filename)
                    else:
                        error = sys.exc_info()[1]
                except:
                    error = sys.exc_info()[1]
            child.set(VALUE, s3_unicode(v))
            if error:
                child.set(ERROR, s3_unicode(('%s:    %s' % (f, error))))
                valid = False
                continue
            record[f] = value
    if valid:
        if postprocess:
            postprocess(element, record)
        return record
    else:
        return None
