Implement insert and update in FauxPropertyDaoJena

This commit is contained in:
Jim Blake 2014-11-05 11:37:52 -05:00
parent c8368dbe2d
commit 852da3ff2a
6 changed files with 743 additions and 170 deletions

View file

@ -11,26 +11,36 @@ import edu.cornell.mannlib.vitro.webapp.auth.policy.bean.RoleRestrictedProperty;
/**
* Represents a specialization on an ObjectProperty, only meaningful for
* display.
*
* 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, RoleRestrictedProperty {
public class FauxProperty extends BaseResourceBean implements ResourceBean,
RoleRestrictedProperty {
// Must be null on insert. Must not be null on update. Ignored on delete.
private String contextUri;
// Must be null on insert. Must not be null on update. Ignored on delete.
private String configUri;
// Must not be null on insert or update. Partial identifier on delete.
private String rangeURI;
// May be null. Partial identifier on delete.
private String domainURI;
private String rangeLabel;
private String domainLabel;
private String groupURI;
private String publicDescription;
private int displayTier;
private int displayLimit;
private boolean collateBySubclass;
private boolean selectFromExisting;
private boolean offerCreateNewOption;
private String customEntryForm;
private String customListView;
/**
* Arguments are in this order to mimic the relationship: subject ==>
* property ==> object
@ -52,32 +62,53 @@ public class FauxProperty extends BaseResourceBean implements ResourceBean, Role
// This is required by OperationUtils.cloneBean()
}
public void setRangeURI(String rangeURI) {
this.rangeURI = rangeURI;
public String getContextUri() {
return contextUri;
}
public void setContextUri(String contextUri) {
this.contextUri = contextUri;
}
public String getConfigUri() {
return configUri;
}
public void setConfigUri(String configUri) {
this.configUri = configUri;
}
// BaseURI becomes an alias for URI
public String getBaseURI() {
return getURI();
}
public void setBaseURI(String baseURI) {
setURI(baseURI);
}
public String getRangeURI() {
return rangeURI;
}
public void setRangeLabel(String rangeLabel) {
this.rangeLabel = rangeLabel;
public void setRangeURI(String rangeURI) {
this.rangeURI = rangeURI;
}
public String getRangeLabel() {
return (rangeLabel == null) ? localName(rangeURI) : rangeLabel;
}
public void setDomainURI(String domainURI) {
this.domainURI = domainURI;
public void setRangeLabel(String rangeLabel) {
this.rangeLabel = rangeLabel;
}
public String getDomainURI() {
return domainURI;
}
public void setDomainLabel(String domainLabel) {
this.domainLabel = domainLabel;
public void setDomainURI(String domainURI) {
this.domainURI = domainURI;
}
public String getDomainLabel() {
@ -85,6 +116,91 @@ public class FauxProperty extends BaseResourceBean implements ResourceBean, Role
: localName(domainURI)) : domainLabel;
}
public void setDomainLabel(String domainLabel) {
this.domainLabel = domainLabel;
}
public String getGroupURI() {
return groupURI;
}
public void setGroupURI(String groupURI) {
this.groupURI = groupURI;
}
// DisplayName becomes an alias for PickListName
public String getDisplayName() {
return getPickListName();
}
public void setDisplayName(String displayName) {
setPickListName(displayName);
}
public String getPublicDescription() {
return publicDescription;
}
public void setPublicDescription(String publicDescription) {
this.publicDescription = publicDescription;
}
public int getDisplayTier() {
return displayTier;
}
public void setDisplayTier(int displayTier) {
this.displayTier = displayTier;
}
public int getDisplayLimit() {
return displayLimit;
}
public void setDisplayLimit(int displayLimit) {
this.displayLimit = displayLimit;
}
public boolean isCollateBySubclass() {
return collateBySubclass;
}
public void setCollateBySubclass(boolean collateBySubclass) {
this.collateBySubclass = collateBySubclass;
}
public boolean isSelectFromExisting() {
return selectFromExisting;
}
public void setSelectFromExisting(boolean selectFromExisting) {
this.selectFromExisting = selectFromExisting;
}
public boolean isOfferCreateNewOption() {
return offerCreateNewOption;
}
public void setOfferCreateNewOption(boolean offerCreateNewOption) {
this.offerCreateNewOption = offerCreateNewOption;
}
public String getCustomEntryForm() {
return customEntryForm;
}
public void setCustomEntryForm(String customEntryForm) {
this.customEntryForm = customEntryForm;
}
public String getCustomListView() {
return customListView;
}
public void setCustomListView(String customListView) {
this.customListView = customListView;
}
private String localName(String uriString) {
try {
return createResource(uriString).getLocalName();
@ -93,4 +209,18 @@ public class FauxProperty extends BaseResourceBean implements ResourceBean, Role
}
}
@Override
public String toString() {
return "FauxProperty[domainURI=" + domainURI + ", baseUri=" + getURI()
+ ", rangeURI=" + rangeURI + ", rangeLabel=" + rangeLabel
+ ", domainLabel=" + domainLabel + ", pickListName="
+ getPickListName() + ", groupURI=" + groupURI
+ "publicDescription=" + publicDescription + ", displayTier="
+ displayTier + ", displayLimit=" + displayLimit
+ ", collateBySubclass=" + collateBySubclass
+ ", selectFromExisting=" + selectFromExisting
+ ", offerCreateNewOption=" + offerCreateNewOption
+ ", customEntryForm=" + customEntryForm + ", customListView="
+ customListView + "]";
}
}

View file

@ -2,8 +2,13 @@
package edu.cornell.mannlib.vitro.webapp.controller.edit;
import static edu.cornell.mannlib.vitro.webapp.modelaccess.ModelAccess.POLICY_NEUTRAL;
import java.text.Collator;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -21,12 +26,16 @@ import edu.cornell.mannlib.vedit.beans.FormObject;
import edu.cornell.mannlib.vedit.beans.Option;
import edu.cornell.mannlib.vedit.controller.BaseEditController;
import edu.cornell.mannlib.vedit.util.FormUtils;
import edu.cornell.mannlib.vedit.validator.Validator;
import edu.cornell.mannlib.vedit.validator.impl.RequiredFieldValidator;
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.beans.Property;
import edu.cornell.mannlib.vitro.webapp.beans.PropertyGroup;
import edu.cornell.mannlib.vitro.webapp.controller.Controllers;
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
import edu.cornell.mannlib.vitro.webapp.controller.edit.utils.RoleLevelOptionsSetup;
import edu.cornell.mannlib.vitro.webapp.dao.FauxPropertyDao;
import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory;
import edu.cornell.mannlib.vitro.webapp.modelaccess.ModelAccess;
@ -81,6 +90,8 @@ public class FauxPropertyRetryController extends BaseEditController {
private static class EpoPopulator {
private final VitroRequest req;
private final ServletContext ctx;
private final WebappDaoFactory wadf;
private final EditProcessObject epo;
private final FauxPropertyDao fpDao;
@ -91,6 +102,7 @@ public class FauxPropertyRetryController extends BaseEditController {
EpoPopulator(HttpServletRequest req, EditProcessObject epo) {
this.req = new VitroRequest(req);
this.ctx = req.getSession().getServletContext();
this.wadf = ModelAccess.on(req).getWebappDaoFactory(POLICY_NEUTRAL);
this.epo = epo;
@ -114,6 +126,8 @@ public class FauxPropertyRetryController extends BaseEditController {
.getObjectPropertyDao()
.getObjectPropertyByURI(beanForEditing.getURI());
setFieldValidators();
doABunchOfOtherJunk();
}
@ -141,10 +155,13 @@ public class FauxPropertyRetryController extends BaseEditController {
return bean;
}
private void doABunchOfOtherJunk() {
// set any validators
// TODO NONE YET
private void setFieldValidators() {
epo.getValidatorMap()
.put("RangeURI",
Arrays.asList(new Validator[] { new RequiredFieldValidator() }));
}
private void doABunchOfOtherJunk() {
// set up any listeners
epo.setChangeListenerList(Collections
.singletonList(new PropertyRestrictionListener(ctx)));
@ -183,21 +200,52 @@ public class FauxPropertyRetryController extends BaseEditController {
private Map<String, List<Option>> createOptionsMap() {
Map<String, List<Option>> map = new HashMap<>();
map.put("DomainVClassURI",
map.put("GroupURI", createClassGroupOptionList());
map.put("DomainURI",
createRootedVClassOptionList(
baseProperty.getDomainVClassURI(),
beanForEditing.getDomainURI()));
map.put("RangeVClassURI",
map.put("RangeURI",
createRootedVClassOptionList(
baseProperty.getRangeVClassURI(),
beanForEditing.getRangeURI()));
map.put("HiddenFromDisplayBelowRoleLevelUsingRoleUri",
RoleLevelOptionsSetup.getDisplayOptionsList(beanForEditing));
map.put("ProhibitedFromUpdateBelowRoleLevelUsingRoleUri",
RoleLevelOptionsSetup.getUpdateOptionsList(beanForEditing));
map.put("HiddenFromPublishBelowRoleLevelUsingRoleUri",
RoleLevelOptionsSetup.getPublishOptionsList(beanForEditing));
return map;
}
private List<Option> createClassGroupOptionList() {
List<Option> groupOptList = getGroupOptList(beanForEditing
.getGroupURI());
Collections.sort(groupOptList,
new OptionsBodyComparator(req.getCollator()));
groupOptList.add(0, new Option("", "none"));
return groupOptList;
}
private List<Option> getGroupOptList(String currentGroupURI) {
List<PropertyGroup> groups = wadf.getPropertyGroupDao()
.getPublicGroups(true);
if (currentGroupURI == null) {
return FormUtils.makeOptionListFromBeans(groups, "URI", "Name",
"", null, false);
} else {
return FormUtils.makeOptionListFromBeans(groups, "URI", "Name",
currentGroupURI, null, true);
}
}
private List<Option> createRootedVClassOptionList(String rootVClassUri,
String currentSelection) {
WebappDaoFactory wadf = req.getUnfilteredWebappDaoFactory();
List<Option> list = new ArrayList<>();
list.add(new Option("", "(none specified)"));
@ -212,6 +260,20 @@ public class FauxPropertyRetryController extends BaseEditController {
return list;
}
private static class OptionsBodyComparator implements
Comparator<Option> {
private final Collator collator;
public OptionsBodyComparator(Collator collator) {
this.collator = collator;
}
@Override
public int compare(Option o1, Option o2) {
return collator.compare(o1.getBody(), o2.getBody());
}
}
}
}

View file

@ -24,7 +24,7 @@ public interface FauxPropertyDao {
*
* @return May return null.
*/
FauxProperty getFauxPropertyFromConfigContextUri(String contextUri);
FauxProperty getFauxPropertyFromContextUri(String contextUri);
/**
* If the display model contains a ConfigContext based on these URIs, get
@ -42,6 +42,36 @@ public interface FauxPropertyDao {
FauxProperty getFauxPropertyByUris(String domainUri, String baseUri,
String rangeUri);
/**
* Creates a new FauxProperty in the display model.
*
* By "a new FauxProperty", we mean a new ConfigContext and a new
* ObjectPropertyDisplayConfig linked to it.
*
* @throws IllegalStateException
* if fp does not have null values for contextUri and configUri,
* or if a FauxProperty already exists with this combination of
* domain, base, and range URIs.
* @throws IllegalArgumentException
* if fp is not internally consistent.
*/
void insertFauxProperty(FauxProperty fp);
/**
* Updates the properties of this FauxProperty in the display model.
*
* By "this FauxProperty", we mean the ConfigContext and
* ObjectPropertyDisplayConfig whose URIs are stored in this FauxProperty.
*
* @throws IllegalStateException
* if the display model contains no such individuals. If you
* want to create a new FauxProperty instance, you should be
* using insertFauxProperty() instead.
* @throws IllegalArgumentException
* if fp is not internally consistent.
*/
void updateFauxProperty(FauxProperty fp);
/**
* Delete this FauxProperty from the display model.
*

View file

@ -32,7 +32,7 @@ public class FauxPropertyDaoFiltering extends BaseFiltering implements FauxPrope
}
@Override
public FauxProperty getFauxPropertyFromConfigContextUri(String contextUri) {
public FauxProperty getFauxPropertyFromContextUri(String contextUri) {
// TODO Auto-generated method stub
throw new RuntimeException(
"FauxPropertyDaoFiltering.getFauxPropertyFromConfigContextUri() not implemented.");
@ -48,9 +48,13 @@ 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 updateFauxProperty(FauxProperty fp) {
// TODO Auto-generated method stub
throw new RuntimeException("FauxPropertyDaoFiltering.updateFauxProperty() not implemented.");
}
@Override
public void deleteFauxProperty(FauxProperty fp) {
// TODO Auto-generated method stub
@ -58,4 +62,11 @@ public class FauxPropertyDaoFiltering extends BaseFiltering implements FauxPrope
}
@Override
public void insertFauxProperty(FauxProperty fp) {
// TODO Auto-generated method stub
throw new RuntimeException("FauxPropertyDao.insertFauxProperty() not implemented.");
}
}

View file

@ -2,23 +2,28 @@
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.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Random;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.hp.hpl.jena.ontology.ObjectProperty;
import com.hp.hpl.jena.ontology.OntModel;
import com.hp.hpl.jena.ontology.OntModelSpec;
import com.hp.hpl.jena.ontology.OntResource;
import com.hp.hpl.jena.query.QuerySolution;
import com.hp.hpl.jena.query.ResultSet;
import com.hp.hpl.jena.rdf.model.ModelFactory;
import com.hp.hpl.jena.rdf.model.Property;
import com.hp.hpl.jena.rdf.model.RDFNode;
import com.hp.hpl.jena.rdf.model.ResIterator;
@ -28,15 +33,15 @@ 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.InsertException;
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;
/**
* TODO
*/
public class FauxPropertyDaoJena implements FauxPropertyDao {
public class FauxPropertyDaoJena extends JenaBaseDao implements FauxPropertyDao {
private static final Log log = LogFactory.getLog(FauxPropertyDaoJena.class);
// ----------------------------------------------------------------------
@ -44,41 +49,53 @@ public class FauxPropertyDaoJena implements FauxPropertyDao {
// ----------------------------------------------------------------------
private static final String APPLICATION_CONTEXT_NS = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationConfiguration#";
private static final Resource CONFIG_CONTEXT = createResource(APPLICATION_CONTEXT_NS
+ "ConfigContext");
private static final Property HAS_CONFIGURATION = createProperty(APPLICATION_CONTEXT_NS
+ "hasConfiguration");
private static final Property CONFIG_CONTEXT_FOR = createProperty(APPLICATION_CONTEXT_NS
+ "configContextFor");
private static final Property QUALIFIED_BY_RANGE = createProperty(APPLICATION_CONTEXT_NS
+ "qualifiedBy");
private static final Property QUALIFIED_BY_DOMAIN = createProperty(APPLICATION_CONTEXT_NS
+ "qualifiedByDomain");
private static OntModel _constModel = ModelFactory
.createOntologyModel(OntModelSpec.OWL_DL_MEM);
private static final Property DISPLAY_NAME = createProperty(APPLICATION_CONTEXT_NS
+ "displayName");
private static final Property RDFS_LABEL = createProperty(VitroVocabulary.LABEL);
private static final Resource CONFIG_CONTEXT = createResource(appContext("ConfigContext"));
private static final Resource OBJECT_PROPERTY_DISPLAY_CONFIG = createResource(appContext("ObjectPropertyDisplayConfig"));
private static final ObjectProperty HAS_CONFIGURATION = createProperty(appContext("hasConfiguration"));
private static final ObjectProperty CONFIG_CONTEXT_FOR = createProperty(appContext("configContextFor"));
// ----------------------------------------------------------------------
// Queries and parsers
// ----------------------------------------------------------------------
private static final ObjectProperty QUALIFIED_BY_RANGE = createProperty(appContext("qualifiedBy"));
private static final ObjectProperty QUALIFIED_BY_DOMAIN = createProperty(appContext("qualifiedByDomain"));
private static final ObjectProperty LIST_VIEW_FILE = createProperty(appContext("listViewConfigFile"));
private static final ObjectProperty DISPLAY_NAME = createProperty(appContext("displayName"));
private static final ObjectProperty PROPERTY_GROUP = createProperty(appContext("propertyGroup"));
private static final ObjectProperty RDFS_LABEL = createProperty(VitroVocabulary.LABEL);
private static final String SITE_CONFIG_NAMESPACE = "http://vitro.mannlib.cornell.edu/ns/vitro/siteConfig/";
private static String appContext(String localName) {
return APPLICATION_CONTEXT_NS + localName;
}
private static ObjectProperty createProperty(String uri) {
return _constModel.createObjectProperty(uri);
}
// ----------------------------------------------------------------------
// The instance
// ----------------------------------------------------------------------
private final LockingOntModelSelector models;
public FauxPropertyDaoJena(WebappDaoFactoryJena wadf) {
this.models = new LockingOntModelSelector(wadf.getOntModelSelector());
super(wadf);
}
@Override
protected OntModel getOntModel() {
return getOntModelSelector().getDisplayModel();
}
@Override
public List<FauxProperty> getFauxPropertiesForBaseUri(String uri) {
List<FauxProperty> list = new ArrayList<>();
try (LockedOntModel displayModel = models.getDisplayModel().read()) {
getOntModel().enterCriticalSection(Lock.READ);
try {
if (uri != null) {
ResIterator contextResources = displayModel
ResIterator contextResources = getOntModel()
.listSubjectsWithProperty(CONFIG_CONTEXT_FOR,
createResource(uri));
for (Resource context : contextResources.toList()) {
@ -86,7 +103,7 @@ public class FauxPropertyDaoJena implements FauxPropertyDao {
continue;
}
FauxProperty fp = getFauxPropertyFromConfigContextUri(context
FauxProperty fp = getFauxPropertyFromContextUri(context
.getURI());
if (fp == null) {
continue;
@ -95,24 +112,27 @@ public class FauxPropertyDaoJena implements FauxPropertyDao {
list.add(fp);
}
}
} finally {
getOntModel().leaveCriticalSection();
}
return list;
}
@Override
public FauxProperty getFauxPropertyFromConfigContextUri(String contextUri) {
try (LockedOntModel displayModel = models.getDisplayModel().read()) {
public FauxProperty getFauxPropertyFromContextUri(String contextUri) {
getOntModel().enterCriticalSection(Lock.READ);
try {
if (contextUri == null) {
return null;
}
Resource context = createResource(contextUri);
if (!displayModel.contains(context, RDF.type, CONFIG_CONTEXT)) {
if (!getOntModel().contains(context, RDF.type, CONFIG_CONTEXT)) {
log.debug("'" + contextUri + "' is not a CONFIG_CONTEXT");
return null;
}
String baseUri = getUriValue(displayModel, context,
String baseUri = getUriValue(getOntModel(), context,
CONFIG_CONTEXT_FOR);
if (baseUri == null) {
log.debug("'" + contextUri + "' has no value for '"
@ -120,7 +140,7 @@ public class FauxPropertyDaoJena implements FauxPropertyDao {
return null;
}
String rangeUri = getUriValue(displayModel, context,
String rangeUri = getUriValue(getOntModel(), context,
QUALIFIED_BY_RANGE);
if (rangeUri == null) {
log.debug("'" + contextUri + "' has no value for '"
@ -129,20 +149,23 @@ public class FauxPropertyDaoJena implements FauxPropertyDao {
}
// domainURI is optional.
String domainUri = getUriValue(displayModel, context,
String domainUri = getUriValue(getOntModel(), context,
QUALIFIED_BY_DOMAIN);
FauxProperty fp = new FauxProperty(domainUri, baseUri, rangeUri);
populateInstance(fp, context);
return fp;
} finally {
getOntModel().leaveCriticalSection();
}
}
@Override
public FauxProperty getFauxPropertyByUris(String domainUri, String baseUri,
String rangeUri) {
Set<ConfigContext> contexts = ConfigContext.findByQualifiers(models,
domainUri, baseUri, rangeUri);
Set<ConfigContext> contexts = ConfigContext.findByQualifiers(
getOntModelSelector().getDisplayModel(), domainUri, baseUri,
rangeUri);
for (ConfigContext context : contexts) {
FauxProperty fp = new FauxProperty(domainUri, baseUri, rangeUri);
populateInstance(fp, createResource(context.getContextUri()));
@ -153,19 +176,192 @@ public class FauxPropertyDaoJena implements FauxPropertyDao {
return null;
}
@Override
public void insertFauxProperty(FauxProperty fp) {
if ((fp.getContextUri() != null) || (fp.getConfigUri() == null)) {
throw new IllegalStateException(
"ContextUri and ConfigUri must be null on insert: contextUri="
+ fp.getContextUri() + ", configUri="
+ fp.getConfigUri());
}
Set<ConfigContext> existingcontexts = ConfigContext.findByQualifiers(
getOntModelSelector().getDisplayModel(), fp.getDomainURI(),
fp.getBaseURI(), fp.getRangeURI());
if (!existingcontexts.isEmpty()) {
throw new IllegalStateException(
"FauxProperty already exists with domainUri="
+ fp.getDomainURI() + ", baseUri="
+ fp.getBaseURI() + ", rangeUri="
+ fp.getRangeURI());
}
try {
fp.setContextUri(getUnusedURI());
fp.setConfigUri(getUnusedURI());
} catch (InsertException e) {
throw new RuntimeException(e);
}
getOntModel().enterCriticalSection(Lock.WRITE);
try {
OntResource context = getOntModel().createOntResource(
fp.getContextUri());
addPropertyResourceValue(context, RDF.type, CONFIG_CONTEXT);
addPropertyResourceURIValue(context, HAS_CONFIGURATION,
fp.getConfigUri());
addPropertyResourceURIValue(context, CONFIG_CONTEXT_FOR,
fp.getBaseURI());
addPropertyResourceURIValue(context, QUALIFIED_BY_RANGE,
fp.getRangeURI());
addPropertyResourceURIValue(context, QUALIFIED_BY_DOMAIN,
fp.getDomainURI());
OntResource config = getOntModel().createOntResource(
fp.getConfigUri());
addPropertyResourceValue(config, RDF.type,
OBJECT_PROPERTY_DISPLAY_CONFIG);
addPropertyResourceURIValue(config, PROPERTY_GROUP,
fp.getGroupURI());
addPropertyStringValue(config, DISPLAY_NAME, fp.getDisplayName(),
getOntModel());
addPropertyStringValue(config, PUBLIC_DESCRIPTION_ANNOT,
fp.getPublicDescription(), getOntModel());
addPropertyIntValue(config, DISPLAY_RANK_ANNOT,
fp.getDisplayTier(), getOntModel());
addPropertyIntValue(config, DISPLAY_LIMIT, fp.getDisplayLimit(),
getOntModel());
addPropertyBooleanValue(config, PROPERTY_COLLATEBYSUBCLASSANNOT,
fp.isCollateBySubclass(), getOntModel());
addPropertyBooleanValue(config, PROPERTY_SELECTFROMEXISTINGANNOT,
fp.isSelectFromExisting(), getOntModel());
addPropertyBooleanValue(config, PROPERTY_OFFERCREATENEWOPTIONANNOT,
fp.isOfferCreateNewOption(), getOntModel());
addPropertyStringValue(config, PROPERTY_CUSTOMENTRYFORMANNOT,
fp.getCustomEntryForm(), getOntModel());
addPropertyStringValue(config, LIST_VIEW_FILE,
fp.getCustomListView(), getOntModel());
} finally {
getOntModel().leaveCriticalSection();
}
}
@Override
public void updateFauxProperty(FauxProperty fp) {
getOntModel().enterCriticalSection(Lock.READ);
try {
if (fp.getContextUri() == null) {
throw new IllegalStateException("ContextURI may not be null: "
+ fp);
}
Resource context = createResource(fp.getContextUri());
if (fp.getConfigUri() == null) {
throw new IllegalStateException("ConfigURI may not be null: "
+ fp);
}
Resource config = createResource(fp.getConfigUri());
if (!getOntModel().contains(context, RDF.type, CONFIG_CONTEXT)) {
throw new IllegalStateException("'" + context + "' is not a '"
+ CONFIG_CONTEXT + "'");
}
if (!getOntModel().contains(config, RDF.type,
OBJECT_PROPERTY_DISPLAY_CONFIG)) {
throw new IllegalStateException("'" + config + "' is not a '"
+ OBJECT_PROPERTY_DISPLAY_CONFIG + "'");
}
if (!getOntModel().contains(context, HAS_CONFIGURATION, config)) {
throw new IllegalStateException("'" + config
+ "' is not a configuration for '" + context + "'");
}
} finally {
getOntModel().leaveCriticalSection();
}
getOntModel().enterCriticalSection(Lock.WRITE);
try {
OntResource context = getOntModel().createOntResource(
fp.getContextUri());
updatePropertyResourceURIValue(context, QUALIFIED_BY_RANGE,
fp.getRangeURI());
updatePropertyResourceURIValue(context, QUALIFIED_BY_DOMAIN,
fp.getDomainURI());
OntResource config = getOntModel().createOntResource(
fp.getConfigUri());
updatePropertyResourceURIValue(config, PROPERTY_GROUP,
fp.getGroupURI());
updatePropertyStringValue(config, DISPLAY_NAME, fp.getDisplayName(),
getOntModel());
updatePropertyStringValue(config, PUBLIC_DESCRIPTION_ANNOT,
fp.getPublicDescription(), getOntModel());
updatePropertyIntValue(config, DISPLAY_RANK_ANNOT,
fp.getDisplayTier(), getOntModel());
updatePropertyIntValue(config, DISPLAY_LIMIT, fp.getDisplayLimit(),
getOntModel());
updatePropertyBooleanValue(config, PROPERTY_COLLATEBYSUBCLASSANNOT,
fp.isCollateBySubclass(), getOntModel(), true);
updatePropertyBooleanValue(config, PROPERTY_SELECTFROMEXISTINGANNOT,
fp.isSelectFromExisting(), getOntModel(), true);
updatePropertyBooleanValue(config, PROPERTY_OFFERCREATENEWOPTIONANNOT,
fp.isOfferCreateNewOption(), getOntModel(), true);
updatePropertyStringValue(config, PROPERTY_CUSTOMENTRYFORMANNOT,
fp.getCustomEntryForm(), getOntModel());
updatePropertyStringValue(config, LIST_VIEW_FILE,
fp.getCustomListView(), getOntModel());
} finally {
getOntModel().leaveCriticalSection();
}
}
@Override
public void deleteFauxProperty(FauxProperty fp) {
Set<ConfigContext> contexts = ConfigContext.findByQualifiers(models,
fp.getDomainURI(), fp.getURI(), fp.getRangeURI());
try (LockedOntModel displayModel = models.getDisplayModel().write()) {
Set<ConfigContext> contexts = ConfigContext.findByQualifiers(
getOntModelSelector().getDisplayModel(), fp.getDomainURI(),
fp.getURI(), fp.getRangeURI());
getOntModel().enterCriticalSection(Lock.READ);
try {
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);
getOntModel().removeAll(configResource, null, null);
getOntModel().removeAll(null, null, configResource);
Resource contextResource = createResource(context
.getContextUri());
getOntModel().removeAll(contextResource, null, null);
getOntModel().removeAll(null, null, contextResource);
}
} finally {
getOntModel().leaveCriticalSection();
}
}
private String getUnusedURI() throws InsertException {
String errMsg = null;
String namespace = SITE_CONFIG_NAMESPACE;
String uri = null;
Random random = new Random(System.currentTimeMillis());
for (int attempts = 0; attempts < 30; attempts++) {
int upperBound = (int) Math.pow(2, attempts + 13);
uri = namespace + ("fp" + random.nextInt(upperBound));
if (!isUriUsed(uri)) {
return uri;
}
}
throw new InsertException("Could not create URI for individual: "
+ errMsg);
}
private boolean isUriUsed(String uri) {
getOntModel().enterCriticalSection(Lock.READ);
try {
return (getOntModel().getOntResource(uri) != null);
} finally {
getOntModel().leaveCriticalSection();
}
}
@ -174,49 +370,94 @@ public class FauxPropertyDaoJena implements FauxPropertyDao {
* ObjectPropertyDisplayConfig.
*/
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);
}
populateLabelsFromTBox(fp);
populateFieldsFromDisplayModel(fp, context);
}
String domainLabel = getStringValue(tboxModel,
createProperty(fp.getDomainURI()), RDFS_LABEL);
if (domainLabel != null) {
fp.setDomainLabel(domainLabel);
}
private void populateLabelsFromTBox(FauxProperty fp) {
fp.setRangeLabel(findLabelForClass(fp.getRangeURI()));
fp.setDomainLabel(findLabelForClass(fp.getDomainURI()));
}
private String findLabelForClass(String classUri) {
if (classUri == null) {
return null;
} else {
OntModel tboxModel = getOntModelSelector().getTBoxModel();
tboxModel.enterCriticalSection(Lock.READ);
try {
return getStringValue(tboxModel, createResource(classUri),
RDFS_LABEL);
} finally {
tboxModel.leaveCriticalSection();
}
}
}
// Display name.
try (LockedOntModel displayModel = models.getDisplayModel().read()) {
String configUri = getUriValue(displayModel, context,
private void populateFieldsFromDisplayModel(FauxProperty fp,
Resource context) {
OntResource config = locateConfigurationFromContext(context);
if (config != null) {
getOntModel().enterCriticalSection(Lock.READ);
try {
fp.setDisplayName(getPropertyStringValue(config, DISPLAY_NAME));
fp.setPublicDescription(getPropertyStringValue(config,
PUBLIC_DESCRIPTION_ANNOT));
fp.setGroupURI(getSingleResourceURIValue(config, PROPERTY_GROUP));
fp.setCustomListView(getPropertyStringValue(config,
LIST_VIEW_FILE));
fp.setDisplayTier(getPropertyIntValue(config,
DISPLAY_RANK_ANNOT));
fp.setDisplayLimit(getPropertyIntValue(config, DISPLAY_LIMIT));
fp.setCollateBySubclass(getPropertyBooleanValue(config,
PROPERTY_COLLATEBYSUBCLASSANNOT));
fp.setSelectFromExisting(getPropertyBooleanValue(config,
PROPERTY_SELECTFROMEXISTINGANNOT));
fp.setOfferCreateNewOption(getPropertyBooleanValue(config,
PROPERTY_OFFERCREATENEWOPTIONANNOT));
fp.setCustomEntryForm(getPropertyStringValue(config,
PROPERTY_CUSTOMENTRYFORMANNOT));
} finally {
getOntModel().leaveCriticalSection();
}
}
}
private OntResource locateConfigurationFromContext(Resource context) {
getOntModel().enterCriticalSection(Lock.READ);
try {
String configUri = getUriValue(getOntModel(), context,
HAS_CONFIGURATION);
if (configUri == null) {
return;
}
Resource config = createResource(configUri);
String displayName = getStringValue(displayModel, config,
DISPLAY_NAME);
if (displayName != null) {
fp.setPickListName(displayName);
return null;
} else {
return getOntModel().createOntResource(configUri);
}
} finally {
getOntModel().leaveCriticalSection();
}
}
// TODO pull all sorts of things from the configuration.
private String getSingleResourceURIValue(OntResource config,
ObjectProperty prop) {
Collection<String> values = getPropertyResourceURIValues(config, prop);
if (values.isEmpty()) {
return null;
} else {
return values.iterator().next();
}
}
/**
* Returns a single URI that is the object of this subject and property.
* Returns null if no valid statement is found.
*
* The model should already be locked.
*/
private String getUriValue(LockedOntModel displayModel, Resource subject,
private String getUriValue(OntModel model, Resource subject,
Property property) {
List<RDFNode> nodeList = displayModel.listObjectsOfProperty(subject,
property).toList();
List<RDFNode> nodeList = model.listObjectsOfProperty(subject, property)
.toList();
if (nodeList.isEmpty()) {
log.warn("'" + subject.getURI() + "' has no value for '"
+ property.getURI() + "'.");
@ -239,11 +480,13 @@ public class FauxPropertyDaoJena implements FauxPropertyDao {
/**
* Returns a single String value that is the object of this subject and
* property. Returns null if no valid statement is found.
*
* The model should already be locked.
*/
private String getStringValue(LockedOntModel displayModel,
Resource subject, Property property) {
List<RDFNode> nodeList = displayModel.listObjectsOfProperty(subject,
property).toList();
private String getStringValue(OntModel model, Resource subject,
Property property) {
List<RDFNode> nodeList = model.listObjectsOfProperty(subject, property)
.toList();
if (nodeList.isEmpty()) {
log.warn("'" + subject.getURI() + "' has no value for '"
+ property.getURI() + "'.");
@ -329,10 +572,10 @@ public class FauxPropertyDaoJena implements FauxPropertyDao {
private static class ConfigContext {
public static Set<ConfigContext> findByQualifiers(
LockingOntModelSelector models, String domainUri,
String baseUri, String rangeUri) {
try (LockedOntModel displayModel = models.getDisplayModel().read()) {
OntModel displayModel, String domainUri, String baseUri,
String rangeUri) {
displayModel.enterCriticalSection(Lock.READ);
try {
String queryString;
if (domainUri == null) {
queryString = bindValues(
@ -351,8 +594,9 @@ public class FauxPropertyDaoJena implements FauxPropertyDao {
domainUri, baseUri, rangeUri);
return new SparqlQueryRunner(displayModel).executeSelect(
parser, queryString);
} finally {
displayModel.leaveCriticalSection();
}
}
private final String contextUri;
@ -392,59 +636,4 @@ public class FauxPropertyDaoJena implements FauxPropertyDao {
}
// ----------------------------------------------------------------------
// Helper classes. Are they worth it, just to use try-with-resources?
// ----------------------------------------------------------------------
private static class LockingOntModelSelector {
private final OntModelSelector oms;
public LockingOntModelSelector(OntModelSelector oms) {
this.oms = oms;
}
public LockableOntModel getDisplayModel() {
return new LockableOntModel(oms.getDisplayModel());
}
public LockableOntModel getTBoxModel() {
return new LockableOntModel(oms.getTBoxModel());
}
}
private static class LockableOntModel {
private final OntModel ontModel;
public LockableOntModel(OntModel ontModel) {
this.ontModel = ontModel;
}
public LockedOntModel read() {
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
implements AutoCloseable {
protected LockedOntModel(OntModel m) {
super(m);
}
/**
* Just unlocks the model. Doesn't actually close it, because we may
* want to use it again.
*/
@Override
public void close() {
super.leaveCriticalSection();
}
}
}

View file

@ -3,14 +3,165 @@
<%@ taglib prefix="form" uri="http://vitro.mannlib.cornell.edu/edit/tags" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<tr class="editformcell">
<td valign="top" colspan="2">
<b>Base property</b><br/>
<input type="text" class="fullWidthInput" name="Base property" value="<form:value name="BaseUri"/>" disabled="disabled" /></br>
<i>a specification of this property</i></br>
</td>
<td valign="top" colspan="2">
<b>Property group</b><br/>
<select name="GroupURI"><form:option name="GroupURI"/></select><br/>
<i>for grouping properties on individual pages</i><br/>
</td>
</tr>
<tr class="editformcell">
<td style="vertical-align:top;" valign="top" colspan="2">
<b>Label for public display</b><br/><br/>
<input type="text" class="fullWidthInput" name="DisplayName" value="<form:value name="DisplayName"/>" maxlength="80" />
<c:set var="DisplayNameError"><form:error name="DisplayName"/></c:set>
<c:if test="${!empty DisplayNameError}">
<span class="notice"><c:out value="${DisplayNameError}"/></span>
</c:if>
</td>
</tr>
<tr><td colspan="5"><hr class="formDivider"/></td></tr>
<tr class="editformcell">
<td valign="top" colspan="2">
<b>Domain class</b><br />
<select name="DomainVClassURI"><form:option name="DomainVClassURI"/></select>
<select name="DomainURI"><form:option name="DomainURI"/></select>
</td>
<td valign="top" colspan="2">
<b>Range class</b><br />
<select name="RangeVClassURI" ><form:option name="RangeVClassURI"/></select>
<select name="RangeURI" ><form:option name="RangeURI"/></select>
<c:set var="RangeURIError"><form:error name="RangeURI"/></c:set>
<c:if test="${!empty RangeURIError}">
<span class="notice"><c:out value="${RangeURIError}"/></span>
</c:if>
</td>
</tr>
<tr><td colspan="5"><hr class="formDivider"/></td></tr>
<tr class="editformcell">
<td valign="top" colspan="5">
<b>Public Description</b> for front-end users, as it will appear on editing forms<br/>
<textarea class="matchingInput" name="PublicDescription"><form:value name="PublicDescription"/></textarea>
<c:set var="PublicDescriptionError"><form:error name="PublicDescription"/></c:set>
<c:if test="${!empty PublicDescriptionError}">
<span class="notice"><c:out value="${PublicDescriptionError}"/></span>
</c:if>
</td>
</tr>
<tr><td colspan="5"><hr class="formDivider"/></td></tr>
<tr class="editformcell">
<td valign="top" colspan="2">
<b>Display level</b><br />
<select name="HiddenFromDisplayBelowRoleLevelUsingRoleUri">
<form:option name="HiddenFromDisplayBelowRoleLevelUsingRoleUri"/>
</select>
</td>
<td valign="top" colspan="2">
<b>Update level</b><br/>
<select name="ProhibitedFromUpdateBelowRoleLevelUsingRoleUri">
<form:option name="ProhibitedFromUpdateBelowRoleLevelUsingRoleUri"/>
</select>
</td>
</tr>
<tr class="editformcell">
<td valign="top" colspan="2">
<b>Publish level</b><br />
<select name="HiddenFromPublishBelowRoleLevelUsingRoleUri">
<form:option name="HiddenFromPublishBelowRoleLevelUsingRoleUri"/>
</select>
</td>
</tr>
<tr><td colspan="5"><hr class="formDivider"/></td></tr>
<tr class="editformcell">
<td valign="top" colspan="1">
<b>Display tier</b> for this property<br/>
<input type="text" class="shortInput" name="DisplayTier" value="<form:value name="DisplayTier"/>" /><br/>
<i><b>lower</b> numbers display first</i><br/>
<c:set var="DisplayTierError"><form:error name="DisplayTier"/></c:set>
<c:if test="${!empty DisplayTierError}">
<span class="notice"><c:out value="${DisplayTierError}"/></span>
</c:if>
</td>
<td valign="top" colspan="2">
<b>Display limit</b> before &quot;more ...&quot; button is displayed<br/>
<input type="text" class="shortInput" name="DisplayLimit" value="<form:value name="DisplayLimit"/>"/>
<c:set var="DisplayLimitError"><form:error name="DisplayLimit"/></c:set>
<c:if test="${!empty DisplayLimitError}">
<span class="notice"><c:out value="${DisplayLimitError}"/></span>
</c:if>
</td>
<td valign="top" colspan="2">
When displaying related individuals from different classes,<br/>
<c:choose>
<c:when test="${collateBySubclass}">
<input name="CollateBySubclass" type="checkbox" value="TRUE" checked="checked"/>collate by subclass
</c:when>
<c:otherwise>
<input name="CollateBySubclass" type="checkbox" value="TRUE"/>collate by subclass
</c:otherwise>
</c:choose>
</td>
</tr>
<tr><td colspan="5"><hr class="formDivider"/></td></tr>
<tr class="editformcell">
<td valign="top" colspan="2">
Select related individuals from existing choices?<br/>
<c:choose>
<c:when test="${selectFromExisting}">
<input name="SelectFromExisting" type="checkbox" value="TRUE" checked="checked"/>provide selection
</c:when>
<c:otherwise>
<input name="SelectFromExisting" type="checkbox" value="TRUE"/>provide selection
</c:otherwise>
</c:choose>
</td>
<td valign="top" colspan="1">
Allow creating new related individuals?<br/>
<c:choose>
<c:when test="${offerCreateNewOption}">
<input name="OfferCreateNewOption" type="checkbox" value="TRUE" checked="checked"/>offer create option
</c:when>
<c:otherwise>
<input name="OfferCreateNewOption" type="checkbox" value="TRUE"/>offer create option
</c:otherwise>
</c:choose>
</td>
</tr>
<tr><td colspan="5"><hr class="formDivider"/></td></tr>
<tr class="editformcell">
<td valign="top" colspan="2">
<b>Custom entry form</b><br/>
<input type="text" class="fullWidthInput" name="CustomEntryForm" value="<form:value name="CustomEntryForm"/>" />
<c:set var="CustomEntryFormError"><form:error name="CustomEntryForm"/></c:set>
<c:if test="${!empty CustomEntryFormError}">
<span class="notice"><c:out value="${CustomEntryFormError}"/></span>
</c:if>
</td>
<td valign="top" colspan="2">
<b>Custom list view</b><br/>
<input type="text" class="fullWidthInput" name="CustomListView" value="<form:value name="CustomListView"/>" />
<c:set var="CustomListViewError"><form:error name="CustomListView"/></c:set>
<c:if test="${!empty CustomListViewError}">
<span class="notice"><c:out value="${CustomListViewError}"/></span>
</c:if>
</td>
</tr>
<tr><td colspan="5"><hr class="formDivider"/></td></tr>