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

org.semanticweb.owlapi.reasoner.structural.StructuralReasoner Maven / Gradle / Ivy

There is a newer version: 5.5.1
Show newest version
/* This file is part of the OWL API.
 * The contents of this file are subject to the LGPL License, Version 3.0.
 * Copyright 2014, The University of Manchester
 * 
 * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * This program 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 General Public License for more details.
 * You should have received a copy of the GNU General Public License along with this program.  If not, see http://www.gnu.org/licenses/.
 *
 * Alternatively, the contents of this file may be used under the terms of the Apache License, Version 2.0 in which case, the provisions of the Apache License Version 2.0 are applicable instead of those above.
 * Licensed 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.semanticweb.owlapi.reasoner.structural;

import static org.semanticweb.owlapi.model.parameters.Imports.INCLUDED;
import static org.semanticweb.owlapi.search.Searcher.sub;
import static org.semanticweb.owlapi.search.Searcher.sup;
import static org.semanticweb.owlapi.util.OWLAPIPreconditions.checkNotNull;
import static org.semanticweb.owlapi.util.OWLAPIPreconditions.verifyNotNull;
import static org.semanticweb.owlapi.util.OWLAPIStreamUtils.asList;
import static org.semanticweb.owlapi.util.OWLAPIStreamUtils.asUnorderedSet;

import java.util.Collection;
import java.util.Collections;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
import java.util.stream.Stream;

import javax.annotation.Nullable;

import org.semanticweb.owlapi.model.AxiomType;
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.OWLDataProperty;
import org.semanticweb.owlapi.model.OWLDataPropertyExpression;
import org.semanticweb.owlapi.model.OWLDifferentIndividualsAxiom;
import org.semanticweb.owlapi.model.OWLEquivalentClassesAxiom;
import org.semanticweb.owlapi.model.OWLLiteral;
import org.semanticweb.owlapi.model.OWLNamedIndividual;
import org.semanticweb.owlapi.model.OWLObject;
import org.semanticweb.owlapi.model.OWLObjectIntersectionOf;
import org.semanticweb.owlapi.model.OWLObjectPropertyAxiom;
import org.semanticweb.owlapi.model.OWLObjectPropertyDomainAxiom;
import org.semanticweb.owlapi.model.OWLObjectPropertyExpression;
import org.semanticweb.owlapi.model.OWLObjectPropertyRangeAxiom;
import org.semanticweb.owlapi.model.OWLOntology;
import org.semanticweb.owlapi.model.OWLSameIndividualAxiom;
import org.semanticweb.owlapi.model.OWLSubClassOfAxiom;
import org.semanticweb.owlapi.model.parameters.AxiomAnnotations;
import org.semanticweb.owlapi.model.parameters.Imports;
import org.semanticweb.owlapi.reasoner.BufferingMode;
import org.semanticweb.owlapi.reasoner.FreshEntityPolicy;
import org.semanticweb.owlapi.reasoner.IndividualNodeSetPolicy;
import org.semanticweb.owlapi.reasoner.InferenceType;
import org.semanticweb.owlapi.reasoner.Node;
import org.semanticweb.owlapi.reasoner.NodeSet;
import org.semanticweb.owlapi.reasoner.OWLReasonerConfiguration;
import org.semanticweb.owlapi.reasoner.ReasonerInterruptedException;
import org.semanticweb.owlapi.reasoner.ReasonerProgressMonitor;
import org.semanticweb.owlapi.reasoner.TimeOutException;
import org.semanticweb.owlapi.reasoner.impl.DefaultNode;
import org.semanticweb.owlapi.reasoner.impl.DefaultNodeSet;
import org.semanticweb.owlapi.reasoner.impl.OWLClassNode;
import org.semanticweb.owlapi.reasoner.impl.OWLClassNodeSet;
import org.semanticweb.owlapi.reasoner.impl.OWLDataPropertyNode;
import org.semanticweb.owlapi.reasoner.impl.OWLDataPropertyNodeSet;
import org.semanticweb.owlapi.reasoner.impl.OWLNamedIndividualNode;
import org.semanticweb.owlapi.reasoner.impl.OWLNamedIndividualNodeSet;
import org.semanticweb.owlapi.reasoner.impl.OWLObjectPropertyNode;
import org.semanticweb.owlapi.reasoner.impl.OWLObjectPropertyNodeSet;
import org.semanticweb.owlapi.reasoner.impl.OWLReasonerBase;
import org.semanticweb.owlapi.search.Filters;
import org.semanticweb.owlapi.util.CollectionFactory;
import org.semanticweb.owlapi.util.OWLObjectPropertyManager;
import org.semanticweb.owlapi.util.Version;

/**
 * This is a simple structural reasoner that essentially answers with told information. It is
 * incomplete.
 *
 * @author Matthew Horridge, The University of Manchester, Information Management Group
 * @since 3.0.0
 */
public class StructuralReasoner extends OWLReasonerBase {

    private static final Version VERSION = new Version(1, 0, 0, 0);
    protected final ReasonerProgressMonitor pm;
    private final ClassHierarchyInfo classHierarchyInfo = new ClassHierarchyInfo();
    private final ObjectPropertyHierarchyInfo objectPropertyHierarchyInfo =
        new ObjectPropertyHierarchyInfo();
    private final DataPropertyHierarchyInfo dataPropertyHierarchyInfo =
        new DataPropertyHierarchyInfo();
    private boolean interrupted = false;
    private boolean prepared = false;

    /**
     * @param rootOntology the ontology
     * @param configuration the reasoner configuration
     * @param bufferingMode the buffering mode
     */
    public StructuralReasoner(OWLOntology rootOntology, OWLReasonerConfiguration configuration,
        BufferingMode bufferingMode) {
        super(rootOntology, configuration, bufferingMode);
        checkNotNull(configuration, "configuration cannot be null");
        pm = configuration.getProgressMonitor();
        prepareReasoner();
    }

