001 // This file is part of the Attempto Java Packages. 002 // Copyright 2008-2009, Attempto Group, University of Zurich (see http://attempto.ifi.uzh.ch). 003 // 004 // The Attempto Java Packages is free software: you can redistribute it and/or modify it under the 005 // terms of the GNU Lesser General Public License as published by the Free Software Foundation, 006 // either version 3 of the License, or (at your option) any later version. 007 // 008 // The Attempto Java Packages is distributed in the hope that it will be useful, but WITHOUT ANY 009 // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 010 // PURPOSE. See the GNU Lesser General Public License for more details. 011 // 012 // You should have received a copy of the GNU Lesser General Public License along with the Attempto 013 // Java Packages. If not, see http://www.gnu.org/licenses/. 014 015 package ch.uzh.ifi.attempto.acewiki.core.ontology; 016 017 import java.io.File; 018 import java.io.FileInputStream; 019 import java.io.FileOutputStream; 020 import java.io.IOException; 021 import java.io.StringWriter; 022 import java.net.URI; 023 import java.net.URISyntaxException; 024 import java.util.ArrayList; 025 import java.util.Collection; 026 import java.util.Collections; 027 import java.util.HashMap; 028 import java.util.HashSet; 029 import java.util.Hashtable; 030 import java.util.List; 031 import java.util.Set; 032 033 import org.coode.owlapi.owlxml.renderer.OWLXMLRenderer; 034 import org.mindswap.pellet.owlapi.Reasoner; 035 import org.semanticweb.owl.apibinding.OWLManager; 036 import org.semanticweb.owl.inference.OWLReasoner; 037 import org.semanticweb.owl.inference.OWLReasonerException; 038 import org.semanticweb.owl.io.StringInputSource; 039 import org.semanticweb.owl.model.OWLClass; 040 import org.semanticweb.owl.model.OWLDescription; 041 import org.semanticweb.owl.model.OWLIndividual; 042 import org.semanticweb.owl.model.OWLObjectOneOf; 043 import org.semanticweb.owl.model.OWLOntology; 044 import org.semanticweb.owl.model.OWLOntologyCreationException; 045 import org.semanticweb.owl.model.OWLOntologySetProvider; 046 import org.semanticweb.owl.model.OWLSubClassAxiom; 047 import org.semanticweb.owl.util.OWLOntologyMerger; 048 049 import uk.ac.manchester.cs.owl.OWLClassImpl; 050 import uk.ac.manchester.cs.owl.OWLDataFactoryImpl; 051 import ch.uzh.ifi.attempto.ape.LexiconEntry; 052 import ch.uzh.ifi.attempto.echocomp.Logger; 053 054 /** 055 * This class represents an AceWiki ontology which consists of ontology element definitions and 056 * of ontological statements. Each ontology element has its own article that consists of ontological 057 * statements. 058 * 059 * @author Tobias Kuhn 060 */ 061 public class Ontology { 062 063 private static final HashMap<String, Ontology> ontologies = new HashMap<String, Ontology>(); 064 065 private ArrayList<OntologyElement> elements = new ArrayList<OntologyElement>(); 066 private Hashtable<String, OntologyElement> wordIndex = new Hashtable<String, OntologyElement>(); 067 private Hashtable<Long, OntologyElement> idIndex = new Hashtable<Long, OntologyElement>(); 068 069 private final String name; 070 private final String baseURI; 071 private long idCount = 0; 072 private long stateID = 0; 073 074 private OWLReasoner reasoner; 075 private OWLOntology differentIndividualsAxiom; 076 077 /** 078 * Creates a new empty ontology with the given name and base URI. 079 * 080 * @param name The name of the ontology. 081 * @param baseURI The base URI that is used to identify the ontology elements. 082 */ 083 private Ontology(String name, String baseURI) { 084 this.name = name.toString(); // null value throws an exception 085 this.baseURI = baseURI; 086 if (baseURI == null) { 087 baseURI = ""; 088 } 089 ontologies.put(name, this); 090 } 091 092 /** 093 * Loads an ontology (or creates an empty ontology if the ontology cannot be found). The complete 094 * URI of the ontology is baseURI + name. 095 * 096 * @param name The name of the ontology. 097 * @param baseURI The base URI that is used to identify the ontology elements. 098 * @return The loaded ontology. 099 */ 100 public synchronized static Ontology loadOntology(String name, String baseURI) { 101 if (ontologies.get(name) != null) { 102 return ontologies.get(name); 103 } 104 Ontology ontology = new Ontology(name, baseURI); 105 ontology.log("loading ontology"); 106 File dataDir = new File("data/" + name); 107 if (dataDir.exists()) { 108 for (File file : dataDir.listFiles()) { 109 try { 110 long id = new Long(file.getName()); 111 ontology.log("reading file: " + file.getName()); 112 FileInputStream in = new FileInputStream(file); 113 byte[] bytes = new byte[in.available()]; 114 in.read(bytes); 115 in.close(); 116 String s = new String(bytes, "UTF-8"); 117 OntologyElement.loadOntologyElement(s, id, ontology); 118 } catch (NumberFormatException ex) { 119 ontology.log("ignoring file: " + file.getName()); 120 } catch (IOException ex) { 121 ontology.log("cannot read file: " + file.getName()); 122 } 123 } 124 } else { 125 ontology.log("no data found; blank ontology is created"); 126 } 127 ontology.refreshReasoner(); 128 return ontology; 129 } 130 131 synchronized void save(OntologyElement oe) { 132 if (!(new File("data")).exists()) (new File("data")).mkdir(); 133 if (!(new File("data/" + name)).exists()) (new File("data/" + name)).mkdir(); 134 135 if (!elements.contains(oe)) { 136 (new File("data/" + name + "/" + oe.getId())).delete(); 137 return; 138 } 139 140 try { 141 FileOutputStream out = new FileOutputStream("data/" + name + "/" + oe.getId()); 142 out.write(oe.serialize().getBytes("UTF-8")); 143 out.close(); 144 } catch (IOException ex) { 145 ex.printStackTrace(); 146 } 147 } 148 149 synchronized void register(OntologyElement element) { 150 if (elements.contains(element)) { 151 log("error: element already registered"); 152 throw new RuntimeException("Registration failed: Element is already registered."); 153 } 154 155 log("register: " + element); 156 stateID++; 157 158 if (element.getId() == -1) { 159 element.setId(nextId()); 160 } 161 elements.add(element); 162 idIndex.put(element.getId(), element); 163 if (element.getId() > idCount) idCount = element.getId(); 164 165 for (String word : element.getWords()) { 166 if (word == null) continue; 167 168 if (wordIndex.get(word) == null) { 169 wordIndex.put(word, element); 170 } else if (wordIndex.get(word) != element) { 171 log("error: word already used"); 172 throw new RuntimeException("Registration failed: The word '" + word + "' is already used."); 173 } 174 } 175 176 if (element instanceof Individual) { 177 updateDifferentIndividualsAxiom(); 178 } 179 180 } 181 182 synchronized void removeFromWordIndex(OntologyElement oe) { 183 for (String word : oe.getWords()) { 184 if (word != null) { 185 wordIndex.remove(word); 186 } 187 } 188 } 189 190 synchronized void addToWordIndex(OntologyElement oe) { 191 for (String word : oe.getWords()) { 192 if (word != null) { 193 if (wordIndex.get(word) == null) { 194 wordIndex.put(word, oe); 195 } else if (wordIndex.get(word) != oe) { 196 throw new RuntimeException("Word update failed: The word '" + word + "' is already used."); 197 } 198 } 199 } 200 } 201 202 /** 203 * Removes the given ontology element from the ontology. 204 * 205 * @param element The ontology element to be removed. 206 */ 207 public synchronized void remove(OntologyElement element) { 208 if (!elements.contains(element)) { 209 log("error: unknown element"); 210 return; 211 } 212 213 log("remove: " + element.getWord()); 214 stateID++; 215 216 for (String word : element.getWords()) { 217 if (word == null) continue; 218 wordIndex.remove(word); 219 } 220 elements.remove(element); 221 idIndex.remove(element.getId()); 222 for (Sentence s : element.getSentences()) { 223 retractSentence(s); 224 } 225 save(element); 226 227 if (element instanceof Individual) { 228 updateDifferentIndividualsAxiom(); 229 } 230 231 } 232 233 /** 234 * Returns all the sentences that use the given word form (by word number) of the given 235 * ontology element. 236 * 237 * @param element The ontology element. 238 * @param wordNumber The word number. 239 * @return A list of all sentence that contain the word. 240 */ 241 public synchronized List<Sentence> getReferences(OntologyElement element, int wordNumber) { 242 List<Sentence> list = new ArrayList<Sentence>(); 243 for (OntologyElement el : elements) { 244 for (Sentence s : el.getSentences()) { 245 if ((wordNumber == -1 && s.contains(element)) || (wordNumber > -1 && s.contains(element, wordNumber))) { 246 list.add(s); 247 } 248 } 249 } 250 return list; 251 } 252 253 /** 254 * Returns all the sentences that use the given ontology element (no matter which word form 255 * is used). 256 * 257 * @param element The ontology element. 258 * @return A list of all sentence that contain the ontology element. 259 */ 260 public synchronized List<Sentence> getReferences(OntologyElement element) { 261 return getReferences(element, -1); 262 } 263 264 /** 265 * Returns the ontology element with the given name, or null if there is no such element. 266 * 267 * @param name The name of the ontology element. 268 * @return The ontology element. 269 */ 270 public OntologyElement get(String name) { 271 return wordIndex.get(name); 272 } 273 274 OntologyElement get(long id) { 275 return idIndex.get(id); 276 } 277 278 /** 279 * Returns all ontology elements. 280 * 281 * @return A collection of all ontology elements. 282 */ 283 public Collection<OntologyElement> getOntologyElements() { 284 return new ArrayList<OntologyElement>(elements); 285 } 286 287 /** 288 * Returns true if the given ontology element is contained by the ontology (identity check). 289 * 290 * @param ontologyElement The ontology element. 291 * @return true if the ontology element is contained by the ontology. 292 */ 293 public boolean contains(OntologyElement ontologyElement) { 294 return elements.contains(ontologyElement); 295 } 296 297 /** 298 * Returns the name of the ontology. 299 * 300 * @return The name of the ontology. 301 */ 302 public String getName() { 303 return name; 304 } 305 306 /** 307 * Returns the URI of the ontology (baseURI + name). 308 * 309 * @return The URI of the ontology. 310 */ 311 public String getURI() { 312 return baseURI + name; 313 } 314 315 /** 316 * Returns the complete ontology as an OWL/XML formatted string. 317 * 318 * @param onlyConsistentSubset If true then only the consistent part of the ontology is included. 319 * @return A string that contains the complete ontology in OWL/XML format. 320 */ 321 public synchronized String getOWLOntologyAsXML(boolean onlyConsistentSubset) { 322 StringWriter sw = new StringWriter(); 323 try { 324 OWLXMLRenderer renderer = new OWLXMLRenderer(OWLManager.createOWLOntologyManager()); 325 renderer.render(getOWLOntology(onlyConsistentSubset), sw); 326 sw.close(); 327 } catch (Exception ex) { 328 ex.printStackTrace(); 329 } 330 return sw.toString(); 331 } 332 333 /** 334 * Returns an OWL ontology object that contains the complete ontology. 335 * 336 * @param onlyConsistentSubset If true then only the consistent part of the ontology is included. 337 * @return An OWL ontology object containing the complete ontology. 338 */ 339 public synchronized OWLOntology getOWLOntology(final boolean onlyConsistentSubset) { 340 OWLOntologySetProvider setProvider = new OWLOntologySetProvider() { 341 342 public Set<OWLOntology> getOntologies() { 343 HashSet<OWLOntology> ontologies = new HashSet<OWLOntology>(); 344 for (OntologyElement el : elements) { 345 for (Sentence s : el.getSentences()) { 346 if (s.isQuestion() || !s.isOWL()) continue; 347 if (onlyConsistentSubset && (!s.isReasonerParticipant() || !s.isIntegrated()) ) continue; 348 349 OWLOntology o = s.getOWLOntology(); 350 if (o != null) ontologies.add(o); 351 } 352 } 353 ontologies.add(differentIndividualsAxiom); 354 return ontologies; 355 } 356 357 }; 358 359 URI uri = null; 360 try { 361 uri = new URI("http://attempto.ifi.uzh.ch/default/"); 362 uri = new URI(getURI()); 363 } catch (URISyntaxException ex) { 364 ex.printStackTrace(); 365 } 366 367 OWLOntology owlOntology = null; 368 try { 369 OWLOntologyMerger ontologyMerger = new OWLOntologyMerger(setProvider); 370 owlOntology = ontologyMerger.createMergedOntology(OWLManager.createOWLOntologyManager(), uri); 371 } catch (Exception ex) { 372 ex.printStackTrace(); 373 } 374 return owlOntology; 375 } 376 377 /** 378 * Returns the complete ontology as one ACE text. 379 * 380 * @param onlyConsistentSubset If true then only the consistent part of the ontology is included. 381 * @return A string that contains the complete ontology as an ACE text. 382 */ 383 public synchronized String getACEText(boolean onlyConsistentSubset) { 384 String t = ""; 385 @SuppressWarnings (value="unchecked") 386 ArrayList<OntologyElement> elementsCopy = (ArrayList<OntologyElement>) elements.clone(); 387 Collections.sort(elementsCopy); 388 for (OntologyElement oe : elementsCopy) { 389 String heading = "\n# " + oe.getHeadword() + "\n\n"; 390 for (Sentence s : oe.getSentences()) { 391 if (!onlyConsistentSubset || (s.isIntegrated() && s.isReasonerParticipant()) ) { 392 if (heading != null) { 393 t += heading; 394 heading = null; 395 } 396 t += s.getText() + "\n\n"; 397 } 398 } 399 } 400 return t; 401 } 402 403 /** 404 * Returns the lexicon definition for all ontology elements in the ACE lexicon format. 405 * 406 * @return A string that contains the lexicon definition. 407 */ 408 public synchronized String getLexiconDef() { 409 ArrayList<String> lexiconEntries = new ArrayList<String>(); 410 for (OntologyElement oe : elements) { 411 for (LexiconEntry le : oe.getLexiconEntries()) { 412 if (!lexiconEntries.contains(le.toString())) { 413 lexiconEntries.add(le.toString()); 414 } 415 } 416 } 417 Collections.sort(lexiconEntries); 418 String t = ""; 419 for (String s : lexiconEntries) { 420 t += s + ".\n"; 421 } 422 return t; 423 } 424 425 private synchronized void refreshReasoner() { 426 log("refresh reasoner"); 427 428 if (reasoner == null) { 429 // for Pellet: 430 reasoner = new Reasoner(OWLManager.createOWLOntologyManager()); 431 // for HermiT: (doesn't work for some reason...) 432 //reasoner = (new ReasonerFactory()).createReasoner(OWLManager.createOWLOntologyManager()); 433 } else { 434 clearOntologies(); 435 } 436 updateDifferentIndividualsAxiom(); 437 438 log("reasoner: loading statements"); 439 HashSet<OWLOntology> ontologies = new HashSet<OWLOntology>(); 440 441 for (OntologyElement oe : elements) { 442 for (Sentence s : oe.getSentences()) { 443 if (s.isReasonerParticipant() && s.isIntegrated()) { 444 OWLOntology o = s.getOWLOntology(); 445 if (o != null) ontologies.add(o); 446 } 447 } 448 } 449 try { 450 reasoner.loadOntologies(ontologies); 451 } catch (OWLReasonerException ex) { 452 ex.printStackTrace(); 453 } 454 log("reasoner: statements loaded"); 455 } 456 457 /** 458 * Refreshes the given ontology element. All sentences that use the ontology element are 459 * updated. 460 * 461 * @param element The ontology element to be refreshed. 462 */ 463 synchronized void refresh(OntologyElement element) { 464 for (Sentence s : getReferences(element)) { 465 if (s.isIntegrated()) { 466 retractSentence(s); 467 s.parse(); 468 commitSentence(s); 469 } else { 470 s.parse(); 471 } 472 } 473 save(element); 474 } 475 476 /** 477 * Uses the ontology manager to read an OWL ontology from a string (that contains an ontology 478 * in OWL-XML format). 479 * 480 * @param owlxml The serialized OWL-XML ontology. 481 * @return The OWL ontology object. 482 * @throws OWLOntologyCreationException If the string cannot be parsed. 483 */ 484 public OWLOntology readOWLOntology(String owlxml) throws OWLOntologyCreationException { 485 return OWLManager.createOWLOntologyManager().loadOntology(new StringInputSource(owlxml)); 486 } 487 488 /** 489 * Commits the sentence. This means that it is added to the reasoner. An integer value is returned 490 * that denotes the success or failure of the operation: 491 * 0 is returned if the operation succeeds. 492 * 1 is returned if it fails because the sentence introduces inconsistency into the knowledge base. 493 * 2 is returned if the reasoner runs out of memory (this can occur sometimes with large ontologies). 494 * 495 * @param sentence The sentence to be commited. 496 * @return An integer value denoting the success/failure of the operation. 497 */ 498 protected synchronized int commitSentence(Sentence sentence) { 499 if (reasoner == null || sentence == null || sentence.isIntegrated()) return 0; 500 501 if (!sentence.isReasonerParticipant()) { 502 sentence.setIntegrated(true); 503 return 0; 504 } 505 506 log("commit sentence"); 507 508 try { 509 loadOntology(sentence.getOWLOntology()); 510 } catch (OutOfMemoryError err) { 511 log("error: out of memory"); 512 System.gc(); 513 refreshReasoner(); 514 return 2; 515 } 516 517 log("check for consistency"); 518 if (isConsistent()) { 519 log("consistent!"); 520 sentence.setIntegrated(true); 521 stateID++; 522 return 0; 523 } else { 524 log("not consistent!"); 525 unloadOntology(sentence.getOWLOntology()); 526 return 1; 527 } 528 } 529 530 /** 531 * Retracts the sentence. This means that the sentence is removed from the reasoner. 532 * 533 * @param sentence The sentence to be retracted. 534 */ 535 protected synchronized void retractSentence(Sentence sentence) { 536 if ( 537 reasoner == null || 538 sentence == null || 539 !sentence.isIntegrated() || 540 !sentence.isReasonerParticipant() 541 ) return; 542 543 log("retract sentence"); 544 stateID++; 545 unloadOntology(sentence.getOWLOntology()); 546 sentence.setIntegrated(false); 547 } 548 549 void log(String text) { 550 Logger.log(name, "onto", 0, "onto", text); 551 } 552 553 /** 554 * Updates the axiom that states that all named individuals are different. Thus, unique 555 * name assumption is applied. 556 */ 557 private synchronized void updateDifferentIndividualsAxiom() { 558 if (reasoner == null) return; 559 560 if (differentIndividualsAxiom != null) { 561 unloadOntology(differentIndividualsAxiom); 562 } 563 564 String owlString = 565 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n" + 566 "<Ontology " + 567 "xml:base=\"http://www.w3.org/2006/12/owl11-xml#\" " + 568 "xmlns=\"http://www.w3.org/2006/12/owl11-xml#\" " + 569 "URI=\"" + getURI() + "/different_individuals/" + stateID + "\">\n" + 570 "\t<DifferentIndividuals>\n"; 571 for (OntologyElement oe : getOntologyElements()) { 572 if (oe instanceof Individual) { 573 String word = ((Individual) oe).getWord(); 574 if (word.startsWith("the ")) word = word.substring(4); 575 owlString += "\t\t<Individual URI=\"" + ((Individual) oe).getURI() + "\" />\n"; 576 } 577 } 578 owlString += 579 "\t</DifferentIndividuals>\n" + 580 "</Ontology>"; 581 582 try { 583 differentIndividualsAxiom = readOWLOntology(owlString); 584 loadOntology(differentIndividualsAxiom); 585 } catch (OWLOntologyCreationException ex) { 586 log("unexpected error"); 587 ex.printStackTrace(); 588 } 589 } 590 591 /** 592 * Returns all concepts the given individual belongs to. The reasoner is used for this. 593 * 594 * @param ind The individual. 595 * @return A list of all concepts of the individual. 596 * @see Individual#getConcepts() 597 */ 598 public synchronized List<Concept> getConcepts(Individual ind) { 599 OWLIndividual owlIndividual = (new OWLDataFactoryImpl()).getOWLIndividual(ind.getURI()); 600 ArrayList<Concept> concepts = new ArrayList<Concept>(); 601 try { 602 Set<Set<OWLClass>> owlClasses = reasoner.getTypes(owlIndividual, false); 603 for (Set<OWLClass> s : owlClasses) { 604 for (OWLClass oc : s) { 605 if (oc.isOWLThing() || oc.isOWLNothing()) continue; 606 String conceptURI = oc.getURI().toASCIIString(); 607 String conceptName = conceptURI.substring(conceptURI.indexOf("#") + 1); 608 concepts.add((Concept) get(conceptName)); 609 } 610 } 611 } catch (OWLReasonerException ex) { 612 ex.printStackTrace(); 613 } 614 return concepts; 615 } 616 617 /** 618 * Returns all individuals that belong to the given concept. The reasoner is used for this. 619 * 620 * @param concept The concept. 621 * @return A list of all individuals of the concept. 622 * @see Concept#getIndividuals() 623 */ 624 public synchronized List<Individual> getIndividuals(Concept concept) { 625 OWLClass owlClass = new OWLClassImpl(new OWLDataFactoryImpl(), concept.getURI()); 626 ArrayList<Individual> individuals = new ArrayList<Individual>(); 627 try { 628 Set<OWLIndividual> owlIndividuals = reasoner.getIndividuals(owlClass, false); 629 for (OWLIndividual oi : owlIndividuals) { 630 String indURI = oi.getURI().toASCIIString(); 631 String indName = indURI.substring(indURI.indexOf("#") + 1); 632 if (!indName.matches("Ind[0-9]+")) { 633 individuals.add((Individual) get(indName)); 634 } 635 } 636 } catch (OWLReasonerException ex) { 637 ex.printStackTrace(); 638 } 639 return individuals; 640 } 641 642 /** 643 * Returns all super-concepts of the given concept. The reasoner is used for this. 644 * 645 * @param concept The concept for which all super-concepts should be returned. 646 * @return A list of all super-concepts. 647 * @see Concept#getSuperConcepts() 648 */ 649 public synchronized List<Concept> getSuperConcepts(Concept concept) { 650 OWLClass owlClass = new OWLClassImpl(new OWLDataFactoryImpl(), concept.getURI()); 651 ArrayList<Concept> concepts = new ArrayList<Concept>(); 652 try { 653 Set<Set<OWLClass>> owlClasses = reasoner.getAncestorClasses(owlClass); 654 for (Set<OWLClass> s : owlClasses) { 655 for (OWLClass oc : s) { 656 if (oc.isOWLThing() || oc.isOWLNothing()) continue; 657 String conceptURI = oc.getURI().toASCIIString(); 658 String conceptName = conceptURI.substring(conceptURI.indexOf("#") + 1); 659 concepts.add((Concept) get(conceptName)); 660 } 661 } 662 } catch (OWLReasonerException ex) { 663 ex.printStackTrace(); 664 } 665 return concepts; 666 } 667 668 /** 669 * Returns all the sub-concepts of the given concept. The reasoner is used for this. 670 * 671 * @param concept The concept for which all sub-concepts should be returned. 672 * @return A list of all sub-concepts. 673 * @see Concept#getSubConcepts() 674 */ 675 public synchronized List<Concept> getSubConcepts(Concept concept) { 676 OWLClass owlClass = new OWLClassImpl(new OWLDataFactoryImpl(), concept.getURI()); 677 ArrayList<Concept> concepts = new ArrayList<Concept>(); 678 try { 679 Set<Set<OWLClass>> owlClasses = reasoner.getDescendantClasses(owlClass); 680 for (Set<OWLClass> s : owlClasses) { 681 for (OWLClass oc : s) { 682 if (oc.isOWLThing() || oc.isOWLNothing()) continue; 683 String conceptURI = oc.getURI().toASCIIString(); 684 String conceptName = conceptURI.substring(conceptURI.indexOf("#") + 1); 685 concepts.add((Concept) get(conceptName)); 686 } 687 } 688 } catch (OWLReasonerException ex) { 689 ex.printStackTrace(); 690 } 691 return concepts; 692 } 693 694 /** 695 * Returns a list of ontology elements that answer the given question. The reasoner is used 696 * for this. In the case the sentence has the form "what is (Individual)?" then the answer 697 * contains all concepts the individual belongs to. Otherwise, the question is 698 * processed as a "DL Query" that describes a concept. In this case, the answer consists 699 * of all individuals that belong to the concept. The null value is returned if the 700 * sentence is not a question. 701 * 702 * @param questionSentence The question sentence that should be answered. 703 * @return A list of ontology elements that are the answer for the question. 704 * @see Sentence#getAnswer() 705 */ 706 public synchronized List<OntologyElement> getAnswer(Sentence questionSentence) { 707 if (!questionSentence.isQuestion()) return null; 708 709 OWLOntology o = questionSentence.getOWLOntology(); 710 if (o == null || o.isEmpty()) return null; 711 712 ArrayList<OntologyElement> answer = new ArrayList<OntologyElement>(); 713 714 try { 715 OWLSubClassAxiom answerOWLAxiom = (OWLSubClassAxiom) o.getAxioms().iterator().next(); 716 OWLDescription answerOWLClass = answerOWLAxiom.getSubClass(); 717 718 if (answerOWLClass instanceof OWLObjectOneOf && ((OWLObjectOneOf) answerOWLClass).getIndividuals().size() == 1) { 719 OWLIndividual oi = ((OWLObjectOneOf) answerOWLClass).getIndividuals().iterator().next(); 720 Set<Set<OWLClass>> owlClasses = reasoner.getTypes(oi, false); 721 for (Set<OWLClass> classSet : owlClasses) { 722 for (OWLClass owlClass : classSet) { 723 String classURI = owlClass.getURI().toASCIIString(); 724 String className = classURI.substring(classURI.indexOf("#") + 1); 725 if (!owlClass.isOWLThing() && !owlClass.isOWLNothing()) { 726 answer.add(get(className)); 727 } 728 } 729 } 730 } else { 731 Set<OWLIndividual> owlIndividuals = reasoner.getIndividuals(answerOWLClass, false); 732 for (OWLIndividual oi : owlIndividuals) { 733 String indURI = oi.getURI().toASCIIString(); 734 String indName = indURI.substring(indURI.indexOf("#") + 1); 735 if (!indName.matches("Ind[0-9]+")) { 736 answer.add(get(indName)); 737 } 738 } 739 } 740 } catch (Exception ex) { 741 ex.printStackTrace(); 742 } 743 return answer; 744 } 745 746 /** 747 * Returns true if the ontology is consistent. If nothing goes wrong, this should always return true. 748 * The reasoner is used for this. 749 * 750 * @return true if the ontology is consistent. 751 */ 752 public synchronized boolean isConsistent() { 753 boolean isConsistent = true; 754 try { 755 isConsistent = reasoner.isSatisfiable((new OWLDataFactoryImpl()).getOWLThing()); 756 } catch (OWLReasonerException ex) { 757 ex.printStackTrace(); 758 } 759 return isConsistent; 760 } 761 762 /** 763 * Checks if the given concept is satisfiable. The reasoner is used for this. 764 * 765 * @param concept The concept. 766 * @return true if the concept is satisfiable. 767 */ 768 public synchronized boolean isSatisfiable(Concept concept) { 769 OWLClass owlClass = new OWLClassImpl(new OWLDataFactoryImpl(), concept.getURI()); 770 boolean isSatisfiable = false; 771 try { 772 isSatisfiable = (!reasoner.isDefined(owlClass) || reasoner.isSatisfiable(owlClass)); 773 } catch (OWLReasonerException ex) { 774 ex.printStackTrace(); 775 } 776 return isSatisfiable; 777 } 778 779 private void loadOntology(OWLOntology ontology) { 780 if (ontology == null) return; 781 782 HashSet<OWLOntology> ontologies = new HashSet<OWLOntology>(); 783 ontologies.add(ontology); 784 try { 785 reasoner.loadOntologies(ontologies); 786 } catch (OWLReasonerException ex) { 787 ex.printStackTrace(); 788 } 789 } 790 791 private void unloadOntology(OWLOntology ontology) { 792 if (ontology == null) return; 793 794 HashSet<OWLOntology> ontologies = new HashSet<OWLOntology>(); 795 ontologies.add(ontology); 796 try { 797 reasoner.unloadOntologies(ontologies); 798 } catch (OWLReasonerException ex) { 799 ex.printStackTrace(); 800 } 801 } 802 803 private void clearOntologies() { 804 try { 805 reasoner.clearOntologies(); 806 } catch (OWLReasonerException ex) { 807 ex.printStackTrace(); 808 } 809 } 810 811 private long nextId() { 812 return ++idCount; 813 } 814 815 /** 816 * Returns the state id of the ontology. This id increases each time the ontology changes (more precisely: 817 * each time the part of the ontology that participates in reasoning changes). This id is used to find out 818 * whether cached information is still valid or has to be recalculated. 819 * 820 * @return The state id of the ontology. 821 */ 822 long getStateID() { 823 return stateID; 824 } 825 826 }