org.dllearner.reasoning.SPARQLReasoner.java Source code

Java tutorial

Introduction

Here is the source code for org.dllearner.reasoning.SPARQLReasoner.java

Source

/**
 * Copyright (C) 2007 - 2016, Jens Lehmann
 *
 * This file is part of DL-Learner.
 *
 * DL-Learner 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.
 *
 * DL-Learner 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/>.
 */
package org.dllearner.reasoning;

import com.clarkparsia.owlapiv3.XSD;
import com.google.common.base.Joiner;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
import org.aksw.jena_sparql_api.core.QueryExecutionFactory;
import org.aksw.jena_sparql_api.delay.core.QueryExecutionFactoryDelay;
import org.aksw.jena_sparql_api.http.QueryExecutionFactoryHttp;
import org.aksw.jena_sparql_api.model.QueryExecutionFactoryModel;
import org.aksw.jena_sparql_api.pagination.core.QueryExecutionFactoryPaginated;
import org.apache.commons.lang3.NotImplementedException;
import org.apache.jena.query.*;
import org.apache.jena.rdf.model.*;
import org.apache.jena.riot.RDFDataMgr;
import org.apache.jena.sparql.engine.http.QueryExceptionHTTP;
import org.apache.jena.vocabulary.OWL;
import org.apache.jena.vocabulary.OWL2;
import org.apache.jena.vocabulary.RDF;
import org.apache.jena.vocabulary.RDFS;
import org.dllearner.core.*;
import org.dllearner.core.annotations.NoConfigOption;
import org.dllearner.core.config.ConfigOption;
import org.dllearner.core.owl.ClassHierarchy;
import org.dllearner.core.owl.DatatypePropertyHierarchy;
import org.dllearner.core.owl.LazyClassHierarchy;
import org.dllearner.core.owl.ObjectPropertyHierarchy;
import org.dllearner.kb.LocalModelBasedSparqlEndpointKS;
import org.dllearner.kb.OWLFile;
import org.dllearner.kb.SparqlEndpointKS;
import org.dllearner.kb.sparql.SPARQLQueryUtils;
import org.dllearner.kb.sparql.SparqlEndpoint;
import org.dllearner.utilities.OWLAPIUtils;
import org.dllearner.utilities.OwlApiJenaUtils;
import org.dllearner.utilities.datastructures.SortedSetTuple;
import org.dllearner.utilities.owl.OWLClassExpressionToSPARQLConverter;
import org.semanticweb.owlapi.model.*;
import org.semanticweb.owlapi.util.OWLObjectDuplicator;
import org.semanticweb.owlapi.vocab.XSDVocabulary;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.Marker;
import org.slf4j.helpers.BasicMarkerFactory;
import uk.ac.manchester.cs.owl.owlapi.OWLDataFactoryImpl;

import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;

/**
 * A reasoner implementation that provides inference services by the execution
 * of SPARQL queries on
 * <ul>
 * <li> local files (usually in forms of JENA API models)</li>
 * <li> remote SPARQL endpoints </li>
 * </ul>
 * <p>
 * Compared to other reasoner implementations, it doesn't do any pre-computation
 * by default because it might be too expensive on very large knowledge bases.
 * </p>
 * @author Lorenz Buehmann
 *
 */
@ComponentAnn(name = "SPARQL Reasoner", shortName = "spr", version = 0.1)
public class SPARQLReasoner extends AbstractReasonerComponent implements SchemaReasoner, IndividualReasoner {

    private static final Logger logger = LoggerFactory.getLogger(SPARQLReasoner.class);
    private final static Marker sparql_debug = new BasicMarkerFactory().getMarker("SD");

    public enum PopularityType {
        CLASS, OBJECT_PROPERTY, DATA_PROPERTY
    }

    private static final ParameterizedSparqlString CLASS_POPULARITY_QUERY = new ParameterizedSparqlString(
            "SELECT (COUNT(*) AS ?cnt) WHERE {?s a ?entity .}");

    private static final ParameterizedSparqlString PROPERTY_POPULARITY_QUERY = new ParameterizedSparqlString(
            "SELECT (COUNT(*) AS ?cnt) WHERE {?s ?entity ?o .}");

    private static final ParameterizedSparqlString INDIVIDUAL_POPULARITY_QUERY = new ParameterizedSparqlString(
            "SELECT (COUNT(*) AS ?cnt) WHERE {?entity ?p ?o .}");

    @ConfigOption(description = "Use alternative relaxed Sparql-queries for Classes and Individuals", defaultValue = "false")
    private boolean laxMode = false;

    @ConfigOption(description = "Whether to use the generic facet generation code, which requires downloading all instances and is thus not recommended", defaultValue = "false")
    private boolean useGenericSplitsCode = false;

    @ConfigOption(description = "Whether to use SPARQL1.1 Value Lists", defaultValue = "false")
    private boolean useValueLists = false;

    private QueryExecutionFactory qef;

    private SparqlEndpointKS ks;
    private ClassHierarchy hierarchy;

    private Map<OWLEntity, Integer> entityPopularityMap = new HashMap<>();
    private Map<OWLClass, Integer> classPopularityMap = new HashMap<>();
    private boolean batchedMode = true;
    private Set<PopularityType> precomputedPopularityTypes = new HashSet<>();

    private boolean prepared = false;

    private OWLClassExpressionToSPARQLConverter converter = new OWLClassExpressionToSPARQLConverter();

    private OWLDataFactory df = new OWLDataFactoryImpl();
    private OWLObjectDuplicator duplicator = new OWLObjectDuplicator(df);

    /**
     * Default constructor for usage of config files + Spring API.
     */
    public SPARQLReasoner() {
        setPrecomputeClassHierarchy(false);
        setPrecomputeObjectPropertyHierarchy(false);
        setPrecomputeDataPropertyHierarchy(false);
    }

    public SPARQLReasoner(SparqlEndpointKS ks) {
        super(ks);
        this.qef = ks.getQueryExecutionFactory();
    }

    public SPARQLReasoner(SparqlEndpoint endpoint) {
        this(new QueryExecutionFactoryHttp(endpoint.getURL().toString(), endpoint.getDefaultGraphURIs()));
    }

    public SPARQLReasoner(Model model) {
        this(new QueryExecutionFactoryModel(model));
    }

    public SPARQLReasoner(QueryExecutionFactory qef) {
        this();
        this.qef = qef;
    }

    /* (non-Javadoc)
     * @see org.dllearner.core.Component#init()
     */
    @Override
    public void init() throws ComponentInitException {
        classPopularityMap = new HashMap<>();

        // this is only done if the reasoner is setup via config file
        if (qef == null) {
            if (ks == null) {
                KnowledgeSource abstract_ks = sources.iterator().next();
                if (SparqlEndpointKS.class.isAssignableFrom(abstract_ks.getClass())) {
                    ks = (SparqlEndpointKS) abstract_ks;
                } else {
                    OWLFile owl_file = (OWLFile) abstract_ks;
                    Model model = RDFDataMgr.loadModel(owl_file.getURL().getFile());
                    logger.debug(sparql_debug,
                            "file reasoning: " + ((owl_file.getReasoning() == null
                                    || owl_file.getReasoning().getReasonerFactory() == null) ? "(none)"
                                            : owl_file.getReasoning().getReasonerFactory().getURI()));
                    ks = new LocalModelBasedSparqlEndpointKS(model, owl_file.getReasoning());
                }
                if (sources.size() > 1) {
                    throw new ComponentInitException("SPARQLReasoner only supports a single knowledge source");
                }
            }
            if (ks.isRemote()) {
                qef = ks.getQueryExecutionFactory();
                qef = new QueryExecutionFactoryDelay(qef, 50);
                //            qef = new QueryExecutionFactoryCacheEx(qef, cache);
                qef = new QueryExecutionFactoryPaginated(qef, 10000);
            } else {
                qef = new QueryExecutionFactoryModel(((LocalModelBasedSparqlEndpointKS) ks).getModel());
            }
        }
    }

    public QueryExecutionFactory getQueryExecutionFactory() {
        return qef;
    }

    public void precomputePopularities(PopularityType... popularityTypes) {
        for (PopularityType popularityType : popularityTypes) {
            switch (popularityType) {
            case CLASS:
                precomputeClassPopularity();
                break;
            case OBJECT_PROPERTY:
                precomputeObjectPropertyPopularity();
                break;
            case DATA_PROPERTY:
                precomputeDataPropertyPopularity();
                break;
            default:
                break;
            }
        }
    }

    public void precomputePropertyDomains() {
        logger.info("precomputing property domains...");
        String query = SPARQLQueryUtils.PREFIXES
                + " select * where {?p rdfs:domain ?dom {?p a owl:ObjectProperty} UNION {?p a owl:DatatypeProperty}}";

        try (QueryExecution qe = qef.createQueryExecution(query)) {
            ResultSet rs = qe.execSelect();

            while (rs.hasNext()) {
                QuerySolution qs = rs.next();
                OWLObjectProperty p = df.getOWLObjectProperty(IRI.create(qs.getResource("p").getURI()));
                OWLClass dom = df.getOWLClass(IRI.create(qs.getResource("dom").getURI()));

                propertyDomains.put(p, dom);
            }
        } catch (Exception e) {
            logger.error("Failed to compute property domains.", e);
        }
        logger.info("finished precomputing property domains.");
    }

    public void precomputeObjectPropertyRanges() {
        logger.info("precomputing object property ranges...");
        String query = SPARQLQueryUtils.PREFIXES + " select * where {?p rdfs:range ?ran; a owl:ObjectProperty }";

        try (QueryExecution qe = qef.createQueryExecution(query)) {
            ResultSet rs = qe.execSelect();

            while (rs.hasNext()) {
                QuerySolution qs = rs.next();
                OWLObjectProperty p = df.getOWLObjectProperty(IRI.create(qs.getResource("p").getURI()));
                OWLClass dom = df.getOWLClass(IRI.create(qs.getResource("ran").getURI()));

                objectPropertyRanges.put(p, dom);
            }
        } catch (Exception e) {
            logger.error("Failed to compute property domains.", e);
        }
        logger.info("finished precomputing property domains.");
    }

    public void precomputePopularity() {
        precomputeClassPopularity();
        precomputeDataPropertyPopularity();
        precomputeObjectPropertyPopularity();
    }

    public boolean isPrecomputed(PopularityType popularityType) {
        return precomputedPopularityTypes.contains(popularityType);
    }

    public void precomputeClassPopularity() {
        if (isPrecomputed(PopularityType.CLASS)) {
            return;
        }
        logger.info("Precomputing class popularity ...");

        long start = System.currentTimeMillis();

        if (batchedMode) {
            String query = "PREFIX owl:<http://www.w3.org/2002/07/owl#>  SELECT ?type (COUNT(?s) AS ?cnt) WHERE {?s a ?type . ?type a owl:Class .} GROUP BY ?type";
            ResultSet rs = executeSelectQuery(query);
            while (rs.hasNext()) {
                QuerySolution qs = rs.next();
                if (qs.get("type").isURIResource()) {
                    OWLClass cls = df.getOWLClass(IRI.create(qs.getResource("type").getURI()));
                    int cnt = qs.getLiteral("cnt").getInt();
                    classPopularityMap.put(cls, cnt);
                }
            }
        } else {
            Set<OWLClass> classes = getOWLClasses();
            String queryTemplate = "SELECT (COUNT(?s) AS ?cnt) WHERE {?s a <%s>}";
            for (OWLClass cls : classes) {
                ResultSet rs = executeSelectQuery(String.format(queryTemplate, cls.toStringID()));
                int cnt = rs.next().getLiteral("cnt").getInt();
                classPopularityMap.put(cls, cnt);
            }
        }
        precomputedPopularityTypes.add(PopularityType.CLASS);

        long end = System.currentTimeMillis();

        logger.info("... done in " + (end - start) + "ms.");
    }

    public void precomputeObjectPropertyPopularity() {
        if (isPrecomputed(PopularityType.OBJECT_PROPERTY)) {
            return;
        }
        logger.info("Precomputing object property popularity ...");

        long start = System.currentTimeMillis();

        if (batchedMode) {
            String query = "PREFIX owl: <http://www.w3.org/2002/07/owl#> SELECT ?p (COUNT(*) AS ?cnt) WHERE {?s ?p ?o . ?p a owl:ObjectProperty .} GROUP BY ?p";
            ResultSet rs = executeSelectQuery(query);
            while (rs.hasNext()) {
                QuerySolution qs = rs.next();
                if (qs.get("p").isURIResource()) {
                    OWLObjectProperty property = df.getOWLObjectProperty(IRI.create(qs.getResource("p").getURI()));
                    int cnt = qs.getLiteral("cnt").getInt();
                    entityPopularityMap.put(property, cnt);
                }
            }
        } else {
            Set<OWLObjectProperty> properties = getOWLObjectProperties();
            String queryTemplate = "SELECT (COUNT(*) AS ?cnt) WHERE {?s <%s> ?o}";
            for (OWLObjectProperty property : properties) {
                ResultSet rs = executeSelectQuery(String.format(queryTemplate, property.toStringID()));
                int cnt = rs.next().getLiteral("cnt").getInt();
                entityPopularityMap.put(property, cnt);
            }
        }
        precomputedPopularityTypes.add(PopularityType.OBJECT_PROPERTY);

        long end = System.currentTimeMillis();

        logger.info("... done in " + (end - start) + "ms.");
    }

