diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/PropertyInstanceDaoJena.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/PropertyInstanceDaoJena.java index 245cf538c..a24a0b570 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/PropertyInstanceDaoJena.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/PropertyInstanceDaoJena.java @@ -14,17 +14,22 @@ import java.util.List; import java.util.Map; import java.util.Set; +import com.hp.hpl.jena.ontology.AllValuesFromRestriction; import com.hp.hpl.jena.ontology.ObjectProperty; 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.Restriction; +import com.hp.hpl.jena.ontology.SomeValuesFromRestriction; import com.hp.hpl.jena.rdf.model.Property; +import com.hp.hpl.jena.rdf.model.ResIterator; import com.hp.hpl.jena.rdf.model.Resource; import com.hp.hpl.jena.rdf.model.ResourceFactory; import com.hp.hpl.jena.rdf.model.Statement; +import com.hp.hpl.jena.rdf.model.StmtIterator; import com.hp.hpl.jena.shared.Lock; import com.hp.hpl.jena.vocabulary.OWL; +import com.hp.hpl.jena.vocabulary.RDFS; import edu.cornell.mannlib.vitro.webapp.beans.Individual; import edu.cornell.mannlib.vitro.webapp.beans.ObjectPropertyStatement; @@ -95,23 +100,40 @@ public class PropertyInstanceDaoJena extends JenaBaseDao implements public Collection getAllPossiblePropInstForIndividual(String individualURI) { Individual ind = getWebappDaoFactory().getIndividualDao().getIndividualByURI(individualURI); - Map propURI2propInst = new HashMap(); + VClassDao vcDao = getWebappDaoFactory().getVClassDao(); + List allTypes = ind.getVClasses(false); // include indirect types - Collections.sort(allTypes, new VClassHierarchyRanker(this.getWebappDaoFactory().getVClassDao())); - for (VClass currClass : allTypes) { - Collection propInstsForClass = getAllPropInstByVClass(currClass.getURI()); - for (PropertyInstance propInst : propInstsForClass) { - if (propURI2propInst.get(propInst.getPropertyURI()) == null) { - propURI2propInst.put(propInst.getPropertyURI(), propInst); - } - } + + Set allSuperclassURIs = new HashSet(); + + for (VClass type : allTypes) { + String classURI = type.getURI(); + if (classURI != null) { + allSuperclassURIs.add(type.getURI()); + } + for (String equivURI : vcDao.getEquivalentClassURIs(classURI)) { + allSuperclassURIs.add(equivURI); + allSuperclassURIs.addAll(vcDao.getAllSuperClassURIs(equivURI)); + } + allSuperclassURIs.addAll(vcDao.getAllSuperClassURIs(classURI)); + } + + List vclasses = new ArrayList(); + for(String vclassURI : allSuperclassURIs) { + VClass vclass = vcDao.getVClassByURI(vclassURI); + if (vclass != null) { + vclasses.add(vclass); + } + } + + Collection piList = getAllPropInstByVClasses(vclasses); + + for (PropertyInstance pi : piList) { + pi.setDomainClassURI(ind.getVClassURI()); + // TODO: improve. This is so the DWR property editing passes the + // individual's VClass to get the right restrictions } - for (PropertyInstance pi : propURI2propInst.values()) { - pi.setDomainClassURI(ind.getVClassURI()); // TODO: improve. This is so the DWR property editing passes the individual's VClass to get the right restrictions - } - List piList = new ArrayList(); - piList.addAll(propURI2propInst.values()); - Collections.sort(piList, new PropInstSorter()); + return piList; } @@ -126,15 +148,19 @@ public class PropertyInstanceDaoJena extends JenaBaseDao implements public int compare(VClass vc1, VClass vc2) { if (vcDao.isSubClassOf(vc1, vc2)) { return -1; - } else return 0; + } else if (vcDao.isSubClassOf(vc2, vc1)) { + return 1; + } else { + return 0; + } } } + public Collection getAllPropInstByVClass(String classURI) { if (classURI==null || classURI.length()<1) { return null; } - List propInsts = new ArrayList(); VClassDao vcDao = getWebappDaoFactory().getVClassDao(); @@ -142,56 +168,135 @@ public class PropertyInstanceDaoJena extends JenaBaseDao implements allSuperclassURIs.add(classURI); for (String equivURI : vcDao.getEquivalentClassURIs(classURI)) { - allSuperclassURIs.add(equivURI); - allSuperclassURIs.addAll(vcDao.getAllSuperClassURIs(equivURI)); + allSuperclassURIs.add(equivURI); + allSuperclassURIs.addAll(vcDao.getAllSuperClassURIs(equivURI)); } allSuperclassURIs.addAll(vcDao.getAllSuperClassURIs(classURI)); + List vclasses = new ArrayList(); + for(String vclassURI : allSuperclassURIs) { + VClass vclass = vcDao.getVClassByURI(vclassURI); + if (vclass != null) { + vclasses.add(vclass); + } + } + return getAllPropInstByVClasses(vclasses); + } + + private void updatePropertyRangeMap(Map map, + String propURI, + Resource[] ranges) { + Resource[] existingRanges = map.get(propURI); + if (existingRanges == null) { + map.put(propURI, ranges); + } else if (existingRanges[0] == null && existingRanges[1] != null) { + existingRanges[0] = ranges[0]; + map.put(propURI, existingRanges); + } else if (existingRanges[0] != null && existingRanges[1] == null) { + existingRanges[1] = ranges[1]; + map.put(propURI, existingRanges); + } + } + + public Collection getAllPropInstByVClasses(List vclasses) { + + List propInsts = new ArrayList(); + + if(vclasses == null || vclasses.isEmpty()) { + return propInsts; + } + + Collections.sort(vclasses, new VClassHierarchyRanker(this.getWebappDaoFactory().getVClassDao())); + OntModel ontModel = getOntModelSelector().getTBoxModel(); try { ontModel.enterCriticalSection(Lock.READ); - Set applicableProperties = new HashSet(); + // map object property URI to an array of two resources: + // the first is the "allValuesFrom" resource and the second is + // "someValuesFrom" + Map applicableProperties = + new HashMap(); try { - for (String VClassURI : allSuperclassURIs) { + for (VClass vclass : vclasses) { + if (vclass.isAnonymous()) { + continue; + } + String VClassURI = vclass.getURI(); + OntClass ontClass = getOntClass(ontModel,VClassURI); if (ontClass != null) { - if (ontClass.isRestriction()) { - // TODO: check if restriction is something like - // maxCardinality 0 or allValuesFrom owl:Nothing, - // in which case the property is NOT applicable! - Restriction rest = (Restriction) ontClass.as(Restriction.class); - OntProperty onProperty = rest.getOnProperty(); - if (onProperty != null && onProperty.canAs(ObjectProperty.class)) { - applicableProperties.add((ObjectProperty)onProperty.as(ObjectProperty.class)); - } + List relatedClasses = new ArrayList(); + relatedClasses.addAll(ontClass.listEquivalentClasses().toList()); + relatedClasses.addAll(ontClass.listSuperClasses().toList()); + for (OntClass relatedClass : relatedClasses) { + // find properties in restrictions + if (relatedClass.isRestriction()) { + // TODO: check if restriction is something like + // maxCardinality 0 or allValuesFrom owl:Nothing, + // in which case the property is NOT applicable! + Restriction rest = (Restriction) relatedClass.as(Restriction.class); + OntProperty onProperty = rest.getOnProperty(); + if (onProperty != null && onProperty.canAs(ObjectProperty.class)) { + Resource[] ranges = new Resource[2]; + if (rest.isAllValuesFromRestriction()) { + ranges[0] = (rest.asAllValuesFromRestriction()).getAllValuesFrom(); + } else if (rest.isSomeValuesFromRestriction()) { + ranges[1] = (rest.asSomeValuesFromRestriction()).getSomeValuesFrom(); + } + updatePropertyRangeMap(applicableProperties, onProperty.getURI(), ranges); + } + } + } + + // find properties with class in domain + ResIterator pit = ontModel.listSubjectsWithProperty( + RDFS.domain, ontClass); + while (pit.hasNext()) { + Resource prop = pit.nextResource(); + if (prop.getNameSpace() != null + && !NONUSER_NAMESPACES.contains( + prop.getNameSpace()) ) { + StmtIterator rangeSit = prop.listProperties( + RDFS.range); + Resource rangeRes = null; + while (rangeSit.hasNext()) { + Statement s = rangeSit.nextStatement(); + if (s.getObject().isURIResource()) { + rangeRes = (Resource) s.getObject(); + } + } + Resource[] ranges = new Resource[2]; + ranges[0] = rangeRes; + updatePropertyRangeMap( + applicableProperties, prop.getURI(), ranges); + + } } } } } catch (Exception e) { - log.error("Unable to get applicable properties for " + classURI - + " by examining property restrictions", e); + log.error("Unable to get applicable properties " + + "by examining property restrictions and domains", e); } - HashSet allSuperclassURIset = new HashSet(allSuperclassURIs); - Iterator ops = ontModel.listObjectProperties(); - while (ops.hasNext()) { - ObjectProperty op = (ObjectProperty) ops.next(); - if (op.getNameSpace() != null && !NONUSER_NAMESPACES.contains(op.getNameSpace()) ) { - Resource domainRes = op.getDomain(); - if (domainRes != null && allSuperclassURIset.contains(getURIStr(domainRes))) { - applicableProperties.add(op); - } - } - } - for (Iterator appPropIt = applicableProperties.iterator(); appPropIt.hasNext();) { - ObjectProperty op = appPropIt.next(); + // make the PropertyInstance objects + for (String propertyURI : applicableProperties.keySet()) { + ObjectProperty op = ontModel.getObjectProperty(propertyURI); + if (op == null) { + continue; + } String domainURIStr = getURIStr(op.getDomain()); - Resource rangeRes = op.getRange(); + Resource[] foundRanges = applicableProperties.get(propertyURI); + Resource rangeRes = (foundRanges[0] != null) + ? foundRanges[0] + : (op.getRange() == null && foundRanges[1] != null) + ? foundRanges[1] + : op.getRange(); PropertyInstance pi = new PropertyInstance(); if (rangeRes != null) { String rangeClassURI; @@ -219,8 +324,7 @@ public class PropertyInstanceDaoJena extends JenaBaseDao implements pi.setRangePublic(getLabelOrId(op)); pi.setDomainPublic(getLabelOrId(op)); propInsts.add(pi); - } - + } } finally { ontModel.leaveCriticalSection(); } diff --git a/webapp/test/edu/cornell/mannlib/vitro/webapp/dao/jena/PropertyInstanceDaoJenaTest.java b/webapp/test/edu/cornell/mannlib/vitro/webapp/dao/jena/PropertyInstanceDaoJenaTest.java index 50c30ad79..f3f71e645 100644 --- a/webapp/test/edu/cornell/mannlib/vitro/webapp/dao/jena/PropertyInstanceDaoJenaTest.java +++ b/webapp/test/edu/cornell/mannlib/vitro/webapp/dao/jena/PropertyInstanceDaoJenaTest.java @@ -3,14 +3,20 @@ package edu.cornell.mannlib.vitro.webapp.dao.jena; import java.io.StringReader; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import junit.framework.Assert; 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.XSD; +import edu.cornell.mannlib.vitro.webapp.beans.PropertyInstance; import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary; import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory; @@ -135,4 +141,66 @@ public class PropertyInstanceDaoJenaTest { void wipeOutModTime(Model model){ model.removeAll(null, model.createProperty(VitroVocabulary.MODTIME), null); } + + @org.junit.Test + public void testGetAllPossiblePropInstForIndividual() { + String n3 = prefixesN3 + + "ex:hasMold a owl:ObjectProperty . \n" + + "ex:hasSpore a owl:ObjectProperty . \n" + + "ex:hasFungus a owl:ObjectProperty . \n" + + "ex:redHerring a owl:ObjectProperty . \n" + + "ex:Person a owl:Class . \n" + + "ex:Agent a owl:Class . \n" + + "ex:Mold a owl:Class . \n" + + "ex:Spore a owl:Class . \n" + + "ex:Fungus a owl:Class . \n" + + "ex:Organism a owl:Class . \n" + + "ex:Mold rdfs:subClassOf ex:Organism . \n" + + "ex:Spore rdfs:subClassOf ex:Organism . \n" + + "ex:Fungus rdfs:subClassOf ex:Organism . \n" + + "ex:Person rdfs:subClassOf ex:Agent . \n" + + "ex:hasFungus rdfs:range ex:Fungus . \n" + + "ex:hasFungus rdfs:domain ex:Agent . \n" + + "ex:Agent rdfs:subClassOf [ a owl:Restriction ; \n" + + "owl:onProperty ex:hasMold ; \n" + + "owl:allValuesFrom ex:Organism ] . \n" + + "ex:Person rdfs:subClassOf [ a owl:Restriction ; \n" + + "owl:onProperty ex:hasMold ; \n" + + "owl:allValuesFrom ex:Mold ] . \n" + + "ex:Agent rdfs:subClassOf [ a owl:Restriction ; \n" + + "owl:onProperty ex:hasSpore ; \n" + + "owl:allValuesFrom ex:Organism ] . \n" + + "ex:Person rdfs:subClassOf [ a owl:Restriction ; \n" + + "owl:onProperty ex:hasSpore ; \n" + + "owl:someValuesFrom ex:Spore ] . \n" + + "ex:bob a ex:Person ; a ex:Agent . \n"; + + // The applicable properties for bob should be: + // 1. hasMold (values from Mold) + // 2. hasSpore (values from Organism) + // 3. hasFungus (values from Fungus) + + OntModel ontModel = (ModelFactory.createOntologyModel(OntModelSpec.OWL_MEM)); + ontModel.read(new StringReader(n3), null, "N3"); + + WebappDaoFactory wadf = new WebappDaoFactoryJena(ontModel); + Assert.assertEquals(4, wadf.getObjectPropertyDao().getAllObjectProperties().size()); + Assert.assertEquals(6, wadf.getVClassDao().getAllVclasses().size()); + Assert.assertNotNull(wadf.getIndividualDao().getIndividualByURI("http://example.com/bob")); + + Collection pinsts = wadf.getPropertyInstanceDao() + .getAllPossiblePropInstForIndividual("http://example.com/bob"); + + Assert.assertEquals(3, pinsts.size()); + + Map propToRange = new HashMap(); + for (PropertyInstance pi : pinsts) { + propToRange.put(pi.getPropertyURI(), pi.getRangeClassURI()); + } + + Assert.assertEquals("http://example.com/Mold", propToRange.get("http://example.com/hasMold")); + Assert.assertEquals("http://example.com/Organism", propToRange.get("http://example.com/hasSpore")); + Assert.assertEquals("http://example.com/Fungus", propToRange.get("http://example.com/hasFungus")); + + } }