From 190bf87979cc8587bd6fcda62682a33bc42a7db2 Mon Sep 17 00:00:00 2001 From: Jim Blake Date: Fri, 31 Oct 2014 18:17:38 -0400 Subject: [PATCH] VIVO-774 More functionality: implement deleteFauxProperty() --- .../vitro/webapp/beans/FauxProperty.java | 39 ++- .../edit/FauxPropertyRetryController.java | 119 +++++++- .../vitro/webapp/dao/FauxPropertyDao.java | 13 + .../filtering/FauxPropertyDaoFiltering.java | 10 + .../webapp/dao/jena/FauxPropertyDaoJena.java | 287 +++++++++++++----- .../vitro/webapp/utils/SparqlQueryRunner.java | 31 ++ .../webapp/utils/SparqlQueryRunnerTest.java | 92 ++++++ .../edit/specific/fauxProperty_retry.jsp | 6 + .../templates/edit/specific/props_edit.jsp | 32 +- 9 files changed, 519 insertions(+), 110 deletions(-) create mode 100644 webapp/test/edu/cornell/mannlib/vitro/webapp/utils/SparqlQueryRunnerTest.java create mode 100644 webapp/web/templates/edit/specific/fauxProperty_retry.jsp diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/beans/FauxProperty.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/beans/FauxProperty.java index 08a440c47..c3025b3ab 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/beans/FauxProperty.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/beans/FauxProperty.java @@ -6,15 +6,27 @@ import static com.hp.hpl.jena.rdf.model.ResourceFactory.createResource; import java.util.Objects; +import edu.cornell.mannlib.vitro.webapp.auth.policy.bean.RoleRestrictedProperty; + /** * Represents a specialization on an ObjectProperty, only meaningful for * display. * - * Must have a baseURI and a rangeURI. Other fields are optional. + * BaseURI is required, may not be null, and may not be modified. + * + * It would be nice to place the same restrictions on rangeURI, but it may be + * null when the FauxProperty is being created, and it may be modified. The DAO + * will need to check rangeURI for validity before accepting an insert or + * modification. + * + * TODO Can we do this more cleanly? Can handle this as two classes FauxProperty + * and NewFauxProperty, and have each class enforce its own internal + * constraints? For example, the range must not be null, must represent a valid + * class, and must be equal to or a subclass of the range of the base property. */ -public class FauxProperty extends BaseResourceBean implements ResourceBean { - private final String rangeURI; - private final String domainURI; +public class FauxProperty extends BaseResourceBean implements ResourceBean, RoleRestrictedProperty { + private String rangeURI; + private String domainURI; private String rangeLabel; private String domainLabel; @@ -28,14 +40,21 @@ public class FauxProperty extends BaseResourceBean implements ResourceBean { * @param baseURI * URI of the property. May not be null. * @param rangeUri - * URI of the object class. May not be null. + * URI of the object class. May be null. */ public FauxProperty(String domainURI, String baseURI, String rangeURI) { super(Objects.requireNonNull(baseURI, "baseURI may not be null")); - this.rangeURI = Objects.requireNonNull(rangeURI, - "rangeURI may not be null"); + this.rangeURI = rangeURI; this.domainURI = domainURI; } + + public FauxProperty() { + // This is required by OperationUtils.cloneBean() + } + + public void setRangeURI(String rangeURI) { + this.rangeURI = rangeURI; + } public String getRangeURI() { return rangeURI; @@ -49,6 +68,10 @@ public class FauxProperty extends BaseResourceBean implements ResourceBean { return (rangeLabel == null) ? localName(rangeURI) : rangeLabel; } + public void setDomainURI(String domainURI) { + this.domainURI = domainURI; + } + public String getDomainURI() { return domainURI; } @@ -61,7 +84,7 @@ public class FauxProperty extends BaseResourceBean implements ResourceBean { return (domainLabel == null) ? (domainURI == null ? "null" : localName(domainURI)) : domainLabel; } - + private String localName(String uriString) { try { return createResource(uriString).getLocalName(); diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/edit/FauxPropertyRetryController.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/edit/FauxPropertyRetryController.java index 57d771a50..93838c4d7 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/edit/FauxPropertyRetryController.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/edit/FauxPropertyRetryController.java @@ -2,16 +2,36 @@ package edu.cornell.mannlib.vitro.webapp.controller.edit; +import java.util.Collections; + +import javax.servlet.RequestDispatcher; +import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import edu.cornell.mannlib.vedit.beans.EditProcessObject; +import edu.cornell.mannlib.vedit.beans.FormObject; import edu.cornell.mannlib.vedit.controller.BaseEditController; +import edu.cornell.mannlib.vedit.util.FormUtils; import edu.cornell.mannlib.vitro.webapp.auth.permissions.SimplePermission; +import edu.cornell.mannlib.vitro.webapp.auth.policy.bean.PropertyRestrictionListener; +import edu.cornell.mannlib.vitro.webapp.beans.FauxProperty; +import edu.cornell.mannlib.vitro.webapp.controller.Controllers; +import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; +import edu.cornell.mannlib.vitro.webapp.dao.FauxPropertyDao; +import edu.cornell.mannlib.vitro.webapp.modelaccess.ModelAccess; /** * TODO */ public class FauxPropertyRetryController extends BaseEditController { + private static final Log log = LogFactory + .getLog(FauxPropertyRetryController.class); + + @Override public void doPost(HttpServletRequest req, HttpServletResponse response) { if (!isAuthorizedToDisplayPage(req, response, @@ -19,13 +39,98 @@ public class FauxPropertyRetryController extends BaseEditController { return; } - // TODO - } - - @Override - public void doGet (HttpServletRequest request, HttpServletResponse response) { - doPost(request, response); - } + VitroRequest request = new VitroRequest(req); + // create an EditProcessObject for this and put it in the session + EditProcessObject epo = super.createEpo(request); + + ServletContext ctx = getServletContext(); + + FauxPropertyDao fpDao = ModelAccess.on(ctx).getWebappDaoFactory() + .getFauxPropertyDao(); + epo.setDataAccessObject(fpDao); + + FauxProperty fpForEditing = null; + if (epo.getUseRecycledBean()) { + fpForEditing = (FauxProperty) epo.getNewBean(); + } else { + String create = request.getParameter("create"); + String baseUri = request.getParameter("baseUri"); + String rangeUri = request.getParameter("rangeUri"); + String domainUri = request.getParameter("domainUri"); + if (create != null) { + fpForEditing = new FauxProperty(null, baseUri, null); + epo.setAction("insert"); + } else { + fpForEditing = fpDao.getFauxPropertyByUris(domainUri, baseUri, + rangeUri); + if (fpForEditing == null) { + throw new IllegalArgumentException( + "FauxProperty does not exist for <" + domainUri + + "> ==> <" + baseUri + "> ==> <" + + rangeUri + ">"); + } + epo.setAction("update"); + } + epo.setOriginalBean(fpForEditing); + } + + // set any validators + // TODO NONE YET + + // set up any listeners + epo.setChangeListenerList(Collections + .singletonList(new PropertyRestrictionListener(ctx))); + + // where should the postinsert pageforwarder go? + // TODO + // make a postdelete pageforwarder that will send us to the control + // panel for the base property. + // TODO + + FormObject foo = new FormObject(); + foo.setErrorMap(epo.getErrMsgMap()); + + // We will need to set a lot of option lists and stuff. + // TODO + + // Put attributes on the request so the JSP can populate the fields. + // request.setAttribute("transitive",propertyForEditing.getTransitive()); + // request.setAttribute("objectIndividualSortPropertyURI", + // propertyForEditing.getObjectIndividualSortPropertyURI()); + // TODO + + // checkboxes are pretty annoying : we don't know if someone *unchecked* + // a box, so we have to default to false on updates. + // propertyForEditing.setSymmetric(false); + // TODO + + epo.setFormObject(foo); + + FormUtils.populateFormFromBean(fpForEditing, epo.getAction(), foo, + epo.getBadValueMap()); + + RequestDispatcher rd = request.getRequestDispatcher(Controllers.BASIC_JSP); + request.setAttribute("bodyJsp","/templates/edit/formBasic.jsp"); + request.setAttribute("colspan","5"); + request.setAttribute("formJsp","/templates/edit/specific/fauxProperty_retry.jsp"); + request.setAttribute("scripts","/templates/edit/formBasic.js"); + request.setAttribute("title","Faux Property Editing Form"); + request.setAttribute("_action",epo.getAction()); + setRequestAttributes(request,epo); + + try { + rd.forward(request, response); + } catch (Exception e) { + log.error("Could not forward to view."); + log.error(e.getMessage()); + log.error(e.getStackTrace()); + } + } + + @Override + public void doGet(HttpServletRequest request, HttpServletResponse response) { + doPost(request, response); + } } diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/FauxPropertyDao.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/FauxPropertyDao.java index 8edfefa9b..8579e1b85 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/FauxPropertyDao.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/FauxPropertyDao.java @@ -42,4 +42,17 @@ public interface FauxPropertyDao { FauxProperty getFauxPropertyByUris(String domainUri, String baseUri, String rangeUri); + /** + * Delete this FauxProperty from the display model. + * + * Delete any ConfigContext that is based on the constraints in this + * FauxProperty, and any ObjectPropertyDisplayConfigs that depend on that + * ConfigContext. + * + * If no such ConfigContext is found, no error is raised. + * + * No check is made to see whether the ObjectPropertyDisplayConfig matches + * the settings on this FauxProperty. + */ + void deleteFauxProperty(FauxProperty fp); } diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/filtering/FauxPropertyDaoFiltering.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/filtering/FauxPropertyDaoFiltering.java index 8d34d7d2b..ac7442a24 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/filtering/FauxPropertyDaoFiltering.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/filtering/FauxPropertyDaoFiltering.java @@ -48,4 +48,14 @@ public class FauxPropertyDaoFiltering extends BaseFiltering implements FauxPrope } + /* (non-Javadoc) + * @see edu.cornell.mannlib.vitro.webapp.dao.FauxPropertyDao#deleteFauxProperty(edu.cornell.mannlib.vitro.webapp.beans.FauxProperty) + */ + @Override + public void deleteFauxProperty(FauxProperty fp) { + // TODO Auto-generated method stub + throw new RuntimeException("FauxPropertyDao.deleteFauxProperty() not implemented."); + + } + } diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/FauxPropertyDaoJena.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/FauxPropertyDaoJena.java index 3201d9b4f..7cbb2f4b3 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/FauxPropertyDaoJena.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/FauxPropertyDaoJena.java @@ -4,16 +4,21 @@ package edu.cornell.mannlib.vitro.webapp.dao.jena; import static com.hp.hpl.jena.rdf.model.ResourceFactory.createProperty; import static com.hp.hpl.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.Collections; +import java.util.HashSet; import java.util.List; +import java.util.Set; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import com.hp.hpl.jena.ontology.OntModel; +import com.hp.hpl.jena.query.QuerySolution; import com.hp.hpl.jena.query.ResultSet; -import com.hp.hpl.jena.query.Syntax; import com.hp.hpl.jena.rdf.model.Property; import com.hp.hpl.jena.rdf.model.RDFNode; import com.hp.hpl.jena.rdf.model.ResIterator; @@ -23,6 +28,7 @@ import com.hp.hpl.jena.vocabulary.RDF; import edu.cornell.mannlib.vitro.webapp.beans.FauxProperty; import edu.cornell.mannlib.vitro.webapp.dao.FauxPropertyDao; +import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary; import edu.cornell.mannlib.vitro.webapp.rdfservice.adapters.AbstractOntModelDecorator; import edu.cornell.mannlib.vitro.webapp.utils.SparqlQueryRunner; import edu.cornell.mannlib.vitro.webapp.utils.SparqlQueryRunner.QueryParser; @@ -51,49 +57,12 @@ public class FauxPropertyDaoJena implements FauxPropertyDao { private static final Property DISPLAY_NAME = createProperty(APPLICATION_CONTEXT_NS + "displayName"); + private static final Property RDFS_LABEL = createProperty(VitroVocabulary.LABEL); // ---------------------------------------------------------------------- // Queries and parsers // ---------------------------------------------------------------------- - private static final String QUERY_LOCATE_CONFIG_CONTEXT_WITH_DOMAIN = "" // - + "PREFIX : \n" // - + "\n" // - + "SELECT DISTINCT ?context \n" // - + "WHERE { \n" // - + " ?context a :ConfigContext ; \n" // - + " :configContextFor ?baseUri ; \n" // - + " :qualifiedByDomain ?domainUri ; \n" // - + " :qualifiedBy ?rangeUri . \n" // - + "} \n"; // - - // TODO Add a filter that will reject solutions that include qualifiedByDomain - private static final String QUERY_LOCATE_CONFIG_CONTEXT_WITH_NO_DOMAIN = "" // - + "PREFIX : \n" // - + "\n" // - + "SELECT DISTINCT ?context \n" // - + "WHERE { \n" // - + " ?context a :ConfigContext ; \n" // - + " :configContextFor ?baseUri ; \n" // - + " :qualifiedBy ?rangeUri . \n" // - + "} \n"; // - - private static final QueryParser PARSER_LOCATE_CONFIG_CONTEXT = new QueryParser() { - @Override - protected String defaultValue() { - return null; - } - - @Override - protected String parseResults(String queryStr, ResultSet results) { - if (results.hasNext()) { - return ifResourcePresent(results.next(), "context", null); - } else { - return null; - } - } - }; - // ---------------------------------------------------------------------- // The instance // ---------------------------------------------------------------------- @@ -164,7 +133,7 @@ public class FauxPropertyDaoJena implements FauxPropertyDao { QUALIFIED_BY_DOMAIN); FauxProperty fp = new FauxProperty(domainUri, baseUri, rangeUri); - populateInstance(fp, displayModel, context); + populateInstance(fp, context); return fp; } } @@ -172,63 +141,74 @@ public class FauxPropertyDaoJena implements FauxPropertyDao { @Override public FauxProperty getFauxPropertyByUris(String domainUri, String baseUri, String rangeUri) { - try (LockedOntModel displayModel = models.getDisplayModel().read()) { - String queryString; - if (domainUri == null) { - queryString = substituteUri( - substituteUri( - QUERY_LOCATE_CONFIG_CONTEXT_WITH_NO_DOMAIN, - baseUri, "baseUri"), rangeUri, "rangeUri"); - } else { - queryString = substituteUri( - substituteUri( - substituteUri( - QUERY_LOCATE_CONFIG_CONTEXT_WITH_DOMAIN, - baseUri, "baseUri"), rangeUri, - "rangeUri"), domainUri, "domainUri"); - } - - String contextUri = new SparqlQueryRunner(displayModel) - .executeSelect(PARSER_LOCATE_CONFIG_CONTEXT, queryString); - - if (contextUri == null) { - log.debug("Can't find a ContextConfig for '" + domainUri - + "', '" + baseUri + "', '" + rangeUri + "'"); - return null; - } - + Set contexts = ConfigContext.findByQualifiers(models, + domainUri, baseUri, rangeUri); + for (ConfigContext context : contexts) { FauxProperty fp = new FauxProperty(domainUri, baseUri, rangeUri); - populateInstance(fp, displayModel, createResource(contextUri)); + populateInstance(fp, createResource(context.getContextUri())); return fp; } + log.debug("Can't find a FauxProperty for '" + domainUri + "', '" + + baseUri + "', '" + rangeUri + "'"); + return null; + } + + @Override + public void deleteFauxProperty(FauxProperty fp) { + Set contexts = ConfigContext.findByQualifiers(models, + fp.getDomainURI(), fp.getURI(), fp.getRangeURI()); + try (LockedOntModel displayModel = models.getDisplayModel().write()) { + for (ConfigContext context : contexts) { + Resource configResource = createResource(context.getConfigUri()); + displayModel.removeAll(configResource, null, null); + displayModel.removeAll(null, null, configResource); + Resource contextResource = createResource(context.getContextUri()); + displayModel.removeAll(contextResource, null, null); + displayModel.removeAll(null, null, contextResource); + } + } } /** * Add labels, annotations, and whatever else we can find on the - * ConfigContext. + * ObjectPropertyDisplayConfig. */ - private void populateInstance(FauxProperty fp, LockedOntModel model, - Resource context) { - String configUri = getUriValue(model, context, HAS_CONFIGURATION); - if (configUri == null) { - return; - } - Resource config = createResource(configUri); + private void populateInstance(FauxProperty fp, Resource context) { + // Range label and domain label. + try (LockedOntModel tboxModel = models.getTBoxModel().read()) { + String rangeLabel = getStringValue(tboxModel, + createProperty(fp.getRangeURI()), RDFS_LABEL); + if (rangeLabel != null) { + fp.setRangeLabel(rangeLabel); + } + + String domainLabel = getStringValue(tboxModel, + createProperty(fp.getDomainURI()), RDFS_LABEL); + if (domainLabel != null) { + fp.setDomainLabel(domainLabel); + } - String displayName = getStringValue(model, config, DISPLAY_NAME); - if (displayName != null) { - fp.setPickListName(displayName); } - + + // Display name. + try (LockedOntModel displayModel = models.getDisplayModel().read()) { + String configUri = getUriValue(displayModel, context, + HAS_CONFIGURATION); + if (configUri == null) { + return; + } + Resource config = createResource(configUri); + + String displayName = getStringValue(displayModel, config, + DISPLAY_NAME); + if (displayName != null) { + fp.setPickListName(displayName); + } + } + // TODO pull all sorts of things from the configuration. - // TODO pull labels for the domain and range classes. } - private String substituteUri(String queryString, String variableName, - String uri) { - return queryString.replace("?" + variableName, "<" + uri + ">"); - } - /** * Returns a single URI that is the object of this subject and property. * Returns null if no valid statement is found. @@ -283,6 +263,135 @@ public class FauxPropertyDaoJena implements FauxPropertyDao { return node.asLiteral().getString(); } + // ---------------------------------------------------------------------- + // ConfigContext + // ---------------------------------------------------------------------- + + private static final String QUERY_LOCATE_CONFIG_CONTEXT_WITH_DOMAIN = "" // + + "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"; // + + // TODO Add a filter that will reject solutions that include + // qualifiedByDomain + private static final String QUERY_LOCATE_CONFIG_CONTEXT_WITH_NO_DOMAIN = "" // + + "PREFIX : \n" // + + "\n" // + + "SELECT DISTINCT ?context \n" // + + "WHERE { \n" // + + " ?context a :ConfigContext ; \n" // + + " :configContextFor ?baseUri ; \n" // + + " :qualifiedBy ?rangeUri ; \n" // + + " :hasConfiguration ?config . \n" // + + "} \n"; // + + private static class ParserLocateConfigContext extends + QueryParser> { + private final String domainUri; + private final String baseUri; + private final String rangeUri; + + public ParserLocateConfigContext(String domainUri, String baseUri, + String rangeUri) { + this.domainUri = domainUri; + this.baseUri = baseUri; + this.rangeUri = rangeUri; + } + + @Override + protected Set defaultValue() { + return Collections.emptySet(); + } + + @Override + protected Set parseResults(String queryStr, + ResultSet results) { + Set set = new HashSet<>(); + while (results.hasNext()) { + QuerySolution row = results.next(); + String contextUri = ifResourcePresent(row, "context", null); + String configUri = ifResourcePresent(row, "config", null); + if (contextUri != null && configUri != null) { + set.add(new ConfigContext(contextUri, configUri, domainUri, + baseUri, rangeUri)); + } + } + return set; + } + } + + private static class ConfigContext { + public static Set findByQualifiers( + LockingOntModelSelector models, String domainUri, + String baseUri, String rangeUri) { + try (LockedOntModel displayModel = models.getDisplayModel().read()) { + + String queryString; + if (domainUri == null) { + queryString = bindValues( + QUERY_LOCATE_CONFIG_CONTEXT_WITH_NO_DOMAIN, + uriValue("baseUri", baseUri), + uriValue("rangeUri", rangeUri)); + } else { + queryString = bindValues( + QUERY_LOCATE_CONFIG_CONTEXT_WITH_DOMAIN, + uriValue("baseUri", baseUri), + uriValue("rangeUri", rangeUri), + uriValue("domainUri", domainUri)); + } + + ParserLocateConfigContext parser = new ParserLocateConfigContext( + domainUri, baseUri, rangeUri); + return new SparqlQueryRunner(displayModel).executeSelect( + parser, queryString); + } + + } + + private final String contextUri; + private final String configUri; + private final String domainUri; + private final String baseUri; + private final String rangeUri; + + public ConfigContext(String contextUri, String configUri, + String domainUri, String baseUri, String rangeUri) { + this.contextUri = contextUri; + this.configUri = configUri; + this.domainUri = domainUri; + this.baseUri = baseUri; + this.rangeUri = rangeUri; + } + + public String getContextUri() { + return contextUri; + } + + public String getConfigUri() { + return configUri; + } + + public String getDomainUri() { + return domainUri; + } + + public String getBaseUri() { + return baseUri; + } + + public String getRangeUri() { + return rangeUri; + } + + } + // ---------------------------------------------------------------------- // Helper classes. Are they worth it, just to use try-with-resources? // ---------------------------------------------------------------------- @@ -297,6 +406,10 @@ public class FauxPropertyDaoJena implements FauxPropertyDao { public LockableOntModel getDisplayModel() { return new LockableOntModel(oms.getDisplayModel()); } + + public LockableOntModel getTBoxModel() { + return new LockableOntModel(oms.getTBoxModel()); + } } private static class LockableOntModel { @@ -310,6 +423,11 @@ public class FauxPropertyDaoJena implements FauxPropertyDao { ontModel.enterCriticalSection(Lock.READ); return new LockedOntModel(ontModel); } + + public LockedOntModel write() { + ontModel.enterCriticalSection(Lock.WRITE); + return new LockedOntModel(ontModel); + } } private static class LockedOntModel extends AbstractOntModelDecorator @@ -328,4 +446,5 @@ public class FauxPropertyDaoJena implements FauxPropertyDao { super.leaveCriticalSection(); } } + } diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/SparqlQueryRunner.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/SparqlQueryRunner.java index 6ffb697ca..f2fda46dc 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/SparqlQueryRunner.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/SparqlQueryRunner.java @@ -136,5 +136,36 @@ public class SparqlQueryRunner { } } + + public static String bindValues(String rawString, VariableValue... values) { + String queryString = rawString; + for (VariableValue value: values) { + queryString = value.bind(queryString); + } + return queryString; + } + + public static UriValue uriValue(String name, String uri) { + return new UriValue(name, uri); + } + public interface VariableValue { + String bind(String rawString); + } + + private static class UriValue implements VariableValue { + private final String name; + private final String uri; + + public UriValue(String name, String uri) { + this.name = name; + this.uri = uri; + } + + @Override + public String bind(String rawString) { + return rawString.replaceAll("\\?" + name + "\\b", "<" + uri + ">"); + } + + } } diff --git a/webapp/test/edu/cornell/mannlib/vitro/webapp/utils/SparqlQueryRunnerTest.java b/webapp/test/edu/cornell/mannlib/vitro/webapp/utils/SparqlQueryRunnerTest.java new file mode 100644 index 000000000..2ae77111e --- /dev/null +++ b/webapp/test/edu/cornell/mannlib/vitro/webapp/utils/SparqlQueryRunnerTest.java @@ -0,0 +1,92 @@ +/* $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/webapp/web/templates/edit/specific/fauxProperty_retry.jsp b/webapp/web/templates/edit/specific/fauxProperty_retry.jsp new file mode 100644 index 000000000..f98542e88 --- /dev/null +++ b/webapp/web/templates/edit/specific/fauxProperty_retry.jsp @@ -0,0 +1,6 @@ +<%-- $This file is distributed under the terms of the license in /doc/license.txt$ --%> + +<%@ taglib prefix="form" uri="http://vitro.mannlib.cornell.edu/edit/tags" %> +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> + +

Like, TOTALLY BOGUS.

\ No newline at end of file diff --git a/webapp/web/templates/edit/specific/props_edit.jsp b/webapp/web/templates/edit/specific/props_edit.jsp index cad21169b..dc9cc1905 100644 --- a/webapp/web/templates/edit/specific/props_edit.jsp +++ b/webapp/web/templates/edit/specific/props_edit.jsp @@ -98,18 +98,25 @@