/******************************************************************************* * 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.output.xml; import java.io.StringWriter; import java.util.Set; import java.util.UUID; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.OutputKeys; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerConfigurationException; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import org.semanticweb.owlapi.model.OWLAxiom; import org.semanticweb.owlapi.model.OWLObject; import org.semanticweb.owlapi.util.ShortFormProvider; import org.semanticweb.owlapi.util.SimpleShortFormProvider; import org.w3c.dom.Document; import org.w3c.dom.Element; import uk.ac.manchester.cs.diff.concept.change.ConceptChange; import uk.ac.manchester.cs.diff.concept.changeset.ConceptChangeSet; import uk.ac.manchester.cs.diff.exception.NotImplementedException; import uk.ac.manchester.cs.diff.unity.changeset.ChangeSet; import uk.ac.manchester.cs.owl.owlapi.mansyntaxrenderer.ManchesterOWLSyntaxObjectRenderer; /** * @author Rafael S. Goncalves <br> * Information Management Group (IMG) <br> * School of Computer Science <br> * University of Manchester <br> */ public class XMLConceptDiffReport implements XMLDiffReport { private final String uuid = UUID.randomUUID().toString(); private ConceptChangeSet changeSet; private DocumentBuilderFactory dbfac; private DocumentBuilder docBuilder; private SimpleShortFormProvider sf; private Document doc; /** * Constructor * @param changeSet Concept diff change set */ public XMLConceptDiffReport(ConceptChangeSet changeSet) { this.changeSet = changeSet; sf = new SimpleShortFormProvider(); dbfac = DocumentBuilderFactory.newInstance(); } /** * Get diff report as an XML document * @return XML document */ public Document getReport() { try { this.docBuilder = dbfac.newDocumentBuilder(); } catch (ParserConfigurationException e) { e.printStackTrace(); } doc = docBuilder.newDocument(); prepDocument(doc, ""); populateDocument(); return doc; } @Override public String getReportAsString(Document doc) { TransformerFactory transfac = TransformerFactory.newInstance(); Transformer trans = null; try { trans = transfac.newTransformer(); } catch (TransformerConfigurationException e) { e.printStackTrace(); } return getXMLAsString(trans, doc); } /** * Transform the document using the given transformer and return the transformation as a string * @param trans Transformer * @param doc XML document * @return String result of transforming the XML document */ private String getXMLAsString(Transformer trans, Document doc) { trans.setOutputProperty(OutputKeys.INDENT, "yes"); // Create string from XML tree StringWriter sw = new StringWriter(); StreamResult result = new StreamResult(sw); DOMSource source = new DOMSource(doc); try { trans.transform(source, result); } catch (TransformerException e) { e.printStackTrace(); } return sw.toString(); } /** * Populate the XML document with ontology 1, 2, and overall changes */ private void populateDocument() { addElement("Ontology1", "lhs", changeSet.getLHSAffectedConcepts().size(), doc, "root", true); addElement("Ontology2", "rhs", changeSet.getRHSAffectedConcepts().size(), doc, "root", true); addLHSConceptDiff("lhs", doc); addRHSConceptDiff("rhs", doc); } /** * Add ontology 1 (LHS) affected concepts * @param parent Parent element * @param d XML document */ private void addLHSConceptDiff(String parent, Document d) { addElement("Specialised", "lhs_spec", changeSet.getLHSSpecialisedConcepts().size(), d, parent, true); addSpecialisationChanges("PurelyDirect", "lhs_pds", changeSet.getLHSPurelyDirectlySpecialised(), d, "lhs_spec"); addSpecialisationChanges("PurelyIndirect", "lhs_pis", changeSet.getLHSPurelyIndirectlySpecialised(), d, "lhs_spec"); addSpecialisationChanges("Mixed", "lhs_ms", changeSet.getLHSMixedSpecialised(), d, "lhs_spec"); addElement("Generalised", "lhs_gen", changeSet.getLHSGeneralisedConcepts().size(), d, parent, true); addGeneralisationChanges("PurelyDirect", "lhs_pdg", changeSet.getLHSPurelyDirectlyGeneralised(), d, "lhs_gen"); addGeneralisationChanges("PurelyIndirect", "lhs_pig", changeSet.getLHSPurelyIndirectlyGeneralised(), d, "lhs_gen"); addGeneralisationChanges("Mixed", "lhs_mg", changeSet.getLHSMixedGeneralised(), d, "lhs_gen"); } /** * Add ontology 2 (RHS) affected concepts * @param parent Parent element * @param d XML document */ private void addRHSConceptDiff(String parent, Document d) { addElement("Specialised", "rhs_spec", changeSet.getRHSSpecialisedConcepts().size(), d, parent, true); addSpecialisationChanges("PurelyDirect", "rhs_pds", changeSet.getRHSPurelyDirectlySpecialised(), d, "rhs_spec"); addSpecialisationChanges("PurelyIndirect", "rhs_pis", changeSet.getRHSPurelyIndirectlySpecialised(), d, "rhs_spec"); addSpecialisationChanges("Mixed", "rhs_ms", changeSet.getRHSMixedSpecialised(), d, "rhs_spec"); addElement("Generalised", "rhs_gen", changeSet.getRHSGeneralisedConcepts().size(), d, parent, true); addGeneralisationChanges("PurelyDirect", "rhs_pdg", changeSet.getRHSPurelyDirectlyGeneralised(), d, "rhs_gen"); addGeneralisationChanges("PurelyIndirect", "rhs_pig", changeSet.getRHSPurelyIndirectlyGeneralised(), d, "rhs_gen"); addGeneralisationChanges("Mixed", "rhs_mg", changeSet.getRHSMixedGeneralised(), d, "rhs_gen"); } /** * Add generalisation changes to the given document * @param desc Change group element name * @param id Id for the element above * @param changes Set of concept changes * @param d XML Document * @param parent Parent element of the change group */ private void addGeneralisationChanges(String desc, String id, Set<? extends ConceptChange> changes, Document d, String parent) { addElement(desc, id, changes.size(), d, parent, true); for(ConceptChange c : changes) { Element witAxsEle = addConceptChange(c, d, id); Set<OWLAxiom> dirgen = c.getDirectGeneralisationWitnesses(); Set<OWLAxiom> indirgen = c.getIndirectGeneralisationWitnesses(); if(c.isDirectlyGeneralised() && c.isIndirectlyGeneralised()) { addWitnessAxioms(witAxsEle, dirgen, d, true); addWitnessAxioms(witAxsEle, indirgen, d, false); witAxsEle.setAttribute("size", ""+(dirgen.size()+indirgen.size())); } else if(c.isDirectlyGeneralised()) { addWitnessAxioms(witAxsEle, dirgen, d, true); witAxsEle.setAttribute("size", ""+dirgen.size()); } else if(c.isIndirectlyGeneralised()) { addWitnessAxioms(witAxsEle, indirgen, d, false); witAxsEle.setAttribute("size", ""+indirgen.size()); } } } /** * Add specialisation changes to the given document * @param desc Change group element name * @param id Id for the element above * @param changes Set of concept changes * @param d XML Document * @param parent Parent element of the change group */ private void addSpecialisationChanges(String desc, String id, Set<? extends ConceptChange> changes, Document d, String parent) { addElement(desc, id, changes.size(), d, parent, true); for(ConceptChange c : changes) { Element witAxsEle = addConceptChange(c, d, id); Set<OWLAxiom> dirspec = c.getDirectSpecialisationWitnesses(); Set<OWLAxiom> indirspec = c.getIndirectSpecialisationWitnesses(); if(c.isDirectlySpecialised() && c.isIndirectlySpecialised()) { addWitnessAxioms(witAxsEle, dirspec, d, true); addWitnessAxioms(witAxsEle, indirspec, d, false); witAxsEle.setAttribute("size", ""+(dirspec.size()+indirspec.size())); } else if(c.isDirectlySpecialised()) { addWitnessAxioms(witAxsEle, dirspec, d, true); witAxsEle.setAttribute("size", ""+dirspec.size()); } else if(c.isIndirectlySpecialised()) { addWitnessAxioms(witAxsEle, indirspec, d, false); witAxsEle.setAttribute("size", ""+indirspec.size()); } } } /** * Add a concept change element * @param c Concept change * @param d XML Document * @param parent Parent element id * @return WitnessAxioms element */ private Element addConceptChange(ConceptChange c, Document d, String parent) { Element cChange = d.createElement("ConceptChange"); Element root = d.getElementById(parent); root.appendChild(cChange); Element cName = d.createElement("ConceptName"); cName.setTextContent(getManchesterRendering(c.getConcept(), sf)); cChange.appendChild(cName); Element wits = d.createElement("WitnessAxioms"); cChange.appendChild(wits); return wits; } /** * Add a set of witness axioms to the WitnessAxioms element specified * @param parent WitnessAxioms element * @param witnesses Set of witness axioms * @param d XML Document * @param direct true if given witness axioms are for a direct change */ private void addWitnessAxioms(Element parent, Set<OWLAxiom> witnesses, Document d, boolean direct) { for(OWLAxiom ax : witnesses) { Element axEle = d.createElement("Axiom"); axEle.setAttribute("direct", "" + direct); axEle.setTextContent(getManchesterRendering(ax, sf)); parent.appendChild(axEle); } } /** * Add element with given name and Id to an XML document * @param name Name of the element * @param id Id of the element * @param size Size of the elements children (axioms) * @param d Document to be added to * @param parent Parent of this new element * @param includeSize Include the size of the children as an attribute of the new element * @return Element */ private Element addElement(String name, String id, int size, Document d, String parent, boolean includeSize) { Element ele = d.createElement(name); if(includeSize) ele.setAttribute("size", "" + size); ele.setAttribute("id", id); ele.setIdAttribute("id", true); Element root = d.getElementById(parent); root.appendChild(ele); return ele; } /** * Prepare output XML document * @param d Document to prepare * @param suffix Suffix for the document identifier */ private void prepDocument(Document d, String suffix) { Element root = d.createElement("root"); root.setAttribute("id", "root"); root.setIdAttribute("id", true); String id = uuid + suffix; root.setAttribute("uuid", id); d.appendChild(root); } /** * Get Manchester syntax of an OWL object * @param obj OWL object * @param sf Short form provider * @return A string with the object's conversion to Manchester syntax */ private String getManchesterRendering(OWLObject obj, ShortFormProvider sf) { StringWriter wr = new StringWriter(); ManchesterOWLSyntaxObjectRenderer render = new ManchesterOWLSyntaxObjectRenderer(wr, sf); obj.accept(render); String str = wr.getBuffer().toString(); str = str.replace("<", ""); str = str.replace(">", ""); return str; } @Override public ChangeSet getChangeSet() { return changeSet; } @Override public Document getXMLDocumentUsingTermNames() { // TODO not implemented throw new NotImplementedException("not implemented".toUpperCase()); } @Override public Document getXMLDocumentUsingLabels() { // TODO not implemented throw new NotImplementedException("not implemented".toUpperCase()); } @Override public Document getXMLDocumentUsingGenSyms() { // TODO not implemented throw new NotImplementedException("not implemented".toUpperCase()); } @Override public String getReportAsHTML(Document doc, String xsltPath) { // TODO not implemented throw new NotImplementedException("not implemented".toUpperCase()); } }