    private static  void handleChanges(Set added,
        Set removed, AbstractHierarchyInfo hierarchyInfo) {
        Set sig = hierarchyInfo.getEntitiesInSignature(added);
        sig.addAll(hierarchyInfo.getEntitiesInSignature(removed));
        hierarchyInfo.processChanges(sig, added, removed);
    }

    private static void printIndent(int level) {
        for (int i = 0; i < level; i++) {
            System.out.print("    ");
        }
    }

    @Override
    public String getReasonerName() {
        return "Structural Reasoner";
    }

    @Override
    public FreshEntityPolicy getFreshEntityPolicy() {
        return FreshEntityPolicy.ALLOW;
    }

    @Override
    public IndividualNodeSetPolicy getIndividualNodeSetPolicy() {
        return IndividualNodeSetPolicy.BY_NAME;
    }

    @Override
    public Version getReasonerVersion() {
        return VERSION;
    }

    @Override
    protected void handleChanges(Set addAxioms, Set removeAxioms) {
        handleChanges(addAxioms, removeAxioms, classHierarchyInfo);
        handleChanges(addAxioms, removeAxioms, objectPropertyHierarchyInfo);
        handleChanges(addAxioms, removeAxioms, dataPropertyHierarchyInfo);
    }

    @Override
    public void interrupt() {
        interrupted = true;
    }

    private void ensurePrepared() {
        if (!prepared) {
            prepareReasoner();
        }
    }

    /**
     * @throws ReasonerInterruptedException on interruption
     * @throws TimeOutException on timeout
     */
    public final void prepareReasoner() {
        classHierarchyInfo.computeHierarchy();
        objectPropertyHierarchyInfo.computeHierarchy();
        dataPropertyHierarchyInfo.computeHierarchy();
        prepared = true;
    }

    @Override
    public void precomputeInferences(InferenceType... inferenceTypes) {
        prepareReasoner();
    }

    @Override
    public boolean isPrecomputed(InferenceType inferenceType) {
        return true;
    }

    @Override
    public Set getPrecomputableInferenceTypes() {
        return CollectionFactory.createSet(InferenceType.CLASS_HIERARCHY,
            InferenceType.OBJECT_PROPERTY_HIERARCHY, InferenceType.DATA_PROPERTY_HIERARCHY);
    }

    protected void throwExceptionIfInterrupted() {
        if (interrupted) {
            interrupted = false;
            throw new ReasonerInterruptedException();
        }
    }

    @Override
    public boolean isConsistent() {
        return true;
    }

    @Override
    public boolean isSatisfiable(OWLClassExpression classExpression) {
        return !classExpression.isAnonymous() && !getEquivalentClasses(classExpression.asOWLClass())
            .contains(getDataFactory().getOWLNothing());
    }

    @Override
    public Node getUnsatisfiableClasses() {
        return OWLClassNode.getBottomNode();
    }

    @Override
    public boolean isEntailed(OWLAxiom axiom) {
        return getRootOntology().containsAxiom(axiom, INCLUDED,
            AxiomAnnotations.IGNORE_AXIOM_ANNOTATIONS);
    }

    @Override
    public boolean isEntailed(Set axioms) {
        for (OWLAxiom ax : axioms) {
            if (!getRootOntology().containsAxiom(ax, INCLUDED,
                AxiomAnnotations.IGNORE_AXIOM_ANNOTATIONS)) {
                return false;
            }
        }
        return true;
    }

    @Override
    public boolean isEntailmentCheckingSupported(AxiomType axiomType) {
        return false;
    }

    @Override
    public Node getTopClassNode() {
        ensurePrepared();
        return classHierarchyInfo.getEquivalents(getDataFactory().getOWLThing());
    }

    @Override
    public Node getBottomClassNode() {
        ensurePrepared();
        return classHierarchyInfo.getEquivalents(getDataFactory().getOWLNothing());
    }

    @Override
    public NodeSet getSubClasses(OWLClassExpression ce, boolean direct) {
        OWLClassNodeSet ns = new OWLClassNodeSet();
        if (!ce.isAnonymous()) {
            ensurePrepared();
            return classHierarchyInfo.getNodeHierarchyChildren(ce.asOWLClass(), direct, ns);
        }
        return ns;
    }

    @Override
    public NodeSet getSuperClasses(OWLClassExpression ce, boolean direct) {
        OWLClassNodeSet ns = new OWLClassNodeSet();
        if (!ce.isAnonymous()) {
            ensurePrepared();
            return classHierarchyInfo.getNodeHierarchyParents(ce.asOWLClass(), direct, ns);
        }
        return ns;
    }

    @Override
    public Node getEquivalentClasses(OWLClassExpression ce) {
        ensurePrepared();
        if (!ce.isAnonymous()) {
            return classHierarchyInfo.getEquivalents(ce.asOWLClass());
        } else {
            return new OWLClassNode();
        }
    }

    @Override
    public NodeSet getDisjointClasses(OWLClassExpression ce) {
        ensurePrepared();
        OWLClassNodeSet nodeSet = new OWLClassNodeSet();
        if (ce.isAnonymous()) {
            return nodeSet;
        }
        getRootOntology().importsClosure().flatMap(o -> o.disjointClassesAxioms(ce.asOWLClass()))
            .flatMap(ax -> ax.classExpressions()).filter(op -> !op.isAnonymous())
            .forEach(op -> nodeSet.addNode(getEquivalentClasses(op)));
        return nodeSet;
    }

    @Override
    public Node getTopObjectPropertyNode() {
        ensurePrepared();
        return objectPropertyHierarchyInfo
            .getEquivalents(getDataFactory().getOWLTopObjectProperty());
    }

    @Override
    public Node getBottomObjectPropertyNode() {
        ensurePrepared();
        return objectPropertyHierarchyInfo
            .getEquivalents(getDataFactory().getOWLBottomObjectProperty());
    }