    public void precomputeDataPropertyPopularity() {
        if (isPrecomputed(PopularityType.DATA_PROPERTY)) {
            return;
        }
        logger.info("Precomputing data property popularity ...");

        long start = System.currentTimeMillis();

        if (batchedMode) {
            String query = "PREFIX owl: <http://www.w3.org/2002/07/owl#> SELECT ?p (COUNT(*) AS ?cnt) WHERE {?s ?p ?o . ?p a owl:DatatypeProperty .} GROUP BY ?p";
            ResultSet rs = executeSelectQuery(query);
            while (rs.hasNext()) {
                QuerySolution qs = rs.next();
                if (qs.get("p").isURIResource()) {
                    OWLDataProperty property = df.getOWLDataProperty(IRI.create(qs.getResource("p").getURI()));
                    int cnt = qs.getLiteral("cnt").getInt();
                    entityPopularityMap.put(property, cnt);
                }
            }
        } else {
            Set<OWLDataProperty> properties = getOWLDataProperties();
            String queryTemplate = "SELECT (COUNT(*) AS ?cnt) WHERE {?s <%s> ?o}";
            for (OWLDataProperty property : properties) {
                ResultSet rs = executeSelectQuery(String.format(queryTemplate, property.toStringID()));
                int cnt = rs.next().getLiteral("cnt").getInt();
                entityPopularityMap.put(property, cnt);
            }
        }
        precomputedPopularityTypes.add(PopularityType.DATA_PROPERTY);

        long end = System.currentTimeMillis();

        logger.info("... done in " + (end - start) + "ms.");
    }

    public int getSubjectCountForProperty(OWLProperty p, long timeout, TimeUnit timeoutUnits) {
        int cnt = -1;
        String query = String.format("SELECT (COUNT(DISTINCT ?s) AS ?cnt) WHERE {?s <%s> ?o.}", p.toStringID());
        ResultSet rs = executeSelectQuery(query, timeout, timeoutUnits);
        if (rs.hasNext()) {
            cnt = rs.next().getLiteral("cnt").getInt();
        }

        return cnt;
    }

    public int getSubjectCountForProperty(OWLProperty p) {
        int cnt = -1;
        String query = String.format("SELECT (COUNT(DISTINCT ?s) AS ?cnt) WHERE {?s <%s> ?o.}", p.toStringID());
        ResultSet rs = executeSelectQuery(query);
        if (rs.hasNext()) {
            cnt = rs.next().getLiteral("cnt").getInt();
        }

        return cnt;
    }

    public int getObjectCountForProperty(OWLObjectProperty p, long timeout, TimeUnit timeoutUnits) {
        int cnt = -1;
        String query = String.format("SELECT (COUNT(DISTINCT ?o) AS ?cnt) WHERE {?s <%s> ?o.}", p.toStringID());
        ResultSet rs = executeSelectQuery(query, timeout, timeoutUnits);
        if (rs.hasNext()) {
            cnt = rs.next().getLiteral("cnt").getInt();
        }

        return cnt;
    }

    public int getObjectCountForProperty(OWLObjectProperty p) {
        int cnt = -1;
        String query = String.format("SELECT (COUNT(DISTINCT ?o) AS ?cnt) WHERE {?s <%s> ?o.}", p.toStringID());
        ResultSet rs = executeSelectQuery(query);
        if (rs.hasNext()) {
            cnt = rs.next().getLiteral("cnt").getInt();
        }

        return cnt;
    }

    /**
     * Computes the popularity of the given entity.
     * @param entity the entity
     * @param <T> the OWL entity type
     * @return the popularity
     */
    public <T extends OWLEntity> int getPopularity(T entity) {
        // check if we have the value cached
        Integer popularity = entityPopularityMap.get(entity);

        // compute the value if not cached
        if (popularity == null) {
            ParameterizedSparqlString queryTemplate;
            if (entity.isOWLClass()) {
                queryTemplate = CLASS_POPULARITY_QUERY;
            } else if (entity.isOWLObjectProperty() || entity.isOWLDataProperty()) {
                queryTemplate = PROPERTY_POPULARITY_QUERY;
            } else if (entity.isOWLNamedIndividual()) {
                queryTemplate = INDIVIDUAL_POPULARITY_QUERY;
            } else {
                throw new IllegalArgumentException(
                        "Popularity computation not supported for entity type " + entity.getEntityType().getName());
            }

            queryTemplate.setIri("entity", entity.toStringID());
            ResultSet rs = executeSelectQuery(queryTemplate.toString());

            popularity = rs.next().getLiteral("cnt").getInt();

            // put to cache
            entityPopularityMap.put(entity, popularity);
        }
        return popularity;
    }

    public int getPopularityOf(OWLClassExpression description) {
        if (classPopularityMap != null && classPopularityMap.containsKey(description)) {
            return classPopularityMap.get(description);
        } else {
            String query = converter.asCountQuery(description).toString();
            ResultSet rs = executeSelectQuery(query);
            int cnt = rs.next().getLiteral("cnt").getInt();
            return cnt;
        }
    }

    @Override
    public ClassHierarchy prepareSubsumptionHierarchy() {
        if (precomputeClassHierarchy) {
            if (!prepared) {
                hierarchy = prepareSubsumptionHierarchyFast();

                prepared = true;
            }
        } else {
            hierarchy = new LazyClassHierarchy(this);
        }
        return hierarchy;
    }

    public boolean isFunctional(OWLObjectProperty property) {
        String query = "ASK {<" + property.toStringID() + "> a <" + OWL.FunctionalProperty.getURI() + ">}";
        return executeAskQuery(query);
    }

    public boolean isInverseFunctional(OWLObjectProperty property) {
        String query = "ASK {<" + property.toStringID() + "> a <" + OWL.InverseFunctionalProperty.getURI() + ">}";
        return executeAskQuery(query);
    }

    public boolean isAsymmetric(OWLObjectProperty property) {
        String query = "ASK {<" + property.toStringID() + "> a <" + OWL2.AsymmetricProperty.getURI() + ">}";
        return executeAskQuery(query);
    }

    public boolean isSymmetric(OWLObjectProperty property) {
        String query = "ASK {<" + property.toStringID() + "> a <" + OWL2.SymmetricProperty.getURI() + ">}";
        return executeAskQuery(query);
    }

    public boolean isIrreflexive(OWLObjectProperty property) {
        String query = "ASK {<" + property.toStringID() + "> a <" + OWL2.IrreflexiveProperty.getURI() + ">}";
        return executeAskQuery(query);
    }

    public boolean isReflexive(OWLObjectProperty property) {
        String query = "ASK {<" + property.toStringID() + "> a <" + OWL2.ReflexiveProperty.getURI() + ">}";
        return executeAskQuery(query);
    }

    public boolean isTransitive(OWLObjectProperty property) {
        String query = "ASK {<" + property.toStringID() + "> a <" + OWL2.TransitiveProperty.getURI() + ">}";
        return executeAskQuery(query);
    }

    public boolean isFunctional(OWLDataProperty property) {
        String query = "ASK {<" + property.toStringID() + "> a <" + OWL.FunctionalProperty.getURI() + ">}";
        return executeAskQuery(query);
    }

    /**
     * Pre-computes the class hierarchy. Instead of executing queries for each class,
     * we query by the predicate rdfs:subClassOf.
     * @return the class hierarchy
     */
    public final ClassHierarchy prepareSubsumptionHierarchyFast() {
        logger.info("Preparing class subsumption hierarchy ...");
        long startTime = System.currentTimeMillis();
        TreeMap<OWLClassExpression, SortedSet<OWLClassExpression>> subsumptionHierarchyUp = new TreeMap<>();
        TreeMap<OWLClassExpression, SortedSet<OWLClassExpression>> subsumptionHierarchyDown = new TreeMap<>();

        String query = "SELECT * WHERE {" + "?sub a <http://www.w3.org/2002/07/owl#Class> . "
        //            + "?sup a <http://www.w3.org/2002/07/owl#Class> . "
                + "?sub (<http://www.w3.org/2000/01/rdf-schema#subClassOf>|<http://www.w3.org/2002/07/owl#equivalentClass>) ?sup ."
                + "FILTER(?sub != ?sup)" + "}";
        ResultSet rs = executeSelectQuery(query);

        while (rs.hasNext()) {
            QuerySolution qs = rs.next();
            if (qs.get("sub").isURIResource() && qs.get("sup").isURIResource()) {
                OWLClass sub = df.getOWLClass(IRI.create(qs.get("sub").asResource().getURI()));
                OWLClass sup = df.getOWLClass(IRI.create(qs.get("sup").asResource().getURI()));

                //add subclasses
                SortedSet<OWLClassExpression> subClasses = subsumptionHierarchyDown.get(sup);
                if (subClasses == null) {
                    subClasses = new TreeSet<>();
                    subsumptionHierarchyDown.put(sup, subClasses);
                }
                subClasses.add(sub);

                //add superclasses
                SortedSet<OWLClassExpression> superClasses = subsumptionHierarchyUp.get(sub);
                if (superClasses == null) {
                    superClasses = new TreeSet<>();
                    subsumptionHierarchyUp.put(sub, superClasses);
                }
                superClasses.add(sup);
            }
        }

        logger.info("... done in {}ms", (System.currentTimeMillis() - startTime));
        hierarchy = new ClassHierarchy(subsumptionHierarchyUp, subsumptionHierarchyDown);

        return hierarchy;
    }

    /* (non-Javadoc)
     * @see org.dllearner.core.AbstractReasonerComponent#prepareObjectPropertyHierarchy()
     */
    @Override
    public ObjectPropertyHierarchy prepareObjectPropertyHierarchy() throws ReasoningMethodUnsupportedException {
        //      if(precomputeObjectPropertyHierarchy) {
        logger.info("Preparing object property subsumption hierarchy ...");
        long startTime = System.currentTimeMillis();
        TreeMap<OWLObjectProperty, SortedSet<OWLObjectProperty>> subsumptionHierarchyUp = new TreeMap<>();
        TreeMap<OWLObjectProperty, SortedSet<OWLObjectProperty>> subsumptionHierarchyDown = new TreeMap<>();

        String query = "SELECT * WHERE {" + "?sub a <http://www.w3.org/2002/07/owl#ObjectProperty> . "
                + "FILTER NOT EXISTS{?sub a <http://www.w3.org/2002/07/owl#DatatypeProperty>}" // TODO remove workaround
                + "FILTER(?sub != <http://www.w3.org/2002/07/owl#bottomObjectProperty> && ?sub != <http://www.w3.org/2002/07/owl#topObjectProperty>)"
                + "OPTIONAL {" + "?sub <http://www.w3.org/2000/01/rdf-schema#subPropertyOf> ?sup ."
                + "?sup a <http://www.w3.org/2002/07/owl#ObjectProperty> . "
                + "FILTER(?sup != ?sub && ?sup != <http://www.w3.org/2002/07/owl#topObjectProperty>)" + "}" + "}";
        ResultSet rs = executeSelectQuery(query);

        while (rs.hasNext()) {
            QuerySolution qs = rs.next();
            if (qs.get("sub").isURIResource()) {
                IRI iri = IRI.create(qs.get("sub").asResource().getURI());

                if (!iri.isReservedVocabulary()) {
                    OWLObjectProperty sub = df.getOWLObjectProperty(iri);

                    // add sub properties entry
                    if (!subsumptionHierarchyDown.containsKey(sub)) {
                        subsumptionHierarchyDown.put(sub, new TreeSet<>());
                    }

                    // add super properties entry
                    if (!subsumptionHierarchyUp.containsKey(sub)) {
                        subsumptionHierarchyUp.put(sub, new TreeSet<>());
                    }

                    // if there is a super property
                    if (qs.get("sup") != null && qs.get("sup").isURIResource()) {
                        OWLObjectProperty sup = df
                                .getOWLObjectProperty(IRI.create(qs.get("sup").asResource().getURI()));

                        // add sub properties entry
                        if (!subsumptionHierarchyDown.containsKey(sup)) {
                            subsumptionHierarchyDown.put(sup, new TreeSet<>());
                        }

                        // add super properties entry
                        if (!subsumptionHierarchyUp.containsKey(sup)) {
                            subsumptionHierarchyUp.put(sup, new TreeSet<>());
                        }

                        // add super properties entry
                        SortedSet<OWLObjectProperty> superClasses = subsumptionHierarchyUp.get(sub);
                        if (superClasses == null) {
                            superClasses = new TreeSet<>();
                            subsumptionHierarchyUp.put(sub, superClasses);
                        }
                        superClasses.add(sup);

                        // add sub properties entry
                        SortedSet<OWLObjectProperty> subProperties = subsumptionHierarchyDown.get(sup);
                        if (subProperties == null) {
                            subProperties = new TreeSet<>();
                            subsumptionHierarchyDown.put(sup, subProperties);
                        }
                        subProperties.add(sub);
                    }
                }
            }
        }
        logger.info("... done in {}ms", (System.currentTimeMillis() - startTime));
        roleHierarchy = new ObjectPropertyHierarchy(subsumptionHierarchyUp, subsumptionHierarchyDown);
        //      }
        return roleHierarchy;
    }

