001    // This file is part of AceWiki.
002    // Copyright 2008-2012, AceWiki developers.
003    // 
004    // AceWiki is free software: you can redistribute it and/or modify it under the terms of the GNU
005    // Lesser General Public License as published by the Free Software Foundation, either version 3 of
006    // the License, or (at your option) any later version.
007    // 
008    // AceWiki is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
009    // even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
010    // Lesser General Public License for more details.
011    // 
012    // You should have received a copy of the GNU Lesser General Public License along with AceWiki. If
013    // not, see http://www.gnu.org/licenses/.
014    
015    package ch.uzh.ifi.attempto.acewiki.core;
016    
017    import java.util.ArrayList;
018    import java.util.HashMap;
019    import java.util.List;
020    import java.util.Map;
021    
022    /**
023     * This reasoner class wraps another reasoner and adds caching functionality.
024     * 
025     * @author Tobias Kuhn
026     */
027    public class CachingReasoner implements AceWikiReasoner {
028            
029            private AceWikiReasoner wrappedReasoner;
030            private Ontology ontology;
031            
032            private Map<String, CachedAnswer> answerCache = new HashMap<String, CachedAnswer>();
033            private Map<Long, CachedConcepts> conCache = new HashMap<Long, CachedConcepts>();
034            private Map<Long, CachedIndividuals> indCache = new HashMap<Long, CachedIndividuals>();
035            private Map<Long, CachedConcepts> supConCache = new HashMap<Long, CachedConcepts>();
036            private Map<Long, CachedConcepts> subConCache = new HashMap<Long, CachedConcepts>();
037            
038            /**
039             * Creates a new caching reasoner for the given reasoner to be wrapped.
040             * 
041             * @param wrappedReasoner The reasoner to be wrapped.
042             */
043            CachingReasoner(AceWikiReasoner wrappedReasoner) {
044                    this.wrappedReasoner = wrappedReasoner;
045            }
046    
047            public synchronized void init(Ontology ontology) {
048                    this.ontology = ontology;
049            }
050            
051            /**
052             * Returns the wrapped reasoner.
053             * 
054             * @return The wrapped reasoner.
055             */
056            public AceWikiReasoner getWrappedReasoner() {
057                    return wrappedReasoner;
058            }
059            
060            private long getState() {
061                    return ontology.getStateID();
062            }
063            
064            /**
065             * Returns whether the there is an up-to-date cached answer for the given question.
066             * 
067             * @param question The question.
068             * @return true if there is an up-to-date cached answer.
069             */
070            public synchronized boolean isCachedAnswerUpToDate(Question question) {
071                    CachedAnswer a = answerCache.get(question.serialize());
072                    if (a != null) {
073                            return a.state == getState();
074                    } else {
075                            return false;
076                    }
077            }
078            
079            /**
080             * Returns the cached answer for the given question, or null if no cached answer exists.
081             * The answer might not be up-to-date.
082             * 
083             * @param question The question.
084             * @return The cached answer.
085             */
086            public synchronized List<AnswerElement> getCachedAnswer(Question question) {
087                    CachedAnswer a = answerCache.get(question.serialize());
088                    if (a != null) {
089                            return new ArrayList<AnswerElement>(a.list);
090                    } else {
091                            return null;
092                    }
093            }
094            
095            /**
096             * Returns the answer for the given question. The cache is used if it is up-to-date.
097             */
098            public synchronized List<AnswerElement> getAnswer(Question question) {
099                    CachedAnswer a = answerCache.get(question.serialize());
100                    if (a != null && a.state == getState()) {
101                            return new ArrayList<AnswerElement>(a.list);
102                    } else {
103                            a = new CachedAnswer();
104                            a.list = wrappedReasoner.getAnswer(question);
105                            a.state = getState();
106                            answerCache.put(question.serialize(), a);
107                            return a.list;
108                    }
109            }
110    
111            /**
112             * Returns true if the concepts of the given individual are cached and up-to-date and thus
113             * do not have to be recalculated.
114             * 
115             * @param ind The individual.
116             * @return true if the cached concepts are up-to-date.
117             */
118            public synchronized boolean areCachedConceptsUpToDate(Individual ind) {
119                    CachedConcepts c = conCache.get(ind.getId());
120                    if (c != null) {
121                            return c.state == getState();
122                    } else {
123                            return false;
124                    }
125            }
126    
127            /**
128             * Returns the cached concepts for the given individual or null if there are no cached
129             * concepts. The returned concepts might not be up-to-date.
130             * 
131             * @param ind The individual.
132             * @return A list of the cached concepts for the given individual.
133             */
134            public synchronized List<Concept> getCachedConcepts(Individual ind) {
135                    CachedConcepts c = conCache.get(ind.getId());
136                    if (c != null) {
137                            return new ArrayList<Concept>(c.list);
138                    } else {
139                            return null;
140                    }
141            }
142            
143            public synchronized List<Concept> getConcepts(Individual ind) {
144                    CachedConcepts c = conCache.get(ind.getId());
145                    if (c != null && c.state == getState()) {
146                            return new ArrayList<Concept>(c.list);
147                    } else {
148                            c = new CachedConcepts();
149                            c.list = wrappedReasoner.getConcepts(ind);
150                            c.state = getState();
151                            conCache.put(ind.getId(), c);
152                            return c.list;
153                    }
154            }
155    
156            /**
157             * Returns true if the individuals of the given concept are cached and up-to-date and thus
158             * do not have to be recalculated.
159             * 
160             * @param concept The concept.
161             * @return true if the cached individuals are up-to-date.
162             */
163            public synchronized boolean areCachedIndividualsUpToDate(Concept concept) {
164                    CachedIndividuals i = indCache.get(concept.getId());
165                    if (i != null) {
166                            return i.state == getState();
167                    } else {
168                            return false;
169                    }
170            }
171    
172            /**
173             * Returns the cached individuals for the given concept or null if there are no cached
174             * individuals. The returned individuals might not be up-to-date.
175             * 
176             * @param concept The concept.
177             * @return A list of the cached individuals for the given concept.
178             */
179            public synchronized List<Individual> getCachedIndividuals(Concept concept) {
180                    CachedIndividuals i = indCache.get(concept.getId());
181                    if (i != null) {
182                            return new ArrayList<Individual>(i.list);
183                    } else {
184                            return null;
185                    }
186            }
187            
188            public synchronized List<Individual> getIndividuals(Concept concept) {
189                    CachedIndividuals i = indCache.get(concept.getId());
190                    if (i != null && i.state == getState()) {
191                            return new ArrayList<Individual>(i.list);
192                    } else {
193                            i = new CachedIndividuals();
194                            i.list = wrappedReasoner.getIndividuals(concept);
195                            i.state = getState();
196                            indCache.put(concept.getId(), i);
197                            return i.list;
198                    }
199            }
200    
201            /**
202             * Returns true if the suber-concepts of the given concept are cached and up-to-date and thus
203             * do not have to be recalculated.
204             * 
205             * @param concept The concept.
206             * @return true if the cached super-concepts are up-to-date.
207             */
208            public synchronized boolean areCachedSuperConceptsUpToDate(Concept concept) {
209                    CachedConcepts c = supConCache.get(concept.getId());
210                    if (c != null) {
211                            return c.state == getState();
212                    } else {
213                            return false;
214                    }
215            }
216    
217            /**
218             * Returns the cached super-concepts for the given concept or null if there are no cached
219             * super-concepts. The returned super-concepts might not be up-to-date.
220             * 
221             * @param concept The concept.
222             * @return A list of the cached super-concepts for the given concept.
223             */
224            public synchronized List<Concept> getCachedSuperConcepts(Concept concept) {
225                    CachedConcepts c = supConCache.get(concept.getId());
226                    if (c != null) {
227                            return new ArrayList<Concept>(c.list);
228                    } else {
229                            return null;
230                    }
231            }
232            
233            public synchronized List<Concept> getSuperConcepts(Concept concept) {
234                    CachedConcepts c = supConCache.get(concept.getId());
235                    if (c != null && c.state == getState()) {
236                            return new ArrayList<Concept>(c.list);
237                    } else {
238                            c = new CachedConcepts();
239                            c.list = wrappedReasoner.getSuperConcepts(concept);
240                            c.state = getState();
241                            supConCache.put(concept.getId(), c);
242                            return c.list;
243                    }
244            }
245    
246            /**
247             * Returns true if the sub-concepts of the given concept are cached and up-to-date and thus
248             * do not have to be recalculated.
249             * 
250             * @param concept The concept.
251             * @return true if the cached sub-concepts are up-to-date.
252             */
253            public synchronized boolean areCachedSubConceptsUpToDate(Concept concept) {
254                    CachedConcepts c = subConCache.get(concept.getId());
255                    if (c != null) {
256                            return c.state == getState();
257                    } else {
258                            return false;
259                    }
260            }
261    
262            /**
263             * Returns the cached sub-concepts for the given concept or null if there are no cached
264             * sub-concepts. The returned sub-concepts might not be up-to-date.
265             * 
266             * @param concept The concept.
267             * @return A list of the cached sub-concepts for the given concept.
268             */
269            public synchronized List<Concept> getCachedSubConcepts(Concept concept) {
270                    CachedConcepts c = subConCache.get(concept.getId());
271                    if (c != null) {
272                            return new ArrayList<Concept>(c.list);
273                    } else {
274                            return null;
275                    }
276            }
277            
278            public synchronized List<Concept> getSubConcepts(Concept concept) {
279                    CachedConcepts c = subConCache.get(concept.getId());
280                    if (c != null && c.state == getState()) {
281                            return new ArrayList<Concept>(c.list);
282                    } else {
283                            c = new CachedConcepts();
284                            c.list = wrappedReasoner.getSubConcepts(concept);
285                            c.state = getState();
286                            subConCache.put(concept.getId(), c);
287                            return c.list;
288                    }
289            }
290    
291            /**
292             * Returns the name of the reasoner.
293             * 
294             * @return The name of the reasoner.
295             */
296            public String getReasonerName() {
297                    return wrappedReasoner.getReasonerName();
298            }
299            
300            /**
301             * Return the version of the reasoner.
302             * 
303             * @return The version of the reasoner.
304             */
305            public String getReasonerVersion() {
306                    return wrappedReasoner.getReasonerVersion();
307            }
308    
309            /**
310             * Return the type of the reasoner.
311             * 
312             * @return The reasoner type.
313             */
314            public String getReasonerType() {
315                    return wrappedReasoner.getReasonerType();
316            }
317            
318            public Map<String, String> getInfo() {
319                    return wrappedReasoner.getInfo();
320            }
321            
322            /**
323             * Loads the reasoner or reasoner interface.
324             */
325            public synchronized void load() {
326                    wrappedReasoner.load();
327            }
328            
329            public synchronized void loadElement(OntologyElement element) {
330                    wrappedReasoner.loadElement(element);
331            }
332            
333            public synchronized void unloadElement(OntologyElement element) {
334                    wrappedReasoner.unloadElement(element);
335            }
336            
337            public synchronized boolean isConsistent() {
338                    return wrappedReasoner.isConsistent();
339            }
340            
341            public synchronized boolean isSatisfiable(Concept concept) {
342                    return wrappedReasoner.isSatisfiable(concept);
343            }
344            
345            public synchronized void loadSentence(Sentence sentence) {
346                    wrappedReasoner.loadSentence(sentence);
347            }
348            
349            public synchronized void unloadSentence(Sentence sentence) {
350                    wrappedReasoner.unloadSentence(sentence);
351            }
352            
353            public synchronized void flushElements() {
354                    wrappedReasoner.flushElements();
355            }
356            
357            
358            // Small internal classes for cached objects:
359            
360            private static class CachedAnswer {
361                    long state = -1;
362                    List<AnswerElement> list;
363            }
364            
365            private static class CachedIndividuals {
366                    long state = -1;
367                    List<Individual> list;
368            }
369            
370            private static class CachedConcepts {
371                    long state = -1;
372                    List<Concept> list;
373            }
374    
375    }