diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/ontology/update/ABoxUpdater.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/ontology/update/ABoxUpdater.java index e60953504..6f9335b01 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/ontology/update/ABoxUpdater.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/ontology/update/ABoxUpdater.java @@ -39,9 +39,7 @@ public class ABoxUpdater { private OntModel newTBoxAnnotationsModel; private OntologyChangeLogger logger; private OntologyChangeRecord record; - private OntClass OWL_THING = (ModelFactory - .createOntologyModel(OntModelSpec.OWL_MEM)) - .createClass(OWL.Thing.getURI()); + private OntClass OWL_THING = (ModelFactory.createOntologyModel(OntModelSpec.OWL_MEM)).createClass(OWL.Thing.getURI()); /** * @@ -311,7 +309,7 @@ public class ABoxUpdater { namedClassList.add(ontClass); } } - OntClass parent = (namedClassList.isEmpty()) + OntClass parent = (!namedClassList.isEmpty()) ? namedClassList.get(0) : OWL_THING; @@ -323,10 +321,9 @@ public class ABoxUpdater { } //log summary of changes - //logger.log("Class " + deletedClass.getURI() + " has been deleted. Any references to it in the knowledge base will be changed to " + - // replacementClass.getURI()); + logger.log("Class " + deletedClass.getURI() + " has been deleted. Any references to it in the knowledge base will be changed to " + replacementClass.getURI()); - AtomicOntologyChange chg = new AtomicOntologyChange(deletedClass.getURI(), replacementClass.getURI(), AtomicChangeType.RENAME); + AtomicOntologyChange chg = new AtomicOntologyChange(deletedClass.getURI(), replacementClass.getURI(), AtomicChangeType.RENAME, change.getNotes()); renameClass(chg); } @@ -349,18 +346,59 @@ public class ABoxUpdater { } private void addProperty(AtomicOntologyChange propObj) throws IOException{ - OntProperty tempProperty = newTboxModel.getOntProperty - (propObj.getDestinationURI()); - if (tempProperty == null) { - logger.logError("Unable to find property " + - propObj.getDestinationURI() + - " in newTBoxModel"); + OntProperty addedProperty = newTboxModel.getOntProperty (propObj.getDestinationURI()); + + if (addedProperty == null) { + logger.logError("Unable to find property " + propObj.getDestinationURI() + " in new TBox"); return; } - OntProperty superProperty = tempProperty.getSuperProperty(); + + // if the newly added property has an inverse in the new TBox, then for all existing + // ABox statements involving that inverse (if the inverse is new also there won't be + // any) add the corresponding statement with the new property. + // + // Shouldn't a reasoner be doing this? + + + OntProperty inverseOfAddedProperty = addedProperty.getInverseOf(); + + if (inverseOfAddedProperty != null) { + Model additions = ModelFactory.createDefaultModel(); + aboxModel.enterCriticalSection(Lock.WRITE); + + try { + StmtIterator iter = aboxModel.listStatements((Resource) null, inverseOfAddedProperty, (RDFNode) null); + + while (iter.hasNext()) { + + Statement stmt = iter.next(); + Statement newStmt = ResourceFactory.createStatement(stmt.getObject().asResource(), addedProperty, stmt.getSubject()); + additions.add(newStmt); + } + + aboxModel.add(additions); + record.recordAdditions(additions); + + if (additions.size() > 0) { + logger.log(additions.size() + " statement" + + ((additions.size() > 1) ? "s" : "") + + " with predicate " + addedProperty.getURI() + " " + + ((additions.size() > 1) ? "were" : "was") + + " added (as an inverse to existing " + inverseOfAddedProperty.getURI() + + " assertions"); + } + + } finally { + aboxModel.leaveCriticalSection(); + } + + } + +/* OntProperty superProperty = addedProperty.getSuperProperty(); if (superProperty == null) { return; } + int count = aboxModel.listStatements( (Resource) null, superProperty, (RDFNode) null).toSet().size(); if (count > 0) { @@ -369,7 +407,7 @@ public class ABoxUpdater { "a new subproperty " + propObj.getDestinationURI() + " in the new ontology version. "); logger.log("Please review uses of this property to see if " + propObj.getDestinationURI() + " is a more appropriate choice."); - } + }*/ } private void deleteProperty(AtomicOntologyChange propObj) throws IOException{ @@ -413,7 +451,7 @@ public class ABoxUpdater { propObj.getSourceURI() + " " + (plural ? "were" : "was") + " removed. "); } } else { - AtomicOntologyChange chg = new AtomicOntologyChange(deletedProperty.getURI(), replacementProperty.getURI(), AtomicChangeType.RENAME); + AtomicOntologyChange chg = new AtomicOntologyChange(deletedProperty.getURI(), replacementProperty.getURI(), AtomicChangeType.RENAME, propObj.getNotes()); renameProperty(chg); } @@ -435,8 +473,7 @@ public class ABoxUpdater { } Model renamePropAddModel = ModelFactory.createDefaultModel(); - Model renamePropRetractModel = - ModelFactory.createDefaultModel(); + Model renamePropRetractModel = ModelFactory.createDefaultModel(); aboxModel.enterCriticalSection(Lock.WRITE); try { diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/ontology/update/AtomicOntologyChange.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/ontology/update/AtomicOntologyChange.java index 8441c24d2..e19b095ff 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/ontology/update/AtomicOntologyChange.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/ontology/update/AtomicOntologyChange.java @@ -13,6 +13,7 @@ public class AtomicOntologyChange { private String sourceURI; private String destinationURI; private AtomicChangeType atomicChangeType; + private String notes; public AtomicOntologyChange() { @@ -20,11 +21,13 @@ public class AtomicOntologyChange { public AtomicOntologyChange(String sourceURI, String destinationURI, - AtomicChangeType atomicChangeType) { + AtomicChangeType atomicChangeType, + String notes) { this.sourceURI = sourceURI; this.destinationURI = destinationURI; this.atomicChangeType = atomicChangeType; + this.notes = notes; } @@ -63,9 +66,17 @@ public class AtomicOntologyChange { public void setAtomicChangeType(AtomicChangeType atomicChangeType) { this.atomicChangeType = atomicChangeType; } - + + public String getNotes() { + return notes; + } + + public void setNotes(String notes) { + this.notes = notes; + } + public enum AtomicChangeType { ADD, DELETE, RENAME } - + } diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/ontology/update/DateTimeMigration.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/ontology/update/DateTimeMigration.java index c2396852d..724abda15 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/ontology/update/DateTimeMigration.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/ontology/update/DateTimeMigration.java @@ -2,9 +2,26 @@ package edu.cornell.mannlib.vitro.webapp.ontology.update; +import java.io.IOException; +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; + +import com.hp.hpl.jena.datatypes.xsd.XSDDateTime; +import com.hp.hpl.jena.ontology.DatatypeProperty; +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.rdf.model.Literal; import com.hp.hpl.jena.rdf.model.Model; import com.hp.hpl.jena.rdf.model.ModelFactory; +import com.hp.hpl.jena.rdf.model.RDFNode; +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; /** @@ -17,7 +34,19 @@ public class DateTimeMigration { private OntModel aboxModel; private OntologyChangeLogger logger; private OntologyChangeRecord record; + + private static final String dateTimeURI = "http://vivoweb.org/ontology/core#dateTime"; + private static final String dateTimePrecisionURI = "http://vivoweb.org/ontology/core#dateTimePrecision"; + private static final String yPrecisionURI = "http://vivoweb.org/ontology/core#yearPrecision"; + private static final String ymPrecisionURI = "http://vivoweb.org/ontology/core#yearMonthPrecision"; + private static final String ymdPrecisionURI = "http://vivoweb.org/ontology/core#yearMonthDayPrecision"; + private static final String ymdtPrecisionURI = "http://vivoweb.org/ontology/core#yearMonthDayTimePrecision"; + + private DatatypeProperty dateTimeProp = (ModelFactory.createOntologyModel(OntModelSpec.OWL_MEM)).createDatatypeProperty(dateTimeURI); + private ObjectProperty dateTimePrecisionProp = (ModelFactory.createOntologyModel(OntModelSpec.OWL_MEM)).createObjectProperty(dateTimePrecisionURI); + + /** * Constructor * @@ -40,24 +69,110 @@ public class DateTimeMigration { * and property definitions in the transition from version 1.1 to 1.2. * */ - public void updateABox() { - + public void updateABox() throws IOException { + + // note: not handling timezones - they are not expected to be in the 1.1.1 data + DateFormat yearFormat = new SimpleDateFormat("yyyy"); + DateFormat yearMonthFormat = new SimpleDateFormat("yyyy-mm"); + DateFormat yearMonthDayFormat = new SimpleDateFormat("yyyy-mm-dd"); + + aboxModel.enterCriticalSection(Lock.WRITE); + + try { + Model additions = ModelFactory.createDefaultModel(); + Model retractions = ModelFactory.createDefaultModel(); + + StmtIterator iter = aboxModel.listStatements((Resource) null, dateTimeProp, (RDFNode) null); + + while (iter.hasNext()) { + Statement newStmt = null; + Date date = null; + Statement stmt = iter.next(); + String precision = getPrecision(stmt); + + if (precision == null) { + logger.log("WARNING: no precision found for individual " + stmt.getSubject().getURI() ); + } else if (yPrecisionURI.equals(precision)) { + try { + date = yearFormat.parse(stmt.getObject().asLiteral().getLexicalForm()); + } catch (ParseException pe) { + logger.log("Parse Exception for year literal: " + stmt.getObject().asLiteral().getLexicalForm() + ". skipping statement."); + } + + newStmt = ResourceFactory.createStatement(stmt.getSubject(), stmt.getPredicate(), getDateTimeLiteral(date) ); + + } else if (ymPrecisionURI.equals(precision)) { + try { + date = yearMonthFormat.parse(stmt.getObject().asLiteral().getLexicalForm()); + } catch (ParseException pe) { + logger.log("Parse Exception for year literal: " + stmt.getObject().asLiteral().getLexicalForm() + ". skipping statement."); + } + + newStmt = ResourceFactory.createStatement(stmt.getSubject(), stmt.getPredicate(), getDateTimeLiteral(date) ); + } else if (ymdPrecisionURI.equals(precision)) { + try { + date = yearMonthDayFormat.parse(stmt.getObject().asLiteral().getLexicalForm()); + } catch (ParseException pe) { + logger.log("Parse Exception for year literal: " + stmt.getObject().asLiteral().getLexicalForm() + ". skipping statement."); + } + + newStmt = ResourceFactory.createStatement(stmt.getSubject(), stmt.getPredicate(), getDateTimeLiteral(date) ); + + } else if (ymdtPrecisionURI.equals(precision)) { + logger.log("WARNING: unhandled precision found for individual " + stmt.getSubject().getURI() + ": " + precision ); + } else { + logger.log("WARNING: unrecognized precision found for individual " + stmt.getSubject().getURI() + ": " + precision ); + } + + if (newStmt != null ) { + additions.add(newStmt); + retractions.add(stmt); + } + } + + aboxModel.remove(retractions); + record.recordRetractions(retractions); + aboxModel.add(additions); + record.recordAdditions(additions); + + } finally { + aboxModel.leaveCriticalSection(); + } + } + + public String getPrecision(Statement stmt) { + + String precision = null; + aboxModel.enterCriticalSection(Lock.WRITE); try { - Model additions = ModelFactory.createDefaultModel(); - Model retractions = ModelFactory.createDefaultModel(); - - - aboxModel.remove(retractions); - record.recordRetractions(retractions); - aboxModel.add(additions); - record.recordAdditions(additions); + StmtIterator iter = aboxModel.listStatements(stmt.getSubject(), dateTimePrecisionProp, (RDFNode) null); + + while (iter.hasNext()) { + Statement statement = iter.next(); + precision = ((Resource)statement.getObject()).getURI(); + } + + return precision; } finally { aboxModel.leaveCriticalSection(); } } + + + public static Literal getDateTimeLiteral(Date date) { + + // Note this loses time zone info, don't know how get parser to extract that + //Calendar cal = Calendar.getInstance( TimeZone.getTimeZone("GMT") ); + + Calendar cal = Calendar.getInstance(); + cal.setTime(date); + XSDDateTime dt = new XSDDateTime(cal); + return ResourceFactory.createTypedLiteral(dt); + + } } \ No newline at end of file diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/ontology/update/OntologyChangeParser.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/ontology/update/OntologyChangeParser.java index 5d411d8ef..8ac37b82f 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/ontology/update/OntologyChangeParser.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/ontology/update/OntologyChangeParser.java @@ -2,18 +2,17 @@ package edu.cornell.mannlib.vitro.webapp.ontology.update; -import edu.cornell.mannlib.vitro.webapp.ontology.update.AtomicOntologyChange; -import edu.cornell.mannlib.vitro.webapp.ontology.update.AtomicOntologyChange.AtomicChangeType; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.StringTokenizer; import org.skife.csv.CSVReader; import org.skife.csv.SimpleReader; -import java.io.FileInputStream; -import java.io.File; -import java.io.IOException; -import java.util.List; -import java.util.StringTokenizer; -import java.util.ArrayList; +import edu.cornell.mannlib.vitro.webapp.ontology.update.AtomicOntologyChange.AtomicChangeType; /** * Performs parsing on Prompt output and provides change object list. @@ -61,12 +60,19 @@ public class OntologyChangeParser { } else { changeObj = new AtomicOntologyChange(); + if (cols[0] != null && cols[0].length() > 0) { changeObj.setSourceURI(cols[0]); } + if (cols[1] != null && cols[1].length() > 0) { changeObj.setDestinationURI(cols[1]); } + + if (cols[4] != null && cols[4].length() > 0) { + changeObj.setNotes(cols[4]); + } + if ("Yes".equals(cols[2])) { changeObj.setAtomicChangeType(AtomicChangeType.RENAME); } else if ("Delete".equals(cols[3])) { @@ -77,6 +83,8 @@ public class OntologyChangeParser { logger.logError("Invalid rename or change type data: '" + cols[2] + " " + cols[3] + "'"); } + + changeObjects.add(changeObj); } diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/ontology/update/OntologyUpdater.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/ontology/update/OntologyUpdater.java index 98eb5dbb1..9c5fc9958 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/ontology/update/OntologyUpdater.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/ontology/update/OntologyUpdater.java @@ -65,7 +65,6 @@ public class OntologyUpdater { if (!logger.errorsWritten()) { // add assertions to the knowledge base showing that the // update was successful, so we don't need to run it again. - // TODO improve error handling in future version. assertSuccess(); } @@ -80,19 +79,19 @@ public class OntologyUpdater { private void performUpdate() throws IOException { - DateTimeMigration dtMigration = new DateTimeMigration(settings.getOntModelSelector().getABoxModel(), logger, record); - dtMigration.updateABox(); - performSparqlConstructAdditions(settings.getSparqlConstructAdditionsDir(), settings.getOntModelSelector().getABoxModel()); performSparqlConstructRetractions(settings.getSparqlConstructDeletionsDir(), settings.getOntModelSelector().getABoxModel()); - + + DateTimeMigration dtMigration = new DateTimeMigration(settings.getOntModelSelector().getABoxModel(), logger, record); + dtMigration.updateABox(); + List rawChanges = getAtomicOntologyChanges(); AtomicOntologyChangeLists changes = new AtomicOntologyChangeLists(rawChanges, settings.getNewTBoxModel(), settings.getOldTBoxModel()); //process the TBox before the ABox - updateTBoxAnnotations(); + //TODO: uncomment updateTBoxAnnotations(); updateABox(changes); @@ -226,8 +225,7 @@ public class OntologyUpdater { private List getAtomicOntologyChanges() throws IOException { - return (new OntologyChangeParser(logger)) - .parseFile(settings.getDiffFile()); + return (new OntologyChangeParser(logger)).parseFile(settings.getDiffFile()); } @@ -339,18 +337,19 @@ public class OntologyUpdater { if (changeObj.getSourceURI() != null){ if (oldTboxModel.getOntProperty(changeObj.getSourceURI()) != null){ - atomicPropertyChanges.add(changeObj); - } - else if (oldTboxModel.getOntClass(changeObj.getSourceURI()) != null) { - atomicClassChanges.add(changeObj); - } - else{ - logger.logError("Source URI is neither a Property" + - " nor a Class. " + "Change Object skipped for sourceURI: " + changeObj.getSourceURI()); + atomicPropertyChanges.add(changeObj); + } else if (oldTboxModel.getOntClass(changeObj.getSourceURI()) != null) { + atomicClassChanges.add(changeObj); + } else if ("Prop".equals(changeObj.getNotes())) { + atomicPropertyChanges.add(changeObj); + } else if ("Class".equals(changeObj.getNotes())) { + atomicClassChanges.add(changeObj); + } else{ + logger.log("WARNING: Source URI is neither a Property" + + " nor a Class. " + "Change Object skipped for sourceURI: " + changeObj.getSourceURI()); } - } - else if(changeObj.getDestinationURI() != null){ + } else if(changeObj.getDestinationURI() != null){ if (newTboxModel.getOntProperty(changeObj.getDestinationURI()) != null) { atomicPropertyChanges.add(changeObj); @@ -358,16 +357,14 @@ public class OntologyUpdater { getDestinationURI()) != null) { atomicClassChanges.add(changeObj); } else{ - logger.logError("Destination URI is neither a Property" + + logger.log("WARNING: Destination URI is neither a Property" + " nor a Class. " + "Change Object skipped for destinationURI: " + changeObj.getDestinationURI()); } - } - else{ - logger.logError("Source and Destination URI can't be null. " - + "Change Object skipped" ); + } else{ + logger.log("WARNING: Source and Destination URI can't be null. " + "Change Object skipped" ); } } - //logger.log("Property and Class change Object lists separated"); + //logger.log("Property and Class change Object lists have been created"); } public List getAtomicClassChanges() {