/******************************************************************************* * This file is part of the Coporate Semantic Web Project. * * This work has been partially supported by the ``InnoProfile-Corporate Semantic Web" project funded by the German Federal * Ministry of Education and Research (BMBF) and the BMBF Innovation Initiative for the New German Laender - Entrepreneurial Regions. * * http://www.corporate-semantic-web.de/ * * * Freie Universitaet Berlin * Copyright (c) 2007-2013 * * * Institut fuer Informatik * Working Group Coporate Semantic Web * Koenigin-Luise-Strasse 24-26 * 14195 Berlin * * http://www.mi.fu-berlin.de/en/inf/groups/ag-csw/ * * * * This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. * You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA or see <http://www.gnu.org/licenses/> ******************************************************************************/ package de.fuberlin.agcsw.svont.changedetection; import java.net.URI; import java.util.Collection; import java.util.Set; import org.apache.log4j.Logger; import org.semanticweb.owlapi.apibinding.OWLManager; import org.semanticweb.owlapi.model.IRI; import org.semanticweb.owlapi.model.OWLAnnotationAssertionAxiom; import org.semanticweb.owlapi.model.OWLAnnotationSubject; import org.semanticweb.owlapi.model.OWLAxiom; import org.semanticweb.owlapi.model.OWLClass; import org.semanticweb.owlapi.model.OWLClassAxiom; import org.semanticweb.owlapi.model.OWLDataProperty; import org.semanticweb.owlapi.model.OWLDataPropertyAxiom; import org.semanticweb.owlapi.model.OWLDeclarationAxiom; import org.semanticweb.owlapi.model.OWLEntity; import org.semanticweb.owlapi.model.OWLIndividual; import org.semanticweb.owlapi.model.OWLIndividualAxiom; import org.semanticweb.owlapi.model.OWLNamedIndividual; import org.semanticweb.owlapi.model.OWLObject; import org.semanticweb.owlapi.model.OWLObjectProperty; import org.semanticweb.owlapi.model.OWLObjectPropertyAxiom; import org.semanticweb.owlapi.model.OWLOntology; import org.semanticweb.owlapi.model.OWLOntologyManager; import cz.cvut.kbss.owldiff.diff.owlapi.Diff; import cz.cvut.kbss.owldiff.diff.syntactic.OntologyDiff; /** * Basic ontology difference Checker * * http://krizik.felk.cvut.cz/km/owldiff/ * * It uses the OWLdiff Pellet difference checking of Ontologies and map the results to a concept level difference * * @author mario * */ public class BaseDiffAlgorithm implements DiffExecutor { final Logger log = Logger.getLogger(BaseDiffAlgorithm.class); /** * OWLAPI Base Ontology object */ private OWLOntology baseOnt; /** * OWLAPU Update Ontologz Object */ private OWLOntology updateOnt; /** * DiffResult Container object */ private DiffResult diffResult; /* (non-Javadoc) * @see de.fuberlin.agcsw.svont.changedetection.DiffExecutor#executeDiff(java.net.URI, java.net.URI) */ public DiffResult executeDiff(URI baseOntologyURI, URI updateOntologyURI) throws Exception { //create Ontology Manager OWLOntologyManager baseM = OWLManager.createOWLOntologyManager(); OWLOntologyManager updateM = OWLManager.createOWLOntologyManager(); //create OWLOntology Objects baseOnt = baseM.loadOntologyFromOntologyDocument(IRI.create(baseOntologyURI)); updateOnt = updateM.loadOntologyFromOntologyDocument(IRI.create(updateOntologyURI)); //create Basic Diff Object from OWLdiff Diff d = new Diff(null, baseM, baseOnt, updateM, updateOnt); // execute base owlDiff algorithm from the OWLdiff // this does a simple structural diff on an axiomatic level log.debug("Executing basic structural diff"); d.diff(false); // create a diff Result Object diffResult = new DiffResult(); //recieve difference Axioms // Collection<OWLAxiom> addedAxioms = d.getUpdateDiff(); // Collection<OWLAxiom> removedAxioms = d.getOriginalDiff(); OntologyDiff addedAxioms = d.getUpdateDiff(); OntologyDiff removedAxioms = d.getOriginalDiff(); // test if the structual diff is empty if (addedAxioms.isEmpty() && removedAxioms.isEmpty()) { // then we finished diffing the ontologies cause there was no // structural change log.info("Ontologies are structural equal!"); return diffResult; } //test if URI changed -- do not check for entailments if different boolean differentURI = baseOnt.getOntologyID().getOntologyIRI().compareTo(updateOnt.getOntologyID().getOntologyIRI()) != 0 ; if ( !differentURI ) { // check for semantical difference log.info("Checking for Entailments"); d.diff(true); // axioms that may be redunant in the update ontology, cause they can be // inferred by the base one Collection<OWLAxiom> possiblyRemove = d.getUpdateInferred(); // axioms that are not lost when ontology gets replaced by the new one Collection<OWLAxiom> inferred = d.getOriginalInferred(); // these axioms dont need to be considered for the semantical diff addedAxioms.getAxioms().removeAll(possiblyRemove); removedAxioms.getAxioms().removeAll(inferred); // test if semantical diff is empty if (addedAxioms.isEmpty() && removedAxioms.isEmpty()) { // then we finished diffing the ontologies cause there was no // semantical change log.info("Ontologies are semantically equal!"); return diffResult; } } else { log.info("URI changed, skipping Entailments-Check"); } // we can be sure that there are differences which will be written diffResult.setEmpty(false); // now we have a (semantical) diff on the axiomatic level. // we need the diff on the conceptional level so we have to map the // axioms to a conceptional diff model log.debug("Mapping axiomatic difference to conceptional level"); processAddedAxioms(addedAxioms); processRemovedAxioms(removedAxioms); // print the diff to the log if (log.isDebugEnabled()) { logAxiomaticDiffResult(diffResult); logConceptionalDiffResult(diffResult); } return diffResult; } /** * Map the added Axioms to the concept level and write Results to the DiffResult object * * @param axioms Collection of Axioms to process */ private void processAddedAxioms(OntologyDiff diff) { Collection<OWLAxiom> axioms = diff.getAxioms(); DiffResult dr = diffResult; for (OWLAxiom a : axioms) { if ((a instanceof OWLClassAxiom)) { // search for classes that "uses" this axiom for (OWLClass entity : updateOnt.getClassesInSignature()) { if (updateOnt.getAxioms(entity).contains(a)) { // this class c has been added or changed if (baseOnt.containsEntityInSignature(entity.getIRI())) { // this class exists in the base ontology so its just a change dr.getChangedClasses().add(entity); } else {// this class has been added dr.getAddedClasses().add(entity); } } } dr.getAddedClassAxioms().add(a); } else if (a instanceof OWLDataPropertyAxiom) { // search for data properties that "uses" this axiom for (OWLDataProperty entity : updateOnt .getDataPropertiesInSignature()) { if (updateOnt.getAxioms(entity).contains(a)) { if (baseOnt.containsDataPropertyInSignature(entity.getIRI())) { // this data property exists in the base ontology so its just a change dr.getChangedDataProperties().add(entity); } else { // this data property has been added dr.getAddedDataProperties().add(entity); } } } dr.getAddedDataPropertyAxioms().add(a); } else if (a instanceof OWLObjectPropertyAxiom) { // search for object properties that "uses" this axiom for (OWLObjectProperty entity : updateOnt .getObjectPropertiesInSignature()) { if (updateOnt.getAxioms(entity).contains(a)) { if (baseOnt .containsObjectPropertyInSignature(entity.getIRI())) { // this data property exists in the base ontology so // its just a change dr.getChangedObjectProperties().add(entity); } else { // this data property has been added dr.getAddedObjectProperties().add(entity); } } } dr.getAddedObjectPropertyAxioms().add(a); } else if (a instanceof OWLIndividualAxiom) { // search for Individuals that "use" this axiom for (OWLIndividual entity : updateOnt.getIndividualsInSignature()) { if (updateOnt.getAxioms(entity).contains(a)) { if (entity instanceof OWLNamedIndividual) { OWLNamedIndividual namedIndividual = (OWLNamedIndividual)entity; if (baseOnt.containsIndividualInSignature(namedIndividual.getIRI())) { // this Individual exists in the base ontology so // its just a change dr.getChangedIndividuals().add(entity); } else { // this Individual has been added dr.getAddedIndividuals().add(entity); } } } } dr.getAddedIndividualAxioms().add(a); } else if (a instanceof OWLAnnotationAssertionAxiom) { log.debug("processing an AnnotationAxiom"); OWLAnnotationAssertionAxiom annotationAx = (OWLAnnotationAssertionAxiom) a; OWLAnnotationSubject subject = annotationAx.getSubject(); if (subject instanceof IRI) { // TODO what about annotations of anonymous classes? Set<OWLEntity> subjectEntities = diff.getOntology().getEntitiesInSignature((IRI)subject); if (subjectEntities.size() != 1) { log.warn("Set of subjects of annotation axiom " + a + " has size " + subjectEntities.size() + ". Should be 1"); continue; } OWLEntity entity = subjectEntities.iterator().next(); if (!baseOnt.containsEntityInSignature(entity)) { processAddingHiddenEntities(dr, entity); } dr.getAddedAnnotationAxioms().add(a); } else { // subject is anonymous log.debug("Annotation with anonymous subject: " + a); } } else if (a instanceof OWLDeclarationAxiom) { log.debug("processing an DeclarationAxiom"); OWLDeclarationAxiom decAx = (OWLDeclarationAxiom) a; OWLEntity entity = decAx.getEntity(); if (!baseOnt.containsEntityInSignature(entity)) { processAddingHiddenEntities(dr, entity); } dr.getAddedDeclarationAxioms().add(a); } else dr.getAddedOtherAxioms().add(a); } } private void processAddingHiddenEntities(DiffResult dr, OWLEntity entity) { // its an annotation of something that wasn't there before if (entity.isOWLClass()) { dr.getAddedClasses().add((OWLClass) entity ); } if (entity.isOWLDataProperty()) { dr.getAddedDataProperties().add((OWLDataProperty) entity ); } if (entity.isOWLObjectProperty()) { dr.getAddedObjectProperties().add((OWLObjectProperty) entity ); } if (entity.isOWLNamedIndividual()) { dr.getAddedIndividuals().add((OWLIndividual) entity ); } } /** * Map the removed Axioms to the concept level and write Results to the DiffResult object * * @param axioms Collection of Axioms to process */ private void processRemovedAxioms(OntologyDiff diff) { Collection<OWLAxiom> axioms = diff.getAxioms(); DiffResult dr = diffResult; for (OWLAxiom a : axioms) { if ((a instanceof OWLClassAxiom)) { // search for classes that "uses" this axiom for (OWLClass c : baseOnt.getClassesInSignature()) { Set<OWLClassAxiom> caxioms = baseOnt.getAxioms(c); if (caxioms.contains(a)) { // this class c has been removed or changed if (updateOnt.containsClassInSignature(c.getIRI())) { // this class exists in the base ontology so its // just a change dr.getChangedClasses().add(c); } else { // this class has been removed dr.getRemovedClasses().add(c); } } } dr.getRemovedClassAxioms().add(a); } else if (a instanceof OWLDataPropertyAxiom) { // search for classes that "uses" this axiom for (OWLDataProperty dp : baseOnt.getDataPropertiesInSignature()) { Set<OWLDataPropertyAxiom> dpaxioms = baseOnt.getAxioms(dp); if (dpaxioms.contains(a)) { // this class c has been removed or changed if (updateOnt .containsDataPropertyInSignature(dp.getIRI())) { // this class exists in the base ontology so its // just a change dr.getChangedDataProperties().add(dp); } else { // this class has been removed dr.getRemovedDataProperties().add(dp); } } } dr.getRemovedDataPropertyAxioms().add(a); } else if (a instanceof OWLObjectPropertyAxiom) { // search for classes that "uses" this axiom for (OWLObjectProperty op : baseOnt.getObjectPropertiesInSignature()) { Set<OWLObjectPropertyAxiom> opaxioms = baseOnt .getAxioms(op); if (opaxioms.contains(a)) { // this class c has been removed or changed if (updateOnt.containsObjectPropertyInSignature(op .getIRI())) { // this class exists in the base ontology so its // just a change dr.getChangedObjectProperties().add(op); } else { // this class has been removed dr.getRemovedObjectProperties().add(op); } } } dr.getRemovedObjectPropertyAxioms().add(a); } else if (a instanceof OWLIndividualAxiom) { // search for classes that "uses" this axiom for (OWLIndividual in : baseOnt.getIndividualsInSignature()) { Set<OWLIndividualAxiom> inaxioms = baseOnt.getAxioms(in); if (inaxioms.contains(a)) { if (in.isNamed()) { // this class c has been removed or changed if (updateOnt.containsIndividualInSignature(((OWLNamedIndividual)in).getIRI())) { // this class exists in the base ontology so its // just a change dr.getChangedIndividuals().add(in); } else { // this class has been removed dr.getRemovedIndividuals().add(in); } } } } dr.getRemovedIndividualAxioms().add(a); } else if (a instanceof OWLAnnotationAssertionAxiom) { log.debug("processing an Annotation Assertion Axiom"); OWLAnnotationAssertionAxiom annotationAx = (OWLAnnotationAssertionAxiom) a; OWLAnnotationSubject subject = annotationAx.getSubject(); if (subject instanceof IRI) { // TODO what about annotations of anonymous classes? Set<OWLEntity> subjectEntities = diff.getOntology().getEntitiesInSignature((IRI)subject); if (subjectEntities.size() != 1) { log.warn("Set of subjects of annotation axiom " + a + " has size " + subjectEntities.size() + ". Should be 1"); continue; } OWLEntity entity = subjectEntities.iterator().next(); if (!updateOnt.containsEntityInSignature(entity)) { processRemovingHiddenEntities(dr, entity); } } else { // subject is anonymous log.debug("Annotation with anonymous subject: " + a); } dr.getRemovedAnnotationAxioms().add(a); } else if (a instanceof OWLDeclarationAxiom) { log.debug("processing an DeclarationAxiom"); OWLDeclarationAxiom decAx = (OWLDeclarationAxiom) a; OWLEntity entity = decAx.getEntity(); if (!updateOnt.containsEntityInSignature(entity)) { processRemovingHiddenEntities(dr, entity); } dr.getRemovedDeclarationAxioms().add(a); } else dr.getRemovedOtherAxioms().add(a); } } private void processRemovingHiddenEntities(DiffResult dr, OWLEntity entity) { // its an annotation of something that wasn't there before if (entity.isOWLClass()) { dr.getRemovedClasses().add((OWLClass) entity ); } if (entity.isOWLDataProperty()) { dr.getRemovedDataProperties().add((OWLDataProperty) entity ); } if (entity.isOWLObjectProperty()) { dr.getRemovedObjectProperties().add((OWLObjectProperty) entity ); } if (entity.isOWLNamedIndividual()) { dr.getRemovedIndividuals().add((OWLIndividual) entity ); } } /** * Writes the axiomatic Changes to Log * * @param dr The diffResult */ private void logAxiomaticDiffResult(DiffResult dr) { log.debug("------------------------------------------"); log.debug("Axiomatic Diff Result:"); log.debug("------------------------------------------"); logAxiomTypes(dr.getAddedClassAxioms(), "Class", "added"); logAxiomTypes(dr.getAddedIndividualAxioms(), "Individual", "added"); logAxiomTypes(dr.getAddedDataPropertyAxioms(), "DataProperty", "added"); logAxiomTypes(dr.getAddedObjectPropertyAxioms(), "ObjectProperty", "added"); logAxiomTypes(dr.getAddedAnnotationAxioms(), "Annotation", "added"); logAxiomTypes(dr.getAddedDeclarationAxioms(), "Declaration", "added"); logAxiomTypes(dr.getAddedOtherAxioms(), "Other", "added"); logAxiomTypes(dr.getRemovedClassAxioms(), "Class", "removed"); logAxiomTypes(dr.getRemovedIndividualAxioms(), "Individual", "removed"); logAxiomTypes(dr.getRemovedDataPropertyAxioms(), "DataProperty", "removed"); logAxiomTypes(dr.getRemovedObjectPropertyAxioms(), "ObjectProperty", "removed"); logAxiomTypes(dr.getRemovedAnnotationAxioms(), "Annotation", "removed"); logAxiomTypes(dr.getRemovedDeclarationAxioms(), "Declaration", "removed"); logAxiomTypes(dr.getRemovedOtherAxioms(), "Other", "removed"); log.debug("-------- COMPLETE ----------"); } /** * Logs specific axiomatic Type * * @param ax Collection of Axioms to Log * @param atype Type of Axioms * @param chgtype ChangeType "added" "removed" "changed" */ private void logAxiomTypes(Collection<OWLAxiom> ax, String atype, String chgtype) { if (ax.size() != 0) { log.debug(chgtype.toUpperCase() + " " + atype + " Axioms:"); for (OWLAxiom a : ax) { log.debug(a); } } else log.debug("No " + chgtype + " " + atype + " Axioms"); log.debug("--------"); } /** * Writes Conceptional Changes to Log * * @param dr The DiffResult Object */ private void logConceptionalDiffResult(DiffResult dr) { log.debug("------------------------------------------"); log.debug("Conceptional Diff Mapping Result:"); log.debug("------------------------------------------"); logConceptionalTypes(dr.getAddedClasses(), "Class", "added"); logConceptionalTypes(dr.getAddedDataProperties(), "Data Property", "added"); logConceptionalTypes(dr.getAddedObjectProperties(), "Object Property", "added"); logConceptionalTypes(dr.getAddedIndividuals(), "Individual", "added"); logConceptionalTypes(dr.getAddedDataTypes(), "Datatype", "added"); logConceptionalTypes(dr.getChangedClasses(), "Class", "changed"); logConceptionalTypes(dr.getChangedDataProperties(), "Data Property", "changed"); logConceptionalTypes(dr.getChangedObjectProperties(), "Object Property", "changed"); logConceptionalTypes(dr.getChangedIndividuals(), "Individual", "changed"); logConceptionalTypes(dr.getChangedDataTypes(), "Datatype", "changed"); logConceptionalTypes(dr.getRemovedClasses(), "Class", "removed"); logConceptionalTypes(dr.getRemovedDataProperties(), "Data Property", "removed"); logConceptionalTypes(dr.getRemovedObjectProperties(), "Object Property", "removed"); logConceptionalTypes(dr.getRemovedIndividuals(), "Individual", "removed"); logConceptionalTypes(dr.getRemovedDataTypes(), "Datatype", "removed"); log.debug("-------- COMPLETE ----------"); } /** * Logs the specific Entity Typ * * @param ents Collection of Entities to log * @param eType Typ of Entity * @param chgtype Changetyp "added" "removed" "changed" */ private void logConceptionalTypes(Collection<? extends OWLObject> ents, String eType, String chgtype) { if (ents.size() != 0) { log.debug(chgtype.toUpperCase() + " " + eType + ":"); for (OWLObject e : ents) { log.debug(e); } } else log.debug("No " + chgtype + " " + eType); log.debug("--------"); } }