Add a fact to the iobject. If a fact term for the iobject type
        with the given fact data type and source (CYBOX, etc.) does not
        exist yet, it is created.

def add_fact(self,
                 fact_term_name,
                 fact_term_attribute,
                 fact_dt_name='String',
                 fact_dt_namespace_name=None,
                 fact_dt_namespace_uri=DINGOS_NAMESPACE_URI,
                 fact_dt_kind=FactDataType.UNKNOWN_KIND,
                 values=None,
                 value_iobject_id=None,
                 value_iobject_ts=None,
                 node_id_name='',
                 is_attribute=False,
                 ns_uri_dict=None,
                 namespaces=None,
                 top_level_namespace=None):
        """
        Add a fact to the iobject. If a fact term for the iobject type
        with the given fact data type and source (CYBOX, etc.) does not
        exist yet, it is created.

        """


        if not values:
            values = []

        if not namespaces:
            namespaces = []

        if not ns_uri_dict:
            ns_uri_dict = None

        # get or create fact_term
        fact_term, created = get_or_create_fact_term(iobject_family_name=self.iobject_family.name,
                                                     fact_term_name=fact_term_name,
                                                     fact_term_attribute=fact_term_attribute,
                                                     iobject_type_name=self.iobject_type.name,
                                                     iobject_type_namespace_uri=self.iobject_type.namespace.uri,
                                                     fact_dt_name=fact_dt_name,
                                                     fact_dt_kind=fact_dt_kind,
                                                     fact_dt_namespace_name=fact_dt_namespace_name,
                                                     fact_dt_namespace_uri=fact_dt_namespace_uri,
                                                     dingos_class_map=self._DCM)



        # get or create fact object

        fact_obj, created = get_or_create_fact(fact_term,
                                               fact_dt_name=fact_dt_name,
                                               fact_dt_namespace_uri=fact_dt_namespace_uri,
                                               values=values,
                                               value_iobject_id=value_iobject_id,
                                               value_iobject_ts=value_iobject_ts,
                                               )



        # get or create node identifier

        node_id, created = dingos_class_map['NodeID'].objects.get_or_create(name=node_id_name)

        # If this is an attribute: determine whether there is a fact for which this fact is
        # an attribute.

        node_id_name_components = node_id_name.split(':')

        attributed_io2f = None
        if node_id_name_components[-1] and node_id_name_components[-1][0] == 'A':
            attributed_node_id_name = ":".join(node_id_name_components[:-1])

            try:
                attributed_io2f = dingos_class_map['InfoObject2Fact'].objects.get(iobject=self,
                                                                                 node_id__name=attributed_node_id_name)
            except InfoObject2Fact.DoesNotExist:
                pass

        io2f = self._DCM['InfoObject2Fact'].objects.create(
            node_id=node_id,
            iobject=self,
            fact=fact_obj,
            attributed_fact=attributed_io2f)

        counter = 0
        io2f2n_list = []


        namespace_map_elts = FactTermNamespaceMap.objects.filter(fact_term=fact_term).order_by('id','namespaces_thru__position').values_list('id',
                                                                                             'namespaces_thru__position',
                                                                                             'namespaces_thru__namespace__uri')

        namespace_maps = []
        for k,g in itertools.groupby(namespace_map_elts,lambda x:x[0]):
            namespace_maps.append(list(g))


        namespace_map = None

        namespaces_uris = map(lambda x:x[0],namespaces)


        for i in namespace_maps:
            if namespaces_uris == map(lambda x:x[2],i):
                namespace_map = FactTermNamespaceMap.objects.get(id=i[0][0])


                break



        if not namespace_map:

            namespace_map = FactTermNamespaceMap.objects.create(fact_term=fact_term)
            for (ns_uri,ns_slug) in namespaces:

                if ns_uri:

                    if not (ns_uri in ns_uri_dict):
                        ns_uri_obj, created = self._DCM['DataTypeNameSpace'].objects.get_or_create(uri=ns_uri,defaults={'name':ns_slug})
                        ns_uri_obj_pk = ns_uri_obj.pk
                        ns_uri_dict[ns_uri]=ns_uri_obj_pk
                    else:
                        ns_uri_obj_pk = ns_uri_dict[ns_uri]

                    io2f2n_list.append(PositionalNamespace(fact_term_namespace_map=namespace_map,position=counter,namespace_id=ns_uri_obj_pk))

                counter +=1
                #self._DCM['IO2F2Namespace'].object.create(io2f=self,position=counter,namespace__pk=ns_uri_obj_pk)

            if len(io2f2n_list)!= 0:
                PositionalNamespace.objects.bulk_create(io2f2n_list)
            else:
                namespace_map.delete()
                namespace_map = None

        if namespace_map:
            io2f.namespace_map = namespace_map
            io2f.save()

        return io2f