def _add_owl_thing(cls, voc_builder: VocabularyBuilder): """Add owl_thing class to the vocabulary in the predefined source By definition each class is a subclass of owl:thing and owl:thing can be a target of relation but owl thing is never mentioned explicitly in ontology files. Args: voc_builder: Builder object for Vocabulary Returns: None """ root_class = Class(iri="http://www.w3.org/2002/07/owl#Thing", comment="Predefined root_class", label="Thing", predefined=True) # as it is the root object it is only a parent of classes which have no # parents yet for class_ in voc_builder.vocabulary.get_classes(): if class_.parent_class_iris == []: class_.parent_class_iris.insert(0, root_class.iri) if root_class.iri not in voc_builder.vocabulary.classes: voc_builder.add_class(root_class) root_class.source_ids.add("PREDEFINED")
def _add_predefined_source(cls, voc_builder: VocabularyBuilder): """ Add a special source to the vocabulary: PREDEFINED Args: voc_builder: Builder object for Vocabulary Returns: None """ if "PREDEFINED" not in voc_builder.vocabulary.sources: source = Source(source_name="Predefined", timestamp=datetime.datetime.now(), predefined=True) voc_builder.add_source(source, "PREDEFINED")
def _compute_ancestor_classes(cls, voc_builder: VocabularyBuilder): """Compute all ancestor classes of classes Args: voc_builder: Builder object for Vocabulary Returns: None """ vocabulary = voc_builder.vocabulary # clear state for class_ in vocabulary.get_classes(): class_.ancestor_class_iris = [] for class_ in vocabulary.get_classes(): queue: List[str] = [] queue.extend(class_.parent_class_iris) while len(queue) > 0: parent = queue.pop() if not voc_builder.entity_is_known(parent): continue class_.ancestor_class_iris.append(parent) grand_parents = \ vocabulary.get_class_by_iri(parent).parent_class_iris for grand_parent in grand_parents: if grand_parent not in class_.ancestor_class_iris: # prevent infinite loop if inheritance circle queue.append(grand_parent)
def _compute_child_classes(cls, voc_builder: VocabularyBuilder): """Compute all child classes of classes Args: voc_builder: Builder object for Vocabulary Returns: None """ vocabulary = voc_builder.vocabulary # clear state for class_ in vocabulary.get_classes(): class_.child_class_iris = [] for class_ in vocabulary.get_classes(): for parent in class_.ancestor_class_iris: if not voc_builder.entity_is_known(parent): continue parent_class = vocabulary.get_class_by_iri(parent) parent_class.child_class_iris.append(class_.iri)
def post_process_vocabulary(cls, vocabulary: Vocabulary, old_vocabulary: Optional[Vocabulary] = None): """Main methode to be called for post processing Args: vocabulary (Vocabulary): Freshly parsed Vocabulary old_vocabulary (Vocabulary): Existing Vocabulary of which the settings should be overtaken Returns: None """ # all methods have to reset the state that they are editing first. # consecutive calls of post_process_vocabulary need to have the same # result voc_builder = VocabularyBuilder(vocabulary=vocabulary) cls._set_labels(voc_builder) cls._add_predefined_source(voc_builder) cls._add_predefined_datatypes(voc_builder) cls._add_owl_thing(voc_builder) cls._remove_duplicate_parents(voc_builder) cls._log_and_clear_dependencies(voc_builder) cls._compute_ancestor_classes(voc_builder) cls._compute_child_classes(voc_builder) cls._combine_relations(voc_builder) if old_vocabulary is not None: cls.transfer_settings(new_vocabulary=vocabulary, old_vocabulary=old_vocabulary) cls._apply_vocabulary_settings(voc_builder) cls._ensure_parent_class(voc_builder) cls._sort_relations(voc_builder) cls._mirror_object_property_inverses(voc_builder) cls._save_initial_label_summary(vocabulary)
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 _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_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 _combine_relations(cls, voc_builder: VocabularyBuilder): """Compute all CombinedRelations Args: voc_builder: Builder object for Vocabulary Returns: None """ vocabulary = voc_builder.vocabulary # clear state vocabulary.combined_object_relations.clear() vocabulary.combined_data_relations.clear() for class_ in vocabulary.get_classes(): class_.combined_object_relation_ids = [] class_.combined_data_relation_ids = [] for class_ in vocabulary.get_classes(): relations_with_property_iri = {} all_relation_ids = [] all_relation_ids.extend(class_.get_relation_ids()) for ancestor_iri in class_.ancestor_class_iris: if not voc_builder.entity_is_known(ancestor_iri): continue ancestor = vocabulary.get_class_by_iri(ancestor_iri) all_relation_ids.extend(ancestor.get_relation_ids()) for relation_id in all_relation_ids: relation = vocabulary.get_relation_by_id(id=relation_id) property_iri = relation.property_iri if property_iri not in relations_with_property_iri: relations_with_property_iri[property_iri] = [] relations_with_property_iri[property_iri].append(relation_id) for property_iri, rel_list in relations_with_property_iri.items(): # These ids should be derived, so that the same combined # relation always ends up with the same id as a class can # only have 1 combined relation of a property these ids are # unique by keeping the ids always the same, we can store # information more efficiently in the database (settings) # if a property iri is not known while parsing an ontology # (dependency not yet parsed) the relations with that # property are going to get ignored, maybe a not should be # displayed if vocabulary.is_id_of_type(property_iri, IdType.data_property): id = "combined-data-relation|{}|{}".format( class_.iri, property_iri) combi = CombinedDataRelation(id=id, property_iri=property_iri, relation_ids=rel_list, class_iri=class_.iri) voc_builder.add_combined_data_relation_for_class( class_iri=class_.iri, cdata=combi) elif vocabulary.is_id_of_type(property_iri, IdType.object_property): id = "combined-object-relation|{}|{}".format( class_.iri, property_iri) combi = CombinedObjectRelation(id=id, property_iri=property_iri, relation_ids=rel_list, class_iri=class_.iri) voc_builder.add_combined_object_relation_for_class( class_iri=class_.iri, crel=combi) else: pass
def _add_predefined_datatypes(cls, voc_builder: VocabularyBuilder): """ Add predefinded datatype_catalogue to the PREDEFINED source; they are not included in an OWL file Args: voc_builder: Builder object for Vocabulary Returns: None """ # Test if datatype_catalogue were already added, if yes skip if 'http://www.w3.org/2002/07/owl#rational' in \ voc_builder.vocabulary.datatypes.keys(): return voc_builder.add_predefined_datatype( Datatype(iri="http://www.w3.org/2002/07/owl#rational", comment="All numbers allowed", type=DatatypeType.number, number_decimal_allowed=True)) voc_builder.add_predefined_datatype( Datatype(iri="http://www.w3.org/2002/07/owl#real", comment="All whole numbers allowed", type=DatatypeType.number, number_decimal_allowed=False)) voc_builder.add_predefined_datatype( Datatype( iri="http://www.w3.org/1999/02/22-rdf-syntax-ns#PlainLiteral", comment="All strings allowed", type=DatatypeType.string)) voc_builder.add_predefined_datatype( Datatype( iri="http://www.w3.org/1999/02/22-rdf-syntax-ns#XMLLiteral", comment="XML Syntax required", type=DatatypeType.string)) voc_builder.add_predefined_datatype( Datatype(iri="http://www.w3.org/2000/01/rdf-schema#Literal", comment="All strings allowed", type=DatatypeType.string)) voc_builder.add_predefined_datatype( Datatype(iri="http://www.w3.org/2001/XMLSchema#anyURI", comment="Needs to start with http://", type=DatatypeType.string)) voc_builder.add_predefined_datatype( Datatype(iri="http://www.w3.org/2001/XMLSchema#base64Binary", comment="Base64Binary", type=DatatypeType.string)) voc_builder.add_predefined_datatype( Datatype(iri="http://www.w3.org/2001/XMLSchema#boolean", comment="True or False", type=DatatypeType.enum, enum_values=["True", "False"])) voc_builder.add_predefined_datatype( Datatype(iri="http://www.w3.org/2001/XMLSchema#byte", comment="Byte Number", type=DatatypeType.number, number_has_range=True, number_range_min=-128, number_range_max=127)) voc_builder.add_predefined_datatype( Datatype(iri="http://www.w3.org/2001/XMLSchema#dateTime", comment="Date with possible timezone", type=DatatypeType.date)) voc_builder.add_predefined_datatype( Datatype(iri="http://www.w3.org/2001/XMLSchema#dateTimeStamp", comment="Date", type=DatatypeType.date)) voc_builder.add_predefined_datatype( Datatype(iri="http://www.w3.org/2001/XMLSchema#decimal", comment="All decimal numbers", type=DatatypeType.number, number_decimal_allowed=True)) voc_builder.add_predefined_datatype( Datatype(iri="http://www.w3.org/2001/XMLSchema#double", comment="64 bit decimal", type=DatatypeType.number, number_decimal_allowed=True)) voc_builder.add_predefined_datatype( Datatype(iri="http://www.w3.org/2001/XMLSchema#float", comment="32 bit decimal", type=DatatypeType.number, number_decimal_allowed=True)) voc_builder.add_predefined_datatype( Datatype(iri="http://www.w3.org/2001/XMLSchema#hexBinary", comment="Hexadecimal", type=DatatypeType.string, allowed_chars=[ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F" ])) voc_builder.add_predefined_datatype( Datatype(iri="http://www.w3.org/2001/XMLSchema#int", comment="Signed 32 bit number", type=DatatypeType.number, number_has_range=True, number_range_min=-2147483648, number_range_max=2147483647)) voc_builder.add_predefined_datatype( Datatype(iri="http://www.w3.org/2001/XMLSchema#integer", comment="All whole numbers", type=DatatypeType.number, number_decimal_allowed=False)) voc_builder.add_predefined_datatype( Datatype(iri="http://www.w3.org/2001/XMLSchema#language", comment="Language code, e.g: en, en-US, fr, or fr-FR", type=DatatypeType.string)) voc_builder.add_predefined_datatype( Datatype(iri="http://www.w3.org/2001/XMLSchema#long", comment="Signed 64 bit integer", type=DatatypeType.number, number_has_range=True, number_range_min=-9223372036854775808, number_range_max=9223372036854775807, number_decimal_allowed=False)) voc_builder.add_predefined_datatype( Datatype(iri="http://www.w3.org/2001/XMLSchema#Name", comment="Name string (dont start with number)", type=DatatypeType.string)) voc_builder.add_predefined_datatype( Datatype(iri="http://www.w3.org/2001/XMLSchema#NCName", comment="Name string : forbidden", type=DatatypeType.string, forbidden_chars=[":"])) voc_builder.add_predefined_datatype( Datatype(iri="http://www.w3.org/2001/XMLSchema#negativeInteger", comment="All negative whole numbers", type=DatatypeType.number, number_has_range=True, number_range_max=-1)) voc_builder.add_predefined_datatype( Datatype(iri="http://www.w3.org/2001/XMLSchema#NMTOKEN", comment="Token string", type=DatatypeType.string)) voc_builder.add_predefined_datatype( Datatype(iri="http://www.w3.org/2001/XMLSchema#nonNegativeInteger", comment="All positive whole numbers", type=DatatypeType.number, number_has_range=True, number_range_min=0)) voc_builder.add_predefined_datatype( Datatype(iri="http://www.w3.org/2001/XMLSchema#nonPositiveInteger", comment="All negative whole numbers", type=DatatypeType.number, number_has_range=True, number_range_max=-1)) voc_builder.add_predefined_datatype( Datatype(iri="http://www.w3.org/2001/XMLSchema#normalizedString", comment="normalized String", type=DatatypeType.string)) voc_builder.add_predefined_datatype( Datatype(iri="http://www.w3.org/2001/XMLSchema#positiveInteger", comment="All positive whole numbers", type=DatatypeType.number, number_has_range=True, number_range_min=0)) voc_builder.add_predefined_datatype( Datatype(iri="http://www.w3.org/2001/XMLSchema#short", comment="signed 16 bit number", type=DatatypeType.number, number_has_range=True, number_range_min=-32768, number_range_max=32767)) voc_builder.add_predefined_datatype( Datatype(iri="http://www.w3.org/2001/XMLSchema#string", comment="String", type=DatatypeType.string)) voc_builder.add_predefined_datatype( Datatype(iri="http://www.w3.org/2001/XMLSchema#token", comment="String", type=DatatypeType.string)) voc_builder.add_predefined_datatype( Datatype(iri="http://www.w3.org/2001/XMLSchema#unsignedByte", comment="unsigned 8 bit number", type=DatatypeType.number, number_has_range=True, number_range_min=0, number_range_max=255)) voc_builder.add_predefined_datatype( Datatype(iri="http://www.w3.org/2001/XMLSchema#unsignedInt", comment="unsigned 32 bit number", type=DatatypeType.number, number_has_range=True, number_range_min=0, number_range_max=4294967295)) voc_builder.add_predefined_datatype( Datatype(iri="http://www.w3.org/2001/XMLSchema#unsignedLong", comment="unsigned 64 bit number", type=DatatypeType.number, number_has_range=True, number_range_min=0, number_range_max=18446744073709551615)) voc_builder.add_predefined_datatype( Datatype(iri="http://www.w3.org/2001/XMLSchema#unsignedShort", comment="unsigned 16 bit number", type=DatatypeType.number, number_has_range=True, number_range_min=0, number_range_max=65535))