001 // This file is part of AceWiki. 002 // Copyright 2008-2012, AceWiki developers. 003 // 004 // AceWiki is free software: you can redistribute it and/or modify it under the terms of the GNU 005 // Lesser General Public License as published by the Free Software Foundation, either version 3 of 006 // the License, or (at your option) any later version. 007 // 008 // AceWiki is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without 009 // even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 010 // Lesser General Public License for more details. 011 // 012 // You should have received a copy of the GNU Lesser General Public License along with AceWiki. If 013 // not, see http://www.gnu.org/licenses/. 014 015 package ch.uzh.ifi.attempto.acewiki.owl; 016 017 import java.net.MalformedURLException; 018 import java.net.URL; 019 import java.util.ArrayList; 020 import java.util.Collections; 021 import java.util.HashMap; 022 import java.util.HashSet; 023 import java.util.LinkedHashMap; 024 import java.util.List; 025 import java.util.Map; 026 import java.util.Set; 027 028 import org.semanticweb.HermiT.Reasoner; 029 import org.semanticweb.owlapi.apibinding.OWLManager; 030 import org.semanticweb.owlapi.model.IRI; 031 import org.semanticweb.owlapi.model.OWLAxiom; 032 import org.semanticweb.owlapi.model.OWLClass; 033 import org.semanticweb.owlapi.model.OWLClassExpression; 034 import org.semanticweb.owlapi.model.OWLDataFactory; 035 import org.semanticweb.owlapi.model.OWLDeclarationAxiom; 036 import org.semanticweb.owlapi.model.OWLDifferentIndividualsAxiom; 037 import org.semanticweb.owlapi.model.OWLLogicalEntity; 038 import org.semanticweb.owlapi.model.OWLNamedIndividual; 039 import org.semanticweb.owlapi.model.OWLOntology; 040 import org.semanticweb.owlapi.model.OWLOntologyCreationException; 041 import org.semanticweb.owlapi.model.OWLOntologyManager; 042 import org.semanticweb.owlapi.owllink.OWLlinkHTTPXMLReasonerFactory; 043 import org.semanticweb.owlapi.owllink.OWLlinkReasonerConfiguration; 044 import org.semanticweb.owlapi.owllink.builtin.response.OWLlinkErrorResponseException; 045 import org.semanticweb.owlapi.profiles.OWL2ELProfile; 046 import org.semanticweb.owlapi.profiles.OWL2QLProfile; 047 import org.semanticweb.owlapi.profiles.OWL2RLProfile; 048 import org.semanticweb.owlapi.profiles.OWLProfile; 049 import org.semanticweb.owlapi.reasoner.OWLReasoner; 050 import org.semanticweb.owlapi.reasoner.OWLReasonerFactory; 051 import org.semanticweb.owlapi.util.Version; 052 053 import uk.ac.manchester.cs.owl.owlapi.OWLDataFactoryImpl; 054 import ch.uzh.ifi.attempto.acewiki.core.AceWikiReasoner; 055 import ch.uzh.ifi.attempto.acewiki.core.AnswerElement; 056 import ch.uzh.ifi.attempto.acewiki.core.Concept; 057 import ch.uzh.ifi.attempto.acewiki.core.InconsistencyException; 058 import ch.uzh.ifi.attempto.acewiki.core.Individual; 059 import ch.uzh.ifi.attempto.acewiki.core.LanguageUtils; 060 import ch.uzh.ifi.attempto.acewiki.core.Ontology; 061 import ch.uzh.ifi.attempto.acewiki.core.OntologyElement; 062 import ch.uzh.ifi.attempto.acewiki.core.Question; 063 import ch.uzh.ifi.attempto.acewiki.core.Sentence; 064 065 /** 066 * This is a reasoner implementation that connects to an OWL reasoner. At the moment, it can 067 * directly connect to HermiT and Pellet. Additionally, reasoners like FaCT++ can be accessed via 068 * the OWLlink interface. 069 * 070 * @author Tobias Kuhn 071 */ 072 public class AceWikiOWLReasoner implements AceWikiReasoner { 073 074 private static OWLDataFactory dataFactory = new OWLDataFactoryImpl(); 075 private static OWLlinkHTTPXMLReasonerFactory owllinkReasonerFactory; 076 077 private static Object owllinkReasonerSyncToken = new Object(); 078 079 private Ontology ontology; 080 081 private OWLOntologyManager manager; 082 private OWLOntology owlOntology; 083 private Map<OWLAxiom, Integer> axiomsMap = new HashMap<OWLAxiom, Integer>(); 084 private OWLReasoner owlReasoner; 085 private String reasonerType = "none"; 086 private Object reasonerSyncToken = new Object(); 087 private OWLDifferentIndividualsAxiom diffIndsAxiom; 088 private boolean diffIndsAxiomOutdated = true; 089 private OWLProfile owlProfile; 090 private String globalRestrPolicy; 091 private Map<String, String> infoMap = new LinkedHashMap<String, String>(); 092 093 /** 094 * Creates a new reasoner object. 095 */ 096 public AceWikiOWLReasoner() { 097 manager = OWLManager.createOWLOntologyManager(); 098 try { 099 owlOntology = manager.createOntology(); 100 } catch (OWLOntologyCreationException ex) { 101 ex.printStackTrace(); 102 } 103 } 104 105 public void init(Ontology ontology) { 106 this.ontology = ontology; 107 108 String p = (getParameter("owl_profile") + "").toLowerCase(); 109 if (p.equals("owl2el")) { 110 owlProfile = new OWL2ELProfile(); 111 } else if (p.equals("owl2ql")) { 112 owlProfile = new OWL2QLProfile(); 113 } else if (p.equals("owl2rl")) { 114 owlProfile = new OWL2RLProfile(); 115 } else { 116 owlProfile = null; 117 } 118 119 String grp = (getParameter("global_restrictions_policy") + "").toLowerCase(); 120 if (grp.equals("unchecked")) { 121 globalRestrPolicy = "unchecked"; 122 } else { 123 globalRestrPolicy = "no_chains"; 124 } 125 126 infoMap.put("global restrictions policy", globalRestrPolicy); 127 infoMap.put("OWL profile", getOWLProfileName()); 128 } 129 130 private List<OntologyElement> getOntologyElements() { 131 return ontology.getOntologyElements(); 132 } 133 134 private OntologyElement getOntologyElement(String name) { 135 return ontology.getElement(name); 136 } 137 138 private String getParameter(String name) { 139 return ontology.getParameter(name); 140 } 141 142 public Map<String, String> getInfo() { 143 return infoMap; 144 } 145 146 /** 147 * Returns a string representing the policy how to enforce the global restrictions on the 148 * axioms in OWL 2. 149 * 150 * @return The global restrictions policy. 151 */ 152 public String getGlobalRestrictionsPolicy() { 153 return globalRestrPolicy; 154 } 155 156 private IRI getIRI() { 157 return IRI.create(ontology.getURI()); 158 } 159 160 /** 161 * Returns the OWL profile that defines which statements are used for reasoning, or null if the 162 * full language of OWL is used. 163 * 164 * @return The used OWL profile. 165 */ 166 public OWLProfile getOWLProfile() { 167 return owlProfile; 168 } 169 170 /** 171 * Returns the name of the current OWL profile. 172 * 173 * @return The OWL profile name. 174 */ 175 public String getOWLProfileName() { 176 if (owlProfile == null) return "OWL 2 Full"; 177 return owlProfile.getName(); 178 } 179 180 /** 181 * Returns a new OWL ontology object representing the full ontology or the consistent part of 182 * it. 183 * 184 * @param consistent true if only the consistent part should be exported. 185 * @return An OWL ontology object of the full ontology. 186 */ 187 public OWLOntology exportOWLOntology(boolean consistent) { 188 Set<OWLAxiom> axioms = new HashSet<OWLAxiom>(); 189 for (OntologyElement el : getOntologyElements()) { 190 OWLDeclarationAxiom owlDecl = null; 191 if (el instanceof OWLOntoElement) { 192 owlDecl = ((OWLOntoElement) el).getOWLDeclaration(); 193 } 194 if (owlDecl != null) { 195 axioms.add(owlDecl); 196 } 197 for (Sentence s : el.getArticle().getSentences()) { 198 OWLSentence os = (OWLSentence) s; 199 if (os instanceof Question || !os.isOWL()) continue; 200 if (consistent && (!os.isReasonable() || !os.isIntegrated())) continue; 201 axioms.addAll(os.getOWLAxioms()); 202 } 203 } 204 axioms.add(diffIndsAxiom); 205 206 OWLOntology o = null; 207 try { 208 o = manager.createOntology(axioms, getIRI()); 209 manager.removeOntology(o); 210 } catch (Exception ex) { 211 ex.printStackTrace(); 212 } 213 214 return o; 215 } 216 217 private OntologyElement get(OWLLogicalEntity owlEntity) { 218 if (owlEntity == null) return null; 219 if (owlEntity.isTopEntity() || owlEntity.isBottomEntity()) return null; 220 String iri = owlEntity.getIRI().toString(); 221 if (!iri.startsWith(ontology.getURI())) return null; 222 String name = iri.substring(iri.indexOf("#") + 1); 223 return getOntologyElement(name); 224 } 225 226 /** 227 * Returns the OWL ontology manager. 228 * 229 * @return The OWL ontology manager. 230 */ 231 public OWLOntologyManager getOWLOntologyManager() { 232 return manager; 233 } 234 235 public String getReasonerName() { 236 if (owlReasoner == null) return null; 237 return owlReasoner.getReasonerName(); 238 } 239 240 public String getReasonerVersion() { 241 if (owlReasoner == null) return null; 242 Version v = owlReasoner.getReasonerVersion(); 243 if (v == null) return null; 244 return v.getMajor() + "." + v.getMinor() + "." + v.getPatch() + "." + v.getBuild(); 245 } 246 247 public String getReasonerType() { 248 return reasonerType; 249 } 250 251 public void load() { 252 log("loading reasoner"); 253 String type = getParameter("reasoner"); 254 if (type == null) type = ""; 255 type = type.toLowerCase(); 256 reasonerType = type; 257 258 String s = getParameter("reasoner_url"); 259 if (s == null || s.length() == 0) s = "http://localhost:8080"; 260 URL url = null; 261 try { 262 url = new URL(s); 263 } catch (MalformedURLException ex) { ex.printStackTrace(); } 264 265 if (owlReasoner != null) owlReasoner.dispose(); 266 267 if (type.equals("none")) { 268 log("no reasoner"); 269 reasonerType = "none"; 270 owlReasoner = null; 271 } else if (type.equals("hermit")) { 272 log("loading HermiT"); 273 reasonerType = "HermiT"; 274 owlReasoner = new Reasoner(owlOntology); 275 } else if (type.equals("pellet")) { 276 log("loading Pellet"); 277 reasonerType = "Pellet"; 278 // The Pellet libraries are not part of the AceWiki package (because of license 279 // reasons). For that reason, the pellet reasoner has to be loaded dynamically. 280 OWLReasonerFactory reasonerFactory = null; 281 try { 282 ClassLoader classLoader = Ontology.class.getClassLoader(); 283 String className = "com.clarkparsia.pellet.owlapiv3.PelletReasonerFactory"; 284 reasonerFactory = (OWLReasonerFactory) classLoader.loadClass(className).newInstance(); 285 } catch (Exception ex) { 286 ex.printStackTrace(); 287 } 288 owlReasoner = reasonerFactory.createNonBufferingReasoner(owlOntology); 289 } else if (type.equals("owllink")) { 290 log("loading OWLlink"); 291 reasonerType = "OWLlink"; 292 if (owllinkReasonerFactory == null) { 293 owllinkReasonerFactory = new OWLlinkHTTPXMLReasonerFactory(); 294 } 295 OWLlinkReasonerConfiguration config = new OWLlinkReasonerConfiguration(url); 296 owlReasoner = owllinkReasonerFactory.createReasoner(owlOntology, config); 297 // reasoner calls over OWLlink have to be synchronized: 298 reasonerSyncToken = owllinkReasonerSyncToken; 299 //} else if (type.equals("dig")) { 300 //try { 301 // reasoner = new DIGReasoner(OWLManager.createOWLOntologyManager()); 302 // ((DIGReasoner) reasoner).getReasoner().setReasonerURL(url); 303 //} catch (Exception ex) { ex.printStackTrace(); } 304 } else if (type.equals("")) { 305 log("no reasoner type specified: loading HermiT as default"); 306 reasonerType = "HermiT"; 307 owlReasoner = new Reasoner(owlOntology); 308 } else { 309 log("ERROR: Unknown reasoner type: " + type); 310 reasonerType = "none"; 311 owlReasoner = null; 312 } 313 updateDifferentIndividualsAxiom(); 314 flush(); 315 316 log("reasoner loaded"); 317 } 318 319 /** 320 * Updates the axiom that states that all named individuals are different. Thus, unique 321 * name assumption is applied. 322 */ 323 private synchronized void updateDifferentIndividualsAxiom() { 324 if (!diffIndsAxiomOutdated) return; 325 326 if (diffIndsAxiom != null) { 327 unloadAxiom(diffIndsAxiom); 328 } 329 330 Set<OWLNamedIndividual> inds = new HashSet<OWLNamedIndividual>(); 331 for (OntologyElement oe : getOntologyElements()) { 332 if (oe instanceof OWLIndividual) { 333 inds.add(((OWLIndividual) oe).getOWLRepresentation()); 334 } 335 } 336 diffIndsAxiom = dataFactory.getOWLDifferentIndividualsAxiom(inds); 337 338 loadAxiom(diffIndsAxiom); 339 340 diffIndsAxiomOutdated = false; 341 } 342 343 public void flushElements() { 344 flush(); 345 } 346 347 private void flush() { 348 if (owlReasoner != null) { 349 synchronized (reasonerSyncToken) { 350 owlReasoner.flush(); 351 } 352 } 353 } 354 355 public void loadElement(OntologyElement element) { 356 OWLDeclarationAxiom owlDecl = null; 357 if (element instanceof OWLOntoElement) { 358 owlDecl = ((OWLOntoElement) element).getOWLDeclaration(); 359 } 360 if (owlDecl != null) { 361 manager.addAxiom(owlOntology, owlDecl); 362 flush(); 363 } 364 if (element instanceof OWLIndividual) { 365 diffIndsAxiomOutdated = true; 366 } 367 } 368 369 public void unloadElement(OntologyElement element) { 370 OWLDeclarationAxiom owlDecl = null; 371 if (element instanceof OWLOntoElement) { 372 owlDecl = ((OWLOntoElement) element).getOWLDeclaration(); 373 } 374 if (owlDecl != null) { 375 manager.removeAxiom(owlOntology, owlDecl); 376 flush(); 377 } 378 if (element instanceof OWLIndividual) { 379 diffIndsAxiomOutdated = true; 380 } 381 } 382 383 public synchronized List<Concept> getConcepts(Individual ind) { 384 List<Concept> concepts = new ArrayList<Concept>(); 385 OWLIndividual owlInd = (OWLIndividual) ind; 386 for (OWLClass oc : getConcepts(owlInd.getOWLRepresentation())) { 387 if (oc.isOWLThing() || oc.isOWLNothing()) continue; 388 String conceptURI = oc.getIRI().toString(); 389 if (conceptURI.startsWith("http://attempto.ifi.uzh.ch/ace#")) continue; 390 String conceptName = conceptURI.substring(conceptURI.indexOf("#") + 1); 391 concepts.add((Concept) getOntologyElement(conceptName)); 392 } 393 return concepts; 394 } 395 396 private synchronized Set<OWLClass> getConcepts(OWLNamedIndividual owlInd) { 397 if (owlReasoner == null) { 398 return Collections.emptySet(); 399 } else { 400 synchronized (reasonerSyncToken) { 401 return owlReasoner.getTypes(owlInd, false).getFlattened(); 402 } 403 } 404 } 405 406 public synchronized List<Individual> getIndividuals(Concept concept) { 407 OWLConcept ac = (OWLConcept) concept; 408 List<Individual> inds = new ArrayList<Individual>(); 409 for (OWLNamedIndividual oi : getIndividuals(ac.getOWLRepresentation())) { 410 OntologyElement oe = get(oi); 411 if (oe instanceof Individual) { 412 inds.add((Individual) oe); 413 } 414 } 415 return inds; 416 } 417 418 private synchronized Set<OWLNamedIndividual> getIndividuals(OWLClassExpression owlClass) { 419 if (owlReasoner == null) { 420 return Collections.emptySet(); 421 } else { 422 synchronized (reasonerSyncToken) { 423 return owlReasoner.getInstances(owlClass, false).getFlattened(); 424 } 425 } 426 } 427 428 public synchronized List<Concept> getSuperConcepts(Concept concept) { 429 OWLConcept ac = (OWLConcept) concept; 430 List<Concept> concepts = new ArrayList<Concept>(); 431 for (OWLClass oc : getSuperConcepts(ac.getOWLRepresentation())) { 432 OntologyElement oe = get(oc); 433 if (oe instanceof Concept) { 434 concepts.add((Concept) oe); 435 } 436 } 437 return concepts; 438 } 439 440 private synchronized Set<OWLClass> getSuperConcepts(OWLClass owlClass) { 441 if (owlReasoner == null) { 442 return Collections.emptySet(); 443 } else { 444 synchronized (reasonerSyncToken) { 445 return owlReasoner.getSuperClasses(owlClass, false).getFlattened(); 446 } 447 } 448 } 449 450 public synchronized List<Concept> getSubConcepts(Concept concept) { 451 OWLConcept ac = (OWLConcept) concept; 452 List<Concept> concepts = new ArrayList<Concept>(); 453 for (OWLClass oc : getSubConcepts(ac.getOWLRepresentation())) { 454 OntologyElement oe = get(oc); 455 if (oe instanceof Concept) { 456 concepts.add((Concept) oe); 457 } 458 } 459 return concepts; 460 } 461 462 private synchronized Set<OWLClass> getSubConcepts(OWLClass owlClass) { 463 if (owlReasoner == null) { 464 return Collections.emptySet(); 465 } else { 466 synchronized (reasonerSyncToken) { 467 return owlReasoner.getSubClasses(owlClass, false).getFlattened(); 468 } 469 } 470 } 471 472 public synchronized List<AnswerElement> getAnswer(Question q) { 473 if (owlReasoner == null) return null; 474 475 OWLQuestion question = (OWLQuestion) q; 476 477 OWLNamedIndividual quInd = question.getQuestionOWLIndividual(); 478 OWLClassExpression quClass = question.getQuestionOWLClass(); 479 List<AnswerElement> list = new ArrayList<AnswerElement>(); 480 481 if (quInd != null) { 482 for (OWLClass oc : getConcepts(quInd)) { 483 OntologyElement oe = get(oc); 484 if (oe instanceof OWLConcept) { 485 list.add((OWLConcept) oe); 486 } 487 } 488 } else if (quClass != null) { 489 Set<OWLNamedIndividual> owlInds = getIndividuals(quClass); 490 for (OWLNamedIndividual oi : owlInds) { 491 OntologyElement oe = get(oi); 492 if (oe instanceof OWLIndividual) { 493 list.add((OWLIndividual) oe); 494 } 495 } 496 } 497 498 LanguageUtils.sortOntologyElements(list); 499 return list; 500 } 501 502 public synchronized boolean isConsistent() { 503 if (owlReasoner == null) return true; 504 boolean c = true; 505 try { 506 synchronized (reasonerSyncToken) { 507 // The method isConsistent is poorly supported by the implementations. 508 //c = reasoner.isConsistent(); 509 c = owlReasoner.isSatisfiable(dataFactory.getOWLThing()); 510 } 511 } catch (Exception ex) { 512 c = false; 513 } 514 return c; 515 } 516 517 public synchronized boolean isSatisfiable(Concept concept) { 518 if (owlReasoner == null) return true; 519 if (!(concept instanceof OWLConcept)) return false; 520 if (owlOntology.containsClassInSignature(((OWLConcept) concept).getIRI())) { 521 synchronized (reasonerSyncToken) { 522 OWLConcept ac = (OWLConcept) concept; 523 return owlReasoner.isSatisfiable(ac.getOWLRepresentation()); 524 } 525 } else { 526 return true; 527 } 528 } 529 530 public void loadSentence(Sentence s) { 531 OWLSentence sentence = (OWLSentence) s; 532 try { 533 for (OWLAxiom ax : sentence.getOWLAxioms()) { 534 loadAxiom(ax); 535 } 536 flush(); 537 } catch (OWLlinkErrorResponseException ex) { 538 // FaCT++ throws an exception here when inconsistency is encountered 539 // TODO Is this always the case? 540 if ("FaCT++.Kernel: inconsistent ontology".equals(ex.getMessage())) { 541 throw new InconsistencyException(); 542 } else { 543 // We get here when the global restrictions are violated with FaCT++ and OWLlink 544 throw ex; 545 } 546 } catch (IllegalArgumentException ex) { 547 // We get here when the global restrictions are violated with HermiT 548 throw ex; 549 } 550 } 551 552 public void unloadSentence(Sentence s) { 553 OWLSentence sentence = (OWLSentence) s; 554 for (OWLAxiom ax : sentence.getOWLAxioms()) { 555 unloadAxiom(ax); 556 } 557 flush(); 558 } 559 560 private void loadAxiom(OWLAxiom ax) { 561 Integer count = axiomsMap.get(ax); 562 if (count == null) count = 0; 563 if (count == 0) { 564 manager.addAxiom(owlOntology, ax); 565 } 566 axiomsMap.put(ax, count+1); 567 } 568 569 private void unloadAxiom(OWLAxiom ax) { 570 Integer count = axiomsMap.get(ax); 571 if (count == 1) { 572 manager.removeAxiom(owlOntology, ax); 573 } 574 axiomsMap.put(ax, count-1); 575 } 576 577 private void log(String text) { 578 ontology.log(text); 579 } 580 581 }