// This file is part of AceWiki. // Copyright 2008-2013, AceWiki developers. // // AceWiki is free software: you can redistribute it and/or modify it under the terms of the GNU // Lesser General Public License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // AceWiki is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without // even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License along with AceWiki. If // not, see http://www.gnu.org/licenses/. package ch.uzh.ifi.attempto.acewiki.core; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * This reasoner class wraps another reasoner and adds caching functionality. * * @author Tobias Kuhn */ public class CachingReasoner implements AceWikiReasoner { private AceWikiReasoner wrappedReasoner; private Ontology ontology; private Map<String, CachedAnswer> answerCache = new HashMap<String, CachedAnswer>(); private Map<Long, CachedConcepts> conCache = new HashMap<Long, CachedConcepts>(); private Map<Long, CachedIndividuals> indCache = new HashMap<Long, CachedIndividuals>(); private Map<Long, CachedConcepts> supConCache = new HashMap<Long, CachedConcepts>(); private Map<Long, CachedConcepts> subConCache = new HashMap<Long, CachedConcepts>(); /** * Creates a new caching reasoner for the given reasoner to be wrapped. * * @param wrappedReasoner The reasoner to be wrapped. */ CachingReasoner(AceWikiReasoner wrappedReasoner) { this.wrappedReasoner = wrappedReasoner; } public synchronized void init(Ontology ontology) { this.ontology = ontology; } /** * Returns the wrapped reasoner. * * @return The wrapped reasoner. */ public AceWikiReasoner getWrappedReasoner() { return wrappedReasoner; } private long getState() { return ontology.getStateID(); } /** * Returns whether the there is an up-to-date cached answer for the given question. * * @param question The question. * @return true if there is an up-to-date cached answer. */ public synchronized boolean isCachedAnswerUpToDate(Question question) { CachedAnswer a = answerCache.get(question.serialize()); if (a != null) { return a.state == getState(); } else { return false; } } /** * Returns the cached answer for the given question, or null if no cached answer exists. * The answer might not be up-to-date. * * @param question The question. * @return The cached answer. */ public synchronized List<AnswerElement> getCachedAnswer(Question question) { CachedAnswer a = answerCache.get(question.serialize()); if (a != null) { return new ArrayList<AnswerElement>(a.list); } else { return null; } } /** * Returns the answer for the given question. The cache is used if it is up-to-date. */ public synchronized List<AnswerElement> getAnswer(Question question) { CachedAnswer a = answerCache.get(question.serialize()); if (a != null && a.state == getState()) { return new ArrayList<AnswerElement>(a.list); } else { a = new CachedAnswer(); a.list = wrappedReasoner.getAnswer(question); a.state = getState(); answerCache.put(question.serialize(), a); return a.list; } } /** * Returns true if the concepts of the given individual are cached and up-to-date and thus * do not have to be recalculated. * * @param ind The individual. * @return true if the cached concepts are up-to-date. */ public synchronized boolean areCachedConceptsUpToDate(Individual ind) { CachedConcepts c = conCache.get(ind.getId()); if (c != null) { return c.state == getState(); } else { return false; } } /** * Returns the cached concepts for the given individual or null if there are no cached * concepts. The returned concepts might not be up-to-date. * * @param ind The individual. * @return A list of the cached concepts for the given individual. */ public synchronized List<Concept> getCachedConcepts(Individual ind) { CachedConcepts c = conCache.get(ind.getId()); if (c != null) { return new ArrayList<Concept>(c.list); } else { return null; } } public synchronized List<Concept> getConcepts(Individual ind) { CachedConcepts c = conCache.get(ind.getId()); if (c != null && c.state == getState()) { return new ArrayList<Concept>(c.list); } else { c = new CachedConcepts(); c.list = wrappedReasoner.getConcepts(ind); c.state = getState(); conCache.put(ind.getId(), c); return c.list; } } /** * Returns true if the individuals of the given concept are cached and up-to-date and thus * do not have to be recalculated. * * @param concept The concept. * @return true if the cached individuals are up-to-date. */ public synchronized boolean areCachedIndividualsUpToDate(Concept concept) { CachedIndividuals i = indCache.get(concept.getId()); if (i != null) { return i.state == getState(); } else { return false; } } /** * Returns the cached individuals for the given concept or null if there are no cached * individuals. The returned individuals might not be up-to-date. * * @param concept The concept. * @return A list of the cached individuals for the given concept. */ public synchronized List<Individual> getCachedIndividuals(Concept concept) { CachedIndividuals i = indCache.get(concept.getId()); if (i != null) { return new ArrayList<Individual>(i.list); } else { return null; } } public synchronized List<Individual> getIndividuals(Concept concept) { CachedIndividuals i = indCache.get(concept.getId()); if (i != null && i.state == getState()) { return new ArrayList<Individual>(i.list); } else { i = new CachedIndividuals(); i.list = wrappedReasoner.getIndividuals(concept); i.state = getState(); indCache.put(concept.getId(), i); return i.list; } } /** * Returns true if the suber-concepts of the given concept are cached and up-to-date and thus * do not have to be recalculated. * * @param concept The concept. * @return true if the cached super-concepts are up-to-date. */ public synchronized boolean areCachedSuperConceptsUpToDate(Concept concept) { CachedConcepts c = supConCache.get(concept.getId()); if (c != null) { return c.state == getState(); } else { return false; } } /** * Returns the cached super-concepts for the given concept or null if there are no cached * super-concepts. The returned super-concepts might not be up-to-date. * * @param concept The concept. * @return A list of the cached super-concepts for the given concept. */ public synchronized List<Concept> getCachedSuperConcepts(Concept concept) { CachedConcepts c = supConCache.get(concept.getId()); if (c != null) { return new ArrayList<Concept>(c.list); } else { return null; } } public synchronized List<Concept> getSuperConcepts(Concept concept) { CachedConcepts c = supConCache.get(concept.getId()); if (c != null && c.state == getState()) { return new ArrayList<Concept>(c.list); } else { c = new CachedConcepts(); c.list = wrappedReasoner.getSuperConcepts(concept); c.state = getState(); supConCache.put(concept.getId(), c); return c.list; } } /** * Returns true if the sub-concepts of the given concept are cached and up-to-date and thus * do not have to be recalculated. * * @param concept The concept. * @return true if the cached sub-concepts are up-to-date. */ public synchronized boolean areCachedSubConceptsUpToDate(Concept concept) { CachedConcepts c = subConCache.get(concept.getId()); if (c != null) { return c.state == getState(); } else { return false; } } /** * Returns the cached sub-concepts for the given concept or null if there are no cached * sub-concepts. The returned sub-concepts might not be up-to-date. * * @param concept The concept. * @return A list of the cached sub-concepts for the given concept. */ public synchronized List<Concept> getCachedSubConcepts(Concept concept) { CachedConcepts c = subConCache.get(concept.getId()); if (c != null) { return new ArrayList<Concept>(c.list); } else { return null; } } public synchronized List<Concept> getSubConcepts(Concept concept) { CachedConcepts c = subConCache.get(concept.getId()); if (c != null && c.state == getState()) { return new ArrayList<Concept>(c.list); } else { c = new CachedConcepts(); c.list = wrappedReasoner.getSubConcepts(concept); c.state = getState(); subConCache.put(concept.getId(), c); return c.list; } } /** * Returns the name of the reasoner. * * @return The name of the reasoner. */ public String getReasonerName() { return wrappedReasoner.getReasonerName(); } /** * Return the version of the reasoner. * * @return The version of the reasoner. */ public String getReasonerVersion() { return wrappedReasoner.getReasonerVersion(); } /** * Return the type of the reasoner. * * @return The reasoner type. */ public String getReasonerType() { return wrappedReasoner.getReasonerType(); } public Map<String, String> getInfo() { return wrappedReasoner.getInfo(); } /** * Loads the reasoner or reasoner interface. */ public synchronized void load() { wrappedReasoner.load(); } public synchronized void loadElement(OntologyElement element) { wrappedReasoner.loadElement(element); } public synchronized void unloadElement(OntologyElement element) { wrappedReasoner.unloadElement(element); } public synchronized boolean isConsistent() { return wrappedReasoner.isConsistent(); } public synchronized boolean isSatisfiable(Concept concept) { return wrappedReasoner.isSatisfiable(concept); } public synchronized void loadSentence(Sentence sentence) { wrappedReasoner.loadSentence(sentence); } public synchronized void unloadSentence(Sentence sentence) { wrappedReasoner.unloadSentence(sentence); } public synchronized void flushElements() { wrappedReasoner.flushElements(); } // Small internal classes for cached objects: private static class CachedAnswer { long state = -1; List<AnswerElement> list; } private static class CachedIndividuals { long state = -1; List<Individual> list; } private static class CachedConcepts { long state = -1; List<Concept> list; } }