Source code for owlapy.swrl

from abc import ABCMeta, abstractmethod
from typing import List, Union
import re

from owlapy.class_expression import OWLClass
from owlapy.iri import IRI
from owlapy.owl_datatype import OWLDatatype
from owlapy.owl_individual import OWLNamedIndividual
from owlapy.owl_literal import OWLLiteral
from owlapy.owl_property import OWLObjectProperty, OWLDataProperty


BUILTINS = ["add", "subtract", "multiply", "divide", "mod", "pow", "abs", "round", "floor", "ceiling",
            "equal", "notEqual", "greaterThan", "lessThan", "greaterThanOrEqual", "lessThanOrEqual",
            "stringConcat", "substring", "contains", "startsWith", "endsWith", "stringLength",
            "matches", "normalizeSpace", "lowerCase", "upperCase", "dateTime", "addDayTimeDuration",
            "subtractDateTimes", "year", "month", "day", "hour", "isInteger", "isString", "isDateTime",
            "isBoolean", "isNumeric"]

DATATYPES = ["decimal", "integer", "nonNegativeInteger", "nonPositiveInteger", "positiveInteger", "negativeInteger",
             "long", "double", "float", "boolean", "string", "date", "dateTime", "dateTimeStamp", "duration",
             "time", "gYearMonth", "gMonthDay", "gYear", "gMonth", "gDay"]

SWRL = "http://www.w3.org/2003/11/swrl#"
SWRLB = "http://www.w3.org/2003/11/swrlb#"