    /* (non-Javadoc)
     * @see org.dllearner.core.AbstractReasonerComponent#prepareObjectPropertyHierarchy()
     */
    @Override
    public DatatypePropertyHierarchy prepareDatatypePropertyHierarchy() throws ReasoningMethodUnsupportedException {
        logger.info("Preparing data property subsumption hierarchy ...");
        long startTime = System.currentTimeMillis();
        TreeMap<OWLDataProperty, SortedSet<OWLDataProperty>> subsumptionHierarchyUp = new TreeMap<>();
        TreeMap<OWLDataProperty, SortedSet<OWLDataProperty>> subsumptionHierarchyDown = new TreeMap<>();

        String query = "SELECT * WHERE {" + "?sub a <http://www.w3.org/2002/07/owl#DatatypeProperty> . "
                + "OPTIONAL {" + "?sub <http://www.w3.org/2000/01/rdf-schema#subPropertyOf> ?sup ."
                + "?sup a <http://www.w3.org/2002/07/owl#DatatypeProperty> . "
                + "FILTER(?sup != ?sub && ?sup != <http://www.w3.org/2002/07/owl#topDatatypeProperty> )" + "}"
                + "FILTER(?sub != <http://www.w3.org/2002/07/owl#topDatatypeProperty> && ?sub != <http://www.w3.org/2002/07/owl#bottomDatatypeProperty>)"
                + "}";
        ResultSet rs = executeSelectQuery(query);

        while (rs.hasNext()) {
            QuerySolution qs = rs.next();
            if (qs.get("sub").isURIResource()) {
                OWLDataProperty sub = df.getOWLDataProperty(IRI.create(qs.get("sub").asResource().getURI()));

                // add sub properties entry
                if (!subsumptionHierarchyDown.containsKey(sub)) {
                    subsumptionHierarchyDown.put(sub, new TreeSet<>());
                }

                // add super properties entry
                if (!subsumptionHierarchyUp.containsKey(sub)) {
                    subsumptionHierarchyUp.put(sub, new TreeSet<>());
                }

                // if there is a super property
                if (qs.get("sup") != null && qs.get("sup").isURIResource()) {
                    OWLDataProperty sup = df.getOWLDataProperty(IRI.create(qs.get("sup").asResource().getURI()));

                    // add sub properties entry
                    if (!subsumptionHierarchyDown.containsKey(sup)) {
                        subsumptionHierarchyDown.put(sup, new TreeSet<>());
                    }

                    // add super properties entry
                    if (!subsumptionHierarchyUp.containsKey(sup)) {
                        subsumptionHierarchyUp.put(sup, new TreeSet<>());
                    }

                    // add super properties entry
                    SortedSet<OWLDataProperty> superClasses = subsumptionHierarchyUp.get(sub);
                    if (superClasses == null) {
                        superClasses = new TreeSet<>();
                        subsumptionHierarchyUp.put(sub, superClasses);
                    }
                    superClasses.add(sup);

                    // add sub properties entry
                    SortedSet<OWLDataProperty> subProperties = subsumptionHierarchyDown.get(sup);
                    if (subProperties == null) {
                        subProperties = new TreeSet<>();
                        subsumptionHierarchyDown.put(sup, subProperties);
                    }
                    subProperties.add(sub);
                }
            }
        }

        logger.info("... done in {}ms", (System.currentTimeMillis() - startTime));
        datatypePropertyHierarchy = new DatatypePropertyHierarchy(subsumptionHierarchyUp, subsumptionHierarchyDown);
        return datatypePropertyHierarchy;
    }

    public Model loadSchema() {
        return loadSchema(null);
    }

    public Model loadSchema(String namespace) {
        Model model = ModelFactory.createDefaultModel();

        //load class hierarchy
        String query = "CONSTRUCT {?s <http://www.w3.org/2000/01/rdf-schema#subClassOf> ?o} WHERE "
                + "{?s <http://www.w3.org/2000/01/rdf-schema#subClassOf> ?o."
                + (namespace != null ? "FILTER(REGEX(STR(?s), '^" + namespace + "'))}" : "");
        model.add(loadIncrementally(query));
        query = "CONSTRUCT {?s <http://www.w3.org/2002/07/owl#equivalentClass> ?o} WHERE {?s <http://www.w3.org/2002/07/owl#equivalentClass> ?o}";
        model.add(loadIncrementally(query));
        query = "CONSTRUCT {?s <http://www.w3.org/2002/07/owl#disjointWith> ?o} WHERE {?s <http://www.w3.org/2002/07/owl#disjointWith> ?o}";
        model.add(loadIncrementally(query));
        //load domain axioms
        query = "CONSTRUCT {?s <http://www.w3.org/2000/01/rdf-schema#domain> ?o. ?s a <http://www.w3.org/2002/07/owl#ObjectProperty> } "
                + "WHERE {?s <http://www.w3.org/2000/01/rdf-schema#domain> ?o.?s a <http://www.w3.org/2002/07/owl#ObjectProperty>}";
        model.add(loadIncrementally(query));
        query = "CONSTRUCT {?s <http://www.w3.org/2000/01/rdf-schema#domain> ?o. ?s a <http://www.w3.org/2002/07/owl#DatatypeProperty>} "
                + "WHERE {?s <http://www.w3.org/2000/01/rdf-schema#domain> ?o. ?s a <http://www.w3.org/2002/07/owl#DatatypeProperty>}";
        model.add(loadIncrementally(query));
        //load range axioms
        query = "CONSTRUCT {?s <http://www.w3.org/2000/01/rdf-schema#range> ?o. ?s a <http://www.w3.org/2002/07/owl#ObjectProperty>} "
                + "WHERE {?s <http://www.w3.org/2000/01/rdf-schema#range> ?o. ?s a <http://www.w3.org/2002/07/owl#ObjectProperty>}";
        model.add(loadIncrementally(query));
        query = "CONSTRUCT {?s <http://www.w3.org/2000/01/rdf-schema#range> ?o. ?s a <http://www.w3.org/2002/07/owl#DatatypeProperty>} "
                + "WHERE {?s <http://www.w3.org/2000/01/rdf-schema#range> ?o. ?s a <http://www.w3.org/2002/07/owl#DatatypeProperty>}";
        model.add(loadIncrementally(query));
        //load property hierarchy
        query = "CONSTRUCT {?s <http://www.w3.org/2000/01/rdf-schema#subPropertyOf> ?o. ?s a <http://www.w3.org/2002/07/owl#ObjectProperty>} "
                + "WHERE {?s <http://www.w3.org/2000/01/rdf-schema#subPropertyOf> ?o. ?s a <http://www.w3.org/2002/07/owl#ObjectProperty>}";
        model.add(loadIncrementally(query));
        query = "CONSTRUCT {?s <http://www.w3.org/2000/01/rdf-schema#subPropertyOf> ?o. ?s a <http://www.w3.org/2002/07/owl#DatatypeProperty>} "
                + "WHERE {?s <http://www.w3.org/2000/01/rdf-schema#subPropertyOf> ?o. ?s a <http://www.w3.org/2002/07/owl#DatatypeProperty>}";
        model.add(loadIncrementally(query));
        query = "CONSTRUCT {?s <http://www.w3.org/2002/07/owl#equivalentProperty> ?o. ?s a <http://www.w3.org/2002/07/owl#ObjectProperty>} "
                + "WHERE {?s <http://www.w3.org/2002/07/owl#equivalentProperty> ?o. ?s a <http://www.w3.org/2002/07/owl#ObjectProperty>}";
        model.add(loadIncrementally(query));
        query = "CONSTRUCT {?s <http://www.w3.org/2002/07/owl#equivalentProperty> ?o. ?s a <http://www.w3.org/2002/07/owl#DatatypeProperty>} "
                + "WHERE {?s <http://www.w3.org/2002/07/owl#equivalentProperty> ?o. ?s a <http://www.w3.org/2002/07/owl#DatatypeProperty>}";
        model.add(loadIncrementally(query));
        query = "CONSTRUCT {?s <http://www.w3.org/2002/07/owl#propertyDisjointWith> ?o. ?s a <http://www.w3.org/2002/07/owl#ObjectProperty>} "
                + "WHERE {?s <http://www.w3.org/2002/07/owl#propertyDisjointWith> ?o. ?s a <http://www.w3.org/2002/07/owl#ObjectProperty>}";
        model.add(loadIncrementally(query));
        query = "CONSTRUCT {?s <http://www.w3.org/2002/07/owl#propertyDisjointWith> ?o. ?s a <http://www.w3.org/2002/07/owl#DatatypeProperty>} "
                + "WHERE {?s <http://www.w3.org/2002/07/owl#propertyDisjointWith> ?o. ?s a <http://www.w3.org/2002/07/owl#DatatypeProperty>}";
        model.add(loadIncrementally(query));
        //load inverse relation
        query = "CONSTRUCT {?s <http://www.w3.org/2002/07/owl#inverseOf> ?o} WHERE {?s <http://www.w3.org/2002/07/owl#inverseOf> ?o}";
        model.add(loadIncrementally(query));
        //load property characteristics
        Set<Resource> propertyCharacteristics = new HashSet<>();
        //      propertyCharacteristics.add(OWL.FunctionalProperty);
        propertyCharacteristics.add(OWL.InverseFunctionalProperty);
        propertyCharacteristics.add(OWL.SymmetricProperty);
        propertyCharacteristics.add(OWL.TransitiveProperty);
        propertyCharacteristics.add(OWL2.ReflexiveProperty);
        propertyCharacteristics.add(OWL2.IrreflexiveProperty);
        propertyCharacteristics.add(OWL2.AsymmetricProperty);

        for (Resource propChar : propertyCharacteristics) {
            query = "CONSTRUCT {?s a <%s>. ?s a <http://www.w3.org/2002/07/owl#ObjectProperty>} WHERE {?s a <%s>.}"
                    .replaceAll("%s", propChar.getURI());
            model.add(loadIncrementally(query));
        }
        //for functional properties we have to distinguish between data and object properties,
        //i.e. we have to keep the property type information, otherwise conversion to OWLAPI ontology makes something curious
        query = "CONSTRUCT {?s a <%s>. ?s a <http://www.w3.org/2002/07/owl#ObjectProperty>} WHERE {?s a <%s>.?s a <http://www.w3.org/2002/07/owl#ObjectProperty>}"
                .replaceAll("%s", OWL.FunctionalProperty.getURI());
        model.add(loadIncrementally(query));
        query = "CONSTRUCT {?s a <%s>. ?s a <http://www.w3.org/2002/07/owl#DatatypeProperty>} WHERE {?s a <%s>.?s a <http://www.w3.org/2002/07/owl#DatatypeProperty>}"
                .replaceAll("%s", OWL.FunctionalProperty.getURI());
        model.add(loadIncrementally(query));

        return model;
    }

    /**
     * Gets all logical axioms according to entities of type owl:Class, owl:ObjectProperty and owl:DatatypeProperty.
     * @return the OWL schema as Jena model
     */
    public Model loadOWLSchema() {
        Model schema = ModelFactory.createDefaultModel();
        String prefixes = "PREFIX owl:<http://www.w3.org/2002/07/owl#> "
                + "PREFIX rdfs:<http://www.w3.org/2000/01/rdf-schema#> ";

        // axioms related to owl:Class entities
        String query = prefixes + "CONSTRUCT {" + "?s a owl:Class." + "?s rdfs:subClassOf ?sup."
                + "?s owl:equivalentClass ?equiv." + "?s owl:disjointWith ?disj." + "} WHERE {" + "?s a owl:Class. "
                + "OPTIONAL{?s rdfs:subClassOf ?sup.} " + "OPTIONAL{?s owl:equivalentClass ?equiv.} "
                + "OPTIONAL{?s owl:disjointWith ?disj.}" + "}";
        schema.add(loadIncrementally(query));

        // axioms related to owl:ObjectProperty entities
        query = prefixes + "CONSTRUCT {" + "?s a owl:ObjectProperty." + "?s a ?type." + "?s rdfs:domain ?domain."
                + "?s rdfs:range ?range." + "} WHERE {" + "?s a owl:ObjectProperty." + "?s a ?type. "
                + "OPTIONAL{?s rdfs:domain ?domain.} " + "OPTIONAL{?s rdfs:range ?range.}" + "}";
        schema.add(loadIncrementally(query));

        // axioms related to owl:DatatypeProperty entities
        query = prefixes + "CONSTRUCT {" + "?s a owl:DatatypeProperty." + "?s a ?type." + "?s rdfs:domain ?domain."
                + "?s rdfs:range ?range." + "} WHERE {" + "?s a owl:DatatypeProperty." + "?s a ?type. "
                + "OPTIONAL{?s rdfs:domain ?domain.} " + "OPTIONAL{?s rdfs:range ?range.}" + "}";
        schema.add(loadIncrementally(query));

        return schema;
    }

