package ch.uzh.ifi.attempto.acewiki.gf;
import java.util.HashSet;
import java.util.Set;
import org.semanticweb.owlapi.apibinding.OWLManager;
import org.semanticweb.owlapi.io.StringDocumentSource;
import org.semanticweb.owlapi.model.OWLAxiom;
import org.semanticweb.owlapi.model.OWLOntology;
import org.semanticweb.owlapi.model.OWLOntologyCreationException;
import org.semanticweb.owlapi.model.OWLOntologyManager;
import org.semanticweb.owlapi.model.OWLSubPropertyChainOfAxiom;
import org.semanticweb.owlapi.model.OWLTransitiveObjectPropertyAxiom;
import org.semanticweb.owlapi.profiles.OWLProfile;
import org.semanticweb.owlapi.profiles.OWLProfileReport;
import org.semanticweb.owlapi.profiles.OWLProfileViolation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.collect.Sets;
import ch.uzh.ifi.attempto.acewiki.owl.AceWikiOWLReasoner2;
import ch.uzh.ifi.attempto.ape.ACEParserResult;
import ch.uzh.ifi.attempto.ape.ACEText;
import ch.uzh.ifi.attempto.ape.OutputType;
/**
* Some static methods for the OWL conversion of GF-based wiki entries.
*
* TODO: work in progress
*
* @author Kaarel Kaljurand
*/
public class GfOwlConverter {
private static final OWLOntologyManager OWL_ONTOLOGY_MANAGER = OWLManager.createOWLOntologyManager();
// TODO: It might be better to use OWLFSSPP as it is smaller in size and
// might be faster to parse. Currently the problem is that OWLFSSPP uses
// anonymous individuals, but these cannot be always reasoned with in OWL2, e.g. in
// the SameIndividual axiom. The OWLXML output has already mapped such individuals
// to globally unique named individuals.
private static final OutputType OWL_SERIALIZATION_TYPE = OutputType.OWLXML;
private static final Logger mLogger = LoggerFactory.getLogger(GfOwlConverter.class);
/**
* Converts the given GF wiki entry into OWL.
* This succeeds if the entry is compatible with ACE and OWL, i.e.
* at least one of the trees can be mapped to OWL.
*/
public static Set<Set<OWLAxiom>> convert(GfGrammar gfGrammar, String uri, GfWikiEntry gfWikiEntry) throws OWLOntologyCreationException {
if (! gfGrammar.isAceCompatible()) {
// TODO: throw exception instead
return null;
}
// Convert all trees to ACE texts
Set<ACEText> acetexts = new HashSet<ACEText>();
for (String tree : gfWikiEntry.getTrees().getTrees()) {
try {
ACEText acetext = GfWikiUtils.getACEText(gfGrammar, tree);
if (acetext != null) {
acetexts.add(acetext);
}
} catch (Exception e) {
// TODO do not ignore exception
}
}
// Stop if no tree mapped to an ACE text
if (acetexts.isEmpty()) {
// TODO: throw exception instead
return null;
}
// Set of axioms from each reading
Set<Set<OWLAxiom>> setOfSetOfAxiom = Sets.newHashSet();
for (ACEText acetext : acetexts) {
ACEParserResult parserResult = GfWikiUtils.parse(acetext, uri, OWL_SERIALIZATION_TYPE);
Set<OWLAxiom> axioms = getOwlAxiomsFromString(parserResult.get(OWL_SERIALIZATION_TYPE));
if (axioms.isEmpty()) {
// One of the readings did not result in any axioms
// TODO: not sure what to do here, maybe just ignore?
} else {
setOfSetOfAxiom.add(axioms);
}
}
return setOfSetOfAxiom;
}
/**
* TODO: move to its own class
*
* Given a set of sets of axioms, choose a single set and return it.
*
* TODO: there can be many disambiguation techniques and the user might want to
* configure which one to use:
*
* 1. choose the first reading
* 2. combine the readings into a disjunction
* 3. prune the readings by checking which are consistent and redundant with respect to the KB, then choose by 1. or 2.
* 4. ...
*/
public static Set<OWLAxiom> disambiguate(Set<Set<OWLAxiom>> setOfSetOfAxiom) {
// We have now a set of axiom sets.
if (setOfSetOfAxiom.isEmpty()) {
throw new IllegalArgumentException("Set of OWL axioms must not be empty");
}
if (setOfSetOfAxiom.size() == 1) {
// If this set contain a single element, then select this element.
return setOfSetOfAxiom.iterator().next();
}
// If this set contains multiple elements (i.e. is ambiguous), then resolve it somehow,
// selecting only a single element.
// TODO
return setOfSetOfAxiom.iterator().next();
}
/**
* <p>Return {@code true} iff the given axioms are reasonable for the given reasoner.</p>
*/
public static boolean isReasonable(AceWikiOWLReasoner2 reasoner, Set<OWLAxiom> axioms) {
// If the reasoner policy does not allow property chains, then
// check if the axioms contain such chains and if so, then return false.
if ("no_chains".equals(reasoner.getGlobalRestrictionsPolicy())) {
for (OWLAxiom ax : axioms) {
if (ax instanceof OWLTransitiveObjectPropertyAxiom || ax instanceof OWLSubPropertyChainOfAxiom) {
mLogger.info("Reasoner policy ({}) violated: {}", reasoner.getGlobalRestrictionsPolicy(), ax);
return false;
}
}
}
// If the reasoner profile does not allow the given axioms then return false.
// TODO: make it more efficient: we probably do not need to create a new ontology
boolean isReasonable = true;
OWLProfile owlProfile = reasoner.getOWLProfile();
if (owlProfile == null) {
// Using the default and most permissive profile
} else {
OWLOntology owlOntology;
try {
owlOntology = OWL_ONTOLOGY_MANAGER.createOntology(axioms);
} catch (OWLOntologyCreationException e) {
return false;
}
if (owlOntology != null) {
OWLProfileReport r = owlProfile.checkOntology(owlOntology);
for (OWLProfileViolation v : r.getViolations()) {
// We do not care about undeclared entities
if (!v.toString().startsWith("Use of undeclared")) {
isReasonable = false;
mLogger.info("Reasoner profile ({}) violation: {}", owlProfile.getName(), v);
break;
}
}
}
OWL_ONTOLOGY_MANAGER.removeOntology(owlOntology);
}
return isReasonable;
}
/**
* <p>Interprets the given string as a serialization of an OWL ontology,
* maps it to a set of OWL axioms and returns those.</p>
*
* @param str serialization of an OWL ontology e.g. in the OWLFSSPP format
* @return set of OWL axioms
* @throws OWLOntologyCreationException
*
* TODO: not sure if this is the most efficient way to do this
*/
public static Set<OWLAxiom> getOwlAxiomsFromString(String str) throws OWLOntologyCreationException {
OWLOntology owlOntology = OWL_ONTOLOGY_MANAGER.loadOntologyFromOntologyDocument(new StringDocumentSource(str));
OWL_ONTOLOGY_MANAGER.removeOntology(owlOntology);
return owlOntology.getAxioms();
}
}