From 9cd5a1329a828d39994cb75d04597af4d1057f92 Mon Sep 17 00:00:00 2001 From: Jim Blake Date: Tue, 3 Jan 2017 13:16:22 -0500 Subject: [PATCH] VIVO-1248 extend SparqlQueryRunner, and combine with older attempts. (#45) * Ignore artifacts that Eclipse creates. * VIVO-1248 Create the utils.sparqlrunner package. This is a merge of most of the edu.cornell.mannlib.vitro.webapp.utils.sparql package with edu.cornell.mannlib.vitro.webapp.utils.SparqlQueryRunner Remove the references to those classes. * VIVO-1248 extend SparqlQueryRunner, and combine with older attempts. --- .gitignore | 5 + .../accounts/UserAccountsSelector.java | 27 +- .../ProxyRelationshipSelector.java | 45 +-- .../ajax/BasicProxiesGetter.java | 12 +- .../admin/SparqlQueryController.java | 2 +- .../ajax/AbstractAjaxResponder.java | 6 +- .../sparqlquery/SparqlQueryApiExecutor.java | 2 +- .../controller/jena/JenaIngestController.java | 2 +- .../webapp/dao/jena/FauxPropertyDaoJena.java | 38 +- .../SelectQueryDocumentModifier.java | 12 +- .../indexing/SelectQueryUriFinder.java | 16 +- .../utils/sparql/RdfServiceQueryContext.java | 125 ------- .../utils/sparql/SelectQueryHolder.java | 36 -- .../utils/sparql/SelectQueryRunner.java | 104 ------ .../utils/{ => sparql}/SparqlQueryUtils.java | 2 +- .../ModelConstructQueryContext.java | 87 +++++ .../sparqlrunner/ModelSelectQueryContext.java | 129 +++++++ .../utils/sparqlrunner/QueryHolder.java | 79 +++++ .../vitro/webapp/utils/sparqlrunner/README.md | 130 +++++++ .../RdfServiceConstructQueryContext.java | 88 +++++ .../RdfServiceSelectQueryContext.java | 116 ++++++ .../utils/sparqlrunner/ResultSetParser.java | 57 +++ .../utils/sparqlrunner/SparqlQueryRunner.java | 127 +++++++ .../sparqlrunner/StringResultsMapping.java | 95 +++++ .../testing/ModelUtilitiesTestHelper.java | 5 + .../ProxyRelationshipSelectorTest.java | 2 - .../webapp/utils/SparqlQueryRunnerTest.java | 92 ----- .../utils/sparqlrunner/QueryHolderTest.java | 155 ++++++++ .../sparqlrunner/ResultSetParserTest.java | 331 ++++++++++++++++++ .../sparqlrunner/SparqlQueryRunnerTest.java | 170 +++++++++ .../StringResultsMappingTest.java | 140 ++++++++ 31 files changed, 1799 insertions(+), 438 deletions(-) delete mode 100644 api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparql/RdfServiceQueryContext.java delete mode 100644 api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparql/SelectQueryHolder.java delete mode 100644 api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparql/SelectQueryRunner.java rename api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/{ => sparql}/SparqlQueryUtils.java (97%) create mode 100644 api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/ModelConstructQueryContext.java create mode 100644 api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/ModelSelectQueryContext.java create mode 100644 api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/QueryHolder.java create mode 100644 api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/README.md create mode 100644 api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/RdfServiceConstructQueryContext.java create mode 100644 api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/RdfServiceSelectQueryContext.java create mode 100644 api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/ResultSetParser.java create mode 100644 api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/SparqlQueryRunner.java create mode 100644 api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/StringResultsMapping.java delete mode 100644 api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/SparqlQueryRunnerTest.java create mode 100644 api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/QueryHolderTest.java create mode 100644 api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/ResultSetParserTest.java create mode 100644 api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/SparqlQueryRunnerTest.java create mode 100644 api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/StringResultsMappingTest.java diff --git a/.gitignore b/.gitignore index b3de3b279..c6f53fe5a 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,8 @@ utilities/sdb_to_tdb/.work **/target **/overlays + +# Eclipse artifacts +**/.settings +**/.classpath +**/.project diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/UserAccountsSelector.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/UserAccountsSelector.java index 0c684134c..af803122a 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/UserAccountsSelector.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/UserAccountsSelector.java @@ -20,9 +20,9 @@ import org.apache.jena.rdf.model.Resource; import edu.cornell.mannlib.vitro.webapp.beans.UserAccount; import edu.cornell.mannlib.vitro.webapp.beans.UserAccount.Status; import edu.cornell.mannlib.vitro.webapp.controller.accounts.UserAccountsOrdering.Field; -import edu.cornell.mannlib.vitro.webapp.utils.SparqlQueryRunner; -import edu.cornell.mannlib.vitro.webapp.utils.SparqlQueryRunner.QueryParser; -import edu.cornell.mannlib.vitro.webapp.utils.SparqlQueryUtils; +import edu.cornell.mannlib.vitro.webapp.utils.sparql.SparqlQueryUtils; +import edu.cornell.mannlib.vitro.webapp.utils.sparqlrunner.ResultSetParser; +import edu.cornell.mannlib.vitro.webapp.utils.sparqlrunner.SparqlQueryRunner; /** * Pull some UserAccounts from the model, based on a set of criteria. @@ -111,8 +111,9 @@ public class UserAccountsSelector { .replace("%offset%", offset()); log.debug("main query: " + qString); - List accounts = new SparqlQueryRunner(model) - .executeSelect(new MainQueryParser(), qString); + List accounts = SparqlQueryRunner + .createSelectQueryContext(model, qString).execute() + .parse(new MainQueryParser()); log.debug("query returns: " + accounts); return accounts; } @@ -126,8 +127,8 @@ public class UserAccountsSelector { .replace("%filterClauses%", filterClauses()); log.debug("count query: " + qString); - int count = new SparqlQueryRunner(model).executeSelect( - new CountQueryParser(), qString); + int count = SparqlQueryRunner.createSelectQueryContext(model, qString) + .execute().parse(new CountQueryParser()); log.debug("result count: " + count); return count; } @@ -139,8 +140,9 @@ public class UserAccountsSelector { PREFIX_LINES).replace("%uri%", uri); log.debug("permissions query: " + qString); - Set permissions = new SparqlQueryRunner(model) - .executeSelect(new PermissionsQueryParser(), qString); + Set permissions = SparqlQueryRunner + .createSelectQueryContext(model, qString).execute() + .parse(new PermissionsQueryParser()); log.debug("permissions for '" + uri + "': " + permissions); account.setPermissionSetUris(permissions); } @@ -214,7 +216,8 @@ public class UserAccountsSelector { return String.valueOf(offset); } - private static class MainQueryParser extends QueryParser> { + private static class MainQueryParser extends + ResultSetParser> { @Override protected List defaultValue() { return Collections.emptyList(); @@ -274,7 +277,7 @@ public class UserAccountsSelector { } } - private static class CountQueryParser extends QueryParser { + private static class CountQueryParser extends ResultSetParser { @Override protected Integer defaultValue() { return 0; @@ -299,7 +302,7 @@ public class UserAccountsSelector { } private static class PermissionsQueryParser extends - QueryParser> { + ResultSetParser> { @Override protected Set defaultValue() { return Collections.emptySet(); diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/manageproxies/ProxyRelationshipSelector.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/manageproxies/ProxyRelationshipSelector.java index 22ba17d6c..95e210dd7 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/manageproxies/ProxyRelationshipSelector.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/manageproxies/ProxyRelationshipSelector.java @@ -16,9 +16,9 @@ import org.apache.jena.query.ResultSet; import edu.cornell.mannlib.vitro.webapp.controller.accounts.manageproxies.ProxyRelationshipSelectionBuilder.ItemInfo; import edu.cornell.mannlib.vitro.webapp.controller.accounts.manageproxies.ProxyRelationshipSelectionBuilder.Relationship; import edu.cornell.mannlib.vitro.webapp.controller.accounts.manageproxies.ProxyRelationshipSelectionCriteria.ProxyRelationshipView; -import edu.cornell.mannlib.vitro.webapp.utils.SparqlQueryRunner; -import edu.cornell.mannlib.vitro.webapp.utils.SparqlQueryRunner.QueryParser; -import edu.cornell.mannlib.vitro.webapp.utils.SparqlQueryUtils; +import edu.cornell.mannlib.vitro.webapp.utils.sparql.SparqlQueryUtils; +import edu.cornell.mannlib.vitro.webapp.utils.sparqlrunner.ResultSetParser; +import edu.cornell.mannlib.vitro.webapp.utils.sparqlrunner.SparqlQueryRunner; /** * A class which will accept a ProxyRelationshipSelectionCriteria and produce a @@ -94,8 +94,9 @@ public class ProxyRelationshipSelector { PREFIX_LINES); qString = replaceFilterClauses(qString); - int count = new SparqlQueryRunner(context.userAccountsModel) - .executeSelect(new CountQueryParser(), qString); + int count = SparqlQueryRunner + .createSelectQueryContext(context.userAccountsModel, qString) + .execute().parse(new CountQueryParser()); log.debug("result count: " + count); builder.count = count; @@ -136,9 +137,9 @@ public class ProxyRelationshipSelector { .replace("%offset%", offset()); qString = replaceFilterClauses(qString); - List relationships = new SparqlQueryRunner( - context.userAccountsModel).executeSelect( - new ProxyBasicsParser(), qString); + List relationships = SparqlQueryRunner + .createSelectQueryContext(context.userAccountsModel, qString) + .execute().parse(new ProxyBasicsParser()); log.debug("getProxyBasics returns: " + relationships); builder.relationships.addAll(relationships); } @@ -177,8 +178,9 @@ public class ProxyRelationshipSelector { .replace("%matchingProperty%", context.matchingProperty) .replace("%externalAuthId%", proxy.externalAuthId); - ItemInfo expansion = new SparqlQueryRunner(context.unionModel) - .executeSelect(new ExpandProxyParser(), qString); + ItemInfo expansion = SparqlQueryRunner + .createSelectQueryContext(context.unionModel, qString) + .execute().parse(new ExpandProxyParser()); proxy.classLabel = expansion.classLabel; proxy.imageUrl = expansion.imageUrl; } @@ -199,9 +201,10 @@ public class ProxyRelationshipSelector { String qString = QUERY_RELATIONSHIPS.replace("%prefixes%", PREFIX_LINES).replace("%proxyUri%", proxy.uri); - List profileUris = new SparqlQueryRunner( - context.userAccountsModel).executeSelect( - new RelationshipsParser(), qString); + List profileUris = SparqlQueryRunner + .createSelectQueryContext(context.userAccountsModel, + qString).execute() + .parse(new RelationshipsParser()); for (String profileUri : profileUris) { r.profileInfos @@ -235,8 +238,9 @@ public class ProxyRelationshipSelector { String qString = QUERY_EXPAND_PROFILE.replace("%prefixes%", PREFIX_LINES).replace("%profileUri%", profile.uri); - ItemInfo expansion = new SparqlQueryRunner(context.unionModel) - .executeSelect(new ExpandProfileParser(), qString); + ItemInfo expansion = SparqlQueryRunner + .createSelectQueryContext(context.unionModel, qString) + .execute().parse(new ExpandProfileParser()); profile.label = expansion.label; profile.classLabel = expansion.classLabel; profile.imageUrl = expansion.imageUrl; @@ -285,7 +289,7 @@ public class ProxyRelationshipSelector { // ---------------------------------------------------------------------- private static class ProxyBasicsParser extends - QueryParser> { + ResultSetParser> { @Override protected List defaultValue() { return Collections.emptyList(); @@ -318,7 +322,7 @@ public class ProxyRelationshipSelector { } } - private static class CountQueryParser extends QueryParser { + private static class CountQueryParser extends ResultSetParser { @Override protected Integer defaultValue() { return 0; @@ -342,7 +346,7 @@ public class ProxyRelationshipSelector { } } - private static class ExpandProxyParser extends QueryParser { + private static class ExpandProxyParser extends ResultSetParser { @Override protected ItemInfo defaultValue() { return new ItemInfo(); @@ -367,7 +371,8 @@ public class ProxyRelationshipSelector { } } - private static class RelationshipsParser extends QueryParser> { + private static class RelationshipsParser extends + ResultSetParser> { @Override protected List defaultValue() { return Collections.emptyList(); @@ -388,7 +393,7 @@ public class ProxyRelationshipSelector { } } - private static class ExpandProfileParser extends QueryParser { + private static class ExpandProfileParser extends ResultSetParser { @Override protected ItemInfo defaultValue() { return new ItemInfo(); diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/manageproxies/ajax/BasicProxiesGetter.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/manageproxies/ajax/BasicProxiesGetter.java index 85db98834..f1c3d0c3e 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/manageproxies/ajax/BasicProxiesGetter.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/manageproxies/ajax/BasicProxiesGetter.java @@ -24,8 +24,8 @@ import edu.cornell.mannlib.vitro.webapp.controller.ajax.AbstractAjaxResponder; import edu.cornell.mannlib.vitro.webapp.controller.freemarker.UrlBuilder; import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary; import edu.cornell.mannlib.vitro.webapp.modelaccess.ModelAccess; -import edu.cornell.mannlib.vitro.webapp.utils.SparqlQueryRunner; -import edu.cornell.mannlib.vitro.webapp.utils.SparqlQueryUtils; +import edu.cornell.mannlib.vitro.webapp.utils.sparql.SparqlQueryUtils; +import edu.cornell.mannlib.vitro.webapp.utils.sparqlrunner.SparqlQueryRunner; import edu.cornell.mannlib.vitro.webapp.web.images.PlaceholderUtil; /** @@ -76,10 +76,10 @@ public class BasicProxiesGetter extends AbstractAjaxResponder { String cleanTerm = SparqlQueryUtils.escapeForRegex(term); String queryStr = QUERY_BASIC_PROXIES.replace("%term%", cleanTerm); - JSONArray jsonArray = new SparqlQueryRunner(userAccountsModel) - .executeSelect( - new BasicProxyInfoParser(placeholderImageUrl), - queryStr); + JSONArray jsonArray = SparqlQueryRunner + .createSelectQueryContext(userAccountsModel, queryStr) + .execute() + .parse(new BasicProxyInfoParser(placeholderImageUrl)); String response = jsonArray.toString(); log.debug(response); diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/admin/SparqlQueryController.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/admin/SparqlQueryController.java index 1b572a5b6..bba1e0865 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/admin/SparqlQueryController.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/admin/SparqlQueryController.java @@ -36,9 +36,9 @@ import edu.cornell.mannlib.vitro.webapp.dao.OntologyDao; import edu.cornell.mannlib.vitro.webapp.modelaccess.ModelAccess; import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService; import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFServiceException; -import edu.cornell.mannlib.vitro.webapp.utils.SparqlQueryUtils; import edu.cornell.mannlib.vitro.webapp.utils.http.AcceptHeaderParsingException; import edu.cornell.mannlib.vitro.webapp.utils.http.NotAcceptableException; +import edu.cornell.mannlib.vitro.webapp.utils.sparql.SparqlQueryUtils; /** * Present the SPARQL Query form, and execute the queries. diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/ajax/AbstractAjaxResponder.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/ajax/AbstractAjaxResponder.java index 3c80b8070..3faa4c968 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/ajax/AbstractAjaxResponder.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/ajax/AbstractAjaxResponder.java @@ -24,7 +24,7 @@ import org.apache.jena.query.ResultSet; import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; import edu.cornell.mannlib.vitro.webapp.dao.IndividualDao; import edu.cornell.mannlib.vitro.webapp.dao.UserAccountsDao; -import edu.cornell.mannlib.vitro.webapp.utils.SparqlQueryRunner; +import edu.cornell.mannlib.vitro.webapp.utils.sparqlrunner.ResultSetParser; /** * A base class for AJAX responder objects, to be instantiated and invoked by @@ -82,7 +82,7 @@ public abstract class AbstractAjaxResponder { */ protected String assembleJsonResponse(List> maps) { JSONArray jsonArray = new JSONArray(); - for (Map map: maps) { + for (Map map : maps) { jsonArray.put(map); } return jsonArray.toString(); @@ -93,7 +93,7 @@ public abstract class AbstractAjaxResponder { * implement "parseSolutionRow()" */ protected abstract static class JsonArrayParser extends - SparqlQueryRunner.QueryParser { + ResultSetParser { @Override protected JSONArray defaultValue() { return new JSONArray(); diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/api/sparqlquery/SparqlQueryApiExecutor.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/api/sparqlquery/SparqlQueryApiExecutor.java index 13a274d36..e440e9131 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/api/sparqlquery/SparqlQueryApiExecutor.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/api/sparqlquery/SparqlQueryApiExecutor.java @@ -10,9 +10,9 @@ import org.apache.jena.query.QueryParseException; import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService; import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFServiceException; -import edu.cornell.mannlib.vitro.webapp.utils.SparqlQueryUtils; import edu.cornell.mannlib.vitro.webapp.utils.http.AcceptHeaderParsingException; import edu.cornell.mannlib.vitro.webapp.utils.http.NotAcceptableException; +import edu.cornell.mannlib.vitro.webapp.utils.sparql.SparqlQueryUtils; /** * The base class for the SPARQL query API. diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/jena/JenaIngestController.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/jena/JenaIngestController.java index 344dfdcc2..e80a29835 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/jena/JenaIngestController.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/jena/JenaIngestController.java @@ -73,12 +73,12 @@ import edu.cornell.mannlib.vitro.webapp.rdfservice.ChangeSet; import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService; import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFServiceException; import edu.cornell.mannlib.vitro.webapp.rdfservice.adapters.VitroModelFactory; -import edu.cornell.mannlib.vitro.webapp.utils.SparqlQueryUtils; import edu.cornell.mannlib.vitro.webapp.utils.jena.JenaIngestUtils; import edu.cornell.mannlib.vitro.webapp.utils.jena.JenaIngestUtils.MergeResult; import edu.cornell.mannlib.vitro.webapp.utils.jena.JenaIngestWorkflowProcessor; import edu.cornell.mannlib.vitro.webapp.utils.jena.JenaOutputUtils; import edu.cornell.mannlib.vitro.webapp.utils.jena.WorkflowOntology; +import edu.cornell.mannlib.vitro.webapp.utils.sparql.SparqlQueryUtils; public class JenaIngestController extends BaseEditController { private static final Log log = LogFactory.getLog(JenaIngestController.class); diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/FauxPropertyDaoJena.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/FauxPropertyDaoJena.java index 61a3ac6db..87b52830c 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/FauxPropertyDaoJena.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/FauxPropertyDaoJena.java @@ -2,9 +2,9 @@ package edu.cornell.mannlib.vitro.webapp.dao.jena; +import static edu.cornell.mannlib.vitro.webapp.utils.sparqlrunner.SparqlQueryRunner.createSelectQueryContext; +import static edu.cornell.mannlib.vitro.webapp.utils.sparqlrunner.SparqlQueryRunner.queryHolder; import static org.apache.jena.rdf.model.ResourceFactory.createResource; -import static edu.cornell.mannlib.vitro.webapp.utils.SparqlQueryRunner.bindValues; -import static edu.cornell.mannlib.vitro.webapp.utils.SparqlQueryRunner.uriValue; import java.util.ArrayList; import java.util.Collection; @@ -16,7 +16,6 @@ import java.util.Set; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; - import org.apache.jena.ontology.ObjectProperty; import org.apache.jena.ontology.OntModel; import org.apache.jena.ontology.OntModelSpec; @@ -33,11 +32,11 @@ import edu.cornell.mannlib.vitro.webapp.beans.FauxProperty; import edu.cornell.mannlib.vitro.webapp.dao.FauxPropertyDao; import edu.cornell.mannlib.vitro.webapp.dao.InsertException; import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary; -import edu.cornell.mannlib.vitro.webapp.utils.SparqlQueryRunner; -import edu.cornell.mannlib.vitro.webapp.utils.SparqlQueryRunner.QueryParser; import edu.cornell.mannlib.vitro.webapp.utils.jena.criticalsection.LockableOntModel; import edu.cornell.mannlib.vitro.webapp.utils.jena.criticalsection.LockableOntModelSelector; import edu.cornell.mannlib.vitro.webapp.utils.jena.criticalsection.LockedOntModel; +import edu.cornell.mannlib.vitro.webapp.utils.sparqlrunner.QueryHolder; +import edu.cornell.mannlib.vitro.webapp.utils.sparqlrunner.ResultSetParser; /** * TODO @@ -522,7 +521,7 @@ public class FauxPropertyDaoJena extends JenaBaseDao implements FauxPropertyDao + "} \n"; // private static class ParserLocateConfigContext extends - QueryParser> { + ResultSetParser> { private final String domainUri; private final String baseUri; private final String rangeUri; @@ -561,30 +560,29 @@ public class FauxPropertyDaoJena extends JenaBaseDao implements FauxPropertyDao LockableOntModel lockableDisplayModel, String domainUri, String baseUri, String rangeUri) { try (LockedOntModel displayModel = lockableDisplayModel.read()) { - String queryString; + QueryHolder qHolder; if (domainUri == null || domainUri.trim().isEmpty() || domainUri.equals(OWL.Thing.getURI())) { - queryString = bindValues( - QUERY_LOCATE_CONFIG_CONTEXT_WITH_NO_DOMAIN, - uriValue("baseUri", baseUri), - uriValue("rangeUri", rangeUri)); + qHolder = queryHolder( + QUERY_LOCATE_CONFIG_CONTEXT_WITH_NO_DOMAIN) + .bindToUri("baseUri", baseUri).bindToUri( + "rangeUri", rangeUri); } else { - queryString = bindValues( - QUERY_LOCATE_CONFIG_CONTEXT_WITH_DOMAIN, - uriValue("baseUri", baseUri), - uriValue("rangeUri", rangeUri), - uriValue("domainUri", domainUri)); + qHolder = queryHolder( + QUERY_LOCATE_CONFIG_CONTEXT_WITH_DOMAIN) + .bindToUri("baseUri", baseUri) + .bindToUri("rangeUri", rangeUri) + .bindToUri("domainUri", domainUri); } if (log.isDebugEnabled()) { log.debug("domainUri=" + domainUri + ", baseUri=" + baseUri - + ", rangeUri=" + rangeUri + ", queryString=" - + queryString); + + ", rangeUri=" + rangeUri + ", qHolder=" + qHolder); } ParserLocateConfigContext parser = new ParserLocateConfigContext( domainUri, baseUri, rangeUri); - Set contexts = new SparqlQueryRunner( - displayModel).executeSelect(parser, queryString); + Set contexts = createSelectQueryContext( + displayModel, qHolder).execute().parse(parser); log.debug("found " + contexts.size() + " contexts: " + contexts); return contexts; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/searchindex/documentBuilding/SelectQueryDocumentModifier.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/searchindex/documentBuilding/SelectQueryDocumentModifier.java index 1494ddbbb..971cf4fe5 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/searchindex/documentBuilding/SelectQueryDocumentModifier.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/searchindex/documentBuilding/SelectQueryDocumentModifier.java @@ -5,7 +5,7 @@ package edu.cornell.mannlib.vitro.webapp.searchindex.documentBuilding; import static edu.cornell.mannlib.vitro.webapp.modelaccess.ModelAccess.WhichService.CONTENT; import static edu.cornell.mannlib.vitro.webapp.search.VitroSearchTermNames.ALLTEXT; import static edu.cornell.mannlib.vitro.webapp.search.VitroSearchTermNames.ALLTEXTUNSTEMMED; -import static edu.cornell.mannlib.vitro.webapp.utils.sparql.SelectQueryRunner.createQueryContext; +import static edu.cornell.mannlib.vitro.webapp.utils.sparqlrunner.SparqlQueryRunner.createSelectQueryContext; import java.util.ArrayList; import java.util.Collections; @@ -24,7 +24,7 @@ import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService; import edu.cornell.mannlib.vitro.webapp.utils.configuration.ContextModelsUser; import edu.cornell.mannlib.vitro.webapp.utils.configuration.Property; import edu.cornell.mannlib.vitro.webapp.utils.configuration.Validation; -import edu.cornell.mannlib.vitro.webapp.utils.sparql.SelectQueryHolder; +import edu.cornell.mannlib.vitro.webapp.utils.sparqlrunner.QueryHolder; /** * Modify the document, adding the results of one or more select queries. @@ -151,10 +151,10 @@ public class SelectQueryDocumentModifier implements DocumentModifier, private List getTextForQuery(String query, Individual ind) { try { - SelectQueryHolder queryHolder = new SelectQueryHolder(query) - .bindToUri("uri", ind.getURI()); - List list = createQueryContext(rdfService, queryHolder) - .execute().getStringFields().flatten(); + QueryHolder queryHolder = new QueryHolder(query).bindToUri("uri", + ind.getURI()); + List list = createSelectQueryContext(rdfService, + queryHolder).execute().toStringFields().flatten(); log.debug(label + " - query: '" + query + "' returns " + list); return list; } catch (Throwable t) { diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/searchindex/indexing/SelectQueryUriFinder.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/searchindex/indexing/SelectQueryUriFinder.java index 27f820b42..98229828c 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/searchindex/indexing/SelectQueryUriFinder.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/searchindex/indexing/SelectQueryUriFinder.java @@ -3,8 +3,8 @@ package edu.cornell.mannlib.vitro.webapp.searchindex.indexing; import static edu.cornell.mannlib.vitro.webapp.modelaccess.ModelAccess.WhichService.CONTENT; -import static edu.cornell.mannlib.vitro.webapp.utils.sparql.SelectQueryRunner.createQueryContext; -import static edu.cornell.mannlib.vitro.webapp.utils.sparql.SelectQueryRunner.selectQuery; +import static edu.cornell.mannlib.vitro.webapp.utils.sparqlrunner.SparqlQueryRunner.createSelectQueryContext; +import static edu.cornell.mannlib.vitro.webapp.utils.sparqlrunner.SparqlQueryRunner.queryHolder; import java.util.ArrayList; import java.util.Collections; @@ -23,7 +23,7 @@ import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService; import edu.cornell.mannlib.vitro.webapp.utils.configuration.ContextModelsUser; import edu.cornell.mannlib.vitro.webapp.utils.configuration.Property; import edu.cornell.mannlib.vitro.webapp.utils.configuration.Validation; -import edu.cornell.mannlib.vitro.webapp.utils.sparql.SelectQueryHolder; +import edu.cornell.mannlib.vitro.webapp.utils.sparqlrunner.QueryHolder; /** * Find URIs based on one or more select queries. @@ -118,7 +118,7 @@ public class SelectQueryUriFinder implements IndexingUriFinder, } private List getUrisForQuery(Statement stmt, String queryString) { - SelectQueryHolder query = selectQuery(queryString); + QueryHolder query = queryHolder(queryString); query = query.bindToUri("predicate", stmt.getPredicate().getURI()); query = tryToBindUri(query, "subject", stmt.getSubject()); @@ -127,12 +127,12 @@ public class SelectQueryUriFinder implements IndexingUriFinder, return Collections.emptyList(); } - return createQueryContext(rdfService, query).execute() - .getStringFields().flatten(); + return createSelectQueryContext(rdfService, query).execute() + .toStringFields().flatten(); } - private SelectQueryHolder tryToBindUri(SelectQueryHolder query, - String name, RDFNode node) { + private QueryHolder tryToBindUri(QueryHolder query, String name, + RDFNode node) { if (query == null) { return null; } diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparql/RdfServiceQueryContext.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparql/RdfServiceQueryContext.java deleted file mode 100644 index a3cdce5c7..000000000 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparql/RdfServiceQueryContext.java +++ /dev/null @@ -1,125 +0,0 @@ -/* $This file is distributed under the terms of the license in /doc/license.txt$ */ - -package edu.cornell.mannlib.vitro.webapp.utils.sparql; - -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.Map; -import java.util.Set; - -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.apache.jena.query.QuerySolution; -import org.apache.jena.query.ResultSet; -import org.apache.jena.rdf.model.RDFNode; - -import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService; -import edu.cornell.mannlib.vitro.webapp.rdfservice.impl.RDFServiceUtils; -import edu.cornell.mannlib.vitro.webapp.utils.sparql.SelectQueryRunner.ExecutingSelectQueryContext; -import edu.cornell.mannlib.vitro.webapp.utils.sparql.SelectQueryRunner.SelectQueryContext; -import edu.cornell.mannlib.vitro.webapp.utils.sparql.SelectQueryRunner.StringResultsMapping; -import edu.cornell.mannlib.vitro.webapp.utils.sparql.SelectQueryRunner.StringResultsMappingImpl; - -/** - * An implementation of QueryContext based on an RDFService. - * - * Package access. Instances should be created only by SelectQueryRunner, or by - * a method on this class. - */ -class RdfServiceQueryContext implements SelectQueryContext { - private static final Log log = LogFactory - .getLog(RdfServiceQueryContext.class); - - private final RDFService rdfService; - private final SelectQueryHolder query; - - RdfServiceQueryContext(RDFService rdfService, SelectQueryHolder query) { - this.rdfService = rdfService; - this.query = query; - } - - @Override - public RdfServiceQueryContext bindVariableToUri(String name, String uri) { - return new RdfServiceQueryContext(rdfService, - query.bindToUri(name, uri)); - } - - @Override - public ExecutingSelectQueryContext execute() { - return new RdfServiceExecutingQueryContext(rdfService, query); - } - - private static class RdfServiceExecutingQueryContext implements - ExecutingSelectQueryContext { - private final RDFService rdfService; - private final SelectQueryHolder query; - - public RdfServiceExecutingQueryContext(RDFService rdfService, - SelectQueryHolder query) { - this.rdfService = rdfService; - this.query = query; - } - - @Override - public StringResultsMapping getStringFields(String... names) { - Set fieldNames = new HashSet<>(Arrays.asList(names)); - StringResultsMappingImpl mapping = new StringResultsMappingImpl(); - try { - ResultSet results = RDFServiceUtils.sparqlSelectQuery( - query.getQueryString(), rdfService); - return mapResultsForQuery(results, fieldNames); - } catch (Exception e) { - log.error( - "problem while running query '" - + query.getQueryString() + "'", e); - } - return mapping; - } - - private StringResultsMapping mapResultsForQuery(ResultSet results, - Set fieldNames) { - StringResultsMappingImpl mapping = new StringResultsMappingImpl(); - while (results.hasNext()) { - Map rowMapping = mapResultsForRow( - results.nextSolution(), fieldNames); - if (!rowMapping.isEmpty()) { - mapping.add(rowMapping); - } - } - return mapping; - } - - private Map mapResultsForRow(QuerySolution row, - Set fieldNames) { - Map map = new HashMap<>(); - for (Iterator names = row.varNames(); names.hasNext();) { - String name = names.next(); - RDFNode node = row.get(name); - String text = getTextForNode(node); - if (StringUtils.isNotBlank(text)) { - map.put(name, text); - } - } - if (!fieldNames.isEmpty()) { - map.keySet().retainAll(fieldNames); - } - return map; - } - - private String getTextForNode(RDFNode node) { - if (node == null) { - return ""; - } else if (node.isLiteral()) { - return node.asLiteral().getString().trim(); - } else { - return node.toString().trim(); - } - } - - } - -} diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparql/SelectQueryHolder.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparql/SelectQueryHolder.java deleted file mode 100644 index d59ce1bea..000000000 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparql/SelectQueryHolder.java +++ /dev/null @@ -1,36 +0,0 @@ -/* $This file is distributed under the terms of the license in /doc/license.txt$ */ - -package edu.cornell.mannlib.vitro.webapp.utils.sparql; - -import java.util.regex.Pattern; - -/** - * Holds the text of a SPARQL Select query, and allows you to perform some lexical - * operations on it. - * - * This is immutable, so don't forget to get the result of the operations. - */ -public class SelectQueryHolder { - private final String queryString; - - public SelectQueryHolder(String queryString) { - this.queryString = queryString; - } - - public String getQueryString() { - return queryString; - } - - public boolean hasVariable(String name) { - String regex = "\\?" + name + "\\b"; - return Pattern.compile(regex).matcher(queryString).find(); - } - - public SelectQueryHolder bindToUri(String name, String uri) { - String regex = "\\?" + name + "\\b"; - String replacement = "<" + uri + ">"; - String bound = queryString.replaceAll(regex, replacement); - return new SelectQueryHolder(bound); - } - -} diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparql/SelectQueryRunner.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparql/SelectQueryRunner.java deleted file mode 100644 index ad640cc5d..000000000 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparql/SelectQueryRunner.java +++ /dev/null @@ -1,104 +0,0 @@ -/* $This file is distributed under the terms of the license in /doc/license.txt$ */ - -package edu.cornell.mannlib.vitro.webapp.utils.sparql; - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService; - -/** - * A conversational tool for handling SPARQL queries. - * - * {@code - * Examples: - * List values = createQueryContext(rdfService, queryString) - * .bindVariableToUri("uri", uri) - * .execute() - * .getStringFields("partner") - * .flatten(); - * - * SelectQueryHolder q = selectQuery(queryString) - * .bindToUri("uri", uri)); - * List map = createQueryContext(rdfService, q) - * .execute() - * .getStringFields(); - * } - * - * The execute() method does not actually execute the query: it merely sets it - * up syntactically. - * - * If you don't supply any field names to getStringFields(), you get all of - * them. - * - * Any string value that returns a blank or empty string is omitted from the - * results. Any row that returns no values is omitted from the results. - */ -public final class SelectQueryRunner { - private static final Log log = LogFactory.getLog(SelectQueryRunner.class); - - private SelectQueryRunner() { - // No need to create an instance. - } - - public static SelectQueryHolder selectQuery(String queryString) { - return new SelectQueryHolder(queryString); - } - - public static SelectQueryContext createQueryContext(RDFService rdfService, - String queryString) { - return createQueryContext(rdfService, selectQuery(queryString)); - } - - public static SelectQueryContext createQueryContext(RDFService rdfService, - SelectQueryHolder query) { - return new RdfServiceQueryContext(rdfService, query); - } - - public static interface SelectQueryContext { - public SelectQueryContext bindVariableToUri(String name, String uri); - - public ExecutingSelectQueryContext execute(); - } - - public static interface ExecutingSelectQueryContext { - public StringResultsMapping getStringFields(String... fieldNames); - } - - public static interface StringResultsMapping extends - List> { - public List flatten(); - - public Set flattenToSet(); - } - - // ---------------------------------------------------------------------- - // Helper classes - // ---------------------------------------------------------------------- - - static class StringResultsMappingImpl extends - ArrayList> implements StringResultsMapping { - - @Override - public List flatten() { - List flat = new ArrayList<>(); - for (Map map : this) { - flat.addAll(map.values()); - } - return flat; - } - - @Override - public Set flattenToSet() { - return new HashSet<>(flatten()); - } - - } - -} diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/SparqlQueryUtils.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparql/SparqlQueryUtils.java similarity index 97% rename from api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/SparqlQueryUtils.java rename to api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparql/SparqlQueryUtils.java index 651932223..cf886b5c0 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/SparqlQueryUtils.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparql/SparqlQueryUtils.java @@ -1,6 +1,6 @@ /* $This file is distributed under the terms of the license in /doc/license.txt$ */ -package edu.cornell.mannlib.vitro.webapp.utils; +package edu.cornell.mannlib.vitro.webapp.utils.sparql; import java.util.Arrays; import java.util.Iterator; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/ModelConstructQueryContext.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/ModelConstructQueryContext.java new file mode 100644 index 000000000..2881fee57 --- /dev/null +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/ModelConstructQueryContext.java @@ -0,0 +1,87 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.vitro.webapp.utils.sparqlrunner; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.jena.query.Query; +import org.apache.jena.query.QueryExecution; +import org.apache.jena.query.QueryExecutionFactory; +import org.apache.jena.query.QueryFactory; +import org.apache.jena.query.Syntax; +import org.apache.jena.rdf.model.Model; +import org.apache.jena.rdf.model.ModelFactory; + +import edu.cornell.mannlib.vitro.webapp.utils.sparqlrunner.SparqlQueryRunner.ConstructQueryContext; +import edu.cornell.mannlib.vitro.webapp.utils.sparqlrunner.SparqlQueryRunner.ExecutingConstructQueryContext; + +/** + * TODO + */ +public class ModelConstructQueryContext implements ConstructQueryContext { + private static final Log log = LogFactory + .getLog(ModelConstructQueryContext.class); + + private static final Syntax SYNTAX = Syntax.syntaxARQ; + + private final Model model; + private final QueryHolder query; + + public ModelConstructQueryContext(Model model, QueryHolder query) { + this.model = model; + this.query = query; + } + + @Override + public ConstructQueryContext bindVariableToUri(String name, String uri) { + return new ModelConstructQueryContext(model, query.bindToUri(name, uri)); + } + + @Override + public ConstructQueryContext bindVariableToPlainLiteral(String name, + String value) { + return new ModelConstructQueryContext(model, query.bindToPlainLiteral( + name, value)); + } + + @Override + public String toString() { + return "ModelConstructQueryContext[query=" + query + "]"; + } + + @Override + public ExecutingConstructQueryContext execute() { + return new ModelExecutingConstructQueryContext(model, query); + } + + private static class ModelExecutingConstructQueryContext implements + ExecutingConstructQueryContext { + private final Model model; + private final QueryHolder query; + + public ModelExecutingConstructQueryContext(Model model, QueryHolder query) { + this.model = model; + this.query = query; + } + + @Override + public Model toModel() { + QueryExecution qe = null; + try { + Query q = QueryFactory.create(query.getQueryString(), SYNTAX); + qe = QueryExecutionFactory.create(q, model); + return qe.execConstruct(); + } catch (Exception e) { + log.error( + "problem while running query '" + + query.getQueryString() + "'", e); + return ModelFactory.createDefaultModel(); + } finally { + if (qe != null) { + qe.close(); + } + } + } + + } +} diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/ModelSelectQueryContext.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/ModelSelectQueryContext.java new file mode 100644 index 000000000..5de518aff --- /dev/null +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/ModelSelectQueryContext.java @@ -0,0 +1,129 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.vitro.webapp.utils.sparqlrunner; + +import java.io.OutputStream; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.jena.query.Query; +import org.apache.jena.query.QueryExecution; +import org.apache.jena.query.QueryExecutionFactory; +import org.apache.jena.query.QueryFactory; +import org.apache.jena.query.ResultSet; +import org.apache.jena.query.ResultSetFormatter; +import org.apache.jena.query.Syntax; +import org.apache.jena.rdf.model.Model; + +import edu.cornell.mannlib.vitro.webapp.utils.sparqlrunner.SparqlQueryRunner.ExecutingSelectQueryContext; +import edu.cornell.mannlib.vitro.webapp.utils.sparqlrunner.SparqlQueryRunner.SelectQueryContext; + +/** + * An implementation of QueryContext based on a Model. + * + * Package access. Instances should be created only by SparqlQueryRunner, or by + * a method on this class. + */ +class ModelSelectQueryContext implements SelectQueryContext { + private static final Log log = LogFactory + .getLog(ModelSelectQueryContext.class); + + private static final Syntax SYNTAX = Syntax.syntaxARQ; + + private final Model model; + private final QueryHolder query; + + public ModelSelectQueryContext(Model model, QueryHolder query) { + this.model = model; + this.query = query; + } + + @Override + public ModelSelectQueryContext bindVariableToUri(String name, String uri) { + return new ModelSelectQueryContext(model, query.bindToUri(name, uri)); + } + + @Override + public ModelSelectQueryContext bindVariableToPlainLiteral(String name, + String value) { + return new ModelSelectQueryContext(model, query.bindToPlainLiteral( + name, value)); + } + + @Override + public String toString() { + return "ModelSelectQueryContext[query=" + query + "]"; + } + + @Override + public ExecutingSelectQueryContext execute() { + return new ModelExecutingQueryContext(model, query); + } + + private static class ModelExecutingQueryContext implements + ExecutingSelectQueryContext { + private final Model model; + private final QueryHolder query; + + public ModelExecutingQueryContext(Model model, QueryHolder query) { + this.model = model; + this.query = query; + } + + @Override + public StringResultsMapping toStringFields(String... names) { + String qString = query.getQueryString(); + Set fieldNames = new HashSet<>(Arrays.asList(names)); + try { + Query q = QueryFactory.create(qString, SYNTAX); + QueryExecution qexec = QueryExecutionFactory.create(q, model); + try { + ResultSet results = qexec.execSelect(); + return new StringResultsMapping(results, fieldNames); + } finally { + qexec.close(); + } + } catch (Exception e) { + log.error("problem while running query '" + qString + "'", e); + return StringResultsMapping.EMPTY; + } + } + + @Override + public T parse(ResultSetParser parser) { + String qString = query.getQueryString(); + try { + Query q = QueryFactory.create(qString, SYNTAX); + QueryExecution qexec = QueryExecutionFactory.create(q, model); + try { + return parser.parseResults(qString, qexec.execSelect()); + } finally { + qexec.close(); + } + } catch (Exception e) { + log.error("problem while running query '" + qString + "'", e); + return parser.defaultValue(); + } + } + + @Override + public void writeToOutput(OutputStream output) { + String qString = query.getQueryString(); + try { + Query q = QueryFactory.create(qString, SYNTAX); + QueryExecution qexec = QueryExecutionFactory.create(q, model); + try { + ResultSetFormatter.outputAsJSON(output, qexec.execSelect()); + } finally { + qexec.close(); + } + } catch (Exception e) { + log.error("problem while running query '" + qString + "'", e); + } + } + + } +} diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/QueryHolder.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/QueryHolder.java new file mode 100644 index 000000000..031542b07 --- /dev/null +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/QueryHolder.java @@ -0,0 +1,79 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.vitro.webapp.utils.sparqlrunner; + +import java.util.Objects; +import java.util.regex.Pattern; + +/** + * Holds the text of a SPARQL query, and allows you to perform some lexical + * operations on it. + * + * This is immutable, so don't forget to get the result of the operations. + */ +public class QueryHolder { + private final String queryString; + + public QueryHolder(String queryString) { + this.queryString = Objects.requireNonNull(queryString); + } + + public String getQueryString() { + return queryString; + } + + public boolean hasVariable(String name) { + String regex = "\\?" + name + "\\b"; + return Pattern.compile(regex).matcher(queryString).find(); + } + + public QueryHolder bindToUri(String name, String uri) { + String regex = "\\?" + name + "\\b"; + String replacement = "<" + uri + ">"; + String bound = replaceWithinBraces(regex, replacement); + return new QueryHolder(bound); + } + + public QueryHolder bindToPlainLiteral(String name, String value) { + String regex = "\\?" + name + "\\b"; + String replacement = '"' + value + '"'; + String bound = replaceWithinBraces(regex, replacement); + return new QueryHolder(bound); + } + + private String replaceWithinBraces(String regex, String replacement) { + int openBrace = queryString.indexOf('{'); + int closeBrace = queryString.lastIndexOf('}'); + if (openBrace == -1 || closeBrace == -1) { + return queryString; + } else { + String prefix = queryString.substring(0, openBrace); + String suffix = queryString.substring(closeBrace); + String patterns = queryString.substring(openBrace, closeBrace); + return prefix + patterns.replaceAll(regex, replacement) + suffix; + } + } + + @Override + public int hashCode() { + return queryString.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } else if (obj == null) { + return false; + } else if (getClass() != obj.getClass()) { + return false; + } + QueryHolder that = (QueryHolder) obj; + return Objects.equals(this.queryString, that.queryString); + } + + @Override + public String toString() { + return "QueryHolder[" + queryString + "]"; + } +} diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/README.md b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/README.md new file mode 100644 index 000000000..9d19b3ec9 --- /dev/null +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/README.md @@ -0,0 +1,130 @@ +# A conversational API for running SPARQL queries + +`edu.cornell.mannlib.vitro.webapp.utils.sparqlrunner` provides a syntactically simple +way to manipulate and execute SPARQL queries. The query may be run against a Jena `Model` +or a Vitro `RDFService`. + +The API supports the most common tasks: + ++ bind URI values to variables in the query. ++ bind plain literal values to variables in the query. ++ execute a CONSTRUCT query and return a Jena model. ++ execute a SELECT query and return the results in a variety of formats: + + a List of String values + + a Set of String values + + a List of Maps of String values + + a JSON-formatted output stream + + the output of a custom parser class. + +The API could reasonably be extended to perform other operations, or to produce +additional forms of output. It currently provides only the functionality that +was immediately useful at the time of writing. + +## Usage Examples + +In general, the usage pattern is to + ++ create the query context ++ optionally bind values to variables in the query ++ execute ++ transform the results + +### SELECT to a List of Strings + +Run a SELECT query, binding a value to the variable `?uri` and returning +a List of the values that are returned for the variable `?partner`. + + List values = createSelectQueryContext(rdfService, queryString) + .bindVariableToUri("uri", uri) + .execute() + .toStringFields("partner") + .flatten(); + +### SELECT to a List of Maps + + Example: Run a SELECT query, returning a list of maps of strings. Each map + represents a record in the ResultSet, with variable names mapped to the + value in that record, translated to a String. + + List> maps = createSelectQueryContext(model, queryString) + .execute() + .toStringFields() + .getListOfMaps(); + +>_Note: Null or empty values are omitted from the maps; empty maps are omitted from the list._ + + +### SELECT to a Parser + + MyQueryResult mqr = createSelectQueryContext(model, queryString) + .bindVariableToUri("uri", uri) + .bindVariableToPlainLiteral("id", "PW-4250") + .execute() + .parse(new MyQueryParser()); + +### CONSTRUCT to a Model + + Model m = createConstructQueryContext(rdfService, queryString) + .execute() + .toModel(); + +## Using the QueryHolder + +The `QueryHolder` class can be useful in itself, for manipulating a query string. + +### Bind variables in a query + + String boundQuery = queryHolder(rawQuery) + .bindToUri("uri", uri) + .getQueryString(); + +### Inquire about unbound variables + + boolean foundIt = queryHolder(rawQuery) + .hasVariable("name"); + +### Prepare a query in advance of executing it. + QueryHolder qh = queryHolder(queryString) + .bindToUri("uri", uri)); + .bindVariableToPlainLiteral("id", "PW-4250") + List map = createSelectQueryContext(model, qh) + .execute() + .toStringFields() + .getListOfMaps(); + +## Parsing a ResultSet + +By writing a parser, you can translate the `ResultSet` from a SPARQL query +into any object, according to the translation algorithm you specify. You must +provide the parser with both a parsing method and a default value. The parser +will return the default value if the parsing method fails for any reason. In +this way, you are assured that the parser will not throw an exception. + +Here is an example of a simple parser. It inspects the first record in the +result set and builds an `ItemInfo` object. + + private static class ExpandProfileParser extends ResultSetParser { + @Override + protected ItemInfo defaultValue() { + return new ItemInfo(); + } + + @Override + protected ItemInfo parseResults(String queryStr, ResultSet results) { + ItemInfo item = new ItemInfo(); + if (results.hasNext()) { + QuerySolution solution = results.next(); + item.label = ifLiteralPresent(solution, "label", ""); + item.classLabel = ifLiteralPresent(solution, "classLabel", ""); + item.imageUrl = ifLiteralPresent(solution, "imageUrl", ""); + } + return item; + } + } + +This parser would be used in this way: + + ItemInfo info = createSelectQueryContext(model, queryString) + .execute() + .parse(new ExpandProfileParser()); + diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/RdfServiceConstructQueryContext.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/RdfServiceConstructQueryContext.java new file mode 100644 index 000000000..d3e0a7f19 --- /dev/null +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/RdfServiceConstructQueryContext.java @@ -0,0 +1,88 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.vitro.webapp.utils.sparqlrunner; + +import static edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService.ModelSerializationFormat.NTRIPLE; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.jena.query.QueryExecution; +import org.apache.jena.rdf.model.Model; +import org.apache.jena.rdf.model.ModelFactory; + +import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService; +import edu.cornell.mannlib.vitro.webapp.rdfservice.impl.RDFServiceUtils; +import edu.cornell.mannlib.vitro.webapp.utils.sparqlrunner.SparqlQueryRunner.ConstructQueryContext; +import edu.cornell.mannlib.vitro.webapp.utils.sparqlrunner.SparqlQueryRunner.ExecutingConstructQueryContext; + +/** + * TODO + */ +public class RdfServiceConstructQueryContext implements ConstructQueryContext { + private static final Log log = LogFactory + .getLog(RdfServiceConstructQueryContext.class); + + private final RDFService rdfService; + private final QueryHolder query; + + public RdfServiceConstructQueryContext(RDFService rdfService, + QueryHolder query) { + this.rdfService = rdfService; + this.query = query; + } + + @Override + public ConstructQueryContext bindVariableToUri(String name, String uri) { + return new RdfServiceConstructQueryContext(rdfService, query.bindToUri( + name, uri)); + } + + @Override + public ConstructQueryContext bindVariableToPlainLiteral(String name, + String value) { + return new RdfServiceConstructQueryContext(rdfService, + query.bindToPlainLiteral(name, value)); + } + + @Override + public ExecutingConstructQueryContext execute() { + return new RdfServiceExecutingConstructQueryContext(rdfService, query); + } + + @Override + public String toString() { + return "RdfServiceConstructQueryContext[query=" + query + "]"; + } + + private static class RdfServiceExecutingConstructQueryContext implements + ExecutingConstructQueryContext { + private final RDFService rdfService; + private final QueryHolder query; + + public RdfServiceExecutingConstructQueryContext(RDFService rdfService, + QueryHolder query) { + this.rdfService = rdfService; + this.query = query; + } + + @Override + public Model toModel() { + QueryExecution qe = null; + try { + return RDFServiceUtils.parseModel(rdfService + .sparqlConstructQuery(query.getQueryString(), NTRIPLE), + NTRIPLE); + } catch (Exception e) { + log.error( + "problem while running query '" + + query.getQueryString() + "'", e); + return ModelFactory.createDefaultModel(); + } finally { + if (qe != null) { + qe.close(); + } + } + } + + } +} diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/RdfServiceSelectQueryContext.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/RdfServiceSelectQueryContext.java new file mode 100644 index 000000000..b0a21052c --- /dev/null +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/RdfServiceSelectQueryContext.java @@ -0,0 +1,116 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.vitro.webapp.utils.sparqlrunner; + +import static edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService.ResultFormat.JSON; + +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +import org.apache.commons.io.IOUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.jena.query.ResultSet; + +import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService; +import edu.cornell.mannlib.vitro.webapp.rdfservice.impl.RDFServiceUtils; +import edu.cornell.mannlib.vitro.webapp.utils.sparqlrunner.SparqlQueryRunner.ExecutingSelectQueryContext; +import edu.cornell.mannlib.vitro.webapp.utils.sparqlrunner.SparqlQueryRunner.SelectQueryContext; + +/** + * An implementation of QueryContext based on an RDFService. + * + * Package access. Instances should be created only by SparqlQueryRunner, or by + * a method on this class. + */ +class RdfServiceSelectQueryContext implements SelectQueryContext { + private static final Log log = LogFactory + .getLog(RdfServiceSelectQueryContext.class); + + private final RDFService rdfService; + private final QueryHolder query; + + RdfServiceSelectQueryContext(RDFService rdfService, QueryHolder query) { + this.rdfService = rdfService; + this.query = query; + } + + @Override + public RdfServiceSelectQueryContext bindVariableToUri(String name, + String uri) { + return new RdfServiceSelectQueryContext(rdfService, query.bindToUri( + name, uri)); + } + + @Override + public RdfServiceSelectQueryContext bindVariableToPlainLiteral(String name, + String value) { + return new RdfServiceSelectQueryContext(rdfService, + query.bindToPlainLiteral(name, value)); + } + + @Override + public String toString() { + return "RdfServiceSelectQueryContext[query=" + query + "]"; + } + + @Override + public ExecutingSelectQueryContext execute() { + return new RdfServiceExecutingQueryContext(rdfService, query); + } + + private static class RdfServiceExecutingQueryContext implements + ExecutingSelectQueryContext { + private final RDFService rdfService; + private final QueryHolder query; + + public RdfServiceExecutingQueryContext(RDFService rdfService, + QueryHolder query) { + this.rdfService = rdfService; + this.query = query; + } + + @Override + public StringResultsMapping toStringFields(String... names) { + Set fieldNames = new HashSet<>(Arrays.asList(names)); + try { + ResultSet results = RDFServiceUtils.sparqlSelectQuery( + query.getQueryString(), rdfService); + return new StringResultsMapping(results, fieldNames); + } catch (Exception e) { + log.error( + "problem while running query '" + + query.getQueryString() + "'", e); + return StringResultsMapping.EMPTY; + } + } + + @Override + public T parse(ResultSetParser parser) { + String qString = query.getQueryString(); + try { + return parser.parseResults(qString, + RDFServiceUtils.sparqlSelectQuery(qString, rdfService)); + } catch (Exception e) { + log.error("problem while running query '" + qString + "'", e); + return parser.defaultValue(); + } + } + + @Override + public void writeToOutput(OutputStream output) { + try { + InputStream resultStream = rdfService.sparqlSelectQuery( + query.getQueryString(), JSON); + IOUtils.copy(resultStream, output); + } catch (Exception e) { + log.error( + "problem while running query '" + + query.getQueryString() + "'", e); + } + } + } +} diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/ResultSetParser.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/ResultSetParser.java new file mode 100644 index 000000000..78e4bebb9 --- /dev/null +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/ResultSetParser.java @@ -0,0 +1,57 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.vitro.webapp.utils.sparqlrunner; + +import org.apache.jena.query.QuerySolution; +import org.apache.jena.query.ResultSet; +import org.apache.jena.rdf.model.Literal; +import org.apache.jena.rdf.model.RDFNode; + +/** + * Define the interface for parsing a result set, and provide some helpful + * methods as well. + */ +public abstract class ResultSetParser { + protected abstract T parseResults(String queryStr, ResultSet results); + + protected abstract T defaultValue(); + + protected String ifResourcePresent(QuerySolution solution, + String variableName, String defaultValue) { + RDFNode node = solution.get(variableName); + if (node == null || !node.isURIResource()) { + return defaultValue; + } + return node.asResource().getURI(); + } + + protected String ifLiteralPresent(QuerySolution solution, + String variableName, String defaultValue) { + Literal literal = solution.getLiteral(variableName); + if (literal == null) { + return defaultValue; + } else { + return literal.getString(); + } + } + + protected long ifLongPresent(QuerySolution solution, String variableName, + long defaultValue) { + Literal literal = solution.getLiteral(variableName); + if (literal == null) { + return defaultValue; + } else { + return literal.getLong(); + } + } + + protected int ifIntPresent(QuerySolution solution, String variableName, + int defaultValue) { + Literal literal = solution.getLiteral(variableName); + if (literal == null) { + return defaultValue; + } else { + return literal.getInt(); + } + } +} diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/SparqlQueryRunner.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/SparqlQueryRunner.java new file mode 100644 index 000000000..419ea7105 --- /dev/null +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/SparqlQueryRunner.java @@ -0,0 +1,127 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.vitro.webapp.utils.sparqlrunner; + +import java.io.OutputStream; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.jena.rdf.model.Model; + +import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService; + +/** + * A conversational tool for handling SPARQL queries. + * + *
+ * Examples:
+ *   List values = createSelectQueryContext(rdfService, queryString)
+ *                             .bindVariableToUri("uri", uri)
+ * 				               .execute()
+ * 				               .toStringFields("partner")
+ * 				               .flatten();
+ * 
+ *   QueryHolder qh = queryHolder(queryString)
+ *                             .bindToUri("uri", uri));
+ *   List map = createSelectQueryContext(model, qh)
+ *                             .execute()
+ *                             .toStringFields();
+ * 
+ * + * The query context can come from either an RDFService or a Model. + * + * The execute() method does not actually execute the query: it merely sets it + * up syntactically. + * + * If you don't supply any field names to toStringFields(), you get all of + * them. + * + * Any string value that returns a blank or empty string is omitted from the + * results. Any row that returns no values is omitted from the results. + */ +public final class SparqlQueryRunner { + private static final Log log = LogFactory.getLog(SparqlQueryRunner.class); + + private SparqlQueryRunner() { + // No need to create an instance. + } + + public static QueryHolder queryHolder(String queryString) { + return new QueryHolder(queryString); + } + + // ------------- SELECT ----------- // + + public static SelectQueryContext createSelectQueryContext(RDFService rdfService, + String queryString) { + return createSelectQueryContext(rdfService, queryHolder(queryString)); + } + + public static SelectQueryContext createSelectQueryContext(RDFService rdfService, + QueryHolder query) { + return new RdfServiceSelectQueryContext(rdfService, query); + } + + public static SelectQueryContext createSelectQueryContext(Model model, + String queryString) { + return createSelectQueryContext(model, queryHolder(queryString)); + } + + public static SelectQueryContext createSelectQueryContext(Model model, + QueryHolder query) { + return new ModelSelectQueryContext(model, query); + } + + public static interface SelectQueryContext { + public SelectQueryContext bindVariableToUri(String name, String uri); + + public SelectQueryContext bindVariableToPlainLiteral(String name, + String value); + + public ExecutingSelectQueryContext execute(); + } + + public static interface ExecutingSelectQueryContext { + public StringResultsMapping toStringFields(String... fieldNames); + + public T parse(ResultSetParser parser); + + public void writeToOutput(OutputStream output); + } + + // ------------- CONSTRUCT ----------- // + + public static ConstructQueryContext createConstructQueryContext(RDFService rdfService, + String queryString) { + return createConstructQueryContext(rdfService, queryHolder(queryString)); + } + + public static ConstructQueryContext createConstructQueryContext(RDFService rdfService, + QueryHolder query) { + return new RdfServiceConstructQueryContext(rdfService, query); + } + + public static ConstructQueryContext createConstructQueryContext(Model model, + String queryString) { + return createConstructQueryContext(model, queryHolder(queryString)); + } + + public static ConstructQueryContext createConstructQueryContext(Model model, + QueryHolder query) { + return new ModelConstructQueryContext(model, query); + } + + public static interface ConstructQueryContext{ + public ConstructQueryContext bindVariableToUri(String name, String uri); + + public ConstructQueryContext bindVariableToPlainLiteral(String name, + String value); + + public ExecutingConstructQueryContext execute(); + } + + public static interface ExecutingConstructQueryContext { + public Model toModel(); + } + +} diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/StringResultsMapping.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/StringResultsMapping.java new file mode 100644 index 000000000..a9b9c1ade --- /dev/null +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/StringResultsMapping.java @@ -0,0 +1,95 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.vitro.webapp.utils.sparqlrunner; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.commons.lang.StringUtils; +import org.apache.jena.query.QuerySolution; +import org.apache.jena.query.ResultSet; +import org.apache.jena.rdf.model.RDFNode; + +/** + * Parse the set of fieldNames against the results of the query, and make the + * extracted values available. + */ +public class StringResultsMapping { + public static final StringResultsMapping EMPTY = new StringResultsMapping(); + private final List> listOfMaps; + + public StringResultsMapping() { + this.listOfMaps = Collections.emptyList(); + } + + public StringResultsMapping(ResultSet results, Set fieldNames) { + this.listOfMaps = mapResultsForQuery(results, fieldNames); + } + + private List> mapResultsForQuery(ResultSet results, + Set fieldNames) { + List> mapping = new ArrayList<>(); + while (results.hasNext()) { + Map rowMapping = mapResultsForRow( + results.nextSolution(), fieldNames); + if (!rowMapping.isEmpty()) { + mapping.add(rowMapping); + } + } + return mapping; + } + + private Map mapResultsForRow(QuerySolution row, + Set fieldNames) { + Map map = new HashMap<>(); + for (Iterator names = row.varNames(); names.hasNext();) { + String name = names.next(); + RDFNode node = row.get(name); + String text = getTextForNode(node); + if (StringUtils.isNotBlank(text)) { + map.put(name, text); + } + } + if (!fieldNames.isEmpty()) { + map.keySet().retainAll(fieldNames); + } + return map; + } + + private String getTextForNode(RDFNode node) { + if (node == null) { + return ""; + } else if (node.isLiteral()) { + return node.asLiteral().getString().trim(); + } else { + return node.toString().trim(); + } + } + + public List> getListOfMaps() { + List> list = new ArrayList<>(); + for (Map map: listOfMaps) { + list.add(new HashMap<>(map)); + } + return list; + } + + public List flatten() { + List flat = new ArrayList<>(); + for (Map map : listOfMaps) { + flat.addAll(map.values()); + } + return flat; + } + + public Set flattenToSet() { + return new HashSet<>(flatten()); + } + +} diff --git a/api/src/test/java/edu/cornell/mannlib/vitro/testing/ModelUtilitiesTestHelper.java b/api/src/test/java/edu/cornell/mannlib/vitro/testing/ModelUtilitiesTestHelper.java index 5eb5bee93..16149d35e 100644 --- a/api/src/test/java/edu/cornell/mannlib/vitro/testing/ModelUtilitiesTestHelper.java +++ b/api/src/test/java/edu/cornell/mannlib/vitro/testing/ModelUtilitiesTestHelper.java @@ -31,6 +31,11 @@ public class ModelUtilitiesTestHelper { createResource(classUri)); } + public static Statement objectProperty(String subjectUri, String propertyUri) { + return createStatement(createResource(subjectUri), + createProperty(propertyUri), createResource()); + } + public static Statement objectProperty(String subjectUri, String propertyUri, String objectUri) { return createStatement(createResource(subjectUri), diff --git a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/manageproxies/ProxyRelationshipSelectorTest.java b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/manageproxies/ProxyRelationshipSelectorTest.java index 703d48a9f..83b7c2721 100644 --- a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/manageproxies/ProxyRelationshipSelectorTest.java +++ b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/manageproxies/ProxyRelationshipSelectorTest.java @@ -16,7 +16,6 @@ import java.util.List; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.apache.log4j.Level; import org.junit.BeforeClass; import org.junit.Ignore; import org.junit.Test; @@ -29,7 +28,6 @@ import org.apache.jena.rdf.model.ModelFactory; import edu.cornell.mannlib.vitro.testing.AbstractTestClass; import edu.cornell.mannlib.vitro.webapp.controller.accounts.manageproxies.ProxyRelationshipSelectionCriteria.ProxyRelationshipView; import edu.cornell.mannlib.vitro.webapp.controller.accounts.manageproxies.ProxyRelationshipSelector.Context; -import edu.cornell.mannlib.vitro.webapp.utils.SparqlQueryRunner; /** * TODO diff --git a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/SparqlQueryRunnerTest.java b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/SparqlQueryRunnerTest.java deleted file mode 100644 index 2ae77111e..000000000 --- a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/SparqlQueryRunnerTest.java +++ /dev/null @@ -1,92 +0,0 @@ -/* $This file is distributed under the terms of the license in /doc/license.txt$ */ - -package edu.cornell.mannlib.vitro.webapp.utils; - -import static org.junit.Assert.*; -import static edu.cornell.mannlib.vitro.webapp.utils.SparqlQueryRunner.*; - -import org.junit.Test; - -import edu.cornell.mannlib.vitro.testing.AbstractTestClass; - -/** - * For now, just test the methods that manipulate the query string. - */ -public class SparqlQueryRunnerTest extends AbstractTestClass { - @Test - public void bindValuesNameNotFound() { - String raw = "No such name here"; - String expected = raw; - assertEquals(expected, bindValues(raw, uriValue("bogus", "BOGUS"))); - } - - @Test - public void bindOneUri() { - String raw = "Replace both ?this and ?this also."; - String expected = "Replace both and also."; - assertEquals(expected, bindValues(raw, uriValue("this", "URI"))); - } - - @Test - public void bindTwoUris() { - String raw = "Replace both ?this and ?that also."; - String expected = "Replace both and also."; - assertEquals( - expected, - bindValues(raw, uriValue("this", "URI"), - uriValue("that", "ANOTHER"))); - } - - @Test - public void honorWordBoundary() { - String raw = "Replace ?this but not ?thistle."; - String expected = "Replace but not ?thistle."; - assertEquals(expected, bindValues(raw, uriValue("this", "URI"))); - } - - @Test - public void honorStringLimit() { - String raw = "?this"; - String expected = ""; - assertEquals(expected, bindValues(raw, uriValue("this", "URI"))); - } - - private static final String REAL_WORLD_RAW = "" // - + "PREFIX : \n" // - + "\n" // - + "SELECT DISTINCT ?context ?config \n" // - + "WHERE { \n" // - + " ?context a :ConfigContext ; \n" // - + " :configContextFor ?baseUri ; \n" // - + " :qualifiedByDomain ?domainUri ; \n" // - + " :qualifiedBy ?rangeUri ; \n" // - + " :hasConfiguration ?config . \n" // - + "} \n"; // - - private static final String REAL_WORLD_EXPECTED = "" // - + "PREFIX : \n" // - + "\n" // - + "SELECT DISTINCT ?context ?config \n" // - + "WHERE { \n" // - + " ?context a :ConfigContext ; \n" // - + " :configContextFor ; \n" // - + " :qualifiedByDomain ; \n" // - + " :qualifiedBy ; \n" // - + " :hasConfiguration ?config . \n" // - + "} \n"; // - - @Test - public void realWorldExample() { - assertEquals( - REAL_WORLD_EXPECTED, - bindValues( - REAL_WORLD_RAW, - uriValue("baseUri", - "http://vivoweb.org/ontology/core#relates"), - uriValue("domainUri", - "http://vivoweb.org/ontology/core#Contract"), - uriValue("rangeUri", - "http://vivoweb.org/ontology/core#ResearcherRole"))); - } - -} diff --git a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/QueryHolderTest.java b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/QueryHolderTest.java new file mode 100644 index 000000000..18143d244 --- /dev/null +++ b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/QueryHolderTest.java @@ -0,0 +1,155 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.vitro.webapp.utils.sparqlrunner; + +import static org.junit.Assert.*; + +import org.junit.Test; + +import edu.cornell.mannlib.vitro.testing.AbstractTestClass; + +/** + * Check the functionality of QueryHolder. + */ +public class QueryHolderTest extends AbstractTestClass { + private static final String SOME_URI = "http://some.uri/"; + private static final String SOME_LITERAL = "abracadabra"; + + private static final String FIND_ONCE = "SELECT ?s ?p WHERE { ?s ?p ?bindMe }"; + private static final String URI_BOUND_ONCE = "SELECT ?s ?p WHERE { ?s ?p <" + + SOME_URI + "> }"; + private static final String LITERAL_BOUND_ONCE = "SELECT ?s ?p WHERE { ?s ?p \"" + + SOME_LITERAL + "\" }"; + + private static final String FIND_TWICE = "CONSTRUCT { ?s ?p ?find_twice } WHERE { ?s ?p ?find_twice }"; + private static final String URI_BOUND_TWICE = "CONSTRUCT { ?s ?p <" + + SOME_URI + "> } WHERE { ?s ?p <" + SOME_URI + "> }"; + private static final String LITERAL_BOUND_TWICE = "CONSTRUCT { ?s ?p \"" + + SOME_LITERAL + "\" } WHERE { ?s ?p \"" + SOME_LITERAL + "\" }"; + + private static final String SQUEEZED = "CONSTRUCT {?s ?p ?squeeze} WHERE {?s ?p ?squeeze}"; + private static final String URI_BOUND_SQUEEZED = "CONSTRUCT {?s ?p <" + + SOME_URI + ">} WHERE {?s ?p <" + SOME_URI + ">}"; + private static final String LITERAL_BOUND_SQUEEZED = "CONSTRUCT {?s ?p \"" + + SOME_LITERAL + "\"} WHERE {?s ?p \"" + SOME_LITERAL + "\"}"; + + private static final String FIND_IN_SELECT_CLAUSE = "SELECT ?s ?p ?bindMe WHERE { ?s ?p ?bindMe }"; + private static final String URI_BOUND_IN_SELECT_CLAUSE = "SELECT ?s ?p ?bindMe WHERE { ?s ?p <" + + SOME_URI + "> }"; + + // ---------------------------------------------------------------------- + // The tests + // ---------------------------------------------------------------------- + + @Test + public void hasVariable_findsOne() { + assertTrue(new QueryHolder(FIND_ONCE).hasVariable("bindMe")); + } + + @Test + public void hasVariable_findsTwo() { + assertTrue(new QueryHolder(FIND_TWICE).hasVariable("find_twice")); + } + + @Test + public void hasVariable_doesntFindOne() { + assertFalse(new QueryHolder(FIND_TWICE).hasVariable("notThere")); + } + + @Test + public void hasVariable_notFooledByExtendedName() { + assertFalse(new QueryHolder(FIND_ONCE).hasVariable("bind")); + } + + @Test + public void hasVariable_notFooledByPunctuation() { + assertTrue(new QueryHolder(SQUEEZED).hasVariable("squeeze")); + } + + @Test + public void bindToUri_notFound_noComplaint() { + bindToUri(FIND_ONCE, "notThere", SOME_URI).yields(FIND_ONCE); + } + + @Test + public void bindToUri_findsOne_bindsIt() { + bindToUri(FIND_ONCE, "bindMe", SOME_URI).yields(URI_BOUND_ONCE); + } + + @Test + public void bindToUri_findsTwo_bindsBoth() { + bindToUri(FIND_TWICE, "find_twice", SOME_URI).yields(URI_BOUND_TWICE); + } + + @Test + public void bindToUri_notFooledByExtendedName() { + bindToUri(FIND_ONCE, "bind", SOME_URI).yields(FIND_ONCE); + } + + @Test + public void bindToUri_notFooledByPunctuation() { + bindToUri(SQUEEZED, "squeeze", SOME_URI).yields(URI_BOUND_SQUEEZED); + } + + @Test + public void bindToPlainLiteral_notFound_noComplaint() { + bindToLiteral(FIND_ONCE, "notThere", SOME_LITERAL).yields(FIND_ONCE); + } + + @Test + public void bindToPlainLiteral_findsOne_bindsIt() { + bindToLiteral(FIND_ONCE, "bindMe", SOME_LITERAL) + .yields(LITERAL_BOUND_ONCE); + } + + @Test + public void bindToPlainLiteral_findsTwo_bindsBoth() { + bindToLiteral(FIND_TWICE, "find_twice", SOME_LITERAL) + .yields(LITERAL_BOUND_TWICE); + } + + @Test + public void bindToPlainLiteral_notFooledByExtendedName() { + bindToLiteral(FIND_ONCE, "bind", SOME_LITERAL).yields(FIND_ONCE); + } + + @Test + public void bindToPlainLiteral_notFooledByPunctuation() { + bindToLiteral(SQUEEZED, "squeeze", SOME_LITERAL) + .yields(LITERAL_BOUND_SQUEEZED); + } + + @Test + public void variableInSelectClauseIsLeftAlone() { + bindToUri(FIND_IN_SELECT_CLAUSE, "bindMe", SOME_URI) + .yields(URI_BOUND_IN_SELECT_CLAUSE); + } + + // ---------------------------------------------------------------------- + // Helper methods and classes + // ---------------------------------------------------------------------- + + private Yielder bindToUri(String query, String name, String uri) { + QueryHolder holder = new QueryHolder(query); + QueryHolder bound = holder.bindToUri(name, uri); + return new Yielder(bound.getQueryString()); + } + + private Yielder bindToLiteral(String query, String name, String value) { + QueryHolder holder = new QueryHolder(query); + QueryHolder bound = holder.bindToPlainLiteral(name, value); + return new Yielder(bound.getQueryString()); + } + + private class Yielder { + private final String actual; + + public Yielder(String actual) { + this.actual = actual; + } + + void yields(String expected) { + assertEquals(expected, actual); + } + } +} diff --git a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/ResultSetParserTest.java b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/ResultSetParserTest.java new file mode 100644 index 000000000..326737682 --- /dev/null +++ b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/ResultSetParserTest.java @@ -0,0 +1,331 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.vitro.webapp.utils.sparqlrunner; + +import static edu.cornell.mannlib.vitro.testing.ModelUtilitiesTestHelper.dataProperty; +import static edu.cornell.mannlib.vitro.testing.ModelUtilitiesTestHelper.model; +import static edu.cornell.mannlib.vitro.testing.ModelUtilitiesTestHelper.objectProperty; +import static org.apache.jena.datatypes.xsd.XSDDatatype.XSDinteger; +import static org.apache.jena.datatypes.xsd.XSDDatatype.XSDlong; +import static org.junit.Assert.assertEquals; + +import org.apache.jena.query.QuerySolution; +import org.apache.jena.query.ResultSet; +import org.apache.jena.rdf.model.Model; +import org.apache.log4j.Level; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; + +import edu.cornell.mannlib.vitro.testing.AbstractTestClass; + +public class ResultSetParserTest extends AbstractTestClass { + + private static final String NAMESPACE = "http://namespace#"; + + private static final String SUBJECT = NAMESPACE + "subject"; + + private static final String PREDICATE_URI = NAMESPACE + "uri"; + private static final String OBJECT_URI = NAMESPACE + "objectUri"; + private static final String OBJECT_URI_DEFAULT = NAMESPACE + + "objectUriDefault"; + + private static final String PREDICATE_ANON = NAMESPACE + "anonymous"; + private static final String OBJECT_ANON_DEFAULT = NAMESPACE + "anonDefault"; + + private static final String PREDICATE_STRING = NAMESPACE + "string"; + private static final String OBJECT_STRING = "objectString"; + private static final String OBJECT_STRING_DEFAULT = "objectStringDefault"; + + private static final String PREDICATE_INT = NAMESPACE + "int"; + private static final Integer OBJECT_INT = 4; + private static final Integer OBJECT_INT_DEFAULT = -1; + + private static final String PREDICATE_LONG = NAMESPACE + "long"; + private static final Long OBJECT_LONG = 888L; + private static final Long OBJECT_LONG_DEFAULT = -333L; + + private static final Object PARSING_FAILURE = "PARSING_FAILURE"; + private static final Object NO_RECORDS_FOUND = "NO_RECORDS_FOUND"; + + private static final String SELECT_QUERY = "" // + + "SELECT ?uri ?anonymous ?string ?int ?long \n" // + + "WHERE { \n" // + + " ?s <" + PREDICATE_URI + "> ?uri . \n" // + + " ?s <" + PREDICATE_ANON + "> ?anon . \n" // + + " ?s <" + PREDICATE_STRING + "> ?string . \n" // + + " ?s <" + PREDICATE_INT + "> ?int . \n" // + + " ?s <" + PREDICATE_LONG + "> ?long . \n" // + + "} \n"; + + private Model model; + + @Before + public void setup() { + setLoggerLevel(ModelSelectQueryContext.class, Level.OFF); + + model = model(objectProperty(SUBJECT, PREDICATE_URI, OBJECT_URI), + objectProperty(SUBJECT, PREDICATE_ANON), + dataProperty(SUBJECT, PREDICATE_STRING, OBJECT_STRING), + dataProperty(SUBJECT, PREDICATE_INT, OBJECT_INT, XSDinteger), + dataProperty(SUBJECT, PREDICATE_LONG, OBJECT_LONG, XSDlong)); + } + + // ---------------------------------------------------------------------- + // The tests + // ---------------------------------------------------------------------- + + @Test + public void errorInParse_yieldsDefaultValue() { + assertParsingResult(PARSING_FAILURE, new BaseParser() { + @Override + Object parseOneLine(QuerySolution solution) { + throw new RuntimeException("I refuse to parse this!"); + } + }); + } + + // ---------------------------------------------------------------------- + + @Test + public void expectedUriFoundUri() { + assertParsingResult(OBJECT_URI, new BaseParser() { + @Override + Object parseOneLine(QuerySolution solution) { + return ifResourcePresent(solution, "uri", OBJECT_URI_DEFAULT); + } + }); + } + + @Test + public void expectedUriFoundNothing_returnsDefault() { + assertParsingResult(OBJECT_URI_DEFAULT, new BaseParser() { + @Override + Object parseOneLine(QuerySolution solution) { + return ifResourcePresent(solution, "nothing", + OBJECT_URI_DEFAULT); + } + }); + } + + /** + * TODO Ignoring because the anonymous node isn't showing up in the + * ResultSet. Why not? Anyway, this behaves like "foundNothing". + */ + @Test + @Ignore + public void expectedUriFoundAnonymous_returnsDefault() { + assertParsingResult(OBJECT_URI_DEFAULT, new BaseParser() { + @Override + Object parseOneLine(QuerySolution solution) { + return ifResourcePresent(solution, "anon", OBJECT_URI_DEFAULT); + } + }); + } + + @Test + public void expectedUriFoundString_returnsDefault() { + assertParsingResult(OBJECT_URI_DEFAULT, new BaseParser() { + @Override + Object parseOneLine(QuerySolution solution) { + return ifResourcePresent(solution, "string", OBJECT_URI_DEFAULT); + } + }); + } + + // ---------------------------------------------------------------------- + + @Test + public void expectedStringFoundString() { + assertParsingResult(OBJECT_STRING, new BaseParser() { + @Override + Object parseOneLine(QuerySolution solution) { + return ifLiteralPresent(solution, "string", + OBJECT_STRING_DEFAULT); + } + }); + } + + @Test + public void expectedStringFoundNothing_returnsDefault() { + assertParsingResult(OBJECT_STRING_DEFAULT, new BaseParser() { + @Override + Object parseOneLine(QuerySolution solution) { + return ifLiteralPresent(solution, "nothing", + OBJECT_STRING_DEFAULT); + } + }); + } + + @Test + public void expectedStringFoundResource_fails() { + assertParsingResult(PARSING_FAILURE, new BaseParser() { + @Override + Object parseOneLine(QuerySolution solution) { + return ifLiteralPresent(solution, "uri", OBJECT_STRING_DEFAULT); + } + }); + } + + @Test + public void expectedStringFoundInt_returnsStringValueOfInt() { + assertParsingResult(OBJECT_INT.toString(), new BaseParser() { + @Override + Object parseOneLine(QuerySolution solution) { + return ifLiteralPresent(solution, "int", OBJECT_STRING_DEFAULT); + } + }); + } + + // ---------------------------------------------------------------------- + + @Test + public void expectedIntFoundInt() { + assertParsingResult(OBJECT_INT, new BaseParser() { + @Override + Object parseOneLine(QuerySolution solution) { + return ifIntPresent(solution, "int", OBJECT_INT_DEFAULT); + } + }); + } + + @Test + public void expectedIntFoundNothing() { + assertParsingResult(OBJECT_INT_DEFAULT, new BaseParser() { + @Override + Object parseOneLine(QuerySolution solution) { + return ifIntPresent(solution, "nothing", OBJECT_INT_DEFAULT); + } + }); + } + + @Test + public void expectedIntFoundResource_fails() { + assertParsingResult(PARSING_FAILURE, new BaseParser() { + @Override + Object parseOneLine(QuerySolution solution) { + return ifIntPresent(solution, "uri", OBJECT_INT_DEFAULT); + } + }); + } + + @Test + public void expectedIntFoundString_fails() { + assertParsingResult(PARSING_FAILURE, new BaseParser() { + @Override + Object parseOneLine(QuerySolution solution) { + return ifIntPresent(solution, "string", OBJECT_INT_DEFAULT); + } + }); + } + + @Test + public void expectedIntFoundLong() { + assertParsingResult(new Integer(OBJECT_LONG.intValue()), + new BaseParser() { + @Override + Object parseOneLine(QuerySolution solution) { + return ifIntPresent(solution, "long", + OBJECT_INT_DEFAULT); + } + }); + } + + // ---------------------------------------------------------------------- + + @Test + public void expectedLongFoundLong() { + assertParsingResult(OBJECT_LONG, new BaseParser() { + @Override + Object parseOneLine(QuerySolution solution) { + return ifLongPresent(solution, "long", OBJECT_LONG_DEFAULT); + } + }); + } + + @Test + public void expectedLongFoundNothing() { + assertParsingResult(OBJECT_LONG_DEFAULT, new BaseParser() { + @Override + Object parseOneLine(QuerySolution solution) { + return ifLongPresent(solution, "nothing", OBJECT_LONG_DEFAULT); + } + }); + } + + @Test + public void expectedLongFoundResource_fails() { + assertParsingResult(PARSING_FAILURE, new BaseParser() { + @Override + Object parseOneLine(QuerySolution solution) { + return ifLongPresent(solution, "uri", OBJECT_LONG_DEFAULT); + } + }); + } + + @Test + public void expectedLongFoundString_fails() { + assertParsingResult(PARSING_FAILURE, new BaseParser() { + @Override + Object parseOneLine(QuerySolution solution) { + return ifLongPresent(solution, "string", OBJECT_LONG_DEFAULT); + } + }); + } + + @Test + public void expectedLongFoundInt() { + assertParsingResult(new Long(OBJECT_INT), new BaseParser() { + @Override + Object parseOneLine(QuerySolution solution) { + return ifLongPresent(solution, "int", OBJECT_LONG_DEFAULT); + } + }); + } + + /** + *
+	 * ifLongPresent, return value or default.
+	 *   present, absent
+	 * 
+ */ + // ---------------------------------------------------------------------- + // Helper methods + // ---------------------------------------------------------------------- + + private void assertParsingResult(Object expected, BaseParser parser) { + Object actual = SparqlQueryRunner + .createSelectQueryContext(model, SELECT_QUERY).execute() + .parse(parser); + assertEquals(expected, actual); + } + + private abstract static class BaseParser extends ResultSetParser { + @Override + protected Object defaultValue() { + return PARSING_FAILURE; + } + + @Override + protected Object parseResults(String queryStr, ResultSet results) { + if (results.hasNext()) { + return parseOneLine(results.next()); + } else { + return NO_RECORDS_FOUND; + } + } + + abstract Object parseOneLine(QuerySolution solution); + } + + // private String dumpSolution(QuerySolution solution) { + // Map fields = new HashMap<>(); + // Iterator names = solution.varNames(); + // while (names.hasNext()) { + // String name = names.next(); + // fields.put(name, solution.get(name)); + // } + // return fields.toString(); + // } + // +} diff --git a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/SparqlQueryRunnerTest.java b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/SparqlQueryRunnerTest.java new file mode 100644 index 000000000..c77dcfa24 --- /dev/null +++ b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/SparqlQueryRunnerTest.java @@ -0,0 +1,170 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.vitro.webapp.utils.sparqlrunner; + +import static edu.cornell.mannlib.vitro.testing.ModelUtilitiesTestHelper.dataProperty; +import static edu.cornell.mannlib.vitro.testing.ModelUtilitiesTestHelper.model; +import static edu.cornell.mannlib.vitro.webapp.utils.sparqlrunner.SparqlQueryRunner.createConstructQueryContext; +import static edu.cornell.mannlib.vitro.webapp.utils.sparqlrunner.SparqlQueryRunner.createSelectQueryContext; +import static edu.cornell.mannlib.vitro.webapp.utils.sparqlrunner.SparqlQueryRunner.queryHolder; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import java.io.ByteArrayOutputStream; +import java.io.UnsupportedEncodingException; +import java.util.Arrays; +import java.util.List; + +import org.apache.jena.atlas.json.JSON; +import org.apache.jena.atlas.json.JsonObject; +import org.apache.jena.rdf.model.Model; +import org.junit.Before; +import org.junit.Test; + +import edu.cornell.mannlib.vitro.testing.AbstractTestClass; +import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService; +import edu.cornell.mannlib.vitro.webapp.rdfservice.impl.jena.model.RDFServiceModel; + +/** + * Top-level smoke tests for the SparqlQueryRunnerTest. Exercise all of the + * methods that create contexts, and show that we can do the simplest of + * operations against them. + */ +public class SparqlQueryRunnerTest extends AbstractTestClass { + private static final String SUBJECT_URI = "http://namespace/subject_uri"; + private static final String PREDICATE_URI = "http://namespace/predicate_uri"; + private static final String OBJECT_VALUE = "object_value"; + + private static final String SELECT_QUERY = "SELECT ?s ?p ?o WHERE { ?s ?p ?o . }"; + private static final JsonObject EXPECTED_SELECT_RESULTS = JSON.parse("" + + "{ " // + + " 'head' : { " // + + " 'vars' : [ 's', 'p', 'o' ] " // + + " } , " // + + " 'results' : { " // + + " 'bindings' : [ " // + + " { " // + + " 'p' : { " // + + " 'type' : 'uri' , " // + + " 'value' : 'http://namespace/predicate_uri' " // + + " } , " // + + " 'o' : { " // + + " 'type' : 'literal' , " // + + " 'value' : 'object_value' " // + + " } , " // + + " 's' : { " // + + " 'type' : 'uri' , " // + + " 'value' : 'http://namespace/subject_uri' " // + + " } " // + + " } " // + + " ] " // + + " } " // + + "} "); + + private static final String CONSTRUCT_QUERY = "CONSTRUCT { ?s ?p ?o } WHERE { ?s ?p ?o }"; + + private Model model; + private RDFService rdfService; + private ByteArrayOutputStream buffer; + private Model constructed; + + @Before + public void setup() { + model = model(dataProperty(SUBJECT_URI, PREDICATE_URI, OBJECT_VALUE)); + rdfService = new RDFServiceModel(model); + buffer = new ByteArrayOutputStream(); + } + + // ---------------------------------------------------------------------- + // SELECT tests + // ---------------------------------------------------------------------- + + @Test + public void selectQueryAgainstModel() { + createSelectQueryContext(model, SELECT_QUERY).execute().writeToOutput( + buffer); + assertExpectedSelectResults(); + } + + @Test + public void selectQueryHolderAgainstModel() { + createSelectQueryContext(model, queryHolder(SELECT_QUERY)).execute() + .writeToOutput(buffer); + assertExpectedSelectResults(); + } + + @Test + public void selectQueryAgainstRDFService() { + createSelectQueryContext(rdfService, SELECT_QUERY).execute() + .writeToOutput(buffer); + assertExpectedSelectResults(); + } + + @Test + public void selectQueryHolderAgainstRDFService() { + createSelectQueryContext(rdfService, queryHolder(SELECT_QUERY)) + .execute().writeToOutput(buffer); + assertExpectedSelectResults(); + } + + /** + * We've shown that all select contexts work. It should suffice that one of + * them can convert to string fields. + */ + @Test + public void selectToStringFields() { + List objectValues = createSelectQueryContext(model, + SELECT_QUERY).execute().toStringFields("o").flatten(); + assertEquals(Arrays.asList(OBJECT_VALUE), objectValues); + } + + // ---------------------------------------------------------------------- + // CONSTRUCT tests + // ---------------------------------------------------------------------- + + @Test + public void constructQueryAgainstModel() { + constructed = createConstructQueryContext(model, CONSTRUCT_QUERY) + .execute().toModel(); + assertExpectedConstructResults(); + } + + @Test + public void constructQueryHolderAgainstModel() { + constructed = createConstructQueryContext(model, + queryHolder(CONSTRUCT_QUERY)).execute().toModel(); + assertExpectedConstructResults(); + } + + @Test + public void constructQueryAgainstRDFService() { + constructed = createConstructQueryContext(rdfService, CONSTRUCT_QUERY) + .execute().toModel(); + assertExpectedConstructResults(); + } + + @Test + public void constructQueryHolderAgainstRDFService() { + constructed = createConstructQueryContext(rdfService, + queryHolder(CONSTRUCT_QUERY)).execute().toModel(); + assertExpectedConstructResults(); + } + + // ---------------------------------------------------------------------- + // Helper methods + // ---------------------------------------------------------------------- + + private void assertExpectedSelectResults() { + try { + JsonObject actual = JSON.parse(buffer.toString("UTF-8")); + assertEquals(EXPECTED_SELECT_RESULTS, actual); + } catch (UnsupportedEncodingException e) { + fail(e.toString()); + } + } + + private void assertExpectedConstructResults() { + assertEquals(model.listStatements().toSet(), constructed + .listStatements().toSet()); + } +} diff --git a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/StringResultsMappingTest.java b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/StringResultsMappingTest.java new file mode 100644 index 000000000..0521cbff8 --- /dev/null +++ b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/StringResultsMappingTest.java @@ -0,0 +1,140 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.vitro.webapp.utils.sparqlrunner; + +import static edu.cornell.mannlib.vitro.testing.ModelUtilitiesTestHelper.dataProperty; +import static edu.cornell.mannlib.vitro.testing.ModelUtilitiesTestHelper.model; +import static edu.cornell.mannlib.vitro.webapp.utils.sparqlrunner.SparqlQueryRunner.createSelectQueryContext; +import static org.junit.Assert.assertEquals; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; + +import org.apache.jena.rdf.model.Model; +import org.junit.Before; +import org.junit.Test; + +import edu.cornell.mannlib.vitro.testing.AbstractTestClass; + +public class StringResultsMappingTest extends AbstractTestClass { + private static final String SUBJECT_URI_1 = "http://namespace/subject_uri_1"; + private static final String PREDICATE_URI_1 = "http://namespace/predicate_uri_1"; + private static final String OBJECT_VALUE_1 = "object_value_1"; + private static final String SUBJECT_URI_2 = "http://namespace/subject_uri_2"; + private static final String PREDICATE_URI_2 = "http://namespace/predicate_uri_2"; + private static final String OBJECT_VALUE_2 = "object_value_2"; + + private static final String SELECT_QUERY = "SELECT ?s ?p ?o WHERE { ?s ?p ?o . }"; + + private static final List> LIST_ALL_FIELDS = list( + map(entry("s", SUBJECT_URI_1), entry("p", PREDICATE_URI_1), + entry("o", OBJECT_VALUE_1)), + map(entry("s", SUBJECT_URI_2), entry("p", PREDICATE_URI_2), + entry("o", OBJECT_VALUE_2))); + private static final List> LIST_ONE_FIELD = list( + map(entry("o", OBJECT_VALUE_1)), map(entry("o", OBJECT_VALUE_2))); + + private static final List FLATTEN_ALL_FIELDS = list(SUBJECT_URI_1, + PREDICATE_URI_1, OBJECT_VALUE_1, SUBJECT_URI_2, PREDICATE_URI_2, + OBJECT_VALUE_2); + private static final List FLATTEN_ONE_FIELD = list(OBJECT_VALUE_1, + OBJECT_VALUE_2); + + private Model model; + + @Before + public void setup() { + model = model( + dataProperty(SUBJECT_URI_1, PREDICATE_URI_1, OBJECT_VALUE_1), + dataProperty(SUBJECT_URI_2, PREDICATE_URI_2, OBJECT_VALUE_2)); + } + + @Test + public void checkMapsOfAllFields() { + assertEquivalentUnorderedLists(LIST_ALL_FIELDS, + createSelectQueryContext(model, SELECT_QUERY).execute() + .toStringFields().getListOfMaps()); + } + + @Test + public void checkMapsOfOneField() { + assertEquivalentUnorderedLists(LIST_ONE_FIELD, + createSelectQueryContext(model, SELECT_QUERY).execute() + .toStringFields("o").getListOfMaps()); + } + + @Test + public void checkFlattenAllFields() { + assertEquivalentUnorderedLists(FLATTEN_ALL_FIELDS, + createSelectQueryContext(model, SELECT_QUERY).execute() + .toStringFields().flatten()); + } + + @Test + public void checkFlattenOneField() { + assertEquivalentUnorderedLists(FLATTEN_ONE_FIELD, + createSelectQueryContext(model, SELECT_QUERY).execute() + .toStringFields("o").flatten()); + } + + @Test + public void checkFlattenToSet() { + assertEquals(new HashSet<>(FLATTEN_ONE_FIELD), + createSelectQueryContext(model, SELECT_QUERY).execute() + .toStringFields("o").flattenToSet()); + } + + // ---------------------------------------------------------------------- + // Helper methods + // ---------------------------------------------------------------------- + + private void assertEquivalentUnorderedLists(List list1, List list2) { + Collections.sort(list1, new ArbitraryOrder<>()); + Collections.sort(list2, new ArbitraryOrder<>()); + assertEquals(list1, list2); + } + + private static class ArbitraryOrder implements Comparator { + @Override + public int compare(T t1, T t2) { + return t1.hashCode() - t2.hashCode(); + } + } + + @SafeVarargs + private static List list(T... items) { + List l = new ArrayList<>(); + for (T item : items) { + l.add(item); + } + return l; + } + + private static Map map(Entry... entries) { + Map m = new HashMap<>(); + for (Entry entry : entries) { + m.put(entry.key, entry.value); + } + return m; + } + + private static Entry entry(String key, String value) { + return new Entry(key, value); + } + + private static class Entry { + final String key; + final String value; + + Entry(String key, String value) { + this.key = key; + this.value = value; + } + } + +}