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 }