    @Override
    public NodeSet getSubObjectProperties(
        OWLObjectPropertyExpression pe, boolean direct) {
        OWLObjectPropertyNodeSet ns = new OWLObjectPropertyNodeSet();
        ensurePrepared();
        return objectPropertyHierarchyInfo.getNodeHierarchyChildren(pe, direct, ns);
    }

    @Override
    public NodeSet getSuperObjectProperties(
        OWLObjectPropertyExpression pe, boolean direct) {
        OWLObjectPropertyNodeSet ns = new OWLObjectPropertyNodeSet();
        ensurePrepared();
        return objectPropertyHierarchyInfo.getNodeHierarchyParents(pe, direct, ns);
    }

    @Override
    public Node getEquivalentObjectProperties(
        OWLObjectPropertyExpression pe) {
        ensurePrepared();
        return objectPropertyHierarchyInfo.getEquivalents(pe);
    }

    @Override
    public NodeSet getDisjointObjectProperties(
        OWLObjectPropertyExpression pe) {
        return new OWLObjectPropertyNodeSet();
    }

    @Override
    public Node getInverseObjectProperties(
        OWLObjectPropertyExpression pe) {
        ensurePrepared();
        OWLObjectPropertyExpression inv = pe.getInverseProperty();
        return getEquivalentObjectProperties(inv);
    }

    @Override
    public NodeSet getObjectPropertyDomains(OWLObjectPropertyExpression pe,
        boolean direct) {
        ensurePrepared();
        DefaultNodeSet result = new OWLClassNodeSet();
        Consumer domains = axiom -> {
            result.addNode(getEquivalentClasses(axiom.getDomain()));
            if (!direct) {
                result.addAllNodes(getSuperClasses(axiom.getDomain(), false).nodes());
            }
        };
        Consumer inverseRanges = ax -> {
            result.addNode(getEquivalentClasses(ax.getRange()));
            if (!direct) {
                result.addAllNodes(getSuperClasses(ax.getRange(), false).nodes());
            }
        };
        getRootOntology().importsClosure().flatMap(o -> o.objectPropertyDomainAxioms(pe))
            .forEach(domains);
        getRootOntology().importsClosure().forEach(o -> getInverseObjectProperties(pe).entities()
            .forEach(invPe -> o.objectPropertyRangeAxioms(invPe).forEach(inverseRanges)));
        return result;
    }

    @Override
    public NodeSet getObjectPropertyRanges(OWLObjectPropertyExpression pe,
        boolean direct) {
        ensurePrepared();
        DefaultNodeSet result = new OWLClassNodeSet();
        getRootOntology().importsClosure().forEach(ontology -> {
            ontology.objectPropertyRangeAxioms(pe).forEach(axiom -> {
                result.addNode(getEquivalentClasses(axiom.getRange()));
                if (!direct) {
                    result.addAllNodes(getSuperClasses(axiom.getRange(), false).nodes());
                }
            });
            getInverseObjectProperties(pe).entities().flatMap(ontology::objectPropertyDomainAxioms)
                .forEach(axiom -> {
                    result.addNode(getEquivalentClasses(axiom.getDomain()));
                    if (!direct) {
                        result.addAllNodes(getSuperClasses(axiom.getDomain(), false).nodes());
                    }
                });
        });
        return result;
    }

    @Override
    public Node getTopDataPropertyNode() {
        ensurePrepared();
        return dataPropertyHierarchyInfo.getEquivalents(getDataFactory().getOWLTopDataProperty());
    }

    @Override
    public Node getBottomDataPropertyNode() {
        ensurePrepared();
        return dataPropertyHierarchyInfo
            .getEquivalents(getDataFactory().getOWLBottomDataProperty());
    }

    @Override
    public NodeSet getSubDataProperties(OWLDataProperty pe, boolean direct) {
        ensurePrepared();
        OWLDataPropertyNodeSet ns = new OWLDataPropertyNodeSet();
        return dataPropertyHierarchyInfo.getNodeHierarchyChildren(pe, direct, ns);
    }

    @Override
    public NodeSet getSuperDataProperties(OWLDataProperty pe, boolean direct) {
        ensurePrepared();
        OWLDataPropertyNodeSet ns = new OWLDataPropertyNodeSet();
        return dataPropertyHierarchyInfo.getNodeHierarchyParents(pe, direct, ns);
    }

    @Override
    public Node getEquivalentDataProperties(OWLDataProperty pe) {
        ensurePrepared();
        return dataPropertyHierarchyInfo.getEquivalents(pe);
    }

    @Override
    public NodeSet getDisjointDataProperties(OWLDataPropertyExpression pe) {
        ensurePrepared();
        DefaultNodeSet result = new OWLDataPropertyNodeSet();
        getRootOntology().importsClosure()
            .flatMap(o -> o.disjointDataPropertiesAxioms(pe.asOWLDataProperty())).forEach(axiom -> {
                for (OWLDataPropertyExpression dpe : axiom.getPropertiesMinus(pe)) {
                    if (!dpe.isAnonymous()) {
                        result.addNode(
                            dataPropertyHierarchyInfo.getEquivalents(dpe.asOWLDataProperty()));
                        result.addAllNodes(
                            getSubDataProperties(dpe.asOWLDataProperty(), false).nodes());
                    }
                }
            });
        return result;
    }

    @Override
    public NodeSet getDataPropertyDomains(OWLDataProperty pe, boolean direct) {
        ensurePrepared();
        DefaultNodeSet result = new OWLClassNodeSet();
        getRootOntology().importsClosure().flatMap(o -> o.dataPropertyDomainAxioms(pe))
            .forEach(ax -> addClasses(direct, result, ax.getDomain()));
        return result;
    }

    protected void addClasses(boolean direct, DefaultNodeSet result,
        OWLClassExpression domain) {
        result.addNode(getEquivalentClasses(domain));
        if (!direct) {
            result.addAllNodes(getSuperClasses(domain, false).nodes());
        }
    }

