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 }