    private Model loadIncrementally(String query) {
        QueryExecutionFactory old = qef;
        qef = new QueryExecutionFactoryPaginated(qef, 10000);
        QueryExecution qe = qef.createQueryExecution(query);
        Model model = qe.execConstruct();
        qe.close();
        qef = old;
        return model;
    }

    @Override
    public Set<OWLClass> getTypesImpl(OWLIndividual individual) {
        String query = String.format(SPARQLQueryUtils.SELECT_INSTANCE_TYPES_QUERY, individual.toStringID());
        ResultSet rs = executeSelectQuery(query);
        SortedSet<OWLClass> types = asOWLEntities(EntityType.CLASS, rs, "var1");
        return types;
    }

    public Set<OWLClass> getTypes(OWLIndividual individual, String namespace) {
        return getTypes(individual);
    }

    public Set<OWLClass> getMostSpecificTypes(OWLIndividual individual) {
        Set<OWLClass> types = new HashSet<>();
        String query = String.format("SELECT ?type WHERE {<%s> a ?type ."
                + "FILTER NOT EXISTS{<%s> a ?moreSpecificType ."
                + "?moreSpecificType <http://www.w3.org/2000/01/rdf-schema#subClassOf> ?type ."
                + "FILTER((?type != ?moreSpecificType) && (?moreSpecificType!= <http://www.w3.org/2002/07/owl#Nothing>))}}",
                individual.toStringID(), individual.toStringID());
        ResultSet rs = executeSelectQuery(query);
        while (rs.hasNext()) {
            QuerySolution qs = rs.next();
            IRI iri = IRI.create(qs.getResource("type").getURI());
            if (!iri.isReservedVocabulary()) {
                types.add(df.getOWLClass(iri));
            }

        }
        return types;
    }

    /**
     * Returns the entity type of the given resource IRI, i.e. whether the resource
     * is a owl:Class, owl:ObjectProperty,owl:DatatypeProperty or owl:NamedIndividual.
     * @param iri the IRI of the entity
     * @return the entity type
     */
    public EntityType<? extends OWLEntity> getOWLEntityType(String iri) {
        ParameterizedSparqlString query = new ParameterizedSparqlString("SELECT ?type WHERE {?s a ?type .}");
        query.setIri("s", iri);
        ResultSet rs = executeSelectQuery(query.toString());
        Set<EntityType<? extends OWLEntity>> entityTypes = new HashSet<>();
        while (rs.hasNext()) {
            QuerySolution qs = rs.next();
            String uri = qs.getResource("type").getURI();
            for (EntityType<? extends OWLEntity> entityType : EntityType.values()) {
                if (entityType.getIRI().toString().equals(uri)) {
                    entityTypes.add(entityType);
                    break;
                }
            }
        }
        if (entityTypes.size() == 1) {
            return entityTypes.iterator().next();
        }
        return null;
    }

    public Set<OWLClass> getTypes(String namespace) {
        return getTypes(namespace, false);
    }

    public Set<OWLClass> getTypes(String namespace, boolean omitEmptyTypes) {
        Set<OWLClass> types = new TreeSet<>();
        String query = "SELECT DISTINCT ?cls WHERE {[] a ?cls ." + (omitEmptyTypes ? "[] a ?cls ." : "")
                + (namespace != null ? ("FILTER(REGEX(?cls,'^" + namespace + "'))") : "") + "}";
        ResultSet rs = executeSelectQuery(query);
        QuerySolution qs;
        while (rs.hasNext()) {
            qs = rs.next();
            types.add(df.getOWLClass(IRI.create(qs.getResource("cls").getURI())));
        }
        return types;
    }

    /* (non-Javadoc)
     * @see org.dllearner.core.BaseReasoner#getNamedClasses()
     */
    @Override
    public SortedSet<OWLClass> getClasses() {
        return getOWLClasses();
    }

    public SortedSet<OWLClass> getOWLClasses() {
        return getOWLClasses(null);
    }

    public SortedSet<OWLClass> getOWLClasses(String namespace) {
        ResultSet rs;
        if (!laxMode) {
            rs = executeSelectQuery(SPARQLQueryUtils.SELECT_CLASSES_QUERY);
        } else {
            rs = executeSelectQuery(SPARQLQueryUtils.SELECT_CLASSES_QUERY_ALT);
        }

        SortedSet<OWLClass> classes = asOWLEntities(EntityType.CLASS, rs, "var1");
        return classes;
    }

    public Set<OWLClass> getNonEmptyOWLClasses() {
        String query = "SELECT DISTINCT ?var1 WHERE {?var1 a <http://www.w3.org/2002/07/owl#Class>. FILTER EXISTS{[] a ?var1}}";
        ResultSet rs = executeSelectQuery(query);
        SortedSet<OWLClass> classes = asOWLEntities(EntityType.CLASS, rs, "var1");
        return classes;
    }

    /* (non-Javadoc)
     * @see org.dllearner.core.BaseReasoner#getIndividuals()
     */
    @Override
    public SortedSet<OWLIndividual> getIndividuals() {
        return getOWLIndividuals();
    }

    public SortedSet<OWLIndividual> getOWLIndividuals() {
        ResultSet rs;
        if (!laxMode) {
            rs = executeSelectQuery(SPARQLQueryUtils.SELECT_INDIVIDUALS_QUERY);
        } else {
            rs = executeSelectQuery(SPARQLQueryUtils.SELECT_INDIVIDUALS_QUERY_ALT);
        }
        SortedSet<OWLIndividual> individuals = new TreeSet<>();
        individuals.addAll(asOWLEntities(EntityType.NAMED_INDIVIDUAL, rs, "var1"));
        return individuals;
    }

    /* (non-Javadoc)
     * @see org.dllearner.core.BaseReasoner#getObjectProperties()
     */
    @Override
    public Set<OWLObjectProperty> getObjectPropertiesImpl() {
        return getOWLObjectProperties();
    }

    public SortedSet<OWLObjectProperty> getOWLObjectProperties() {
        return getOWLObjectProperties(null);
    }

    public SortedSet<OWLObjectProperty> getOWLObjectProperties(String namespace) {
        ResultSet rs = executeSelectQuery(SPARQLQueryUtils.SELECT_OBJECT_PROPERTIES_QUERY);

        SortedSet<OWLObjectProperty> properties = asOWLEntities(EntityType.OBJECT_PROPERTY, rs, "var1");
        return properties;
    }

    /* (non-Javadoc)
     * @see org.dllearner.core.AbstractReasonerComponent#getDatatypePropertiesImpl()
     */
    @Override
    protected Set<OWLDataProperty> getDatatypePropertiesImpl() throws ReasoningMethodUnsupportedException {
        return getOWLDataProperties();
    }

    public SortedSet<OWLDataProperty> getOWLDataProperties() {
        return getOWLDataProperties(null);
    }

    public SortedSet<OWLDataProperty> getOWLDataProperties(String namespace) {
        ResultSet rs = executeSelectQuery(SPARQLQueryUtils.SELECT_DATA_PROPERTIES_QUERY);

        SortedSet<OWLDataProperty> properties = asOWLEntities(EntityType.DATA_PROPERTY, rs, "var1");
        return properties;
    }

    public Set<OWLDataProperty> getDataPropertiesByRange(XSDVocabulary xsdType) {
        return getDataPropertiesByRange(xsdType.getIRI());
    }

    public Set<OWLDataProperty> getDataPropertiesByRange(Set<OWLDatatype> dts) {
        Set<OWLDataProperty> r = new TreeSet<>();
        for (OWLDatatype dt : dts) {
            r.addAll(getDataPropertiesByRange(dt.getIRI()));
        }
        return r;
    }

    public Set<OWLDataProperty> getDataPropertiesByRange(IRI iri) {
        String query = String.format(SPARQLQueryUtils.SELECT_DATA_PROPERTIES_BY_RANGE_QUERY, iri.toString());
        logger.debug(sparql_debug, "get properties by range query: " + query);
        ResultSet rs = executeSelectQuery(query);

        SortedSet<OWLDataProperty> properties = asOWLEntities(EntityType.DATA_PROPERTY, rs, "var1");
        return properties;
    }

    /* (non-Javadoc)
     * @see org.dllearner.core.AbstractReasonerComponent#getIntDatatypePropertiesImpl()
     */
    @Override
    protected Set<OWLDataProperty> getIntDatatypePropertiesImpl() throws ReasoningMethodUnsupportedException {
        return getDataPropertiesByRange(OWLAPIUtils.intDatatypes);
    }

    /* (non-Javadoc)
     * @see org.dllearner.core.AbstractReasonerComponent#getDoubleDatatypePropertiesImpl()
     */
    @Override
    protected Set<OWLDataProperty> getDoubleDatatypePropertiesImpl() throws ReasoningMethodUnsupportedException {
        return getDataPropertiesByRange(OWLAPIUtils.floatDatatypes);
    }

    /* (non-Javadoc)
     * @see org.dllearner.core.AbstractReasonerComponent#getBooleanDatatypePropertiesImpl()
     */
    @Override
    protected Set<OWLDataProperty> getBooleanDatatypePropertiesImpl() throws ReasoningMethodUnsupportedException {
        return getDataPropertiesByRange(OWLAPIUtils.fixedDatatypes);
    }

    /* (non-Javadoc)
     * @see org.dllearner.core.AbstractReasonerComponent#getStringDatatypePropertiesImpl()
     */
    @Override
    protected Set<OWLDataProperty> getStringDatatypePropertiesImpl() throws ReasoningMethodUnsupportedException {
        return getDataPropertiesByRange(XSDVocabulary.STRING);
    }

    public Set<OWLProperty> getProperties(boolean inferType, String namespace) {
        Set<OWLProperty> properties = new HashSet<>();
        String query = "SELECT DISTINCT ?p ?type WHERE {?s ?p ?o."
                + (namespace != null ? ("FILTER(REGEX(?p,'^" + namespace + "'))") : "") + "OPTIONAL{?p a ?type.}}";
        ResultSet rs = executeSelectQuery(query);
        Multimap<String, String> uri2Types = HashMultimap.create();
        QuerySolution qs;
        while (rs.hasNext()) {
            qs = rs.next();
            String uri = qs.getResource("p").getURI();
            String type = "";
            if (qs.getResource("type") != null) {
                type = qs.getResource("type").getURI();
            }
            uri2Types.put(uri, type);
        }
        for (Entry<String, Collection<String>> entry : uri2Types.asMap().entrySet()) {
            String uri = entry.getKey();
            Collection<String> types = entry.getValue();
            if (types.contains(OWL.ObjectProperty.getURI()) && !types.contains(OWL.DatatypeProperty.getURI())) {
                properties.add(df.getOWLObjectProperty(IRI.create(uri)));
            } else if (!types.contains(OWL.ObjectProperty.getURI())
                    && types.contains(OWL.DatatypeProperty.getURI())) {
                properties.add(df.getOWLDataProperty(IRI.create(uri)));
            } else if (inferType) {
                //infer the type by values
                query = "SELECT ?o WHERE {?s <" + uri + "> ?o. } LIMIT 100";
                rs = executeSelectQuery(query);
                boolean op = true;
                boolean dp = true;
                RDFNode node;
                while (rs.hasNext()) {
                    node = rs.next().get("o");
                    op = node.isResource();
                    dp = node.isLiteral();
                }
                if (op && !dp) {
                    properties.add(df.getOWLObjectProperty(IRI.create(uri)));
                } else if (!op && dp) {
                    properties.add(df.getOWLDataProperty(IRI.create(uri)));
                } else {
                    //not possible to decide
                }
            }
        }
        return properties;
    }

