package diff; import java.io.File; import java.util.*; import java.util.stream.Collectors; import org.semanticweb.owlapi.apibinding.OWLManager; import org.semanticweb.owlapi.model.*; import org.semanticweb.owlapi.search.EntitySearcher; import widoco.Configuration; /** * @author James Malone * @version 1.02 * Use the CompareOntologies class and subsequent doFindAllChanges methods to invoke a diff * between two ontologies. * Diff results can be output to xml file using the writeDiffAsXMLFile method * * * NOTICE: * @author Daniel Garijo * Code adapted to Widoco by Daniel Garijo (heavily changed) * The changes made to the original bubastis code have been made so the original tool can be used * within Widoco without having to invoke bubastis through the command line. They are summarized as follows: * - Simplified the original code removing unused constructors. * - Refined the changes among two classes (now it will return classes with different labels). * - Added change tracking of properties and data properties. * Original bubastis code has been obtained from: https://github.com/EBISPOT/bubastis */ public class CompareOntologies { //classes private ArrayList<OWLAxiomInfo> modifiedClasses = new ArrayList<OWLAxiomInfo>(); private ArrayList<OWLAxiomInfo> newClasses = new ArrayList<OWLAxiomInfo>(); private ArrayList<OWLAxiomInfo> deletedClasses = new ArrayList<OWLAxiomInfo>(); //properties private ArrayList<OWLAxiomInfo> modifiedProperties = new ArrayList<OWLAxiomInfo>(); private ArrayList<OWLAxiomInfo> newProperties = new ArrayList<OWLAxiomInfo>(); private ArrayList<OWLAxiomInfo> deletedProperties = new ArrayList<OWLAxiomInfo>(); //data properties private ArrayList<OWLAxiomInfo> modifiedDataProperties = new ArrayList<OWLAxiomInfo>(); private ArrayList<OWLAxiomInfo> newDataProperties = new ArrayList<OWLAxiomInfo>(); private ArrayList<OWLAxiomInfo> deletedDataProperties = new ArrayList<OWLAxiomInfo>(); private String oldVersion, newVersion; /** * @param oldOntologyLocation location of the first ontology to be compared (the older ontology in most cases) * @param c Configuration where the information of the current ontology is loaded */ public CompareOntologies(String oldOntologyLocation, Configuration c) { //Create 2 OWLOntologyManager which manages a set of ontologies OWLOntologyManager manager1 = OWLManager.createOWLOntologyManager(); OWLOntology ontology1; try { ontology1= manager1.loadOntologyFromOntologyDocument(new File(oldOntologyLocation)); } catch (Exception e) { System.err.println("Error while loading the first ontology"); return; } System.out.println("loading ontology 1 complete"); // try { // ontology2= manager2.loadOntologyFromOntologyDocument(new File(ontology2Location)); // } catch (Exception e) { // System.err.println("Error while loading the second ontology"); // return; // } // System.out.println("loading ontology 2 complete"); this.doFindAllChanges(ontology1, c.getMainOntology().getOWLAPIModel()); } /** * Perform diff on two ontologies supplying the two OWLOntologyManager classes and * the two OWLOntology classes which have the ontologies pre-loaded * * @param ont1 first ontology to be compared (the older ontology in most cases) * @param ont2 second ontology to compare to ont1 (the newer ontology in most cases) */ private void doFindAllChanges(OWLOntology ont1, OWLOntology ont2) { this.oldVersion = ont1.getOntologyID().getOntologyIRI().toString(); this.newVersion = ont2.getOntologyID().getOntologyIRI().toString(); //this.modifiedClasses = compareAllClassAxioms(manager1, ont1, manager2, ont2); this.modifiedClasses = compareAllClassAxioms(ont1, ont2); this.newClasses = findNewClasses( ont1, ont2); this.deletedClasses = findDeletedClasses(ont1, ont2); this.modifiedProperties = compareAllObjectPropertyAxioms(ont1,ont2); this.newProperties = findNewProperties(ont1, ont2); this.deletedProperties = findDeletedProperties(ont1, ont2); this.modifiedDataProperties = compareAllDataPropertyAxioms(ont1, ont2); this.newDataProperties = findNewDataProperties(ont1, ont2); this.deletedDataProperties = findDeletedDataProperties(ont1, ont2); } /** * Method for walking through all the classes in a given ontology and * comparing with classes in another given ontology and identifying * differences in axioms between any two identical classes - identical here * to mean having same class URI * * @param ont1 first ontology to be compared (the older ontology in most cases) * @param ont2 second ontology to compare to ont1 (the newer ontology in most cases) * @return all the axioms that have changed in a class. */ private ArrayList<OWLAxiomInfo> compareAllClassAxioms(OWLOntology ont1, OWLOntology ont2) { ArrayList<OWLAxiomInfo> classDifferences = new ArrayList<OWLAxiomInfo>(); //get all classes from first ontology and walk through them for (OWLClass ont1Class : ont1.getClassesInSignature()) { //find the current class on the second ontology. Compare them. if (ont2.containsClassInSignature(ont1Class.getIRI())) { OWLAxiomInfo tempDiffs = this.getDifferences(ont1Class.getIRI(), new HashSet<Object>(ont1.getAxioms(ont1Class)), new HashSet<Object>(ont2.getAxioms(ont1Class))); //differences in annotations OWLAxiomInfo tempDiffs2 = this.getDifferences(ont1Class.getIRI(), EntitySearcher.getAnnotationAssertionAxioms(ont1Class,ont1).collect(Collectors.toSet()), EntitySearcher.getAnnotationAssertionAxioms(ont1Class,ont2).collect(Collectors.toSet())); // new HashSet<OWLAxiom>(ont1Class.getAnnotationAssertionAxioms(ont1)), // new HashSet<OWLAxiom>(ont1Class.getAnnotationAssertionAxioms(ont2))); //merge sets. tempDiffs.addDeleteChangeAxioms(tempDiffs2.getDeletedAxioms()); tempDiffs.addNewChangeAxioms(tempDiffs2.getNewAxioms()); if (!tempDiffs.isEmpty()){ classDifferences.add(tempDiffs); } } } return classDifferences; } /** * Method designed to track the changes in the property axioms of the ontologies * @param ont1 old version of the ontology * @param ont2 new version of the ontology * @return */ private ArrayList<OWLAxiomInfo> compareAllObjectPropertyAxioms(OWLOntology ont1, OWLOntology ont2) { ArrayList<OWLAxiomInfo> propertyDifferences = new ArrayList<OWLAxiomInfo>(); //get all classes from first ontology and walk through them for (OWLObjectProperty ont1Property : ont1.getObjectPropertiesInSignature()) { //find the current class on the second ontology. Compare them. if (ont2.containsObjectPropertyInSignature(ont1Property.getIRI())) { OWLAxiomInfo tempDiffs = this.getDifferences(ont1Property.getIRI(), new HashSet<Object>(ont1.getAxioms(ont1Property)), new HashSet<Object>(ont2.getAxioms(ont1Property))); //differences in annotations OWLAxiomInfo tempDiffs2 = this.getDifferences(ont1Property.getIRI(), EntitySearcher.getAnnotationAssertionAxioms(ont1Property, ont1).collect(Collectors.toSet()), EntitySearcher.getAnnotationAssertionAxioms(ont1Property, ont2).collect(Collectors.toSet())); // new HashSet<Object>(ont1Property.getAnnotationAssertionAxioms(ont1)), // new HashSet<Object>(ont1Property.getAnnotationAssertionAxioms(ont2))); //merge sets. tempDiffs.addDeleteChangeAxioms(tempDiffs2.getDeletedAxioms()); tempDiffs.addNewChangeAxioms(tempDiffs2.getNewAxioms()); if (!tempDiffs.isEmpty()){ propertyDifferences.add(tempDiffs); } } } return propertyDifferences; } /** * Method designed to track the changes in the data property axioms of the ontologies * @param ont1 old version of the ontology * @param ont2 new version of the ontology * @return */ private ArrayList<OWLAxiomInfo> compareAllDataPropertyAxioms(OWLOntology ont1, OWLOntology ont2) { ArrayList<OWLAxiomInfo> propertyDifferences = new ArrayList<OWLAxiomInfo>(); //get all classes from first ontology and walk through them for (OWLDataProperty ont1Property : ont1.getDataPropertiesInSignature()) { //find the current class on the second ontology. Compare them. if (ont2.containsDataPropertyInSignature(ont1Property.getIRI())) { OWLAxiomInfo tempDiffs = this.getDifferences(ont1Property.getIRI(), new HashSet<Object>(ont1.getAxioms(ont1Property)), new HashSet<Object>(ont2.getAxioms(ont1Property))); //differences in annotations OWLAxiomInfo tempDiffs2 = this.getDifferences(ont1Property.getIRI(), EntitySearcher.getAnnotationAssertionAxioms(ont1Property, ont1).collect(Collectors.toSet()), EntitySearcher.getAnnotationAssertionAxioms(ont1Property, ont2).collect(Collectors.toSet())); // new HashSet<Object>(ont1Property.getAnnotationAssertionAxioms(ont1)), // new HashSet<Object>(ont1Property.getAnnotationAssertionAxioms(ont2))); //merge sets. tempDiffs.addDeleteChangeAxioms(tempDiffs2.getDeletedAxioms()); tempDiffs.addNewChangeAxioms(tempDiffs2.getNewAxioms()); if (!tempDiffs.isEmpty()){ propertyDifferences.add(tempDiffs); } } } return propertyDifferences; } /** * Method to get the different axioms between two sets. * @param entity the entity we want to track on each set. * @param setOnto1 the set of axioms on ontology 1 * @param setOnto2 the set of axioms on ontology 2 * @return */ private OWLAxiomInfo getDifferences(IRI entity, Set<Object>setOnto1, Set<Object>setOnto2){ OWLAxiomInfo tempDiffs = new OWLAxiomInfo(entity, null, null); if(!setOnto1.equals(setOnto2)){ Set<Object> newChanges = new HashSet<Object>(setOnto2); newChanges.removeAll(setOnto1); Set<Object> deletedChanges = new HashSet<Object>(setOnto1); deletedChanges.removeAll(setOnto2); tempDiffs.addNewChangeAxioms(newChanges); tempDiffs.addDeleteChangeAxioms(deletedChanges); } return tempDiffs; } /** * method to find classes that have been deleted, that is classes that appear * in ontology 1 but not in ontology 2. It chains to the findNewClasses and * simply reverses the order, i.e. classes in ontology 1 but not in ontology 2 * * @param ont1 first ontology to be compared (the older ontology in most cases) * @param ont2 second ontology to compare to ont1 (the newer ontology in most cases) * @return axioms from deleted classes */ private ArrayList<OWLAxiomInfo> findDeletedClasses(OWLOntology ont1, OWLOntology ont2) { return findNewClasses(ont2, ont1); } /** * Method to find all the deleted properties, i.e., those that appear in ont2 and do not * appear on on1 * @param ont1 old version of the ontology * @param ont2 new version of the ontology * @return */ private ArrayList<OWLAxiomInfo> findDeletedProperties(OWLOntology ont1, OWLOntology ont2) { return findNewProperties(ont2, ont1); } /** * Method to find all the deleted data properties, i.e., those that appear in ont2 and do not * appear on on1 * @param ont1 old version of the ontology * @param ont2 new version of the ontology * @return */ private ArrayList<OWLAxiomInfo> findDeletedDataProperties(OWLOntology ont1, OWLOntology ont2) { return findNewDataProperties(ont2, ont1); } /** * method to find classes that are 'new', that is classes that appear * in ontology 2 but not in ontology 1 * * @param ont1 first ontology to be compared (the older ontology in most cases) * @param ont2 second ontology to compare to ont1 (the newer ontology in most cases) * @return */ private ArrayList<OWLAxiomInfo> findNewClasses(OWLOntology ont1, OWLOntology ont2) { ArrayList<OWLAxiomInfo> newC = new ArrayList<OWLAxiomInfo>(); //get all classes from 2nd ontology and walk through them for (OWLClass ont2Class : ont2.getClassesInSignature()) { //if there is no reference in ontology 1 to the class in ontoloy 2 then it's new if (!ont1.containsClassInSignature(ont2Class.getIRI())) { Set<Object> newClassAxiomsSet = new HashSet<Object>(ont2.getAxioms(ont2Class)); //create information for the new class OWLAxiomInfo tempNewClass = new OWLAxiomInfo(ont2Class.getIRI(), newClassAxiomsSet,null); newC.add(tempNewClass); } } return newC; } /** * Method to find properties that are new, i.e., that appear in ont2 but not in ont1 * @param ont1 old version of the ontology * @param ont2 new version of the ontology * @return */ private ArrayList<OWLAxiomInfo> findNewProperties(OWLOntology ont1, OWLOntology ont2) { ArrayList<OWLAxiomInfo> newC = new ArrayList<OWLAxiomInfo>(); for (OWLObjectProperty ont2Prop : ont2.getObjectPropertiesInSignature()) { if (!ont1.containsObjectPropertyInSignature(ont2Prop.getIRI())) { Set<Object> newProprAxiomsSet = new HashSet<Object>(ont2.getAxioms(ont2Prop)); OWLAxiomInfo tempNewProp = new OWLAxiomInfo(ont2Prop.getIRI(), newProprAxiomsSet,null); newC.add(tempNewProp); } } return newC; } /** * Method to find data properties that are new, i.e., that appear in ont2 but not in ont1 * @param ont1 old version of the ontology * @param ont2 new version of the ontology * @return */ private ArrayList<OWLAxiomInfo> findNewDataProperties(OWLOntology ont1, OWLOntology ont2) { ArrayList<OWLAxiomInfo> newC = new ArrayList<OWLAxiomInfo>(); for (OWLDataProperty ont2Prop : ont2.getDataPropertiesInSignature()) { if (!ont1.containsDataPropertyInSignature(ont2Prop.getIRI())) { Set<Object> newDataProprAxiomsSet = new HashSet<Object>(ont2.getAxioms(ont2Prop)); OWLAxiomInfo tempNewProp = new OWLAxiomInfo(ont2Prop.getIRI(), newDataProprAxiomsSet,null); newC.add(tempNewProp); } } return newC; } /** * get method to retrieve information about all the classes with differences * * @return list of classes with information about differences */ public ArrayList<OWLAxiomInfo> getClassesWithDifferences() { return modifiedClasses; } /** * get method to retrieve information about all the classes that are new to second ontology * * @return list of classes with information about the classes new to the second ontology */ public ArrayList<OWLAxiomInfo> getNewClasses() { return newClasses; } /** * Get all of the classes that have been deleted between versions * * @return the deletedClasses */ public ArrayList<OWLAxiomInfo> getDeletedClasses() { return deletedClasses; } public ArrayList<OWLAxiomInfo> getDeletedDataProperties() { return deletedDataProperties; } public ArrayList<OWLAxiomInfo> getDeletedProperties() { return deletedProperties; } public ArrayList<OWLAxiomInfo> getModifiedClasses() { return modifiedClasses; } public ArrayList<OWLAxiomInfo> getModifiedDataProperties() { return modifiedDataProperties; } public ArrayList<OWLAxiomInfo> getModifiedProperties() { return modifiedProperties; } public ArrayList<OWLAxiomInfo> getNewDataProperties() { return newDataProperties; } public ArrayList<OWLAxiomInfo> getNewProperties() { return newProperties; } public String getNewVersion() { return newVersion; } public String getOldVersion() { return oldVersion; } }