/*******************************************************************************
* This file is part of ecco.
*
* ecco is distributed under the terms of the GNU Lesser General Public License (LGPL), Version 3.0.
*
* Copyright 2011-2014, The University of Manchester
*
* ecco 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.
*
* ecco 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 ecco.
* If not, see http://www.gnu.org/licenses/.
******************************************************************************/
package uk.ac.manchester.cs.diff.axiom;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import org.semanticweb.owlapi.model.OWLAxiom;
import org.semanticweb.owlapi.model.OWLEntity;
import org.semanticweb.owlapi.model.OWLOntology;
import org.semanticweb.owlapi.reasoner.OWLReasoner;
import uk.ac.manchester.cs.diff.axiom.changeset.LogicalChangeSet;
import uk.ac.manchester.cs.diff.axiom.changeset.StructuralChangeSet;
import uk.ac.manchester.cs.diff.exception.InconsistentOntologyException;
import uk.ac.manchester.cs.diff.output.csv.CSVAxiomDiffReport;
import uk.ac.manchester.cs.diff.output.xml.XMLAxiomDiffReport;
import uk.ac.manchester.cs.diff.utils.ReasonerLoader;
/**
* @author Rafael S. Goncalves <br>
* Information Management Group (IMG) <br>
* School of Computer Science <br>
* University of Manchester <br>
*/
public class LogicalDiffConcurrent implements AxiomDiff {
private OWLOntology ont1, ont2;
private StructuralChangeSet structChangeSet;
private LogicalChangeSet logicalChangeSet;
private OWLReasoner ont1reasoner, ont2reasoner;
private double diffTime;
private boolean verbose;
/**
* Constructor
* @param ont1 Ontology 1
* @param ont2 Ontology 2
* @param verbose Verbose mode
*/
public LogicalDiffConcurrent(OWLOntology ont1, OWLOntology ont2, boolean verbose) {
this.ont1 = ont1;
this.ont2 = ont2;
this.verbose = verbose;
}
/**
* Constructor that takes a structural change set
* @param ont1 Ontology 1
* @param ont2 Ontology 2
* @param changeSet Structural change set
* @param verbose Verbose mode
*/
public LogicalDiffConcurrent(OWLOntology ont1, OWLOntology ont2, StructuralChangeSet changeSet, boolean verbose) {
this.ont1 = ont1;
this.ont2 = ont2;
this.structChangeSet = changeSet;
this.verbose = verbose;
}
/**
* Get logical changes between ontologies given a reasoner instance per ontology
* @param ont1reasoner Instance of a reasoner loaded with ontology 1
* @param ont2reasoner Instance of a reasoner loaded with ontology 2
* @return Logical change set
*/
public LogicalChangeSet getDiff(OWLReasoner ont1reasoner, OWLReasoner ont2reasoner) {
this.ont1reasoner = ont1reasoner;
this.ont2reasoner = ont2reasoner;
return getDiff();
}
/**
* Get logical changes between ontologies
* @return Logical change set
*/
@SuppressWarnings("deprecation")
public LogicalChangeSet getDiff() {
if(logicalChangeSet != null) return logicalChangeSet;
if(structChangeSet == null) structChangeSet = new StructuralDiff(ont1, ont2, verbose).getDiff();
if(ont1reasoner == null) ont1reasoner = new ReasonerLoader(ont1).createReasoner(false);
if(ont2reasoner == null) ont2reasoner = new ReasonerLoader(ont2).createReasoner(false);
if(!ont1reasoner.isConsistent())
throw new InconsistentOntologyException("Ontology 1 is inconsistent. Cannot perform logical diff on inconsistent input.");
if(!ont2reasoner.isConsistent())
throw new InconsistentOntologyException("Ontology 2 is inconsistent. Cannot perform logical diff on inconsistent input.");
if(verbose) System.out.print(" Verifying axiom impact... ");
long start = System.currentTimeMillis();
IneffectualChangeChecker ineffAddChecker = new IneffectualChangeChecker(structChangeSet.getAddedAxioms(), ont1reasoner);
IneffectualChangeChecker ineffRemChecker = new IneffectualChangeChecker(structChangeSet.getRemovedAxioms(), ont2reasoner);
ExecutorService executor = Executors.newFixedThreadPool(2);
List<Callable<Set<OWLAxiom>>> l = new ArrayList<Callable<Set<OWLAxiom>>>();
l.add(ineffRemChecker); l.add(ineffAddChecker);
Future<Set<OWLAxiom>> ia = executor.submit(ineffAddChecker);
Future<Set<OWLAxiom>> ir = executor.submit(ineffRemChecker);
Set<OWLAxiom> ineffectualAdditions = null, ineffectualRemovals = null;
try {
ineffectualAdditions = ia.get();
ineffectualRemovals = ir.get();
} catch(InterruptedException e) {
e.printStackTrace();
} catch(ExecutionException e) {
e.printStackTrace();
}
executor.shutdown();
Set<OWLAxiom> effectualAdditions = new HashSet<OWLAxiom>(structChangeSet.getAddedAxioms());
effectualAdditions.removeAll(ineffectualAdditions);
Set<OWLAxiom> effectualRemovals = new HashSet<OWLAxiom>(structChangeSet.getRemovedAxioms());
effectualRemovals.removeAll(ineffectualRemovals);
long end = System.currentTimeMillis();
diffTime = (end-start)/1000.0;
logicalChangeSet = new LogicalChangeSet(effectualAdditions, ineffectualAdditions, effectualRemovals, ineffectualRemovals, structChangeSet);
logicalChangeSet.setDiffTime(diffTime);
if(verbose) System.out.println("done (" + diffTime + " secs)");
if(verbose) printDiff();
return logicalChangeSet;
}
/**
* Ineffectual change checker worker
*/
public class IneffectualChangeChecker implements Callable<Set<OWLAxiom>> {
private Set<OWLAxiom> axioms;
private OWLReasoner reasoner;
/**
* Constructor
* @param axioms Set of axioms to be checked
* @param reasoner Reasoner instance
*/
public IneffectualChangeChecker(Set<OWLAxiom> axioms, OWLReasoner reasoner) {
this.axioms = axioms;
this.reasoner = reasoner;
}
@Override
public Set<OWLAxiom> call() {
Set<OWLAxiom> ineffectual = null;
Set<OWLEntity> ontSig = reasoner.getRootOntology().getSignature();
ineffectual = new HashSet<OWLAxiom>();
for(OWLAxiom axiom : axioms) {
if(ontSig.containsAll(axiom.getSignature())) {
if(reasoner.isEntailed(axiom))
ineffectual.add(axiom);
}
}
return ineffectual;
}
}
/**
* Print diff results
*/
public void printDiff() {
System.out.println(
"\tEffectual Additions: " + logicalChangeSet.getEffectualAdditionAxioms().size() +
"\n\tEffectual Removals: " + logicalChangeSet.getEffectualRemovalAxioms().size() +
"\n\tIneffectual Additions: " + logicalChangeSet.getIneffectualAdditionAxioms().size() +
"\n\tIneffectual Removals: " + logicalChangeSet.getIneffectualRemovalAxioms().size());
}
/**
* Get an XML change report for the change set computed by this diff
* @return XML change report object
*/
public XMLAxiomDiffReport getXMLReport() {
if(logicalChangeSet == null) logicalChangeSet = getDiff();
return new XMLAxiomDiffReport(ont1, ont2, logicalChangeSet);
}
/**
* Get a CSV change report
* @return Change report as a CSV document
*/
public String getCSVChangeReport() {
if(logicalChangeSet == null) logicalChangeSet = this.getDiff();
CSVAxiomDiffReport report = new CSVAxiomDiffReport();
report.getReport(structChangeSet);
return report.getReport(logicalChangeSet);
}
/**
* Determine if ontologies are logically equivalent
* @return true if ontologies are logically equivalent, false otherwise
*/
public boolean isEquivalent() {
if(logicalChangeSet == null) logicalChangeSet = this.getDiff();
if(logicalChangeSet.getEffectualAdditionAxioms().isEmpty() && logicalChangeSet.getEffectualRemovalAxioms().isEmpty())
return true;
else
return false;
}
/**
* Convenience method to get the StructuralChangeSet
* @return Structural diff change set
*/
public StructuralChangeSet getStructuralChangeSet() {
if(structChangeSet != null)
return structChangeSet;
else
return new StructuralDiff(ont1, ont2, verbose).getDiff();
}
}