    public Set<OWLProperty> getProperties(boolean inferType) {
        Set<OWLProperty> properties = new TreeSet<>();
        String query = "SELECT DISTINCT ?p ?type WHERE {?s ?p ?o. OPTIONAL{?p a ?type.}}";
        ResultSet rs = executeSelectQuery(query);
        Multimap<String, String> uri2Types = HashMultimap.create();
        QuerySolution qs;
        while (rs.hasNext()) {
            qs = rs.next();
            String uri = qs.getResource("p").getURI();
            String type = "";
            if (qs.getResource("type") != null) {
                type = qs.getResource("type").getURI();
            }
            uri2Types.put(uri, type);
        }
        for (Entry<String, Collection<String>> entry : uri2Types.asMap().entrySet()) {
            String uri = entry.getKey();
            Collection<String> types = entry.getValue();
            if (types.contains(OWL.ObjectProperty.getURI()) && !types.contains(OWL.DatatypeProperty.getURI())) {
                properties.add(df.getOWLObjectProperty(IRI.create(uri)));
            } else if (!types.contains(OWL.ObjectProperty.getURI())
                    && types.contains(OWL.DatatypeProperty.getURI())) {
                properties.add(df.getOWLDataProperty(IRI.create(uri)));
            } else if (inferType) {
                //infer the type by values
                query = "SELECT ?o WHERE {?s <" + uri + "> ?o. } LIMIT 100";
                rs = executeSelectQuery(query);
                boolean op = true;
                boolean dp = true;
                RDFNode node;
                while (rs.hasNext()) {
                    node = rs.next().get("o");
                    op = node.isResource();
                    dp = node.isLiteral();
                }
                if (op && !dp) {
                    properties.add(df.getOWLObjectProperty(IRI.create(uri)));
                } else if (!op && dp) {
                    properties.add(df.getOWLDataProperty(IRI.create(uri)));
                } else {
                    //not possible to decide
                }
            }
        }
        return properties;
    }

    /**
     * Returns a set of sibling classes, i.e. classes that are on the same level
     * in the class hierarchy.
     * @param cls the OWL class
     * @return the sibling classes
     */
    public Set<OWLClass> getSiblingClasses(OWLClass cls) {
        String query = SPARQLQueryUtils.SELECT_SIBLING_CLASSES_QUERY.replace("%s", cls.toStringID());
        ResultSet rs = executeSelectQuery(query);
        Set<OWLClass> siblings = asOWLEntities(EntityType.CLASS, rs, "var1");
        return siblings;
    }

    @Override
    public boolean hasTypeImpl(OWLClassExpression description, OWLIndividual individual) {
        if (description.isOWLThing()) { // owl:Thing -> TRUE
            return true;
        } else if (description.isOWLNothing()) { // owl:Nothing -> FALSE
            return false;
        } else if (!description.isAnonymous()) { // atomic classes
            String query = String.format("ASK {<%s> a <%s>}", individual.toStringID(),
                    description.asOWLClass().toStringID());
            boolean result = executeAskQuery(query);
            return result;
        } else { // complex class expressions
            //TODO use ASK queries
            SortedSet<OWLIndividual> individuals = getIndividuals(description, Collections.singleton(individual));
            return individuals.contains(individual);
            //         String queryBody = converter.convert("?ind", description);
            //         queryBody = queryBody.replace("?ind", "<" + individual.toStringID() + ">");
            //         String query = "ASK {" + queryBody + "}";
            //         // FIXME universal and cardinality restrictions do not work with ASK queries
        }
    }

    @Override
    public SortedSet<OWLIndividual> hasTypeImpl(OWLClassExpression description, Set<OWLIndividual> individuals) {
        SortedSet<OWLIndividual> allIndividuals = getIndividuals(description, individuals);
        //allIndividuals.retainAll(individuals);
        return allIndividuals;
    }

    @Override
    public SortedSet<OWLIndividual> getIndividualsImpl(OWLClassExpression description) {
        return getIndividuals(description, 0);
    }

    public SortedSet<OWLIndividual> getIndividuals(OWLClassExpression description, int limit,
            Set<OWLIndividual> indValues) {
        // we need to copy it to get something like A AND B from A AND A AND B
        description = duplicator.duplicateObject(description);

        SortedSet<OWLIndividual> individuals = new TreeSet<>();
        String query;

        if (indValues != null) {
            String tp = converter.convert("?ind", description);
            query = "SELECT DISTINCT ?ind WHERE { \n" + "VALUES ?ind { \n";
            for (OWLIndividual x : indValues) {
                query += "<" + x.toStringID() + "> ";
            }
            query += "}. \n " + tp + "\n}";
            //query = converter.asQuery("?ind", description).toString();
            //System.exit(1); // XXX
        } else {
            query = converter.asQuery("?ind", description, false).toString();//System.out.println(query);
        }
        if (limit != 0) {
            query += " LIMIT " + limit;
        }
        //      query = String.format(SPARQLQueryUtils.PREFIXES + " SELECT ?ind WHERE {?ind rdf:type/rdfs:subClassOf* <%s> .}", description.asOWLClass().toStringID());
        if (logger.isDebugEnabled()) {
            Thread.dumpStack();
            logger.debug(sparql_debug, "get individuals query: " + query);
        }
        ResultSet rs = executeSelectQuery(query);
        while (rs.hasNext()) {
            QuerySolution qs = rs.next();
            if (qs.get("ind").isURIResource()) {
                individuals.add(df.getOWLNamedIndividual(IRI.create(qs.getResource("ind").getURI())));
            }
        }
        logger.debug(sparql_debug, "get individuals result: " + individuals);
        return individuals;
    }

    public SortedSet<OWLIndividual> getIndividuals(OWLClassExpression description, int limit) {
        return getIndividuals(description, limit, null);
    }

    public SortedSet<OWLIndividual> getIndividuals(OWLClassExpression description, Set<OWLIndividual> indValues) {
        return getIndividuals(description, 0, indValues);
    }

    public int getIndividualsCount(OWLClassExpression description, int limit, Set<OWLIndividual> indValues) {
        description = duplicator.duplicateObject(description);

        String query;

        if (indValues != null) {
            String tp = converter.convert("?ind", description);
            query = "SELECT (COUNT(DISTINCT ?ind) as ?cnt) WHERE { \n" + "VALUES ?ind { \n";
            for (OWLIndividual x : indValues) {
                query += "<" + x.toStringID() + "> ";
            }
            query += "}. \n " + tp + "\n}";
        } else {
            query = converter.asQuery("?ind", description, true).toString();
            System.err.println(query);
            System.exit(1);
        }
        if (limit != 0) {
            query += " LIMIT " + limit;
        }
        if (logger.isDebugEnabled()) {
            logger.debug(sparql_debug, "get individuals query: " + query);
        }
        ResultSet rs = executeSelectQuery(query);
        while (rs.hasNext()) {
            QuerySolution qs = rs.next();
            if (qs.get("cnt").isLiteral()) {
                int ret = qs.get("cnt").asLiteral().getInt();
                logger.debug(sparql_debug, "result: " + ret);
                return ret;
            }
        }
        throw new Error("no result");
    }

    public int getIndividualsCount(OWLClassExpression description, int limit) {
        return getIndividualsCount(description, limit, null);
    }

    public int getIndividualsCount(OWLClassExpression description, Set<OWLIndividual> indValues) {
        return getIndividualsCount(description, 0, indValues);
    }

    /**
     * @param wantedClass the class to which the individuals must belong to
     * @param excludeClass the class to which the individuals must not belong to
     * @param limit the max. number of individuals
     * @return get individuals of class wantedClass excluding all individuals of type excludeClass
     */
    public SortedSet<OWLIndividual> getIndividualsExcluding(OWLClassExpression wantedClass,
            OWLClassExpression excludeClass, int limit) {
        if (wantedClass.isAnonymous()) {
            throw new UnsupportedOperationException("Only named classes are supported.");
        }
        SortedSet<OWLIndividual> individuals = new TreeSet<>();
        String query = "SELECT DISTINCT ?ind WHERE {" + "?ind a <" + ((OWLClass) wantedClass).toStringID() + "> . "
                + "FILTER NOT EXISTS { ?ind a <" + ((OWLClass) excludeClass).toStringID() + "> } }";
        if (limit != 0) {
            query += " LIMIT " + limit;
        }
        ResultSet rs = executeSelectQuery(query);
        QuerySolution qs;
        while (rs.hasNext()) {
            qs = rs.next();
            if (qs.get("ind").isURIResource()) {
                individuals.add(df.getOWLNamedIndividual(IRI.create(qs.getResource("ind").getURI())));
            }
        }
        return individuals;
    }

    //   /**
    //    * @param cls
    //    * @param limit
    //    * @return Random Individuals not including any of the input class individuals
    //    * @author sherif
    //    */
    //   public SortedSet<OWLIndividual> getRandomIndividuals(OWLClass cls, int limit) {
    //      SortedSet<OWLIndividual> individuals = new TreeSet<OWLIndividual>();
    //      String query =
    //            " SELECT DISTINCT ?ind WHERE {"+
    //                  "?ind ?p ?o ."+
    //                  "FILTER(NOT EXISTS { ?ind a <" + cls.toStringID() + "> } ) }";
    //      if(limit != 0) {
    //         query += " LIMIT " + limit;
    //      }
    //      ResultSet rs = executeSelectQuery(query);
    //      QuerySolution qs;
    //      while(rs.hasNext()){
    //         qs = rs.next();
    //         if(qs.get("ind").isURIResource()){
    //            individuals.add(df.getOWLNamedIndividual(IRI.create(qs.getResource("ind").getURI())));
    //         }
    //      }
    //      return individuals;
    //   }

    //   /**
    //    * @param cls
    //    * @param limit
    //    * @return Random Individuals not including any of the input classes individuals
    //    * @author sherif
    //    */
    //   public SortedSet<OWLIndividual> getRandomIndividuals(Set<OWLClass> cls, int limit) {
    //      SortedSet<OWLIndividual> individuals = new TreeSet<OWLIndividual>();
    //
    //      String filterStr="";
    //      for(OWLClass nc : cls){
    //         filterStr = filterStr.concat("FILTER(NOT EXISTS { ?ind a <").concat(nc.toStringID()).concat("> } ) ");
    //      }
    //
    //      String query =
    //            " SELECT DISTINCT ?ind WHERE {"+
    //                  "?ind a ?o .?o <http://www.w3.org/2000/01/rdf-schema#subClassOf> <http://www.w3.org/2002/07/owl#Class>"+
    //                  filterStr+ " }";
    //      if(limit != 0) {
    //         query += " LIMIT " + limit;
    //      }
    //
    //      System.out.println("!!!!!!!!!!!!!!!!!!!! "+query);
    //      ResultSet rs = executeSelectQuery(query);
    //      QuerySolution qs;
    //      while(rs.hasNext()){
    //         qs = rs.next();
    //         if(qs.get("ind").isURIResource()){
    //            individuals.add(df.getOWLNamedIndividual(IRI.create(qs.getResource("ind").getURI())));
    //         }
    //      }
    //      return individuals;
    //   }

    //   /**
    //    * @param cls
    //    * @param limit
    //    * @return Super class of the input class Individuals not including any of the input class individuals
    //    * @author sherif
    //    */
    //   public SortedSet<OWLIndividual> getSuperClassIndividuals(OWLClass cls, int limit) {
    //      SortedSet<OWLIndividual> individuals = new TreeSet<OWLIndividual>();
    //      Set<OWLClassExpression> superClasses = getSuperClasses(cls);
    //
    //      for(OWLClassExpression sup : superClasses){
    //         if(!sup.isAnonymous()) {
    //            String query = "SELECT DISTINCT ?ind WHERE { "
    //                  + "?ind a <" + sup.asOWLClass().toStringID() + "> . "
    //                  + "FILTER NOT EXISTS { ?ind a <" + cls.toStringID() + "> }  }";
    //            if(limit != 0) {
    //               query += " LIMIT " + limit/superClasses.size();
    //            }
    //
    //            System.out.println("----------------------------------------------  "+query);
    //
    //            ResultSet rs = executeSelectQuery(query);
    //            QuerySolution qs;
    //            while(rs.hasNext()){
    //               qs = rs.next();
    //               if(qs.get("ind").isURIResource()){
    //                  individuals.add(df.getOWLNamedIndividual(IRI.create(qs.getResource("ind").getURI())));
    //               }
    //            }
    //            System.out.println(individuals.size());
    //            System.out.println(individuals);
    //         }
    //      }
    //
    //      return individuals;
    //   }

    @Override
    public SortedSetTuple<OWLIndividual> doubleRetrievalImpl(OWLClassExpression description) {
        throw new UnsupportedOperationException();
    }

    @Override
    public Set<OWLIndividual> getRelatedIndividualsImpl(OWLIndividual individual,
            OWLObjectProperty objectProperty) {
        Set<OWLIndividual> individuals = new HashSet<>();
        String query = String.format("SELECT ?ind WHERE {<%s> <%s> ?ind, FILTER(isIRI(?ind))}",
                individual.toStringID(), objectProperty.toStringID());

        ResultSet rs = executeSelectQuery(query);
        QuerySolution qs;
        while (rs.hasNext()) {
            qs = rs.next();
            individuals.add(df.getOWLNamedIndividual(IRI.create(qs.getResource("ind").getURI())));
        }
        return individuals;
    }