    @Override
    public NodeSet getTypes(OWLNamedIndividual ind, boolean direct) {
        ensurePrepared();
        DefaultNodeSet result = new OWLClassNodeSet();
        getRootOntology().importsClosure().flatMap(o -> o.classAssertionAxioms(ind))
            .forEach(ax -> addClasses(direct, result, ax.getClassExpression()));
        return result;
    }

    @Override
    public NodeSet getInstances(OWLClassExpression ce, boolean direct) {
        ensurePrepared();
        DefaultNodeSet result = new OWLNamedIndividualNodeSet();
        if (ce.isAnonymous()) {
            return result;
        }
        OWLClass cls = ce.asOWLClass();
        Set clses = new HashSet<>();
        clses.add(cls);
        if (!direct) {
            clses.addAll(asList(getSubClasses(cls, false).entities()));
        }
        for (OWLClass curCls : clses) {
            getRootOntology().importsClosure().flatMap(o -> o.classAssertionAxioms(curCls))
                .map(ax -> ax.getIndividual()).filter(i -> !i.isAnonymous())
                .map(i -> i.asOWLNamedIndividual()).forEach(i -> {
                    if (getIndividualNodeSetPolicy().equals(IndividualNodeSetPolicy.BY_SAME_AS)) {
                        result.addNode(getSameIndividuals(i));
                    } else {
                        result.addNode(new OWLNamedIndividualNode(i));
                    }
                });
        }
        return result;
    }

    @Override
    public NodeSet getObjectPropertyValues(OWLNamedIndividual ind,
        OWLObjectPropertyExpression pe) {
        ensurePrepared();
        OWLNamedIndividualNodeSet result = new OWLNamedIndividualNodeSet();
        Node inverses = getInverseObjectProperties(pe);
        getRootOntology().importsClosure().flatMap(o -> o.objectPropertyAssertionAxioms(ind))
            .forEach(axiom -> {
                if (!axiom.getObject().isAnonymous() && axiom.getProperty().equals(pe)) {
                    if (getIndividualNodeSetPolicy().equals(IndividualNodeSetPolicy.BY_SAME_AS)) {
                        result
                            .addNode(getSameIndividuals(axiom.getObject().asOWLNamedIndividual()));
                    } else {
                        result.addNode(
                            new OWLNamedIndividualNode(axiom.getObject().asOWLNamedIndividual()));
                    }
                }
                // Inverse of pe
                if (axiom.getObject().equals(ind) && !axiom.getSubject().isAnonymous()) {
                    OWLObjectPropertyExpression invPe = axiom.getProperty().getInverseProperty();
                    if (!invPe.isAnonymous() && inverses.contains(invPe.asOWLObjectProperty())) {
                        if (getIndividualNodeSetPolicy()
                            .equals(IndividualNodeSetPolicy.BY_SAME_AS)) {
                            result.addNode(
                                getSameIndividuals(axiom.getObject().asOWLNamedIndividual()));
                        } else {
                            result.addNode(new OWLNamedIndividualNode(
                                axiom.getObject().asOWLNamedIndividual()));
                        }
                    }
                }
            });
        // Could do other stuff like inspecting owl:hasValue restrictions
        return result;
    }

    @Override
    public Set getDataPropertyValues(OWLNamedIndividual ind, OWLDataProperty pe) {
        ensurePrepared();
        Set literals = new HashSet<>();
        Set superProperties =
            asUnorderedSet(Stream.concat(getSuperDataProperties(pe, false).entities(),
                getEquivalentDataProperties(pe).entities()));
        getRootOntology().importsClosure().flatMap(o -> o.dataPropertyAssertionAxioms(ind))
            .forEach(ax -> {
                if (superProperties.contains(ax.getProperty().asOWLDataProperty())) {
                    literals.add(ax.getObject());
                }
            });
        return literals;
    }

    @Override
    public Node getSameIndividuals(OWLNamedIndividual ind) {
        ensurePrepared();
        Set inds = new HashSet<>();
        Set processed = new HashSet<>();
        List stack = new LinkedList<>();
        stack.add(ind);
        while (!stack.isEmpty()) {
            OWLNamedIndividual currentInd = stack.remove(0);
            Stream axioms = getRootOntology().importsClosure()
                .flatMap(o -> o.sameIndividualAxioms(currentInd)).filter(processed::add);
            axioms.forEach(ax -> ax.individuals().filter(i -> i.isNamed())
                .filter(i -> inds.add(i.asOWLNamedIndividual()))
                .forEach(i -> stack.add(i.asOWLNamedIndividual())));
        }
        if (inds.isEmpty()) {
            inds.add(ind);
        }
        return new OWLNamedIndividualNode(inds);
    }

    @Override
    public NodeSet getDifferentIndividuals(OWLNamedIndividual ind) {
        ensurePrepared();
        Set inds = new HashSet<>();
        Set processed = new HashSet<>();
        List stack = new LinkedList<>();
        stack.add(ind);
        while (!stack.isEmpty()) {
            OWLNamedIndividual currentInd = stack.remove(0);
            Stream axioms = Imports.INCLUDED.stream(getRootOntology())
                .flatMap(o -> o.differentIndividualAxioms(currentInd)).filter(processed::add);
            axioms.forEach(ax -> ax.individuals().filter(i -> i.isOWLNamedIndividual())
                .map(i -> i.asOWLNamedIndividual()).forEach(i -> {
                    if (inds.add(i)) {
                        stack.add(i);
                    }
                }));
        }
        if (inds.isEmpty()) {
            inds.add(ind);
        }
        return new OWLNamedIndividualNodeSet(
            asUnorderedSet(inds.stream().map(this::getSameIndividuals)));
    }

    protected OWLDataFactory getDataFactory() {
        return getRootOntology().getOWLOntologyManager().getOWLDataFactory();
    }

