'Parse and validate an XML element and add it as new item
to the job.
@param element: the element
@param original: the original DB record (if already available,
will otherwise be looked-up by this function)
@param components: a dictionary of components (as in S3Resource)
to include in the job (defaults to all
defined components)
@param parent: the parent item (if this is a component)
@param joinby: the component join key(s) (if this is a component)
@return: a unique identifier for the new item, or None if there
was an error. self.error contains the last error, and
self.error_tree an element tree with all failing elements
including error attributes.'

def add_item(self, element=None, original=None, components=None, parent=None, joinby=None):

    if (element in self.elements):
        return self.elements[element]
    item = S3ImportItem(self)
    item_id = item.item_id
    self.items[item_id] = item
    if (element is not None):
        self.elements[element] = item_id
    if (not item.parse(element, original=original, files=self.files)):
        self.error = item.error
        item.accepted = False
        if (parent is None):
            self.error_tree.append(deepcopy(item.element))
    else:
        table = item.table
        components = current.s3db.get_components(table, names=components)
        cnames = Storage()
        cinfos = Storage()
        for alias in components:
            component = components[alias]
            pkey = component.pkey
            if component.linktable:
                ctable = component.linktable
                fkey = component.lkey
            else:
                ctable = component.table
                fkey = component.fkey
            ctablename = ctable._tablename
            if (ctablename in cnames):
                cnames[ctablename].append(alias)
            else:
                cnames[ctablename] = [alias]
            cinfos[(ctablename, alias)] = Storage(component=component, ctable=ctable, pkey=pkey, fkey=fkey, first=True)
        add_item = self.add_item
        xml = current.xml
        UID = xml.UID
        for celement in xml.components(element, names=cnames.keys()):
            ctablename = celement.get(xml.ATTRIBUTE.name, None)
            if ((not ctablename) or (ctablename not in cnames)):
                continue
            calias = celement.get(xml.ATTRIBUTE.alias, None)
            if (calias is None):
                aliases = cnames[ctablename]
                if (len(aliases) == 1):
                    calias = aliases[0]
                else:
                    calias = ctablename.split('_', 1)[1]
            if ((ctablename, calias) not in cinfos):
                continue
            else:
                cinfo = cinfos[(ctablename, calias)]
            component = cinfo.component
            ctable = cinfo.ctable
            pkey = cinfo.pkey
            fkey = cinfo.fkey
            original = None
            if (not component.multiple):
                if (not cinfo.first):
                    continue
                cinfo.first = False
                if item.id:
                    db = current.db
                    query = ((table.id == item.id) & (table[pkey] == ctable[fkey]))
                    if (UID in ctable.fields):
                        row = db(query).select(ctable[UID], limitby=(0, 1)).first()
                        if row:
                            original = row[UID]
                    else:
                        original = db(query).select(ctable.ALL, limitby=(0, 1)).first()
            item_id = add_item(element=celement, original=original, parent=item, joinby=(pkey, fkey))
            if (item_id is None):
                item.error = self.error
                self.error_tree.append(deepcopy(item.element))
            else:
                citem = self.items[item_id]
                citem.parent = item
                item.components.append(citem)
        lookahead = self.lookahead
        directory = self.directory
        table = item.table
        tree = self.tree
        if (tree is not None):
            fields = [table[f] for f in table.fields]
            rfields = filter(s3_has_foreign_key, fields)
            item.references = lookahead(element, table=table, fields=rfields, tree=tree, directory=directory)
            for reference in item.references:
                entry = reference.entry
                if (entry and (entry.element is not None)):
                    if (not entry.item_id):
                        item_id = add_item(element=entry.element)
                        if item_id:
                            entry.update(item_id=item_id)
        if (parent is not None):
            entry = Storage(item_id=parent.item_id, element=parent.element, tablename=parent.tablename)
            item.references.append(Storage(field=joinby, entry=entry))
        data = item.data
        deleted = data.get(xml.DELETED, False)
        if deleted:
            field = xml.REPLACEDBY
            replaced_by = data.get(field, None)
            if replaced_by:
                rl = lookahead(element, table=table, tree=tree, directory=directory, lookup=(table, field, replaced_by))
                if rl:
                    entry = rl[0].entry
                    if ((entry.element is not None) and (not entry.item_id)):
                        item_id = add_item(element=entry.element)
                        if item_id:
                            entry.item_id = item_id
                    item.references.append(Storage(field=field, entry=entry))
    return item.item_id
