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