    /**
     * @param showBottomNode true if bottom node is to be showed
     */
    public void dumpClassHierarchy(boolean showBottomNode) {
        dumpClassHierarchy(OWLClassNode.getTopNode(), 0, showBottomNode);
    }

    private void dumpClassHierarchy(Node cls, int level, boolean showBottomNode) {
        if (!showBottomNode && cls.isBottomNode()) {
            return;
        }
        printIndent(level);
        OWLClass representative = cls.getRepresentativeElement();
        System.out.println(getEquivalentClasses(representative));
        for (Node subCls : getSubClasses(representative, true)) {
            dumpClassHierarchy(subCls, level + 1, showBottomNode);
        }
    }

    /**
     * @param showBottomNode true if bottom node is to be showed
     */
    public void dumpObjectPropertyHierarchy(boolean showBottomNode) {
        dumpObjectPropertyHierarchy(OWLObjectPropertyNode.getTopNode(), 0, showBottomNode);
    }

    private void dumpObjectPropertyHierarchy(Node cls, int level,
        boolean showBottomNode) {
        if (!showBottomNode && cls.isBottomNode()) {
            return;
        }
        printIndent(level);
        OWLObjectPropertyExpression representative = cls.getRepresentativeElement();
        System.out.println(getEquivalentObjectProperties(representative));
        for (Node subProp : getSubObjectProperties(representative,
            true)) {
            dumpObjectPropertyHierarchy(subProp, level + 1, showBottomNode);
        }
    }

    /**
     * @param showBottomNode true if bottom node is to be showed
     */
    public void dumpDataPropertyHierarchy(boolean showBottomNode) {
        dumpDataPropertyHierarchy(OWLDataPropertyNode.getTopNode(), 0, showBottomNode);
    }

    private void dumpDataPropertyHierarchy(Node cls, int level,
        boolean showBottomNode) {
        if (!showBottomNode && cls.isBottomNode()) {
            return;
        }
        printIndent(level);
        OWLDataProperty representative = cls.getRepresentativeElement();
        System.out.println(getEquivalentDataProperties(representative));
        for (Node subProp : getSubDataProperties(representative, true)) {
            dumpDataPropertyHierarchy(subProp, level + 1, showBottomNode);
        }
    }

    /**
     * An interface for objects who can provide the parents and children of some object.
     *
     * @param  type of elements
     */
    private interface RawHierarchyProvider {

        /**
         * Gets the parents as asserted. These parents may also be children (resulting in
         * equivalences).
         *
         * @param child The child whose parents are to be retrieved
         * @return The raw asserted parents of the specified child. If the child does not have any
         *         parents then the empty set can be returned.
         */
        Collection getParents(T child);

        /**
         * Gets the children as asserted.
         *
         * @param parent The parent whose children are to be retrieved
         * @return The raw asserted children of the speicified parent
         */
        Collection getChildren(T parent);
    }

    private static class NodeCache {

        private final AbstractHierarchyInfo hierarchyInfo;
        private final Map> map = new HashMap<>();
        @Nullable
        private Node topNode;
        @Nullable
        private Node bottomNode;

        protected NodeCache(AbstractHierarchyInfo hierarchyInfo) {
            this.hierarchyInfo = hierarchyInfo;
            clearTopNode();
            clearBottomNode();
        }

        public void addNode(Node node) {
            node.entities().forEach(e -> {
                map.put(e, node);
                if (e.isTopEntity()) {
                    topNode = node;
                } else if (e.isBottomEntity()) {
                    bottomNode = node;
                }
            });
        }

        public Set> getNodes(Set elements) {
            Set> result = new HashSet<>();
            for (T element : elements) {
                result.add(getNode(element));
            }
            return result;
        }

        public Node getNode(T containing) {
            Node parentNode = map.get(containing);
            if (parentNode != null) {
                return parentNode;
            } else {
                return hierarchyInfo.createNode(CollectionFactory.createSet(containing));
            }
        }

        public void addNode(Set elements) {
            addNode(hierarchyInfo.createNode(elements));
        }

        public Node getTopNode() {
            return verifyNotNull(topNode);
        }

        public Node getBottomNode() {
            return verifyNotNull(bottomNode);
        }

        public final void clearTopNode() {
            removeNode(hierarchyInfo.topEntity);
            topNode =
                hierarchyInfo.createNode(CollectionFactory.createSet(hierarchyInfo.topEntity));
            addNode(getTopNode());
        }

        public final void clearBottomNode() {
            removeNode(hierarchyInfo.bottomEntity);
            bottomNode =
                hierarchyInfo.createNode(CollectionFactory.createSet(hierarchyInfo.bottomEntity));
            addNode(getBottomNode());
        }

        public void clearNodes(Set containing) {
            for (T entity : containing) {
                removeNode(entity);
            }
        }

        public void clear() {
            map.clear();
            clearTopNode();
            clearBottomNode();
        }

        public void removeNode(T containing) {
            Node node = map.remove(containing);
            if (node != null) {
                node.entities().forEach(map::remove);
            }
        }
    }

    // HierarchyInfo
    private abstract class AbstractHierarchyInfo {

        private final RawHierarchyProvider rawParentChildProvider;
        private final Set directChildrenOfTopNode = new HashSet<>();
        private final Set directParentsOfBottomNode = new HashSet<>();
        private final NodeCache nodeCache;
        private final String name;
        /**
         * The entity that always appears in the top node in the hierarchy.
         */
        protected T topEntity;
        /**
         * The entity that always appears as the bottom node in the hierarchy.
         */
        protected T bottomEntity;
        private int classificationSize;

        AbstractHierarchyInfo(String name, T topEntity, T bottomEntity,
            RawHierarchyProvider rawParentChildProvider) {
            this.topEntity = topEntity;
            this.bottomEntity = bottomEntity;
            nodeCache = new NodeCache<>(this);
            this.rawParentChildProvider = rawParentChildProvider;
            this.name = name;
        }

