text: """Module contains the RDFParser that can create a Vocabulary object out of a
  given ontology"""
  
  import uuid
  from enum import Enum
  from typing import List, Tuple
  
  import rdflib
  
  from filip.models.base import LogLevel
  from filip.semantics.ontology_parser.vocabulary_builder import VocabularyBuilder
  from filip.semantics.vocabulary import Source, IdType, \
      Vocabulary,RestrictionType, ObjectProperty, DataProperty, Relation, \
      TargetStatement, StatementType, DatatypeType, Datatype, Class, Individual
  
  
  specifier_base_iris = ["http://www.w3.org/2002/07/owl",
                         "http://www.w3.org/1999/02/22-rdf-syntax-ns",
                         "http://www.w3.org/XML/1998/namespace",
                         "http://www.w3.org/2001/XMLSchema",
                         "http://www.w3.org/2000/01/rdf-schema"]
  """
  Defines a set of base iris, that describe elements that belong to the 
  description language not the ontology itself
  """
  
  
  class Tags(str, Enum):
      """
      Collection of tags used as structures in ontologies, that were used more
      than once in the rdfparser code
      """
      rdf_type = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type',
      owl_intersection = 'http://www.w3.org/2002/07/owl#intersectionOf',
      owl_union = 'http://www.w3.org/2002/07/owl#unionOf',
      owl_one_of = 'http://www.w3.org/2002/07/owl#oneOf',
      owl_individual = 'http://www.w3.org/2002/07/owl#NamedIndividual',
      owl_on_class = 'http://www.w3.org/2002/07/owl#onClass',
      owl_on_data_range = 'http://www.w3.org/2002/07/owl#onDataRange'
  
  
  def get_iri_from_uriref(uriref: rdflib.URIRef) -> str:
      """Give an Uriref object, returns an iri
  
      Args:
          uriref: Object describing the iri
  
      Returns:
          str
      """
      return str(uriref)
  
  
  def get_base_out_of_iri(iri: str) -> str:
      """Give an iri, returns an the ontology base name
  
         Args:
             iri
  
         Returns:
             str
         """
      if "#" in iri:
          index = iri.find("#")
          return iri[:index]
      else:
          # for example if uri looks like:
          # http://webprotege.stanford.edu/RDwpQ8vbi7HaApq8VoqJUXH
          index = iri.rfind("/")
          return iri[:index]
  
  
  class RdfParser:
      """
      Class that parses a given source into a vocabulary.
      """
      def __init__(self):
          self.current_source = None
          """Current source which is parsed, used for Log entries"""
          self.current_class_iri = None
          """Iri of class which is currently parsed, used for Log entries"""
  
      def _add_logging_information(self, level: LogLevel,
                                   entity_type: IdType, entity_iri: str,
                                   msg: str):
          """Add an entry to the parsing log
  
          Args:
              level (LogLevel): severe, warning or info
              entity_type (IdType)
              entity_iri (str)
              msg (str): Message to inform the user about the occurred issue
  
          Returns:
              None
          """
          if self.current_source is not None:
              self.current_source.add_parsing_log_entry(level, entity_type,
                                                        entity_iri, msg)
  
      def parse_source_into_vocabulary(self, source: Source,
                                       vocabulary: Vocabulary) -> bool:
          """ Parse a Source into the given vocabulary
          Args:
              source (Source)
              vocabulary (Vocabulary)
  
          Returns:
              bool, True if success, False if Error occurred, as an invalid File
          """
  
          # if this is the predefined source don't parse it, just pretend it
          # was successful
          if source.predefined:
              return True
  
          voc_builder = VocabularyBuilder(vocabulary=vocabulary)
          g = rdflib.Graph()
  
          # format = rdflib.util.guess_format(source.source_path)
          voc_builder.add_source(source)
          voc_builder.set_current_source(source.id)
  
          g.parse(data=source.content, format="turtle")
  
          ontology_nodes = list(g.subjects(
              object=rdflib.term.URIRef("http://www.w3.org/2002/07/owl#Ontology"),
              predicate=rdflib.term.URIRef(Tags.rdf_type.value)))
  
          # a source may have no ontology iri defined
          # if wanted on this place more info about the ontology can be extracted
          if len(ontology_nodes) > 0:
              source.ontology_iri = get_iri_from_uriref(ontology_nodes[0])
  
          self.current_source = source
  
          self._parse_to_vocabulary(g, voc_builder)
  
          return True
  
      def _is_object_defined_by_other_source(self, a: rdflib.term,
                                             graph: rdflib.Graph) -> bool:
          """ Test if the term is defined outside the current source
  
          Args:
              a (rdflib.term): Term to check
              graph (rdflib.graph): graph extracted from source
  
          Returns:
              bool
          """
  
          # if an object is defined by an other source it carries the predicate
          # ("isDefinedBy"). Then don't parse the object
          defined_tags = list(graph.objects(
              subject=a, predicate=rdflib.term.URIRef(
                  "http://www.w3.org/2000/01/rdf-schema#isDefinedBy")))
          return len(defined_tags) > 0
  
      def _parse_to_vocabulary(self, graph: rdflib.Graph,
                               voc_builder: VocabularyBuilder):
          """Parse an graph that was extracted from a TTL file into the vocabulary
  
          Args:
              graph (rdflib.Graph)
              voc_builder (VocabularyBuilder): Builder object to manipulate a
                  vocabulary
  
          Returns:
              None
          """
  
          # OWLClasses
          for a in graph.subjects(
                  object=rdflib.term.URIRef(
                      "http://www.w3.org/2002/07/owl#Class"),
                  predicate=rdflib.term.URIRef(Tags.rdf_type.value)):
  
              if isinstance(a, rdflib.term.BNode):
                  pass
                  # owl:Class can also occure in complex target statements of
                  # relations as BNode, ignore it here
              else:
  
                  # defined in other source -> ignore
                  if self._is_object_defined_by_other_source(a, graph=graph):
                      continue
  
                  iri, label, comment = self._extract_annotations(graph, a)
                  c = Class(iri=iri, label=label, comment=comment)
                  voc_builder.add_class(class_=c)
  
          # Class properties
          found_class_iris = set()
          for class_node in graph.subjects(
                  predicate=rdflib.term.URIRef(
                      "http://www.w3.org/2000/01/rdf-schema#subClassOf")):
  
              class_iri = get_iri_from_uriref(class_node)
              found_class_iris.add(class_iri)
  
          for class_iri in found_class_iris:
              # parent class / relation parsing
              for sub in graph.objects(
                      subject=rdflib.term.URIRef(class_iri),
                      predicate=rdflib.term.URIRef
                          ('http://www.w3.org/2000/01/rdf-schema#subClassOf')):
                  self.current_class_iri = class_iri  # used only for logging
                  self._parse_subclass_term(graph=graph,
                                            voc_builder=voc_builder,
                                            node=sub,
                                            class_iri=class_iri)
  
          # OWlObjectProperties
          for a in graph.subjects(
                  object=rdflib.term.URIRef(
                      "http://www.w3.org/2002/07/owl#ObjectProperty"),
                  predicate=rdflib.term.URIRef(Tags.rdf_type.value)):
  
              if isinstance(a, rdflib.term.BNode):
                  self._add_logging_information(LogLevel.WARNING,
                                                IdType.object_property,
                                                "unknown",
                                                "Found unparseable statement")
  
              else:
                  # defined in other source -> ignore
                  if self._is_object_defined_by_other_source(a, graph):
                      continue
  
                  iri, label, comment = self._extract_annotations(graph, a)
  
                  obj_prop = ObjectProperty(iri=iri, label=label, comment=comment)
                  voc_builder.add_object_property(obj_prop)
                  # extract inverse properties, it can be multiple but only
                  # URIRefs allowed no union/intersection
                  for inverse_iri_node in graph.objects(subject=a,
                          predicate=rdflib.term.URIRef(
                          'http://www.w3.org/2002/07/owl#inverseOf')):
                      if isinstance(inverse_iri_node, rdflib.term.BNode):
                          self._add_logging_information(
                              LogLevel.CRITICAL, IdType.object_property, iri,
                              "Complex inverseProperty statements aren't allowed")
                      else:
                          inverse_iri = get_iri_from_uriref(inverse_iri_node)
                          obj_prop.add_inverse_property_iri(inverse_iri)
  
          # OWlDataProperties
          for a in graph.subjects(
                  object=rdflib.term.URIRef(
                      "http://www.w3.org/2002/07/owl#DatatypeProperty"),
                  predicate=rdflib.term.URIRef(Tags.rdf_type.value)):
  
              if isinstance(a, rdflib.term.BNode):
                  self._add_logging_information(LogLevel.WARNING,
                                                IdType.data_property, "unknown",
                                               "Found unparseable statement")
  
              else:
                  # defined in other source -> ignore
                  if self._is_object_defined_by_other_source(a, graph):
                      continue
  
                  iri, label, comment = self._extract_annotations(graph, a)
  
                  data_prop = DataProperty(iri=iri, label=label, comment=comment)
                  voc_builder.add_data_property(data_prop)
  
          # OWLDataTypes
          # only the custom created datatype_catalogue are listed in the file,
          # the predefined are automatically added at the start
          # of post processing
          for a in graph.subjects(
                  object=rdflib.term.URIRef(
                      "http://www.w3.org/2000/01/rdf-schema#Datatype"),
                  predicate=rdflib.term.URIRef(Tags.rdf_type.value)):
  
              if isinstance(a, rdflib.term.BNode):
                  # self._add_logging_information(LogLevel.WARNING,
                  #                              IdType.datatype, "unknown",
                  #                              "Found unparseable statement")
                  pass
                  #e.g: :
                  # customDataType4 rdf:type rdfs:Datatype ;
                  # owl:equivalentClass [ rdf:type rdfs:Datatype ;....
                  # the second Datatype triggers this if condition,
                  # but we can ignore this statement
  
              else:
                  # defined in other source -> ignore
                  if self._is_object_defined_by_other_source(a, graph):
                      continue
  
                  iri, label, comment = self._extract_annotations(graph, a)
  
                  datatype = Datatype(iri=iri, label=label, comment=comment)
                  voc_builder.add_datatype(datatype=datatype)
  
                  # a datatype can be empty -> use string
                  # a datatype can have multiple equivalent classes
                  # (predefined types) -> ignore for now
                  # a datatype can contain an enum of possible values ->
                  # most interesting
                  # under the predicate owl:equivalentClass is than a
                  # list(first, rest, nil) under the pred.
                  # oneOf with the values
  
                  enum_values = []
                  for equivalent_class in graph.objects(
                          subject=a,
                          predicate=rdflib.term.URIRef(
                              "http://www.w3.org/2002/07/owl#equivalentClass")):
  
                      if isinstance(equivalent_class, rdflib.term.URIRef):
                          # points to an other defined datatype, ignore
                          pass
                      else:
                          # is a bNode and points to owl:oneOf
                          enum_literals = self.\
                              _extract_objects_out_of_single_combination(
                                  graph, equivalent_class, accept_and=False,
                                  accept_or=False, accept_one_of=True)
                          for literal in enum_literals:
                              enum_values.append(str(literal))
                  datatype.enum_values = enum_values
                  if len(enum_values) > 0:
                      datatype.type = DatatypeType.enum
                  else:
                      datatype.type = DatatypeType.string
  
          # OWLIndividuals
  
          for a in graph.subjects(
                  object=rdflib.term.URIRef(Tags.owl_individual.value),
                  predicate=rdflib.term.URIRef(Tags.rdf_type.value)):
  
              if isinstance(a, rdflib.term.BNode):
                  self._add_logging_information(LogLevel.WARNING,
                                                IdType.individual, "unknown",
                                               "Found unparseable statement")
  
              else:
                  # defined in other source -> ignore
                  if self._is_object_defined_by_other_source(a, graph):
                      continue
  
                  iri, label, comment = self._extract_annotations(graph, a)
                  objects = graph.objects(subject=a,
                                          predicate=
                                          rdflib.term.URIRef(Tags.rdf_type.value))
                  # superclasses = types
                  types = []
                  for object in objects:
                      if not object == \
                             rdflib.term.URIRef(Tags.owl_individual.value):
                          types.extend(self.
                              _extract_objects_out_of_layered_combination(
                                          graph, object, True, False))
  
                  individual = Individual(iri=iri, label=label, comment=comment)
                  for type in types:
                      individual.parent_class_iris.append(
                          get_iri_from_uriref(type))
                  voc_builder.add_individual(individual=individual)
  
          # As seen for example in the bricks ontology an individual can be
          # declared with :individual1 rdf:type :Class1
          # this type of declaration is hard to completly detect
          # we need to see that the object is a class iri and not a specifier iri.
          # as we may not have loaded all dependencies we can not simply look it
          # up in vocabulary
          # -> getbase uri of statement and filter all known specifier uris
          for sub in graph.subjects(
                  predicate=rdflib.term.URIRef(Tags.rdf_type.value)):
              for obj in graph.objects(subject=sub,
                                       predicate=
                                       rdflib.term.URIRef(Tags.rdf_type.value)):
  
                  if isinstance(obj, rdflib.term.BNode):
                      continue
                  obj_iri = get_iri_from_uriref(obj)
  
                  obj_base_iri = get_base_out_of_iri(iri=obj_iri)
                  if obj_base_iri not in specifier_base_iris:
                      iri, label, comment = \
                          self._extract_annotations(graph, sub)
                      if not voc_builder.entity_is_known(iri):
                          iri, label, comment = \
                              self._extract_annotations(graph, sub)
                          individual = Individual(iri=iri,
                                                  label=label,
                                                  comment=comment)
                          individual.parent_class_iris.append(obj_iri)
                          voc_builder.add_individual(individual)
  
      def _extract_annotations(self, graph: rdflib.Graph,
                               node: rdflib.term.URIRef) -> Tuple[str, str, str]:
          """ Extract out of a node term the owl annotations (iri, label, comment)
  
          Args:
              graph (rdflib.graph): Graph describing ontology
              node (rdflib.term.URIRef): Entity node
  
          Returns:
              [str,str,str]: [iri, label, comment]
          """
          iri = str(node)
          label = graph.label(node).title()
          comment = graph.comment(node).title()
  
          return iri, label, comment
  
      def _parse_subclass_term(self, graph: rdflib.Graph,
                               voc_builder: VocabularyBuilder,
                               node: rdflib.term, class_iri: str):
          """Parse a subclass term of the given node and class_iri
  
          Args:
              graph (rdflib.graph): Graph describing ontology
              vocabulary (Vocabulary): Vocabualry to parse into
              node (rdflib.term)
              class_iri (str)
  
          Returns:
              None
          """
  
          # class could have been only defined in other source, than no class
          # is defined, but as we have found a relation for a class, the class
          # needs to exist
          if class_iri not in voc_builder.vocabulary.classes:
              voc_builder.add_class(class_=Class(iri=class_iri))
  
          # node can be 1 of 3 things:
          #   - a parentclass statment -> UriRef
          #   - a relation statment -> BNode
          #   - an intersection of parentclasses ,
          #   relations and intersections -> BNode
          if isinstance(node, rdflib.term.BNode):
              # sub has no IRI and is therefore a relation
  
              # extract the subpredicates and subobjects as statments
              # if node is a relation:
              #      in total there should be 3-4 statments:
              #      rdf:type pointing to owl:Restriction
              #      owl:onProperty pointing to a data or object property
              #      1-2 staments which values are exepted, this can point to an
              #      URIRef or BNode
  
              # if node is a intersection:
              #      it has the predicate owl:intersectionOf
              #      and a set of objects
  
              predicates = []
              objects = []
              for p in graph.predicates(subject=node):
                  predicates.append(p)
              for o in graph.objects(subject=node):
                  objects.append(o)
  
              # Combination of statements
              if rdflib.term.URIRef(Tags.owl_intersection.value) in predicates:
                  objects = self._extract_objects_out_of_single_combination(
                      graph, node, True, False)
                  for object in objects:
                      self._parse_subclass_term(graph=graph,
                                                voc_builder=voc_builder,
                                                node=object, class_iri=class_iri)
  
              elif rdflib.term.URIRef(Tags.owl_union.value) in predicates:
                  self._add_logging_information(
                      LogLevel.CRITICAL, IdType.class_, class_iri,
                      "Relation statements combined with or")
  
              elif rdflib.term.URIRef(Tags.owl_one_of.value) in predicates:
                  self._add_logging_information(
                      LogLevel.CRITICAL, IdType.class_, class_iri,
                      "Relation statements combined with oneOf")
  
              # Relation statement
              else:
  
                  additional_statements = {}
                  rdf_type = ""
                  owl_on_property = ""
  
                  for i in range(len(predicates)):
                      if predicates[i] == rdflib.term.URIRef(Tags.rdf_type.value):
                          rdf_type = get_iri_from_uriref(objects[i])
                      elif predicates[i] == rdflib.term.URIRef(
                              "http://www.w3.org/2002/07/owl#onProperty"):
                          owl_on_property = get_iri_from_uriref(objects[i])
                      else:
                          additional_statements[
                              get_iri_from_uriref(predicates[i])] = objects[i]
  
                  relation_is_ok = True
                  if not rdf_type == "http://www.w3.org/2002/07/owl#Restriction":
                      self._add_logging_information(
                          LogLevel.CRITICAL, IdType.class_, class_iri,
                          "Class has an unknown subClass statement")
                      relation_is_ok = False
  
                  if owl_on_property == "":
                      self._add_logging_information(
                          LogLevel.CRITICAL, IdType.class_, class_iri,
                          "Class has a relation without a property")
                      relation_is_ok = False
  
                  # object or data relation?
                  if relation_is_ok:
                      relation = None
                      id = uuid.uuid4().hex
                      # this id can and should be random. a class_iri can have a
                      # property_iri multiple times, to assign always the same id
                      # for the same relation is not worth the trouble
  
                      relation = Relation(property_iri=owl_on_property, id=id)
                      voc_builder.add_relation_for_class(class_iri, relation)
  
                      # go through the additional statement to figure out the
                      # targetIRI and the restrictionType/cardinality
                      self._parse_relation_type(graph, relation,
                                                additional_statements)
  
          # parent-class statement or empty list element
          else:
              # owlThing is the root object, but it is not declared as a class
              # in the file to prevent None pointer when looking up parents,
              # a class that has a parent owlThing simply has no parents
              if not get_iri_from_uriref(node) == \
                     "http://www.w3.org/1999/02/22-rdf-syntax-ns#nil":
                  # ignore empty lists
                  if not get_iri_from_uriref(node) == \
                         "http://www.w3.org/2002/07/owl#Thing":
                      voc_builder.vocabulary.\
                          get_class_by_iri(class_iri).parent_class_iris.\
                          append(get_iri_from_uriref(node))
  
      def _parse_relation_type(self, graph: rdflib.Graph,
                               relation: Relation, statements: {}):
          """
          Parse the relation type and depending on the result the
          cardinality or value of relation
          
          Args:
              graph: underlying ontology graph
              relation: Relation object into which the information are saved
              statements: Ontology statements concerning the relation
          
          Returns:
              None
          """
          treated_statements = []
          for statement in statements:
              if statement == "http://www.w3.org/2002/07/owl#someValuesFrom":
                  relation.restriction_type = RestrictionType.some
                  self._parse_relation_values(graph, relation,
                                              statements[statement])
              elif statement == "http://www.w3.org/2002/07/owl#allValuesFrom":
                  relation.restriction_type = RestrictionType.only
                  self._parse_relation_values(graph, relation,
                                              statements[statement])
              elif statement == "http://www.w3.org/2002/07/owl#hasValue":
                  relation.restriction_type = RestrictionType.value
                  # has Value can only point to a single value
                  self._parse_has_value(graph, relation,
                                        statements[statement])
              elif statement == "http://www.w3.org/2002/07/owl#maxCardinality":
                  relation.restriction_type = RestrictionType.max
                  self._parse_cardinality(graph, relation, statement,
                                          statements, treated_statements)
              elif statement == "http://www.w3.org/2002/07/owl#minCardinality":
                  relation.restriction_type = RestrictionType.min
                  self._parse_cardinality(graph, relation, statement,
                                          statements, treated_statements)
              elif statement == "http://www.w3.org/2002/07/owl#cardinality":
                  relation.restriction_type = RestrictionType.exactly
                  self._parse_cardinality(graph,  relation, statement,
                                          statements, treated_statements)
              elif statement == \
                      "http://www.w3.org/2002/07/owl#maxQualifiedCardinality":
                  relation.restriction_type = RestrictionType.max
                  self._parse_cardinality(graph, relation, statement,
                                          statements, treated_statements)
              elif statement == \
                      "http://www.w3.org/2002/07/owl#minQualifiedCardinality":
                  relation.restriction_type = RestrictionType.min
                  self._parse_cardinality(graph, relation, statement,
                                          statements, treated_statements)
              elif statement == \
                      "http://www.w3.org/2002/07/owl#qualifiedCardinality":
                  relation.restriction_type = RestrictionType.exactly
                  self._parse_cardinality(graph, relation, statement,
                                          statements, treated_statements)
  
              treated_statements.append(statement)
  
          for statement in statements:
              if statement not in treated_statements:
                  self._add_logging_information(
                    LogLevel.CRITICAL, IdType.class_, self.current_class_iri,
                    "Relation with property {} has an untreated restriction "
                    "{}".format(relation.property_iri, statement))
  
      def _parse_cardinality(self, graph: rdflib.Graph,
                             relation: Relation, statement, statements,
                             treated_statements):
          """Parse the cardinality of a relation
  
          Args:
              graph: underlying ontology graph
              relation: Relation object into which the information are saved
              statement: The statement that is actively treated
              statements: Ontology statements concerning the relation
              treated_statements: Statements that were already treated
  
          Returns:
              None
          """
          if Tags.owl_on_class.value in statements:
              relation.restriction_cardinality = str(statements[statement])
              target = statements[Tags.owl_on_class.value]
              self._parse_relation_values(graph, relation, target)
              treated_statements.append(Tags.owl_on_class.value)
          elif Tags.owl_on_data_range.value in statements:
              relation.restriction_cardinality = str(statements[statement])
              target = statements[Tags.owl_on_data_range.value]
              self._parse_relation_values(graph, relation, target)
              treated_statements.append(Tags.owl_on_data_range.value)
          else:
              # has From:
              # in File: owl:maxCardinality "1"^^xsd:nonNegativeInteger
              # e.g.: {'http://www.w3.org/2002/07/owl#maxCardinality':
              #        rdflib.term.Literal('1', datatype=
              #        rdflib.term.URIRef('
              #        http://www.w3.org/2001/XMLSchema#nonNegativeInteger'))}
  
              # in this case the file does not state a datarange that is allowed.
              # Therefore the target gets set to the universal string
  
              relation.restriction_cardinality = statements[statement].value
              datatype = "http://www.w3.org/2001/XMLSchema#string"
              target_statement = TargetStatement(type=StatementType.LEAF,
                                                 target_iri=datatype)
              relation.target_statement = target_statement
  
      def _parse_has_value(self, graph: rdflib.Graph, relation: Relation,
                           node: rdflib.term):
          """Parse the value of a relation
  
          Args:
             graph: underlying ontology graph
             relation: Relation object into which the information are saved
             node: (complex) Graph node containing the value
          
          Returns:
             None
          """
          self._parse_relation_values(graph, relation, node)
          # for hasValue only a target-statement that is a leaf is allowed
          if not relation.target_statement.type == StatementType.LEAF:
              self._add_logging_information(
                  LogLevel.CRITICAL,
                  IdType.class_,
                  self.current_class_iri,
                  f"In hasValue relation with property {relation.property_iri} "
                  f"target is a complex expression")
  
      def _parse_relation_values(self, graph: rdflib.Graph,
                                 relation: Relation, node: rdflib.term):
          """
          Parse the value of a relation out of a node that can be complex;
          consisting out of a combination of multiple other nodes
  
          Args:
             graph: underlying ontology graph
             relation: Relation object into which the information are saved
             node: (complex) Graph node containing the value
  
          Returns:
             None
          """
          target_statement = TargetStatement()
          relation.target_statement = target_statement
  
          queue = [(node, target_statement)]
          while not len(queue) == 0:
              current_term, current_statement = queue.pop(0)
              if isinstance(current_term, rdflib.URIRef):
                  target_iri = get_iri_from_uriref(current_term)
  
                  current_statement.set_target(target_iri=target_iri)
              else:
                  if rdflib.term.URIRef(Tags.owl_intersection.value) in \
                          graph.predicates(subject=current_term):
  
                      current_statement.type = StatementType.AND
                  elif rdflib.term.URIRef(Tags.owl_union.value) in \
                          graph.predicates(subject=current_term):
  
                      current_statement.type = StatementType.OR
                  else:
                      current_statement.set_target(
                          target_iri="Target statement has no iri",
                          target_data_value=str(current_term))
  
                      continue
  
                  child_nodes = self._extract_objects_out_of_single_combination(
                      graph, current_term, True, True)
                  for child_node in child_nodes:
                      new_statement = TargetStatement()
                      current_statement.target_statements.append(new_statement)
                      queue.append((child_node, new_statement))
  
      # an intersection/union is a basic list, it consists out of a chain of
      # bnode, where each bnode has the "first"and "rest" predicate, first
      # contains our object, rest is a pointer to the next part of the chain.
      # the list is over if rest points to "NIL"
      # this methode extracts all objects of a single layered intersection,
      # if the intersection contains further intersections these are contained in
      # the result list as BNode
      def _extract_objects_out_of_single_combination(self, graph: rdflib.Graph,
                                                     node: rdflib.term.BNode,
                                                     accept_and: bool,
                                                     accept_or: bool,
                                                     accept_one_of: bool = False):
          """
          An intersection/union is a basic list, it consits out of a chain of
          bnode,where each bnode has the "first"and "rest" predicate,
          first contains our object, rest is a pointer to the next part of the
          chain. The list is over if rest points to "NIL"
          This methode extracts all objects of a single layered intersection,
          if the intersection contains further intersections these are contained
          in the result list as BNode
  
          Args:
              graph: underlying ontology graph
              node: (complex) Graph node containing the value
              accept_or (bool): true, if combinations with "or" are allowed to be
                  parsed
              accept_and (bool): true, if combinations with "and" are allowed
                  to be parsed
              accept_one_of (bool): true, if ne_of statements are allowed
                  to be parsed
  
          Returns:
             None
          """
          predicates = list(graph.predicates(subject=node))
  
          # the passed startnode needs to contain an intersection or a union
          # both at the same time should not be possible
          start_node = None
          if rdflib.term.URIRef(Tags.owl_intersection.value) \
                  in predicates:
              if accept_and:
                  start_node = next(graph.objects(
                      subject=node,
                      predicate=rdflib.term.URIRef(Tags.owl_intersection.value)))
          elif rdflib.term.URIRef(Tags.owl_union.value) \
                  in predicates:
              if accept_or:
                  start_node = next(graph.objects(
                      subject=node,
                      predicate=rdflib.term.URIRef(Tags.owl_union.value)))
          elif rdflib.term.URIRef(Tags.owl_one_of.value) \
                  in predicates:
              if accept_one_of:
                  start_node = next(graph.objects(
                      subject=node,
                      predicate=rdflib.term.URIRef(Tags.owl_one_of.value)))
          else:
              self._add_logging_information(
                  LogLevel.CRITICAL, IdType.class_, self.current_class_iri,
                  f"Intern Error - invalid {node} passed to list extraction")
  
          result = []
          rest = start_node
          if start_node is None:
              return []
  
          while not rest == rdflib.term.URIRef(
                  'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil'):
  
              first = next(graph.objects(predicate=rdflib.term.URIRef(
                  'http://www.w3.org/1999/02/22-rdf-syntax-ns#first'),
                  subject=rest))
              result.append(first)
              rest = next(graph.objects(predicate=rdflib.term.URIRef(
                          'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest'),
                                        subject=rest))
  
          return result
  
      def _extract_objects_out_of_layered_combination(
              self, graph: rdflib.Graph, node: rdflib.term.BNode,
              accept_and: bool, accept_or: bool) -> List[rdflib.term.URIRef]:
          """Extract all nodes out of a complex combination
  
          Args:
              graph: underlying ontology graph
              node: (complex) Graph node containing the complex combination
              accept_or (bool): true, if combinations with "or" are allowed to be
                  parsed
              accept_and (bool): true, if combinations with "and" are allowed
                  to be parsed
  
          Returns:
             List[rdflib.term.URIRef], list of terms out of combination
          """
          result = []
          queue = [node]
  
          while len(queue) > 0:
              node = queue.pop()
              if isinstance(node, rdflib.term.URIRef):
                  result.append(node)
              else:
                  queue.extend(self._extract_objects_out_of_single_combination
                               (graph, node, accept_and, accept_or))
          return result
  
  