    @Override
    public Set<OWLLiteral> getRelatedValuesImpl(OWLIndividual individual, OWLDataProperty datatypeProperty) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public Map<OWLObjectProperty, Set<OWLIndividual>> getObjectPropertyRelationshipsImpl(OWLIndividual individual) {
        Map<OWLObjectProperty, Set<OWLIndividual>> prop2individuals = new HashMap<>();
        String query = String.format(
                "SELECT ?prop ?ind WHERE {" + "<%s> ?prop ?ind."
                        + " FILTER(isIRI(?ind) && ?prop != <%s> && ?prop != <%s>)}",
                individual.toStringID(), RDF.type.getURI(), OWL.sameAs.getURI());

        ResultSet rs = executeSelectQuery(query);
        QuerySolution qs;
        Set<OWLIndividual> individuals;
        OWLObjectProperty property;
        OWLIndividual ind;
        while (rs.hasNext()) {
            qs = rs.next();
            ind = df.getOWLNamedIndividual(IRI.create(qs.getResource("ind").getURI()));
            property = df.getOWLObjectProperty(IRI.create(qs.getResource("prop").getURI()));
            individuals = prop2individuals.get(property);
            if (individuals == null) {
                individuals = new HashSet<>();
                prop2individuals.put(property, individuals);
            }
            individuals.add(ind);

        }
        return prop2individuals;
    }

    @Override
    public Map<OWLIndividual, SortedSet<OWLIndividual>> getPropertyMembersImpl(OWLObjectProperty objectProperty) {
        Map<OWLIndividual, SortedSet<OWLIndividual>> subject2objects = new HashMap<>();
        String query = String.format("SELECT ?s ?o WHERE {" + "?s <%s> ?o." + " FILTER(isIRI(?o))}",
                objectProperty.toStringID());

        ResultSet rs = executeSelectQuery(query);
        QuerySolution qs;
        OWLIndividual sub;
        OWLIndividual obj;
        SortedSet<OWLIndividual> objects;
        while (rs.hasNext()) {
            qs = rs.next();
            if (qs.getResource("s") == null || qs.getResource("s").getURI() == null) {
                logger.warn(sparql_debug, "The ?s is empty {} {}", query, qs);
                continue;
            }
            sub = df.getOWLNamedIndividual(IRI.create(qs.getResource("s").getURI()));
            obj = df.getOWLNamedIndividual(IRI.create(qs.getResource("o").getURI()));
            objects = subject2objects.get(sub);
            if (objects == null) {
                objects = new TreeSet<>();
                subject2objects.put(sub, objects);
            }
            objects.add(obj);

        }
        return subject2objects;
    }

    @Override
    public Map<OWLIndividual, SortedSet<OWLLiteral>> getDatatypeMembersImpl(OWLDataProperty dataProperty) {
        Map<OWLIndividual, SortedSet<OWLLiteral>> subject2objects = new HashMap<>();

        String query = String.format(SPARQLQueryUtils.SELECT_PROPERTY_RELATIONSHIPS_QUERY,
                dataProperty.toStringID());

        ResultSet rs = executeSelectQuery(query);
        QuerySolution qs;
        while (rs.hasNext()) {
            qs = rs.next();
            OWLIndividual sub = df.getOWLNamedIndividual(IRI.create(qs.getResource("var1").getURI()));
            OWLLiteral obj = OwlApiJenaUtils.getOWLLiteral(qs.getLiteral("var2"));
            SortedSet<OWLLiteral> objects = subject2objects.get(sub);
            if (objects == null) {
                objects = new TreeSet<>();
                subject2objects.put(sub, objects);
            }
            objects.add(obj);
        }
        return subject2objects;
    }

    private String datatypeSparqlFilter(Iterable<OWLDatatype> dts) {
        return Joiner.on(" || ").join(StreamSupport.stream(dts.spliterator(), false)
                .map(input -> "DATATYPE(?o) = <" + input.toStringID() + ">").collect(Collectors.toList()));
    }

    @Override
    public Map<OWLIndividual, SortedSet<Double>> getDoubleDatatypeMembersImpl(OWLDataProperty datatypeProperty) {
        Map<OWLIndividual, SortedSet<Double>> subject2objects = new HashMap<>();
        String query = "SELECT ?s ?o WHERE {" + String.format("?s <%s> ?o.", datatypeProperty.toStringID())
                + " FILTER(" + datatypeSparqlFilter(OWLAPIUtils.floatDatatypes) + ")}";

        ResultSet rs = executeSelectQuery(query);
        QuerySolution qs;
        OWLIndividual sub;
        Double obj;
        SortedSet<Double> objects;
        while (rs.hasNext()) {
            qs = rs.next();
            sub = df.getOWLNamedIndividual(IRI.create(qs.getResource("s").getURI()));
            Literal val = qs.getLiteral("o").asLiteral();
            if ("NAN".equals(val.getLexicalForm())) {
                // DBPedia bug
                obj = Double.NaN;
            } else {
                obj = val.getDouble();
            }
            //obj = qs.getLiteral("o").getDouble();
            objects = subject2objects.get(sub);
            if (objects == null) {
                objects = new TreeSet<>();
                subject2objects.put(sub, objects);
            }
            objects.add(obj);

        }
        return subject2objects;
    }

    @Override
    public Map<OWLIndividual, SortedSet<Integer>> getIntDatatypeMembersImpl(OWLDataProperty datatypeProperty) {
        Map<OWLIndividual, SortedSet<Integer>> subject2objects = new HashMap<>();
        String query = "SELECT ?s ?o WHERE {" + String.format("?s <%s> ?o.", datatypeProperty.toStringID())
                + " FILTER(" + datatypeSparqlFilter(OWLAPIUtils.intDatatypes) + ")}";

        ResultSet rs = executeSelectQuery(query);
        QuerySolution qs;
        OWLIndividual sub;
        Integer obj;
        SortedSet<Integer> objects;
        while (rs.hasNext()) {
            qs = rs.next();
            sub = df.getOWLNamedIndividual(IRI.create(qs.getResource("s").getURI()));
            obj = qs.getLiteral("o").getInt();
            objects = subject2objects.get(sub);
            if (objects == null) {
                objects = new TreeSet<>();
                subject2objects.put(sub, objects);
            }
            objects.add(obj);

        }
        return subject2objects;
    }

    @Override
    public Map<OWLIndividual, SortedSet<Boolean>> getBooleanDatatypeMembersImpl(OWLDataProperty datatypeProperty) {
        Map<OWLIndividual, SortedSet<Boolean>> subject2objects = new HashMap<>();
        String query = "SELECT ?s ?o WHERE {" + String.format("?s <%s> ?o.", datatypeProperty.toStringID())
                + " FILTER(" + datatypeSparqlFilter(OWLAPIUtils.fixedDatatypes) + ")}";

        ResultSet rs = executeSelectQuery(query);
        QuerySolution qs;
        OWLIndividual sub;
        Boolean obj;
        SortedSet<Boolean> objects;
        while (rs.hasNext()) {
            qs = rs.next();
            sub = df.getOWLNamedIndividual(IRI.create(qs.getResource("s").getURI()));
            obj = qs.getLiteral("o").getBoolean();
            objects = subject2objects.get(sub);
            if (objects == null) {
                objects = new TreeSet<>();
                subject2objects.put(sub, objects);
            }
            objects.add(obj);

        }
        return subject2objects;
    }

    @Override
    public SortedSet<OWLIndividual> getTrueDatatypeMembersImpl(OWLDataProperty datatypeProperty) {
        SortedSet<OWLIndividual> members = new TreeSet<>();
        String query = String.format(
                "SELECT ?ind WHERE {" + "?ind <%s> ?o."
                        + " FILTER(isLiteral(?o) && DATATYPE(?o) = <%s> && ?o = %s)}",
                datatypeProperty.toStringID(), XSD.BOOLEAN.toStringID(),
                "\"true\"^^<" + XSD.BOOLEAN.toStringID() + ">");

        ResultSet rs = executeSelectQuery(query);
        QuerySolution qs;
        while (rs.hasNext()) {
            qs = rs.next();
            members.add(df.getOWLNamedIndividual(IRI.create(qs.getResource("ind").getURI())));

        }
        return members;
    }

    @Override
    public SortedSet<OWLIndividual> getFalseDatatypeMembersImpl(OWLDataProperty datatypeProperty) {
        SortedSet<OWLIndividual> members = new TreeSet<>();
        String query = String.format(
                "SELECT ?ind WHERE {" + "?ind <%s> ?o."
                        + " FILTER(isLiteral(?o) && DATATYPE(?o) = <%s> && ?o = %s)}",
                datatypeProperty.toStringID(), XSD.BOOLEAN.toStringID(),
                "\"false\"^^<" + XSD.BOOLEAN.toStringID() + ">");

        ResultSet rs = executeSelectQuery(query);
        QuerySolution qs;
        while (rs.hasNext()) {
            qs = rs.next();
            members.add(df.getOWLNamedIndividual(IRI.create(qs.getResource("ind").getURI())));

        }
        return members;
    }

    @Override
    public Map<OWLIndividual, SortedSet<String>> getStringDatatypeMembersImpl(OWLDataProperty datatypeProperty) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public Set<OWLClass> getInconsistentClassesImpl() {
        throw new UnsupportedOperationException();
    }

    private OWLClassExpression computeDomain(OWLProperty property) {
        String query = String.format("SELECT ?domain WHERE {" + "<%s> <%s> ?domain. FILTER(isIRI(?domain))" + "}",
                property.toStringID(), RDFS.domain.getURI());

        try (QueryExecution qe = qef.createQueryExecution(query)) {
            ResultSet rs = qe.execSelect();
            SortedSet<OWLClassExpression> domains = new TreeSet<>();
            while (rs.hasNext()) {
                QuerySolution qs = rs.next();
                domains.add(df.getOWLClass(IRI.create(qs.getResource("domain").getURI())));
            }
            domains.remove(df.getOWLThing());
            if (domains.size() == 1) {
                return domains.first();
            } else if (domains.size() > 1) {
                return df.getOWLObjectIntersectionOf(domains);
            }
            return df.getOWLThing();
        } catch (Exception e) {
            logger.error("Failed to compute the domain for " + property + ".", e);
        }
        return null;
    }

    @Override
    public OWLClassExpression getDomainImpl(OWLObjectProperty property) {
        return propertyDomains.computeIfAbsent(property, k -> computeDomain(property));
    }

    public Set<OWLObjectProperty> getObjectPropertiesWithDomain(OWLClass domain) {
        Set<OWLObjectProperty> properties = new TreeSet<>();

        String query = "SELECT ?p WHERE {?p <http://www.w3.org/2000/01/rdf-schema#domain> <" + domain.toStringID()
                + ">.}";
        ResultSet rs = executeSelectQuery(query);
        QuerySolution qs;
        while (rs.hasNext()) {
            qs = rs.next();
            properties.add(df.getOWLObjectProperty(IRI.create(qs.getResource("p").getURI())));
        }

        return properties;
    }

    public Set<OWLObjectProperty> getObjectProperties(OWLClass cls) {
        Set<OWLObjectProperty> properties = new TreeSet<>();

        String query = "SELECT DISTINCT ?p WHERE {?s a <" + cls.toStringID() + ">. ?s ?p ?o}";
        ResultSet rs = executeSelectQuery(query);
        QuerySolution qs;
        while (rs.hasNext()) {
            qs = rs.next();
            properties.add(df.getOWLObjectProperty(IRI.create(qs.getResource("p").getURI())));
        }

        return properties;
    }

    public SortedSet<OWLClass> getDomains(OWLObjectProperty objectProperty) {
        String query = String.format("SELECT ?domain WHERE {" + "<%s> <%s> ?domain. FILTER(isIRI(?domain))" + "}",
                objectProperty.toStringID(), RDFS.domain.getURI());

        ResultSet rs = executeSelectQuery(query);
        QuerySolution qs;
        SortedSet<OWLClass> domains = new TreeSet<>();
        while (rs.hasNext()) {
            qs = rs.next();
            domains.add(df.getOWLClass(IRI.create(qs.getResource("domain").getURI())));

        }
        return domains;
    }

    @Override
    public OWLClassExpression getDomainImpl(OWLDataProperty property) {
        return propertyDomains.computeIfAbsent(property, k -> computeDomain(property));
    }

    @Override
    public OWLClassExpression getRangeImpl(OWLObjectProperty property) {
        return objectPropertyRanges.computeIfAbsent(property, k -> {
            String query = String.format("SELECT ?range WHERE {" + "<%s> <%s> ?range. FILTER(isIRI(?range))" + "}",
                    property.toStringID(), RDFS.range.getURI());

            try (QueryExecution qe = qef.createQueryExecution(query)) {
                ResultSet rs = qe.execSelect();
                SortedSet<OWLClassExpression> ranges = new TreeSet<>();
                while (rs.hasNext()) {
                    QuerySolution qs = rs.next();
                    ranges.add(df.getOWLClass(IRI.create(qs.getResource("range").getURI())));
                }
                ranges.remove(df.getOWLThing());
                if (ranges.size() == 1) {
                    return ranges.first();
                } else if (ranges.size() > 1) {
                    return df.getOWLObjectIntersectionOf(ranges);
                }
                return df.getOWLThing();
            } catch (Exception e) {
                logger.error("Failed to compute range for " + property, e);
            }
            return null;
        });
    }