        public RawHierarchyProvider getRawParentChildProvider() {
            return rawParentChildProvider;
        }

        /**
         * Gets the set of relevant entities from the specified ontology.
         *
         * @param ont The ontology
         * @return A set of entities to be "classified"
         */
        protected abstract Stream getEntities(OWLOntology ont);

        /**
         * Creates a node for a given set of entities.
         *
         * @param cycle The set of entities
         * @return A node
         */
        protected abstract DefaultNode createNode(Set cycle);

        protected abstract DefaultNode createNode();

        /**
         * Gets the set of relevant entities in a particular axiom.
         *
         * @param ax The axiom
         * @return The set of relevant entities in the signature of the specified axiom
         */
        protected abstract Stream getEntitiesInSignature(OWLAxiom ax);

        Set getEntitiesInSignature(Set axioms) {
            return asUnorderedSet(axioms.stream().flatMap(this::getEntitiesInSignature));
        }

        public void computeHierarchy() {
            pm.reasonerTaskStarted("Computing " + name + " hierarchy");
            pm.reasonerTaskBusy();
            nodeCache.clear();
            Map> cache = new HashMap<>();
            Set entities =
                asUnorderedSet(getRootOntology().importsClosure().flatMap(this::getEntities));
            classificationSize = entities.size();
            pm.reasonerTaskProgressChanged(0, classificationSize);
            updateForSignature(entities, cache);
            pm.reasonerTaskStopped();
        }

        private void updateForSignature(Set signature, @Nullable Map> cache) {
            HashSet> cyclesResult = new HashSet<>();
            Set processed = new HashSet<>();
            nodeCache.clearTopNode();
            nodeCache.clearBottomNode();
            nodeCache.clearNodes(signature);
            directChildrenOfTopNode.removeAll(signature);
            Set equivTopOrChildrenOfTop = new HashSet<>();
            Set equivBottomOrParentsOfBottom = new HashSet<>();
            for (T entity : signature) {
                if (!processed.contains(entity)) {
                    pm.reasonerTaskProgressChanged(processed.size(), signature.size());
                    tarjan(entity, 0, new LinkedList(), new HashMap(),
                        new HashMap(), cyclesResult, processed, new HashSet(), cache,
                        equivTopOrChildrenOfTop, equivBottomOrParentsOfBottom);
                    throwExceptionIfInterrupted();
                }
            }
            // Store new cycles
            for (Set cycle : cyclesResult) {
                nodeCache.addNode(cycle);
            }
            directChildrenOfTopNode.addAll(equivTopOrChildrenOfTop);
            nodeCache.getTopNode().entities().forEach(directChildrenOfTopNode::remove);
            directParentsOfBottomNode.addAll(equivBottomOrParentsOfBottom);
            nodeCache.getBottomNode().entities().forEach(directParentsOfBottomNode::remove);
            // Now check that each found cycle has a proper parent an child
            for (Set node : cyclesResult) {
                if (!node.contains(topEntity) && !node.contains(bottomEntity)) {
                    boolean childOfTop = true;
                    for (T element : node) {
                        Collection parents = rawParentChildProvider.getParents(element);
                        parents.removeAll(node);
                        nodeCache.getTopNode().entities().forEach(parents::remove);
                        if (!parents.isEmpty()) {
                            childOfTop = false;
                            break;
                        }
                    }
                    if (childOfTop) {
                        directChildrenOfTopNode.addAll(node);
                    }
                    boolean parentOfBottom = true;
                    for (T element : node) {
                        Collection children = rawParentChildProvider.getChildren(element);
                        children.removeAll(node);
                        nodeCache.getBottomNode().entities().forEach(children::remove);
                        if (!children.isEmpty()) {
                            parentOfBottom = false;
                            break;
                        }
                    }
                    if (parentOfBottom) {
                        directParentsOfBottomNode.addAll(node);
                    }
                }
            }
        }

        /**
         * Processes the specified signature that represents the signature of potential changes.
         *
         * @param signature The signature
         * @param added added axioms
         * @param removed removed axioms
         */
        @SuppressWarnings("unused")
        public void processChanges(Set signature, Set added, Set removed) {
            updateForSignature(signature, null);
        }

        /**
         * Applies the tarjan algorithm for a given entity. This computes the cycle that the entity
         * is involved in (if any).
         *
         * @param entity The entity
         * @param inputIndex index
         * @param stack stack
         * @param indexMap index map
         * @param lowlinkMap low link map
         * @param result result
         * @param processed processed
         * @param stackEntities stack entities
         * @param cache A cache of children to parents - may be {@code null} if no caching is to
         *        take place.
         * @param childrenOfTop A set of entities that have a raw parent that is the top entity
         * @param parentsOfBottom A set of entities that have a raw parent that is the bottom entity
         */
        public void tarjan(T entity, int inputIndex, Deque stack, Map indexMap,
            Map lowlinkMap, Set> result, Set processed, Set stackEntities,
            @Nullable Map> cache, Set childrenOfTop, Set parentsOfBottom) {
            int index = inputIndex;
            throwExceptionIfInterrupted();
            if (processed.add(entity)) {
                Collection rawChildren = rawParentChildProvider.getChildren(entity);
                if (rawChildren.isEmpty() || rawChildren.contains(bottomEntity)) {
                    parentsOfBottom.add(entity);
                }
            }
            pm.reasonerTaskProgressChanged(processed.size(), classificationSize);
            indexMap.put(entity, Integer.valueOf(index));
            lowlinkMap.put(entity, Integer.valueOf(index));
            index += 1;
            stack.push(entity);
            stackEntities.add(entity);
            // Get the raw parents - cache if necessary
            Collection rawParents;
            if (cache != null) {
                // We are therefore caching raw parents of children.
                rawParents = cache.computeIfAbsent(entity, e -> computeParents(e, childrenOfTop));
            } else {
                rawParents = computeParents(entity, childrenOfTop);
            }
            for (T superEntity : rawParents) {
                if (!indexMap.containsKey(superEntity)) {
                    tarjan(superEntity, index, stack, indexMap, lowlinkMap, result, processed,
                        stackEntities, cache, childrenOfTop, parentsOfBottom);
                    lowlinkMap.put(entity,
                        Integer.valueOf(Math.min(lowlinkMap.get(entity).intValue(),
                            lowlinkMap.get(superEntity).intValue())));
                } else if (stackEntities.contains(superEntity)) {
                    lowlinkMap.put(entity,
                        Integer.valueOf(Math.min(lowlinkMap.get(entity).intValue(),
                            indexMap.get(superEntity).intValue())));
                }
            }
            if (lowlinkMap.get(entity).equals(indexMap.get(entity))) {
                Set scc = new HashSet<>();
                while (true) {
                    T clsPrime = stack.pop();
                    stackEntities.remove(clsPrime);
                    scc.add(clsPrime);
                    if (clsPrime.equals(entity)) {
                        break;
                    }
                }
                if (scc.size() > 1) {
                    // We ADD a cycle
                    result.add(scc);
                }
            }
        }

