/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.stanbol.reasoners.owlapi; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.stanbol.reasoners.servicesapi.InconsistentInputException; import org.apache.stanbol.reasoners.servicesapi.ReasoningService; import org.apache.stanbol.reasoners.servicesapi.ReasoningServiceException; import org.apache.stanbol.reasoners.servicesapi.UnsupportedTaskException; import org.semanticweb.owlapi.apibinding.OWLManager; import org.semanticweb.owlapi.model.MissingImportEvent; import org.semanticweb.owlapi.model.MissingImportListener; import org.semanticweb.owlapi.model.OWLAxiom; import org.semanticweb.owlapi.model.OWLOntology; import org.semanticweb.owlapi.model.OWLOntologyCreationException; import org.semanticweb.owlapi.model.OWLOntologyLoaderListener; import org.semanticweb.owlapi.model.OWLOntologyManager; import org.semanticweb.owlapi.model.SWRLRule; import org.semanticweb.owlapi.model.OWLOntologyLoaderListener.LoadingFinishedEvent; import org.semanticweb.owlapi.model.OWLOntologyLoaderListener.LoadingStartedEvent; import org.semanticweb.owlapi.reasoner.InconsistentOntologyException; import org.semanticweb.owlapi.reasoner.OWLReasoner; import org.semanticweb.owlapi.util.InferredAxiomGenerator; import org.semanticweb.owlapi.util.InferredClassAssertionAxiomGenerator; import org.semanticweb.owlapi.util.InferredDataPropertyCharacteristicAxiomGenerator; import org.semanticweb.owlapi.util.InferredDisjointClassesAxiomGenerator; import org.semanticweb.owlapi.util.InferredEquivalentClassAxiomGenerator; import org.semanticweb.owlapi.util.InferredEquivalentDataPropertiesAxiomGenerator; import org.semanticweb.owlapi.util.InferredEquivalentObjectPropertyAxiomGenerator; import org.semanticweb.owlapi.util.InferredInverseObjectPropertiesAxiomGenerator; import org.semanticweb.owlapi.util.InferredObjectPropertyCharacteristicAxiomGenerator; import org.semanticweb.owlapi.util.InferredOntologyGenerator; import org.semanticweb.owlapi.util.InferredPropertyAssertionGenerator; import org.semanticweb.owlapi.util.InferredSubClassAxiomGenerator; import org.semanticweb.owlapi.util.InferredSubDataPropertyAxiomGenerator; import org.semanticweb.owlapi.util.InferredSubObjectPropertyAxiomGenerator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This abstract class implements basic methods for a reasoning service based on OWLApi */ public abstract class AbstractOWLApiReasoningService implements OWLApiReasoningService { private final Logger log = LoggerFactory.getLogger(getClass()); /** * An OWLOntologyManager to be used to place a target ontology. We don't want to use the manager bound to * the input ontologies for that, we don't want to interfere with it. * * Other {@see OWLApiReasoningService}s may want to change this. * * @return */ protected OWLOntologyManager createOWLOntologyManager() { log.info("createOWLOntologyManager()"); // We want a single instance here OWLOntologyManager manager = OWLManager.createOWLOntologyManager(); log.info("manager: {}", manager); // FIXME Which is the other way of doing this? // Maybe -> OWLOntologyManagerProperties(); manager.setSilentMissingImportsHandling(true); // Listening for missing imports manager.addMissingImportListener(new MissingImportListener() { @Override public void importMissing(MissingImportEvent arg0) { log.warn("Missing import {} ", arg0.getImportedOntologyURI()); } }); manager.addOntologyLoaderListener(new OWLOntologyLoaderListener() { @Override public void finishedLoadingOntology(LoadingFinishedEvent arg0) { log.info("Finished loading {} (imported: {})", arg0.getOntologyID(), arg0.isImported()); } @Override public void startedLoadingOntology(LoadingStartedEvent arg0) { log.info("Started loading {} (imported: {}) ...", arg0.getOntologyID(), arg0.isImported()); log.info(" ... from {}", arg0.getDocumentIRI().toString()); } }); return manager; } /** * Method to be implemented by subclasses. * * @param ontology * @return */ protected abstract OWLReasoner getReasoner(OWLOntology ontology); /** * Generic method for running the reasoner * * @param input * @param generators * @return */ @Override public Set<OWLAxiom> run(OWLOntology input, List<InferredAxiomGenerator<? extends OWLAxiom>> generators) throws ReasoningServiceException, InconsistentInputException { log.debug("run(OWLOntology input, List<InferredAxiomGenerator<? extends OWLAxiom>> generators)"); try { // Get the manager OWLOntologyManager manager = createOWLOntologyManager(); // Get the reasoner OWLReasoner reasoner = getReasoner(input); log.info("Running {} reasoner on {} ", reasoner.getClass(), input.getOntologyID()); // To generate inferred axioms InferredOntologyGenerator inferred = new InferredOntologyGenerator(reasoner, generators); // We fill an anonymous ontology with the result, the return the // axiom set Set<OWLAxiom> axioms = new HashSet<OWLAxiom>(); try { OWLOntology output = manager.createOntology(); log.debug("Created output ontology: {}", output); try { inferred.fillOntology(manager, output); } catch (InconsistentOntologyException i) { throw i; } catch (Throwable t) { log.error("Some problem occurred:\n {}", t.getStackTrace()); throw new ReasoningServiceException(); } log.debug("Filled ontology: {}", output); log.debug("Temporary ID is {}", output.getOntologyID()); axioms = manager.getOntology(output.getOntologyID()).getAxioms(); // IMPORTANT We remove the ontology from the manager manager.removeOntology(output); } catch (OWLOntologyCreationException e) { log.error("An exception have been thrown when instantiating the ontology"); throw new ReasoningServiceException(); } return axioms; } catch (InconsistentOntologyException inconsistent) { /** * TODO Add report. Why it is inconsistent? */ throw new InconsistentInputException(); } catch (Exception exception) { log.error("An exception have been thrown while executing method run()", exception); throw new ReasoningServiceException(); } } /** * Merges the SWRL rules in the input ontology, then calls run(OWLOntology,List<InferredAxiomGenerator<? * extends OWLAxiom>>) * * @param ontology * @param rules * @param generators * @return */ @Override public Set<OWLAxiom> run(OWLOntology ontology, List<SWRLRule> rules, List<InferredAxiomGenerator<? extends OWLAxiom>> generators) throws ReasoningServiceException, InconsistentInputException { log.debug("Called method run(OWLOntology,List<SWRLRule>,List)"); OWLOntologyManager manager = ontology.getOWLOntologyManager(); log.debug("Adding SWRL rules to the input ontology."); Set<SWRLRule> ruleSet = new HashSet<SWRLRule>(); ruleSet.addAll(rules); manager.addAxioms(ontology, ruleSet); if(log.isDebugEnabled()) for(OWLAxiom a:ontology.getAxioms()){ log.debug("Axiom {}",a); } log.debug("Calling the run method."); return run(ontology, generators); } /** * This method provides the default implementation for executing one of the default tasks with no * additional arguments. * * TODO: Add support for the filtered parameter on task 'classify'; */ @Override public Set<OWLAxiom> runTask(String taskID, OWLOntology data) throws UnsupportedTaskException, ReasoningServiceException, InconsistentInputException { if (taskID.equals(ReasoningService.Tasks.CLASSIFY)) { return classify(data); } else if (taskID.equals(ReasoningService.Tasks.ENRICH)) { return enrich(data); } else throw new UnsupportedTaskException(); } /** * This method provides the default implementation for executing one of the default tasks. * * TODO: Add support for the filtered parameter on task 'classify'; */ @Override public Set<OWLAxiom> runTask(String taskID, OWLOntology data, List<SWRLRule> rules, boolean filtered, Map<String,List<String>> parameters) throws UnsupportedTaskException, ReasoningServiceException, InconsistentInputException { log.info("Called task {} with data {}", taskID, data); if (taskID.equals(ReasoningService.Tasks.CLASSIFY)) { if (rules != null) { return classify(data, rules); } else { log.warn("No rules attached"); return classify(data); } } else if (taskID.equals(ReasoningService.Tasks.ENRICH)) { if (rules != null) { return enrich(data, rules, filtered); } else { return enrich(data, filtered); } } else throw new UnsupportedTaskException(); } /** * {@see InferredAxiomGenerator}s to use for the classify() reasoning method. * * Subclasses may want to change this. * * @return */ protected List<InferredAxiomGenerator<? extends OWLAxiom>> getClassifyAxiomGenerators() { List<InferredAxiomGenerator<? extends OWLAxiom>> gens = new ArrayList<InferredAxiomGenerator<? extends OWLAxiom>>(); gens.add(new InferredClassAssertionAxiomGenerator()); gens.add(new InferredSubClassAxiomGenerator()); gens.add(new InferredEquivalentClassAxiomGenerator()); gens.add(new InferredDisjointClassesAxiomGenerator()); return gens; } /** * {@see InferredAxiomGenerator}s to use for the enrich() reasoning method. * * Subclasses may want to change this. * * @return */ protected List<InferredAxiomGenerator<? extends OWLAxiom>> getEnrichAxiomGenerators() { List<InferredAxiomGenerator<? extends OWLAxiom>> gens = new ArrayList<InferredAxiomGenerator<? extends OWLAxiom>>(); // Classes gens.add(new InferredClassAssertionAxiomGenerator()); gens.add(new InferredSubClassAxiomGenerator()); gens.add(new InferredEquivalentClassAxiomGenerator()); gens.add(new InferredDisjointClassesAxiomGenerator()); // data properties gens.add(new InferredDataPropertyCharacteristicAxiomGenerator()); gens.add(new InferredEquivalentDataPropertiesAxiomGenerator()); gens.add(new InferredSubDataPropertyAxiomGenerator()); // object properties gens.add(new InferredEquivalentObjectPropertyAxiomGenerator()); gens.add(new InferredInverseObjectPropertiesAxiomGenerator()); gens.add(new InferredObjectPropertyCharacteristicAxiomGenerator()); gens.add(new InferredSubObjectPropertyAxiomGenerator()); // individuals gens.add(new InferredClassAssertionAxiomGenerator()); gens.add(new InferredPropertyAssertionGenerator()); return gens; } /** * Classify, returns only axioms about classes and instances * * This is the default implementation of task {@see ReasoningService.Tasks.CLASSIFY}. Subclasses may want * to change it. * * @param ontology * @return */ protected Set<OWLAxiom> classify(OWLOntology ontology) throws ReasoningServiceException, InconsistentInputException { log.info("classify(OWLOntology ontology)"); return run(ontology, getClassifyAxiomGenerators()); } /** * Classify, merge SWRL rules in the input ontology, before * * This is the default implementation of task {@see ReasoningService.Tasks.CLASSIFY} when rules are given. * Subclasses may want to change it. * * @param ontology * @param rules * @return * @throws InconsistentInputException * @throws ReasoningServiceException */ protected Set<OWLAxiom> classify(OWLOntology ontology, List<SWRLRule> rules) throws ReasoningServiceException, InconsistentInputException { log.debug("Calling classify(OWLOntology ontology, List<SWRLRule> rules) "); return run(ontology, rules, getClassifyAxiomGenerators()); } /** * Enrich, return all inferences. This is the same as enrich(ontology,false); * * This is the default implementation of task {@see ReasoningService.Tasks.ENRICH}. Subclasses may want to * change it. * * @param ontology * @return * @throws ReasoningServiceException * @throws InconsistentInputException */ protected Set<OWLAxiom> enrich(OWLOntology ontology) throws ReasoningServiceException, InconsistentInputException { return run(ontology, getEnrichAxiomGenerators()); } /** * Enrich, return all inferences. If filtered = false, then merge the inferences with the input * * This is the default implementation of task {@see ReasoningService.Tasks.ENRICH}. Subclasses may want to * change it. * * @param ontology * @param filtered * @return * @throws ReasoningServiceException * @throws InconsistentInputException */ protected Set<OWLAxiom> enrich(OWLOntology ontology, boolean filtered) throws ReasoningServiceException, InconsistentInputException { log.debug("Calling enrich(OWLOntology ontology, filtered) "); // If filtered = false, then we merge the output with the input if (filtered) { return run(ontology, getEnrichAxiomGenerators()); } else { Set<OWLAxiom> output = ontology.getAxioms(); output.addAll(run(ontology, getEnrichAxiomGenerators())); return output; } } /** * Enrich, merge SWRL rules and return all inferences. This is the same as enrich(ontology,rules,false) * * This is the default implementation of task {@see ReasoningService.Tasks.ENRICH}. Subclasses may want to * change it. * * @param ontology * @param rules * @return * @throws ReasoningServiceException * @throws InconsistentInputException */ protected Set<OWLAxiom> enrich(OWLOntology ontology, List<SWRLRule> rules) throws ReasoningServiceException, InconsistentInputException { log.debug("Calling enrich(OWLOntology ontology, List<SWRLRule> rules) "); return run(ontology, rules, getEnrichAxiomGenerators()); } /** * Enrich, merge SWRL rules and return all inferences. If filtered = false, then merge the inferences with * the input. * * This is the default implementation of task {@see ReasoningService.Tasks.ENRICH}. Subclasses may want to * change it. * * @param ontology * @param rules * @param filtered * @return * @throws ReasoningServiceException * @throws InconsistentInputException */ protected Set<OWLAxiom> enrich(OWLOntology ontology, List<SWRLRule> rules, boolean filtered) throws ReasoningServiceException, InconsistentInputException { log.debug("Calling enrich(OWLOntology ontology, List<SWRLRule> rules) "); if (filtered) { return run(ontology, rules, getEnrichAxiomGenerators()); } else { Set<OWLAxiom> output = ontology.getAxioms(); output.addAll(run(ontology, rules, getEnrichAxiomGenerators())); return output; } } /** * Only check consistency. * * Subclasses may want to change how. * * @param ontology * @return * @throws ReasoningServiceException */ @Override public boolean isConsistent(OWLOntology ontology) throws ReasoningServiceException { try { return getReasoner(ontology).isConsistent(); } catch (Exception e) { log.error("An error have been thrown while attempting to check consistency. Message was: {}", e.getLocalizedMessage()); // TODO Add explanation of this exception throw new ReasoningServiceException(); } } /** * Only check consistency. * * Subclasses may want to change how. * * @param ontology * @param rules * @return * @throws ReasoningServiceException */ @Override public boolean isConsistent(OWLOntology ontology, List<SWRLRule> rules) throws ReasoningServiceException { log.debug("Create a input ontology to merge rules in."); OWLOntology input; try { OWLOntologyManager manager = createOWLOntologyManager(); input = manager.createOntology(); Set<SWRLRule> ruleSet = new HashSet<SWRLRule>(); ruleSet.addAll(rules); manager.addAxioms(input, ruleSet); input = manager.getOntology(input.getOntologyID()); log.debug("Created ontology: {}", input); return getReasoner(ontology).isConsistent(); } catch (OWLOntologyCreationException e) { log.error("An error have been thrown while attempting to create ontology. Message was: {}", e.getLocalizedMessage()); // TODO Add explanation of this exception throw new ReasoningServiceException(); } } /** * The abstract implementation of an OWLApi based reasoning service supports all default {@see * ReasoningService.Tasks}. Subclasses may want to extend this. */ @Override public List<String> getSupportedTasks() { return ReasoningService.Tasks.DEFAULT_TASKS; } @Override public boolean supportsTask(String taskID) { return getSupportedTasks().contains(taskID); } /** * OWLApi based reasoning services have type {@see OWLOntology} as input type */ public Class<OWLOntology> getModelType() { return OWLOntology.class; } /** * OWLApi based reasoning services have type {@see SWRLRule} as rule type */ public Class<SWRLRule> getRuleType() { return SWRLRule.class; } /** * OWLApi based reasoning services have type {@see OWLAxiom} as statement type */ public Class<OWLAxiom> getStatementType() { return OWLAxiom.class; } }