All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.semanticweb.HermiT.structural.OWLClausification Maven / Gradle / Ivy

Go to download

HermiT is reasoner for ontologies written using the Web Ontology Language (OWL). Given an OWL file, HermiT can determine whether or not the ontology is consistent, identify subsumption relationships between classes, and much more. This is the maven build of HermiT and is designed for people who wish to use HermiT from within the OWL API. It is now versioned in the main HermiT version repository, although not officially supported by the HermiT developers. The version number of this package is a composite of the HermiT version and a value representing the OWLAPI release it is compatible with. Note that the group id for the upstream HermiT is com.hermit-reasoner, while this fork is released under net.sourceforge.owlapi. This fork exists to allow HermiT users to use newer OWLAPI versions than the ones supported by the original HermiT codebase. This package includes the Jautomata library (http://jautomata.sourceforge.net/), and builds with it directly. This library appears to be no longer under active development, and so a "fork" seems appropriate. No development is intended or anticipated on this code base.

The newest version!
/* Copyright 2008, 2009, 2010 by the Oxford University Computing Laboratory

   This file is part of HermiT.

   HermiT 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.

   HermiT 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 HermiT.  If not, see .
 */
package org.semanticweb.HermiT.structural;

import static org.semanticweb.owlapi.util.OWLAPIStreamUtils.asList;

import java.util.*;

import org.semanticweb.HermiT.Configuration;
import org.semanticweb.HermiT.Prefixes;
import org.semanticweb.HermiT.datatypes.DatatypeRegistry;
import org.semanticweb.HermiT.datatypes.UnsupportedDatatypeException;
import org.semanticweb.HermiT.model.AnnotatedEquality;
import org.semanticweb.HermiT.model.AtLeastConcept;
import org.semanticweb.HermiT.model.AtLeastDataRange;
import org.semanticweb.HermiT.model.Atom;
import org.semanticweb.HermiT.model.AtomicConcept;
import org.semanticweb.HermiT.model.AtomicRole;
import org.semanticweb.HermiT.model.Constant;
import org.semanticweb.HermiT.model.ConstantEnumeration;
import org.semanticweb.HermiT.model.DLClause;
import org.semanticweb.HermiT.model.DLOntology;
import org.semanticweb.HermiT.model.DLPredicate;
import org.semanticweb.HermiT.model.DatatypeRestriction;
import org.semanticweb.HermiT.model.DescriptionGraph;
import org.semanticweb.HermiT.model.Equality;
import org.semanticweb.HermiT.model.Individual;
import org.semanticweb.HermiT.model.Inequality;
import org.semanticweb.HermiT.model.InternalDatatype;
import org.semanticweb.HermiT.model.LiteralConcept;
import org.semanticweb.HermiT.model.LiteralDataRange;
import org.semanticweb.HermiT.model.NodeIDLessEqualThan;
import org.semanticweb.HermiT.model.NodeIDsAscendingOrEqual;
import org.semanticweb.HermiT.model.Role;
import org.semanticweb.HermiT.model.Term;
import org.semanticweb.HermiT.model.Variable;
import org.semanticweb.owlapi.apibinding.OWLManager;
import org.semanticweb.owlapi.model.*;
import org.semanticweb.owlapi.vocab.OWL2Datatype;
/**OWLClausification.*/
public class OWLClausification {
    private static final String INVALID_NORMAL_FORM = "Internal error: invalid normal form.";
    protected static final Variable X=Variable.create("X");
    protected static final Variable Y=Variable.create("Y");
    protected static final Variable Z=Variable.create("Z");

    protected final Configuration m_configuration;

    /**
     * @param configuration configuration
     */
    public OWLClausification(Configuration configuration) {
        m_configuration=configuration;
    }
    /**
     * @param rootOntology rootOntology
     * @param descriptionGraphs descriptionGraphs
     * @return clausifications
     */
    public Object[] preprocessAndClausify(OWLOntology rootOntology,Collection descriptionGraphs) {
        OWLDataFactory factory=rootOntology.getOWLOntologyManager().getOWLDataFactory();
        Optional defaultDocumentIRI = rootOntology.getOntologyID().getDefaultDocumentIRI();
        String ontologyIRI=defaultDocumentIRI.isPresent()?defaultDocumentIRI.get().toString(): "urn:hermit:kb" ;
        OWLAxioms axioms=new OWLAxioms();
        OWLNormalization normalization=new OWLNormalization(factory,axioms,0);
        normalization.processOntology(rootOntology);
        BuiltInPropertyManager builtInPropertyManager=new BuiltInPropertyManager(factory);
        builtInPropertyManager.axiomatizeBuiltInPropertiesAsNeeded(axioms);
        ObjectPropertyInclusionManager objectPropertyInclusionManager=new ObjectPropertyInclusionManager(axioms);
        // now object property inclusion manager added all non-simple properties to axioms.m_complexObjectPropertyExpressions
        // now that we know which roles are non-simple, we can decide which negative object property assertions have to be
        // expressed as concept assertions so that transitivity rewriting applies properly.
        objectPropertyInclusionManager.rewriteNegativeObjectPropertyAssertions(factory,axioms,normalization.m_definitions.size());
        objectPropertyInclusionManager.rewriteAxioms(factory,axioms,0);
        OWLAxiomsExpressivity axiomsExpressivity=new OWLAxiomsExpressivity(axioms);
        DLOntology dlOntology=clausify(factory,ontologyIRI,axioms,axiomsExpressivity,descriptionGraphs!=null?descriptionGraphs:Collections.emptySet());
        return new Object[] { objectPropertyInclusionManager,dlOntology };
    }
    /**
     * @param factory factory
     * @param ontologyIRI ontologyIRI
     * @param axioms axioms
     * @param axiomsExpressivity axiomsExpressivity
     * @param descriptionGraphs descriptionGraphs
     * @return dl ontology
     */
    public DLOntology clausify(OWLDataFactory factory,String ontologyIRI,OWLAxioms axioms,OWLAxiomsExpressivity axiomsExpressivity,Collection descriptionGraphs) {
        List dlClauses=new ArrayList<>();
        Set allUnknownDatatypeRestrictions=new HashSet<>();
        for (List inclusion : axioms.m_simpleObjectPropertyInclusions) {
            Atom subRoleAtom=getRoleAtom(inclusion.get(0),X,Y);
            Atom superRoleAtom=getRoleAtom(inclusion.get(1),X,Y);
            DLClause dlClause=DLClause.create(new Atom[] { superRoleAtom },new Atom[] { subRoleAtom });
            dlClauses.add(dlClause);
        }
        for (List inclusion : axioms.m_dataPropertyInclusions) {
            Atom subProp=getRoleAtom(inclusion.get(0),X,Y);
            Atom superProp=getRoleAtom(inclusion.get(1),X,Y);
            DLClause dlClause=DLClause.create(new Atom[] { superProp },new Atom[] { subProp });
            dlClauses.add(dlClause);
        }
        for (OWLObjectPropertyExpression objectPropertyExpression : axioms.m_asymmetricObjectProperties) {
            Atom roleAtom=getRoleAtom(objectPropertyExpression,X,Y);
            Atom inverseRoleAtom=getRoleAtom(objectPropertyExpression,Y,X);
            DLClause dlClause=DLClause.create(new Atom[] {},new Atom[] { roleAtom,inverseRoleAtom });
            dlClauses.add(dlClause);
        }
        for (OWLObjectPropertyExpression objectPropertyExpression : axioms.m_reflexiveObjectProperties) {
            Atom roleAtom=getRoleAtom(objectPropertyExpression,X,X);
            Atom bodyAtom=Atom.create(AtomicConcept.THING,X);
            DLClause dlClause=DLClause.create(new Atom[] { roleAtom },new Atom[] { bodyAtom });
            dlClauses.add(dlClause);
        }
        for (OWLObjectPropertyExpression objectPropertyExpression : axioms.m_irreflexiveObjectProperties) {
            Atom roleAtom=getRoleAtom(objectPropertyExpression,X,X);
            DLClause dlClause=DLClause.create(new Atom[] {},new Atom[] { roleAtom });
            dlClauses.add(dlClause);
        }
        for (List properties : axioms.m_disjointObjectProperties)
            for (int i=0;i properties : axioms.m_disjointDataProperties)
            for (int i=0;i positiveFacts=new HashSet<>();
        Set negativeFacts=new HashSet<>();
        DataRangeConverter dataRangeConverter=new DataRangeConverter(m_configuration.warningMonitor,axioms.m_definedDatatypesIRIs,allUnknownDatatypeRestrictions,m_configuration.ignoreUnsupportedDatatypes);
        NormalizedAxiomClausifier clausifier=new NormalizedAxiomClausifier(dataRangeConverter,positiveFacts);
        for (List inclusion : axioms.m_conceptInclusions) {
            for (OWLClassExpression description : inclusion)
                description.accept(clausifier);
            DLClause dlClause=clausifier.getDLClause();
            dlClauses.add(dlClause.getSafeVersion(AtomicConcept.THING));
        }
        NormalizedDataRangeAxiomClausifier normalizedDataRangeAxiomClausifier=new NormalizedDataRangeAxiomClausifier(dataRangeConverter,factory, axioms.m_definedDatatypesIRIs);
        for (List inclusion : axioms.m_dataRangeInclusions) {
            for (OWLDataRange description : inclusion)
                description.accept(normalizedDataRangeAxiomClausifier);
            DLClause dlClause=normalizedDataRangeAxiomClausifier.getDLClause();
            dlClauses.add(dlClause.getSafeVersion(InternalDatatype.RDFS_LITERAL));
        }
        for (OWLHasKeyAxiom hasKey : axioms.m_hasKeys)
            dlClauses.add(clausifyKey(hasKey));
        FactClausifier factClausifier=new FactClausifier(dataRangeConverter,positiveFacts,negativeFacts);
        for (OWLIndividualAxiom fact : axioms.m_facts)
            fact.accept(factClausifier);
        for (DescriptionGraph descriptionGraph : descriptionGraphs)
            descriptionGraph.produceStartDLClauses(dlClauses);
        Set atomicConcepts=new HashSet<>();
        Set atomicObjectRoles=new HashSet<>();
        Set complexObjectRoles=new HashSet<>();
        Set atomicDataRoles=new HashSet<>();
        for (OWLClass owlClass : axioms.m_classes)
            atomicConcepts.add(AtomicConcept.create(owlClass.getIRI().toString()));
        Set individuals=new HashSet<>();
        for (OWLNamedIndividual owlIndividual : axioms.m_namedIndividuals) {
            Individual individual=Individual.create(owlIndividual.getIRI().toString());
            individuals.add(individual);
            // all named individuals are tagged with a concept, so that keys/rules are
            // only applied to them
            if (!axioms.m_hasKeys.isEmpty() || !axioms.m_rules.isEmpty())
                positiveFacts.add(Atom.create(AtomicConcept.INTERNAL_NAMED,individual));
        }
        for (OWLObjectProperty objectProperty : axioms.m_objectProperties)
            atomicObjectRoles.add(AtomicRole.create(objectProperty.getIRI().toString()));
        for (OWLObjectPropertyExpression objectPropertyExpression : axioms.m_complexObjectPropertyExpressions)
            complexObjectRoles.add(getRole(objectPropertyExpression));
        for (OWLDataProperty dataProperty : axioms.m_dataProperties)
            atomicDataRoles.add(AtomicRole.create(dataProperty.getIRI().toString()));
        // Clausify SWRL rules
        if (!axioms.m_rules.isEmpty())
            new NormalizedRuleClausifier(axioms.m_objectPropertiesOccurringInOWLAxioms,descriptionGraphs,dataRangeConverter,dlClauses).processRules(axioms.m_rules);
        //drop duplicates
        dlClauses=new ArrayList<>(new LinkedHashSet<>(dlClauses));
        // Create the DL ontology
        return new DLOntology(ontologyIRI,dlClauses,positiveFacts,negativeFacts,atomicConcepts,atomicObjectRoles,complexObjectRoles,atomicDataRoles,allUnknownDatatypeRestrictions,axioms.m_definedDatatypesIRIs,individuals,axiomsExpressivity.m_hasInverseRoles,axiomsExpressivity.m_hasAtMostRestrictions,axiomsExpressivity.m_hasNominals,axiomsExpressivity.m_hasDatatypes);
    }
    protected DLClause clausifyKey(OWLHasKeyAxiom object) {
        List headAtoms=new ArrayList<>();
        List bodyAtoms=new ArrayList<>();
        // we have two named individuals (corresponding to X1 and X2) that
        // might have to be equated
        Variable X2=Variable.create("X2");
        Variable X1=Variable.create("X1");
        headAtoms.add(Atom.create(Equality.INSTANCE,X1,X2));
        // keys only work on datatypes and named individuals
        bodyAtoms.add(Atom.create(AtomicConcept.INTERNAL_NAMED,X1));
        bodyAtoms.add(Atom.create(AtomicConcept.INTERNAL_NAMED,X2));
        // the concept expression of a hasKey statement is either a concept
        // name or a negated concept name after normalization
        OWLClassExpression description=object.getClassExpression();
        if (description instanceof OWLClass) {
            OWLClass owlClass=(OWLClass)description;
            if (!owlClass.isOWLThing()) {
                bodyAtoms.add(Atom.create(AtomicConcept.create(owlClass.getIRI().toString()),X1));
                bodyAtoms.add(Atom.create(AtomicConcept.create(owlClass.getIRI().toString()),X2));
            }
        }
        else if (description instanceof OWLObjectComplementOf) {
            OWLClassExpression internal=((OWLObjectComplementOf)description).getOperand();
            if (internal instanceof OWLClass) {
                OWLClass owlClass=(OWLClass)internal;
                headAtoms.add(Atom.create(AtomicConcept.create(owlClass.getIRI().toString()),X1));
                headAtoms.add(Atom.create(AtomicConcept.create(owlClass.getIRI().toString()),X2));
            }
            else
                throw new IllegalStateException(INVALID_NORMAL_FORM);
        }
        else
            throw new IllegalStateException(INVALID_NORMAL_FORM);
        int yIndex=1;
        // object properties always go to the body
        for (OWLObjectPropertyExpression p : asList(object.objectPropertyExpressions())) {
            Variable y;
            y=Variable.create("Y"+yIndex);
            yIndex++;
            bodyAtoms.add(getRoleAtom(p,X1,y));
            bodyAtoms.add(getRoleAtom(p,X2,y));
            // also the key criteria are named in case of object properties
            bodyAtoms.add(Atom.create(AtomicConcept.INTERNAL_NAMED,y));
        }
        // data properties go to the body, but with different variables
        // the head gets an atom asserting inequality between that data values
        for (OWLDataPropertyExpression d : asList(object.dataPropertyExpressions())) {
            Variable y;
            y=Variable.create("Y"+yIndex);
            yIndex++;
            bodyAtoms.add(getRoleAtom(d,X1,y));
            Variable y2;
            y2=Variable.create("Y"+yIndex);
            yIndex++;
            bodyAtoms.add(getRoleAtom(d,X2,y2));
            headAtoms.add(Atom.create(Inequality.INSTANCE,y,y2));
        }
        Atom[] hAtoms=new Atom[headAtoms.size()];
        headAtoms.toArray(hAtoms);
        Atom[] bAtoms=new Atom[bodyAtoms.size()];
        bodyAtoms.toArray(bAtoms);
        return DLClause.create(hAtoms,bAtoms);
    }
    private static boolean contains(OWLAxioms axioms, OWLDataProperty p) {
        for(List e: axioms.m_dataPropertyInclusions) {
            for(OWLDataPropertyExpression candidate:e) {
                if(candidate.equals(p)) {
                    return true;
                }
            }
        }
        return false;
    }
    
    protected static LiteralConcept getLiteralConcept(OWLClassExpression description) {
        if (description instanceof OWLClass) {
            return AtomicConcept.create(((OWLClass)description).getIRI().toString());
        }
        else if (description instanceof OWLObjectComplementOf) {
            OWLClassExpression internal=((OWLObjectComplementOf)description).getOperand();
            if (!(internal instanceof OWLClass))
                throw new IllegalStateException(INVALID_NORMAL_FORM);
            return AtomicConcept.create(((OWLClass)internal).getIRI().toString()).getNegation();
        }
        else
            throw new IllegalStateException(INVALID_NORMAL_FORM);
    }
    protected static Role getRole(OWLObjectPropertyExpression objectPropertyExpression) {
        if (objectPropertyExpression instanceof OWLObjectProperty)
            return AtomicRole.create(((OWLObjectProperty)objectPropertyExpression).getIRI().toString());
        else if (objectPropertyExpression instanceof OWLObjectInverseOf) {
            OWLObjectPropertyExpression internal=((OWLObjectInverseOf)objectPropertyExpression).getInverse();
            if (!(internal instanceof OWLObjectProperty))
                throw new IllegalStateException(INVALID_NORMAL_FORM);
            return AtomicRole.create(((OWLObjectProperty)internal).getIRI().toString()).getInverse();
        }
        else
            throw new IllegalStateException(INVALID_NORMAL_FORM);
    }
    protected static AtomicRole getAtomicRole(OWLDataPropertyExpression dataPropertyExpression) {
        return AtomicRole.create(((OWLDataProperty)dataPropertyExpression).getIRI().toString());
    }
    protected static Atom getRoleAtom(OWLObjectPropertyExpression objectProperty,Term first,Term second) {
        if (!objectProperty.isAnonymous()) {
            AtomicRole role=AtomicRole.create(objectProperty.asOWLObjectProperty().getIRI().toString());
            return Atom.create(role,first,second);
        }
        else if (objectProperty.isAnonymous()) {
            OWLObjectProperty internalObjectProperty=objectProperty.getNamedProperty();
            AtomicRole role=AtomicRole.create(internalObjectProperty.getIRI().toString());
            return Atom.create(role,second,first);
        }
        else
            throw new IllegalStateException("Internal error: unsupported type of object property!");
    }
    protected static Atom getRoleAtom(OWLDataPropertyExpression dataProperty,Term first,Term second) {
        if (dataProperty instanceof OWLDataProperty) {
            AtomicRole property=AtomicRole.create(((OWLDataProperty)dataProperty).getIRI().toString());
            return Atom.create(property,first,second);
        }
        else
            throw new IllegalStateException("Internal error: unsupported type of data property!");
    }
    protected static Individual getIndividual(OWLIndividual individual) {
        if (individual.isAnonymous())
            return Individual.createAnonymous(individual.asOWLAnonymousIndividual().getID().toString());
        else
            return Individual.create(individual.asOWLNamedIndividual().getIRI().toString());
    }

    protected static class NormalizedAxiomClausifier implements OWLClassExpressionVisitor {
        protected final DataRangeConverter m_dataRangeConverter;
        protected final List m_headAtoms;
        protected final List m_bodyAtoms;
        protected final Set m_positiveFacts;
        protected int m_yIndex;
        protected int m_zIndex;

        public NormalizedAxiomClausifier(DataRangeConverter dataRangeConverter,Set positiveFacts) {
            m_dataRangeConverter=dataRangeConverter;
            m_headAtoms=new ArrayList<>();
            m_bodyAtoms=new ArrayList<>();
            m_positiveFacts=positiveFacts;
        }
        protected DLClause getDLClause() {
            Atom[] headAtoms=new Atom[m_headAtoms.size()];
            m_headAtoms.toArray(headAtoms);
            Atom[] bodyAtoms=new Atom[m_bodyAtoms.size()];
            m_bodyAtoms.toArray(bodyAtoms);
            DLClause dlClause=DLClause.create(headAtoms,bodyAtoms);
            m_headAtoms.clear();
            m_bodyAtoms.clear();
            m_yIndex=0;
            m_zIndex=0;
            return dlClause;
        }
        protected void ensureYNotZero() {
            if (m_yIndex==0)
                m_yIndex++;
        }
        protected Variable nextY() {
            Variable result;
            if (m_yIndex==0)
                result=Y;
            else
                result=Variable.create("Y"+m_yIndex);
            m_yIndex++;
            return result;
        }
        protected Variable nextZ() {
            Variable result;
            if (m_zIndex==0)
                result=Z;
            else
                result=Variable.create("Z"+m_zIndex);
            m_zIndex++;
            return result;
        }
        protected AtomicConcept getConceptForNominal(OWLIndividual individual) {
            AtomicConcept result;
            if (individual.isAnonymous()) {
                result=AtomicConcept.create("internal:anon#"+individual.asOWLAnonymousIndividual().getID().toString());
            }
            else {
                result=AtomicConcept.create("internal:nom#"+individual.asOWLNamedIndividual().getIRI().toString());
            }
            m_positiveFacts.add(Atom.create(result,getIndividual(individual)));
            return result;
        }

        // Various types of descriptions

        @Override
        public void visit(OWLClass object) {
            m_headAtoms.add(Atom.create(AtomicConcept.create(object.getIRI().toString()),X));
        }
        @Override
        public void visit(OWLObjectIntersectionOf object) {
            throw new IllegalStateException(INVALID_NORMAL_FORM);
        }
        @Override
        public void visit(OWLObjectUnionOf object) {
            throw new IllegalStateException(INVALID_NORMAL_FORM);
        }
        @Override
        public void visit(OWLObjectComplementOf object) {
            OWLClassExpression description=object.getOperand();
            if (description instanceof OWLObjectHasSelf) {
                OWLObjectPropertyExpression objectProperty=((OWLObjectHasSelf)description).getProperty();
                Atom roleAtom=getRoleAtom(objectProperty,X,X);
                m_bodyAtoms.add(roleAtom);
            }
            else if (description instanceof OWLObjectOneOf && ((OWLObjectOneOf)description).individuals().count()==1) {
                OWLIndividual individual=((OWLObjectOneOf)description).individuals().iterator().next();
                m_bodyAtoms.add(Atom.create(getConceptForNominal(individual),X));
            }
            else if (!(description instanceof OWLClass))
                throw new IllegalStateException(INVALID_NORMAL_FORM);
            else
                m_bodyAtoms.add(Atom.create(AtomicConcept.create(((OWLClass)description).getIRI().toString()),X));
        }
        @Override
        public void visit(OWLObjectOneOf object) {
            object.individuals().forEach(i-> {
                Variable z=nextZ();
                AtomicConcept conceptForNominal=getConceptForNominal(i);
                m_headAtoms.add(Atom.create(Equality.INSTANCE,X,z));
                m_bodyAtoms.add(Atom.create(conceptForNominal,z));
            });
        }
        @Override
        public void visit(OWLObjectSomeValuesFrom object) {
            OWLClassExpression filler=object.getFiller();
            if (filler instanceof OWLObjectOneOf) {
                ((OWLObjectOneOf)filler).individuals().forEach(i-> {
                    Variable z=nextZ();
                    m_bodyAtoms.add(Atom.create(getConceptForNominal(i),z));
                    m_headAtoms.add(getRoleAtom(object.getProperty(),X,z));
                });
            }
            else {
                LiteralConcept toConcept=getLiteralConcept(filler);
                Role onRole=getRole(object.getProperty());
                AtLeastConcept atLeastConcept=AtLeastConcept.create(1,onRole,toConcept);
                if (!atLeastConcept.isAlwaysFalse())
                    m_headAtoms.add(Atom.create(atLeastConcept,X));
            }
        }
        @Override
        public void visit(OWLObjectAllValuesFrom object) {
            Variable y=nextY();
            m_bodyAtoms.add(getRoleAtom(object.getProperty(),X,y));
            OWLClassExpression filler=object.getFiller();
            if (filler instanceof OWLClass) {
                AtomicConcept atomicConcept=AtomicConcept.create(((OWLClass)filler).getIRI().toString());
                if (!atomicConcept.isAlwaysFalse())
                    m_headAtoms.add(Atom.create(atomicConcept,y));
            }
            else if (filler instanceof OWLObjectOneOf) {
                ((OWLObjectOneOf)filler).individuals().forEach(i-> {
                    Variable zInd=nextZ();
                    m_bodyAtoms.add(Atom.create(getConceptForNominal(i),zInd));
                    m_headAtoms.add(Atom.create(Equality.INSTANCE,y,zInd));
                });
            }
            else if (filler instanceof OWLObjectComplementOf) {
                OWLClassExpression operand=((OWLObjectComplementOf)filler).getOperand();
                if (operand instanceof OWLClass) {
                    AtomicConcept internalAtomicConcept=AtomicConcept.create(((OWLClass)operand).getIRI().toString());
                    if (!internalAtomicConcept.isAlwaysTrue())
                        m_bodyAtoms.add(Atom.create(internalAtomicConcept,y));
                }
                else if (operand instanceof OWLObjectOneOf && ((OWLObjectOneOf)operand).individuals().count()==1) {
                    OWLIndividual individual=((OWLObjectOneOf)operand).individuals().iterator().next();
                    m_bodyAtoms.add(Atom.create(getConceptForNominal(individual),y));
                }
                else
                    throw new IllegalStateException(INVALID_NORMAL_FORM);
            }
            else
                throw new IllegalStateException(INVALID_NORMAL_FORM);
        }
        @Override
        public void visit(OWLObjectHasValue object) {
            throw new IllegalStateException(INVALID_NORMAL_FORM);
        }
        @Override
        public void visit(OWLObjectHasSelf object) {
            OWLObjectPropertyExpression objectProperty=object.getProperty();
            Atom roleAtom=getRoleAtom(objectProperty,X,X);
            m_headAtoms.add(roleAtom);
        }
        @Override
        public void visit(OWLObjectMinCardinality object) {
            LiteralConcept toConcept=getLiteralConcept(object.getFiller());
            Role onRole=getRole(object.getProperty());
            AtLeastConcept atLeastConcept=AtLeastConcept.create(object.getCardinality(),onRole,toConcept);
            if (!atLeastConcept.isAlwaysFalse())
                m_headAtoms.add(Atom.create(atLeastConcept,X));
        }
        @Override
        public void visit(OWLObjectMaxCardinality object) {
            int cardinality=object.getCardinality();
            OWLObjectPropertyExpression onObjectProperty=object.getProperty();
            OWLClassExpression filler=object.getFiller();
            ensureYNotZero();
            boolean isPositive;
            AtomicConcept atomicConcept;
            if (filler instanceof OWLClass) {
                isPositive=true;
                atomicConcept=AtomicConcept.create(((OWLClass)filler).getIRI().toString());
                if (atomicConcept.isAlwaysTrue())
                    atomicConcept=null;
            }
            else if (filler instanceof OWLObjectComplementOf) {
                OWLClassExpression internal=((OWLObjectComplementOf)filler).getOperand();
                if (!(internal instanceof OWLClass))
                    throw new IllegalStateException("Internal error: Invalid ontology normal form.");
                isPositive=false;
                atomicConcept=AtomicConcept.create(((OWLClass)internal).getIRI().toString());
                if (atomicConcept.isAlwaysFalse())
                    atomicConcept=null;
            }
            else
                throw new IllegalStateException("Internal error: Invalid ontology normal form.");
            Role onRole=getRole(onObjectProperty);
            LiteralConcept toConcept=getLiteralConcept(filler);
            AnnotatedEquality annotatedEquality=AnnotatedEquality.create(cardinality,onRole,toConcept);
            Variable[] yVars=new Variable[cardinality+1];
            for (int i=0;i2) {
                for (int i=0;i m_definedDatatypeIRIs;
        protected final List m_headAtoms;
        protected final List m_bodyAtoms;
        protected final OWLDataFactory m_factory;
        protected int m_yIndex;

        public NormalizedDataRangeAxiomClausifier(DataRangeConverter dataRangeConverter,OWLDataFactory factory,Set definedDatatypeIRIs) {
            m_dataRangeConverter=dataRangeConverter;
            m_definedDatatypeIRIs=definedDatatypeIRIs;
            m_headAtoms=new ArrayList<>();
            m_bodyAtoms=new ArrayList<>();
            m_factory=factory;
        }
        protected DLClause getDLClause() {
            Atom[] headAtoms=new Atom[m_headAtoms.size()];
            m_headAtoms.toArray(headAtoms);
            Atom[] bodyAtoms=new Atom[m_bodyAtoms.size()];
            m_bodyAtoms.toArray(bodyAtoms);
            DLClause dlClause=DLClause.create(headAtoms,bodyAtoms);
            m_headAtoms.clear();
            m_bodyAtoms.clear();
            m_yIndex=0;
            return dlClause;
        }
        protected void ensureYNotZero() {
            if (m_yIndex==0)
                m_yIndex++;
        }
        protected Variable nextY() {
            Variable result;
            if (m_yIndex==0)
                result=Y;
            else
                result=Variable.create("Y"+m_yIndex);
            m_yIndex++;
            return result;
        }

        // Various types of descriptions

        @Override
        public void visit(OWLDatatype dt) {
            LiteralDataRange literalRange=m_dataRangeConverter.convertDataRange(dt);
            m_headAtoms.add(Atom.create((DLPredicate)literalRange,X));
        }
        @Override
        public void visit(OWLDataIntersectionOf dr) {
            throw new IllegalStateException(INVALID_NORMAL_FORM);
        }
        @Override
        public void visit(OWLDataUnionOf dr) {
            throw new IllegalStateException(INVALID_NORMAL_FORM);
        }
        private static String datatypeIRI(OWLDataRange r) {
            if(r.isOWLDatatype()) {
                return r.asOWLDatatype().getIRI().toString();
            }
            return null;
        }
        @Override
        public void visit(OWLDataComplementOf dr) {
            String iri=datatypeIRI(dr.getDataRange());
            if (iri!=null && (Prefixes.isInternalIRI(iri) || m_definedDatatypeIRIs.contains(iri))) {
                m_bodyAtoms.add(Atom.create(InternalDatatype.create(iri),X));
            }
            else {
                LiteralDataRange literalRange=m_dataRangeConverter.convertDataRange(dr);
                if (literalRange.isNegatedInternalDatatype()) {
                    InternalDatatype negatedDatatype=(InternalDatatype)literalRange.getNegation();
                    if (!negatedDatatype.isAlwaysTrue())
                        m_bodyAtoms.add(Atom.create(negatedDatatype,X));
                }
                else {
                    if (!literalRange.isAlwaysFalse())
                        m_headAtoms.add(Atom.create((DLPredicate)literalRange,X));
                }
            }
        }
        @Override
        public void visit(OWLDataOneOf object) {
            LiteralDataRange literalRange=m_dataRangeConverter.convertDataRange(object);
            m_headAtoms.add(Atom.create((DLPredicate)literalRange,X));
        }
        @Override
        public void visit(OWLFacetRestriction node) {
            throw new IllegalStateException("Internal error: Invalid normal form. ");
        }
        @Override
        public void visit(OWLDatatypeRestriction node) {
            LiteralDataRange literalRange=m_dataRangeConverter.convertDataRange(node);
            m_headAtoms.add(Atom.create((DLPredicate)literalRange,X));
        }
        @Override
        public void visit(OWLLiteral node) {
            throw new IllegalStateException("Internal error: Invalid normal form. ");
        }
    }

    protected static class DataRangeConverter implements OWLDataVisitorEx {
        protected final Configuration.WarningMonitor m_warningMonitor;
        protected final boolean m_ignoreUnsupportedDatatypes;
        protected final Set m_definedDatatypeIRIs; // contains custom datatypes from DatatypeDefinition axioms
        protected final Set m_allUnknownDatatypeRestrictions;

        public DataRangeConverter(Configuration.WarningMonitor warningMonitor,Set definedDatatypeIRIs,Set allUnknownDatatypeRestrictions,boolean ignoreUnsupportedDatatypes) {
            m_warningMonitor=warningMonitor;
            m_definedDatatypeIRIs=definedDatatypeIRIs;
            m_ignoreUnsupportedDatatypes=ignoreUnsupportedDatatypes;
            m_allUnknownDatatypeRestrictions=allUnknownDatatypeRestrictions;
        }
        public LiteralDataRange convertDataRange(OWLDataRange dataRange) {
            return (LiteralDataRange)dataRange.accept(this);
        }
        @Override
        public Object visit(OWLDatatype object) {
            String datatypeURI=object.getIRI().toString();
            if (InternalDatatype.RDFS_LITERAL.getIRI().equals(datatypeURI))
                return InternalDatatype.RDFS_LITERAL;
            if (datatypeURI.startsWith("internal:defdata#") || m_definedDatatypeIRIs.contains(object.getIRI().toString()))
                return InternalDatatype.create(datatypeURI);
            DatatypeRestriction datatype=DatatypeRestriction.create(datatypeURI,DatatypeRestriction.NO_FACET_URIs,DatatypeRestriction.NO_FACET_VALUES);
            if (datatypeURI.startsWith("internal:unknown-datatype#"))
                m_allUnknownDatatypeRestrictions.add(datatype);
            else {
                try {
                    DatatypeRegistry.validateDatatypeRestriction(datatype);
                }
                catch (UnsupportedDatatypeException e) {
                    if (m_ignoreUnsupportedDatatypes) {
                        if (m_warningMonitor!=null)
                            m_warningMonitor.warning("Ignoring unsupported datatype '"+object.getIRI().toString()+"'.");
                        m_allUnknownDatatypeRestrictions.add(datatype);
                    }
                    else
                        throw e;
                }
            }
            return datatype;
        }
        @Override
        public Object visit(OWLDataComplementOf object) {
            return convertDataRange(object.getDataRange()).getNegation();
        }
        @Override
        public Object visit(OWLDataOneOf object) {
            Set constants=new LinkedHashSet<>();
            object.values().forEach(l-> constants.add((Constant)l.accept(this)));
            Constant[] constantsArray=new Constant[constants.size()];
            constants.toArray(constantsArray);
            return ConstantEnumeration.create(constantsArray);
        }
        @Override
        public Object visit(OWLDatatypeRestriction object) {
            if (!(object.getDatatype().isOWLDatatype()))
                throw new IllegalArgumentException("Datatype restrictions are supported only on OWL datatypes.");
            String datatypeURI=object.getDatatype().getIRI().toString();
            if (InternalDatatype.RDFS_LITERAL.getIRI().equals(datatypeURI)) {
                if (object.facetRestrictions().count()>0)
                    throw new IllegalArgumentException("rdfs:Literal does not support any facets.");
                return InternalDatatype.RDFS_LITERAL;
            }
            List list=asList(object.facetRestrictions());
            String[] facetURIs=new String[list.size()];
            Constant[] facetValues=new Constant[list.size()];
            int index=0;
            for (OWLFacetRestriction facet : list) {
                facetURIs[index]=facet.getFacet().getIRI().toURI().toString();
                facetValues[index]=(Constant)facet.getFacetValue().accept(this);
                index++;
            }
            DatatypeRestriction datatype=DatatypeRestriction.create(datatypeURI,facetURIs,facetValues);
            DatatypeRegistry.validateDatatypeRestriction(datatype);
            return datatype;
        }
        @Override
        public Object visit(OWLFacetRestriction object) {
            throw new IllegalStateException("Internal error: should not get in here.");
        }
        @Override
        public Object visit(OWLLiteral object) {
            try {
                if (object.isRDFPlainLiteral()||object.getDatatype().getIRI().equals(OWL2Datatype.RDF_LANG_STRING.getIRI())) {
                    if (object.hasLang())
                        return Constant.create(object.getLiteral()+"@"+object.getLang(),Prefixes.s_semanticWebPrefixes.get("rdf:")+"PlainLiteral");
                    else
                        return Constant.create(object.getLiteral()+"@",Prefixes.s_semanticWebPrefixes.get("rdf:")+"PlainLiteral");
                }
                else
                    return Constant.create(object.getLiteral(),object.getDatatype().getIRI().toString());
            }
            catch (UnsupportedDatatypeException e) {
                if (m_ignoreUnsupportedDatatypes) {
                    if (m_warningMonitor!=null)
                        m_warningMonitor.warning("Ignoring unsupported datatype '"+object.toString()+"'.");
                    return Constant.createAnonymous(object.getLiteral());
                }
                else
                    throw e;
            }
        }
        @Override
        public Object visit(OWLDataIntersectionOf node) {
            throw new IllegalStateException(INVALID_NORMAL_FORM);
        }
        @Override
        public Object visit(OWLDataUnionOf node) {
            throw new IllegalStateException(INVALID_NORMAL_FORM);
        }
    }

    protected static class FactClausifier implements OWLAxiomVisitor {
        protected final DataRangeConverter m_dataRangeConverter;
        protected final Set m_positiveFacts;
        protected final Set m_negativeFacts;

        public FactClausifier(DataRangeConverter dataRangeConverter,Set positiveFacts,Set negativeFacts) {
            m_dataRangeConverter=dataRangeConverter;
            m_positiveFacts=positiveFacts;
            m_negativeFacts=negativeFacts;
        }
        @Override
        public void visit(OWLSameIndividualAxiom object) {
            List individuals= asList(object.individuals());
            for (int i=0;i individuals=asList(object.individuals());
            for (int i=0;i {
        protected final Set m_objectPropertiesOccurringInOWLAxioms;
        protected final DataRangeConverter m_dataRangeConverter;
        protected final Collection m_dlClauses;
        protected final List m_headAtoms;
        protected final List m_bodyAtoms;
        protected final Set m_abstractVariables;
        protected final Set m_graphObjectProperties=new HashSet<>();
        protected boolean m_containsObjectProperties;
        protected boolean m_containsGraphObjectProperties;
        protected boolean m_containsNonGraphObjectProperties;
        protected boolean m_containsUndeterminedObjectProperties;

        public NormalizedRuleClausifier(Set objectPropertiesOccurringInOWLAxioms,Collection descriptionGraphs,DataRangeConverter dataRangeConverter,Collection dlClauses) {
            m_objectPropertiesOccurringInOWLAxioms=objectPropertiesOccurringInOWLAxioms;
            m_dataRangeConverter=dataRangeConverter;
            m_dlClauses=dlClauses;
            m_headAtoms=new ArrayList<>();
            m_bodyAtoms=new ArrayList<>();
            m_abstractVariables=new HashSet<>();
            OWLDataFactory factory=OWLManager.getOWLDataFactory();
            for (DescriptionGraph descriptionGraph : descriptionGraphs)
                for (int i=0;i rules) {
            List unprocessedRules=new ArrayList<>(rules);
            boolean changed=true;
            while (!unprocessedRules.isEmpty() && changed) {
                changed=false;
                Iterator iterator=unprocessedRules.iterator();
                while (iterator.hasNext()) {
                    OWLAxioms.DisjunctiveRule rule=iterator.next();
                    determineRuleType(rule);
                    if (m_containsGraphObjectProperties && m_containsNonGraphObjectProperties)
                        throw new IllegalArgumentException("A SWRL rule mixes graph and non-graph object properties, which is not supported.");
                    determineUndeterminedObjectProperties(rule);
                    if (!m_containsUndeterminedObjectProperties) {
                        iterator.remove();
                        clausify(rule,m_containsNonGraphObjectProperties || !m_containsObjectProperties);
                        changed=true;
                    }
                }
            }
            m_containsObjectProperties=false;
            m_containsGraphObjectProperties=false;
            m_containsNonGraphObjectProperties=true;
            m_containsUndeterminedObjectProperties=false;
            for (OWLAxioms.DisjunctiveRule rule : unprocessedRules) {
                determineUndeterminedObjectProperties(rule);
                clausify(rule,true);
            }
        }
        protected void determineRuleType(OWLAxioms.DisjunctiveRule rule) {
            m_containsObjectProperties=false;
            m_containsGraphObjectProperties=false;
            m_containsNonGraphObjectProperties=false;
            m_containsUndeterminedObjectProperties=false;
            for (SWRLAtom atom : rule.m_body)
                checkRuleAtom(atom);
            for (SWRLAtom atom : rule.m_head)
                checkRuleAtom(atom);
        }
        protected void checkRuleAtom(SWRLAtom atom) {
            if (atom instanceof SWRLObjectPropertyAtom) {
                m_containsObjectProperties=true;
                OWLObjectProperty objectProperty=((SWRLObjectPropertyAtom)atom).getPredicate().getNamedProperty();
                boolean isGraphObjectProperty=m_graphObjectProperties.contains(objectProperty);
                boolean isNonGraphObjectProperty=m_objectPropertiesOccurringInOWLAxioms.contains(objectProperty);
                if (isGraphObjectProperty)
                    m_containsGraphObjectProperties=true;
                if (isNonGraphObjectProperty)
                    m_containsNonGraphObjectProperties=true;
                if (!isGraphObjectProperty && !isNonGraphObjectProperty)
                    m_containsUndeterminedObjectProperties=true;
            }
        }
        protected void determineUndeterminedObjectProperties(OWLAxioms.DisjunctiveRule rule) {
            if (m_containsUndeterminedObjectProperties) {
                if (m_containsGraphObjectProperties) {
                    for (SWRLAtom atom : rule.m_body)
                        makeGraphObjectProperty(atom);
                    for (SWRLAtom atom : rule.m_head)
                        makeGraphObjectProperty(atom);
                    m_containsUndeterminedObjectProperties=false;
                }
                else if (m_containsNonGraphObjectProperties) {
                    for (SWRLAtom atom : rule.m_body)
                        makeNonGraphObjectProperty(atom);
                    for (SWRLAtom atom : rule.m_head)
                        makeNonGraphObjectProperty(atom);
                    m_containsUndeterminedObjectProperties=false;
                }
            }
        }
        protected void makeGraphObjectProperty(SWRLAtom atom) {
            if (atom instanceof SWRLObjectPropertyAtom) {
                OWLObjectProperty objectProperty=((SWRLObjectPropertyAtom)atom).getPredicate().getNamedProperty();
                m_graphObjectProperties.add(objectProperty);
            }
        }
        protected void makeNonGraphObjectProperty(SWRLAtom atom) {
            if (atom instanceof SWRLObjectPropertyAtom) {
                OWLObjectProperty objectProperty=((SWRLObjectPropertyAtom)atom).getPredicate().getNamedProperty();
                m_objectPropertiesOccurringInOWLAxioms.add(objectProperty);
            }
        }
        protected void clausify(OWLAxioms.DisjunctiveRule rule,boolean restrictToNamed) {
            m_headAtoms.clear();
            m_bodyAtoms.clear();
            m_abstractVariables.clear();
            for (SWRLAtom atom : rule.m_body)
                m_bodyAtoms.add(atom.accept(this));
            for (SWRLAtom atom : rule.m_head)
                m_headAtoms.add(atom.accept(this));
            if (restrictToNamed) {
                for (Variable variable : m_abstractVariables)
                    m_bodyAtoms.add(Atom.create(AtomicConcept.INTERNAL_NAMED,variable));
            }
            DLClause dlClause=DLClause.create(m_headAtoms.toArray(new Atom[m_headAtoms.size()]),m_bodyAtoms.toArray(new Atom[m_bodyAtoms.size()]));
            m_dlClauses.add(dlClause);
            m_headAtoms.clear();
            m_bodyAtoms.clear();
            m_abstractVariables.clear();
        }
        @Override
        public Atom visit(SWRLClassAtom atom) {
            if (atom.getPredicate().isAnonymous())
                throw new IllegalStateException("Internal error: SWRL rule class atoms should be normalized to contain only named classes, but this class atom has a complex concept: "+atom.getPredicate());
            Variable variable=toVariable(atom.getArgument());
            m_abstractVariables.add(variable);
            return Atom.create(AtomicConcept.create(atom.getPredicate().asOWLClass().getIRI().toString()),variable);
        }
        @Override
        public Atom visit(SWRLDataRangeAtom atom) {
            Variable variable=toVariable(atom.getArgument());
            LiteralDataRange literalRange=m_dataRangeConverter.convertDataRange(atom.getPredicate());
            return Atom.create((DLPredicate)literalRange,variable);
        }
        @Override
        public Atom visit(SWRLObjectPropertyAtom atom) {
            Variable variable1=toVariable(atom.getFirstArgument());
            Variable variable2=toVariable(atom.getSecondArgument());
            m_abstractVariables.add(variable1);
            m_abstractVariables.add(variable2);
            return getRoleAtom(atom.getPredicate().asOWLObjectProperty(),variable1,variable2);
        }
        @Override
        public Atom visit(SWRLDataPropertyAtom atom) {
            Variable variable1=toVariable(atom.getFirstArgument());
            Variable variable2=toVariable(atom.getSecondArgument());
            m_abstractVariables.add(variable1);
            return getRoleAtom(atom.getPredicate().asOWLDataProperty(),variable1,variable2);
        }
        @Override
        public Atom visit(SWRLSameIndividualAtom atom) {
            Variable variable1=toVariable(atom.getFirstArgument());
            Variable variable2=toVariable(atom.getSecondArgument());
            return Atom.create(Equality.INSTANCE,variable1,variable2);
        }
        @Override
        public Atom visit(SWRLDifferentIndividualsAtom atom) {
            Variable variable1=toVariable(atom.getFirstArgument());
            Variable variable2=toVariable(atom.getSecondArgument());
            return Atom.create(Inequality.INSTANCE,variable1,variable2);
        }
        @Override
        public Atom visit(SWRLBuiltInAtom node) {
            throw new UnsupportedOperationException("Rules with SWRL built-in atoms are not yet supported. ");
        }
        @Override
        public Atom visit(SWRLRule rule) {
            throw new IllegalStateException("Internal error: this part of the code is unused.");
        }
        @Override
        public Atom visit(SWRLVariable node) {
            throw new IllegalStateException("Internal error: this part of the code is unused.");
        }
        @Override
        public Atom visit(SWRLIndividualArgument atom) {
            throw new IllegalStateException("Internal error: this part of the code is unused.");
        }
        @Override
        public Atom visit(SWRLLiteralArgument arg) {
            throw new IllegalStateException("Internal error: this part of the code is unused.");
        }
        protected static Variable toVariable(SWRLIArgument argument) {
            if (argument instanceof SWRLVariable)
                return Variable.create(((SWRLVariable)argument).getIRI().toString());
            else
                throw new IllegalStateException("Internal error: all arguments in a SWRL rule should have been normalized to variables.");
        }
        protected static Variable toVariable(SWRLDArgument argument) {
            if (argument instanceof SWRLVariable)
                return Variable.create(((SWRLVariable)argument).getIRI().toString());
            else
                throw new IllegalStateException("Internal error: all arguments in a SWRL rule should have been normalized to variables.");
        }
    }
}