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