// 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.owl; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; import org.semanticweb.HermiT.Reasoner; import org.semanticweb.owlapi.apibinding.OWLManager; import org.semanticweb.owlapi.model.IRI; import org.semanticweb.owlapi.model.OWLAxiom; import org.semanticweb.owlapi.model.OWLClass; import org.semanticweb.owlapi.model.OWLClassExpression; import org.semanticweb.owlapi.model.OWLDataFactory; import org.semanticweb.owlapi.model.OWLDeclarationAxiom; import org.semanticweb.owlapi.model.OWLDifferentIndividualsAxiom; import org.semanticweb.owlapi.model.OWLLogicalEntity; import org.semanticweb.owlapi.model.OWLNamedIndividual; import org.semanticweb.owlapi.model.OWLOntology; import org.semanticweb.owlapi.model.OWLOntologyCreationException; import org.semanticweb.owlapi.model.OWLOntologyManager; import org.semanticweb.owlapi.owllink.OWLlinkHTTPXMLReasonerFactory; import org.semanticweb.owlapi.owllink.OWLlinkReasonerConfiguration; import org.semanticweb.owlapi.owllink.builtin.response.OWLlinkErrorResponseException; import org.semanticweb.owlapi.profiles.OWL2ELProfile; import org.semanticweb.owlapi.profiles.OWL2QLProfile; import org.semanticweb.owlapi.profiles.OWL2RLProfile; import org.semanticweb.owlapi.profiles.OWLProfile; import org.semanticweb.owlapi.reasoner.OWLReasoner; import org.semanticweb.owlapi.reasoner.OWLReasonerFactory; import org.semanticweb.owlapi.util.Version; import uk.ac.manchester.cs.owl.owlapi.OWLDataFactoryImpl; import ch.uzh.ifi.attempto.acewiki.core.AnswerElement; import ch.uzh.ifi.attempto.acewiki.core.Concept; import ch.uzh.ifi.attempto.acewiki.core.InconsistencyException; import ch.uzh.ifi.attempto.acewiki.core.Individual; import ch.uzh.ifi.attempto.acewiki.core.LanguageUtils; import ch.uzh.ifi.attempto.acewiki.core.Ontology; import ch.uzh.ifi.attempto.acewiki.core.OntologyElement; import ch.uzh.ifi.attempto.acewiki.core.Question; import ch.uzh.ifi.attempto.acewiki.core.Sentence; /** * This is a reasoner implementation that connects to an OWL reasoner. At the moment, it can * directly connect to HermiT and Pellet. Additionally, reasoners like FaCT++ can be accessed via * the OWLlink interface. * * @author Tobias Kuhn */ public class AceWikiOWLReasoner extends AbstractAceWikiOWLReasoner { private static OWLDataFactory dataFactory = new OWLDataFactoryImpl(); private static OWLlinkHTTPXMLReasonerFactory owllinkReasonerFactory; private static Object owllinkReasonerSyncToken = new Object(); private Ontology ontology; private OWLOntologyManager manager; private OWLOntology owlOntology; private Map<OWLAxiom, Integer> axiomsMap = new HashMap<OWLAxiom, Integer>(); private OWLReasoner owlReasoner; private String reasonerType = "none"; private Object reasonerSyncToken = new Object(); private OWLDifferentIndividualsAxiom diffIndsAxiom; private boolean diffIndsAxiomOutdated = true; private OWLProfile owlProfile; private String globalRestrPolicy; private Map<String, String> infoMap = new LinkedHashMap<String, String>(); /** * Creates a new reasoner object. */ public AceWikiOWLReasoner() { manager = OWLManager.createOWLOntologyManager(); try { owlOntology = manager.createOntology(); } catch (OWLOntologyCreationException ex) { ex.printStackTrace(); } } public void init(Ontology ontology) { this.ontology = ontology; String p = (getParameter("owl_profile") + "").toLowerCase(); if (p.equals("owl2el")) { owlProfile = new OWL2ELProfile(); } else if (p.equals("owl2ql")) { owlProfile = new OWL2QLProfile(); } else if (p.equals("owl2rl")) { owlProfile = new OWL2RLProfile(); } else { owlProfile = null; } String grp = (getParameter("global_restrictions_policy") + "").toLowerCase(); if (grp.equals("unchecked")) { globalRestrPolicy = "unchecked"; } else { globalRestrPolicy = "no_chains"; } infoMap.put("global restrictions policy", globalRestrPolicy); infoMap.put("OWL profile", getOWLProfileName()); } private List<OntologyElement> getOntologyElements() { return ontology.getOntologyElements(); } private OntologyElement getOntologyElement(String name) { return ontology.getElement(name); } private String getParameter(String name) { return ontology.getParameter(name); } public Map<String, String> getInfo() { return infoMap; } /** * Returns a string representing the policy how to enforce the global restrictions on the * axioms in OWL 2. * * @return The global restrictions policy. */ public String getGlobalRestrictionsPolicy() { return globalRestrPolicy; } private IRI getIRI() { return IRI.create(ontology.getURI()); } /** * Returns the OWL profile that defines which statements are used for reasoning, or null if the * full language of OWL is used. * * @return The used OWL profile. */ public OWLProfile getOWLProfile() { return owlProfile; } /** * Returns the name of the current OWL profile. * * @return The OWL profile name. */ public String getOWLProfileName() { if (owlProfile == null) return "OWL 2 Full"; return owlProfile.getName(); } /** * Returns a new OWL ontology object representing the full ontology or the consistent part of * it. * * @param consistent true if only the consistent part should be exported. * @return An OWL ontology object of the full ontology. */ public OWLOntology exportOWLOntology(boolean consistent) { Set<OWLAxiom> axioms = new HashSet<OWLAxiom>(); for (OntologyElement el : getOntologyElements()) { OWLDeclarationAxiom owlDecl = null; if (el instanceof OWLOntoElement) { owlDecl = ((OWLOntoElement) el).getOWLDeclaration(); } if (owlDecl != null) { axioms.add(owlDecl); } for (Sentence s : el.getArticle().getSentences()) { OWLSentence os = (OWLSentence) s; if (os instanceof Question || !os.isOWL()) continue; if (consistent && (!os.isReasonable() || !os.isIntegrated())) continue; axioms.addAll(os.getOWLAxioms()); } } axioms.add(diffIndsAxiom); OWLOntology o = null; try { o = manager.createOntology(axioms, getIRI()); manager.removeOntology(o); } catch (Exception ex) { ex.printStackTrace(); } return o; } private OntologyElement get(OWLLogicalEntity owlEntity) { if (owlEntity == null) return null; if (owlEntity.isTopEntity() || owlEntity.isBottomEntity()) return null; String iri = owlEntity.getIRI().toString(); if (!iri.startsWith(ontology.getURI())) return null; String name = iri.substring(iri.indexOf("#") + 1); return getOntologyElement(name); } /** * Returns the OWL ontology manager. * * @return The OWL ontology manager. */ public OWLOntologyManager getOWLOntologyManager() { return manager; } public String getReasonerName() { if (owlReasoner == null) return null; return owlReasoner.getReasonerName(); } public String getReasonerVersion() { if (owlReasoner == null) return null; Version v = owlReasoner.getReasonerVersion(); if (v == null) return null; return v.getMajor() + "." + v.getMinor() + "." + v.getPatch() + "." + v.getBuild(); } public String getReasonerType() { return reasonerType; } public void load() { log("loading reasoner"); String type = getParameter("reasoner"); if (type == null) type = ""; type = type.toLowerCase(); reasonerType = type; String s = getParameter("reasoner_url"); if (s == null || s.length() == 0) s = "http://localhost:8080"; URL url = null; try { url = new URL(s); } catch (MalformedURLException ex) { ex.printStackTrace(); } if (owlReasoner != null) owlReasoner.dispose(); if (type.equals("none")) { log("no reasoner"); reasonerType = "none"; owlReasoner = null; } else if (type.equals("hermit")) { log("loading HermiT"); reasonerType = "HermiT"; owlReasoner = new Reasoner(owlOntology); } else if (type.equals("pellet")) { log("loading Pellet"); reasonerType = "Pellet"; // The Pellet libraries are not part of the AceWiki package (because of license // reasons). For that reason, the pellet reasoner has to be loaded dynamically. OWLReasonerFactory reasonerFactory = null; try { ClassLoader classLoader = Ontology.class.getClassLoader(); String className = "com.clarkparsia.pellet.owlapiv3.PelletReasonerFactory"; reasonerFactory = (OWLReasonerFactory) classLoader.loadClass(className).newInstance(); } catch (Exception ex) { ex.printStackTrace(); } owlReasoner = reasonerFactory.createNonBufferingReasoner(owlOntology); } else if (type.equals("owllink")) { log("loading OWLlink"); reasonerType = "OWLlink"; if (owllinkReasonerFactory == null) { owllinkReasonerFactory = new OWLlinkHTTPXMLReasonerFactory(); } OWLlinkReasonerConfiguration config = new OWLlinkReasonerConfiguration(url); owlReasoner = owllinkReasonerFactory.createReasoner(owlOntology, config); // reasoner calls over OWLlink have to be synchronized: reasonerSyncToken = owllinkReasonerSyncToken; //} else if (type.equals("dig")) { //try { // reasoner = new DIGReasoner(OWLManager.createOWLOntologyManager()); // ((DIGReasoner) reasoner).getReasoner().setReasonerURL(url); //} catch (Exception ex) { ex.printStackTrace(); } } else if (type.equals("")) { log("no reasoner type specified: loading HermiT as default"); reasonerType = "HermiT"; owlReasoner = new Reasoner(owlOntology); } else { log("ERROR: Unknown reasoner type: " + type); reasonerType = "none"; owlReasoner = null; } updateDifferentIndividualsAxiom(); flush(); log("reasoner loaded"); } /** * Updates the axiom that states that all named individuals are different. Thus, unique * name assumption is applied. */ private synchronized void updateDifferentIndividualsAxiom() { if (!diffIndsAxiomOutdated) return; if (diffIndsAxiom != null) { unloadAxiom(diffIndsAxiom); } Set<OWLNamedIndividual> inds = new HashSet<OWLNamedIndividual>(); for (OntologyElement oe : getOntologyElements()) { if (oe instanceof OWLIndividual) { inds.add(((OWLIndividual) oe).getOWLRepresentation()); } } diffIndsAxiom = dataFactory.getOWLDifferentIndividualsAxiom(inds); loadAxiom(diffIndsAxiom); diffIndsAxiomOutdated = false; } public void flushElements() { flush(); } private void flush() { if (owlReasoner != null) { synchronized (reasonerSyncToken) { owlReasoner.flush(); } } } public void loadElement(OntologyElement element) { OWLDeclarationAxiom owlDecl = null; if (element instanceof OWLOntoElement) { owlDecl = ((OWLOntoElement) element).getOWLDeclaration(); } if (owlDecl != null) { manager.addAxiom(owlOntology, owlDecl); flush(); } if (element instanceof OWLIndividual) { diffIndsAxiomOutdated = true; } } public void unloadElement(OntologyElement element) { OWLDeclarationAxiom owlDecl = null; if (element instanceof OWLOntoElement) { owlDecl = ((OWLOntoElement) element).getOWLDeclaration(); } if (owlDecl != null) { manager.removeAxiom(owlOntology, owlDecl); flush(); } if (element instanceof OWLIndividual) { diffIndsAxiomOutdated = true; } } public synchronized List<Concept> getConcepts(Individual ind) { List<Concept> concepts = new ArrayList<Concept>(); OWLIndividual owlInd = (OWLIndividual) ind; for (OWLClass oc : getConcepts(owlInd.getOWLRepresentation())) { if (oc.isOWLThing() || oc.isOWLNothing()) continue; String conceptURI = oc.getIRI().toString(); if (conceptURI.startsWith("http://attempto.ifi.uzh.ch/ace#")) continue; String conceptName = conceptURI.substring(conceptURI.indexOf("#") + 1); concepts.add((Concept) getOntologyElement(conceptName)); } return concepts; } private synchronized Set<OWLClass> getConcepts(OWLNamedIndividual owlInd) { if (owlReasoner == null) { return Collections.emptySet(); } else { synchronized (reasonerSyncToken) { return owlReasoner.getTypes(owlInd, false).getFlattened(); } } } public synchronized List<Individual> getIndividuals(Concept concept) { OWLConcept ac = (OWLConcept) concept; List<Individual> inds = new ArrayList<Individual>(); for (OWLNamedIndividual oi : getIndividuals(ac.getOWLRepresentation())) { OntologyElement oe = get(oi); if (oe instanceof Individual) { inds.add((Individual) oe); } } return inds; } private synchronized Set<OWLNamedIndividual> getIndividuals(OWLClassExpression owlClass) { if (owlReasoner == null) { return Collections.emptySet(); } else { synchronized (reasonerSyncToken) { return owlReasoner.getInstances(owlClass, false).getFlattened(); } } } public synchronized List<Concept> getSuperConcepts(Concept concept) { OWLConcept ac = (OWLConcept) concept; List<Concept> concepts = new ArrayList<Concept>(); for (OWLClass oc : getSuperConcepts(ac.getOWLRepresentation())) { OntologyElement oe = get(oc); if (oe instanceof Concept) { concepts.add((Concept) oe); } } return concepts; } private synchronized Set<OWLClass> getSuperConcepts(OWLClass owlClass) { if (owlReasoner == null) { return Collections.emptySet(); } else { synchronized (reasonerSyncToken) { return owlReasoner.getSuperClasses(owlClass, false).getFlattened(); } } } public synchronized List<Concept> getSubConcepts(Concept concept) { OWLConcept ac = (OWLConcept) concept; List<Concept> concepts = new ArrayList<Concept>(); for (OWLClass oc : getSubConcepts(ac.getOWLRepresentation())) { OntologyElement oe = get(oc); if (oe instanceof Concept) { concepts.add((Concept) oe); } } return concepts; } private synchronized Set<OWLClass> getSubConcepts(OWLClass owlClass) { if (owlReasoner == null) { return Collections.emptySet(); } else { synchronized (reasonerSyncToken) { return owlReasoner.getSubClasses(owlClass, false).getFlattened(); } } } public synchronized List<AnswerElement> getAnswer(Question q) { if (owlReasoner == null) return null; OWLQuestion question = (OWLQuestion) q; OWLNamedIndividual quInd = question.getQuestionOWLIndividual(); OWLClassExpression quClass = question.getQuestionOWLClass(); List<AnswerElement> list = new ArrayList<AnswerElement>(); if (quInd != null) { for (OWLClass oc : getConcepts(quInd)) { OntologyElement oe = get(oc); if (oe instanceof OWLConcept) { list.add((OWLConcept) oe); } } } else if (quClass != null) { Set<OWLNamedIndividual> owlInds = getIndividuals(quClass); for (OWLNamedIndividual oi : owlInds) { OntologyElement oe = get(oi); if (oe instanceof OWLIndividual) { list.add((OWLIndividual) oe); } } } LanguageUtils.sortOntologyElements(list); return list; } public synchronized boolean isConsistent() { if (owlReasoner == null) return true; boolean c = true; try { synchronized (reasonerSyncToken) { // The method isConsistent is poorly supported by the implementations. //c = reasoner.isConsistent(); c = owlReasoner.isSatisfiable(dataFactory.getOWLThing()); } } catch (Exception ex) { c = false; } return c; } public synchronized boolean isSatisfiable(Concept concept) { if (owlReasoner == null) return true; if (!(concept instanceof OWLConcept)) return false; if (owlOntology.containsClassInSignature(((OWLConcept) concept).getIRI())) { synchronized (reasonerSyncToken) { OWLConcept ac = (OWLConcept) concept; return owlReasoner.isSatisfiable(ac.getOWLRepresentation()); } } else { return true; } } public void loadSentence(Sentence s) { OWLSentence sentence = (OWLSentence) s; try { for (OWLAxiom ax : sentence.getOWLAxioms()) { loadAxiom(ax); } flush(); } catch (OWLlinkErrorResponseException ex) { // FaCT++ throws an exception here when inconsistency is encountered // TODO Is this always the case? if ("FaCT++.Kernel: inconsistent ontology".equals(ex.getMessage())) { throw new InconsistencyException(); } else { // We get here when the global restrictions are violated with FaCT++ and OWLlink throw ex; } } catch (IllegalArgumentException ex) { // We get here when the global restrictions are violated with HermiT throw ex; } } public void unloadSentence(Sentence s) { OWLSentence sentence = (OWLSentence) s; for (OWLAxiom ax : sentence.getOWLAxioms()) { unloadAxiom(ax); } flush(); } private void loadAxiom(OWLAxiom ax) { Integer count = axiomsMap.get(ax); if (count == null) count = 0; if (count == 0) { manager.addAxiom(owlOntology, ax); } axiomsMap.put(ax, count+1); } private void unloadAxiom(OWLAxiom ax) { Integer count = axiomsMap.get(ax); if (count == 1) { manager.removeAxiom(owlOntology, ax); } axiomsMap.put(ax, count-1); } private void log(String text) { ontology.log(text); } }