001/** 002 * Copyright (C) 2007 - 2016, Jens Lehmann 003 * 004 * This file is part of DL-Learner. 005 * 006 * DL-Learner is free software; you can redistribute it and/or modify 007 * it under the terms of the GNU General Public License as published by 008 * the Free Software Foundation; either version 3 of the License, or 009 * (at your option) any later version. 010 * 011 * DL-Learner is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 014 * GNU General Public License for more details. 015 * 016 * You should have received a copy of the GNU General Public License 017 * along with this program. If not, see <http://www.gnu.org/licenses/>. 018 */ 019package org.dllearner.algorithms.qtl.util; 020 021import org.apache.jena.datatypes.RDFDatatype; 022import org.apache.jena.datatypes.xsd.XSDDatatype; 023import org.apache.jena.rdf.model.Literal; 024import org.apache.jena.vocabulary.OWL; 025import org.apache.jena.vocabulary.RDF; 026import org.apache.jena.vocabulary.RDFS; 027import org.dllearner.algorithms.qtl.datastructures.QueryTree; 028import org.dllearner.algorithms.qtl.datastructures.impl.QueryTreeImpl; 029import org.dllearner.algorithms.qtl.datastructures.impl.QueryTreeImpl.LiteralNodeConversionStrategy; 030import org.dllearner.algorithms.qtl.datastructures.impl.QueryTreeImpl.NodeType; 031import org.dllearner.core.StringRenderer; 032import org.dllearner.core.StringRenderer.Rendering; 033import org.semanticweb.owlapi.apibinding.OWLManager; 034import org.semanticweb.owlapi.model.*; 035import org.semanticweb.owlapi.util.DefaultPrefixManager; 036import org.semanticweb.owlapi.vocab.OWL2Datatype; 037import org.semanticweb.owlapi.vocab.OWLFacet; 038import uk.ac.manchester.cs.owl.owlapi.OWLDataFactoryImpl; 039 040import javax.xml.bind.DatatypeConverter; 041import java.util.*; 042 043/** 044 * Converts query trees into OWL class expressions and vice versa. 045 * @author Lorenz Buehmann 046 * 047 */ 048public class QueryTreeConverter implements OWLClassExpressionVisitor, OWLDataRangeVisitor{ 049 050 OWLDataFactory df = new OWLDataFactoryImpl(); 051 052 Stack<QueryTree<String>> stack = new Stack<>(); 053 int id = 0; 054 055 /** 056 * Returns a OWL class expression of the given query trees. 057 * @param tree the query tree 058 */ 059 public OWLClassExpression asOWLClassExpression(QueryTree<String> tree){ 060 Set<OWLClassExpression> classExpressions = asOWLClassExpressions(tree); 061 OWLClassExpression expression; 062 if(classExpressions.isEmpty()){ 063 expression = df.getOWLThing(); 064 } else if(classExpressions.size() == 1){ 065 expression = classExpressions.iterator().next(); 066 } else { 067 expression = df.getOWLObjectIntersectionOf(classExpressions); 068 } 069 return expression; 070 } 071 072 /** 073 * Returns a set of OWL class expression representations of the given query tree. 074 * @param tree the query tree 075 */ 076 public Set<OWLClassExpression> asOWLClassExpressions(QueryTree<String> tree){ 077 Set<OWLClassExpression> classExpressions = new HashSet<>(); 078 079 List<QueryTree<String>> children = tree.getChildren(); 080 for(QueryTree<String> child : children){ 081 String childLabel = child.getUserObject(); 082 String predicateString = (String) tree.getEdge(child); 083 if(predicateString.equals(RDF.type.getURI()) || predicateString.equals(RDFS.subClassOf.getURI())){//A 084 if(child.isVarNode()){ 085 classExpressions.addAll(asOWLClassExpressions(child)); 086 } else { 087 if(!childLabel.equals(OWL.Thing.getURI())){//avoid trivial owl:Thing statements 088 classExpressions.add(df.getOWLClass(IRI.create(childLabel))); 089 } 090 } 091 } else { 092 if(child.isLiteralNode()){ 093 OWLDataProperty p = df.getOWLDataProperty(IRI.create((String) tree.getEdge(child))); 094 if(childLabel.equals("?")){//p some int 095 Set<Literal> literals = child.getLiterals(); 096 OWLDataRange dataRange = null; 097 if(literals.isEmpty()){//happens if there are heterogeneous datatypes 098 String datatypeURI = OWL2Datatype.RDFS_LITERAL.getIRI().toString(); 099 dataRange = df.getOWLDatatype(IRI.create(datatypeURI)); 100 } else { 101 for (LiteralNodeConversionStrategy strategy : LiteralNodeConversionStrategy.values()) { 102 switch (strategy) { 103 case DATATYPE: 104 Literal lit = literals.iterator().next(); 105 RDFDatatype datatype = lit.getDatatype(); 106 String datatypeURI; 107 if (datatype == null) { 108 datatypeURI = OWL2Datatype.RDF_PLAIN_LITERAL.getIRI().toString(); 109 } else { 110 datatypeURI = datatype.getURI(); 111 } 112 dataRange = df.getOWLDatatype(IRI.create(datatypeURI)); 113 break; 114 case DATA_ONE_OF: 115 dataRange = asDataOneOf(df, literals); 116 break; 117 case MIN_MAX: 118 dataRange = asFacet(df, literals); 119 break; 120 case MIN: 121 dataRange = asMinFacet(df, literals); 122 break; 123 case MAX: 124 dataRange = asMaxFacet(df, literals); 125 break; 126 } 127 } 128 } 129 classExpressions.add(df.getOWLDataSomeValuesFrom(p, dataRange)); 130 } else {//p value 1.2 131 Set<Literal> literals = child.getLiterals(); 132 Literal lit = literals.iterator().next(); 133 OWLLiteral owlLiteral = asOWLLiteral(df, lit); 134 classExpressions.add(df.getOWLDataHasValue(p, owlLiteral)); 135 } 136 } else { 137 OWLObjectProperty p = df.getOWLObjectProperty(IRI.create((String) tree.getEdge(child))); 138 OWLClassExpression filler; 139 if(child.isVarNode()){//p some C 140 Set<OWLClassExpression> fillerClassExpressions = asOWLClassExpressions(child); 141 if(fillerClassExpressions.isEmpty()){ 142 filler = df.getOWLThing(); 143 } else if(fillerClassExpressions.size() == 1){ 144 filler = fillerClassExpressions.iterator().next(); 145 } else { 146 filler = df.getOWLObjectIntersectionOf(fillerClassExpressions); 147 } 148 classExpressions.add(df.getOWLObjectSomeValuesFrom(p, filler)); 149 } else {//p value {a} 150 classExpressions.add(df.getOWLObjectHasValue(p, df.getOWLNamedIndividual(IRI.create(childLabel)))); 151 } 152 } 153 } 154 } 155 return classExpressions; 156 } 157 158 private OWLDataRange asFacet(OWLDataFactory df, Set<Literal> literals){ 159 //return Boolean datatype because it doesn't make sense to return a facet of Boolean values 160 if(getOWLDatatype(df, literals).equals(df.getBooleanOWLDatatype())){ 161 return df.getBooleanOWLDatatype(); 162 } 163 Literal min = getMin(literals); 164 Literal max = getMax(literals); 165 166 OWLFacetRestriction minRestriction = df.getOWLFacetRestriction(OWLFacet.MIN_INCLUSIVE, asOWLLiteral(df, min)); 167 OWLFacetRestriction maxRestriction = df.getOWLFacetRestriction(OWLFacet.MAX_INCLUSIVE, asOWLLiteral(df, max)); 168 169 return df.getOWLDatatypeRestriction(getOWLDatatype(df, literals), minRestriction, maxRestriction); 170 } 171 172 private OWLDataRange asMinFacet(OWLDataFactory df, Set<Literal> literals){ 173 //return Boolean datatype because it doesn't make sense to return a facet of Boolean values 174 if(getOWLDatatype(df, literals).equals(df.getBooleanOWLDatatype())){ 175 return df.getBooleanOWLDatatype(); 176 } 177 Literal min = getMin(literals); 178 179 OWLFacetRestriction minRestriction = df.getOWLFacetRestriction(OWLFacet.MIN_INCLUSIVE, asOWLLiteral(df, min)); 180 181 return df.getOWLDatatypeRestriction(getOWLDatatype(df, literals), minRestriction); 182 } 183 184 private OWLDataRange asMaxFacet(OWLDataFactory df, Set<Literal> literals){ 185 //return Boolean datatype because it doesn't make sense to return a facet of Boolean values 186 if(getOWLDatatype(df, literals).equals(df.getBooleanOWLDatatype())){ 187 return df.getBooleanOWLDatatype(); 188 } 189 Literal max = getMax(literals); 190 191 OWLFacetRestriction maxRestriction = df.getOWLFacetRestriction(OWLFacet.MAX_INCLUSIVE, asOWLLiteral(df, max)); 192 193 return df.getOWLDatatypeRestriction(getOWLDatatype(df, literals), maxRestriction); 194 } 195 196 private OWLDataRange asDataOneOf(OWLDataFactory df, Set<Literal> literals){ 197 //return Boolean datatype because it doesn't make sense to return a enumeration of Boolean values 198 if(getOWLDatatype(df, literals).equals(df.getBooleanOWLDatatype())){ 199 return df.getBooleanOWLDatatype(); 200 } 201 return df.getOWLDataOneOf(asOWLLiterals(df, literals)); 202 } 203 204 private Set<OWLLiteral> asOWLLiterals(OWLDataFactory df, Set<Literal> literals){ 205 Set<OWLLiteral> owlLiterals = new HashSet<>(literals.size()); 206 for (Literal literal : literals) { 207 owlLiterals.add(asOWLLiteral(df, literal)); 208 } 209 return owlLiterals; 210 } 211 212 private OWLLiteral asOWLLiteral(OWLDataFactory df, Literal literal){ 213 OWLLiteral owlLiteral; 214 if(literal.getDatatypeURI() == null){ 215 owlLiteral = df.getOWLLiteral(literal.getLexicalForm(), literal.getLanguage()); 216 } else { 217 owlLiteral = df.getOWLLiteral(literal.getLexicalForm(), df.getOWLDatatype(IRI.create(literal.getDatatypeURI()))); 218 } 219 return owlLiteral; 220 } 221 222 private Literal getMin(Set<Literal> literals){ 223 Iterator<Literal> iter = literals.iterator(); 224 Literal min = iter.next(); 225 Literal l; 226 while(iter.hasNext()){ 227 l = iter.next(); 228 if(l.getDatatype() == XSDDatatype.XSDinteger || l.getDatatype() == XSDDatatype.XSDint){ 229 min = (l.getInt() < min.getInt()) ? l : min; 230 } else if(l.getDatatype() == XSDDatatype.XSDdouble || l.getDatatype() == XSDDatatype.XSDdecimal){ 231 min = (l.getDouble() < min.getDouble()) ? l : min; 232 } else if(l.getDatatype() == XSDDatatype.XSDfloat){ 233 min = (l.getFloat() < min.getFloat()) ? l : min; 234 } else if(l.getDatatype() == XSDDatatype.XSDdate){ 235 min = (DatatypeConverter.parseDate(l.getLexicalForm()).compareTo(DatatypeConverter.parseDate(min.getLexicalForm())) < 0) ? l : min; 236 } 237 } 238 return min; 239 } 240 241 private Literal getMax(Set<Literal> literals){ 242 Iterator<Literal> iter = literals.iterator(); 243 Literal max = iter.next(); 244 Literal l; 245 while(iter.hasNext()){ 246 l = iter.next(); 247 if(l.getDatatype() == XSDDatatype.XSDinteger || l.getDatatype() == XSDDatatype.XSDint){ 248 max = (l.getInt() > max.getInt()) ? l : max; 249 } else if(l.getDatatype() == XSDDatatype.XSDdouble || l.getDatatype() == XSDDatatype.XSDdecimal){ 250 max = (l.getDouble() > max.getDouble()) ? l : max; 251 } else if(l.getDatatype() == XSDDatatype.XSDfloat){ 252 max = (l.getFloat() > max.getFloat()) ? l : max; 253 } else if(l.getDatatype() == XSDDatatype.XSDdate){ 254 max = (DatatypeConverter.parseDate(l.getLexicalForm()).compareTo(DatatypeConverter.parseDate(max.getLexicalForm())) > 0) ? l : max; 255 } 256 } 257 return max; 258 } 259 260 private OWLDatatype getOWLDatatype(OWLDataFactory df, Set<Literal> literals){ 261 return df.getOWLDatatype(IRI.create(literals.iterator().next().getDatatypeURI())); 262 } 263 264 265 /** 266 * Converts a OWL class expression into a query tree, if possible. Note that this is not possible 267 * for all OWL constructs, e.g. universal restrictions are not allowed. An exceptions is thrown if the conversion 268 * fails. 269 * @param expression 270 * @return 271 */ 272 public QueryTree<String> asQueryTree(OWLClassExpression expression){ 273// stack.push(new QueryTreeImpl<String>("?")); 274 reset(); 275 expression.accept(this); 276 return stack.pop(); 277 } 278 279 private void reset(){ 280 id = 0; 281 stack.clear(); 282 } 283 284 private void fireUnsupportedFeatureException(OWLClassExpression expression) { 285 throw new IllegalArgumentException("Conversion of " + expression.getClass().getSimpleName() + " is not supported."); 286 } 287 288 /* (non-Javadoc) 289 * @see org.semanticweb.owlapi.model.OWLClassExpressionVisitor#visit(org.semanticweb.owlapi.model.OWLClass) 290 */ 291 @Override 292 public void visit(OWLClass cls) { 293 stack.peek().addChild(new QueryTreeImpl<>(cls.toStringID(), NodeType.RESOURCE, id++), RDF.type.getURI()); 294 } 295 296 /* (non-Javadoc) 297 * @see org.semanticweb.owlapi.model.OWLClassExpressionVisitor#visit(org.semanticweb.owlapi.model.OWLObjectIntersectionOf) 298 */ 299 @Override 300 public void visit(OWLObjectIntersectionOf expr) { 301 boolean root = stack.isEmpty(); 302 stack.push(new QueryTreeImpl<>("?", NodeType.VARIABLE, id++)); 303 for (OWLClassExpression op : expr.getOperandsAsList()) { 304 op.accept(this); 305 } 306// if(!root) 307// stack.pop(); 308 } 309 310 /* (non-Javadoc) 311 * @see org.semanticweb.owlapi.model.OWLClassExpressionVisitor#visit(org.semanticweb.owlapi.model.OWLObjectUnionOf) 312 */ 313 @Override 314 public void visit(OWLObjectUnionOf expr) { 315 } 316 317 /* (non-Javadoc) 318 * @see org.semanticweb.owlapi.model.OWLClassExpressionVisitor#visit(org.semanticweb.owlapi.model.OWLObjectComplementOf) 319 */ 320 @Override 321 public void visit(OWLObjectComplementOf expr) { 322 fireUnsupportedFeatureException(expr); 323 } 324 325 /* (non-Javadoc) 326 * @see org.semanticweb.owlapi.model.OWLClassExpressionVisitor#visit(org.semanticweb.owlapi.model.OWLObjectSomeValuesFrom) 327 */ 328 @Override 329 public void visit(OWLObjectSomeValuesFrom expr) { 330 QueryTree<String> parent = stack.peek(); 331 QueryTree<String> child; 332 OWLClassExpression filler = expr.getFiller(); 333 if(filler.isAnonymous()){ 334 if(!(filler instanceof OWLObjectIntersectionOf)){ 335 stack.push(new QueryTreeImpl<>("?", NodeType.VARIABLE, id++)); 336 } 337 expr.getFiller().accept(this); 338 child = stack.pop(); 339 } else { 340 child = new QueryTreeImpl<>(filler.asOWLClass().toStringID(), NodeType.RESOURCE, id++); 341 } 342 parent.addChild(child, expr.getProperty().asOWLObjectProperty().toStringID()); 343 } 344 345 /* (non-Javadoc) 346 * @see org.semanticweb.owlapi.model.OWLClassExpressionVisitor#visit(org.semanticweb.owlapi.model.OWLObjectAllValuesFrom) 347 */ 348 @Override 349 public void visit(OWLObjectAllValuesFrom expr) { 350 fireUnsupportedFeatureException(expr); 351 } 352 353 /* (non-Javadoc) 354 * @see org.semanticweb.owlapi.model.OWLClassExpressionVisitor#visit(org.semanticweb.owlapi.model.OWLObjectHasValue) 355 */ 356 @Override 357 public void visit(OWLObjectHasValue expr) { 358 QueryTree<String> tree = stack.peek(); 359 tree.addChild(new QueryTreeImpl<>(expr.getFiller().asOWLNamedIndividual().toStringID(), NodeType.RESOURCE, id++), expr.getProperty().asOWLObjectProperty().toStringID()); 360 } 361 362 /* (non-Javadoc) 363 * @see org.semanticweb.owlapi.model.OWLClassExpressionVisitor#visit(org.semanticweb.owlapi.model.OWLObjectMinCardinality) 364 */ 365 @Override 366 public void visit(OWLObjectMinCardinality expr) { 367 fireUnsupportedFeatureException(expr); 368 } 369 370 /* (non-Javadoc) 371 * @see org.semanticweb.owlapi.model.OWLClassExpressionVisitor#visit(org.semanticweb.owlapi.model.OWLObjectExactCardinality) 372 */ 373 @Override 374 public void visit(OWLObjectExactCardinality expr) { 375 fireUnsupportedFeatureException(expr); 376 } 377 378 /* (non-Javadoc) 379 * @see org.semanticweb.owlapi.model.OWLClassExpressionVisitor#visit(org.semanticweb.owlapi.model.OWLObjectMaxCardinality) 380 */ 381 @Override 382 public void visit(OWLObjectMaxCardinality expr) { 383 fireUnsupportedFeatureException(expr); 384 } 385 386 /* (non-Javadoc) 387 * @see org.semanticweb.owlapi.model.OWLClassExpressionVisitor#visit(org.semanticweb.owlapi.model.OWLObjectHasSelf) 388 */ 389 @Override 390 public void visit(OWLObjectHasSelf expr) { 391 } 392 393 /* (non-Javadoc) 394 * @see org.semanticweb.owlapi.model.OWLClassExpressionVisitor#visit(org.semanticweb.owlapi.model.OWLObjectOneOf) 395 */ 396 @Override 397 public void visit(OWLObjectOneOf expr) { 398 } 399 400 /* (non-Javadoc) 401 * @see org.semanticweb.owlapi.model.OWLClassExpressionVisitor#visit(org.semanticweb.owlapi.model.OWLDataSomeValuesFrom) 402 */ 403 @Override 404 public void visit(OWLDataSomeValuesFrom expr) { 405 QueryTree<String> tree = stack.peek(); 406 expr.getFiller().accept(this); 407 QueryTree<String> child = stack.pop(); 408 tree.addChild(child, expr.getProperty().asOWLDataProperty().toStringID()); 409 } 410 411 /* (non-Javadoc) 412 * @see org.semanticweb.owlapi.model.OWLClassExpressionVisitor#visit(org.semanticweb.owlapi.model.OWLDataAllValuesFrom) 413 */ 414 @Override 415 public void visit(OWLDataAllValuesFrom expr) { 416 fireUnsupportedFeatureException(expr); 417 } 418 419 /* (non-Javadoc) 420 * @see org.semanticweb.owlapi.model.OWLClassExpressionVisitor#visit(org.semanticweb.owlapi.model.OWLDataHasValue) 421 */ 422 @Override 423 public void visit(OWLDataHasValue expr) { 424 } 425 426 /* (non-Javadoc) 427 * @see org.semanticweb.owlapi.model.OWLClassExpressionVisitor#visit(org.semanticweb.owlapi.model.OWLDataMinCardinality) 428 */ 429 @Override 430 public void visit(OWLDataMinCardinality expr) { 431 fireUnsupportedFeatureException(expr); 432 } 433 434 /* (non-Javadoc) 435 * @see org.semanticweb.owlapi.model.OWLClassExpressionVisitor#visit(org.semanticweb.owlapi.model.OWLDataExactCardinality) 436 */ 437 @Override 438 public void visit(OWLDataExactCardinality expr) { 439 fireUnsupportedFeatureException(expr); 440 } 441 442 /* (non-Javadoc) 443 * @see org.semanticweb.owlapi.model.OWLClassExpressionVisitor#visit(org.semanticweb.owlapi.model.OWLDataMaxCardinality) 444 */ 445 @Override 446 public void visit(OWLDataMaxCardinality expr) { 447 fireUnsupportedFeatureException(expr); 448 } 449 450 /* (non-Javadoc) 451 * @see org.semanticweb.owlapi.model.OWLDataRangeVisitor#visit(org.semanticweb.owlapi.model.OWLDatatype) 452 */ 453 @Override 454 public void visit(OWLDatatype arg0) { 455 } 456 457 /* (non-Javadoc) 458 * @see org.semanticweb.owlapi.model.OWLDataRangeVisitor#visit(org.semanticweb.owlapi.model.OWLDataOneOf) 459 */ 460 @Override 461 public void visit(OWLDataOneOf arg0) { 462 } 463 464 /* (non-Javadoc) 465 * @see org.semanticweb.owlapi.model.OWLDataRangeVisitor#visit(org.semanticweb.owlapi.model.OWLDataComplementOf) 466 */ 467 @Override 468 public void visit(OWLDataComplementOf arg0) { 469 } 470 471 /* (non-Javadoc) 472 * @see org.semanticweb.owlapi.model.OWLDataRangeVisitor#visit(org.semanticweb.owlapi.model.OWLDataIntersectionOf) 473 */ 474 @Override 475 public void visit(OWLDataIntersectionOf arg0) { 476 } 477 478 /* (non-Javadoc) 479 * @see org.semanticweb.owlapi.model.OWLDataRangeVisitor#visit(org.semanticweb.owlapi.model.OWLDataUnionOf) 480 */ 481 @Override 482 public void visit(OWLDataUnionOf arg0) { 483 } 484 485 /* (non-Javadoc) 486 * @see org.semanticweb.owlapi.model.OWLDataRangeVisitor#visit(org.semanticweb.owlapi.model.OWLDatatypeRestriction) 487 */ 488 @Override 489 public void visit(OWLDatatypeRestriction arg0) { 490 } 491 492 public static void main(String[] args) throws Exception { 493 StringRenderer.setRenderer(Rendering.DL_SYNTAX); 494 OWLOntologyManager man = OWLManager.createOWLOntologyManager(); 495 OWLDataFactory df = man.getOWLDataFactory(); 496 PrefixManager pm = new DefaultPrefixManager(); 497 pm.setDefaultPrefix("http://example.org/"); 498 OWLClassExpression ce = df.getOWLObjectIntersectionOf( 499 df.getOWLClass("A", pm), 500 df.getOWLObjectSomeValuesFrom( 501 df.getOWLObjectProperty("p", pm), 502 df.getOWLObjectSomeValuesFrom( 503 df.getOWLObjectProperty("r", pm), 504 df.getOWLObjectIntersectionOf( 505 df.getOWLClass("A", pm), 506 df.getOWLClass("B", pm)))) 507 ); 508 System.out.println(ce); 509 QueryTreeConverter converter = new QueryTreeConverter(); 510 QueryTree<String> tree = converter.asQueryTree(ce); 511 tree.dump(); 512 } 513 514}