Skip to content

Commit

Permalink
core: exporters: owl2: fix ignored export preference for HasKey axiom.
Browse files Browse the repository at this point in the history
Like all other axioms the HasKey axiom has a preference for filtering
it from the exported ontology. However this preference was not
taken into account, meaning HasKey axioms would always be exported.

This commit fixes this behaviour, and also refactors the way HasKey
nodes are translated so that subsequent translations of the same
node are cached.

Closes #156
  • Loading branch information
mnamici committed Sep 29, 2021
1 parent 9040015 commit 85fea10
Showing 1 changed file with 41 additions and 25 deletions.
66 changes: 41 additions & 25 deletions eddy/core/exporters/owl2.py
Original file line number Diff line number Diff line change
Expand Up @@ -640,8 +640,7 @@ def convert(self, node):
elif node.type() is Item.RangeRestrictionNode:
self._converted[node.diagram.name][node.id] = self.getRangeRestriction(node)
elif node.type() is Item.HasKeyNode:
self.createHasKeyAxiom(node)
return
self._converted[node.diagram.name][node.id] = self.getHasKey(node)
else:
raise ValueError('no conversion available for node %s' % node)
return self._converted[node.diagram.name][node.id]
Expand Down Expand Up @@ -1044,6 +1043,38 @@ def getFacet(self, node):
facet = self.getOWLApiFacet(nodeFacet.constrainingFacet)
return self.df.getOWLFacetRestriction(facet, literal)

def getHasKey(self, node):
"""
Build and returns a collection of expressions that can be used to build
a OWL 2 HasKey axiom. The first item of the collection is a the class expression,
and the remaining items are object and data property expressions that uniquely
identify named instances of the class expression.
:type node: HasKeyNode
:rtype: list
"""
f1 = lambda x: x.type() is Item.InputEdge
f2 = lambda x: x.identity() is Identity.Concept
f3 = lambda x: x.identity() is Identity.Role
f4 = lambda x: x.identity() is Identity.Attribute
classes = node.incomingNodes(filter_on_edges=f1, filter_on_nodes=f2)
objProps = node.incomingNodes(filter_on_edges=f1, filter_on_nodes=f3)
dtProps = node.incomingNodes(filter_on_edges=f1, filter_on_nodes=f4)

if not classes:
raise DiagramMalformedError(node, 'missing class expression operand')
if len(classes) > 1:
raise DiagramMalformedError(node, 'too many class expression operands')
if not (objProps or dtProps):
raise DiagramMalformedError(node, 'HasKey nodes must be connected to at least '
'an (object or data) property expression')

collection = [self.convert(first(classes))]
for prop in objProps:
collection.append(self.convert(prop))
for prop in dtProps:
collection.append(self.convert(prop))
return collection

def getIndividual(self, node):
"""
Build and returns a OWL 2 individual using the given graphol node.
Expand Down Expand Up @@ -1486,29 +1517,12 @@ def createHasKeyAxiom(self, node):
Generate a OWL 2 HasKeyAxiom starting from node.
:type node: HasKeyNode
"""
f1 = lambda x: x.type() is Item.InputEdge
f2 = lambda x: x.identity() is Identity.Concept
f3 = lambda x: x.identity() is Identity.Role
f4 = lambda x: x.identity() is Identity.Attribute
classes = node.incomingNodes(filter_on_edges=f1, filter_on_nodes=f2)
objProps = node.incomingNodes(filter_on_edges=f1, filter_on_nodes=f3)
dtProps = node.incomingNodes(filter_on_edges=f1, filter_on_nodes=f4)

if not classes:
raise DiagramMalformedError(node, 'missing class expression operand')
if len(classes) > 1:
raise DiagramMalformedError(node, 'too many class expression operands')
if not (objProps or dtProps):
raise DiagramMalformedError(node, 'HasKey nodes must be connected to at least one object (or data) property expression')

owlClExpr = self.convert(first(classes))
owlPropExprs = self.HashSet()
for prop in objProps:
owlPropExprs.add(self.convert(prop))
for prop in dtProps:
owlPropExprs.add(self.convert(prop))

self.addAxiom(self.df.getOWLHasKeyAxiom(owlClExpr, owlPropExprs))
if OWLAxiom.HasKey in self.axiomsList:
properties = self.HashSet()
expression = self.convert(node)[0]
for prop in self.convert(node)[1:]:
properties.add(prop)
self.addAxiom(self.df.getOWLHasKeyAxiom(expression, properties))

def createInverseObjectPropertiesAxiom(self, edge):
"""
Expand Down Expand Up @@ -1821,6 +1835,8 @@ def run(self):
self.createPropertyDomainAxiom(node)
elif node.type() is Item.RangeRestrictionNode:
self.createPropertyRangeAxiom(node)
elif node.type() is Item.HasKeyNode:
self.createHasKeyAxiom(node)
self.step(+1)

LOGGER.debug('Generated OWL 2 axioms from nodes (axioms = %s)', len(self.axioms()))
Expand Down

0 comments on commit 85fea10

Please sign in to comment.