diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/DataPropertyDaoJena.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/DataPropertyDaoJena.java index ae415445d..9fe136015 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/DataPropertyDaoJena.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/DataPropertyDaoJena.java @@ -10,14 +10,12 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; 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.AllValuesFromRestriction; import com.hp.hpl.jena.ontology.DatatypeProperty; -import com.hp.hpl.jena.ontology.OntClass; import com.hp.hpl.jena.ontology.OntModel; import com.hp.hpl.jena.ontology.OntProperty; import com.hp.hpl.jena.ontology.OntResource; @@ -575,26 +573,12 @@ public class DataPropertyDaoJena extends PropertyDaoJena implements ontModel.enterCriticalSection(Lock.WRITE); try { com.hp.hpl.jena.ontology.DatatypeProperty jDataprop = ontModel.getDatatypeProperty(dtp.getURI()); - if (dtp.getPublicName() == null || dtp.getPublicName().length() == 0) { - jDataprop.removeAll(RDFS.label); - } else { - jDataprop.setLabel(dtp.getPublicName(), (String) getDefaultLanguage()); - } - Resource domainRes = null; - if ( (dtp.getDomainClassURI() != null) && (dtp.getDomainClassURI().length()>0) ) { - domainRes = ontModel.getResource(dtp.getDomainClassURI()); - } - jDataprop.removeAll(RDFS.domain); - if (domainRes != null) { - jDataprop.setDomain(domainRes); - } - if (dtp.getRangeDatatypeURI() != null && !dtp.getRangeDatatypeURI().equals("")) { - Resource rangeResource = ontModel.getResource(dtp.getRangeDatatypeURI()); - if (rangeResource != null) - jDataprop.setRange(rangeResource); - } else { - jDataprop.removeAll(RDFS.range); - } + + updateRDFSLabel(jDataprop, dtp.getPublicName()); + + updatePropertyResourceURIValue(jDataprop, RDFS.domain,dtp.getDomainClassURI(),ontModel); + updatePropertyResourceURIValue(jDataprop, RDFS.range,dtp.getRangeDatatypeURI(),ontModel); + if (dtp.getFunctional()) { if (!ontModel.contains(jDataprop,RDF.type,OWL.FunctionalProperty)) { ontModel.add(jDataprop,RDF.type,OWL.FunctionalProperty); @@ -604,37 +588,25 @@ public class DataPropertyDaoJena extends PropertyDaoJena implements ontModel.remove(jDataprop,RDF.type,OWL.FunctionalProperty); } } + updatePropertyStringValue(jDataprop, EXAMPLE, dtp.getExample(), ontModel); updatePropertyStringValue(jDataprop, DESCRIPTION_ANNOT, dtp.getDescription(), ontModel); updatePropertyStringValue(jDataprop, PUBLIC_DESCRIPTION_ANNOT, dtp.getPublicDescription(), ontModel); updatePropertyNonNegativeIntValue(jDataprop, DISPLAY_RANK_ANNOT, dtp.getDisplayTier(), ontModel); updatePropertyNonNegativeIntValue(jDataprop, DISPLAY_LIMIT, dtp.getDisplayLimit(), ontModel); - //updatePropertyStringValue(jDataprop, HIDDEN_ANNOT, dtp.getHidden(), ontModel); - jDataprop.removeAll(HIDDEN_FROM_DISPLAY_BELOW_ROLE_LEVEL_ANNOT); - if (HIDDEN_FROM_DISPLAY_BELOW_ROLE_LEVEL_ANNOT != null && dtp.getHiddenFromDisplayBelowRoleLevel() != null) { // only need to add if present - jDataprop.addProperty(HIDDEN_FROM_DISPLAY_BELOW_ROLE_LEVEL_ANNOT, ResourceFactory.createResource(dtp.getHiddenFromDisplayBelowRoleLevel().getURI())); - } - jDataprop.removeAll(PROHIBITED_FROM_UPDATE_BELOW_ROLE_LEVEL_ANNOT); - if (PROHIBITED_FROM_UPDATE_BELOW_ROLE_LEVEL_ANNOT != null && dtp.getProhibitedFromUpdateBelowRoleLevel() != null) { // only need to add if present - jDataprop.addProperty(PROHIBITED_FROM_UPDATE_BELOW_ROLE_LEVEL_ANNOT, ResourceFactory.createResource(dtp.getProhibitedFromUpdateBelowRoleLevel().getURI())); - } - /* - updatePropertyBooleanValue(jDataprop, PROPERTY_SELFEDITPROHIBITEDANNOT, dtp.isSelfEditProhibited(), ontModel, JenaBaseDao.KEEP_ONLY_IF_TRUE); - updatePropertyBooleanValue(jDataprop, PROPERTY_CURATOREDITPROHIBITEDANNOT, dtp.isCuratorEditProhibited(), ontModel, JenaBaseDao.KEEP_ONLY_IF_TRUE); - */ - try { - jDataprop.removeAll(PROPERTY_INPROPERTYGROUPANNOT); - if (dtp.getGroupURI() != null && dtp.getGroupURI().length()>0) { - String badURIErrorStr = checkURI(dtp.getGroupURI()); - if (badURIErrorStr == null) { - jDataprop.addProperty(PROPERTY_INPROPERTYGROUPANNOT, ontModel.getResource(dtp.getGroupURI())); - } else { - log.error(badURIErrorStr); - } - } - } catch (Exception e) { - log.error("error linking data property "+dtp.getURI()+" to property group"); - } + + if (dtp.getHiddenFromDisplayBelowRoleLevel() != null) { + updatePropertyResourceURIValue(jDataprop,HIDDEN_FROM_DISPLAY_BELOW_ROLE_LEVEL_ANNOT,dtp.getHiddenFromDisplayBelowRoleLevel().getURI(),ontModel); + } + + if (dtp.getProhibitedFromUpdateBelowRoleLevel() != null) { + updatePropertyResourceURIValue(jDataprop,PROHIBITED_FROM_UPDATE_BELOW_ROLE_LEVEL_ANNOT,dtp.getProhibitedFromUpdateBelowRoleLevel().getURI(),ontModel); + } + + if (dtp.getGroupURI() != null) { + updatePropertyResourceURIValue(jDataprop,PROPERTY_INPROPERTYGROUPANNOT,dtp.getGroupURI(),ontModel); + } + updatePropertyStringValue(jDataprop,PROPERTY_CUSTOMENTRYFORMANNOT,dtp.getCustomEntryForm(),ontModel); } finally { ontModel.leaveCriticalSection(); diff --git a/webapp/test/edu/cornell/mannlib/vitro/webapp/dao/jena/DataPropertyDaoJenaTest.java b/webapp/test/edu/cornell/mannlib/vitro/webapp/dao/jena/DataPropertyDaoJenaTest.java new file mode 100644 index 000000000..eacc22501 --- /dev/null +++ b/webapp/test/edu/cornell/mannlib/vitro/webapp/dao/jena/DataPropertyDaoJenaTest.java @@ -0,0 +1,150 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.vitro.webapp.dao.jena; + + +import org.junit.Assert; +import org.junit.Test; + +import com.hp.hpl.jena.ontology.DatatypeProperty; +import com.hp.hpl.jena.ontology.OntModel; +import com.hp.hpl.jena.ontology.OntModelSpec; +import com.hp.hpl.jena.rdf.model.Model; +import com.hp.hpl.jena.rdf.model.ModelFactory; +import com.hp.hpl.jena.vocabulary.OWL; +import com.hp.hpl.jena.vocabulary.RDF; +import com.hp.hpl.jena.vocabulary.RDFS; + +import edu.cornell.mannlib.vitro.webapp.beans.DataProperty; +import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary; + +/** + * + * + */ + +public class DataPropertyDaoJenaTest { + + @Test + // Test that the DataPropertyDaoJena::updateDataProperty method will only update the jena model for + // those properties in DataProperty that have a different value from what is already in the + // jena model for that property. + // + // Specifically, updateDataProperty method should not remove a statement from the model and + // then add the same statement back in. The reason for this is that in vitro the "immutable" properties + // are stored in a sub-model and the user-editable properties are stored in a super-model and + // all updates are performed against the super-model, so removing and then re-adding + // the same statement may result in a change of state (if the statement was in the sub-model + // it will migrate to the super-model) because of the way jena handles additions and + // deletions with respect to super and sub models. This migration of statements may cause + // undesirable behavior in the vitro application. + + public void minimalUpdates(){ + + // 1. create two models and attach one as a sub-model of the other + // 2. populate the sub-model with one statement for each of the 13 properties represented in DataProperty + // 3. save the state of both the sub-model and the super-model + // 4. populate a DataProperty object with the data in the (combined) model and call the updateDataProperty + // (having made no changes to the DataProperty object) + // 5. verify that both the sub-model and the super-model are unchanged + + String propertyURI = "http://vivoweb.org/ontology/core#addressCity"; + + OntModel superModel = ModelFactory.createOntologyModel(OntModelSpec.OWL_MEM); // this simulates the user-editable ontology in vivo + OntModel subModel = ModelFactory.createOntologyModel(OntModelSpec.OWL_MEM); // this simulates the core ontology in vivo + superModel.addSubModel(subModel); + + String rdfsLabel = "this is the rdfs label"; + String lang = "en-US"; + + // populate sub-model + DatatypeProperty property1 = subModel.createDatatypeProperty(propertyURI); + + property1.setLabel(rdfsLabel,lang); + property1.setPropertyValue(RDFS.domain, subModel.createResource("http://thisIsTheDomainClassURI")); + property1.setPropertyValue(RDFS.range, subModel.createResource("http://thisIsTheRangeClassURI")); + property1.addProperty(RDF.type, OWL.FunctionalProperty); + property1.setPropertyValue(subModel.createProperty(VitroVocabulary.EXAMPLE_ANNOT), subModel.createTypedLiteral("this is the example")); + property1.setPropertyValue(subModel.createProperty(VitroVocabulary.DESCRIPTION_ANNOT), subModel.createTypedLiteral("this is the description")); + property1.setPropertyValue(subModel.createProperty(VitroVocabulary.PUBLIC_DESCRIPTION_ANNOT), subModel.createTypedLiteral("this is the public description")); + property1.setPropertyValue(subModel.createProperty(VitroVocabulary.DISPLAY_RANK_ANNOT), subModel.createTypedLiteral(21)); + property1.setPropertyValue(subModel.createProperty(VitroVocabulary.DISPLAY_LIMIT), subModel.createTypedLiteral(5)); + property1.setPropertyValue(subModel.createProperty(VitroVocabulary.HIDDEN_FROM_DISPLAY_BELOW_ROLE_LEVEL_ANNOT), subModel.createResource("http://vitro.mannlib.cornell.edu/ns/vitro/role#curator")); + property1.setPropertyValue(subModel.createProperty(VitroVocabulary.PROHIBITED_FROM_UPDATE_BELOW_ROLE_LEVEL_ANNOT), subModel.createResource("http://vitro.mannlib.cornell.edu/ns/vitro/role#selfEditor")); + property1.setPropertyValue(subModel.createProperty(VitroVocabulary.PROPERTY_INPROPERTYGROUPANNOT), subModel.createResource("http://thisIsTheInPropertyGroupURI")); + property1.setPropertyValue(subModel.createProperty(VitroVocabulary.PROPERTY_CUSTOMENTRYFORMANNOT), subModel.createResource("http://thisIsTheCustomFormEntryURI")); + + // Save copies of sub-model and super-model + + // uncommment the next two lines to debug failures + //System.out.println("**Before updating data property:"); + //printModels(superModel, subModel); + + superModel.removeSubModel(subModel); + + OntModel origSubModel = ModelFactory.createOntologyModel(OntModelSpec.OWL_MEM); + origSubModel.add(subModel); + OntModel origSuperModel = ModelFactory.createOntologyModel(OntModelSpec.OWL_MEM); + origSuperModel.add(superModel); + + superModel.addSubModel(subModel); + + // Populate the DataProperty with the data in the sub-model and then update the combined model + // (from the unchanged object). + WebappDaoFactoryJena wdfj = new WebappDaoFactoryJena(superModel); + DataPropertyDaoJena dpdj = (DataPropertyDaoJena) wdfj.getDataPropertyDao(); + DataProperty dataProperty = dpdj.getDataPropertyByURI(propertyURI); // the DataProperty will be populated + // with the information already in + // the jena model. + + + Assert.assertEquals(dataProperty.getPublicName(), property1.getLabel(lang)); + + dpdj.updateDataProperty(dataProperty); // we haven't changed any values here, so + // the models should be unchanged. + + // Verify that the sub-model and super-model are both unchanged + + // uncommment the next two lines to debug failures + //System.out.println("\n**After updating data property:"); + //printModels(superModel,subModel); + + superModel.removeSubModel(subModel); + + //modtime affects the diff but we don't care about that difference + wipeOutModTime(origSubModel); + wipeOutModTime(origSuperModel); + wipeOutModTime(subModel); + wipeOutModTime(superModel); + + Assert.assertTrue(subModel.isIsomorphicWith(origSubModel)); + Assert.assertTrue(superModel.isIsomorphicWith(origSuperModel)); + + } + + + void printModels(OntModel superModel, OntModel subModel) { + + // Detach the submodel for printing to get an accurate + // account of what is in each. + + superModel.removeSubModel(subModel); + + System.out.println("\nThe sub-model has " + subModel.size() + " statements:"); + System.out.println("---------------------------------------------------"); + subModel.writeAll(System.out,"N3",null); + + System.out.println("\nThe super-model has " + superModel.size() + " statements:"); + System.out.println("---------------------------------------------------"); + superModel.write(System.out,"N3",null); + + superModel.addSubModel(subModel); + + } + + + void wipeOutModTime(Model model){ + model.removeAll(null, model.createProperty(VitroVocabulary.MODTIME), null); + } + +}