[docs] class Variable(metaclass=ABCMeta): """Represents a variable in SWRL syntax""" iri: IRI # should have the correct namespace, e.g: http://www.w3.org/2003/11/swrl#x def __init__(self, iri:Union[IRI, str]): if isinstance(iri, str): self.iri = IRI.create(iri) else: self.iri = iri
[docs] def is_i_variable(self): if isinstance(self, IVariable): return True return False
[docs] def is_d_variable(self): if isinstance(self, DVariable): return True return False
[docs] def __eq__(self, other): if type(other) is type(self): return self.iri == other.iri
[docs] def __str__(self): return "?" + self.iri.reminder
[docs] def __hash__(self): return hash(self.iri)
[docs] class DVariable(Variable): """Represents a data variable in SWRL syntax"""
[docs] def __repr__(self): return f"DVariable({self.iri})"
[docs] class IVariable(Variable): """Represents a individual variable in SWRL syntax"""
[docs] def __repr__(self): return f"IVariable({self.iri})"
[docs] class Atom(metaclass=ABCMeta): """Represents an Atom in SWRL syntax"""
[docs] def __eq__(self, other): if type(other) is type(self): return self.__repr__() == other.__repr__()
[docs] @staticmethod def from_string(atom_str: str, namespace: str, dp_predicates: List[str] = None): """ Parses a SWRL atom like 'parent(?x, ?y)' or 'person(?x)'. Returns: An Atom object. """ # Regular expression to extract predicate and variable list from a string atom pattern = r'^\s*([a-zA-Z_][\w\-]*)\s*\(\s*([^\)]*)\s*\)\s*$' match = re.match(pattern, atom_str) if not match: raise ValueError(f"Invalid SWRL atom: {atom_str}") predicate = match.group(1) raw_args = match.group(2) args = [arg.strip() for arg in raw_args.split(',') if arg.strip()] if len(args) == 1: if predicate in DATATYPES: if "?" in args[0]: argument = DVariable(SWRL + args[0][1:]) else: argument = OWLLiteral(args[0]) return DataRangeAtom(OWLDatatype("http://www.w3.org/2001/XMLSchema#" + predicate), argument) else: if "?" in args[0]: argument = IVariable(SWRL + args[0][1:]) else: argument = OWLNamedIndividual(namespace + args[0]) return ClassAtom(OWLClass(namespace + predicate), argument) elif len(args) == 2 and predicate not in BUILTINS + ["sameAs", "differentFrom"]: if dp_predicates is not None and predicate in dp_predicates: if "?" in args[0]: args[0] = IVariable(SWRL + args[0][1:]) else: args[0] = OWLNamedIndividual(args[0]) if "?" in args[1]: args[1] = DVariable(SWRL + args[1][1:]) else: args[1] = OWLLiteral(args[1]) return DataPropertyAtom(OWLDataProperty(namespace + predicate), args[0], args[1]) else: for i in range(0, 2): if "?" in args[i]: args[i] = IVariable(SWRL + args[i][1:]) else: args[i] = OWLNamedIndividual(namespace + args[i]) return ObjectPropertyAtom(OWLObjectProperty(namespace + predicate), args[0], args[1]) elif len(args) == 2 and predicate == "sameAs": for i in range(0, 2): if "?" in args[i]: args[i] = IVariable(SWRL + args[i][1:]) else: args[i] = OWLNamedIndividual(namespace + args[i]) return SameAsAtom(args[0], args[1]) elif len(args) == 2 and predicate == "differentFrom": for i in range(0, 2): if "?" in args[i]: args[i] = IVariable(namespace + args[i][1:]) else: args[i] = OWLNamedIndividual(namespace + args[i]) return DifferentFromAtom(args[0], args[1]) elif predicate in BUILTINS: for i in range(0, len(args)): if "?" in args[i]: args[i] = DVariable(SWRL + args[i][1:]) else: args[i] = OWLNamedIndividual(namespace + args[i]) return BuiltInAtom(IRI.create(SWRLB + predicate), args) else: raise ValueError(f"Invalid SWRL atom: {atom_str}")
[docs] @abstractmethod def is_class_assertion(self): pass
[docs] @abstractmethod def is_property_assertion(self): pass
[docs] @abstractmethod def is_same_as(self): pass
[docs] @abstractmethod def is_different_from(self): pass
[docs] @abstractmethod def is_builtin(self): pass
[docs] def r(argument): """Returns the right string format of a given argument depending on its type""" if isinstance(argument, Variable): return str(argument) return argument.iri.reminder
[docs] def t(argument): """Returns the representation for a given argument""" return f"{type(argument).__name__}({argument.iri.str})"
[docs] class ClassAtom(Atom): """Represents a class atom in SWRL syntax""" argument1: Union[IVariable, OWLNamedIndividual] cls: OWLClass def __init__(self, cls: OWLClass, argument1: Union[IVariable, OWLNamedIndividual]): self.cls = cls self.argument1 = argument1
[docs] def is_class_assertion(self): return True
[docs] def is_property_assertion(self): return False
[docs] def is_same_as(self): return False
[docs] def is_different_from(self): return False
[docs] def is_builtin(self): return False
[docs] def __str__(self): return f"{r(self.cls)}({r(self.argument1)})"
[docs] def __repr__(self): return f"ClassAtom({t(self.cls)}, {t(self.argument1)})"
[docs] def __hash__(self): return hash(f"ClassAtom({str(self.cls)}, {str(self.argument1)})")
[docs] class DataRangeAtom(Atom): """Represents a data range atom in SWRL syntax""" argument1: DVariable datatype: OWLDatatype def __init__(self, datatype: OWLDatatype, argument1: DVariable): self.datatype = datatype self.argument1 = argument1
[docs] def is_class_assertion(self): return True
[docs] def is_property_assertion(self): return False
[docs] def is_same_as(self): return False
[docs] def is_different_from(self): return False
[docs] def is_builtin(self): return False
[docs] def __str__(self): return f"{r(self.datatype)}({r(self.argument1)})"
[docs] def __repr__(self): return f"DataRangeAtom({t(self.datatype)} {t(self.argument1)})"
[docs] def __hash__(self): return hash(f"DataRangeAtom({str(self.datatype)}, {str(self.argument1)})")
[docs] class PropertyAtom(Atom, metaclass=ABCMeta): """Represents a property atom in SWRL syntax""" def __init__(self, prop: Union[OWLObjectProperty, OWLDataProperty], argument1, argument2): self.argument1 = argument1 self.argument2 = argument2 self.prop = prop
[docs] def is_class_assertion(self): return False
[docs] def is_property_assertion(self): return True
[docs] def is_same_as(self): return False
[docs] def is_different_from(self): return False
[docs] def is_builtin(self): return False
[docs] def __str__(self): return f"{r(self.prop)}({r(self.argument1)}, {r(self.argument2)})"
[docs] class ObjectPropertyAtom(PropertyAtom): """Represents an object property atom in SWRL syntax""" argument1: Union[OWLNamedIndividual, IVariable] argument2: Union[OWLNamedIndividual, IVariable] prop: OWLObjectProperty def __init__(self, prop: OWLObjectProperty, argument1: Union[OWLNamedIndividual, IVariable], argument2: Union[OWLNamedIndividual, IVariable]): super().__init__(prop, argument1, argument2)
[docs] def __repr__(self): return f"ObjectPropertyAtom({t(self.prop)}, {t(self.argument1)}, {t(self.argument2)})"
[docs] def __hash__(self): return hash(f"ObjectPropertyAtom({str(self.prop)}, {str(self.argument1)}, {str(self.argument2)})")
[docs] class DataPropertyAtom(PropertyAtom): """Represents a data property atom in SWRL syntax""" argument1: Union[OWLNamedIndividual, IVariable] argument2: Union[OWLLiteral, DVariable] prop: OWLDataProperty def __init__(self, prop: OWLDataProperty, argument1: Union[OWLNamedIndividual, IVariable], argument2: Union[OWLLiteral, DVariable]): super().__init__(prop, argument1, argument2)
[docs] def __repr__(self): return f"DataPropertyAtom({t(self.prop)}, {t(self.argument1)}, {t(self.argument2)})"
[docs] def __hash__(self): return hash(f"DataPropertyAtom({str(self.prop)}, {str(self.argument1)}, {str(self.argument2)})")
[docs] class SameAsAtom(Atom): """Represents a 'same-as' atom in SWRL syntax""" argument1: Union[IVariable, OWLNamedIndividual] argument2: Union[IVariable, OWLNamedIndividual] def __init__(self, argument1: Union[IVariable, OWLNamedIndividual], argument2: Union[IVariable, OWLNamedIndividual]): self.argument1 = argument1 self.argument2 = argument2
[docs] def is_class_assertion(self): return False
[docs] def is_property_assertion(self): return False
[docs] def is_same_as(self): return True
[docs] def is_different_from(self): return False
[docs] def is_builtin(self): return False
[docs] def __str__(self): return f"sameAs({r(self.argument1)}, {r(self.argument2)})"
[docs] def __repr__(self): return f"SameAs({t(self.argument1)}, {t(self.argument2)})"
[docs] def __hash__(self): return hash(f"SameAs({str(self.argument1)}, {str(self.argument2)})")
[docs] class DifferentFromAtom(Atom): """Represents a 'different-from' atom in SWRL syntax""" argument1: Union[IVariable, OWLNamedIndividual] argument2: Union[IVariable, OWLNamedIndividual] def __init__(self, argument1: Union[IVariable, OWLNamedIndividual], argument2: Union[IVariable, OWLNamedIndividual]): self.argument1 = argument1 self.argument2 = argument2
[docs] def is_class_assertion(self): return False
[docs] def is_property_assertion(self): return False
[docs] def is_same_as(self): return False
[docs] def is_different_from(self): return True
[docs] def is_builtin(self): return False
[docs] def __str__(self): return f"differentFrom({r(self.argument1)}, {r(self.argument2)})"
[docs] def __repr__(self): return f"DifferentFrom({t(self.argument1)}, {t(self.argument2)})"
[docs] def __hash__(self): return hash(f"DifferentFrom({str(self.argument1)}, {str(self.argument2)})")
[docs] class BuiltInAtom(Atom): """Represents a built-in atom in SWRL syntax""" predicate: IRI # should have the correct prefix, e.g: http://www.w3.org/2003/11/swrlb#divide arguments: List[Union[DVariable, OWLLiteral]] def __init__(self, predicate: IRI, arguments: List[Union[DVariable, OWLLiteral]]): self.predicate = predicate self.arguments = arguments
[docs] def is_class_assertion(self): return False
[docs] def is_property_assertion(self): return False
[docs] def is_same_as(self): return False
[docs] def is_different_from(self): return False
[docs] def is_builtin(self): return True
[docs] def __str__(self): args_to_print = "" for arg in self.arguments: if isinstance(arg, OWLLiteral): args_to_print += str(arg._v) + ", " else: args_to_print += str(arg) + ", " return f"{str(self.predicate.reminder)}({args_to_print[:-2]})"
[docs] def __repr__(self): args_list ='[' for arg in self.arguments: args_list = arg.__repr__() args_list += ']' return f'BuiltInAtom(IRI.create({self.predicate.str}), {args_list})'
[docs] class Rule: """Represents a rule in SWRL syntax""" body_atoms: Union[Atom, List[Atom]] head_atoms: Union[Atom, List[Atom]] def __init__(self, body_atoms: Union[Atom, List[Atom]], head_atoms: Union[Atom, List[Atom]]): self.body = body_atoms self.head = head_atoms
[docs] @staticmethod def from_string(rule: str, namespace: str, dp_predicates: List[str] = None): """ Parses a SWRL rule given as a string. Use '^' for composition of atoms and '->' for consequent implication. E.g. of a valid rule: 'parent(?x,?y) ^ brother(?y,?z) -> uncle(?x,?z)' Args: rule: The SWRL rule in string format that is to be parsed namespace: The namespace of the ontology dp_predicates: (optional) List of data property predicates that will help in the correct mapping of property type (by default property atoms are considered as object property atoms except when specifying data property predicates in this argument) Returns: A SWRL Rule object. """ body_str = rule.split("->")[0] head_str = rule.split("->")[1] body_atoms_str = body_str.split("^") head_atoms_str = head_str.split("^") body_atoms = [Atom.from_string(atom_str, namespace, dp_predicates) for atom_str in body_atoms_str] head_atoms = [Atom.from_string(atom_str, namespace, dp_predicates) for atom_str in head_atoms_str] return Rule(body_atoms, head_atoms)
[docs] def __str__(self): body = self.body head = self.head if isinstance(self.body, List): body = " ^ ".join(map(str, self.body)) if isinstance(self.head, List): head = " ^ ".join(map(str, self.head)) return f"{body} -> {head}"
[docs] def __repr__(self): return f"Rule({[a.__repr__() for a in self.body]}, {[a.__repr__() for a in self.head]})"