    public SortedSet<OWLClass> getRanges(OWLObjectProperty objectProperty) {
        String query = String.format("SELECT ?range WHERE {" + "<%s> <%s> ?range. FILTER(isIRI(?range))" + "}",
                objectProperty.toStringID(), RDFS.range.getURI());

        ResultSet rs = executeSelectQuery(query);
        QuerySolution qs;
        SortedSet<OWLClass> ranges = new TreeSet<>();
        while (rs.hasNext()) {
            qs = rs.next();
            ranges.add(df.getOWLClass(IRI.create(qs.getResource("range").getURI())));
        }
        return ranges;
    }

    public boolean isObjectProperty(String propertyURI) {
        String query = String.format("ASK {<%s> a <%s>}", propertyURI, OWL.ObjectProperty.getURI());
        boolean isObjectProperty = executeAskQuery(query);
        return isObjectProperty;
    }

    public boolean isObjectProperty(String propertyURI, boolean analyzeData) {
        String query = String.format("ASK {<%s> a <%s>}", propertyURI, OWL.ObjectProperty.getURI());
        boolean isObjectProperty = executeAskQuery(query);
        if (!isObjectProperty && analyzeData) {
            query = String.format("ASK {?s <%s> ?o.FILTER(isURI(?o))}", propertyURI);
            isObjectProperty = executeAskQuery(query);
        }
        return isObjectProperty;
    }

    public boolean isDataProperty(String propertyURI) {
        if (propertyURI.equals("http://www.w3.org/2000/01/rdf-schema#label"))
            return true;
        String query = String.format("ASK {<%s> a <%s>}", propertyURI, OWL.DatatypeProperty.getURI());
        boolean isDataProperty = executeAskQuery(query);
        return isDataProperty;
    }

    public boolean isDataProperty(String propertyURI, boolean analyzeData) {
        if (propertyURI.equals("http://www.w3.org/2000/01/rdf-schema#label"))
            return true;
        String query = String.format("ASK {<%s> a <%s>}", propertyURI, OWL.DatatypeProperty.getURI());
        boolean isDataProperty = executeAskQuery(query);
        if (!isDataProperty && analyzeData) {
            query = String.format("ASK {?s <%s> ?o.FILTER(isLITERAL(?o))}", propertyURI);
            isDataProperty = executeAskQuery(query);
        }
        return isDataProperty;
    }

    public int getIndividualsCount(OWLClass cls) {
        String query = String.format("SELECT (COUNT(?s) AS ?cnt) WHERE {?s a <%s>.}", cls.toStringID());
        ResultSet rs = executeSelectQuery(query);
        int cnt = rs.next().get(rs.getResultVars().get(0)).asLiteral().getInt();
        return cnt;

    }

    public int getPropertyCount(OWLObjectProperty property) {
        String query = String.format("SELECT (COUNT(*) AS ?cnt) WHERE {?s <%s> ?o.}", property.toStringID());
        ResultSet rs = executeSelectQuery(query);
        int cnt = rs.next().get(rs.getResultVars().get(0)).asLiteral().getInt();
        return cnt;

    }

    public SortedSet<OWLObjectProperty> getInverseObjectProperties(OWLObjectProperty property) {
        SortedSet<OWLObjectProperty> inverseObjectProperties = new TreeSet<>();
        String query = "SELECT ?p WHERE {" + "{<%p> <%ax> ?p.} UNION {?p <%ax> <%p>}}"
                .replace("%p", property.toStringID()).replace("%ax", OWL.inverseOf.getURI());
        ResultSet rs = executeSelectQuery(query);
        QuerySolution qs;
        while (rs.hasNext()) {
            qs = rs.next();
            inverseObjectProperties.add(df.getOWLObjectProperty(IRI.create(qs.getResource("p").getURI())));

        }
        return inverseObjectProperties;
    }

    @Override
    public OWLDataRange getRangeImpl(OWLDataProperty datatypeProperty) {
        String query = String.format("SELECT ?range WHERE {" + "<%s> <%s> ?range. FILTER(isIRI(?range))" + "}",
                datatypeProperty.toStringID(), RDFS.range.getURI());

        ResultSet rs = executeSelectQuery(query);
        QuerySolution qs;
        OWLDataRange range = null;
        while (rs.hasNext()) {
            qs = rs.next();
            range = df.getOWLDatatype(IRI.create(qs.getResource("range").getURI()));

        }
        return range;
    }

    @Override
    public boolean isSuperClassOfImpl(OWLClassExpression superClass, OWLClassExpression subClass) {
        if (subClass.isAnonymous() || superClass.isAnonymous()) {
            //         throw new IllegalArgumentException("Only named classes are supported.");
            return false;
        }
        String query = String.format("ASK {<%s> <%s> <%s>.}", ((OWLClass) subClass).toStringID(),
                RDFS.subClassOf.getURI(), ((OWLClass) superClass).toStringID());
        boolean superClassOf = executeAskQuery(query);
        return superClassOf;
    }

    @Override
    public boolean isEquivalentClassImpl(OWLClassExpression class1, OWLClassExpression class2) {
        if (class1.isAnonymous() || class2.isAnonymous()) {
            //         throw new IllegalArgumentException("Only named classes are supported.");
            return false;
        }
        String query = String.format("ASK {<%s> <%s> <%s>.}", ((OWLClass) class1).toStringID(),
                OWL.equivalentClass.getURI(), ((OWLClass) class2).toStringID());
        boolean equivalentClass = executeAskQuery(query);
        return equivalentClass;
    }

    @Override
    public Set<OWLClassExpression> getAssertedDefinitions(OWLClass cls) {
        // currently we are not able to do this because of the blank node style representation of complex class expressions in RDF
        return Collections.emptySet();
        //      Set<OWLClassExpression> definitions = new HashSet<OWLClassExpression>();
        //      String query = String.format(SPARQLQueryUtils.SELECT_EQUIVALENT_CLASSES_QUERY, cls.toStringID(), cls.toStringID());
        //
        //      ResultSet rs = executeSelectQuery(query);
        //      QuerySolution qs;
        //      while(rs.hasNext()){
        //         qs = rs.next();
        ////         definitions.add(df.getOWLClass(IRI.create(qs.getResource("class").getURI())));
        //      }
        //      return definitions;
    }

    @Override
    public Set<OWLClassExpression> isSuperClassOfImpl(Set<OWLClassExpression> superClasses,
            OWLClassExpression subClasses) {
        // TODO Auto-generated method stub
        return null;
    }

    public SortedSet<OWLClassExpression> getMostGeneralClasses() {
        return hierarchy.getMostGeneralClasses();
    }

    public SortedSet<OWLClass> getMostSpecificClasses() {
        SortedSet<OWLClass> classes = new TreeSet<>();
        String query = "SELECT ?cls WHERE {?cls a <http://www.w3.org/2002/07/owl#Class>. "
                + "FILTER NOT EXISTS{?sub <http://www.w3.org/2000/01/rdf-schema#subClassOf> ?cls. FILTER(?sub != <http://www.w3.org/2002/07/owl#Nothing>)}}";
        ResultSet rs = executeSelectQuery(query);
        QuerySolution qs;
        while (rs.hasNext()) {
            qs = rs.next();
            classes.add(df.getOWLClass(IRI.create(qs.getResource("cls").getURI())));
        }
        return classes;
    }

    @Override
    public SortedSet<OWLClassExpression> getSuperClassesImpl(OWLClassExpression description) {
        String query;
        if (description.isAnonymous()) {
            throw new IllegalArgumentException("Only named classes are supported.");
        } else if (description.isOWLThing()) {
            return Sets.newTreeSet();
        } else if (description.isOWLNothing()) {
            query = SPARQLQueryUtils.SELECT_LEAF_CLASSES_OWL;
        } else {
            query = String.format(SPARQLQueryUtils.SELECT_DIRECT_SUPERCLASS_OF_QUERY,
                    description.asOWLClass().toStringID());
        }

        ResultSet rs = executeSelectQuery(query);

        SortedSet<OWLClass> superClasses = asOWLEntities(EntityType.CLASS, rs, "var1");
        superClasses.remove(description);
        //      System.out.println("Sup(" + description + "):" + superClasses);
        return new TreeSet<OWLClassExpression>(superClasses);
    }

    public SortedSet<OWLClassExpression> getSuperClasses(OWLClassExpression description, boolean direct) {
        if (description.isAnonymous()) {
            throw new IllegalArgumentException("Only named classes are supported.");
        }
        SortedSet<OWLClassExpression> superClasses = new TreeSet<>();
        String query;
        if (direct) {
            query = String.format(
                    "SELECT ?sup {<%s> <http://www.w3.org/2000/01/rdf-schema#subClassOf> ?sup. FILTER(isIRI(?sup))}",
                    description.asOWLClass().toStringID());
        } else {
            query = String.format(
                    "SELECT ?sup {<%s> <http://www.w3.org/2000/01/rdf-schema#subClassOf>* ?sup. FILTER(isIRI(?sup))}",
                    description.asOWLClass().toStringID());
        }
        ResultSet rs = executeSelectQuery(query);
        QuerySolution qs;
        while (rs.hasNext()) {
            qs = rs.next();
            superClasses.add(df.getOWLClass(IRI.create(qs.getResource("sup").getURI())));
        }
        superClasses.remove(description);
        System.out.println("Sup(" + description + "):" + superClasses);
        return superClasses;
    }

    @Override
    public SortedSet<OWLClassExpression> getSubClassesImpl(OWLClassExpression description) {
        return getSubClasses(description, true);
    }

    public SortedSet<OWLClassExpression> getSubClasses(OWLClassExpression description, boolean direct) {
        if (description.isAnonymous()) {
            throw new IllegalArgumentException("Only named classes are supported.");
        }
        SortedSet<OWLClassExpression> subClasses = new TreeSet<>();

        String query;
        if (description.isOWLThing()) {
            query = SPARQLQueryUtils.SELECT_TOP_LEVEL_OWL_CLASSES;
        } else {
            query = String.format(SPARQLQueryUtils.SELECT_SUBCLASS_OF_QUERY, description.asOWLClass().toStringID());
            if (direct) {

            } else {

            }
        }

        ResultSet rs = executeSelectQuery(query);
        subClasses.addAll(asOWLEntities(EntityType.CLASS, rs, "var1"));

        subClasses.remove(description);
        subClasses.remove(df.getOWLNothing());
        //      System.out.println("Sub(" + description + "):" + subClasses);
        return new TreeSet<>(subClasses);
    }

    public boolean isSuperClassOf(OWLClass sup, OWLClass sub, boolean direct) {
        String query = direct ? SPARQLQueryUtils.SELECT_SUPERCLASS_OF_QUERY
                : SPARQLQueryUtils.SELECT_SUPERCLASS_OF_QUERY_RDFS;
        query = String.format(query, sub.toStringID());
        ResultSet rs = executeSelectQuery(query);
        SortedSet<OWLClass> superClasses = asOWLEntities(EntityType.CLASS, rs, "var1");
        return superClasses.contains(sup);
    }

    @Override
    public SortedSet<OWLObjectProperty> getSuperPropertiesImpl(OWLObjectProperty objectProperty) {
        SortedSet<OWLObjectProperty> properties = new TreeSet<>();
        String query = String.format(SPARQLQueryUtils.SELECT_SUPERPROPERTY_OF_QUERY, objectProperty.toStringID());
        ResultSet rs = executeSelectQuery(query);
        QuerySolution qs;
        while (rs.hasNext()) {
            qs = rs.next();
            properties.add(df.getOWLObjectProperty(IRI.create(qs.getResource("var1").getURI())));
        }
        properties.remove(objectProperty);
        properties.remove(df.getOWLTopObjectProperty());
        return properties;
    }

    @Override
    public SortedSet<OWLObjectProperty> getSubPropertiesImpl(OWLObjectProperty objectProperty) {
        SortedSet<OWLObjectProperty> properties = new TreeSet<>();
        String query = String.format(SPARQLQueryUtils.SELECT_SUBPROPERTY_OF_QUERY, objectProperty.toStringID());
        ResultSet rs = executeSelectQuery(query);
        QuerySolution qs;
        while (rs.hasNext()) {
            qs = rs.next();
            properties.add(df.getOWLObjectProperty(IRI.create(qs.getResource("var1").getURI())));
        }
        properties.remove(objectProperty);
        properties.remove(df.getOWLBottomObjectProperty());
        return properties;
    }