        protected Collection computeParents(T entity, Set childrenOfTop) {
            Collection rawParents;
            rawParents = rawParentChildProvider.getParents(entity);
            // Note down if our entity is a
            if (rawParents.isEmpty() || rawParents.contains(topEntity)) {
                childrenOfTop.add(entity);
            }
            return rawParents;
        }

        public NodeSet getNodeHierarchyChildren(T parent, boolean direct, DefaultNodeSet ns) {
            Node node = nodeCache.getNode(parent);
            if (node.isBottomNode()) {
                return ns;
            }
            Set directChildren = new HashSet<>();
            for (T equiv : node) {
                directChildren.addAll(rawParentChildProvider.getChildren(equiv));
                if (directParentsOfBottomNode.contains(equiv)) {
                    ns.addNode(nodeCache.getBottomNode());
                }
            }
            node.entities().forEach(directChildren::remove);
            if (node.isTopNode()) {
                // Special treatment
                directChildren.addAll(directChildrenOfTopNode);
            }
            for (Node childNode : nodeCache.getNodes(directChildren)) {
                ns.addNode(childNode);
            }
            if (!direct) {
                for (T child : directChildren) {
                    getNodeHierarchyChildren(child, direct, ns);
                }
            }
            return ns;
        }

        public NodeSet getNodeHierarchyParents(T child, boolean direct, DefaultNodeSet ns) {
            Node node = nodeCache.getNode(child);
            if (node.isTopNode()) {
                return ns;
            }
            Set directParents = new HashSet<>();
            for (T equiv : node) {
                directParents.addAll(rawParentChildProvider.getParents(equiv));
                if (directChildrenOfTopNode.contains(equiv)) {
                    ns.addNode(nodeCache.getTopNode());
                }
            }
            node.entities().forEach(directParents::remove);
            if (node.isBottomNode()) {
                // Special treatment
                directParents.addAll(directParentsOfBottomNode);
            }
            for (Node parentNode : nodeCache.getNodes(directParents)) {
                ns.addNode(parentNode);
            }
            if (!direct) {
                for (T parent : directParents) {
                    getNodeHierarchyParents(parent, direct, ns);
                }
            }
            return ns;
        }

        public Node getEquivalents(T element) {
            return nodeCache.getNode(element);
        }
    }

    private class ClassHierarchyInfo extends AbstractHierarchyInfo {

        ClassHierarchyInfo() {
            super("class", getDataFactory().getOWLThing(), getDataFactory().getOWLNothing(),
                new RawClassHierarchyProvider());
        }

        @Override
        protected Stream getEntitiesInSignature(OWLAxiom ax) {
            return ax.classesInSignature();
        }

        @Override
        protected DefaultNode createNode(Set cycle) {
            return new OWLClassNode(cycle);
        }

        @Override
        protected Stream getEntities(OWLOntology ont) {
            return ont.classesInSignature();
        }

        @Override
        protected DefaultNode createNode() {
            return new OWLClassNode();
        }
    }

    private class ObjectPropertyHierarchyInfo
        extends AbstractHierarchyInfo {

        ObjectPropertyHierarchyInfo() {
            super("object property", getDataFactory().getOWLTopObjectProperty(),
                getDataFactory().getOWLBottomObjectProperty(),
                new RawObjectPropertyHierarchyProvider());
        }

        @Override
        protected Stream getEntitiesInSignature(OWLAxiom ax) {
            Set result = new HashSet<>();
            ax.objectPropertiesInSignature().forEach(p -> {
                result.add(p);
                result.add(p.getInverseProperty());
            });
            return result.stream();
        }

        @Override
        protected Stream getEntities(OWLOntology ont) {
            Set result = new HashSet<>();
            ont.objectPropertiesInSignature().forEach(p -> {
                result.add(p);
                result.add(p.getInverseProperty());
            });
            return result.stream();
        }

        @Override
        protected DefaultNode createNode(
            Set cycle) {
            return new OWLObjectPropertyNode(cycle);
        }

        @Override
        protected DefaultNode createNode() {
            return new OWLObjectPropertyNode();
        }

        @Override
        public void processChanges(Set signature, Set added,
            Set removed) {
            boolean rebuild = false;
            for (OWLAxiom ax : added) {
                if (ax instanceof OWLObjectPropertyAxiom) {
                    rebuild = true;
                    break;
                }
            }
            if (!rebuild) {
                for (OWLAxiom ax : removed) {
                    if (ax instanceof OWLObjectPropertyAxiom) {
                        rebuild = true;
                        break;
                    }
                }
            }
            if (rebuild) {
                ((RawObjectPropertyHierarchyProvider) getRawParentChildProvider()).rebuild();
            }
            super.processChanges(signature, added, removed);
        }
    }

    private class DataPropertyHierarchyInfo extends AbstractHierarchyInfo {

