/* * Copyright (c) 2009-2012 Clark & Parsia, LLC. <http://www.clarkparsia.com> * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.clarkparsia.empire.ds; import com.complexible.common.base.Functions2; import com.clarkparsia.empire.ds.impl.TripleSourceAdapter; import com.clarkparsia.empire.Dialect; import com.clarkparsia.empire.Empire; import com.clarkparsia.empire.util.EmpireUtil; import com.clarkparsia.empire.impl.serql.SerqlDialect; import com.clarkparsia.empire.impl.sparql.ARQSPARQLDialect; import com.complexible.common.openrdf.model.Graphs; import com.google.common.collect.Collections2; import com.google.common.collect.Iterables; import com.google.common.collect.Sets; import com.google.common.base.Function; import org.openrdf.model.Resource; import org.openrdf.model.Graph; import org.openrdf.model.Value; import org.openrdf.model.BNode; import org.openrdf.model.vocabulary.RDF; import org.openrdf.query.BindingSet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Collection; import java.util.Collections; /** * <p>Collection of utility methods for working with Empire DataSources</p> * * @author Michael Grove * * @since 0.7 * @version 0.7.1 * * @see DataSource * @see TripleSource */ public final class DataSourceUtil { /** * The logger */ private static final Logger LOGGER = LoggerFactory.getLogger(Empire.class.getName()); /** * No instances */ private DataSourceUtil() { } /** * <p>Returns the given {@link DataSource} as a {@link TripleSource}. If the DataSource does not natively support * the interface, a wrapper is provided that delegates the triple level calls to SPARQL queries.</p> * @param theSource the source * @return the DataSource as a TripleSource. * @see TripleSourceAdapter * @throws DataSourceException if the TripleSource cannot be created. */ public static TripleSource asTripleSource(DataSource theSource) throws DataSourceException { if (theSource == null) { throw new DataSourceException("Cannot create triple source from null data source"); } if (theSource instanceof TripleSource) { return (TripleSource) theSource; } else { return new TripleSourceAdapter(theSource); } } /** * Do a poor-man's describe on the given resource, querying its context if that is supported, or otherwise * querying the graph in general. * @param theSource the {@link com.clarkparsia.empire.ds.DataSource} to query * @param theObj the object to do the "describe" operation on * @return all the statements about the given object * @throws QueryException if there is an error while querying for the graph */ public static Graph describe(DataSource theSource, Object theObj) throws QueryException { String aNG = null; if (EmpireUtil.asSupportsRdfId(theObj).getRdfId() == null) { return Graphs.newGraph(); } if (theSource instanceof SupportsNamedGraphs && EmpireUtil.hasNamedGraphSpecified(theObj)) { java.net.URI aURI = EmpireUtil.getNamedGraph(theObj); if (aURI != null) { aNG = aURI.toString(); } } Dialect aDialect = theSource.getQueryFactory().getDialect(); Resource aResource = EmpireUtil.asResource(EmpireUtil.asSupportsRdfId(theObj)); // bnode instabilty in queries will just yield either a parse error or incorrect query results because the bnode // will get treated as a variable, and it will just grab the entire database, which is not what we want if (aResource instanceof BNode && !(aDialect instanceof ARQSPARQLDialect)) { return Graphs.newGraph(); } // TODO: if source supports describe queries, use that. String aSPARQL = "construct {?s ?p ?o}\n" + (aNG == null ? "" : "from <" + aNG + ">\n") + "where {?s ?p ?o. filter(?s = " + aDialect.asQueryString(aResource) + ") }"; String aSeRQL = "construct {s} p {o}\n" + (aNG == null ? "from\n" : "from context <" + aNG + ">\n") + "{s} p {o} where s = " + aDialect.asQueryString(aResource) + ""; Graph aGraph; if (theSource.getQueryFactory().getDialect() instanceof SerqlDialect) { aGraph = theSource.graphQuery(aSeRQL); } else { // fall back on sparql aGraph = theSource.graphQuery(aSPARQL); } return aGraph; } /** * Do a poor-man's ask on the given resource to see if any triples using the resource (as the subject) exist, * querying its context if that is supported, or otherwise querying the graph in general. * @param theSource the {@link com.clarkparsia.empire.ds.DataSource} to query * @param theObj the object to do the "ask" operation on * @return true if there are statements about the object, false otherwise * @throws QueryException if there is an error while querying for the graph */ public static boolean exists(DataSource theSource, Object theObj) throws QueryException { String aNG = null; if (EmpireUtil.asSupportsRdfId(theObj).getRdfId() == null) { return false; } if (theSource instanceof SupportsNamedGraphs && EmpireUtil.hasNamedGraphSpecified(theObj)) { java.net.URI aURI = EmpireUtil.getNamedGraph(theObj); if (aURI != null) { aNG = aURI.toString(); } } Dialect aDialect = theSource.getQueryFactory().getDialect(); String aSPARQL = "select distinct ?s\n" + (aNG == null ? "" : "from <" + aNG + ">\n") + "where {?s ?p ?o. filter(?s = " + aDialect.asQueryString(EmpireUtil.asResource(EmpireUtil.asSupportsRdfId(theObj))) + ") } limit 1"; String aSeRQL = "select distinct s\n" + (aNG == null ? "from\n" : "from context <" + aNG + ">\n") + "{s} p {o} where s = " + aDialect.asQueryString(EmpireUtil.asResource(EmpireUtil.asSupportsRdfId(theObj))) + " limit 1"; ResultSet aResults; if (theSource.getQueryFactory().getDialect() instanceof SerqlDialect) { aResults = theSource.selectQuery(aSeRQL); } else { // fall back on sparql aResults = theSource.selectQuery(aSPARQL); } try { return aResults.hasNext(); } finally { aResults.close(); } } /** * Return the type of the resource in the data source. * @param theSource the data source * @param theConcept the concept whose type to lookup * @return the rdf:type of the concept, or null if there is an error or one cannot be found. */ public static org.openrdf.model.Resource getType(DataSource theSource, Resource theConcept) { Iterable<org.openrdf.model.Resource> aTypes = getTypes(theSource, theConcept); if (aTypes == null || Iterables.isEmpty(aTypes)) { return null; } else { return Iterables.getFirst(aTypes, null); } } public static Iterable<org.openrdf.model.Resource> getTypes(final DataSource theSource, final Resource theConcept) { if (theSource == null) { return Collections.emptySet(); } try { final Collection<Value> aTypes = getValues(theSource, theConcept, RDF.TYPE); if (aTypes.isEmpty()) { return Collections.emptySet(); } else { return Iterables.transform(aTypes, Functions2.cast(Resource.class)); } } catch (DataSourceException e) { LOGGER.error("There was an error while getting the type of a resource", e); return Collections.emptySet(); } } /** * Return the values for the property on the given resource. * @param theSource the data source to query for values * @param theSubject the subject to get property values for * @param thePredicate the property to get values for * @return a collection of all the values of the property on the given resource * @throws com.clarkparsia.empire.ds.DataSourceException if there is an error while querying the data source. */ public static Collection<Value> getValues(final DataSource theSource, final Resource theSubject, final org.openrdf.model.URI thePredicate) throws DataSourceException { final String aSPARQLQuery = "select ?obj\n" + "where {\n" + theSource.getQueryFactory().getDialect().asQueryString(theSubject) + " <" + thePredicate.stringValue() + "> ?obj. }"; final String aSERQLQuery = "select obj\n" + "from\n" + "{"+theSource.getQueryFactory().getDialect().asQueryString(theSubject) + "} <" + thePredicate.stringValue() + "> {obj} "; ResultSet aResults = null; try { if (theSource.getQueryFactory().getDialect().equals(SerqlDialect.instance())) { aResults = theSource.selectQuery(aSERQLQuery); } else { aResults = theSource.selectQuery(aSPARQLQuery); } return Collections2.transform(Sets.newHashSet(aResults), new Function<BindingSet, Value>() { public Value apply(final BindingSet theIn) { return theIn.getValue("obj"); } }); } catch (Exception e) { throw new DataSourceException(e); } finally { if (aResults != null) { aResults.close(); } } } /** * Return the values for the property on the given resource. * @param theSource the data source to query for values * @param theSubject the subject to get property values for * @param thePredicate the property to get values for * @return the first value of the resource * @throws com.clarkparsia.empire.ds.DataSourceException if there is an error while querying the data source. */ public static Value getValue(final DataSource theSource, final Resource theSubject, final org.openrdf.model.URI thePredicate) throws DataSourceException { Collection<Value> aValues = getValues(theSource, theSubject, thePredicate); if (aValues.isEmpty()) { return null; } else { return aValues.iterator().next(); } } }