    public SortedSet<OWLObjectProperty> getEquivalentProperties(OWLObjectProperty objectProperty) {
        SortedSet<OWLObjectProperty> properties = new TreeSet<>();
        String query = String.format(SPARQLQueryUtils.SELECT_EQUIVALENT_PROPERTIES_QUERY,
                objectProperty.toStringID(), objectProperty.toStringID());
        ResultSet rs = executeSelectQuery(query);
        QuerySolution qs;
        while (rs.hasNext()) {
            qs = rs.next();
            properties.add(df.getOWLObjectProperty(IRI.create(qs.getResource("var1").getURI())));
        }
        return properties;
    }

    public SortedSet<OWLObjectProperty> getDisjointProperties(OWLObjectProperty objectProperty) {
        SortedSet<OWLObjectProperty> properties = new TreeSet<>();
        String query = String.format(SPARQLQueryUtils.SELECT_DISJOINT_PROPERTIES_QUERY, objectProperty.toStringID(),
                objectProperty.toStringID());
        ResultSet rs = executeSelectQuery(query);
        QuerySolution qs;
        while (rs.hasNext()) {
            qs = rs.next();
            properties.add(df.getOWLObjectProperty(IRI.create(qs.getResource("var1").getURI())));
        }
        return properties;
    }

    public SortedSet<OWLDataProperty> getEquivalentProperties(OWLDataProperty objectProperty) {
        SortedSet<OWLDataProperty> superProperties = new TreeSet<>();
        String query = String.format(SPARQLQueryUtils.SELECT_EQUIVALENT_PROPERTIES_QUERY,
                objectProperty.toStringID(), objectProperty.toStringID());
        ResultSet rs = executeSelectQuery(query);
        QuerySolution qs;
        while (rs.hasNext()) {
            qs = rs.next();
            superProperties.add(df.getOWLDataProperty(IRI.create(qs.getResource("var1").getURI())));
        }
        return superProperties;
    }

    @Override
    public SortedSet<OWLDataProperty> getSuperPropertiesImpl(OWLDataProperty dataProperty) {
        SortedSet<OWLDataProperty> properties = new TreeSet<>();
        String query = String.format(SPARQLQueryUtils.SELECT_SUPERPROPERTY_OF_QUERY, dataProperty.toStringID());
        ResultSet rs = executeSelectQuery(query);
        QuerySolution qs;
        while (rs.hasNext()) {
            qs = rs.next();
            properties.add(df.getOWLDataProperty(IRI.create(qs.getResource("var1").getURI())));
        }
        properties.remove(dataProperty);
        properties.remove(df.getOWLTopDataProperty());
        return properties;
    }

    @Override
    public SortedSet<OWLDataProperty> getSubPropertiesImpl(OWLDataProperty dataProperty) {
        SortedSet<OWLDataProperty> properties = new TreeSet<>();
        String query = String.format(SPARQLQueryUtils.SELECT_SUPERPROPERTY_OF_QUERY, dataProperty.toStringID());
        ResultSet rs = executeSelectQuery(query);
        QuerySolution qs;
        while (rs.hasNext()) {
            qs = rs.next();
            properties.add(df.getOWLDataProperty(IRI.create(qs.getResource("var1").getURI())));
        }
        properties.remove(dataProperty);
        properties.remove(df.getOWLBottomDataProperty());
        return properties;
    }

    public SortedSet<OWLDataProperty> getDisjointProperties(OWLDataProperty dataProperty) {
        SortedSet<OWLDataProperty> properties = new TreeSet<>();
        String query = String.format(SPARQLQueryUtils.SELECT_DISJOINT_PROPERTIES_QUERY, dataProperty.toStringID(),
                dataProperty.toStringID());
        ResultSet rs = executeSelectQuery(query);
        QuerySolution qs;
        while (rs.hasNext()) {
            qs = rs.next();
            properties.add(df.getOWLDataProperty(IRI.create(qs.getResource("var1").getURI())));
        }
        properties.remove(dataProperty);
        return properties;
    }

    /* (non-Javadoc)
     * @see org.dllearner.core.AbstractReasonerComponent#getObjectPropertyDomains()
     */
    @Override
    public Map<OWLObjectProperty, OWLClassExpression> getObjectPropertyDomains() {
        Map<OWLObjectProperty, OWLClassExpression> result = new HashMap<>();

        String query = SPARQLQueryUtils.PREFIXES
                + "SELECT ?p ?dom WHERE {?p a owl:ObjectProperty . OPTIONAL{?p rdfs:domain ?dom .}}";
        ResultSet rs = executeSelectQuery(query);
        while (rs.hasNext()) {
            QuerySolution qs = rs.next();
            OWLObjectProperty op = df.getOWLObjectProperty(IRI.create(qs.getResource("p").getURI()));

            // default domain is owl:Thing
            OWLClassExpression domain = df.getOWLThing();
            if (qs.get("dom") != null) {
                if (qs.get("dom").isURIResource()) {
                    domain = df.getOWLClass(IRI.create(qs.getResource("dom").getURI()));

                } else {
                    logger.warn("Can not resolve complex domain for object property " + op);
                }
            }
            result.put(op, domain);
        }
        return result;
    }

    /* (non-Javadoc)
     * @see org.dllearner.core.AbstractReasonerComponent#getObjectPropertyRanges()
     */
    @Override
    public Map<OWLObjectProperty, OWLClassExpression> getObjectPropertyRanges() {
        Map<OWLObjectProperty, OWLClassExpression> result = new HashMap<>();

        String query = SPARQLQueryUtils.PREFIXES
                + "SELECT ?p ?ran WHERE {?p a owl:ObjectProperty . OPTIONAL{?p rdfs:range ?ran .}}";
        ResultSet rs = executeSelectQuery(query);
        while (rs.hasNext()) {
            QuerySolution qs = rs.next();
            OWLObjectProperty op = df.getOWLObjectProperty(IRI.create(qs.getResource("p").getURI()));

            // default range is owl:Thing
            OWLClassExpression range = df.getOWLThing();
            if (qs.get("ran") != null) {
                if (qs.get("ran").isURIResource()) {
                    range = df.getOWLClass(IRI.create(qs.getResource("ran").getURI()));
                } else {
                    logger.warn("Can not resolve complex range for object property " + op);
                }
            }
            result.put(op, range);
        }
        return result;
    }

    /* (non-Javadoc)
     * @see org.dllearner.core.AbstractReasonerComponent#getDataPropertyDomains()
     */
    @Override
    public Map<OWLDataProperty, OWLClassExpression> getDataPropertyDomains() {
        Map<OWLDataProperty, OWLClassExpression> result = new HashMap<>();

        String query = SPARQLQueryUtils.PREFIXES
                + "SELECT ?p ?dom WHERE {?p a owl:DatatypeProperty . OPTIONAL{?p rdfs:domain ?dom .}}";
        ResultSet rs = executeSelectQuery(query);
        while (rs.hasNext()) {
            QuerySolution qs = rs.next();
            OWLDataProperty dp = df.getOWLDataProperty(IRI.create(qs.getResource("p").getURI()));

            // default domain is owl:Thing
            OWLClassExpression domain = df.getOWLThing();
            if (qs.get("dom") != null) {
                if (qs.get("dom").isURIResource()) {
                    domain = df.getOWLClass(IRI.create(qs.getResource("dom").getURI()));

                } else {
                    logger.warn("Can not resolve complex domain for data property " + dp);
                }
            }
            result.put(dp, domain);
        }
        return result;
    }

    /**
     * Convert a SPARQL resultset into OWL entities based on the given entity type.
     * @param entityType the entity type
     * @param rs the Jena resultset
     * @param var the var name in the resultset
     * @return a set of entities of the given entity type
     */
    private <E extends OWLEntity> SortedSet<E> asOWLEntities(EntityType<E> entityType, ResultSet rs, String var) {
        Collection<IRI> entityIRIs = new HashSet<>();
        while (rs.hasNext()) {
            QuerySolution qs = rs.next();
            Resource resource = qs.getResource(var);
            if (resource.isURIResource()) {
                entityIRIs.add(IRI.create(resource.getURI()));
            }
        }
        return asOWLEntities(entityType, entityIRIs);
    }

    /**
     * Convert a collection of IRIs into OWL entities based on the given entity type.
     * @param entityType the entity type
     * @param entityIRIs the entity IRIs
     * @return a set of entities of the given entity type
     */
    private <E extends OWLEntity> SortedSet<E> asOWLEntities(EntityType<E> entityType, Collection<IRI> entityIRIs) {
        SortedSet<E> entities = new TreeSet<>();
        for (IRI iri : entityIRIs) {
            if (!iri.isReservedVocabulary()) {
                entities.add(df.getOWLEntity(entityType, iri));
            }
        }

        // remove top and bottom entities
        entities.remove(df.getOWLThing());
        entities.remove(df.getOWLNothing());
        entities.remove(df.getOWLTopObjectProperty());
        entities.remove(df.getOWLBottomObjectProperty());
        entities.remove(df.getOWLTopDataProperty());
        entities.remove(df.getOWLBottomDataProperty());

        return entities;
    }

    protected ResultSet executeSelectQuery(String queryString, long timeout, TimeUnit timeoutUnits) {
        logger.trace("Sending query \n {}", queryString);//System.out.println(queryString);
        try (QueryExecution qe = qef.createQueryExecution(queryString)) {
            qe.setTimeout(timeout, timeoutUnits);
            ResultSet rs = qe.execSelect();
            return ResultSetFactory.copyResults(rs);
        } catch (QueryExceptionHTTP e) {
            throw new QueryExceptionHTTP("Error sending query \"" + queryString + "\" to endpoint " + qef.getId(),
                    e);
        } catch (Exception e) {
            throw new RuntimeException("Error sending query \"" + queryString + "\" to endpoint " + qef.getId(), e);
        }
    }

    protected ResultSet executeSelectQuery(String queryString) {
        return executeSelectQuery(queryString, -1, TimeUnit.MILLISECONDS);
    }

    protected boolean executeAskQuery(String queryString) {
        logger.trace("Sending query \n {}", queryString);
        QueryExecution qe = qef.createQueryExecution(queryString);
        boolean ret = qe.execAsk();
        qe.close();
        return ret;
    }

    /**
     * @return TRUE if the class hierarchy was computed before, otherwise FALSE
     */
    public boolean isPrepared() {
        return hierarchy != null;
    }

    public boolean supportsSPARQL1_1() {
        try {
            String query = "SELECT ?s WHERE {?s a <http://foo.org/A> . FILTER NOT EXISTS {?s a <http://foo.org/B>}} LIMIT 1";
            ResultSet rs = executeSelectQuery(query);
            return true;
        } catch (Exception e) {
            logger.error("Endpoint does not support SPARQL 1.1, e.g. FILTER NOT EXISTS", e);
        }
        return false;
    }

    /* (non-Javadoc)
     * @see org.dllearner.core.BaseReasoner#getBaseURI()
     */
    @Override
    public String getBaseURI() {
        return null;
    }

    /* (non-Javadoc)
     * @see org.dllearner.core.BaseReasoner#getPrefixes()
     */
    @Override
    public Map<String, String> getPrefixes() {
        return null;
    }

    /* (non-Javadoc)
     * @see org.dllearner.core.AbstractReasonerComponent#remainsSatisfiableImpl(org.semanticweb.owlapi.model.OWLAxiom)
     */
    @Override
    protected boolean remainsSatisfiableImpl(OWLAxiom axiom) throws ReasoningMethodUnsupportedException {
        return true;
    }

    /* (non-Javadoc)
     * @see org.dllearner.core.AbstractReasonerComponent#getReasonerType()
     */
    @Override
    public ReasonerType getReasonerType() {
        return ReasonerType.SPARQL_NATIVE;
    }

    /* (non-Javadoc)
     * @see org.dllearner.core.AbstractReasonerComponent#releaseKB()
     */
    @Override
    public void releaseKB() {
    }

    public boolean isLaxMode() {
        return laxMode;
    }

    public void setLaxMode(boolean laxMode) {
        this.laxMode = laxMode;
    }

    public boolean isUseGenericSplitsCode() {
        return useGenericSplitsCode;
    }

    public void setUseGenericSplitsCode(boolean useGenericSplitsCode) {
        this.useGenericSplitsCode = useGenericSplitsCode;
    }

    /* (non-Javadoc)
     * @see org.dllearner.core.AbstractReasonerComponent#getDatatype(org.semanticweb.owlapi.model.OWLDataProperty)
     */
    @Override
    public OWLDatatype getDatatype(OWLDataProperty dp) {
        OWLDataRange range = getRangeImpl(dp);
        if (range != null && range.isDatatype()) {
            return range.asOWLDatatype();
        }
        return XSD.STRING;
    }

    /* (non-Javadoc)
     * @see org.dllearner.core.AbstractReasonerComponent#setSynchronized()
     */
    @Override
    @NoConfigOption
    public void setSynchronized() {
        throw new NotImplementedException("Method setSynchronized() not implemented yet!");
    }

    public boolean isUseValueLists() {
        return useValueLists;
    }

    public void setUseValueLists(boolean useValueLists) {
        this.useValueLists = useValueLists;
    }

}