        DataPropertyHierarchyInfo() {
            super("data property", getDataFactory().getOWLTopDataProperty(),
                getDataFactory().getOWLBottomDataProperty(),
                new RawDataPropertyHierarchyProvider());
        }

        @Override
        protected Stream getEntitiesInSignature(OWLAxiom ax) {
            return ax.dataPropertiesInSignature();
        }

        @Override
        protected Stream getEntities(OWLOntology ont) {
            return ont.dataPropertiesInSignature();
        }

        @Override
        protected DefaultNode createNode(Set cycle) {
            return new OWLDataPropertyNode(cycle);
        }

        @Override
        protected DefaultNode createNode() {
            return new OWLDataPropertyNode();
        }
    }

    private class RawClassHierarchyProvider implements RawHierarchyProvider {

        RawClassHierarchyProvider() {}

        @Override
        public Collection getParents(OWLClass child) {
            Collection result = new HashSet<>();
            for (OWLOntology ont : asList(getRootOntology().importsClosure())) {
                for (OWLSubClassOfAxiom ax : asList(ont.subClassAxiomsForSubClass(child))) {
                    OWLClassExpression superCls = ax.getSuperClass();
                    if (!superCls.isAnonymous()) {
                        result.add(superCls.asOWLClass());
                    } else if (superCls instanceof OWLObjectIntersectionOf) {
                        OWLObjectIntersectionOf intersectionOf = (OWLObjectIntersectionOf) superCls;
                        for (OWLClassExpression conjunct : intersectionOf.asConjunctSet()) {
                            if (!conjunct.isAnonymous()) {
                                result.add(conjunct.asOWLClass());
                            }
                        }
                    }
                }
                for (OWLEquivalentClassesAxiom ax : asList(ont.equivalentClassesAxioms(child))) {
                    for (OWLClassExpression ce : ax.getClassExpressionsMinus(child)) {
                        if (!ce.isAnonymous()) {
                            result.add(ce.asOWLClass());
                        } else if (ce instanceof OWLObjectIntersectionOf) {
                            OWLObjectIntersectionOf intersectionOf = (OWLObjectIntersectionOf) ce;
                            for (OWLClassExpression conjunct : intersectionOf.asConjunctSet()) {
                                if (!conjunct.isAnonymous()) {
                                    result.add(conjunct.asOWLClass());
                                }
                            }
                        }
                    }
                }
            }
            return result;
        }

        @Override
        public Collection getChildren(OWLClass parent) {
            Collection result = new HashSet<>();
            for (OWLAxiom ax : asUnorderedSet(
                getRootOntology().referencingAxioms(parent, INCLUDED))) {
                if (ax instanceof OWLSubClassOfAxiom) {
                    OWLSubClassOfAxiom sca = (OWLSubClassOfAxiom) ax;
                    if (!sca.getSubClass().isAnonymous()) {
                        Set conjuncts = sca.getSuperClass().asConjunctSet();
                        if (conjuncts.contains(parent)) {
                            result.add(sca.getSubClass().asOWLClass());
                        }
                    }
                } else if (ax instanceof OWLEquivalentClassesAxiom) {
                    OWLEquivalentClassesAxiom eca = (OWLEquivalentClassesAxiom) ax;
                    eca.classExpressions().filter(ce -> ce.containsConjunct(parent))
                        .forEach(ce -> eca.classExpressions().forEach(sub -> {
                            if (!sub.isAnonymous() && !sub.equals(ce)) {
                                result.add(sub.asOWLClass());
                            }
                        }));
                }
            }
            return result;
        }
    }

    private class RawObjectPropertyHierarchyProvider
        implements RawHierarchyProvider {

        private OWLObjectPropertyManager propertyManager;
        private Map> sub2Super;
        private Map> super2Sub;

        RawObjectPropertyHierarchyProvider() {
            rebuild();
        }

        public final void rebuild() {
            propertyManager = new OWLObjectPropertyManager(getRootOntology());
            sub2Super = propertyManager.getPropertyHierarchy();
            super2Sub = new HashMap<>();
            for (Map.Entry> e : sub2Super
                .entrySet()) {
                e.getValue().forEach(
                    a -> super2Sub.computeIfAbsent(a, x -> new HashSet<>()).add(e.getKey()));
            }
        }

        @Override
        public Collection getParents(
            OWLObjectPropertyExpression child) {
            if (child.isBottomEntity()) {
                return Collections.emptySet();
            }
            Set propertyExpressions = sub2Super.get(child);
            if (propertyExpressions == null) {
                return Collections.emptySet();
            } else {
                return new HashSet<>(propertyExpressions);
            }
        }

        @Override
        public Collection getChildren(
            OWLObjectPropertyExpression parent) {
            if (parent.isTopEntity()) {
                return Collections.emptySet();
            }
            Set propertyExpressions = super2Sub.get(parent);
            if (propertyExpressions == null) {
                return Collections.emptySet();
            } else {
                return new HashSet<>(propertyExpressions);
            }
        }
    }

    private class RawDataPropertyHierarchyProvider
        implements RawHierarchyProvider {

        RawDataPropertyHierarchyProvider() {}

        @Override
        public Collection getParents(OWLDataProperty child) {
            Set properties = new HashSet<>();
            Stream axioms =
                getRootOntology().axioms(Filters.subDataPropertyWithSub, child, INCLUDED);
            sup(axioms, OWLDataPropertyExpression.class)
                .forEach(p -> properties.add(p.asOWLDataProperty()));
            return properties;
        }

        @Override
        public Collection getChildren(OWLDataProperty parent) {
            Set properties = new HashSet<>();
            Stream axioms =
                getRootOntology().axioms(Filters.subDataPropertyWithSuper, parent, INCLUDED);
            sub(axioms, OWLDataPropertyExpression.class)
                .forEach(p -> properties.add(p.asOWLDataProperty()));
            return properties;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy