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